rios0rios0/pipelines

Change Docker build pipelines to just add the tag on the image instead of triggering the whole building process again with a different tag

Open

#230 opened on Dec 19, 2025

 (0 comments) (1 reaction) (2 assignees)Shell (5 forks)github user discovery
documentationenhancementhelp wanted

Repository metrics

Stars
 (22 stars)
PR merge metrics
 (Avg merge 8h 32m) (39 merged PRs in 30d)

Description

Summary

Problem: When a release tag is pushed we re-run the full Docker build pipeline that already ran on main producing :latest. This wastes CI time and resources because the image content is identical — we only need to add a release tag to the already-built image.
Goal: On tag events, fetch the :latest image produced by the main build, add the release tag, and push the new tag — do not rebuild.

Background

  • Current flow: main push → build image registry/org/image:latest → push. Tag push → pipeline triggers full rebuild and pushes registry/org/image:vX.Y.Z.
  • Desired flow: main push → build and push :latest. Tag push → pull :latest, tag it as the release tag, push the new tag.
  • Benefits: faster releases, lower CI cost, consistent image content between :latest and release tags, fewer build failures caused by rebuild nondeterminism.

Proposed Solution

  1. Add a lightweight pipeline that runs on push of tags only.
  2. That pipeline should:
    • Authenticate to the container registry.
    • Pull the :latest image for the same repository.
    • Tag the pulled image with the release tag (the git tag name).
    • Push the new tag to the registry.
  3. Keep the existing build pipeline for main unchanged (it still produces :latest).
  4. Add safeguards to ensure the :latest image exists and that the digest matches the commit referenced by the tag when possible.

Implementation Plan

Pipeline trigger

  • New workflow file or job triggered on push with tags:
on:
  push:
    tags:
      - 'v*' # or your tag pattern

Example GitHub Actions job (Docker Hub or GHCR)

jobs:
  retag:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Log in to registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: Pull latest image
        run: |
          IMAGE=ghcr.io/org/repo
          docker pull ${IMAGE}:latest

      - name: Tag with release
        run: |
          IMAGE=ghcr.io/org/repo
          TAG=${GITHUB_REF#refs/tags/}
          docker tag ${IMAGE}:latest ${IMAGE}:${TAG}

      - name: Push release tag
        run: |
          IMAGE=ghcr.io/org/repo
          TAG=${GITHUB_REF#refs/tags/}
          docker push ${IMAGE}:${TAG}

Registry specific notes

  • ECR: use aws ecr get-login-password and docker pull/docker tag/docker push.
  • Private registries: ensure credentials are available in secrets and the runner has network access.
  • Multi-arch images: if :latest is a manifest list, use docker manifest or skopeo to copy/retag the manifest list rather than pulling individual arch images. Example: skopeo copy docker://registry/org/repo:latest docker://registry/org/repo:${TAG}.

Acceptance Criteria

  • Tag push no longer triggers a full Docker build job for that repository; instead the retag job runs and completes in under 2 minutes (network permitting).
  • The release tag points to the exact same image digest as :latest produced by the main build.
  • Existing main build pipeline remains unchanged and continues to produce :latest.
  • Logs show docker pull, docker tag, and docker push steps; no docker build is executed during tag pipeline.
  • Tests for at least one repository in the monorepo confirm the image content is identical (digest equality).

Testing Plan

  • Dry run: create a test tag vtest-<timestamp> and verify the retag job pulls :latest and pushes vtest-....
  • Digest check: after pushing, compare docker inspect --format='{{index .RepoDigests 0}}' or skopeo inspect to ensure digests match.
  • Failure modes: simulate missing :latest and confirm pipeline fails with a clear error message and guidance to rebuild main.
  • Multi-arch: test with a multi-arch :latest image and verify manifest copy preserves all architectures.

Rollback and Risks

  • Risk: :latest may not correspond to the tag commit if main build failed or was re-run. Mitigation: add a check that :latest digest is associated with the expected commit when possible (store image digest as build artifact or in registry metadata). If mismatch, fail the retag and require a rebuild.
  • Risk: network or registry rate limits. Mitigation: add retries and backoff.
  • Rollback: revert the tag-only workflow and re-enable the previous tag-triggered build job.

Tasks and Labels

  • Tasks
    • Create retag-on-tag.yml workflow (or add job to existing workflows) — implement for GHCR and Docker Hub templates.
    • Add registry credential secrets documentation.
    • Add digest verification step (optional but recommended).
    • Add tests for single-arch and multi-arch images.
    • Update README and CI docs.
  • Suggested labels: ci, docker, performance, low-risk
  • Suggested assignees: @devops-team, @owner-of-pipelines
  • Estimate: 2–4 hours for basic implementation; 1–2 additional days to cover multi-arch and digest verification.

Contributor guide