Skip to content

Commit 04b0226

Browse files
committed
fix: use pnpm staged publishing
1 parent 9ce6cb0 commit 04b0226

2 files changed

Lines changed: 92 additions & 41 deletions

File tree

.github/scripts/stage-packages.mjs

Lines changed: 87 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,46 @@
11
#!/usr/bin/env node
2-
import { spawnSync } from "node:child_process";
3-
import { existsSync, readFileSync, readdirSync } from "node:fs";
4-
import { dirname, join, relative } from "node:path";
2+
import { spawnSync } from 'node:child_process';
3+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
4+
import { dirname, join, relative } from 'node:path';
55

66
const root = process.cwd();
7-
const config = JSON.parse(readFileSync(join(root, ".changeset/config.json"), "utf8"));
7+
const config = JSON.parse(
8+
readFileSync(join(root, '.changeset/config.json'), 'utf8')
9+
);
810
const ignored = new Set(config.ignore || []);
9-
const access = config.access || "public";
11+
const access = config.access || 'public';
1012

1113
function readJson(path) {
12-
return JSON.parse(readFileSync(path, "utf8"));
14+
return JSON.parse(readFileSync(path, 'utf8'));
1315
}
1416

1517
function packageJsonPathsFromPnpmWorkspace() {
16-
const workspace = join(root, "pnpm-workspace.yaml");
18+
const workspace = join(root, 'pnpm-workspace.yaml');
1719
if (!existsSync(workspace)) return [];
1820

1921
const patterns = [];
20-
for (const rawLine of readFileSync(workspace, "utf8").split(/\r?\n/)) {
22+
for (const rawLine of readFileSync(workspace, 'utf8').split(/\r?\n/)) {
2123
const line = rawLine.trim();
22-
if (!line.startsWith("- ")) continue;
23-
const pattern = line.slice(2).replace(/^['\"]|['\"]$/g, "");
24-
if (!pattern || pattern.startsWith("!")) continue;
24+
if (!line.startsWith('- ')) continue;
25+
const pattern = line.slice(2).replace(/^['\"]|['\"]$/g, '');
26+
if (!pattern || pattern.startsWith('!')) continue;
2527
patterns.push(pattern);
2628
}
2729

2830
const paths = [];
2931
for (const pattern of patterns) {
30-
if (pattern.endsWith("/*")) {
32+
if (pattern.endsWith('/*')) {
3133
const dir = join(root, pattern.slice(0, -2));
3234
if (!existsSync(dir)) continue;
3335
for (const entry of readdirSync(dir, { withFileTypes: true })) {
34-
if (entry.isDirectory()) paths.push(join(dir, entry.name, "package.json"));
36+
if (entry.isDirectory())
37+
paths.push(join(dir, entry.name, 'package.json'));
3538
}
36-
} else if (pattern.endsWith("/**")) {
39+
} else if (pattern.endsWith('/**')) {
3740
// These workspaces are docs/examples and not published packages in this release flow.
3841
continue;
3942
} else {
40-
paths.push(join(root, pattern, "package.json"));
43+
paths.push(join(root, pattern, 'package.json'));
4144
}
4245
}
4346

@@ -46,19 +49,24 @@ function packageJsonPathsFromPnpmWorkspace() {
4649

4750
function packageJsonPaths() {
4851
const paths = new Set(packageJsonPathsFromPnpmWorkspace());
49-
paths.add(join(root, "package.json"));
52+
paths.add(join(root, 'package.json'));
5053
return [...paths].filter(existsSync);
5154
}
5255

5356
function versionExists(name, version) {
54-
const result = spawnSync("npm", ["view", `${name}@${version}`, "version", "--json"], {
55-
encoding: "utf8",
56-
stdio: ["ignore", "pipe", "pipe"],
57-
});
57+
const result = spawnSync(
58+
'npm',
59+
['view', `${name}@${version}`, 'version', '--json'],
60+
{
61+
encoding: 'utf8',
62+
stdio: ['ignore', 'pipe', 'pipe'],
63+
}
64+
);
5865

5966
if (result.status === 0) return true;
6067
const output = `${result.stdout}\n${result.stderr}`;
61-
if (output.includes("E404") || output.includes("No match found")) return false;
68+
if (output.includes('E404') || output.includes('No match found'))
69+
return false;
6270

6371
process.stdout.write(result.stdout);
6472
process.stderr.write(result.stderr);
@@ -67,13 +75,48 @@ function versionExists(name, version) {
6775

6876
function distTag(version) {
6977
const prerelease = version.match(/^[^-]+-([0-9A-Za-z-]+)/);
70-
return prerelease ? prerelease[1] : "latest";
78+
return prerelease ? prerelease[1] : 'latest';
79+
}
80+
81+
function runGit(args) {
82+
const result = spawnSync('git', args, {
83+
cwd: root,
84+
encoding: 'utf8',
85+
stdio: ['ignore', 'pipe', 'pipe'],
86+
});
87+
process.stdout.write(result.stdout);
88+
process.stderr.write(result.stderr);
89+
if (result.status !== 0) process.exit(result.status || 1);
90+
}
91+
92+
function hasLocalGitTag(tagName) {
93+
const result = spawnSync(
94+
'git',
95+
['rev-parse', '--verify', '--quiet', `refs/tags/${tagName}`],
96+
{
97+
cwd: root,
98+
stdio: 'ignore',
99+
}
100+
);
101+
return result.status === 0;
102+
}
103+
104+
function createGitTag(tagName) {
105+
if (hasLocalGitTag(tagName)) {
106+
console.log(`Git tag ${tagName} already exists locally.`);
107+
} else {
108+
runGit(['tag', tagName, '-m', tagName]);
109+
}
110+
111+
// changesets/action parses this line, then pushes the tag and creates the GitHub release.
112+
console.log(`New tag: ${tagName}`);
71113
}
72114

73115
const staged = [];
74116
for (const packageJsonPath of packageJsonPaths()) {
75117
const pkg = readJson(packageJsonPath);
76-
if (!pkg.name || !pkg.version || pkg.private || ignored.has(pkg.name)) continue;
118+
if (!pkg.name || !pkg.version || pkg.private || ignored.has(pkg.name))
119+
continue;
77120
if (versionExists(pkg.name, pkg.version)) {
78121
console.log(`Skipping ${pkg.name}@${pkg.version}; already published.`);
79122
continue;
@@ -82,42 +125,49 @@ for (const packageJsonPath of packageJsonPaths()) {
82125
const packageDir = dirname(packageJsonPath);
83126
const tag = distTag(pkg.version);
84127
const args = [
85-
"stage",
86-
"publish",
128+
'stage',
129+
'publish',
87130
packageDir,
88-
"--provenance",
89-
"--access",
131+
'--provenance',
132+
'--access',
90133
pkg.publishConfig?.access || access,
91-
"--tag",
134+
'--tag',
92135
tag,
93-
"--json",
136+
'--json',
94137
];
95138

96139
console.log(`Staging ${pkg.name}@${pkg.version} with dist-tag ${tag}...`);
97-
const result = spawnSync("npm", args, {
140+
const result = spawnSync('pnpm', args, {
98141
cwd: root,
99-
encoding: "utf8",
100-
stdio: ["ignore", "pipe", "pipe"],
142+
encoding: 'utf8',
143+
stdio: ['ignore', 'pipe', 'pipe'],
101144
});
102145
process.stdout.write(result.stdout);
103146
process.stderr.write(result.stderr);
104147
if (result.status !== 0) process.exit(result.status || 1);
105148

106149
const stageId = result.stdout.match(/"stageId"\s*:\s*"([^"]+)"/)?.[1];
150+
const tagName = `${pkg.name}@${pkg.version}`;
151+
createGitTag(tagName);
152+
107153
staged.push({
108154
name: pkg.name,
109155
version: pkg.version,
110-
path: relative(root, packageDir) || ".",
156+
path: relative(root, packageDir) || '.',
111157
stageId,
112158
});
113159
}
114160

115161
if (staged.length === 0) {
116-
console.log("No unpublished packages to stage.");
162+
console.log('No unpublished packages to stage.');
117163
} else {
118-
console.log("Staged packages:");
164+
console.log('Staged packages:');
119165
for (const pkg of staged) {
120-
console.log(`- ${pkg.name}@${pkg.version}${pkg.stageId ? ` (${pkg.stageId})` : ""}`);
166+
console.log(
167+
`- ${pkg.name}@${pkg.version}${pkg.stageId ? ` (${pkg.stageId})` : ''}`
168+
);
121169
}
122-
console.log("Approve staged packages with `npm stage approve <stage-id>` after review.");
170+
console.log(
171+
'Approve staged packages with `npm stage approve <stage-id>` after review.'
172+
);
123173
}

.github/workflows/release.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ jobs:
3939
- name: Setup pnpm
4040
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3
4141
with:
42-
version: 11.0.0
42+
version: 11.3.0
4343
run_install: false
4444

4545
- name: Install Dependencies
46-
run: pnpm install --frozen-lockfile false
46+
run: pnpm install --frozen-lockfile
4747

4848
- name: Create Release Pull Request
4949
id: changesets
@@ -85,15 +85,16 @@ jobs:
8585
- name: Setup pnpm
8686
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3
8787
with:
88-
version: 11.0.0
88+
version: 11.3.0
8989
run_install: false
9090

9191
- name: Install Dependencies
92-
run: pnpm install --frozen-lockfile false
92+
run: pnpm install --frozen-lockfile
9393

9494
- name: Publish packages
9595
uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3
9696
with:
9797
publish: node .github/scripts/stage-packages.mjs
98+
createGithubReleases: true
9899
env:
99100
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)