forked from d3ara1n/pi-extensions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpublish.js
More file actions
157 lines (127 loc) · 5.67 KB
/
Copy pathpublish.js
File metadata and controls
157 lines (127 loc) · 5.67 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
149
150
151
152
153
154
155
156
157
#!/usr/bin/env node
/**
* Publish script for pi-extensions monorepo.
*
* Usage:
* node publish.js <package-name> [patch|minor|major]
* node publish.js pi-context-include # auto patch bump
* node publish.js pi-context-include minor # bump minor
*
* Flow:
* 1. Check published version on npm
* 2. Compare with local version
* 3. Auto-bump if needed
* 4. Confirm → git commit + tag + push → npm publish
*/
const { execSync } = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
// ── Helpers ─────────────────────────────────────────────
function run(cmd, options = {}) {
try {
return execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], ...options }).trim();
} catch (e) {
if (options.allowFail) return null;
console.error(`✗ Command failed: ${cmd}`);
console.error(e.stderr?.trim() || e.message);
process.exit(1);
}
}
function info(msg) { console.log(`✓ ${msg}`); }
function warn(msg) { console.log(`⚠ ${msg}`); }
function die(msg) { console.error(`✗ ${msg}`); process.exit(1); }
function bumpVersion(version, type) {
const [major, minor, patch] = version.split(".").map(Number);
if (type === "major") return `${major + 1}.0.0`;
if (type === "minor") return `${major}.${minor + 1}.0`;
return `${major}.${minor}.${patch + 1}`;
}
// ── Args ────────────────────────────────────────────────
const pkgName = process.argv[2];
const bumpType = process.argv[3] || "patch";
if (!pkgName) {
console.log("Usage: node publish.js <package-name> [patch|minor|major]");
console.log("\nAvailable packages:");
const dirs = fs.readdirSync(path.join(__dirname, "packages"), { withFileTypes: true });
for (const d of dirs.filter((d) => d.isDirectory())) {
const pkgJson = path.join(__dirname, "packages", d.name, "package.json");
if (fs.existsSync(pkgJson)) {
const pkg = JSON.parse(fs.readFileSync(pkgJson, "utf-8"));
console.log(` ${d.name.padEnd(24)} ${pkg.description || ""}`);
}
}
process.exit(0);
}
// ── Resolve package ─────────────────────────────────────
const pkgDir = path.join(__dirname, "packages", pkgName);
if (!fs.existsSync(pkgDir)) die(`Package not found: ${pkgDir}`);
const pkgJsonPath = path.join(pkgDir, "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
const fullName = pkg.name;
console.log("");
console.log("────────────────────────────────────────");
console.log(` ${fullName}`);
console.log("────────────────────────────────────────");
console.log("");
// ── 1. Check remote version ─────────────────────────────
const remoteVersion = run(`npm view ${fullName} version 2>/dev/null`, { allowFail: true }) || "0.0.0";
const localVersion = pkg.version;
console.log(` Remote: ${remoteVersion}`);
console.log(` Local: ${localVersion}`);
console.log("");
// ── 2. Determine target version ─────────────────────────
let targetVersion;
if (remoteVersion === "0.0.0") {
targetVersion = localVersion;
info(`First publish, using local version ${targetVersion}`);
} else {
const [rmajor, rminor, rpatch] = remoteVersion.split(".").map(Number);
const [lmajor, lminor, lpatch] = localVersion.split(".").map(Number);
const localHigher = lmajor > rmajor || lminor > rminor || lpatch > rpatch;
if (localHigher) {
targetVersion = localVersion;
info(`Local version is higher, publishing as-is: ${targetVersion}`);
} else {
targetVersion = bumpVersion(remoteVersion, bumpType);
info(`Auto-bumping (${bumpType}): ${remoteVersion} → ${targetVersion}`);
}
}
// ── 3. Confirm ──────────────────────────────────────────
const readline = require("node:readline");
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.question(`\n Publish ${fullName}@${targetVersion}? [Y/n] `, (answer) => {
rl.close();
if (answer.toLowerCase() === "n") {
warn("Cancelled");
process.exit(0);
}
// ── 4. Update package.json version (temporary) ────────
const originalVersion = pkg.version;
pkg.version = targetVersion;
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
info(`Version set to ${targetVersion}`);
const tag = `${fullName}@${targetVersion}`;
// ── 5. npm publish first ──────────────────────────────
try {
execSync(`npm publish -w ${pkgDir} --access public`, { stdio: "inherit" });
info(`Published to npm: ${tag}`);
// ── 6. Git commit + tag + push (only on success) ────
run(`git add ${path.relative(process.cwd(), pkgJsonPath)}`);
run(`git commit -m "release: ${tag}"`, { allowFail: true });
run(`git tag -m "${tag}" ${tag}`);
info(`Git tag: ${tag}`);
run("git push");
run("git push --tags");
info("Git pushed");
console.log("");
info("Done! 🚀");
console.log("");
console.log(` Install: pi install npm:${fullName}`);
console.log("");
} catch (e) {
// Publish failed — revert package.json
pkg.version = originalVersion;
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
die(`Failed to publish: ${e.stderr?.trim() || e.message}`);
}
});