From 32f2d53ba6fa8e4dd3a5fcd1f61c894c3fe2b844 Mon Sep 17 00:00:00 2001 From: Nevil Date: Wed, 6 Aug 2025 14:17:33 +0530 Subject: [PATCH 01/15] add: github action to publish docker image --- .github/workflows/docker-image.yml | 87 ++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 000000000..c996faef5 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,87 @@ +name: Build and Push Docker Image + +on: + workflow_dispatch: + inputs: + tag_version: + description: 'Docker image tag version (e.g., 3.3.11)' + required: true + +env: + DOCKER_IMAGE_NAME: elevate-user # Configure your image name here + DOCKER_REGISTRY: docker.io + DOCKER_NAMESPACE: shikshalokamqa + +jobs: + docker-image-build-and-push: + if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/docker-image' + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - 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 (fail if not provided) + id: get-version + run: | + # Use workflow_dispatch input if provided + if [ ! -z "${{ github.event.inputs.tag_version }}" ]; then + VERSION="${{ github.event.inputs.tag_version }}" + # Or use TAG_VERSION env if set (for push/PR) + elif [ ! -z "${TAG_VERSION}" ]; then + VERSION="${TAG_VERSION}" + else + echo "Error: Docker image version must be provided as workflow_dispatch input or TAG_VERSION env." + exit 1 + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Check if version exists on Docker Hub + id: check-version + run: | + VERSION=${{ steps.get-version.outputs.version }} + RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "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 + else + echo "Tag $VERSION does not exist, proceeding with build" + 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: Image digest + run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" + + - name: Print pushed tags + run: | + echo "Pushed tags:" + echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' \ No newline at end of file From 020da8d2a391878edff5f5d9b973d83cfc1a9f02 Mon Sep 17 00:00:00 2001 From: Nevil Date: Wed, 6 Aug 2025 14:55:51 +0530 Subject: [PATCH 02/15] Update docker-image.yml --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c996faef5..29002d710 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -14,7 +14,7 @@ env: jobs: docker-image-build-and-push: - if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/docker-image' + if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: @@ -84,4 +84,4 @@ jobs: - name: Print pushed tags run: | echo "Pushed tags:" - echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' \ No newline at end of file + echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' From 35df28ec8056ab5e09ff08b68a1f6371a684bfdc Mon Sep 17 00:00:00 2001 From: Nevil Date: Wed, 15 Oct 2025 11:52:04 +0530 Subject: [PATCH 03/15] chore(ci): allow Docker build from specified branch with default staging --- .github/workflows/docker-image.yml | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 29002d710..1b658cf39 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -6,20 +6,26 @@ on: 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 # Configure your image name here + DOCKER_IMAGE_NAME: elevate-user DOCKER_REGISTRY: docker.io DOCKER_NAMESPACE: shikshalokamqa jobs: docker-image-build-and-push: - if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' steps: - - name: Checkout code + - 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 @@ -30,17 +36,12 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get Docker tag version (fail if not provided) + - name: Get Docker tag version id: get-version run: | - # Use workflow_dispatch input if provided - if [ ! -z "${{ github.event.inputs.tag_version }}" ]; then - VERSION="${{ github.event.inputs.tag_version }}" - # Or use TAG_VERSION env if set (for push/PR) - elif [ ! -z "${TAG_VERSION}" ]; then - VERSION="${TAG_VERSION}" - else - echo "Error: Docker image version must be provided as workflow_dispatch input or TAG_VERSION env." + VERSION="${{ github.event.inputs.tag_version }}" + if [ -z "$VERSION" ]; then + echo "Error: tag_version input required" exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT @@ -53,8 +54,6 @@ jobs: if [ "$RESPONSE" -eq 200 ]; then echo "Error: Tag $VERSION already exists on Docker Hub" exit 1 - else - echo "Tag $VERSION does not exist, proceeding with build" fi - name: Extract metadata @@ -78,9 +77,6 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Image digest - run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" - - name: Print pushed tags run: | echo "Pushed tags:" From b8f494836eb69592a9ed792de76d910e059c32fa Mon Sep 17 00:00:00 2001 From: Nevil Date: Wed, 15 Oct 2025 13:10:52 +0530 Subject: [PATCH 04/15] fix: get-version safely --- .github/workflows/docker-image.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1b658cf39..bbdd29832 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -39,12 +39,16 @@ jobs: - name: Get Docker tag version id: get-version run: | - VERSION="${{ github.event.inputs.tag_version }}" + # Safely assign without evaluating input + echo "version=${{ github.event.inputs.tag_version }}" | tr -d '\r' > safe_output.txt + VERSION=$(cat safe_output.txt) if [ -z "$VERSION" ]; then echo "Error: tag_version input required" exit 1 fi - echo "version=$VERSION" >> $GITHUB_OUTPUT + # Write to GITHUB_OUTPUT securely + printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT" + - name: Check if version exists on Docker Hub id: check-version From bc9ff33ef83b1dd275802ede0fc5d511f3f7575b Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 31 Oct 2025 12:06:06 +0530 Subject: [PATCH 05/15] Simplify version retrieval in Docker workflow Refactor version assignment to directly use input. --- .github/workflows/docker-image.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index bbdd29832..8b60eb6c4 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -38,15 +38,20 @@ jobs: - name: Get Docker tag version id: get-version + shell: bash run: | - # Safely assign without evaluating input - echo "version=${{ github.event.inputs.tag_version }}" | tr -d '\r' > safe_output.txt - VERSION=$(cat safe_output.txt) + VERSION="${{ github.event.inputs.tag_version }}" if [ -z "$VERSION" ]; then echo "Error: tag_version input required" exit 1 fi - # Write to GITHUB_OUTPUT securely + # Strip leading "v" if present + VERSION="${VERSION#v}" + # Optional: enforce x.y.z + if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){2}$ ]]; then + echo "Error: tag_version must look like 3.3.23" + exit 1 + fi printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT" From d9066aabc884204188598c695ea0a4d3b216981b Mon Sep 17 00:00:00 2001 From: Nevil Date: Thu, 5 Mar 2026 19:33:35 +0530 Subject: [PATCH 06/15] udpated: github actions --- .github/workflows/docker-image.yml | 48 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 8b60eb6c4..1eeec4019 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -16,10 +16,13 @@ env: DOCKER_REGISTRY: docker.io DOCKER_NAMESPACE: shikshalokamqa +permissions: + contents: write + packages: write + jobs: docker-image-build-and-push: runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' steps: - name: Checkout code from target branch @@ -41,30 +44,46 @@ jobs: shell: bash run: | VERSION="${{ github.event.inputs.tag_version }}" - if [ -z "$VERSION" ]; then - echo "Error: tag_version input required" - exit 1 - fi # Strip leading "v" if present VERSION="${VERSION#v}" - # Optional: enforce x.y.z - if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){2}$ ]]; then - echo "Error: tag_version must look like 3.3.23" + # Enforce x.y.z or x.y.z-rc.1 format + if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9\.]+)?$ ]]; then + echo "Error: tag_version must look like 3.4.0 or 3.4.0-rc.1" 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 + echo "Error: Git tag $VERSION already exists" + exit 1 + fi - name: Check if version exists on Docker Hub - id: check-version run: | - VERSION=${{ steps.get-version.outputs.version }} - RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "https://hub.docker.com/v2/namespaces/${{ env.DOCKER_NAMESPACE }}/repositories/${{ env.DOCKER_IMAGE_NAME }}/tags/$VERSION") + VERSION="${{ steps.get-version.outputs.version }}" + TOKEN=$(curl -s \ + -u "${{ secrets.DOCKERHUB_USERNAME }}:${{ secrets.DOCKERHUB_TOKEN }}" \ + "https://hub.docker.com/v2/users/login" | jq -r .token) + 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 fi + - 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: Extract metadata id: meta uses: docker/metadata-action@v5 @@ -86,7 +105,8 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Print pushed tags + - name: Job summary run: | - echo "Pushed tags:" - echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' + 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 \ No newline at end of file From 68fcbfa186df16c4ffe48d447496e2579a555f4d Mon Sep 17 00:00:00 2001 From: Nevil Date: Thu, 5 Mar 2026 19:46:53 +0530 Subject: [PATCH 07/15] updated: github actions --- .github/workflows/docker-image.yml | 48 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1eeec4019..8d0c97982 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,4 +1,4 @@ -name: Build and Push Docker Image +name: Build, Tag, and Push Docker Image on: workflow_dispatch: @@ -18,7 +18,6 @@ env: permissions: contents: write - packages: write jobs: docker-image-build-and-push: @@ -47,8 +46,8 @@ jobs: # 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]+(-[A-Za-z0-9\.]+)?$ ]]; then - echo "Error: tag_version must look like 3.4.0 or 3.4.0-rc.1" + 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" @@ -65,25 +64,38 @@ jobs: - name: Check if version exists on Docker Hub run: | VERSION="${{ steps.get-version.outputs.version }}" - TOKEN=$(curl -s \ + + LOGIN_RESPONSE=$(curl -s -w "\n%{http_code}" \ -u "${{ secrets.DOCKERHUB_USERNAME }}:${{ secrets.DOCKERHUB_TOKEN }}" \ - "https://hub.docker.com/v2/users/login" | jq -r .token) + "https://hub.docker.com/v2/users/login") + 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: 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: Extract metadata id: meta uses: docker/metadata-action@v5 @@ -105,6 +117,14 @@ jobs: 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 From 200f719190f48d28ec684d29b3a3e392c4ac813a Mon Sep 17 00:00:00 2001 From: Nevil Date: Thu, 5 Mar 2026 19:58:21 +0530 Subject: [PATCH 08/15] updated: github actions --- .github/workflows/docker-image.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 8d0c97982..23cfb16ea 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -64,9 +64,11 @@ jobs: - name: Check if version exists on Docker Hub run: | VERSION="${{ steps.get-version.outputs.version }}" - + LOGIN_RESPONSE=$(curl -s -w "\n%{http_code}" \ - -u "${{ secrets.DOCKERHUB_USERNAME }}:${{ secrets.DOCKERHUB_TOKEN }}" \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"username": "${{ secrets.DOCKERHUB_USERNAME }}", "password": "${{ secrets.DOCKERHUB_TOKEN }}"}' \ "https://hub.docker.com/v2/users/login") LOGIN_HTTP=$(echo "$LOGIN_RESPONSE" | tail -n1) LOGIN_BODY=$(echo "$LOGIN_RESPONSE" | head -n-1) From b7bc66c54f192242c2250e9567df15b9400acc6c Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 12:37:45 +0530 Subject: [PATCH 09/15] added: prod release job --- .github/workflows/prod-release.yml | 146 +++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 .github/workflows/prod-release.yml diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml new file mode 100644 index 000000000..3d8db2815 --- /dev/null +++ b/.github/workflows/prod-release.yml @@ -0,0 +1,146 @@ +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 + +jobs: + promote: + runs-on: ubuntu-latest + + steps: + - name: Validate RC tag + id: version + run: | + RC_TAG="${{ github.event.inputs.rc_tag }}" + 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 + 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 + + - 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 }}" + + 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_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." + + - name: Pull RC image + run: | + docker pull \ + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.rc }} + + - name: Retag image for production + run: | + docker tag \ + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.rc }} \ + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} + + - name: Push production image + run: | + docker push \ + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} + + - 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" -m "Release $VERSION" + git push origin "$VERSION" + + - name: Job summary + run: | + DIGEST=$(docker inspect \ + --format='{{index .RepoDigests 0}}' \ + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }}) + 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 \ No newline at end of file From 7a6321dc749ce8a50f410f96c2850b55ec2468d1 Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 13:57:57 +0530 Subject: [PATCH 10/15] updated: prod relase action --- .github/workflows/prod-release.yml | 43 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 3d8db2815..6b548c147 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -61,11 +61,16 @@ jobs: RC_TAG="${{ steps.version.outputs.rc }}" RELEASE_TAG="${{ steps.version.outputs.release }}" - 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") + # 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") LOGIN_HTTP=$(echo "$LOGIN_RESPONSE" | tail -n1) LOGIN_BODY=$(echo "$LOGIN_RESPONSE" | head -n-1) @@ -106,21 +111,15 @@ jobs: fi echo "Production tag $RELEASE_TAG is absent — safe to promote." - - name: Pull RC image - run: | - docker pull \ - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.rc }} + # 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 image for production + - name: Retag RC image as production (multi-arch) run: | - docker tag \ - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.rc }} \ - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} - - - name: Push production image - run: | - docker push \ - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }} + 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: | @@ -137,9 +136,11 @@ jobs: - name: Job summary run: | - DIGEST=$(docker inspect \ - --format='{{index .RepoDigests 0}}' \ - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }}) + # 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 From 62032365eefa20e0063607bae4beb2463df71e54 Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 14:54:03 +0530 Subject: [PATCH 11/15] updated: release job --- .github/workflows/prod-release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 6b548c147..1b07957c3 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -22,8 +22,9 @@ jobs: steps: - name: Validate RC tag id: version + env: + RC_TAG: ${{ github.event.inputs.rc_tag }} run: | - RC_TAG="${{ github.event.inputs.rc_tag }}" 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 From f9422103be10f5033bb81dc0579605ad707323fb Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 15:11:12 +0530 Subject: [PATCH 12/15] update: release action --- .github/workflows/prod-release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 1b07957c3..c3ebfb0a0 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -15,6 +15,10 @@ env: permissions: contents: write +concurrency: + group: promote + cancel-in-progress: false + jobs: promote: runs-on: ubuntu-latest @@ -39,6 +43,7 @@ jobs: fetch-depth: 0 - name: Verify RC commit exists in main + id: verify run: | RC_TAG="${{ steps.version.outputs.rc }}" git fetch origin main --tags @@ -50,6 +55,7 @@ jobs: 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 @@ -132,7 +138,7 @@ jobs: echo "Error: Git tag $VERSION already exists" exit 1 fi - git tag -a "$VERSION" -m "Release $VERSION" + git tag -a "$VERSION" "${{ steps.verify.outputs.rc_commit }}" -m "Release $VERSION" git push origin "$VERSION" - name: Job summary From d143e13ae22ad1dabc167329838d864917d8c972 Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 15:18:58 +0530 Subject: [PATCH 13/15] fix: main to master --- .github/workflows/prod-release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index c3ebfb0a0..162e25511 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -42,17 +42,17 @@ jobs: with: fetch-depth: 0 - - name: Verify RC commit exists in main + - name: Verify RC commit exists in master id: verify run: | RC_TAG="${{ steps.version.outputs.rc }}" - git fetch origin main --tags + git fetch origin master --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." + if git merge-base --is-ancestor "$RC_COMMIT" origin/master; then + echo "RC commit is present in master — safe to promote." else - echo "Error: RC tag commit is NOT in main." - echo "Merge staging → main before promoting." + echo "Error: RC tag commit is NOT in master." + echo "Merge staging → master before promoting." exit 1 fi echo "rc_commit=$RC_COMMIT" >> $GITHUB_OUTPUT From 8a6187066f2cd7380522cb364162bc31b27442ee Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 6 Mar 2026 15:24:16 +0530 Subject: [PATCH 14/15] updated prod release --- .github/workflows/prod-release.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 162e25511..0fb3cde78 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -42,17 +42,17 @@ jobs: with: fetch-depth: 0 - - name: Verify RC commit exists in master + - name: Verify RC commit exists in main id: verify run: | RC_TAG="${{ steps.version.outputs.rc }}" - git fetch origin master --tags + git fetch origin main --tags RC_COMMIT=$(git rev-list -n 1 "$RC_TAG") - if git merge-base --is-ancestor "$RC_COMMIT" origin/master; then - echo "RC commit is present in master — safe to promote." + 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 master." - echo "Merge staging → master before promoting." + 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 @@ -143,12 +143,20 @@ jobs: - 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 \ No newline at end of file + { + echo "### Production Promotion Complete 🚀" + echo "" + echo "| Field | Value |" + echo "| --- | --- |" + echo "| **RC tag** | \`${{ steps.version.outputs.rc }}\` |" + echo "| **Release tag** | \`${{ steps.version.outputs.release }}\` |" + echo "| **Validated commit** | \`${{ steps.verify.outputs.rc_commit }}\` |" + echo "| **Docker image** | \`${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.release }}\` |" + echo "| **Digest** | \`$DIGEST\` |" + echo "| **Triggered by** | @${{ github.actor }} |" + echo "| **Run** | [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) |" + } >> $GITHUB_STEP_SUMMARY \ No newline at end of file From b8d8a3a09eae55ae1d4d3ad03c64e6887f5500cb Mon Sep 17 00:00:00 2001 From: Nevil Date: Fri, 17 Jul 2026 18:49:21 +0530 Subject: [PATCH 15/15] fix: issue with actions --- .github/workflows/prod-release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 0fb3cde78..913838020 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -42,17 +42,17 @@ jobs: with: fetch-depth: 0 - - name: Verify RC commit exists in main + - name: Verify RC commit exists in master id: verify run: | RC_TAG="${{ steps.version.outputs.rc }}" - git fetch origin main --tags + git fetch origin master --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." + if git merge-base --is-ancestor "$RC_COMMIT" origin/master; then + echo "RC commit is present in master — safe to promote." else - echo "Error: RC tag commit is NOT in main." - echo "Merge staging → main before promoting." + echo "Error: RC tag commit is NOT in master." + echo "Merge staging → master before promoting." exit 1 fi echo "rc_commit=$RC_COMMIT" >> $GITHUB_OUTPUT