From 8d2ca81445439f44f909032786bd411a3dcd017a Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Mon, 27 Jul 2026 13:32:11 -0500 Subject: [PATCH 1/4] ci: restore Buildkite CDN release pipeline with OIDC auth Un-reverts #1391 - architects are still evaluating whether GHA can deploy to S3 under a new proposal, so Buildkite is the safe path for now. Auth is redesigned from the original #1382 draft: that version fetched a Buildkite cluster secret (`analytics-next-cdn-release`) that was never actually provisioned, then did a plain `aws sts assume-role` relying on a Segment-fleet ambient identity (`SEGMENT_CONTEXTS`) that doesn't exist on Twilio's Buildkite agents. This version instead follows Twilio's documented OIDC pattern (buildkite-agent oidc request-token + sts assume-role-with-web-identity) - no ambient identity or cluster secret dependency. The upload role (arn:aws:iam::812113486725:role/ajs-private-assets-upload, owned by cdp-infra) and PROD_BUCKET are confirmed real values, recovered from the last successful legacy Buildkite build. The remaining env vars (STAGE_BUCKET, *_CDN_OAI, *_CUSTOM_DOMAIN_OAI, *_SHADOW) are placeholders pending a chamber read. Still blocked on: - cdp-infra adding a trust statement to ajs-private-assets-upload for Twilio Buildkite's OIDC issuer, scoped to this pipeline/branch - BUILDKITE_API_TOKEN repo secret (write_builds on cdp-analytics-next) - filling in the remaining chamber-sourced env vars above cdp-analytics-next's leftover GitHub webhook (build_branches/ build_pull_requests) has already been disabled directly via the Buildkite API, so this trigger step is now the only thing that can start a build there. --- .buildkite/pipeline.yml | 44 +++++++++++++++++++++++++++++++++++ .github/workflows/publish.yml | 10 ++++++++ 2 files changed, 54 insertions(+) create mode 100644 .buildkite/pipeline.yml diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 000000000..39eac6604 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,44 @@ +agents: + queue: general-039 + +env: + NODE_IMAGE: '018537234677.dkr.ecr.us-east-1.amazonaws.com/docker.io/library/node:20.19.4-bookworm' + AJS_PRIVATE_ASSETS_UPLOAD: 'arn:aws:iam::812113486725:role/ajs-private-assets-upload' + PROD_BUCKET: 'segment-ajs-renderer-compiled-production' + # TODO(cdp-infra trust-policy change pending): confirm/fill in before first real run. + STAGE_BUCKET: 'TODO' + PROD_CDN_OAI: 'TODO' + STAGE_CDN_OAI: 'TODO' + PROD_CUSTOM_DOMAIN_OAI: 'TODO' + STAGE_CUSTOM_DOMAIN_OAI: 'TODO' + PROD_SHADOW: 'TODO' + STAGE_SHADOW: 'TODO' + +steps: + - label: '[Browser] Release to CDN :rocket:' + if: build.branch == "master" + commands: | + webtoken_file="$(mktemp)" + buildkite-agent oidc request-token --audience sts.amazonaws.com > "$webtoken_file" + + CREDS=$(aws sts assume-role-with-web-identity \ + --role-arn "$AJS_PRIVATE_ASSETS_UPLOAD" \ + --role-session-name "buildkite-$BUILDKITE_PIPELINE_SLUG-$BUILDKITE_BUILD_NUMBER-cdn" \ + --web-identity-token "$(cat "$webtoken_file")" \ + --query 'Credentials' --output json) + rm -f "$webtoken_file" + + export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r .AccessKeyId) + export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r .SecretAccessKey) + export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r .SessionToken) + + docker run --rm \ + -v "$$PWD:/workdir" -w /workdir \ + -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN \ + -e PROD_BUCKET -e STAGE_BUCKET -e PROD_SHADOW -e STAGE_SHADOW \ + -e PROD_CDN_OAI -e STAGE_CDN_OAI -e PROD_CUSTOM_DOMAIN_OAI -e STAGE_CUSTOM_DOMAIN_OAI \ + ${NODE_IMAGE} \ + sh -c ' + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 yarn install --immutable + yarn run -T browser release:cdn + ' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f197f30f5..38bb914e0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -101,3 +101,13 @@ jobs: run: yarn scripts create-release-from-tags env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Trigger CDN release build + run: | + curl -sS -f -X POST \ + "https://api.buildkite.com/v2/organizations/twilio/pipelines/cdp-analytics-next/builds" \ + -H "Authorization: Bearer ${BUILDKITE_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"commit\":\"${GITHUB_SHA}\",\"branch\":\"master\",\"message\":\"CDN release for ${GITHUB_SHA}\"}" + env: + BUILDKITE_API_TOKEN: ${{ secrets.BUILDKITE_API_TOKEN }} + GITHUB_SHA: ${{ github.sha }} From a44c151b9ab03d4219ebd478ba05f226b97466de Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Fri, 31 Jul 2026 11:26:34 -0500 Subject: [PATCH 2/4] ci: replace Buildkite CDN pipeline with a GHA deploy-cdn job (OIDC) ADR approved GHA + OIDC directly for CDN deploy, per GitHub's documented pattern: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-aws Removes .buildkite/pipeline.yml and the Buildkite-API trigger step added in this branch's earlier commits. Adds a deploy-cdn job to publish.yml that assumes ajs-private-assets-upload directly via aws-actions/configure-aws-credentials (no Buildkite, no cluster secrets, no BUILDKITE_API_TOKEN needed) and runs the existing release:cdn script. Scoped to the same `production` GitHub Environment that already manually-gates npm publish, so both share one approval gate. Still blocked on: - cdp-infra reviewing/applying the paired trust-policy PRs (terracode-all-accounts#1650, terracode-platform#93) - filling in the remaining chamber-sourced env var placeholders (STAGE_BUCKET, *_CDN_OAI, *_CUSTOM_DOMAIN_OAI, *_SHADOW) cdp-analytics-next's Buildkite pipeline is left disabled (webhook already turned off) rather than deleted outright, in case it's still wanted as a reference - no further action needed there. --- .buildkite/pipeline.yml | 44 ----------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 .buildkite/pipeline.yml diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml deleted file mode 100644 index 39eac6604..000000000 --- a/.buildkite/pipeline.yml +++ /dev/null @@ -1,44 +0,0 @@ -agents: - queue: general-039 - -env: - NODE_IMAGE: '018537234677.dkr.ecr.us-east-1.amazonaws.com/docker.io/library/node:20.19.4-bookworm' - AJS_PRIVATE_ASSETS_UPLOAD: 'arn:aws:iam::812113486725:role/ajs-private-assets-upload' - PROD_BUCKET: 'segment-ajs-renderer-compiled-production' - # TODO(cdp-infra trust-policy change pending): confirm/fill in before first real run. - STAGE_BUCKET: 'TODO' - PROD_CDN_OAI: 'TODO' - STAGE_CDN_OAI: 'TODO' - PROD_CUSTOM_DOMAIN_OAI: 'TODO' - STAGE_CUSTOM_DOMAIN_OAI: 'TODO' - PROD_SHADOW: 'TODO' - STAGE_SHADOW: 'TODO' - -steps: - - label: '[Browser] Release to CDN :rocket:' - if: build.branch == "master" - commands: | - webtoken_file="$(mktemp)" - buildkite-agent oidc request-token --audience sts.amazonaws.com > "$webtoken_file" - - CREDS=$(aws sts assume-role-with-web-identity \ - --role-arn "$AJS_PRIVATE_ASSETS_UPLOAD" \ - --role-session-name "buildkite-$BUILDKITE_PIPELINE_SLUG-$BUILDKITE_BUILD_NUMBER-cdn" \ - --web-identity-token "$(cat "$webtoken_file")" \ - --query 'Credentials' --output json) - rm -f "$webtoken_file" - - export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r .AccessKeyId) - export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r .SecretAccessKey) - export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r .SessionToken) - - docker run --rm \ - -v "$$PWD:/workdir" -w /workdir \ - -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN \ - -e PROD_BUCKET -e STAGE_BUCKET -e PROD_SHADOW -e STAGE_SHADOW \ - -e PROD_CDN_OAI -e STAGE_CDN_OAI -e PROD_CUSTOM_DOMAIN_OAI -e STAGE_CUSTOM_DOMAIN_OAI \ - ${NODE_IMAGE} \ - sh -c ' - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 yarn install --immutable - yarn run -T browser release:cdn - ' From 2b51410b8e7ad271dd7b75c6f5219d507a7b35f2 Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Fri, 31 Jul 2026 11:26:45 -0500 Subject: [PATCH 3/4] ci: add deploy-cdn job to publish.yml Follow-up to the previous commit - the .buildkite/pipeline.yml deletion landed but this file's changes didn't get staged. --- .github/workflows/publish.yml | 45 +++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 38bb914e0..d20dae4f3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -101,13 +101,38 @@ jobs: run: yarn scripts create-release-from-tags env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Trigger CDN release build - run: | - curl -sS -f -X POST \ - "https://api.buildkite.com/v2/organizations/twilio/pipelines/cdp-analytics-next/builds" \ - -H "Authorization: Bearer ${BUILDKITE_API_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "{\"commit\":\"${GITHUB_SHA}\",\"branch\":\"master\",\"message\":\"CDN release for ${GITHUB_SHA}\"}" - env: - BUILDKITE_API_TOKEN: ${{ secrets.BUILDKITE_API_TOKEN }} - GITHUB_SHA: ${{ github.sha }} + + deploy-cdn: + name: Deploy to CDN + needs: [should-release, test, publish] + if: needs.should-release.outputs.release == 'true' + runs-on: ubuntu-latest-large + environment: production # same manual-approval gate as npm publish + permissions: + contents: read + id-token: write # AWS OIDC + Artifactory OIDC + env: + PROD_BUCKET: 'segment-ajs-renderer-compiled-production' + # TODO(cdp-infra trust-policy PRs pending): confirm/fill in before first real run. + STAGE_BUCKET: 'TODO' + PROD_CDN_OAI: 'TODO' + STAGE_CDN_OAI: 'TODO' + PROD_CUSTOM_DOMAIN_OAI: 'TODO' + STAGE_CUSTOM_DOMAIN_OAI: 'TODO' + PROD_SHADOW: 'TODO' + STAGE_SHADOW: 'TODO' + steps: + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - name: Artifactory OIDC Auth + uses: ./.github/actions/artifactory-oidc + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3 + with: + node-version: 20 + - run: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn install --immutable + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 + with: + role-to-assume: arn:aws:iam::812113486725:role/ajs-private-assets-upload + role-session-name: gha-analytics-next-cdn-deploy + aws-region: us-west-2 + - run: yarn run -T browser release:cdn From 453b851b673d70bbf034b135c254f80dc16e7371 Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Wed, 5 Aug 2026 11:32:16 -0500 Subject: [PATCH 4/4] fix: set BUILDKITE_BRANCH explicitly in deploy-cdn job release.js resolves the release branch via `BUILDKITE_BRANCH || git branch --show-current`. actions/checkout leaves the repo in detached HEAD by default, so the git fallback would resolve to an empty string rather than erroring, silently corrupting the S3 key path (analytics-next/br///... instead of .../br/master//...). Setting BUILDKITE_BRANCH explicitly (same var name the script already prioritizes) avoids touching the script itself. --- .github/workflows/publish.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d20dae4f3..15606b1ec 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -112,6 +112,12 @@ jobs: contents: read id-token: write # AWS OIDC + Artifactory OIDC env: + # release.js resolves the branch via `BUILDKITE_BRANCH || git branch + # --show-current` - actions/checkout leaves the repo in detached HEAD, + # so the git fallback would silently resolve to an empty string + # instead of erroring. Set this explicitly instead of touching the + # script itself. + BUILDKITE_BRANCH: ${{ github.ref_name }} PROD_BUCKET: 'segment-ajs-renderer-compiled-production' # TODO(cdp-infra trust-policy PRs pending): confirm/fill in before first real run. STAGE_BUCKET: 'TODO'