-
Notifications
You must be signed in to change notification settings - Fork 126
157 lines (145 loc) · 6.12 KB
/
Copy pathrelease.yml
File metadata and controls
157 lines (145 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
name: Release
on:
push:
branches: [master]
paths:
- 'package.json'
permissions: {}
# Serialize releases and cancel an older run still pending approval when a
# newer version lands, so an approved-late stale run cannot publish backwards.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
check:
if: github.repository == 'node-modules/urllib'
name: Check version
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version_changed: ${{ steps.version.outputs.changed }}
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Check whether version is already published
id: version
run: |
set -euo pipefail
# Compare the exact version against the registry, not against `latest`,
# so prereleases (published under their own dist-tag) are not treated
# as perpetually newer than the stable `latest` and re-released.
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
STATUS=0
OUTPUT=$(npm view "urllib@$VERSION" version 2>&1) || STATUS=$?
if [ "$STATUS" -eq 0 ] && [ -n "$OUTPUT" ]; then
echo "urllib@$VERSION is already published; nothing to release."
echo "changed=false" >> "$GITHUB_OUTPUT"
elif [ "$STATUS" -ne 0 ] && ! grep -q 'E404' <<<"$OUTPUT"; then
# Not a "version missing" 404 -> auth/network/registry error; fail loudly.
echo "::error::npm view failed for urllib@$VERSION (not a 404):"
printf '%s\n' "$OUTPUT"
exit 1
else
echo "urllib@$VERSION is not published yet; proceeding."
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
request-approval:
name: Request approval
runs-on: ubuntu-latest
needs: check
if: needs.check.outputs.version_changed == 'true'
permissions: {}
env:
DINGTALK_WEBHOOK_URL: ${{ secrets.DINGTALK_RELEASE_WEBHOOK_URL }}
DINGTALK_WEBHOOK_SECRET: ${{ secrets.DINGTALK_RELEASE_WEBHOOK_SECRET }}
VERSION: ${{ needs.check.outputs.version }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Notify DingTalk
# Best-effort: a webhook failure must not block the manual approval gate.
continue-on-error: true
run: |
set -euo pipefail
# DingTalk signed webhook (加签): sign = urlencode(base64(HMAC-SHA256(secret, "timestamp\nsecret")))
TIMESTAMP=$(date +%s%3N)
SIGN=$(printf '%s\n%s' "$TIMESTAMP" "$DINGTALK_WEBHOOK_SECRET" \
| openssl dgst -sha256 -hmac "$DINGTALK_WEBHOOK_SECRET" -binary \
| base64 | tr -d '\n')
SIGN_ENC=$(jq -rn --arg s "$SIGN" '$s | @uri')
URL="${DINGTALK_WEBHOOK_URL}×tamp=${TIMESTAMP}&sign=${SIGN_ENC}"
TEXT=$(printf '### urllib release v%s\n\nAwaiting manual approval before publishing to npm.\n\n[Review and approve](%s)' "$VERSION" "$RUN_URL")
PAYLOAD=$(jq -n --arg text "$TEXT" \
'{msgtype: "markdown", markdown: {title: "urllib release approval", text: $text}}')
curl -fsS --connect-timeout 10 --max-time 30 \
--retry 3 --retry-delay 2 --retry-all-errors \
-X POST "$URL" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD"
release:
name: Publish to npm
runs-on: ubuntu-latest
# Manual approval gate: configure an Environment named "release" with
# required reviewers in repo settings. The job pauses here until approved.
environment: release
needs: [check, request-approval]
if: needs.check.outputs.version_changed == 'true'
permissions:
contents: write
id-token: write
env:
VERSION: ${{ needs.check.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
# Node 24 bundles npm >= 11.5.1, required for OIDC trusted publishing.
node-version: '24'
cache: true
sfw: true
- name: Determine npm dist-tag
id: dist-tag
run: |
set -euo pipefail
# Any semver with a pre-release part ("X.Y.Z-<id>...") must not go to
# latest. Strip build metadata first, then use the first pre-release
# identifier as the dist-tag (e.g. 4.10.0-beta.0 -> beta, 1.2.3-0 -> 0).
CORE="${VERSION%%+*}"
case "$CORE" in
*-*)
PRE="${CORE#*-}"
echo "tag=${PRE%%.*}" >> "$GITHUB_OUTPUT"
;;
*)
echo "tag=latest" >> "$GITHUB_OUTPUT"
;;
esac
- name: Re-check version before publish
run: |
set -euo pipefail
# Guard against a stale run approved after a newer version was published.
STATUS=0
OUTPUT=$(npm view "urllib@$VERSION" version 2>&1) || STATUS=$?
if [ "$STATUS" -eq 0 ] && [ -n "$OUTPUT" ]; then
echo "::error::urllib@$VERSION is already published; aborting to avoid republishing a stale version."
exit 1
elif [ "$STATUS" -ne 0 ] && ! grep -q 'E404' <<<"$OUTPUT"; then
echo "::error::npm view failed for urllib@$VERSION (not a 404); aborting:"
printf '%s\n' "$OUTPUT"
exit 1
fi
echo "urllib@$VERSION is not yet published; proceeding to publish."
- name: Publish to npm
run: npm publish --access public --tag ${{ steps.dist-tag.outputs.tag }}
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
generate_release_notes: true
name: v${{ env.VERSION }}
tag_name: v${{ env.VERSION }}
target_commitish: ${{ github.sha }}
prerelease: ${{ steps.dist-tag.outputs.tag != 'latest' }}