-
Notifications
You must be signed in to change notification settings - Fork 25
Add Docker Publishing Workflows and Update CircleCI Configuration #893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
32f2d53
b7f6970
020da8d
35df28e
b8f4948
f7d7bf3
0ebebe1
6eeb08a
a3bcc4a
bc9ff33
f78daa7
3c9ed3d
d66e074
13a22f2
d9066aa
68fcbfa
cdb3284
200f719
b7bc66c
7a6321d
6203236
f942210
dca3785
3ae42a3
d4c4bc8
2a82b54
cbadf99
33f3c1b
bc7f2be
bd40501
83aa5fe
8e1b61f
79cf8c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,134 @@ | ||||||||||||||||||||||||||||||
| name: Build, Tag, and Push Docker Image | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||
| tag_version: | ||||||||||||||||||||||||||||||
| description: 'Docker image tag version (e.g., 3.3.11)' | ||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||
| branch: | ||||||||||||||||||||||||||||||
| description: 'Branch to build from (default: staging)' | ||||||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||||||
| default: 'staging' | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||
| DOCKER_IMAGE_NAME: elevate-user | ||||||||||||||||||||||||||||||
| DOCKER_REGISTRY: docker.io | ||||||||||||||||||||||||||||||
| DOCKER_NAMESPACE: shikshalokamqa | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||
| docker-image-build-and-push: | ||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||
| - name: Checkout code from target branch | ||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||||||||||||||||||||||||||||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - 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: Check if Git tag already exists | ||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||
| VERSION="${{ steps.get-version.outputs.version }}" | ||||||||||||||||||||||||||||||
| git fetch --tags | ||||||||||||||||||||||||||||||
| if git rev-parse "$VERSION" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Strictly check for Git tags to avoid branch collisions. Using 💚 Proposed fix to check tags strictly- if git rev-parse "$VERSION" >/dev/null 2>&1; then
+ if git rev-parse "refs/tags/$VERSION" >/dev/null 2>&1; then📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| echo "Error: Git tag $VERSION already exists" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - 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") | ||||||||||||||||||||||||||||||
|
Comment on lines
+68
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Build JSON safely to prevent breakage or injection. Constructing the JSON payload manually with string interpolation breaks if the Docker Hub token contains special characters (like quotes or backslashes), leading to invalid JSON and a failed login. Use 🔧 Proposed fix for robust JSON construction- 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")
+ 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")📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| LOGIN_HTTP=$(echo "$LOGIN_RESPONSE" | tail -n1) | ||||||||||||||||||||||||||||||
| LOGIN_BODY=$(echo "$LOGIN_RESPONSE" | head -n-1) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if [ "$LOGIN_HTTP" -ne 200 ]; then | ||||||||||||||||||||||||||||||
| echo "Error: Docker Hub login failed with HTTP $LOGIN_HTTP" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| TOKEN=$(echo "$LOGIN_BODY" | jq -r .token) | ||||||||||||||||||||||||||||||
| if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then | ||||||||||||||||||||||||||||||
| echo "Error: Docker Hub login succeeded but returned no token" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \ | ||||||||||||||||||||||||||||||
| -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||||||||||||||||
| "https://hub.docker.com/v2/namespaces/${{ env.DOCKER_NAMESPACE }}/repositories/${{ env.DOCKER_IMAGE_NAME }}/tags/$VERSION") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if [ "$RESPONSE" -eq 200 ]; then | ||||||||||||||||||||||||||||||
| echo "Error: Tag $VERSION already exists on Docker Hub" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| elif [ "$RESPONSE" -eq 404 ]; then | ||||||||||||||||||||||||||||||
| echo "Tag $VERSION not found on Docker Hub — safe to push" | ||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||
| echo "Error: Unexpected HTTP $RESPONSE from Docker Hub tag check; aborting to fail safe" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - name: Extract metadata | ||||||||||||||||||||||||||||||
| id: meta | ||||||||||||||||||||||||||||||
| uses: docker/metadata-action@v5 | ||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||
| images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }} | ||||||||||||||||||||||||||||||
| tags: | | ||||||||||||||||||||||||||||||
| type=raw,value=${{ steps.get-version.outputs.version }} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - name: Build and push Docker image | ||||||||||||||||||||||||||||||
| id: build | ||||||||||||||||||||||||||||||
| uses: docker/build-push-action@v5 | ||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||
| context: . | ||||||||||||||||||||||||||||||
| file: ./Dockerfile | ||||||||||||||||||||||||||||||
| push: true | ||||||||||||||||||||||||||||||
| tags: ${{ steps.meta.outputs.tags }} | ||||||||||||||||||||||||||||||
| labels: ${{ steps.meta.outputs.labels }} | ||||||||||||||||||||||||||||||
| platforms: linux/amd64,linux/arm64 | ||||||||||||||||||||||||||||||
| cache-from: type=gha | ||||||||||||||||||||||||||||||
| cache-to: type=gha,mode=max | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - name: Create and push Git tag | ||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - name: Job summary | ||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||
| echo "### Docker Image Published 🚀" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||
| echo "**Tag:** ${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||
| echo "**Digest:** ${{ steps.build.outputs.digest }}" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,154 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Promote RC to Production Release | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| rc_tag: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Approved RC tag (example: 3.4.0-rc.2)" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| DOCKER_IMAGE_NAME: elevate-user | ||||||||||||||||||||||||||||||||||||||||||||||||||
| DOCKER_REGISTRY: docker.io | ||||||||||||||||||||||||||||||||||||||||||||||||||
| DOCKER_NAMESPACE: shikshalokamqa | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| concurrency: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| group: promote | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cancel-in-progress: false | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| promote: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Validate RC tag | ||||||||||||||||||||||||||||||||||||||||||||||||||
| id: version | ||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| RC_TAG: ${{ github.event.inputs.rc_tag }} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! [[ "$RC_TAG" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-rc\.[0-9]+$ ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Invalid rc_tag format. Must look like 3.4.0-rc.2" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
| RELEASE_TAG=$(echo "$RC_TAG" | sed 's/-rc\.[0-9]*//') | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "rc=$RC_TAG" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "release=$RELEASE_TAG" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Checkout repository | ||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fetch-depth: 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - 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: Login to Docker Hub | ||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: docker/login-action@v3 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check RC image exists and production tag is absent on Docker Hub | ||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| RC_TAG="${{ steps.version.outputs.rc }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| RELEASE_TAG="${{ steps.version.outputs.release }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # 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") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Pass secrets via environment variables to prevent shell injection and corruption. Injecting secrets directly into the Instead, map the secrets to the step's 🔒 Proposed fix - name: Check RC image exists and production tag is absent on Docker Hub
+ env:
+ DOCKER_USER: ${{ secrets.DOCKERHUB_USERNAME }}
+ DOCKER_PASS: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
RC_TAG="${{ steps.version.outputs.rc }}"
RELEASE_TAG="${{ steps.version.outputs.release }}"
# 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 }}" \
+ --arg username "$DOCKER_USER" \
+ --arg password "$DOCKER_PASS" \
'{"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")📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| LOGIN_HTTP=$(echo "$LOGIN_RESPONSE" | tail -n1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| LOGIN_BODY=$(echo "$LOGIN_RESPONSE" | head -n-1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "$LOGIN_HTTP" -ne 200 ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Docker Hub login failed with HTTP $LOGIN_HTTP" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| TOKEN=$(echo "$LOGIN_BODY" | jq -r .token) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Docker Hub login succeeded but returned no token" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Verify RC image exists | ||||||||||||||||||||||||||||||||||||||||||||||||||
| RC_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://hub.docker.com/v2/namespaces/${{ env.DOCKER_NAMESPACE }}/repositories/${{ env.DOCKER_IMAGE_NAME }}/tags/$RC_TAG") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "$RC_RESPONSE" -eq 404 ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: RC image $RC_TAG not found on Docker Hub" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ "$RC_RESPONSE" -ne 200 ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Unexpected HTTP $RC_RESPONSE checking RC image — aborting to fail safe" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "RC image $RC_TAG confirmed on Docker Hub." | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Verify production tag does not already exist | ||||||||||||||||||||||||||||||||||||||||||||||||||
| PROD_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://hub.docker.com/v2/namespaces/${{ env.DOCKER_NAMESPACE }}/repositories/${{ env.DOCKER_IMAGE_NAME }}/tags/$RELEASE_TAG") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "$PROD_RESPONSE" -eq 200 ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Production tag $RELEASE_TAG already exists on Docker Hub" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ "$PROD_RESPONSE" -ne 404 ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Error: Unexpected HTTP $PROD_RESPONSE checking production tag — aborting to fail safe" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Production tag $RELEASE_TAG is absent — safe to promote." | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # FIX 2: Replace pull/tag/push with imagetools create to preserve multi-arch manifest | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Set up Docker Buildx | ||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: docker/setup-buildx-action@v3 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - 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" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Job summary | ||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # FIX 3: Use imagetools inspect to get digest from remote (image not pulled locally) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| DIGEST=$(docker buildx imagetools inspect \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| --format '{{json .Manifest}}' \ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| | jq -r '.digest') | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "### Production Promotion Complete 🚀" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "**RC Image:** ${{ steps.version.outputs.rc }}" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "**Release Image:** ${{ steps.version.outputs.release }}" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "**Digest:** $DIGEST" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
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
Prevent code injection via template expansion.
Interpolating
github.event.inputs.tag_versiondirectly into the bash script can lead to command injection if the input contains malicious characters. Passing the input via an environment variable is the recommended secure pattern.🛡️ Proposed fix to pass input via environment variable
- name: Get Docker tag version id: get-version shell: bash + env: + TAG_VERSION: ${{ github.event.inputs.tag_version }} run: | - VERSION="${{ github.event.inputs.tag_version }}" + VERSION="$TAG_VERSION" # Strip leading "v" if present VERSION="${VERSION#v}"📝 Committable suggestion
🧰 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
Source: Linters/SAST tools