Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/prepare_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Prepare Release

permissions: {}

on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (without v prefix, e.g. 4.9.1 or 4.10.0-beta.0)'
required: true
type: string

concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true

jobs:
prepare:
if: github.repository == 'node-modules/urllib'
name: Prepare Release
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0

Comment thread
fengmk2 marked this conversation as resolved.
- name: Validate version
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
# Require semver without a leading "v", e.g. 4.9.1 or 4.10.0-beta.0
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$'; then
echo "::error::Invalid version '$VERSION'. Expected semver without 'v' prefix, e.g. 4.9.1 or 4.10.0-beta.0"
exit 1
fi

- name: Bump version
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
sed -i -E "s/^(\s*\"version\":\s*)\"[^\"]+\"/\1\"$VERSION\"/" package.json
grep -qF "\"version\": \"$VERSION\"" package.json || { echo "::error::Failed to update package.json"; exit 1; }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Comment thread
fengmk2 marked this conversation as resolved.
Outdated
echo "Updated package.json to $VERSION"
Comment thread
fengmk2 marked this conversation as resolved.
Outdated

- name: Create pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
commit-message: 'release: v${{ inputs.version }}'
title: 'release: v${{ inputs.version }}'
branch: release/v${{ inputs.version }}
base: master
body: |
Release urllib v${{ inputs.version }}.

Merging this PR updates the version on `master` and triggers the release
workflow, which publishes to npm and creates the GitHub Release after
manual approval.
assignees: fengmk2
98 changes: 77 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,83 @@ name: Release

on:
push:
tags:
- 'v*'
branches: [master]
paths:
- 'package.json'

permissions:
contents: write
id-token: write
permissions: {}

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 version changes
uses: EndBug/version-check@095362f3cd50f690c8fa0e6afeea81834bd8d320 # v3.0.0
id: version
with:
static-checking: localIsNew
file-url: https://unpkg.com/urllib@latest/package.json
Comment thread
fengmk2 marked this conversation as resolved.
Outdated
file-name: package.json

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}&timestamp=${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
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'
Comment thread
fengmk2 marked this conversation as resolved.
Comment thread
fengmk2 marked this conversation as resolved.
permissions:
contents: write
id-token: write
env:
VERSION: ${{ needs.check.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
Expand All @@ -24,22 +87,12 @@ jobs:
cache: true
sfw: true

- name: Verify version matches tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_VERSION=$(node -p "require('./package.json').version")
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)"
exit 1
fi

- name: Determine npm dist-tag
id: dist-tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
if echo "$TAG_VERSION" | grep -qE '-([a-zA-Z]+)'; then
# Extract pre-release identifier (e.g., "beta" from "5.0.0-beta.0")
PRE_TAG=$(echo "$TAG_VERSION" | sed -E 's/.*-([a-zA-Z]+).*/\1/')
if echo "$VERSION" | grep -qE '-([a-zA-Z]+)'; then
# Extract pre-release identifier (e.g. "beta" from "4.10.0-beta.0")
PRE_TAG=$(echo "$VERSION" | sed -E 's/.*-([a-zA-Z]+).*/\1/')
echo "tag=$PRE_TAG" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> "$GITHUB_OUTPUT"
Expand All @@ -49,7 +102,10 @@ jobs:
run: npm publish --access public --tag ${{ steps.dist-tag.outputs.tag }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v3
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' }}
Loading