Skip to content

Commit 9ccd595

Browse files
dahliaclaude
andcommitted
Migrate PR CI to pull_request and make publishing opt-in
This change addresses the npm classic token revocation by migrating to trusted publishing (OIDC) for PR pre-releases. Changes: - Change CI trigger from pull_request_target to pull_request for security - Create new publish-pr.yaml workflow with workflow_dispatch for opt-in PR pre-release publishing (packages and docs preview) - Add composite actions for setup-deno and setup-node-and-pnpm to reduce duplication across workflows - Add scripts/generate_packages_table.ts to dynamically generate package tables for PR comments (supports new packages in PRs or next branch) - Update CONTRIBUTING.md to document the new opt-in PR build process Closes #491 Co-Authored-By: Claude <[email protected]>
1 parent 925ddd7 commit 9ccd595

File tree

8 files changed

+489
-273
lines changed

8 files changed

+489
-273
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: Setup Deno
2+
description: Set up Deno with the project's required version
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- uses: denoland/setup-deno@v2
8+
with:
9+
deno-version: 2.5.6 # Keep in sync with mise.toml
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Setup Node.js and pnpm
2+
description: Set up Node.js and pnpm with caching
3+
4+
inputs:
5+
pnpm-version:
6+
description: pnpm version to install
7+
required: false
8+
default: '10'
9+
10+
runs:
11+
using: composite
12+
steps:
13+
- uses: pnpm/action-setup@v4
14+
with:
15+
version: ${{ inputs.pnpm-version }}
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: lts/*
19+
cache: pnpm

.github/workflows/build.yaml

Lines changed: 33 additions & 253 deletions
Large diffs are not rendered by default.

.github/workflows/publish-pr.yaml

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
name: Publish PR pre-release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
pr_number:
7+
description: 'PR number to publish'
8+
required: true
9+
type: number
10+
publish_packages:
11+
description: 'Publish packages to JSR/npm'
12+
type: boolean
13+
default: true
14+
publish_docs:
15+
description: 'Publish docs preview to Cloudflare'
16+
type: boolean
17+
default: true
18+
19+
jobs:
20+
get-pr-info:
21+
runs-on: ubuntu-latest
22+
outputs:
23+
head_sha: ${{ steps.pr.outputs.head_sha }}
24+
head_repo: ${{ steps.pr.outputs.head_repo }}
25+
steps:
26+
- name: Get PR info
27+
id: pr
28+
uses: actions/github-script@v7
29+
with:
30+
script: |
31+
const pr = await github.rest.pulls.get({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
pull_number: ${{ inputs.pr_number }}
35+
});
36+
if (pr.data.state !== 'open') {
37+
core.setFailed(`PR #${{ inputs.pr_number }} is not open`);
38+
return;
39+
}
40+
core.setOutput('head_sha', pr.data.head.sha);
41+
core.setOutput('head_repo', pr.data.head.repo.full_name);
42+
43+
publish-packages:
44+
if: inputs.publish_packages
45+
needs: [get-pr-info]
46+
runs-on: ubuntu-latest
47+
permissions:
48+
id-token: write
49+
contents: read
50+
pull-requests: write
51+
outputs:
52+
version: ${{ steps.versioning.outputs.version }}
53+
short_version: ${{ steps.versioning.outputs.short_version }}
54+
packages_table: ${{ steps.packages-table.outputs.table }}
55+
packages_links: ${{ steps.packages-table.outputs.links }}
56+
steps:
57+
- uses: actions/checkout@v4
58+
with:
59+
repository: ${{ needs.get-pr-info.outputs.head_repo }}
60+
ref: ${{ needs.get-pr-info.outputs.head_sha }}
61+
- uses: ./.github/actions/setup-deno
62+
- uses: ./.github/actions/setup-node-and-pnpm
63+
with:
64+
pnpm-version: latest
65+
- run: sudo npm install -g npm@latest && npm --version
66+
- name: Generate PR version
67+
run: |
68+
jq \
69+
--arg pr_number "${{ inputs.pr_number }}" \
70+
--arg build "$GITHUB_RUN_NUMBER" \
71+
--arg commit "${HEAD_SHA::8}" \
72+
'.version = .version + "-pr." + $pr_number + "." + $build + "+" + $commit' \
73+
deno.json > deno.json.tmp
74+
mv deno.json.tmp deno.json
75+
working-directory: ${{ github.workspace }}/packages/fedify/
76+
env:
77+
HEAD_SHA: ${{ needs.get-pr-info.outputs.head_sha }}
78+
- id: versioning
79+
run: |
80+
set -ex
81+
echo version="$(jq -r .version deno.json)" >> $GITHUB_OUTPUT
82+
echo short_version="$(jq -r .version deno.json | sed 's/[+].*//')" >> $GITHUB_OUTPUT
83+
working-directory: ${{ github.workspace }}/packages/fedify/
84+
- run: deno task check-versions --fix
85+
# Don't know why, but the .gitignore list is not overridden by include list
86+
# in deno.json:
87+
- run: rm src/vocab/.gitignore
88+
working-directory: ${{ github.workspace }}/packages/fedify/
89+
- run: |
90+
pnpm install
91+
pnpm pack --recursive --filter='!./examples/**'
92+
rm fedify-cli-*.tgz
93+
- name: Publish to JSR
94+
run: |
95+
set -ex
96+
deno task -f @fedify/fedify codegen
97+
max_attempts=5
98+
attempt=1
99+
until deno publish --allow-dirty; do
100+
exit_code=$?
101+
if [[ $attempt -ge $max_attempts ]]; then
102+
echo "deno publish failed after $max_attempts attempts"
103+
exit $exit_code
104+
fi
105+
echo "deno publish failed (attempt $attempt/$max_attempts), retrying in 30 seconds..."
106+
sleep 30
107+
((attempt++))
108+
done
109+
- name: Publish to npm
110+
run: |
111+
set -ex
112+
for pkg in fedify-*.tgz; do
113+
npm publish \
114+
--logs-dir=. \
115+
--provenance \
116+
--access public \
117+
--tag "pr-${{ inputs.pr_number }}" \
118+
"$pkg" \
119+
|| grep "Cannot publish over previously published version" *.log
120+
done
121+
- name: Generate packages table
122+
id: packages-table
123+
run: |
124+
set -e
125+
VERSION="${{ steps.versioning.outputs.version }}"
126+
SHORT_VERSION="${{ steps.versioning.outputs.short_version }}"
127+
128+
{
129+
echo 'table<<EOFTABLE'
130+
deno run -A scripts/generate_packages_table.ts --format=table "$VERSION" "$SHORT_VERSION"
131+
echo 'EOFTABLE'
132+
} >> $GITHUB_OUTPUT
133+
134+
{
135+
echo 'links<<EOFLINKS'
136+
deno run -A scripts/generate_packages_table.ts --format=links "$VERSION" "$SHORT_VERSION"
137+
echo 'EOFLINKS'
138+
} >> $GITHUB_OUTPUT
139+
140+
publish-docs:
141+
if: inputs.publish_docs
142+
needs: [get-pr-info]
143+
runs-on: ubuntu-latest
144+
permissions:
145+
contents: read
146+
outputs:
147+
deployment_url: ${{ steps.wrangler.outputs.deployment-url }}
148+
steps:
149+
- uses: actions/checkout@v4
150+
with:
151+
repository: ${{ needs.get-pr-info.outputs.head_repo }}
152+
ref: ${{ needs.get-pr-info.outputs.head_sha }}
153+
- uses: ./.github/actions/setup-deno
154+
- uses: ./.github/actions/setup-node-and-pnpm
155+
- run: |
156+
set -ex
157+
pnpm install
158+
EXTRA_NAV_TEXT=Stable \
159+
EXTRA_NAV_LINK="$STABLE_DOCS_URL" \
160+
SITEMAP_HOSTNAME="$UNSTABLE_DOCS_URL" \
161+
JSR_REF_VERSION=unstable \
162+
pnpm run build
163+
env:
164+
PLAUSIBLE_DOMAIN: ${{ secrets.PLAUSIBLE_DOMAIN }}
165+
STABLE_DOCS_URL: ${{ vars.STABLE_DOCS_URL }}
166+
UNSTABLE_DOCS_URL: ${{ vars.UNSTABLE_DOCS_URL }}
167+
working-directory: ${{ github.workspace }}/docs/
168+
- id: wrangler
169+
uses: cloudflare/wrangler-action@v3
170+
with:
171+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
172+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
173+
gitHubToken: ${{ github.token }}
174+
command: >-
175+
pages deploy .vitepress/dist
176+
--project-name=${{ vars.CLOUDFLARE_PROJECT_NAME }}
177+
--branch=pr-${{ inputs.pr_number }}
178+
packageManager: pnpm
179+
workingDirectory: ${{ github.workspace }}/docs/
180+
181+
comment-on-pr:
182+
needs: [get-pr-info, publish-packages, publish-docs]
183+
if: always() && needs.get-pr-info.result == 'success' && (needs.publish-packages.result == 'success' || needs.publish-docs.result == 'success')
184+
runs-on: ubuntu-latest
185+
permissions:
186+
pull-requests: write
187+
steps:
188+
- name: Delete old comment
189+
uses: thollander/actions-comment-pull-request@v3
190+
with:
191+
pr-number: ${{ inputs.pr_number }}
192+
comment-tag: publish
193+
mode: delete
194+
- name: Post comment
195+
uses: thollander/actions-comment-pull-request@v3
196+
with:
197+
pr-number: ${{ inputs.pr_number }}
198+
comment-tag: publish
199+
message: |
200+
Pre-release has been published for this pull request:
201+
202+
${{ needs.publish-packages.result == 'success' && format('## Packages
203+
204+
{0}
205+
206+
{1}
207+
208+
', needs.publish-packages.outputs.packages_table, needs.publish-packages.outputs.packages_links) || '' }}${{ needs.publish-docs.result == 'success' && format('## Documentation
209+
210+
The docs for this pull request have been published:
211+
212+
<{0}>', needs.publish-docs.outputs.deployment_url) || '' }}
213+
214+
# cSpell: ignore npmjs thollander

.github/workflows/remove-npm-pr-tags.yaml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,7 @@ jobs:
1313
- name: Checkout repository
1414
uses: actions/checkout@v4
1515

16-
- name: Install pnpm
17-
uses: pnpm/action-setup@v4
18-
with:
19-
version: 10
20-
21-
- name: Setup Node.js
22-
uses: actions/setup-node@v4
23-
with:
24-
node-version: lts/*
25-
cache: 'pnpm'
16+
- uses: ./.github/actions/setup-node-and-pnpm
2617

2718
- name: Remove PR tags from npm
2819
# Remove tags in fedify packages if exists

.github/workflows/sponsors.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ jobs:
1616
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
1717
persist-credentials: true
1818
fetch-depth: 0
19-
- uses: denoland/setup-deno@v1
20-
with:
21-
deno-version: 2.5.0 # Keep in sync with mise.toml
19+
- uses: ./.github/actions/setup-deno
2220
- uses: qoomon/actions--setup-git@v1
2321
- run: |
2422
set -e

CONTRIBUTING.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,14 @@ for any dependency-related changes.
228228

229229
### Pull request builds
230230

231-
Each pull request is automatically built and published to the JSR and npm
232-
registries as a pre-release. You can test the pull request by installing
233-
the pre-release version of the Fedify library. The version number of
234-
the pre-release version consists of the base version number, the pull request
235-
number, the build number, and the commit hash, which looks like
236-
`1.2.3-pr.456.789+abcdef01`. You can find the exact version number in
237-
the comment left by the build process in the pull request.
231+
Pre-release versions can be published for pull requests on request. If you need
232+
a pre-release version to test your changes, ask a maintainer in the PR comments.
233+
A maintainer can then trigger the pre-release build from the GitHub Actions tab.
234+
235+
The version number of the pre-release version consists of the base version
236+
number, the pull request number, the build number, and the commit hash, which
237+
looks like `1.2.3-pr.456.789+abcdef01`. Once published, a comment will be
238+
posted on the PR with the exact version numbers and installation instructions.
238239

239240

240241
Build

0 commit comments

Comments
 (0)