forked from eggjs/egg
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpublish.js
More file actions
148 lines (127 loc) · 4.14 KB
/
publish.js
File metadata and controls
148 lines (127 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env node
/**
* Resilient per-package publish script.
*
* Unlike `pnpm -r publish`, this script:
* - Skips packages that are already published on npm (safe for retries)
* - Publishes each package individually so one failure doesn't block others
* - Retries failed packages once
* - Exits 0 only when all packages are published successfully
*
* Usage:
* node scripts/publish.js --tag=latest [--provenance] [--dry-run]
*/
import { execFileSync } from 'node:child_process';
import path from 'node:path';
import { getPublishablePackages } from './utils.js';
const args = process.argv.slice(2);
const isDryRun = args.includes('--dry-run');
const useProvenance = args.includes('--provenance');
let npmTag = 'latest';
const tagArg = args.find((arg) => arg.startsWith('--tag='));
if (tagArg) {
npmTag = tagArg.split('=')[1];
}
const baseDir = path.join(import.meta.dirname, '..');
const packages = getPublishablePackages(baseDir);
console.log(
`📦 Publishing ${packages.length} packages (tag: ${npmTag}${isDryRun ? ', dry-run' : ''}${useProvenance ? ', provenance' : ''})`,
);
/**
* Check if a specific version of a package is already published on npm.
*/
function isPublished(name, version) {
try {
const result = execFileSync('npm', ['view', `${name}@${version}`, 'version'], {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 15000,
}).trim();
return result === version;
} catch {
// Could be 404 (not published) or network error.
// Either way, we should attempt to publish.
return false;
}
}
/**
* Publish a single package using pnpm --filter (preserves workspace context
* so that workspace: protocol references are properly resolved).
*/
function publishOne(pkg) {
const publishArgs = ['--filter', pkg.name, 'publish', '--no-git-checks', '--access', 'public', '--tag', npmTag];
if (useProvenance) publishArgs.push('--provenance');
if (isDryRun) publishArgs.push('--dry-run');
execFileSync('pnpm', publishArgs, {
cwd: baseDir,
stdio: 'inherit',
env: { ...process.env, NPM_CONFIG_LOGLEVEL: 'verbose' },
timeout: 120000,
});
}
const published = [];
const skipped = [];
const toRetry = [];
for (const pkg of packages) {
const label = `${pkg.name}@${pkg.version}`;
// Skip packages already on npm (safe for retries)
if (!isDryRun && isPublished(pkg.name, pkg.version)) {
console.log(` ⏭️ ${label} already published`);
skipped.push(label);
continue;
}
try {
publishOne(pkg);
console.log(` ✅ ${label}`);
published.push(label);
} catch {
// Double-check: the publish might have actually succeeded
// (e.g. npm returned non-zero but the package landed)
if (!isDryRun && isPublished(pkg.name, pkg.version)) {
console.log(` ⏭️ ${label} already published (confirmed after error)`);
skipped.push(label);
} else {
console.error(` ❌ ${label} failed, will retry`);
toRetry.push(pkg);
}
}
}
// Retry failed packages once
const finalFailed = [];
if (toRetry.length > 0 && !isDryRun) {
console.log(`\n🔄 Retrying ${toRetry.length} failed package(s)...`);
for (const pkg of toRetry) {
const label = `${pkg.name}@${pkg.version}`;
if (isPublished(pkg.name, pkg.version)) {
console.log(` ⏭️ ${label} now published`);
skipped.push(label);
continue;
}
try {
publishOne(pkg);
console.log(` ✅ ${label} (retry)`);
published.push(label);
} catch {
if (isPublished(pkg.name, pkg.version)) {
console.log(` ⏭️ ${label} now published (confirmed after retry error)`);
skipped.push(label);
} else {
console.error(` ❌ ${label} retry failed`);
finalFailed.push(label);
}
}
}
}
// Summary
console.log('\n📊 Publish Summary:');
console.log(` Published: ${published.length}`);
console.log(` Skipped: ${skipped.length}`);
console.log(` Failed: ${finalFailed.length}`);
if (finalFailed.length > 0) {
console.error('\n❌ Failed packages:');
for (const label of finalFailed) {
console.error(` - ${label}`);
}
process.exit(1);
}
console.log('\n✅ All packages published successfully!');