Skip to content

Commit 2380378

Browse files
committed
ci: harden the desktop changeset linker
Accept quoted bump values, keep the CLI entry's indentation on the inserted desktop line, and resolve the main-module guard through pathToFileURL so a path with URL characters still runs the linker.
1 parent fdd00ed commit 2380378

2 files changed

Lines changed: 21 additions & 4 deletions

File tree

scripts/release/link-desktop-changesets.mjs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515

1616
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
1717
import { join } from 'node:path';
18+
import { pathToFileURL } from 'node:url';
1819

1920
export const CLI_PACKAGE = '@pymodel/pythinker-code';
2021
export const DESKTOP_PACKAGE = '@pymodel/pythinker-desktop';
2122

22-
const FRONTMATTER_LINE = /^(\s*)(?:"([^"]+)"|'([^']+)'|([^:'"]+?))\s*:\s*([A-Za-z]+)\s*$/u;
23+
const FRONTMATTER_LINE = /^(\s*)(?:"([^"]+)"|'([^']+)'|([^:'"]+?))\s*:\s*(?:"([A-Za-z]+)"|'([A-Za-z]+)'|([A-Za-z]+))\s*$/u;
2324

2425
/**
2526
* Add the desktop package to one changeset when it names the CLI alone.
@@ -36,19 +37,21 @@ export function linkDesktop(source) {
3637

3738
let cliLine = -1;
3839
let cliLevel = null;
40+
let cliIndent = '';
3941
for (const [index, line] of lines.entries()) {
4042
const match = FRONTMATTER_LINE.exec(line);
4143
if (match === null) continue;
4244
const name = match[2] ?? match[3] ?? match[4];
4345
if (name === DESKTOP_PACKAGE) return null;
4446
if (name === CLI_PACKAGE) {
4547
cliLine = index;
46-
cliLevel = match[5].toLowerCase();
48+
cliLevel = (match[5] ?? match[6] ?? match[7]).toLowerCase();
49+
cliIndent = match[1];
4750
}
4851
}
4952
if (cliLine === -1) return null;
5053

51-
lines.splice(cliLine + 1, 0, `"${DESKTOP_PACKAGE}": ${cliLevel}`);
54+
lines.splice(cliLine + 1, 0, `${cliIndent}"${DESKTOP_PACKAGE}": ${cliLevel}`);
5255
return `---\n${lines.join('\n')}${normalized.slice(end + 1)}`;
5356
}
5457

@@ -71,7 +74,7 @@ export function linkDesktopChangesets(dir) {
7174
return changed;
7275
}
7376

74-
if (process.argv[1] !== undefined && import.meta.url === new URL(`file://${process.argv[1]}`).href) {
77+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
7578
const changed = linkDesktopChangesets(process.argv[2] ?? '.changeset');
7679
for (const name of changed) console.log(`linked desktop bump: ${name}`);
7780
console.log(`${changed.length} changeset(s) now bump ${DESKTOP_PACKAGE} alongside ${CLI_PACKAGE}.`);

scripts/release/link-desktop-changesets.test.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ void test('leaves a changeset that already names desktop alone', () => {
2121
assert.equal(linkDesktop(linkDesktop(cliOnly)), null);
2222
});
2323

24+
void test('accepts quoted bump values and does not duplicate the desktop entry', () => {
25+
const quoted = '---\n"@pymodel/pythinker-code": "minor"\n---\n\nx\n';
26+
assert.equal(linkDesktop(quoted), '---\n"@pymodel/pythinker-code": "minor"\n"@pymodel/pythinker-desktop": minor\n---\n\nx\n');
27+
assert.equal(linkDesktop(linkDesktop(quoted)), null);
28+
assert.equal(linkDesktop("---\n'@pymodel/pythinker-desktop': 'patch'\n\"@pymodel/pythinker-code\": 'patch'\n---\n\nx\n"), null);
29+
});
30+
31+
void test('reuses the CLI entry indentation', () => {
32+
assert.equal(
33+
linkDesktop('---\n "@pymodel/pythinker-code": patch\n---\n\nx\n'),
34+
'---\n "@pymodel/pythinker-code": patch\n "@pymodel/pythinker-desktop": patch\n---\n\nx\n',
35+
);
36+
});
37+
2438
void test('ignores changesets that do not name the CLI', () => {
2539
assert.equal(linkDesktop('---\n"@pymodel/klient": patch\n---\n\nx\n'), null);
2640
assert.equal(linkDesktop('no frontmatter\n'), null);

0 commit comments

Comments
 (0)