Automated YAML/JSON value updates for GitOps repositories via GitHub API
Project description
gitops-replacer
A lightweight CLI tool that automates value updates in GitOps repositories using marker comments. Replace values across multiple GitHub repositories with a single command, enabling automated deployment workflows.
Features
- Marker-based Approach: Uses
# gitops-replacer: <name>comments to locate values - Format Preservation: No YAML parsing - comments, quotes, and formatting are preserved
- Multiple Dependencies: Update different values in the same file via unique markers
- Flexible Modes: Dry-run for validation, apply mode for commits
- CI/CD Integration: Built-in CI mode with
GITHUB_REFpattern matching - Multiple Repositories: Update values across any number of repos and files
- Configuration Formats: JSON (default) and YAML support
- Performance Optimized: Response caching eliminates duplicate API calls
- Robust HTTP: Automatic retries, timeouts, and error handling
Requirements
- Python 3.10+
- A GitHub/GitHub Enterprise token with content read/write access
Installation
Via pip (recommended)
# Install from PyPI
pip install gitops-replacer
# Verify installation
gitops-replacer --help
From source
# Clone repository
git clone https://github.com/slauger/gitops-replacer.git
cd gitops-replacer
# Install in development mode
pip install -e .
# Or run directly
python -m gitops_replacer --help
Quick Start
- Add marker comments to your target files (see Marker Format)
- Create a configuration file (default:
gitops-replacer.json) - Run a dry-run:
gitops-replacer "1.2.3"
- Apply changes (commit to target repos):
gitops-replacer --apply "1.2.3"
Marker Format
Add a comment above the line you want to update:
dependencies:
# gitops-replacer: my-app
- name: my-app
version: "0.0.0-e0f72bb"
repository: oci://registry.example.com/charts
# gitops-replacer: another-chart
- name: another-chart
version: "1.0.0"
repository: oci://registry.example.com/charts
The tool will:
- Find the line with
# gitops-replacer: <depName> - Replace the value on the next line (preserving key, quotes, and formatting)
Examples
Chart.yaml (Helm dependency version):
dependencies:
# gitops-replacer: my-app
- name: my-app
version: "0.0.0-e0f72bb"
repository: oci://registry.example.com/charts
values.yaml (image tag):
# gitops-replacer: my-app-image
image: registry.example.com/myorg/my-app:1.2.3
Note: Only YAML files are supported (JSON has no comments). GitOps manifests are typically YAML.
CLI
usage: gitops-replacer [-h] [--config <file>] [--apply] [--ci]
[--name <string>] [--email <string>]
[--message <string>] [--api <string>]
[--verbose]
<string>
--configPath to the configuration file (default:gitops-replacer.json). JSON recommended.--applyApply changes (commit). Without this flag the tool runs in dry-run.--ciCI mode: validatesGITHUB_REFagainstwhen/exceptregex patterns from config.--nameCommit author name (default: envGIT_COMMIT_NAMEorReplacer Bot).--emailCommit author email (default: envGIT_COMMIT_EMAILorreplacer-bot@localhost.localdomain).--messageCommit message template (default:fix: update {} to {}). First{}is depName, second is value.--apiGitHub API URL (default: envGITHUB_API_URLorhttps://api.github.com).--verbosePrint file contents and desired state (use with care in CI logs).- Positional:
value- the new value to set at the marked location.
Environment
GITHUB_TOKEN(required) – token with access to read/write repository contents.GITHUB_REF(required when--ci) – the current ref string, e.g.,refs/heads/main. Falls back toGIT_REFfor backwards compatibility.
Recommended token scopes:
- Public repos only:
public_repo - Private repos:
repo - GitHub Enterprise: equivalent content permissions
Configuration
Default format is JSON. YAML (.yaml/.yml) is supported as well.
JSON schema (per entry)
{
"gitops-replacer": [
{
"repository": "acme/gitops",
"branch": "main",
"file": "apps/my-app/Chart.yaml",
"depName": "my-app",
"when": "^refs/heads/main$"
}
]
}
Fields
| Field | Description |
|---|---|
repository |
Target repo on GitHub (ORG/REPO format) |
branch |
Target branch |
file |
Target file path relative to repo root |
depName |
Dependency name (must match marker in file) |
when |
Regex that must match GITHUB_REF when --ci is enabled (optional) |
except |
Regex that must not match GITHUB_REF when --ci is enabled (optional) |
The tool uses
re.match(anchored at the string start). Use^...$in your patterns if you require a full match.
Examples
JSON (default)
{
"gitops-replacer": [
{
"repository": "acme/gitops",
"branch": "main",
"file": "apps/my-app/Chart.yaml",
"depName": "my-app",
"when": "^refs/heads/(main|release/.*)$"
},
{
"repository": "acme/gitops",
"branch": "develop",
"file": "apps/my-app-dev/Chart.yaml",
"depName": "my-app",
"except": "^refs/heads/legacy/"
}
]
}
YAML (alternative)
gitops-replacer:
- repository: acme/gitops
branch: main
file: apps/my-app/Chart.yaml
depName: my-app
when: '^refs/heads/(main|release/.*)$'
- repository: acme/gitops
branch: develop
file: apps/my-app-dev/Chart.yaml
depName: my-app
except: '^refs/heads/legacy/'
How it works
- Validation: Checks CLI arguments, environment variables, and configuration file
- Precheck Phase: Validates access to all target repositories/files (caches responses)
- Replace Phase: Downloads files (reuses cached data), finds marker comments, replaces values
- Commit Phase: If
--applyis set and changes detected, commits via GitHub Contents API - Exit Codes: Returns
0on success, non-zero on failures
Why Marker-based?
Traditional approaches parse YAML, modify the data structure, and serialize back. This often breaks:
- Comments are lost
- Quote styles change (
"1.0"becomes'1.0'or1.0) - Key ordering may change
- Multi-line strings get reformatted
The marker-based approach works on raw text:
- Explicit: Only marked lines are modified
- Safe: No risk of unintended changes
- Preserving: Comments, quotes, and formatting stay intact
Exit Codes
0success (no changes or committed changes)1validation or API error
Use Cases
Automated Deployment Pipeline
Update chart version when a new release is built:
# In your CI/CD pipeline after publishing a chart
gitops-replacer --ci --apply "0.1.0-abc123"
Multi-Environment Updates
Use CI mode to update different environments based on branch:
{
"gitops-replacer": [
{
"repository": "myorg/gitops",
"branch": "main",
"file": "apps/production/Chart.yaml",
"depName": "myapp",
"when": "^refs/heads/main$"
},
{
"repository": "myorg/gitops",
"branch": "main",
"file": "apps/staging/Chart.yaml",
"depName": "myapp",
"when": "^refs/heads/(main|develop)$"
}
]
}
Troubleshooting
Common Issues
401 Unauthorized
- Verify
GITHUB_TOKENis set correctly - Check token has
repoorpublic_reposcope - For GitHub Enterprise, confirm token has access to the organization
404 Not Found
- Verify
repository,branch, andfilepaths in config - Check branch name spelling (case-sensitive)
- Ensure file exists at the specified path
No marker found
- Confirm the marker comment exists in the target file
- Check
depNamein config matches the marker exactly - Marker format:
# gitops-replacer: <depName>
No changes detected
- The current value already matches the new value
- Use
--verboseto see file contents
Debug Mode
Run with --verbose to see:
- Full API URLs being called
- Complete file contents before replacement
- Desired file contents after replacement
Warning: Verbose mode may expose sensitive data in logs.
Contributing
Contributions are welcome! Please ensure:
- Code follows existing style and patterns
- Changes are tested with both dry-run and apply modes
- Documentation is updated for new features
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gitops_replacer-0.1.1.tar.gz.
File metadata
- Download URL: gitops_replacer-0.1.1.tar.gz
- Upload date:
- Size: 13.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21b00b90bc8544e23c37838c50c7539a366921ea8f851f0d4bffdca51403cabb
|
|
| MD5 |
b261266ee9414687eb844734d2758c41
|
|
| BLAKE2b-256 |
fd045e88135b09f8dce6d2e2f5a8c9e16702cb58579333bb0235232ccb75d7fd
|
Provenance
The following attestation bundles were made for gitops_replacer-0.1.1.tar.gz:
Publisher:
release.yml on slauger/gitops-replacer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitops_replacer-0.1.1.tar.gz -
Subject digest:
21b00b90bc8544e23c37838c50c7539a366921ea8f851f0d4bffdca51403cabb - Sigstore transparency entry: 929110827
- Sigstore integration time:
-
Permalink:
slauger/gitops-replacer@b7de530df1a985c23de22649e5da266527c869ec -
Branch / Tag:
refs/heads/main - Owner: https://github.com/slauger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b7de530df1a985c23de22649e5da266527c869ec -
Trigger Event:
push
-
Statement type:
File details
Details for the file gitops_replacer-0.1.1-py3-none-any.whl.
File metadata
- Download URL: gitops_replacer-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a8d4ad79e638674c2257f65c14deae9abdc1433c1bcaa46cbd85f7d71971c5d
|
|
| MD5 |
4fb50fa14f620712145ff12b5e8bf0b9
|
|
| BLAKE2b-256 |
8707a396bcf7488890b39b5f79a4c836df9bd8d68a481f96e4ab23b5e60a6ebe
|
Provenance
The following attestation bundles were made for gitops_replacer-0.1.1-py3-none-any.whl:
Publisher:
release.yml on slauger/gitops-replacer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitops_replacer-0.1.1-py3-none-any.whl -
Subject digest:
1a8d4ad79e638674c2257f65c14deae9abdc1433c1bcaa46cbd85f7d71971c5d - Sigstore transparency entry: 929110829
- Sigstore integration time:
-
Permalink:
slauger/gitops-replacer@b7de530df1a985c23de22649e5da266527c869ec -
Branch / Tag:
refs/heads/main - Owner: https://github.com/slauger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b7de530df1a985c23de22649e5da266527c869ec -
Trigger Event:
push
-
Statement type: