From fdbaae166e0d23dbad267a2ef7af2734f865733f Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Tue, 21 Jul 2026 11:28:46 -0500 Subject: [PATCH] ci: add Artifactory OIDC auth to the release/publish pipeline Mirrors the setup already shipped in segmentio/analytics-next: a composite action exchanges a GitHub OIDC token for a short-lived Artifactory access token and configures Yarn Berry (top-level env vars + an explicit npmRegistries block, since Yarn Berry's tarball fetcher needs the latter separately from resolution) to install through it, instead of hitting the public registry directly. Wired into every job in release.yml that installs dependencies (ci, dry-run, beta, production) - dependency resolution during release should go through Artifactory's curation policy the same way regular installs eventually will. Also passes NPM_CONFIG_IGNORE_SCRIPTS through the actual publish steps (beta/production), matching the same hardening in analytics-next. Provenance is already handled by release.config.js's existing provenance: true npm plugin option, so no NPM_CONFIG_PROVENANCE env var is needed here the way it was for analytics-next's changesets-based publish. Prerequisites already in place, confirmed before this PR: - segmentio/analytics-react-native is already in Artifactory's trusted_builds list (twilio-internal/artifactory-cloud-twilio-config) - the ARTIFACTORY_URL repo variable is already set - @semantic-release/npm is already >= 13 and devbox's pinned Node 22 resolves npm 11.11.0, both required for npm's native OIDC trusted-publisher support - no version bump needed Not done here (needs manual npmjs.org access, can't be automated): each of the 17 publicly-published packages in this monorepo needs its own npm trusted-publisher registration (repo + release.yml + environment name) before publish will actually authenticate via OIDC instead of the existing NPM_TOKEN secret, which today is fully unused by this workflow. --- .github/actions/artifactory-oidc/action.yml | 58 +++++++++++++++++++++ .github/workflows/release.yml | 19 +++++++ .husky/commit-msg | 2 +- devbox.lock | 2 +- 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 .github/actions/artifactory-oidc/action.yml diff --git a/.github/actions/artifactory-oidc/action.yml b/.github/actions/artifactory-oidc/action.yml new file mode 100644 index 000000000..66c5c7ea1 --- /dev/null +++ b/.github/actions/artifactory-oidc/action.yml @@ -0,0 +1,58 @@ +name: 'Artifactory OIDC Auth' +description: 'Exchange GitHub OIDC token for an Artifactory access token and configure Yarn Berry to resolve through it' + +runs: + using: 'composite' + steps: + - name: Exchange OIDC token for Artifactory access token + shell: bash + run: | + set -euo pipefail + + if ! command -v jq >/dev/null 2>&1; then + echo "::error::jq is required by the artifactory-oidc action but was not found on the runner." + exit 1 + fi + + BASE_URL=$(echo "${ARTIFACTORY_URL}" | sed -E 's#/+$##') + if [ -z "$BASE_URL" ]; then + echo "::error::ARTIFACTORY_URL is empty." + exit 1 + fi + + GITHUB_JWT=$(curl -sS \ + -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ + "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${BASE_URL}" \ + | jq -r '.value') + + RESP=$(curl -sS "${BASE_URL}/access/api/v1/oidc/token" \ + -H 'Content-Type: application/json' \ + -d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:token-exchange\", + \"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\", + \"subject_token\":\"${GITHUB_JWT}\", + \"provider_name\":\"github-actions-segmentio\"}") + ART_TOKEN=$(echo "$RESP" | jq -r '.access_token // empty') + + if [ -z "$ART_TOKEN" ]; then + echo "::error::OIDC token exchange failed. Redacted response below:" + echo "$RESP" | jq 'walk(if type == "object" then with_entries(if (.key | test("token"; "i")) then .value = "" else . end) else . end)' 2>/dev/null \ + || echo "::error::(response withheld — not valid JSON)" + exit 1 + fi + echo "::add-mask::$ART_TOKEN" + + REGISTRY="${BASE_URL}/artifactory/api/npm/virtual-npm-thirdparty/" + { + echo "YARN_NPM_REGISTRY_SERVER=${REGISTRY}" + echo "YARN_NPM_AUTH_TOKEN=${ART_TOKEN}" + echo "YARN_NPM_ALWAYS_AUTH=true" + } >> "$GITHUB_ENV" + + cat >> .yarnrc.yml <