@pubm/plugin-brew 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +313 -0
- package/package.json +36 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/brew-core.ts
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
|
|
9
|
+
// src/formula.ts
|
|
10
|
+
function toClassName(name) {
|
|
11
|
+
return name.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
12
|
+
}
|
|
13
|
+
function findAsset(assets, platform) {
|
|
14
|
+
return assets.find((a) => a.platform === platform);
|
|
15
|
+
}
|
|
16
|
+
function generateFormula(opts) {
|
|
17
|
+
const className = toClassName(opts.name);
|
|
18
|
+
const darwinArm64 = findAsset(opts.assets, "darwin-arm64");
|
|
19
|
+
const darwinX64 = findAsset(opts.assets, "darwin-x64");
|
|
20
|
+
const linuxArm64 = findAsset(opts.assets, "linux-arm64");
|
|
21
|
+
const linuxX64 = findAsset(opts.assets, "linux-x64");
|
|
22
|
+
return `class ${className} < Formula
|
|
23
|
+
desc "${opts.desc}"
|
|
24
|
+
homepage "${opts.homepage}"
|
|
25
|
+
version "${opts.version}"
|
|
26
|
+
license "${opts.license}"
|
|
27
|
+
|
|
28
|
+
on_macos do
|
|
29
|
+
if Hardware::CPU.arm?
|
|
30
|
+
url "${darwinArm64?.url ?? "PLACEHOLDER"}"
|
|
31
|
+
sha256 "${darwinArm64?.sha256 ?? "PLACEHOLDER"}"
|
|
32
|
+
elsif Hardware::CPU.intel?
|
|
33
|
+
url "${darwinX64?.url ?? "PLACEHOLDER"}"
|
|
34
|
+
sha256 "${darwinX64?.sha256 ?? "PLACEHOLDER"}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
on_linux do
|
|
39
|
+
if Hardware::CPU.arm?
|
|
40
|
+
url "${linuxArm64?.url ?? "PLACEHOLDER"}"
|
|
41
|
+
sha256 "${linuxArm64?.sha256 ?? "PLACEHOLDER"}"
|
|
42
|
+
elsif Hardware::CPU.intel?
|
|
43
|
+
url "${linuxX64?.url ?? "PLACEHOLDER"}"
|
|
44
|
+
sha256 "${linuxX64?.sha256 ?? "PLACEHOLDER"}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def install
|
|
49
|
+
bin.install "${opts.name}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test do
|
|
53
|
+
system "#{bin}/${opts.name}", "--version"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
function updateFormula(content, version, assets) {
|
|
59
|
+
let updated = content.replace(/version\s+"[^"]+"/, `version "${version}"`);
|
|
60
|
+
for (const asset of assets) {
|
|
61
|
+
const platformPatterns = {
|
|
62
|
+
"darwin-arm64": { os: "on_macos", cpu: "arm" },
|
|
63
|
+
"darwin-x64": { os: "on_macos", cpu: "intel" },
|
|
64
|
+
"linux-arm64": { os: "on_linux", cpu: "arm" },
|
|
65
|
+
"linux-x64": { os: "on_linux", cpu: "intel" }
|
|
66
|
+
};
|
|
67
|
+
const pattern = platformPatterns[asset.platform];
|
|
68
|
+
if (!pattern)
|
|
69
|
+
continue;
|
|
70
|
+
const osBlockRegex = new RegExp(`(${pattern.os}\\s+do[\\s\\S]*?Hardware::CPU\\.${pattern.cpu}\\?\\s*\\n\\s*)url\\s+"[^"]+"(\\s*\\n\\s*)sha256\\s+"[^"]+"`, "m");
|
|
71
|
+
updated = updated.replace(osBlockRegex, `$1url "${asset.url}"$2sha256 "${asset.sha256}"`);
|
|
72
|
+
}
|
|
73
|
+
return updated;
|
|
74
|
+
}
|
|
75
|
+
function mapReleaseAssets(assets) {
|
|
76
|
+
const platformMap = {
|
|
77
|
+
"darwin-arm64": "darwin-arm64",
|
|
78
|
+
"darwin-x64": "darwin-x64",
|
|
79
|
+
"linux-arm64": "linux-arm64",
|
|
80
|
+
"linux-x64": "linux-x64"
|
|
81
|
+
};
|
|
82
|
+
const result = [];
|
|
83
|
+
for (const asset of assets) {
|
|
84
|
+
for (const [key, platform] of Object.entries(platformMap)) {
|
|
85
|
+
if (asset.name.includes(key)) {
|
|
86
|
+
result.push({ platform, url: asset.url, sha256: asset.sha256 });
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/git-identity.ts
|
|
95
|
+
import { execSync } from "node:child_process";
|
|
96
|
+
function ensureGitIdentity(cwd) {
|
|
97
|
+
const opts = cwd ? { cwd, encoding: "utf-8" } : { encoding: "utf-8" };
|
|
98
|
+
try {
|
|
99
|
+
execSync("git config user.name", { ...opts, stdio: "pipe" });
|
|
100
|
+
} catch {
|
|
101
|
+
execSync('git config user.name "pubm[bot]"', opts);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
execSync("git config user.email", { ...opts, stdio: "pipe" });
|
|
105
|
+
} catch {
|
|
106
|
+
execSync('git config user.email "pubm[bot]@users.noreply.github.com"', opts);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/brew-core.ts
|
|
111
|
+
function brewCore(options) {
|
|
112
|
+
return {
|
|
113
|
+
name: "@pubm/plugin-brew-core",
|
|
114
|
+
commands: [
|
|
115
|
+
{
|
|
116
|
+
name: "brew",
|
|
117
|
+
description: "Manage Homebrew formula",
|
|
118
|
+
subcommands: [
|
|
119
|
+
{
|
|
120
|
+
name: "init-core",
|
|
121
|
+
description: "Generate homebrew-core formula",
|
|
122
|
+
action: async () => {
|
|
123
|
+
const cwd = process.cwd();
|
|
124
|
+
const pkgPath = resolve(cwd, "package.json");
|
|
125
|
+
const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, "utf-8")) : {};
|
|
126
|
+
const name = pkg.name?.replace(/^@[^/]+\//, "") ?? "my-tool";
|
|
127
|
+
const desc = pkg.description ?? "A CLI tool";
|
|
128
|
+
const homepage = pkg.homepage ?? `https://github.com/${name}`;
|
|
129
|
+
const license = pkg.license ?? "MIT";
|
|
130
|
+
const content = generateFormula({
|
|
131
|
+
name,
|
|
132
|
+
desc,
|
|
133
|
+
homepage,
|
|
134
|
+
license,
|
|
135
|
+
version: pkg.version ?? "0.0.0",
|
|
136
|
+
assets: []
|
|
137
|
+
});
|
|
138
|
+
const formulaPath = resolve(cwd, options.formula);
|
|
139
|
+
mkdirSync(dirname(formulaPath), { recursive: true });
|
|
140
|
+
writeFileSync(formulaPath, content);
|
|
141
|
+
console.log(`homebrew-core formula generated at ${options.formula}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
hooks: {
|
|
148
|
+
afterRelease: async (_ctx, releaseCtx) => {
|
|
149
|
+
if (options.packageName && releaseCtx.packageName !== options.packageName) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
153
|
+
const cwd = process.cwd();
|
|
154
|
+
const pkgPath = resolve(cwd, "package.json");
|
|
155
|
+
const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, "utf-8")) : {};
|
|
156
|
+
const name = pkg.name?.replace(/^@[^/]+\//, "") ?? "my-tool";
|
|
157
|
+
const formulaAssets = mapReleaseAssets(releaseCtx.assets);
|
|
158
|
+
try {
|
|
159
|
+
execSync2("gh repo fork homebrew/homebrew-core --clone=false", {
|
|
160
|
+
stdio: "pipe"
|
|
161
|
+
});
|
|
162
|
+
} catch {}
|
|
163
|
+
const username = execSync2("gh api user --jq .login", {
|
|
164
|
+
encoding: "utf-8"
|
|
165
|
+
}).trim();
|
|
166
|
+
const tmpDir = join(tmpdir(), `pubm-brew-core-${Date.now()}`);
|
|
167
|
+
execSync2(`git clone --depth 1 https://github.com/${username}/homebrew-core.git ${tmpDir}`, { stdio: "inherit" });
|
|
168
|
+
ensureGitIdentity(tmpDir);
|
|
169
|
+
const formulaPath = join(tmpDir, "Formula", `${name}.rb`);
|
|
170
|
+
let content;
|
|
171
|
+
if (existsSync(formulaPath)) {
|
|
172
|
+
const existing = readFileSync(formulaPath, "utf-8");
|
|
173
|
+
content = updateFormula(existing, releaseCtx.version, formulaAssets);
|
|
174
|
+
} else {
|
|
175
|
+
content = generateFormula({
|
|
176
|
+
name,
|
|
177
|
+
desc: pkg.description ?? "A CLI tool",
|
|
178
|
+
homepage: pkg.homepage ?? "",
|
|
179
|
+
license: pkg.license ?? "MIT",
|
|
180
|
+
version: releaseCtx.version,
|
|
181
|
+
assets: formulaAssets
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
mkdirSync(dirname(formulaPath), { recursive: true });
|
|
185
|
+
writeFileSync(formulaPath, content);
|
|
186
|
+
const branchName = `${name}-${releaseCtx.version}`;
|
|
187
|
+
execSync2([
|
|
188
|
+
`cd ${tmpDir}`,
|
|
189
|
+
`git checkout -b ${branchName}`,
|
|
190
|
+
`git add Formula/${name}.rb`,
|
|
191
|
+
`git commit -m "${name} ${releaseCtx.version}"`,
|
|
192
|
+
`git push origin ${branchName}`
|
|
193
|
+
].join(" && "), { stdio: "inherit" });
|
|
194
|
+
execSync2([
|
|
195
|
+
`cd ${tmpDir}`,
|
|
196
|
+
`gh pr create --repo homebrew/homebrew-core --title "${name} ${releaseCtx.version}" --body "Update ${name} formula to version ${releaseCtx.version}"`
|
|
197
|
+
].join(" && "), { stdio: "inherit" });
|
|
198
|
+
console.log(`PR created to homebrew/homebrew-core for ${name} ${releaseCtx.version}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// src/brew-tap.ts
|
|
204
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
205
|
+
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
206
|
+
function brewTap(options) {
|
|
207
|
+
return {
|
|
208
|
+
name: "@pubm/plugin-brew-tap",
|
|
209
|
+
commands: [
|
|
210
|
+
{
|
|
211
|
+
name: "brew",
|
|
212
|
+
description: "Manage Homebrew formula",
|
|
213
|
+
subcommands: [
|
|
214
|
+
{
|
|
215
|
+
name: "init",
|
|
216
|
+
description: "Generate Homebrew formula template",
|
|
217
|
+
action: async () => {
|
|
218
|
+
const cwd = process.cwd();
|
|
219
|
+
const pkgPath = resolve2(cwd, "package.json");
|
|
220
|
+
const pkg = existsSync2(pkgPath) ? JSON.parse(readFileSync2(pkgPath, "utf-8")) : {};
|
|
221
|
+
const name = pkg.name?.replace(/^@[^/]+\//, "") ?? "my-tool";
|
|
222
|
+
const desc = pkg.description ?? "A CLI tool";
|
|
223
|
+
const homepage = pkg.homepage ?? `https://github.com/${name}`;
|
|
224
|
+
const license = pkg.license ?? "MIT";
|
|
225
|
+
const content = generateFormula({
|
|
226
|
+
name,
|
|
227
|
+
desc,
|
|
228
|
+
homepage,
|
|
229
|
+
license,
|
|
230
|
+
version: pkg.version ?? "0.0.0",
|
|
231
|
+
assets: []
|
|
232
|
+
});
|
|
233
|
+
const formulaPath = resolve2(cwd, options.formula);
|
|
234
|
+
mkdirSync2(dirname2(formulaPath), { recursive: true });
|
|
235
|
+
writeFileSync2(formulaPath, content);
|
|
236
|
+
console.log(`Formula generated at ${options.formula}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
hooks: {
|
|
243
|
+
afterRelease: async (_ctx, releaseCtx) => {
|
|
244
|
+
if (options.packageName && releaseCtx.packageName !== options.packageName) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const formulaAssets = mapReleaseAssets(releaseCtx.assets);
|
|
248
|
+
const formulaPath = resolve2(process.cwd(), options.formula);
|
|
249
|
+
let content;
|
|
250
|
+
if (existsSync2(formulaPath)) {
|
|
251
|
+
const existing = readFileSync2(formulaPath, "utf-8");
|
|
252
|
+
content = updateFormula(existing, releaseCtx.version, formulaAssets);
|
|
253
|
+
} else {
|
|
254
|
+
const cwd = process.cwd();
|
|
255
|
+
const pkgPath = resolve2(cwd, "package.json");
|
|
256
|
+
const pkg = existsSync2(pkgPath) ? JSON.parse(readFileSync2(pkgPath, "utf-8")) : {};
|
|
257
|
+
const name = pkg.name?.replace(/^@[^/]+\//, "") ?? "my-tool";
|
|
258
|
+
content = generateFormula({
|
|
259
|
+
name,
|
|
260
|
+
desc: pkg.description ?? "A CLI tool",
|
|
261
|
+
homepage: pkg.homepage ?? "",
|
|
262
|
+
license: pkg.license ?? "MIT",
|
|
263
|
+
version: releaseCtx.version,
|
|
264
|
+
assets: formulaAssets
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
mkdirSync2(dirname2(formulaPath), { recursive: true });
|
|
268
|
+
writeFileSync2(formulaPath, content);
|
|
269
|
+
console.log(`Formula updated at ${options.formula}`);
|
|
270
|
+
if (!options.repo) {
|
|
271
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
272
|
+
ensureGitIdentity();
|
|
273
|
+
execSync2(`git add ${formulaPath}`, { stdio: "inherit" });
|
|
274
|
+
execSync2(`git commit -m "chore(brew): update formula to ${releaseCtx.version}"`, { stdio: "inherit" });
|
|
275
|
+
try {
|
|
276
|
+
execSync2("git push", { stdio: "inherit" });
|
|
277
|
+
} catch {
|
|
278
|
+
const branch = `pubm/brew-formula-v${releaseCtx.version}`;
|
|
279
|
+
execSync2(`git checkout -b ${branch}`, { stdio: "inherit" });
|
|
280
|
+
execSync2(`git push origin ${branch}`, { stdio: "inherit" });
|
|
281
|
+
execSync2(`gh pr create --title "chore(brew): update formula to ${releaseCtx.version}" --body "Automated formula update by pubm"`, { stdio: "inherit" });
|
|
282
|
+
console.log(`Created PR on branch ${branch}`);
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (options.repo) {
|
|
287
|
+
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
288
|
+
const { basename, join: join2 } = await import("node:path");
|
|
289
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
290
|
+
const tmpDir = join2(tmpdir2(), `pubm-brew-tap-${Date.now()}`);
|
|
291
|
+
const formulaFile = basename(formulaPath);
|
|
292
|
+
execSync2(`git clone --depth 1 ${options.repo} ${tmpDir}`, {
|
|
293
|
+
stdio: "inherit"
|
|
294
|
+
});
|
|
295
|
+
ensureGitIdentity(tmpDir);
|
|
296
|
+
const targetDir = join2(tmpDir, "Formula");
|
|
297
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
298
|
+
writeFileSync2(join2(targetDir, formulaFile), content);
|
|
299
|
+
execSync2([
|
|
300
|
+
`cd ${tmpDir}`,
|
|
301
|
+
`git add Formula/${formulaFile}`,
|
|
302
|
+
`git commit -m "Update ${formulaFile} to ${releaseCtx.version}"`,
|
|
303
|
+
"git push"
|
|
304
|
+
].join(" && "), { stdio: "inherit" });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
brewTap,
|
|
312
|
+
brewCore
|
|
313
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pubm/plugin-brew",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "pubm plugin for Homebrew formula publishing workflows",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm && bunx tsc --project tsconfig.build.json",
|
|
19
|
+
"check": "biome check",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "vitest --run --passWithNoTests",
|
|
22
|
+
"coverage": "vitest --run --coverage"
|
|
23
|
+
},
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@pubm/core": ">=0.3.6"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/syi0808/pubm.git",
|
|
34
|
+
"directory": "packages/plugins/plugin-brew"
|
|
35
|
+
}
|
|
36
|
+
}
|