Skip to main content

Automated container image updates for GitOps repositories via GitHub API

Project description

gitops-image-replacer

License: MIT Python 3.8+ PyPI version PyPI downloads

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 GIT_REF pattern 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

  1. Create a configuration file (default: gitops-image-replacer.json).
  2. 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
    
  3. 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>
  • --config Path to the configuration file (default: gitops-image-replacer.json). JSON recommended.
  • --apply Apply changes (commit). Without this flag the tool runs in dry-run.
  • --ci CI mode: validates GIT_REF against when/except regex patterns from config.
  • --name Commit author name (default: env GIT_COMMIT_NAME or Replacer Bot).
  • --email Commit author email (default: env GIT_COMMIT_EMAIL or replacer-bot@localhost.localdomain).
  • --message Commit message template (default: fix: update image to {}).
  • --api GitHub API URL (default: env GITHUB_API_URL or https://github.com/api/v3).
  • --verbose Print 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.
  • GIT_REF (required when --ci) – the current ref string, e.g., refs/heads/main.

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/REPO path on GitHub/GHE
  • branch (string): target branch
  • file (string): target file path in the repository
  • when (string, optional): regex that must match GIT_REF when --ci is enabled
  • except (string, optional): regex that must not match GIT_REF when --ci is 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

  1. Validation: Checks CLI arguments, environment variables, and configuration file
  2. Precheck Phase: Validates access to all target repositories/files (caches responses)
  3. Replace Phase: Downloads files (reuses cached data), performs safe regex replacement
  4. Commit Phase: If --apply is set and changes detected, commits via GitHub Contents API
  5. Exit Codes: Returns 0 on 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

  • 0 success (no changes or committed changes)
  • 1 validation or API error

Best Practices

Testing and Validation

  • Always start with dry-run: Test your configuration without --apply first
  • Use verbose mode carefully: --verbose prints file contents - avoid in CI logs with sensitive data
  • Validate regex patterns: Use ^...$ anchors in when/except for full string matches

Configuration Management

  • Keep config versioned: Store gitops-image-replacer.json in 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/except patterns reduce unnecessary runs
  • Leverage caching: The tool automatically caches API responses

Troubleshooting

Common Issues

401 Unauthorized

  • Verify GITHUB_TOKEN is set correctly
  • Check token has repo or public_repo scope
  • For GitHub Enterprise, confirm token has access to the organization

404 Not Found

  • Verify repository, branch, and file paths 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 --verbose to 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitops_image_replacer-1.0.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitops_image_replacer-1.0.0-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file gitops_image_replacer-1.0.0.tar.gz.

File metadata

  • Download URL: gitops_image_replacer-1.0.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gitops_image_replacer-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3dd170f70fc3de87243d2d7840d54b4bb6b0e5ffda3138d86cdd051b02e342fb
MD5 8574f40a80d6aa97cb33f6eea920fd3e
BLAKE2b-256 e070393e2d257d7e3480bae3dfbbc51fdd38c906f2c7d9815e3d8bb7ac69de30

See more details on using hashes here.

Provenance

The following attestation bundles were made for gitops_image_replacer-1.0.0.tar.gz:

Publisher: release.yml on slauger/gitops-image-replacer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gitops_image_replacer-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for gitops_image_replacer-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fd391f88d1b4b0db932fb78a46581e81ccf7b2b4c3cf8d7d0b9d12067ec47f99
MD5 721ff53906b37e3d996fb10a714a84b9
BLAKE2b-256 9b9ebd7a6eb72bf36dfa379f28cfba1bb3e55c6cb8618553a4852dac92d242d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for gitops_image_replacer-1.0.0-py3-none-any.whl:

Publisher: release.yml on slauger/gitops-image-replacer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page