Automated container image updates for GitOps repositories via GitHub API
Project description
gitops-image-replacer
A lightweight CLI tool that automates container image updates in GitOps repositories. Replace image tags and/or digests across multiple GitHub repositories with a single command, enabling automated deployment workflows.
Features
- Flexible Modes: Dry-run for validation, apply mode for commits
- CI/CD Integration: Built-in CI mode with
GITHUB_REFpattern matching - Complete Image Support: Handles tags, digests, and combined references (e.g.,
registry.io/ns/app:1.2.3@sha256:...) - Multiple Repositories: Update images across any number of repos and files
- Configuration Formats: JSON (default) and YAML support
- Performance Optimized: Response caching eliminates duplicate API calls
- Safe Operations: Regex escaping prevents unintended replacements
- Robust HTTP: Automatic retries, timeouts, and error handling
- Clean Logging: Minimal output by default, verbose mode for debugging
Requirements
- Python 3.8+
- A GitHub/GitHub Enterprise token with content read/write access
Installation
Via pip (recommended)
# Install from PyPI
pip install gitops-image-replacer
# Verify installation
gitops-image-replacer --help
From source
# Clone repository
git clone https://github.com/slauger/gitops-image-replacer.git
cd gitops-image-replacer
# Install in development mode
pip install -e .
# Or run directly
python -m gitops_image_replacer --help
Quick Start
- Create a configuration file (default:
gitops-image-replacer.json). - Run a dry-run:
gitops-image-replacer docker.io/example/app:2.0.0 gitops-image-replacer docker.io/example/app:2.0.0@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 gitops-image-replacer docker.io/example/app@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 gitops-image-replacer --config gitops-image-replacer.json docker.io/example/app:2.0.0
- Apply changes (commit to target repos):
gitops-image-replacer --apply docker.io/example/app:2.0.0 gitops-image-replacer --config gitops-image-replacer.json --apply docker.io/example/app:2.0.0
CLI
usage: gitops-image-replacer [-h] [--config <file>] [--apply] [--ci]
[--name <string>] [--email <string>]
[--message <string>] [--api <string>]
[--verbose]
<string>
--configPath to the configuration file (default:gitops-image-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 image to {}).--apiGitHub API URL (default: envGITHUB_API_URLorhttps://github.com/api/v3).--verbosePrint file contents and desired state (use with care in CI logs).- Positional:
image(e.g.,docker.io/foo/bar:2.0.0). Tag and digest are optional.
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-image-replacer": [
{
"repository": "org/repo",
"branch": "main",
"file": "path/to/values.yaml",
"when": "^refs/heads/(main|release/.*)$",
"except": "^refs/heads/feature/"
}
]
}
Fields
repository(string):ORG/REPOpath on GitHub/GHEbranch(string): target branchfile(string): target file path in the repositorywhen(string, optional): regex that must matchGITHUB_REFwhen--ciis enabledexcept(string, optional): regex that must not matchGITHUB_REFwhen--ciis enabled
The tool uses
re.match(anchored at the string start). Use^...$in your patterns if you require a full match.
Examples
JSON (default)
{
"gitops-image-replacer": [
{
"repository": "acme/online-shop",
"branch": "main",
"file": "deploy/values.yaml",
"when": "^refs/heads/(main|release/.*)$"
},
{
"repository": "acme/payments",
"branch": "develop",
"file": "charts/payments/values.yaml",
"except": "^refs/heads/legacy/"
}
]
}
YAML (alternative)
gitops-image-replacer:
- repository: acme/online-shop
branch: main
file: deploy/values.yaml
when: '^refs/heads/(main|release/.*)$'
- repository: acme/payments
branch: develop
file: charts/payments/values.yaml
except: '^refs/heads/legacy/'
Supported Image Formats
The tool validates and matches container images using a comprehensive regex pattern. All components are optional except the image name.
Regex Pattern
(?P<repository>[\w.\-_]+((?::\d+|)(?=/[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+))|)(?:/|)(?P<image>[a-zA-Z0-9.\-_]+(?:/[a-zA-Z0-9.\-_]+|))(:(?P<tag>[\w.\-_]{1,127})|)?(@(?P<digest>sha256:[a-f0-9]{64}))?
Pattern Components
- Repository/Registry (optional):
docker.io,gcr.io,registry.example.com:5000- Supports hostnames with optional ports
- Alphanumeric, dots, dashes, underscores
- Image Name (required):
library/nginx,myorg/myapp,MyApp- Case-sensitive (supports both uppercase and lowercase)
- Can include organization/namespace
- Tag (optional):
:latest,:v1.2.3,:20241125-abc123- Max 127 characters
- Alphanumeric, dots, dashes, underscores
- Digest (optional):
@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1- SHA256 hash (64 hex characters, lowercase)
Valid Examples
# Simple image name
nginx
# With registry and tag
docker.io/library/nginx:1.25
# With digest only
gcr.io/myproject/myapp@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
# Tag and digest (immutable reference)
registry.example.com:5000/org/app:v2.0.0@sha256:abc...
# Case-sensitive names
ghcr.io/MyOrg/MyApp:latest
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), performs safe regex replacement
- Commit Phase: If
--applyis set and changes detected, commits via GitHub Contents API - Exit Codes: Returns
0on success, non-zero on failures
Performance Optimization
The tool caches file contents from the precheck phase, eliminating duplicate API calls during the replace phase. This reduces GitHub API usage by approximately 50% in typical scenarios.
Exit Codes
0success (no changes or committed changes)1validation or API error
Best Practices
Testing and Validation
- Always start with dry-run: Test your configuration without
--applyfirst - Use verbose mode carefully:
--verboseprints file contents - avoid in CI logs with sensitive data - Validate regex patterns: Use
^...$anchors inwhen/exceptfor full string matches
Configuration Management
- Keep config versioned: Store
gitops-image-replacer.jsonin your repository - Use meaningful names: Standard naming makes the file's purpose obvious
- Document patterns: Comment your regex patterns (especially in CI mode)
Security
- Limit token scope: Use minimal permissions (contents: write)
- Rotate tokens: Regularly update GitHub tokens
- Review changes: Use dry-run before production deployments
Performance
- Minimize targets: Only configure files that need updates
- Use CI patterns wisely:
when/exceptpatterns reduce unnecessary runs - Leverage caching: The tool automatically caches API responses
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 changes detected
- Confirm target file contains the exact image name (case-sensitive)
- Tags and digests are optional in the search pattern
- Use
--verboseto see file contents and verify the image reference format
Rate limiting (429/403)
- Built-in retries handle temporary rate limits
- Reduce execution frequency in CI/CD pipelines
- Consider using GitHub App tokens for higher limits
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.
Use Cases
Automated Deployment Pipeline
Update production manifests when a new image is built:
# In your CI/CD pipeline after building image
gitops-image-replacer --apply docker.io/myorg/myapp:${CI_COMMIT_SHA}
Multi-Environment Updates
Use CI mode to update different environments based on branch:
{
"gitops-image-replacer": [
{
"repository": "myorg/gitops-production",
"branch": "main",
"file": "apps/myapp/deployment.yaml",
"when": "^refs/heads/main$"
},
{
"repository": "myorg/gitops-staging",
"branch": "main",
"file": "apps/myapp/deployment.yaml",
"when": "^refs/heads/(main|release/.*)$"
}
]
}
Digest Pinning
Update images with immutable digest references:
gitops-image-replacer --apply \
registry.example.com/myapp:v2.0.0@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
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_image_replacer-1.1.2.tar.gz.
File metadata
- Download URL: gitops_image_replacer-1.1.2.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3738bdbca2f2a7e7687931333477a168a58074d7455e706b165e46f8fa6ba3dc
|
|
| MD5 |
6155bf2d7fd589e7443d686ff5e5a43f
|
|
| BLAKE2b-256 |
74ace5e9c41a394a112336a9c6918416abad23197ca1918f2856f19874dd2322
|
Provenance
The following attestation bundles were made for gitops_image_replacer-1.1.2.tar.gz:
Publisher:
release.yml on slauger/gitops-image-replacer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitops_image_replacer-1.1.2.tar.gz -
Subject digest:
3738bdbca2f2a7e7687931333477a168a58074d7455e706b165e46f8fa6ba3dc - Sigstore transparency entry: 929110781
- Sigstore integration time:
-
Permalink:
slauger/gitops-image-replacer@8bcd59579772256900b7cff8ab04109fe111cba7 -
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@8bcd59579772256900b7cff8ab04109fe111cba7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file gitops_image_replacer-1.1.2-py3-none-any.whl.
File metadata
- Download URL: gitops_image_replacer-1.1.2-py3-none-any.whl
- Upload date:
- Size: 10.6 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 |
d8b13ad8cf4ab07d8daf198d14ca310afea0266680b7c22662cf7f8797bbc050
|
|
| MD5 |
b7b02202117f9b23f0d40537c27268a1
|
|
| BLAKE2b-256 |
a744ae050954f493a740b8e8c369faef00b2da70df7222feeff1627303516fc7
|
Provenance
The following attestation bundles were made for gitops_image_replacer-1.1.2-py3-none-any.whl:
Publisher:
release.yml on slauger/gitops-image-replacer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitops_image_replacer-1.1.2-py3-none-any.whl -
Subject digest:
d8b13ad8cf4ab07d8daf198d14ca310afea0266680b7c22662cf7f8797bbc050 - Sigstore transparency entry: 929110782
- Sigstore integration time:
-
Permalink:
slauger/gitops-image-replacer@8bcd59579772256900b7cff8ab04109fe111cba7 -
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@8bcd59579772256900b7cff8ab04109fe111cba7 -
Trigger Event:
push
-
Statement type: