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.
- 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
- Python 3.10+
- A GitHub/GitHub Enterprise token with content read/write access
# Install from PyPI
pip install gitops-replacer
# Verify installation
gitops-replacer --help# 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- 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"
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/chartsThe tool will:
- Find the line with
# gitops-replacer: <depName> - Replace the value on the next line (preserving key, quotes, and formatting)
Chart.yaml (Helm dependency version):
dependencies:
# gitops-replacer: my-app
- name: my-app
version: "0.0.0-e0f72bb"
repository: oci://registry.example.com/chartsvalues.yaml (image tag):
# gitops-replacer: my-app-image
image: registry.example.com/myorg/my-app:1.2.3Note: Only YAML files are supported (JSON has no comments). GitOps manifests are typically YAML.
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.
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
Default format is JSON. YAML (.yaml/.yml) is supported as well.
{
"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.
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/'- 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
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
0success (no changes or committed changes)1validation or API error
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"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)$"
}
]
}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
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.
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
This project is licensed under the MIT License - see the LICENSE file for details.