Skip to content

Commit 06991c0

Browse files
committed
fix: address release workflow review findings
1 parent b0fe52b commit 06991c0

5 files changed

Lines changed: 28 additions & 7 deletions

File tree

.github/workflows/_native-build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ jobs:
6666
steps:
6767
- name: Checkout
6868
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pinned from v6.0.2
69+
with:
70+
persist-credentials: false
6971

7072
- name: Setup pnpm
7173
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # pinned from v6

.github/workflows/nightly.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pinned from v6.0.2
2424
with:
2525
fetch-depth: 0
26+
persist-credentials: false
2627

2728
- name: Setup pnpm
2829
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # pinned from v6
@@ -70,6 +71,8 @@ jobs:
7071
steps:
7172
- name: Checkout
7273
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pinned from v6.0.2
74+
with:
75+
persist-credentials: false
7376

7477
- name: Setup Node.js
7578
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # pinned from v7.0.0

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ jobs:
311311
with:
312312
app-id: ${{ vars.RELEASE_BOT_APP_ID }}
313313
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
314+
permission-contents: write
314315

315316
- name: Cut required desktop tag
316317
env:

scripts/release/release-status.test.mjs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,30 @@ function json(body, status = 200) {
3535

3636
function fixtureFetch({ cliAssets = expectedCliAssets } = {}) {
3737
return async (input) => {
38-
const url = String(input);
39-
if (url.includes('registry.npmjs.org')) return json({ latest: '1.3.0' });
40-
if (url.includes('code.pythinker.com')) {
38+
const url = new URL(String(input));
39+
if (url.hostname === 'registry.npmjs.org') return json({ latest: '1.3.0' });
40+
if (url.hostname === 'code.pythinker.com') {
4141
return json({
4242
version: '1.3.0',
4343
platforms: Object.fromEntries(nativeTargets.map((target) => [target, {}])),
4444
});
4545
}
46-
if (url.includes('/pythinker-code/releases/tags/')) {
46+
if (url.hostname === 'api.github.com' && url.pathname.startsWith('/repos/PyModel/pythinker-code/releases/tags/')) {
4747
return json({
4848
tag_name: '@pymodel/pythinker-code@1.3.0',
4949
assets: cliAssets.map((name) => ({ name })),
5050
});
5151
}
52-
if (url.endsWith('/pythinker-desktop-releases/releases/latest')) {
52+
if (url.hostname === 'api.github.com' && url.pathname === '/repos/PyModel/pythinker-desktop-releases/releases/latest') {
5353
return json({ tag_name: 'v0.2.1', assets: [{ name: 'latest.yml' }] });
5454
}
55-
if (url.includes('open-vsx.org')) {
55+
if (url.hostname === 'open-vsx.org') {
5656
return json({
5757
version: '0.9.5',
5858
downloads: Object.fromEntries(nativeTargets.map((target) => [target, `https://${target}`])),
5959
});
6060
}
61-
if (url.includes('marketplace.visualstudio.com')) {
61+
if (url.hostname === 'marketplace.visualstudio.com') {
6262
return json({
6363
results: [{
6464
extensions: [{
@@ -71,6 +71,14 @@ function fixtureFetch({ cliAssets = expectedCliAssets } = {}) {
7171
};
7272
}
7373

74+
void test('fixture routing rejects trusted hostnames outside the URL host', async () => {
75+
const response = await fixtureFetch()(
76+
'https://example.test/registry.npmjs.org/code.pythinker.com/open-vsx.org/marketplace.visualstudio.com',
77+
);
78+
79+
assert.equal(response.status, 404);
80+
});
81+
7482
void test('reports all live release lanes aligned', async (t) => {
7583
const rootDir = await fixtureRoot(t);
7684
const result = await collectReleaseStatus({ rootDir, fetchImpl: fixtureFetch() });

scripts/release/release-workflows.test.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ const read = (path) => readFileSync(resolve(root, path), 'utf8');
88

99
void test('release workflow uses full push-boundary lane signals and isolated jobs', () => {
1010
const workflow = read('.github/workflows/release.yml');
11+
const desktopJob = workflow.slice(
12+
workflow.indexOf(' cut-desktop-tag:'),
13+
workflow.indexOf(' publish-vscode-extension:'),
14+
);
1115
assert.match(workflow, /github\.event\.before/u);
1216
assert.match(workflow, /pythinker_native_release:.*cli_version_bumped/u);
1317
assert.match(workflow, /^ cut-desktop-tag:/mu);
1418
assert.match(workflow, /uses: \.\/\.github\/workflows\/vscode-release\.yml/u);
1519
assert.match(workflow, /^ release-summary:/mu);
1620
assert.doesNotMatch(workflow, /HEAD\^:apps\/vscode\/package\.json/u);
21+
assert.match(desktopJob, /permission-contents: write/u);
1722
assert.match(workflow, /pythinker_release_tag: \$\{\{ steps\.pythinker-release\.outputs\.tag \|\|/u);
1823
assert.match(workflow, /APPLE_CERTIFICATE_P12: \$\{\{ secrets\.MAC_CSC_LINK \}\}/u);
1924
assert.match(workflow, /APPLE_NOTARIZATION_KEY_P8: \$\{\{ secrets\.APPLE_API_KEY_P8 \}\}/u);
@@ -35,6 +40,7 @@ void test('native releases fail without requested signing and attest each zip',
3540
assert.match(workflow, /actions\/attest@[0-9a-f]{40}/u);
3641
assert.match(workflow, /if-no-files-found: error/u);
3742
assert.doesNotMatch(workflow, /uses: actions\/(checkout|setup-node|upload-artifact)@v\d+/u);
43+
assert.equal(workflow.match(/persist-credentials: false/gu)?.length, 1);
3844
assert.match(manualWorkflow, /id-token: write/u);
3945
assert.match(manualWorkflow, /attestations: write/u);
4046
assert.match(manualWorkflow, /artifact-metadata: write/u);
@@ -46,4 +52,5 @@ void test('nightly reconciliation maintains one release drift issue', () => {
4652
assert.match(workflow, /Release lane drift detected/u);
4753
assert.match(workflow, /issues: write/u);
4854
assert.doesNotMatch(workflow, /uses: actions\/(checkout|setup-node)@v\d+/u);
55+
assert.equal(workflow.match(/persist-credentials: false/gu)?.length, 2);
4956
});

0 commit comments

Comments
 (0)