From 20ef7afad80b09cad53ae8a7fcf3e09dfe834268 Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Wed, 2 Sep 2026 19:22:23 +0530 Subject: [PATCH 1/7] ci(transpiler): push only to uniphi ECR The workflow fanned out to six registries, but five of them read org-level secrets (AWS_ACCOUNT_ID, GCR_PUSH_SA, SERVERLESS_BETA/PROD_AWS_ACCOUNT_ID, vars.GCR_PROJECT_ID) that are scoped to e6-query-engine and not shared with this repo. Unset secrets interpolate to the empty string, so the very first step built "arn:aws:iam:::role/GHActionsECRPush" and the run died at "Request ARN is invalid" before anything was built. Uniphi is the one registry this repo does hold a secret for (UNIPHI_AWS_ACCOUNT_ID), so make it the sole push target and drop the rest. Add an explicit precondition check so a missing account id fails with a readable message rather than an invalid-ARN error five steps later. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 103 +++++++--------------------- 1 file changed, 23 insertions(+), 80 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index d78b301f60..addc7eb51c 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -1,11 +1,13 @@ # Build and Release for Transpiler (sqlglot converter API) -# Builds the multi-arch transpiler image and pushes to all 6 registries. -# Mirrors the legacy Jenkinsfile build; registry/login/push pattern follows -# core-platform ci-operator.yml. No security scans (build + push only). +# Builds the multi-arch transpiler image and pushes it to the uniphi ECR. # -# BOOTSTRAP COPY: trigger is workflow_dispatch-only so merging this to main -# registers the workflow in the Actions tab WITHOUT firing a build/push. -# PR #258 replaces this with the full version (adds the push-to-main trigger). +# Uniphi is the only push target: it is the one registry this repo holds +# credentials for. The other five registries the Jenkinsfile fans out to +# (main ECR, GCR, ACR, serverless beta/prod) need org-level secrets that are +# scoped to e6-query-engine and not shared with this repo, so wiring them here +# only produced an empty account id and an "Request ARN is invalid" failure at +# the first assume-role. Add a registry back only once its secret is actually +# readable from e6data/sqlglot. name: Build - Transpiler on: @@ -19,18 +21,8 @@ env: RELEASE_NAME: transpiler # Preserve the legacy Jenkins versioning scheme: 3.0. VERSION: 3.0.${{ github.run_number }} - ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - ECR_REPOSITORY: transpiler UNIPHI_ECR_REGISTRY: ${{ secrets.UNIPHI_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com UNIPHI_ECR_REPOSITORY: transpiler - GCR_REGISTRY: us-docker.pkg.dev/${{ vars.GCR_PROJECT_ID }}/e6data - GCR_REPOSITORY: transpiler - ACR_REGISTRY: e6labs.azurecr.io - ACR_REPOSITORY: e6data/transpiler - SERVERLESS_BETA_ECR_REGISTRY: ${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - SERVERLESS_BETA_ECR_REPOSITORY: transpiler - SERVERLESS_PROD_ECR_REGISTRY: ${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - SERVERLESS_PROD_ECR_REPOSITORY: transpiler concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} @@ -48,6 +40,18 @@ jobs: - name: Setup RunsOn Magic Cache uses: runs-on/action@v2 + # An unset secret interpolates to the empty string, which yields the + # nonsense role arn "arn:aws:iam:::role/..." and a bare ".dkr.ecr..." + # registry. Fail here with a readable message instead of at assume-role. + - name: Check uniphi credentials are present + env: + UNIPHI_AWS_ACCOUNT_ID: ${{ secrets.UNIPHI_AWS_ACCOUNT_ID }} + run: | + if [ -z "${UNIPHI_AWS_ACCOUNT_ID:-}" ]; then + echo "::error::UNIPHI_AWS_ACCOUNT_ID is unset — cannot push the transpiler image." + exit 1 + fi + - name: Generate production tags id: tags run: | @@ -73,20 +77,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 - # ----- Registry authentication (one docker login per registry) ----- - # AWS creds are rotated per account; each ECR login is captured into the - # docker config before switching to the next account's credentials. - - - name: Setup AWS Creds (main) - uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GHActionsECRPush - aws-region: us-east-1 - - - name: Login to ECR (main) - run: | - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.ECR_REGISTRY }} - - name: Setup Uniphi AWS Creds uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 with: @@ -97,57 +87,16 @@ jobs: run: | aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} - - name: Setup Serverless Beta AWS Creds - uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 - with: - role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}:role/GHActionsECRPush - aws-region: us-east-1 - - - name: Login to Serverless Beta ECR - run: | - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_BETA_ECR_REGISTRY }} - - - name: Setup Serverless Prod AWS Creds - uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 - with: - role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}:role/GHActionsECRPush - aws-region: us-east-1 - - - name: Login to Serverless Prod ECR - run: | - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_PROD_ECR_REGISTRY }} - - - name: Authenticate to GCP - uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2 - with: - credentials_json: ${{ secrets.GCR_PUSH_SA }} - - - name: Login to GCR - run: | - echo '${{ secrets.GCR_PUSH_SA }}' | docker login -u _json_key --password-stdin us-docker.pkg.dev - - - name: Login to ACR - run: | - echo ${{ secrets.ACR_TOKEN }} | docker login --username e6labs --password-stdin e6labs.azurecr.io - echo "Successfully authenticated to Azure ACR (e6labs)" - - name: Build and push multi-arch image id: build-push run: | - # Build and push multi-arch image with all tags to all 6 registries - # (ECR, Uniphi ECR, GCR, ACR, Serverless Beta, Serverless Prod) IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" TAG_ARGS="" for tag in "${TAG_ARRAY[@]}"; do - TAG_ARGS="$TAG_ARGS -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:$tag" TAG_ARGS="$TAG_ARGS -t ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}:$tag" - TAG_ARGS="$TAG_ARGS -t ${{ env.GCR_REGISTRY }}/${{ env.GCR_REPOSITORY }}:$tag" - TAG_ARGS="$TAG_ARGS -t ${{ env.ACR_REGISTRY }}/${{ env.ACR_REPOSITORY }}:$tag" - TAG_ARGS="$TAG_ARGS -t ${{ env.SERVERLESS_BETA_ECR_REGISTRY }}/${{ env.SERVERLESS_BETA_ECR_REPOSITORY }}:$tag" - TAG_ARGS="$TAG_ARGS -t ${{ env.SERVERLESS_PROD_ECR_REGISTRY }}/${{ env.SERVERLESS_PROD_ECR_REPOSITORY }}:$tag" done - echo "Building and pushing image with ${#TAG_ARRAY[@]} tags to 6 registries (ECR, Uniphi ECR, GCR, ACR, Serverless Beta, Serverless Prod)" + echo "Building and pushing image with ${#TAG_ARRAY[@]} tags to Uniphi ECR" docker buildx build \ --platform linux/amd64,linux/arm64 \ @@ -159,8 +108,7 @@ jobs: --push \ -f Dockerfile . - # Get digest (same across all registries) - DIGEST=$(docker buildx imagetools inspect ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest --format '{{json .}}' | jq -r '.manifest.digest') + DIGEST=$(docker buildx imagetools inspect ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}:latest --format '{{json .}}' | jq -r '.manifest.digest') echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Summary @@ -171,10 +119,5 @@ jobs: echo "Digest: ${{ steps.build-push.outputs.digest }}" echo "" echo "Pushed tags: ${{ steps.tags.outputs.tags }}" - echo "Registries:" - echo " - ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}" + echo "Registry:" echo " - ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" - echo " - ${{ env.GCR_REGISTRY }}/${{ env.GCR_REPOSITORY }}" - echo " - ${{ env.ACR_REGISTRY }}/${{ env.ACR_REPOSITORY }}" - echo " - ${{ env.SERVERLESS_BETA_ECR_REGISTRY }}/${{ env.SERVERLESS_BETA_ECR_REPOSITORY }}" - echo " - ${{ env.SERVERLESS_PROD_ECR_REGISTRY }}/${{ env.SERVERLESS_PROD_ECR_REPOSITORY }}" From 7dcc03982248b2207d7d100f74512f4ec3b6d320 Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Wed, 2 Sep 2026 23:26:18 +0530 Subject: [PATCH 2/7] ci(transpiler): build each arch natively instead of under QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-runner `--platform linux/amd64,linux/arm64` build emulated amd64 via QEMU on an arm64 runner, and the image build compiles the sqlglotrs Rust extension. cargo aborts under emulation on Alpine — musl's Scudo allocator trips "CHECK failed @ primary32.h:183" and qemu takes signal 6 — so every run died in `maturin build` regardless of which registry it targeted. Restore the topology the Jenkinsfile used: build each architecture on a runner of that architecture, push by digest, and merge the two into one manifest with `imagetools create`. Tags are attached only at the merge, so a half-finished matrix can no longer move `latest`. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 117 ++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index addc7eb51c..b887d71af6 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -3,11 +3,19 @@ # # Uniphi is the only push target: it is the one registry this repo holds # credentials for. The other five registries the Jenkinsfile fans out to -# (main ECR, GCR, ACR, serverless beta/prod) need org-level secrets that are +# (main ECR, GCR, ACR, serverless beta/prod) read org-level secrets that are # scoped to e6-query-engine and not shared with this repo, so wiring them here -# only produced an empty account id and an "Request ARN is invalid" failure at -# the first assume-role. Add a registry back only once its secret is actually -# readable from e6data/sqlglot. +# only produced an empty account id and "Request ARN is invalid" at the first +# assume-role. Add a registry back only once its secret is readable from +# e6data/sqlglot. +# +# Each architecture is built on a runner of that architecture and the two are +# merged into one manifest afterwards, exactly as the Jenkinsfile did. Do NOT +# collapse this into a single `--platform linux/amd64,linux/arm64` build: the +# sqlglotrs Rust extension is compiled during the image build, and cargo dies +# under QEMU on Alpine ("Scudo ERROR: CHECK failed @ primary32.h:183" -> +# "qemu: uncaught target signal 6") because musl's hardened allocator does not +# survive user-mode emulation. name: Build - Transpiler on: @@ -29,8 +37,18 @@ concurrency: cancel-in-progress: false jobs: - release: - runs-on: runs-on=${{ github.run_id }}/runner=8cpu-linux-arm64/extras=s3-cache + build: + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + platform: linux/amd64 + runner: 8cpu-linux-x64 + - arch: arm64 + platform: linux/arm64 + runner: 8cpu-linux-arm64 + runs-on: runs-on=${{ github.run_id }}/runner=${{ matrix.runner }}/extras=s3-cache steps: - name: Checkout code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -52,6 +70,54 @@ jobs: exit 1 fi + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 + + - name: Setup Uniphi AWS Creds + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.UNIPHI_AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + + - name: Login to Uniphi ECR + run: | + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} + + # Push by digest only; the merge job attaches the human-readable tags to + # the combined manifest so a half-finished matrix never moves `latest`. + - name: Build and push ${{ matrix.arch }} by digest + id: build + run: | + set -euo pipefail + IMAGE="${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" + + docker buildx build \ + --platform ${{ matrix.platform }} \ + --cache-from type=gha,scope=${{ matrix.arch }} \ + --cache-to type=gha,mode=max,scope=${{ matrix.arch }} \ + --provenance=mode=max \ + --sbom=true \ + --output type=image,name=$IMAGE,push-by-digest=true,push=true \ + --metadata-file meta.json \ + -f Dockerfile . + + DIGEST=$(jq -r '."containerimage.digest"' meta.json) + echo "Built ${{ matrix.arch }} -> $DIGEST" + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: digest-${{ matrix.arch }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: build + runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-arm64/extras=s3-cache + steps: - name: Generate production tags id: tags run: | @@ -71,8 +137,12 @@ jobs: echo "tags=$TAGS" >> $GITHUB_OUTPUT echo "Production tags: $TAGS" - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + - name: Download digests + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + path: /tmp/digests + pattern: digest-* + merge-multiple: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 @@ -87,28 +157,27 @@ jobs: run: | aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} - - name: Build and push multi-arch image - id: build-push + - name: Create multi-arch manifest + id: manifest run: | - IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" + set -euo pipefail + IMAGE="${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" + TAG_ARGS="" + IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" for tag in "${TAG_ARRAY[@]}"; do - TAG_ARGS="$TAG_ARGS -t ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}:$tag" + TAG_ARGS="$TAG_ARGS -t $IMAGE:$tag" done - echo "Building and pushing image with ${#TAG_ARRAY[@]} tags to Uniphi ECR" + SRC_ARGS="" + for d in /tmp/digests/*; do + SRC_ARGS="$SRC_ARGS $IMAGE@sha256:$(basename "$d")" + done + echo "Merging:$SRC_ARGS" - docker buildx build \ - --platform linux/amd64,linux/arm64 \ - --cache-from type=gha \ - --cache-to type=gha,mode=max \ - --provenance=mode=max \ - --sbom=true \ - $TAG_ARGS \ - --push \ - -f Dockerfile . + docker buildx imagetools create $TAG_ARGS $SRC_ARGS - DIGEST=$(docker buildx imagetools inspect ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}:latest --format '{{json .}}' | jq -r '.manifest.digest') + DIGEST=$(docker buildx imagetools inspect "$IMAGE:latest" --format '{{json .}}' | jq -r '.manifest.digest') echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Summary @@ -116,7 +185,7 @@ jobs: echo "Release Complete!" echo "Version: ${{ env.VERSION }}" echo "Git SHA: ${{ steps.tags.outputs.short_sha }}" - echo "Digest: ${{ steps.build-push.outputs.digest }}" + echo "Digest: ${{ steps.manifest.outputs.digest }}" echo "" echo "Pushed tags: ${{ steps.tags.outputs.tags }}" echo "Registry:" From beb34e17f7009133dc38538c829f4a23e3ac0446 Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Wed, 2 Sep 2026 23:33:13 +0530 Subject: [PATCH 3/7] ci(transpiler): hand digests to the merge job via staging tags The merge job could not read the per-arch digests: RunsOn points ACTIONS_RESULTS_URL at its own cache proxy, which does not implement the artifacts v4 ListArtifacts API and answers with HTML, so download-artifact failed all five attempts on "Unexpected token 'O' ... is not valid JSON". Push each arch to a stage-- tag and have the merge read those two refs instead, the same way native-executor-image.yml stages per-arch tags. No artifact round-trip, and the staging tags fall to the ECR lifecycle. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 38 ++++++++--------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index b887d71af6..21f409bab9 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -83,10 +83,12 @@ jobs: run: | aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} - # Push by digest only; the merge job attaches the human-readable tags to - # the combined manifest so a half-finished matrix never moves `latest`. - - name: Build and push ${{ matrix.arch }} by digest - id: build + # Push a per-arch staging tag rather than uploading digests as artifacts: + # RunsOn points ACTIONS_RESULTS_URL at its own cache proxy, which does not + # implement the artifacts v4 ListArtifacts API (it answers with HTML, so + # download-artifact dies on "not valid JSON"). The merge job reads these + # two tags instead. They are staging only — reaped by the ECR lifecycle. + - name: Build and push ${{ matrix.arch }} run: | set -euo pipefail IMAGE="${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" @@ -97,23 +99,10 @@ jobs: --cache-to type=gha,mode=max,scope=${{ matrix.arch }} \ --provenance=mode=max \ --sbom=true \ - --output type=image,name=$IMAGE,push-by-digest=true,push=true \ - --metadata-file meta.json \ + -t "$IMAGE:stage-${{ github.run_id }}-${{ matrix.arch }}" \ + --push \ -f Dockerfile . - DIGEST=$(jq -r '."containerimage.digest"' meta.json) - echo "Built ${{ matrix.arch }} -> $DIGEST" - mkdir -p /tmp/digests - touch "/tmp/digests/${DIGEST#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: digest-${{ matrix.arch }} - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - merge: needs: build runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-arm64/extras=s3-cache @@ -137,13 +126,6 @@ jobs: echo "tags=$TAGS" >> $GITHUB_OUTPUT echo "Production tags: $TAGS" - - name: Download digests - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - path: /tmp/digests - pattern: digest-* - merge-multiple: true - - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 @@ -170,8 +152,8 @@ jobs: done SRC_ARGS="" - for d in /tmp/digests/*; do - SRC_ARGS="$SRC_ARGS $IMAGE@sha256:$(basename "$d")" + for arch in amd64 arm64; do + SRC_ARGS="$SRC_ARGS $IMAGE:stage-${{ github.run_id }}-$arch" done echo "Merging:$SRC_ARGS" From dff218c1d17cfe4ec88334c405d1711b2e22f76a Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Thu, 3 Sep 2026 00:44:48 +0530 Subject: [PATCH 4/7] ci(transpiler): restore all six registries, guarded per registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit cut the fan-out down to uniphi. Restore main ECR, GCR, ACR and the serverless beta/prod ECRs alongside it, but guard each on its own secret instead of assuming all six are configured. Five of the six read org-level secrets scoped to e6-query-engine and not shared with this repo, and an unset secret interpolates to "" — which is what built "arn:aws:iam:::role/GHActionsECRPush" and produced the original "Request ARN is invalid". A registry whose secret is absent is now skipped, and the Summary prints what was pushed and what was skipped so a green run never implies an image is somewhere it is not. Sharing the org secrets with e6data/sqlglot enables the rest with no change here. Today that means uniphi and ACR; the build still fails outright if no registry at all is configured. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 232 ++++++++++++++++++++++------ 1 file changed, 187 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index 21f409bab9..da4715f88f 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -1,13 +1,17 @@ # Build and Release for Transpiler (sqlglot converter API) -# Builds the multi-arch transpiler image and pushes it to the uniphi ECR. +# Builds the multi-arch transpiler image and pushes it to every registry this +# repo holds credentials for: main ECR, uniphi ECR, GCR, ACR and the serverless +# beta/prod ECRs. # -# Uniphi is the only push target: it is the one registry this repo holds -# credentials for. The other five registries the Jenkinsfile fans out to -# (main ECR, GCR, ACR, serverless beta/prod) read org-level secrets that are -# scoped to e6-query-engine and not shared with this repo, so wiring them here -# only produced an empty account id and "Request ARN is invalid" at the first -# assume-role. Add a registry back only once its secret is readable from -# e6data/sqlglot. +# Each registry is guarded on its own secret. Five of the six read org-level +# secrets that are scoped to e6-query-engine and, as of this writing, are NOT +# shared with e6data/sqlglot — only ACR_TOKEN and UNIPHI_AWS_ACCOUNT_ID are set +# here. An unset secret interpolates to the empty string, which silently builds +# the nonsense role arn "arn:aws:iam:::role/..." and fails with "Request ARN is +# invalid", so a registry whose secret is missing is SKIPPED rather than fatal. +# The Summary step prints exactly what was pushed and what was skipped; share +# the org secrets with this repo and the remaining registries light up with no +# change here. # # Each architecture is built on a runner of that architecture and the two are # merged into one manifest afterwards, exactly as the Jenkinsfile did. Do NOT @@ -29,8 +33,30 @@ env: RELEASE_NAME: transpiler # Preserve the legacy Jenkins versioning scheme: 3.0. VERSION: 3.0.${{ github.run_number }} + + # Account ids double as the "is this registry configured?" flag; the guards + # below test these rather than the secrets context, which is not available + # to a step-level `if:`. + AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} + UNIPHI_AWS_ACCOUNT_ID: ${{ secrets.UNIPHI_AWS_ACCOUNT_ID }} + SERVERLESS_BETA_AWS_ACCOUNT_ID: ${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }} + SERVERLESS_PROD_AWS_ACCOUNT_ID: ${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }} + GCR_PROJECT_ID: ${{ vars.GCR_PROJECT_ID }} + # Non-empty when set, so it guards exactly like the account ids above. + ACR_ENABLED: ${{ secrets.ACR_TOKEN != '' && 'yes' || '' }} + + ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + ECR_REPOSITORY: transpiler UNIPHI_ECR_REGISTRY: ${{ secrets.UNIPHI_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com UNIPHI_ECR_REPOSITORY: transpiler + GCR_REGISTRY: us-docker.pkg.dev/${{ vars.GCR_PROJECT_ID }}/e6data + GCR_REPOSITORY: transpiler + ACR_REGISTRY: e6labs.azurecr.io + ACR_REPOSITORY: e6data/transpiler + SERVERLESS_BETA_ECR_REGISTRY: ${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + SERVERLESS_BETA_ECR_REPOSITORY: transpiler + SERVERLESS_PROD_ECR_REGISTRY: ${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + SERVERLESS_PROD_ECR_REPOSITORY: transpiler concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} @@ -58,40 +84,88 @@ jobs: - name: Setup RunsOn Magic Cache uses: runs-on/action@v2 - # An unset secret interpolates to the empty string, which yields the - # nonsense role arn "arn:aws:iam:::role/..." and a bare ".dkr.ecr..." - # registry. Fail here with a readable message instead of at assume-role. - - name: Check uniphi credentials are present - env: - UNIPHI_AWS_ACCOUNT_ID: ${{ secrets.UNIPHI_AWS_ACCOUNT_ID }} - run: | - if [ -z "${UNIPHI_AWS_ACCOUNT_ID:-}" ]; then - echo "::error::UNIPHI_AWS_ACCOUNT_ID is unset — cannot push the transpiler image." - exit 1 - fi - - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 + # ----- Registry authentication (one docker login per registry) ----- + # AWS creds are rotated per account; each ECR login is captured into the + # docker config before switching to the next account's credentials. + - name: Setup AWS Creds (main) + if: env.AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to ECR (main) + if: env.AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.ECR_REGISTRY }} + - name: Setup Uniphi AWS Creds + if: env.UNIPHI_AWS_ACCOUNT_ID != '' uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 with: role-to-assume: arn:aws:iam::${{ secrets.UNIPHI_AWS_ACCOUNT_ID }}:role/GHActionsECRPush aws-region: us-east-1 - - name: Login to Uniphi ECR - run: | - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} + if: env.UNIPHI_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} + + - name: Setup Serverless Beta AWS Creds + if: env.SERVERLESS_BETA_AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to Serverless Beta ECR + if: env.SERVERLESS_BETA_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_BETA_ECR_REGISTRY }} + + - name: Setup Serverless Prod AWS Creds + if: env.SERVERLESS_PROD_AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to Serverless Prod ECR + if: env.SERVERLESS_PROD_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_PROD_ECR_REGISTRY }} + + - name: Authenticate to GCP + if: env.GCR_PROJECT_ID != '' + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2 + with: + credentials_json: ${{ secrets.GCR_PUSH_SA }} + - name: Login to GCR + if: env.GCR_PROJECT_ID != '' + run: echo '${{ secrets.GCR_PUSH_SA }}' | docker login -u _json_key --password-stdin us-docker.pkg.dev + + - name: Login to ACR + if: env.ACR_ENABLED != '' + run: echo ${{ secrets.ACR_TOKEN }} | docker login --username e6labs --password-stdin e6labs.azurecr.io # Push a per-arch staging tag rather than uploading digests as artifacts: # RunsOn points ACTIONS_RESULTS_URL at its own cache proxy, which does not # implement the artifacts v4 ListArtifacts API (it answers with HTML, so # download-artifact dies on "not valid JSON"). The merge job reads these - # two tags instead. They are staging only — reaped by the ECR lifecycle. + # tags instead. They are staging only — reaped by the ECR lifecycle. - name: Build and push ${{ matrix.arch }} run: | set -euo pipefail - IMAGE="${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" + STAGE="stage-${{ github.run_id }}-${{ matrix.arch }}" + + TAG_ARGS="" + add() { if [ -n "$1" ]; then TAG_ARGS="$TAG_ARGS -t $2:$STAGE"; echo " will push $2:$STAGE"; fi; } + add "${AWS_ACCOUNT_ID:-}" "${ECR_REGISTRY}/${ECR_REPOSITORY}" + add "${UNIPHI_AWS_ACCOUNT_ID:-}" "${UNIPHI_ECR_REGISTRY}/${UNIPHI_ECR_REPOSITORY}" + add "${GCR_PROJECT_ID:-}" "${GCR_REGISTRY}/${GCR_REPOSITORY}" + add "${ACR_ENABLED:-}" "${ACR_REGISTRY}/${ACR_REPOSITORY}" + add "${SERVERLESS_BETA_AWS_ACCOUNT_ID:-}" "${SERVERLESS_BETA_ECR_REGISTRY}/${SERVERLESS_BETA_ECR_REPOSITORY}" + add "${SERVERLESS_PROD_AWS_ACCOUNT_ID:-}" "${SERVERLESS_PROD_ECR_REGISTRY}/${SERVERLESS_PROD_ECR_REPOSITORY}" + + if [ -z "$TAG_ARGS" ]; then + echo "::error::No registry credentials are configured — nothing to push." + exit 1 + fi docker buildx build \ --platform ${{ matrix.platform }} \ @@ -99,7 +173,7 @@ jobs: --cache-to type=gha,mode=max,scope=${{ matrix.arch }} \ --provenance=mode=max \ --sbom=true \ - -t "$IMAGE:stage-${{ github.run_id }}-${{ matrix.arch }}" \ + $TAG_ARGS \ --push \ -f Dockerfile . @@ -129,38 +203,96 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 + - name: Setup AWS Creds (main) + if: env.AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to ECR (main) + if: env.AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.ECR_REGISTRY }} + - name: Setup Uniphi AWS Creds + if: env.UNIPHI_AWS_ACCOUNT_ID != '' uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 with: role-to-assume: arn:aws:iam::${{ secrets.UNIPHI_AWS_ACCOUNT_ID }}:role/GHActionsECRPush aws-region: us-east-1 - - name: Login to Uniphi ECR - run: | - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} + if: env.UNIPHI_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.UNIPHI_ECR_REGISTRY }} + + - name: Setup Serverless Beta AWS Creds + if: env.SERVERLESS_BETA_AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to Serverless Beta ECR + if: env.SERVERLESS_BETA_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_BETA_ECR_REGISTRY }} + + - name: Setup Serverless Prod AWS Creds + if: env.SERVERLESS_PROD_AWS_ACCOUNT_ID != '' + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}:role/GHActionsECRPush + aws-region: us-east-1 + - name: Login to Serverless Prod ECR + if: env.SERVERLESS_PROD_AWS_ACCOUNT_ID != '' + run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ env.SERVERLESS_PROD_ECR_REGISTRY }} - - name: Create multi-arch manifest + - name: Authenticate to GCP + if: env.GCR_PROJECT_ID != '' + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2 + with: + credentials_json: ${{ secrets.GCR_PUSH_SA }} + - name: Login to GCR + if: env.GCR_PROJECT_ID != '' + run: echo '${{ secrets.GCR_PUSH_SA }}' | docker login -u _json_key --password-stdin us-docker.pkg.dev + + - name: Login to ACR + if: env.ACR_ENABLED != '' + run: echo ${{ secrets.ACR_TOKEN }} | docker login --username e6labs --password-stdin e6labs.azurecr.io + + # Tags are attached only here, so a half-finished matrix never moves + # `latest` in any registry. + - name: Create multi-arch manifests id: manifest run: | set -euo pipefail - IMAGE="${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" + RUN_ID="${{ github.run_id }}" - TAG_ARGS="" - IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" - for tag in "${TAG_ARRAY[@]}"; do - TAG_ARGS="$TAG_ARGS -t $IMAGE:$tag" - done + TARGETS="" + add() { if [ -n "$1" ]; then TARGETS="$TARGETS $2"; fi; } + add "${AWS_ACCOUNT_ID:-}" "${ECR_REGISTRY}/${ECR_REPOSITORY}" + add "${UNIPHI_AWS_ACCOUNT_ID:-}" "${UNIPHI_ECR_REGISTRY}/${UNIPHI_ECR_REPOSITORY}" + add "${GCR_PROJECT_ID:-}" "${GCR_REGISTRY}/${GCR_REPOSITORY}" + add "${ACR_ENABLED:-}" "${ACR_REGISTRY}/${ACR_REPOSITORY}" + add "${SERVERLESS_BETA_AWS_ACCOUNT_ID:-}" "${SERVERLESS_BETA_ECR_REGISTRY}/${SERVERLESS_BETA_ECR_REPOSITORY}" + add "${SERVERLESS_PROD_AWS_ACCOUNT_ID:-}" "${SERVERLESS_PROD_ECR_REGISTRY}/${SERVERLESS_PROD_ECR_REPOSITORY}" - SRC_ARGS="" - for arch in amd64 arm64; do - SRC_ARGS="$SRC_ARGS $IMAGE:stage-${{ github.run_id }}-$arch" + IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" + PUSHED="" + for image in $TARGETS; do + TAG_ARGS="" + for tag in "${TAG_ARRAY[@]}"; do + TAG_ARGS="$TAG_ARGS -t $image:$tag" + done + echo "::group::Merging $image" + docker buildx imagetools create $TAG_ARGS \ + "$image:stage-$RUN_ID-amd64" \ + "$image:stage-$RUN_ID-arm64" + echo "::endgroup::" + PUSHED="$PUSHED $image" done - echo "Merging:$SRC_ARGS" - docker buildx imagetools create $TAG_ARGS $SRC_ARGS - - DIGEST=$(docker buildx imagetools inspect "$IMAGE:latest" --format '{{json .}}' | jq -r '.manifest.digest') + # Same content everywhere, so any target's manifest digest will do. + FIRST=$(echo $TARGETS | awk '{print $1}') + DIGEST=$(docker buildx imagetools inspect "$FIRST:latest" --format '{{json .}}' | jq -r '.manifest.digest') echo "digest=$DIGEST" >> $GITHUB_OUTPUT + echo "pushed=$PUSHED" >> $GITHUB_OUTPUT - name: Summary run: | @@ -170,5 +302,15 @@ jobs: echo "Digest: ${{ steps.manifest.outputs.digest }}" echo "" echo "Pushed tags: ${{ steps.tags.outputs.tags }}" - echo "Registry:" - echo " - ${{ env.UNIPHI_ECR_REGISTRY }}/${{ env.UNIPHI_ECR_REPOSITORY }}" + echo "" + echo "Pushed to:" + for r in ${{ steps.manifest.outputs.pushed }}; do echo " ✓ $r"; done + echo "" + echo "Skipped (secret not available to this repo):" + [ -z "${AWS_ACCOUNT_ID:-}" ] && echo " ✗ main ECR (AWS_ACCOUNT_ID)" || true + [ -z "${UNIPHI_AWS_ACCOUNT_ID:-}" ] && echo " ✗ uniphi ECR (UNIPHI_AWS_ACCOUNT_ID)" || true + [ -z "${GCR_PROJECT_ID:-}" ] && echo " ✗ GCR (vars.GCR_PROJECT_ID/GCR_PUSH_SA)" || true + [ -z "${ACR_ENABLED:-}" ] && echo " ✗ ACR (ACR_TOKEN)" || true + [ -z "${SERVERLESS_BETA_AWS_ACCOUNT_ID:-}" ] && echo " ✗ serverless beta ECR (SERVERLESS_BETA_AWS_ACCOUNT_ID)" || true + [ -z "${SERVERLESS_PROD_AWS_ACCOUNT_ID:-}" ] && echo " ✗ serverless prod ECR (SERVERLESS_PROD_AWS_ACCOUNT_ID)" || true + true From 732fa9905c6b6d33274727a8b7c76dabde8ac264 Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Thu, 3 Sep 2026 00:57:10 +0530 Subject: [PATCH 5/7] ci(transpiler): correct the ACR path and offset the version series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things the ACR push would have got wrong even with a working token. The registry has no e6data/transpiler repository. e6labs.azurecr.io/transpiler is the live one — 159 tags, last written 2026-09-02, and the path the Jenkinsfile pushes to — while e6-engine/transpiler has been stale since 2026-05. Point ACR_REPOSITORY at the real path. github.run_number restarts at 1 for this workflow, so VERSION would have been 3.0.6 while Jenkins is already at 3.0.195, publishing version numbers that run backwards into a registry that still holds 3.0.6-* and 3.0.7-* from the old series. Offset the run number past Jenkins; nothing in 3.0.10xx exists yet. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index da4715f88f..5dc249d8c5 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -31,8 +31,13 @@ permissions: env: RELEASE_NAME: transpiler - # Preserve the legacy Jenkins versioning scheme: 3.0. - VERSION: 3.0.${{ github.run_number }} + # Preserve the legacy Jenkins versioning scheme: 3.0.. github.run_number + # restarts at 1 for this workflow while Jenkins is already at 3.0.195, so it is + # offset past the Jenkins series — otherwise the first GHA runs would publish + # 3.0.6/3.0.7 on top of a registry that already holds 3.0.6-* from years ago and + # version numbers would appear to run backwards. Raise the 1000 below if Jenkins + # ever catches up to it. + VERSION: 3.0.${{ 1000 + github.run_number }} # Account ids double as the "is this registry configured?" flag; the guards # below test these rather than the secrets context, which is not available @@ -52,7 +57,10 @@ env: GCR_REGISTRY: us-docker.pkg.dev/${{ vars.GCR_PROJECT_ID }}/e6data GCR_REPOSITORY: transpiler ACR_REGISTRY: e6labs.azurecr.io - ACR_REPOSITORY: e6data/transpiler + # The live path: e6labs.azurecr.io/transpiler holds 159 tags and is what the + # Jenkinsfile pushes to (AZURE_IMAGE). e6data/transpiler does not exist, and + # e6-engine/transpiler has been stale since 2026-05. + ACR_REPOSITORY: transpiler SERVERLESS_BETA_ECR_REGISTRY: ${{ secrets.SERVERLESS_BETA_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com SERVERLESS_BETA_ECR_REPOSITORY: transpiler SERVERLESS_PROD_ECR_REGISTRY: ${{ secrets.SERVERLESS_PROD_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com From a5e7f4e98ec5e75bf0479d5624b54b5a2885d51c Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Thu, 3 Sep 2026 01:14:35 +0530 Subject: [PATCH 6/7] ci(transpiler): require uniphi and ACR, keep the other four optional uniphi and ACR are the registries the transpiler is actually pulled from, so treat a missing or empty credential for either as a hard error instead of letting the guards skip them silently. The remaining four stay optional and drop out on their own while their org secrets are unshared. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index 5dc249d8c5..27a3588d36 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -92,6 +92,23 @@ jobs: - name: Setup RunsOn Magic Cache uses: runs-on/action@v2 + # uniphi and ACR are required targets: these are the registries people + # actually pull the transpiler from, so a missing credential for either is + # a hard error rather than a silent skip. The other four are optional and + # drop out on their own when their org secret is not shared with this repo. + - name: Check required registry credentials + run: | + # `run:` executes under `bash -e`, so a bare `[ ... ] && x=y` would abort + # the step whenever the test is false. Use if-blocks. + missing="" + if [ -z "${UNIPHI_AWS_ACCOUNT_ID:-}" ]; then missing="$missing UNIPHI_AWS_ACCOUNT_ID"; fi + if [ -z "${ACR_ENABLED:-}" ]; then missing="$missing ACR_TOKEN"; fi + if [ -n "$missing" ]; then + echo "::error::Required registry credentials are missing or empty:$missing" + exit 1 + fi + echo "Required registries (uniphi, ACR) are configured." + - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 @@ -208,6 +225,23 @@ jobs: echo "tags=$TAGS" >> $GITHUB_OUTPUT echo "Production tags: $TAGS" + # uniphi and ACR are required targets: these are the registries people + # actually pull the transpiler from, so a missing credential for either is + # a hard error rather than a silent skip. The other four are optional and + # drop out on their own when their org secret is not shared with this repo. + - name: Check required registry credentials + run: | + # `run:` executes under `bash -e`, so a bare `[ ... ] && x=y` would abort + # the step whenever the test is false. Use if-blocks. + missing="" + if [ -z "${UNIPHI_AWS_ACCOUNT_ID:-}" ]; then missing="$missing UNIPHI_AWS_ACCOUNT_ID"; fi + if [ -z "${ACR_ENABLED:-}" ]; then missing="$missing ACR_TOKEN"; fi + if [ -n "$missing" ]; then + echo "::error::Required registry credentials are missing or empty:$missing" + exit 1 + fi + echo "Required registries (uniphi, ACR) are configured." + - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 From ef6bdb92b83c5e9692d974e80e21a372d122bcc1 Mon Sep 17 00:00:00 2001 From: Vaibhav C Poojari Date: Thu, 3 Sep 2026 10:53:15 +0530 Subject: [PATCH 7/7] ci(transpiler): compute the version offset in shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Actions expressions have no arithmetic operators, so `VERSION: 3.0.${{ 1000 + github.run_number }}` was not a value but a workflow parse error — every dispatch since it landed was rejected with "Unexpected symbol: '+'" before a run could even be created. Keep the offset as a plain env var and do the addition in the tags step, which is already shell. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JwhfyJPyZdVru8vXnyP25J --- .github/workflows/ci-transpiler.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-transpiler.yml b/.github/workflows/ci-transpiler.yml index 27a3588d36..89a4057221 100644 --- a/.github/workflows/ci-transpiler.yml +++ b/.github/workflows/ci-transpiler.yml @@ -37,7 +37,10 @@ env: # 3.0.6/3.0.7 on top of a registry that already holds 3.0.6-* from years ago and # version numbers would appear to run backwards. Raise the 1000 below if Jenkins # ever catches up to it. - VERSION: 3.0.${{ 1000 + github.run_number }} + # NOTE: the offset is applied in the tags step below, not here — GitHub + # Actions expressions have no arithmetic operators, so `${{ 1000 + ... }}` + # is a workflow parse error rather than a value. + VERSION_OFFSET: 1000 # Account ids double as the "is this registry configured?" flag; the guards # below test these rather than the secrets context, which is not available @@ -211,6 +214,9 @@ jobs: run: | SHORT_SHA="${GITHUB_SHA:0:7}" DATE_TAG=$(date +%Y.%m.%d) + VERSION="3.0.$(( VERSION_OFFSET + GITHUB_RUN_NUMBER ))" + + echo "version=$VERSION" >> $GITHUB_OUTPUT echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "date_tag=$DATE_TAG" >> $GITHUB_OUTPUT @@ -219,8 +225,8 @@ jobs: TAGS="latest" TAGS="$TAGS,sha-$SHORT_SHA" TAGS="$TAGS,$DATE_TAG-$SHORT_SHA" - TAGS="$TAGS,${{ env.VERSION }}" - TAGS="$TAGS,${{ env.VERSION }}-$SHORT_SHA" + TAGS="$TAGS,$VERSION" + TAGS="$TAGS,$VERSION-$SHORT_SHA" echo "tags=$TAGS" >> $GITHUB_OUTPUT echo "Production tags: $TAGS" @@ -339,7 +345,7 @@ jobs: - name: Summary run: | echo "Release Complete!" - echo "Version: ${{ env.VERSION }}" + echo "Version: ${{ steps.tags.outputs.version }}" echo "Git SHA: ${{ steps.tags.outputs.short_sha }}" echo "Digest: ${{ steps.manifest.outputs.digest }}" echo ""