@@ -13,64 +13,56 @@ concurrency:
1313 cancel-in-progress : true
1414
1515jobs :
16- publish-v3 :
17- if : github.event_name == 'push' && github.event.ref == 'refs/heads/v3.x'
16+ setup :
17+ if : github.event_name == 'push'
1818 runs-on : ubuntu-latest
19- environment :
20- name : npm
21- url : https://npmjs.com/package/dd-trace
22- permissions :
23- id-token : write
19+ # Space-separated npm dist-tags for each release line.
20+ # The latest branch always gets "latest" prepended automatically.
21+ # Add a line here when creating a new release branch.
2422 env :
25- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
23+ DIST_TAGS : |
24+ v3.x latest-node14
25+ v4.x latest-node16
26+ v5.x latest-node18 latest-node20
27+ outputs :
28+ npm_tags : ${{ steps.compute.outputs.npm_tags }}
29+ is_latest : ${{ steps.compute.outputs.is_latest }}
2630 steps :
27- - uses : DataDog/dd-octo-sts-action@96a25462dbcb10ebf0bfd6e2ccc917d2ab235b9a # v1.0.4
28- id : octo-sts
29- with :
30- scope : DataDog/dd-trace-js
31- policy : self.github.release.push-tags
32- - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33- - uses : ./.github/actions/node
34- - run : npm publish --tag latest-node14
35- - id : pkg
31+ - id : compute
32+ env :
33+ GH_TOKEN : ${{ github.token }}
3634 run : |
37- content=`cat ./package.json | tr '\n' ' '`
38- echo "json=$content" >> $GITHUB_OUTPUT
39- - run : |
40- git tag v${{ fromJson(steps.pkg.outputs.json).version }}
41- git push https://x-access-token:${{ steps.octo-sts.outputs.token }}@github.com/${{ github.repository }}.git v${{ fromJson(steps.pkg.outputs.json).version }}
42- - run : node scripts/release/notes
35+ branch="${{ github.ref_name }}"
4336
44- publish-v4 :
45- if : github.event_name == 'push' && github.event.ref == 'refs/heads/v4.x'
46- runs-on : ubuntu-latest
47- environment :
48- name : npm
49- url : https://npmjs.com/package/dd-trace
50- permissions :
51- id-token : write
52- env :
53- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
54- steps :
55- - uses : DataDog/dd-octo-sts-action@96a25462dbcb10ebf0bfd6e2ccc917d2ab235b9a # v1.0.4
56- id : octo-sts
57- with :
58- scope : DataDog/dd-trace-js
59- policy : self.github.release.push-tags
60- - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
61- - uses : ./.github/actions/node
62- - run : npm publish --tag latest-node16
63- - id : pkg
64- run : |
65- content=`cat ./package.json | tr '\n' ' '`
66- echo "json=$content" >> $GITHUB_OUTPUT
67- - run : |
68- git tag v${{ fromJson(steps.pkg.outputs.json).version }}
69- git push https://x-access-token:${{ steps.octo-sts.outputs.token }}@github.com/${{ github.repository }}.git v${{ fromJson(steps.pkg.outputs.json).version }}
70- - run : node scripts/release/notes
37+ # Find the latest release line by intersecting the GitHub branch
38+ # list with DIST_TAGS. This ensures a branch that exists in the
39+ # repo but is not yet wired into the config cannot steal the
40+ # latest tag before triggers and dist-tag mappings are updated.
41+ configured=$(echo "$DIST_TAGS" | awk '{print $1}')
42+ latest=$(gh api "repos/${{ github.repository }}/branches" \
43+ --paginate \
44+ --jq '.[].name' \
45+ | grep -E '^v[0-9]+\.x$' \
46+ | grep -Fx -f <(echo "$configured") \
47+ | sort -V \
48+ | tail -1)
49+
50+ # Collect the tags defined for this branch (everything after the branch name)
51+ branch_tags=$(echo "$DIST_TAGS" | awk -v b="$branch" '$1==b{$1=""; sub(/^ /,""); print}')
7152
72- publish-latest :
73- if : github.event_name == 'push' && github.event.ref == 'refs/heads/v5.x'
53+ if [ "$branch" = "$latest" ]; then
54+ npm_tags="latest ${branch_tags:-latest-${branch%.x}}"
55+ is_latest="true"
56+ else
57+ npm_tags="${branch_tags:-latest-${branch%.x}}"
58+ is_latest="false"
59+ fi
60+
61+ echo "npm_tags=$npm_tags" >> "$GITHUB_OUTPUT"
62+ echo "is_latest=$is_latest" >> "$GITHUB_OUTPUT"
63+
64+ publish :
65+ needs : setup
7466 runs-on : ubuntu-latest
7567 environment :
7668 name : npm
@@ -89,20 +81,76 @@ jobs:
8981 with :
9082 persist-credentials : false
9183 - uses : ./.github/actions/node
92- - run : npm publish
9384 - id : pkg
9485 run : |
9586 content=`cat ./package.json | tr '\n' ' '`
9687 echo "json=$content" >> $GITHUB_OUTPUT
97- - run : |
98- git tag v${{ fromJson(steps.pkg.outputs.json).version }}
99- git push https://x-access-token:${{ steps.octo-sts.outputs.token }}@github.com/${{ github.repository }}.git v${{ fromJson(steps.pkg.outputs.json).version }}
100- - run : node scripts/release/notes --latest
88+ - name : Publish
89+ run : |
90+ version="${{ fromJson(steps.pkg.outputs.json).version }}"
91+ tags=(${{ needs.setup.outputs.npm_tags }})
92+
93+ # Exchange the GitHub OIDC token for an npm registry token so
94+ # that npm publish and npm dist-tag add share the same auth.
95+ # npm only runs this exchange internally for publish (oidc.js:141)
96+ # and does not persist the token, so dist-tag add would fail
97+ # without doing it explicitly here.
98+ # Audience format and exchange endpoint from npm/lib/utils/oidc.js.
99+ oidc_token=$(curl -sS -fH \
100+ "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
101+ "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=npm:registry.npmjs.org" \
102+ | jq -r '.value')
103+ npm_token=$(curl -sS -fX POST \
104+ -H "Authorization: Bearer $oidc_token" \
105+ -H "Content-Type: application/json" \
106+ "https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/dd-trace" \
107+ | jq -r '.token')
108+ export NODE_AUTH_TOKEN="$npm_token"
109+
110+ # Idempotent: skip publish if this version already exists so
111+ # that a rerun after a partial failure can still repair dist-tags,
112+ # git tags, release notes, and docs.
113+ if npm view "dd-trace@$version" version 2>/dev/null | grep -qF "$version"; then
114+ local_integrity=$(npm pack --dry-run --json 2>/dev/null | jq -r '.[0].integrity')
115+ published_integrity=$(npm view "dd-trace@$version" dist.integrity 2>/dev/null)
116+ if [ "$local_integrity" != "$published_integrity" ]; then
117+ echo "ERROR: package content at current HEAD differs from dd-trace@$version on the registry" >&2
118+ echo " Published: $published_integrity" >&2
119+ echo " Current: $local_integrity" >&2
120+ exit 1
121+ fi
122+ echo "Version $version already published with identical content, skipping npm publish"
123+ else
124+ npm publish --tag "${tags[0]}"
125+ fi
126+
127+ # Always reconcile all expected dist-tags.
128+ for tag in "${tags[@]:1}"; do
129+ npm dist-tag add "dd-trace@$version" "$tag"
130+ done
131+ - name : Tag release
132+ run : |
133+ version="${{ fromJson(steps.pkg.outputs.json).version }}"
134+ remote="https://x-access-token:${{ steps.octo-sts.outputs.token }}@github.com/${{ github.repository }}.git"
135+ if git ls-remote --tags "$remote" "v$version" | grep -q "v$version"; then
136+ echo "Tag v$version already exists, skipping"
137+ else
138+ git tag "v$version"
139+ git push "$remote" "v$version"
140+ fi
141+ - name : Release notes
142+ run : |
143+ if [ "${{ needs.setup.outputs.is_latest }}" = "true" ]; then
144+ node scripts/release/notes --latest
145+ else
146+ node scripts/release/notes
147+ fi
101148 env :
102149 GH_TOKEN : ${{ steps.octo-sts.outputs.token }}
103150
104151 docs :
105- needs : " publish-latest"
152+ needs : [setup, publish]
153+ if : needs.setup.outputs.is_latest == 'true'
106154 runs-on : ubuntu-latest
107155 permissions :
108156 id-token : write
0 commit comments