Authenticate CI to Develocity with workload identity (#1109)

## Changes

Moves the workflows that publish to solutions.develocity.cloud from the
long-lived `DV_SOLUTIONS_ACCESS_KEY` to GitHub OIDC tokens, using
Develocity workload identity.

- **`ci-check-and-unit-test.yml` and `ci-update-dist.yml`**: the
Develocity npm agent reads `DEVELOCITY_ACCESS_KEY` directly, and an OIDC
token can't be used there. A pre-step mints the OIDC token and exchanges
it at `/api/auth/token?expiresInHours=2`, the call setup-gradle makes,
for a short-lived Develocity access token. The npm steps read it from
`steps.develocity-oidc.outputs.token-access-key`.
- **`demo-job-summary.yml`** (`develocity-access-key-set` job): a
pre-step mints the OIDC token and passes it to `setup-gradle` as
`develocity-access-key` in `host=token` form. setup-gradle exchanges it,
the same as it does with an access key.

Each job gets `id-token: write`. Pull requests from forks get no OIDC
token, just as they get no secrets today: the step warns and exits
cleanly, and the builds run unauthenticated.

## Not in this PR

`suite-integ-test-other.yml` still passes `DV_SOLUTIONS_ACCESS_KEY` to
`integ-test-inject-develocity.yml`. Those jobs test setup-gradle's own
access-key handling: swapping a classic access key for a short-lived
token, the `GRADLE_ENTERPRISE_ACCESS_KEY` variant, and not leaking the
key when the exchange fails. Feeding them an OIDC token would still run
the exchange, but would stop covering the classic access-key path.
Should they keep a long-lived key on purpose?

## Verification

- [x] CI-check-and-unit-test on this PR ([run
41095618008](https://github.com/gradle/actions/actions/runs/41095618008)):
green, the OIDC token is exchanged, and the npm agent published Build
Scans to solutions.develocity.cloud, for example
[ejvi6m5cprrhg](https://solutions.develocity.cloud/s/ejvi6m5cprrhg) and
[goncdygtosovo](https://solutions.develocity.cloud/s/goncdygtosovo)
- [x] Demo Job Summary dispatched on this branch ([run
41095628373](https://github.com/gradle/actions/actions/runs/41095628373)),
`develocity-access-key-set` job: green, setup-gradle logs `Requesting
short-lived Develocity access token for solutions.develocity.cloud` with
no warning. The sample project publishes to scans.gradle.com, as before.
- [ ] CI-update-dist runs only on `main`/release branches and commits
`dist`, so it is verified after merge
This commit is contained in:
Iñaki Villar authored and GitHub committed 2026-10-30 11:08:40 -07:00
commit c8a326d857
3 files changed
+63 -6

No files matched your search

+24 -3
View File
@@ -14,6 +14,9 @@ permissions:
jobs:
check-format-and-unit-test:
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
@@ -36,13 +39,31 @@ jobs:
run: |
npm exec -y -- pacote extract @gradle-tech/develocity-agent@3.0.1 ~/.node_libraries/@gradle-tech/develocity-agent
- name: Get GitHub OIDC token for Develocity
id: develocity-oidc
shell: bash
run: |
if [ -z "$ACTIONS_ID_TOKEN_REQUEST_URL" ]; then
echo "::warning::No GitHub OIDC token available (e.g. PR from a fork), Develocity will not be authenticated"
exit 0
fi
token=$(curl -sSfG -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
--data-urlencode "audience=https://solutions.develocity.cloud" \
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r .value)
echo "::add-mask::$token"
# Exchange the OIDC token for a short-lived Develocity access token, for the Develocity npm agent that reads DEVELOCITY_ACCESS_KEY directly
access_token=$(curl -sSf --retry 3 -X POST -H "Authorization: Bearer $token" \
"https://solutions.develocity.cloud/api/auth/token?expiresInHours=2")
echo "::add-mask::$access_token"
echo "token-access-key=solutions.develocity.cloud=$access_token" >> "$GITHUB_OUTPUT"
- name: Install npm dependencies
run: |
npm clean-install
working-directory: sources
env:
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
DEVELOCITY_ACCESS_KEY: '${{ steps.develocity-oidc.outputs.token-access-key }}'
- name: Check formatting and compile
run: |
@@ -51,7 +72,7 @@ jobs:
working-directory: sources
env:
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
DEVELOCITY_ACCESS_KEY: '${{ steps.develocity-oidc.outputs.token-access-key }}'
- name: Run unit tests
run: |
@@ -59,4 +80,4 @@ jobs:
working-directory: sources
env:
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
DEVELOCITY_ACCESS_KEY: '${{ steps.develocity-oidc.outputs.token-access-key }}'
+21 -2
View File
@@ -20,6 +20,7 @@ jobs:
if: github.repository == 'gradle/actions'
permissions:
contents: write
id-token: write
runs-on: ubuntu-latest
steps:
@@ -38,13 +39,31 @@ jobs:
run: |
npm exec -y -- pacote extract @gradle-tech/develocity-agent@3.0.1 ~/.node_libraries/@gradle-tech/develocity-agent
- name: Get GitHub OIDC token for Develocity
id: develocity-oidc
shell: bash
run: |
if [ -z "$ACTIONS_ID_TOKEN_REQUEST_URL" ]; then
echo "::warning::No GitHub OIDC token available (e.g. PR from a fork), Develocity will not be authenticated"
exit 0
fi
token=$(curl -sSfG -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
--data-urlencode "audience=https://solutions.develocity.cloud" \
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r .value)
echo "::add-mask::$token"
# Exchange the OIDC token for a short-lived Develocity access token, for the Develocity npm agent that reads DEVELOCITY_ACCESS_KEY directly
access_token=$(curl -sSf --retry 3 -X POST -H "Authorization: Bearer $token" \
"https://solutions.develocity.cloud/api/auth/token?expiresInHours=2")
echo "::add-mask::$access_token"
echo "token-access-key=solutions.develocity.cloud=$access_token" >> "$GITHUB_OUTPUT"
- name: Install npm dependencies
run: |
npm clean-install
working-directory: sources
env:
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
DEVELOCITY_ACCESS_KEY: '${{ steps.develocity-oidc.outputs.token-access-key }}'
- name: Build distribution
run: |
@@ -54,7 +73,7 @@ jobs:
env:
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
DEVELOCITY_URL: 'https://solutions.develocity.cloud'
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
DEVELOCITY_ACCESS_KEY: '${{ steps.develocity-oidc.outputs.token-access-key }}'
- name: Copy the generated sources/dist directory to the top-level dist
run: |
+18 -1
View File
@@ -259,6 +259,9 @@ jobs:
develocity-access-key-set:
needs: build-distribution
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout sources
@@ -266,10 +269,24 @@ jobs:
- name: Initialize integ-test
uses: ./.github/actions/init-integ-test
- name: Get GitHub OIDC token for Develocity
id: develocity-oidc
shell: bash
run: |
if [ -z "$ACTIONS_ID_TOKEN_REQUEST_URL" ]; then
echo "::warning::No GitHub OIDC token available (e.g. PR from a fork), Develocity will not be authenticated"
exit 0
fi
token=$(curl -sSfG -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
--data-urlencode "audience=https://solutions.develocity.cloud" \
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r .value)
echo "::add-mask::$token"
echo "access-key=solutions.develocity.cloud=$token" >> "$GITHUB_OUTPUT"
- name: Setup Gradle
uses: ./setup-gradle
with:
develocity-access-key: ${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}
develocity-access-key: ${{ steps.develocity-oidc.outputs.access-key }}
- name: Build kotlin-dsl project
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew assemble