Skip to content

Add manual workflows for Docker build and RC promotion#890

Open
nevil-mathew wants to merge 21 commits into
developfrom
github-ac
Open

Add manual workflows for Docker build and RC promotion#890
nevil-mathew wants to merge 21 commits into
developfrom
github-ac

Conversation

@nevil-mathew

@nevil-mathew nevil-mathew commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added a manual workflow to build and publish Docker images with versioned tags.
    • Added a separate manual workflow to promote approved release-candidate images to production tags.
  • Bug Fixes

    • Added checks to prevent publishing duplicate Docker tags or Git tags.
    • Added validation to ensure release-candidate tags follow the expected version format and point to the correct mainline commit.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds two manual GitHub Actions workflows: one to build and push a versioned Docker image from a selected branch, and one to promote an RC tag to a production release after verifying commit ancestry and Docker Hub tag state.

Changes

Docker image publishing

Layer / File(s) Summary
Trigger and settings
.github/workflows/docker-image.yml
Defines the manual inputs, default source branch, Docker image naming, registry namespace, and tag-writing permissions.
Validation and publish
.github/workflows/docker-image.yml
Checks out the selected branch, logs in to Docker Hub, validates the requested tag, checks existing Git and Docker Hub tags, generates image metadata, and builds and pushes the multi-arch image.
Git tag and summary
.github/workflows/docker-image.yml
Creates and pushes the annotated Git tag and writes the published tag and image digest to the job summary.

Production release promotion

Layer / File(s) Summary
Trigger and RC tag validation
.github/workflows/prod-release.yml
Defines the workflow inputs, Docker image environment values, concurrency handling, and RC tag to release tag derivation.
Mainline and Docker Hub gates
.github/workflows/prod-release.yml
Checks that the RC commit is contained in origin/main, confirms the RC Docker tag exists, confirms the production tag is absent, and creates the production manifest tag from the RC image.
Git tag and summary
.github/workflows/prod-release.yml
Creates and pushes the production Git tag and writes the RC tag, release tag, commit SHA, image reference, digest, actor, and run link to the job summary.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ELEVATE-Project/user#784: Adds related Docker image build-and-publish workflow logic with tag validation and Docker Hub API checks.
  • ELEVATE-Project/user#878: Touches the same image-publishing path, including version validation, Docker Hub tag checks, and annotated release tagging.
  • ELEVATE-Project/user#879: Implements the promotion workflow that matches the new prod-release.yml ancestry, Docker Hub, and Git tagging flow.

Poem

I hop through tags by moonlit light,
and nudge each manifest just right.
From RC sighs to release cheer,
the burrow hums—Docker is near.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: two manually triggered workflows for Docker image builds and RC-to-release promotion.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch github-ac

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Add manual workflows for Docker build and RC promotion Jun 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker-image.yml:
- Around line 27-30: The checkout step in the Docker image workflow is leaving
the GitHub token in the workspace, which can be exposed during the image build.
Update the existing actions/checkout usage in the workflow to disable credential
persistence, then ensure the token is only provided to the final git push step
that needs it. Use the checkout step and the later push step in the workflow to
locate the change.
- Around line 64-72: The Docker Hub login payload is being built with raw string
interpolation, which can break the JSON when the credentials contain special
characters. Update the Docker Hub login step in the workflow to construct the
request body safely the same way `prod-release.yml` does, using the existing
login command flow and a JSON-safe builder such as `jq --arg` around the `curl`
call. Keep the change localized to the Docker Hub login request so the `Check if
version exists on Docker Hub` step always sends valid JSON.
- Around line 28-36: The workflow currently uses mutable third-party action
tags, which should be replaced with full commit SHAs for supply-chain safety.
Update each `uses:` entry in the release workflow, including the checkout,
buildx setup, Docker login, metadata, build/push, and any other action
references in this job, to pinned commit digests instead of version tags. Keep
the existing behavior and inputs the same while changing only the action
references, and use the action names like actions/checkout,
docker/setup-buildx-action, and docker/login-action to locate all affected
steps.
- Around line 22-24: Add a workflow-level concurrency guard in the docker-image
publish job so only one run per normalized release version can proceed at a
time. Update the docker-image-build-and-push job in the workflow to use a
concurrency group derived from the same normalized tag version used by the tag
validation and push steps, and ensure it cancels or blocks duplicate dispatches
for that version before the later Docker tag and Git tag operations run. Use the
existing version normalization logic and the publish job name as the anchors
when wiring this in.
- Around line 41-53: The Get Docker tag version step is expanding the
user-controlled tag_version input directly inside the bash script, so move that
input into an env variable on the workflow step and read it from the shell there
instead. Update the get-version step in docker-image.yml so the script uses the
environment value for VERSION, keeping the existing validation and output logic
intact.

In @.github/workflows/prod-release.yml:
- Around line 125-142: The production release workflow in the retag-and-tag
section is checking for an existing Git tag too late, after mutating the Docker
image tag. Move the Git tag existence validation ahead of the `docker buildx
imagetools create` step, and update the check in the `Create production Git tag`
flow to use `refs/tags/$VERSION` so the gate prevents any Docker Hub changes
when the tag already exists.
- Around line 40-43: The checkout step is persisting the repository write token
in Git config, which can expose credentials to later steps; update the
workflow’s actions/checkout usage to disable credential persistence and scope
the token only to the tag-push step. Use the checkout step and the later tag
publishing/push step in the same workflow as the places to adjust, ensuring only
the push operation receives the write token while all other steps run without
stored credentials.
- Around line 45-58: The RC verification step is resolving the tag name too
loosely, so update the shell logic in the `verify` step to peel the exact tag
ref for `RC_TAG` using the tag-specific ref under `git rev-list`/`git rev-parse`
before running the ancestry check. Keep the existing `git merge-base
--is-ancestor` gate, but ensure it operates on the commit reached from
`refs/tags/$RC_TAG` so only the intended RC tag is approved for promotion.
- Line 41: The prod release workflow is using mutable Action tags, which should
be replaced with immutable SHAs for deterministic and safer execution. Update
the workflow steps that use actions/checkout, docker/login-action, and
docker/setup-buildx-action to pin them to the provided verified commit SHAs
instead of `@v4/`@v3 tags, keeping the existing step structure in the release job
intact.
- Around line 71-80: The Docker Hub login step still interpolates secrets
directly into the shell script, which can allow shell metacharacters in the
values to affect execution. Update the workflow step that builds LOGIN_RESPONSE
to pass DOCKERHUB_USERNAME and DOCKERHUB_TOKEN through step-level env vars, then
reference those env vars inside the jq --arg call instead of using inline GitHub
expression expansion. Keep the existing jq and curl flow in place, but ensure
the secret values are only read from env within the login step.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d4094ad-2fc0-4391-beee-6f86f8effebb

📥 Commits

Reviewing files that changed from the base of the PR and between 29d62b7 and 8a61870.

📒 Files selected for processing (2)
  • .github/workflows/docker-image.yml
  • .github/workflows/prod-release.yml

Comment on lines +22 to +24
jobs:
docker-image-build-and-push:
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Serialize runs per release tag.

The “tag absent” checks and the later push/tag steps are not atomic. Two dispatches for the same tag_version can both pass validation, and the losing run can still overwrite the Docker tag before its Git tag push fails. Add a concurrency group keyed by the normalized version so only one publish for a given tag can run at a time.

Also applies to: 55-128

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-image.yml around lines 22 - 24, Add a
workflow-level concurrency guard in the docker-image publish job so only one run
per normalized release version can proceed at a time. Update the
docker-image-build-and-push job in the workflow to use a concurrency group
derived from the same normalized tag version used by the tag validation and push
steps, and ensure it cancels or blocks duplicate dispatches for that version
before the later Docker tag and Git tag operations run. Use the existing version
normalization logic and the publish job name as the anchors when wiring this in.

Comment on lines +27 to +30
- name: Checkout code from target branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch || 'staging' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Don't persist the workflow token in the build context.

actions/checkout leaves the write-scoped GitHub token in .git/config by default. Because this job then builds a Docker image from a user-selected branch using the repo workspace as context, a branch-controlled Dockerfile can exfiltrate that token during the build. Set persist-credentials: false on checkout, then pass GITHUB_TOKEN only to the final git push step.

Suggested change
     - name: Checkout code from target branch
       uses: actions/checkout@v4
       with:
         ref: ${{ github.event.inputs.branch || 'staging' }}
+        persist-credentials: false
...
     - name: Create and push Git tag
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
       run: |
         VERSION="${{ steps.get-version.outputs.version }}"
         git config user.name "github-actions"
         git config user.email "github-actions@github.com"
         git tag -a "$VERSION" -m "Release $VERSION"
-        git push origin "$VERSION"
+        git push "https://x-access-token:${GITHUB_TOKEN}`@github.com/`${GITHUB_REPOSITORY}.git" "$VERSION"

Also applies to: 109-120, 122-128

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-30: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 28-28: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-image.yml around lines 27 - 30, The checkout step
in the Docker image workflow is leaving the GitHub token in the workspace, which
can be exposed during the image build. Update the existing actions/checkout
usage in the workflow to disable credential persistence, then ensure the token
is only provided to the final git push step that needs it. Use the checkout step
and the later push step in the workflow to locate the change.

Source: Linters/SAST tools

Comment on lines +28 to +36
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch || 'staging' }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin every third-party action to a full commit SHA.

These uses: entries are all mutable tags today. With a release workflow that has Docker Hub credentials and contents: write, that leaves the pipeline open to upstream action compromise. Please pin each action to its published commit digest.

Also applies to: 103-111

🧰 Tools
🪛 zizmor (1.26.1)

[error] 28-28: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 33-33: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 36-36: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-image.yml around lines 28 - 36, The workflow
currently uses mutable third-party action tags, which should be replaced with
full commit SHAs for supply-chain safety. Update each `uses:` entry in the
release workflow, including the checkout, buildx setup, Docker login, metadata,
build/push, and any other action references in this job, to pinned commit
digests instead of version tags. Keep the existing behavior and inputs the same
while changing only the action references, and use the action names like
actions/checkout, docker/setup-buildx-action, and docker/login-action to locate
all affected steps.

Source: Linters/SAST tools

Comment on lines +41 to +53
- name: Get Docker tag version
id: get-version
shell: bash
run: |
VERSION="${{ github.event.inputs.tag_version }}"
# Strip leading "v" if present
VERSION="${VERSION#v}"
# Enforce x.y.z or x.y.z-rc.1 format
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "Error: tag_version must look like 3.4.0 or 3.4.0-rc.1 (no other suffixes allowed)"
exit 1
fi
printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Move tag_version into env before the shell parses it.

Line 45 expands a user-controlled workflow input directly into bash, so the injection happens before your regex validation. Read the input from an environment variable instead of embedding ${{ ... }} in the script body.

Suggested change
     - name: Get Docker tag version
       id: get-version
       shell: bash
+      env:
+        INPUT_TAG_VERSION: ${{ github.event.inputs.tag_version }}
       run: |
-        VERSION="${{ github.event.inputs.tag_version }}"
+        VERSION="$INPUT_TAG_VERSION"
         # Strip leading "v" if present
         VERSION="${VERSION#v}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Get Docker tag version
id: get-version
shell: bash
run: |
VERSION="${{ github.event.inputs.tag_version }}"
# Strip leading "v" if present
VERSION="${VERSION#v}"
# Enforce x.y.z or x.y.z-rc.1 format
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "Error: tag_version must look like 3.4.0 or 3.4.0-rc.1 (no other suffixes allowed)"
exit 1
fi
printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT"
- name: Get Docker tag version
id: get-version
shell: bash
env:
INPUT_TAG_VERSION: ${{ github.event.inputs.tag_version }}
run: |
VERSION="$INPUT_TAG_VERSION"
# Strip leading "v" if present
VERSION="${VERSION#v}"
# Enforce x.y.z or x.y.z-rc.1 format
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "Error: tag_version must look like 3.4.0 or 3.4.0-rc.1 (no other suffixes allowed)"
exit 1
fi
printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT"
🧰 Tools
🪛 zizmor (1.26.1)

[error] 45-45: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-image.yml around lines 41 - 53, The Get Docker tag
version step is expanding the user-controlled tag_version input directly inside
the bash script, so move that input into an env variable on the workflow step
and read it from the shell there instead. Update the get-version step in
docker-image.yml so the script uses the environment value for VERSION, keeping
the existing validation and output logic intact.

Source: Linters/SAST tools

Comment on lines +64 to +72
- name: Check if version exists on Docker Hub
run: |
VERSION="${{ steps.get-version.outputs.version }}"

LOGIN_RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "${{ secrets.DOCKERHUB_USERNAME }}", "password": "${{ secrets.DOCKERHUB_TOKEN }}"}' \
"https://hub.docker.com/v2/users/login")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Build the Docker Hub login JSON safely.

This payload is assembled with raw string interpolation, so a valid Docker Hub username/token containing JSON-significant characters can make the login request malformed. prod-release.yml already fixes the same call with jq --arg; mirror that here to avoid spurious auth failures.

🧰 Tools
🪛 zizmor (1.26.1)

[info] 66-66: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-image.yml around lines 64 - 72, The Docker Hub
login payload is being built with raw string interpolation, which can break the
JSON when the credentials contain special characters. Update the Docker Hub
login step in the workflow to construct the request body safely the same way
`prod-release.yml` does, using the existing login command flow and a JSON-safe
builder such as `jq --arg` around the `curl` call. Keep the change localized to
the Docker Hub login request so the `Check if version exists on Docker Hub` step
always sends valid JSON.

Comment on lines +40 to +43
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid persisting the write token across all later steps.

actions/checkout leaves the contents: write token in Git config by default. Set persist-credentials: false and provide the token only for the tag push step.

🔒 Proposed credential scoping change
     - name: Checkout repository
       uses: actions/checkout@v4
       with:
         fetch-depth: 0
+        persist-credentials: false
...
     - name: Create production Git tag
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
       run: |
         VERSION="${{ steps.version.outputs.release }}"
         git config user.name "github-actions"
         git config user.email "github-actions@github.com"
         git fetch --tags
         if git rev-parse "$VERSION" >/dev/null 2>&1; then
           echo "Error: Git tag $VERSION already exists"
           exit 1
         fi
         git tag -a "$VERSION" "${{ steps.verify.outputs.rc_commit }}" -m "Release $VERSION"
-        git push origin "$VERSION"
+        git -c http.https://github.com/.extraheader="AUTHORIZATION: bearer $GITHUB_TOKEN" push origin "$VERSION"

Also applies to: 131-142

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 40-43: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 41-41: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-release.yml around lines 40 - 43, The checkout step
is persisting the repository write token in Git config, which can expose
credentials to later steps; update the workflow’s actions/checkout usage to
disable credential persistence and scope the token only to the tag-push step.
Use the checkout step and the later tag publishing/push step in the same
workflow as the places to adjust, ensuring only the push operation receives the
write token while all other steps run without stored credentials.

Source: Linters/SAST tools

echo "release=$RELEASE_TAG" >> $GITHUB_OUTPUT

- name: Checkout repository
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for ref in \
  "actions/checkout v4" \
  "docker/login-action v3" \
  "docker/setup-buildx-action v3"
do
  repo="${ref% *}"
  tag="${ref#* }"
  echo "== ${repo}@${tag} =="
  git ls-remote --tags "https://github.com/${repo}.git" "refs/tags/${tag}" "refs/tags/${tag}^{}"
done

Repository: ELEVATE-Project/user

Length of output: 411


Pin release workflow Actions to immutable SHAs.

This workflow holds contents: write permissions, making mutable Action tags (@v4, @v3) an unacceptable supply-chain risk for a production release path. Resolve each tag to the following verified commit SHAs to ensure deterministic execution:

  • actions/checkout@v434e114876b0b11c390a56381ad16ebd13914f8d5
  • docker/login-action@v3c94ce9fb468520275223c153574b00df6fe4bcc9
  • docker/setup-buildx-action@v38d2750c68a42422c14e847fe6c8ac0403b4cbd6f
Current Action References
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
      - uses: docker/setup-buildx-action@v3
🧰 Tools
🪛 zizmor (1.26.1)

[error] 41-41: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-release.yml at line 41, The prod release workflow is
using mutable Action tags, which should be replaced with immutable SHAs for
deterministic and safer execution. Update the workflow steps that use
actions/checkout, docker/login-action, and docker/setup-buildx-action to pin
them to the provided verified commit SHAs instead of `@v4/`@v3 tags, keeping the
existing step structure in the release job intact.

Source: Linters/SAST tools

Comment on lines +45 to +58
- name: Verify RC commit exists in main
id: verify
run: |
RC_TAG="${{ steps.version.outputs.rc }}"
git fetch origin main --tags
RC_COMMIT=$(git rev-list -n 1 "$RC_TAG")
if git merge-base --is-ancestor "$RC_COMMIT" origin/main; then
echo "RC commit is present in main — safe to promote."
else
echo "Error: RC tag commit is NOT in main."
echo "Merge staging → main before promoting."
exit 1
fi
echo "rc_commit=$RC_COMMIT" >> $GITHUB_OUTPUT

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Resolve the RC as an exact tag ref.

git rev-list "$RC_TAG" can resolve other refs with the same name. Since this gate is specifically approving an RC tag, peel refs/tags/$RC_TAG explicitly before checking ancestry.

✅ Proposed exact-ref check
-        RC_COMMIT=$(git rev-list -n 1 "$RC_TAG")
+        RC_COMMIT=$(git rev-parse --verify "refs/tags/$RC_TAG^{commit}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Verify RC commit exists in main
id: verify
run: |
RC_TAG="${{ steps.version.outputs.rc }}"
git fetch origin main --tags
RC_COMMIT=$(git rev-list -n 1 "$RC_TAG")
if git merge-base --is-ancestor "$RC_COMMIT" origin/main; then
echo "RC commit is present in main — safe to promote."
else
echo "Error: RC tag commit is NOT in main."
echo "Merge staging → main before promoting."
exit 1
fi
echo "rc_commit=$RC_COMMIT" >> $GITHUB_OUTPUT
- name: Verify RC commit exists in main
id: verify
run: |
RC_TAG="${{ steps.version.outputs.rc }}"
git fetch origin main --tags
RC_COMMIT=$(git rev-parse --verify "refs/tags/$RC_TAG^{commit}")
if git merge-base --is-ancestor "$RC_COMMIT" origin/main; then
echo "RC commit is present in main — safe to promote."
else
echo "Error: RC tag commit is NOT in main."
echo "Merge staging → main before promoting."
exit 1
fi
echo "rc_commit=$RC_COMMIT" >> $GITHUB_OUTPUT
🧰 Tools
🪛 zizmor (1.26.1)

[info] 48-48: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-release.yml around lines 45 - 58, The RC verification
step is resolving the tag name too loosely, so update the shell logic in the
`verify` step to peel the exact tag ref for `RC_TAG` using the tag-specific ref
under `git rev-list`/`git rev-parse` before running the ancestry check. Keep the
existing `git merge-base --is-ancestor` gate, but ensure it operates on the
commit reached from `refs/tags/$RC_TAG` so only the intended RC tag is approved
for promotion.

Comment on lines +71 to +80
# FIX 1: Build JSON safely with jq --arg to avoid special-character injection
LOGIN_RESPONSE=$(jq -n \
--arg username "${{ secrets.DOCKERHUB_USERNAME }}" \
--arg password "${{ secrets.DOCKERHUB_TOKEN }}" \
'{"username": $username, "password": $password}' \
| curl -s -w "\n%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-d @- \
"https://hub.docker.com/v2/users/login")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pass Docker Hub secrets through env, not inline shell expansion.

jq --arg protects the JSON, but the secret values are still template-expanded into the shell script first. Put them in step env vars so shell metacharacters in a token cannot alter the script.

🔐 Proposed safer secret handling
     - name: Check RC image exists and production tag is absent on Docker Hub
+      env:
+        DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
+        DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
       run: |
...
         LOGIN_RESPONSE=$(jq -n \
-          --arg username "${{ secrets.DOCKERHUB_USERNAME }}" \
-          --arg password "${{ secrets.DOCKERHUB_TOKEN }}" \
+          --arg username "$DOCKERHUB_USERNAME" \
+          --arg password "$DOCKERHUB_TOKEN" \
           '{"username": $username, "password": $password}' \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# FIX 1: Build JSON safely with jq --arg to avoid special-character injection
LOGIN_RESPONSE=$(jq -n \
--arg username "${{ secrets.DOCKERHUB_USERNAME }}" \
--arg password "${{ secrets.DOCKERHUB_TOKEN }}" \
'{"username": $username, "password": $password}' \
| curl -s -w "\n%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-d @- \
"https://hub.docker.com/v2/users/login")
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
# FIX 1: Build JSON safely with jq --arg to avoid special-character injection
LOGIN_RESPONSE=$(jq -n \
--arg username "$DOCKERHUB_USERNAME" \
--arg password "$DOCKERHUB_TOKEN" \
'{"username": $username, "password": $password}' \
| curl -s -w "\n%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-d `@-` \
"https://hub.docker.com/v2/users/login")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-release.yml around lines 71 - 80, The Docker Hub
login step still interpolates secrets directly into the shell script, which can
allow shell metacharacters in the values to affect execution. Update the
workflow step that builds LOGIN_RESPONSE to pass DOCKERHUB_USERNAME and
DOCKERHUB_TOKEN through step-level env vars, then reference those env vars
inside the jq --arg call instead of using inline GitHub expression expansion.
Keep the existing jq and curl flow in place, but ensure the secret values are
only read from env within the login step.

Comment on lines +125 to +142
- name: Retag RC image as production (multi-arch)
run: |
docker buildx imagetools create \
--tag ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} \
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.rc }}

- name: Create production Git tag
run: |
VERSION="${{ steps.version.outputs.release }}"
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git fetch --tags
if git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "Error: Git tag $VERSION already exists"
exit 1
fi
git tag -a "$VERSION" "${{ steps.verify.outputs.rc_commit }}" -m "Release $VERSION"
git push origin "$VERSION"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Check the Git production tag before mutating Docker Hub.

The workflow creates the production Docker tag before checking whether the Git tag already exists. If Line 137 fails, Docker Hub has already been changed and reruns will then fail at the Docker tag gate.

Move the Git tag absence check before docker buildx imagetools create, and use refs/tags/$VERSION for the check.

🧰 Tools
🪛 zizmor (1.26.1)

[info] 128-128: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 129-129: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 133-133: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 141-141: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-release.yml around lines 125 - 142, The production
release workflow in the retag-and-tag section is checking for an existing Git
tag too late, after mutating the Docker image tag. Move the Git tag existence
validation ahead of the `docker buildx imagetools create` step, and update the
check in the `Create production Git tag` flow to use `refs/tags/$VERSION` so the
gate prevents any Docker Hub changes when the tag already exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant