Skip to content

Commit 9ea2bb3

Browse files
committed
Harden opt-in preview publishing
1 parent 9886607 commit 9ea2bb3

3 files changed

Lines changed: 73 additions & 22 deletions

File tree

.github/workflows/pr-preview.yml

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,84 @@ on:
1111
type: string
1212

1313
concurrency:
14-
group: publish-pr-preview-${{ github.event.pull_request.number || inputs.pr_number }}
14+
group: publish-pr-preview-${{ github.event.pull_request.number || github.run_id }}
1515
cancel-in-progress: false
1616

1717
jobs:
18-
build:
18+
context:
1919
if: >-
2020
github.event_name == 'workflow_dispatch' ||
2121
(github.event.label.name == 'publish-preview' &&
2222
github.event.pull_request.head.repo.full_name == github.repository)
2323
runs-on: ubuntu-latest
24+
timeout-minutes: 5
25+
permissions:
26+
contents: read
27+
pull-requests: read
28+
outputs:
29+
pr_number: ${{ steps.context.outputs.pr_number }}
30+
head_sha: ${{ steps.context.outputs.head_sha }}
31+
steps:
32+
- name: Validate pull request context
33+
id: context
34+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
35+
env:
36+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
37+
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
38+
INPUT_PR_NUMBER: ${{ inputs.pr_number }}
39+
WORKFLOW_REF: ${{ github.ref }}
40+
with:
41+
script: |
42+
const rawPrNumber = context.eventName === 'workflow_dispatch'
43+
? process.env.INPUT_PR_NUMBER
44+
: process.env.EVENT_PR_NUMBER;
45+
if (!/^[1-9][0-9]*$/.test(rawPrNumber || '')) {
46+
core.setFailed('Pull request number must contain ASCII digits only.');
47+
return;
48+
}
49+
50+
if (context.eventName === 'workflow_dispatch') {
51+
const defaultRef = `refs/heads/${process.env.DEFAULT_BRANCH}`;
52+
if (process.env.WORKFLOW_REF !== defaultRef) {
53+
core.setFailed(`Run manual previews from ${defaultRef}.`);
54+
return;
55+
}
56+
}
57+
58+
const prNumber = Number(rawPrNumber);
59+
if (!Number.isSafeInteger(prNumber)) {
60+
core.setFailed('Pull request number is outside the supported range.');
61+
return;
62+
}
63+
const {data: pullRequest} = await github.rest.pulls.get({
64+
owner: context.repo.owner,
65+
repo: context.repo.repo,
66+
pull_number: prNumber,
67+
});
68+
if (pullRequest.state !== 'open') {
69+
core.setFailed(`Pull request #${prNumber} is not open.`);
70+
return;
71+
}
72+
if (pullRequest.head.repo?.full_name !== `${context.repo.owner}/${context.repo.repo}`) {
73+
core.setFailed('Preview publication is limited to branches in this repository.');
74+
return;
75+
}
76+
77+
core.setOutput('pr_number', String(prNumber));
78+
core.setOutput('head_sha', pullRequest.head.sha);
79+
80+
build:
81+
needs: context
82+
runs-on: ubuntu-latest
2483
timeout-minutes: 10
2584
permissions:
2685
contents: read
2786
outputs:
2887
preview_version: ${{ steps.version.outputs.preview_version }}
29-
pr_number: ${{ steps.context.outputs.pr_number }}
3088
steps:
3189
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3290
with:
33-
ref: ${{ github.event.pull_request.head.sha || format('refs/pull/{0}/head', inputs.pr_number) }}
91+
ref: ${{ needs.context.outputs.head_sha }}
3492
fetch-depth: 0
3593
persist-credentials: false
3694

@@ -44,13 +102,6 @@ jobs:
44102
- name: Install distribution validator
45103
run: python -m pip install "twine>=4.0.0"
46104

47-
- name: Record preview context
48-
id: context
49-
env:
50-
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
51-
INPUT_PR_NUMBER: ${{ inputs.pr_number }}
52-
run: echo "pr_number=${EVENT_PR_NUMBER:-$INPUT_PR_NUMBER}" >> "$GITHUB_OUTPUT"
53-
54105
- name: Inject deterministic preview version
55106
env:
56107
PREVIEW_ID: ${{ github.run_id }}
@@ -84,7 +135,7 @@ jobs:
84135
retention-days: 14
85136

86137
publish:
87-
needs: build
138+
needs: [context, build]
88139
runs-on: ubuntu-latest
89140
timeout-minutes: 10
90141
permissions:
@@ -108,7 +159,7 @@ jobs:
108159
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
109160
env:
110161
PREVIEW_VERSION: ${{ needs.build.outputs.preview_version }}
111-
PR_NUMBER: ${{ needs.build.outputs.pr_number }}
162+
PR_NUMBER: ${{ needs.context.outputs.pr_number }}
112163
with:
113164
script: |
114165
const marker = '<!-- socketdev-pr-preview -->';

.hooks/sync_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ def read_preview_id():
135135
print("❌ `--preview-id` requires a numeric value.")
136136
sys.exit(1)
137137

138-
if not preview_id.isdigit():
139-
print("❌ `--preview-id` must contain digits only.")
138+
if not preview_id.isascii() or not preview_id.isdigit():
139+
print("❌ `--preview-id` must contain ASCII digits only.")
140140
sys.exit(1)
141141
return preview_id
142142

CONTRIBUTING.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ complete local check is:
1616

1717
```bash
1818
python -m pytest
19-
ruff check .
20-
ruff format --check .
2119
hatch build
2220
python -m twine check dist/*
2321
```
@@ -36,14 +34,16 @@ that is trusted to run with the repository's publishing permissions.
3634
For a pull request from this repository, apply the `publish-preview` label. The
3735
`Publish PR Preview` workflow will build and validate a uniquely versioned
3836
`socketdev` prerelease, publish it to TestPyPI, and add or update a pull request
39-
comment with the exact version and installation command. Label-triggered
40-
publication is skipped for pull requests from forks.
37+
comment with the exact version and installation command. Both label-triggered
38+
and manually dispatched previews are limited to open pull requests whose
39+
branches belong to this repository.
4140

4241
The workflow reacts when the label is added; pushing another commit while the
4342
label remains on the pull request does not publish a new preview. To publish the
4443
new pull request head or retry a failed publication, remove `publish-preview`
4544
and apply it again.
4645

47-
Maintainers can also open **Actions > Publish PR Preview > Run workflow** and
48-
enter the pull request number. Manual dispatch is useful when a label should
49-
remain unchanged or a publication needs to be retried.
46+
Maintainers can also open **Actions > Publish PR Preview > Run workflow**, run
47+
it from the repository's default branch, and enter the pull request number.
48+
Manual dispatch is useful when a label should remain unchanged or a
49+
publication needs to be retried.

0 commit comments

Comments
 (0)