@pubm/plugin-brew 0.4.7 → 0.4.9
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/brew-core.d.ts +1 -1
- package/dist/brew-tap.d.ts +1 -1
- package/dist/index.js +178 -15
- package/package.json +2 -2
package/dist/brew-core.d.ts
CHANGED
package/dist/brew-tap.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { resolvePhases } from "@pubm/core";
|
|
8
9
|
|
|
9
10
|
// src/formula.ts
|
|
10
11
|
function toClassName(name) {
|
|
@@ -204,8 +205,64 @@ function brewCore(options) {
|
|
|
204
205
|
]
|
|
205
206
|
}
|
|
206
207
|
],
|
|
208
|
+
credentials: (ctx) => {
|
|
209
|
+
if (ctx.options.mode !== "ci")
|
|
210
|
+
return [];
|
|
211
|
+
return [
|
|
212
|
+
{
|
|
213
|
+
key: "brew-github-token",
|
|
214
|
+
env: "PUBM_BREW_GITHUB_TOKEN",
|
|
215
|
+
label: "GitHub PAT for Homebrew (homebrew-core)",
|
|
216
|
+
tokenUrl: "https://github.com/settings/tokens/new?scopes=repo,workflow",
|
|
217
|
+
tokenUrlLabel: "github.com",
|
|
218
|
+
ghSecretName: "PUBM_BREW_GITHUB_TOKEN",
|
|
219
|
+
required: true
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
},
|
|
223
|
+
checks: (ctx) => {
|
|
224
|
+
const phases = resolvePhases(ctx.options);
|
|
225
|
+
if (!phases.includes("publish") && ctx.options.mode !== "ci")
|
|
226
|
+
return [];
|
|
227
|
+
if (ctx.options.mode === "ci") {
|
|
228
|
+
return [
|
|
229
|
+
{
|
|
230
|
+
title: "Checking Homebrew core token availability",
|
|
231
|
+
phase: "conditions",
|
|
232
|
+
task: async (ctx2, task) => {
|
|
233
|
+
const token = ctx2.runtime.pluginTokens?.["brew-github-token"];
|
|
234
|
+
if (!token) {
|
|
235
|
+
throw new Error("PUBM_BREW_GITHUB_TOKEN is required for homebrew-core publishing.");
|
|
236
|
+
}
|
|
237
|
+
task.output = "Homebrew core token verified";
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
return [
|
|
243
|
+
{
|
|
244
|
+
title: "Checking GitHub CLI access for homebrew-core",
|
|
245
|
+
phase: "conditions",
|
|
246
|
+
task: async (_ctx, task) => {
|
|
247
|
+
const { execFileSync } = await import("node:child_process");
|
|
248
|
+
try {
|
|
249
|
+
execFileSync("gh", ["auth", "status"], { stdio: "pipe" });
|
|
250
|
+
task.output = "GitHub CLI authenticated";
|
|
251
|
+
} catch {
|
|
252
|
+
throw new Error("GitHub CLI is not authenticated. Run `gh auth login` first.");
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
execFileSync("gh", ["repo", "view", "homebrew/homebrew-core", "--json", "name"], { stdio: "pipe" });
|
|
256
|
+
task.output = "Access to homebrew/homebrew-core verified";
|
|
257
|
+
} catch {
|
|
258
|
+
throw new Error("Cannot access homebrew/homebrew-core. Check your GitHub permissions.");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
},
|
|
207
264
|
hooks: {
|
|
208
|
-
afterRelease: async (
|
|
265
|
+
afterRelease: async (ctx, releaseCtx) => {
|
|
209
266
|
if (options.packageName && releaseCtx.displayLabel !== options.packageName) {
|
|
210
267
|
return;
|
|
211
268
|
}
|
|
@@ -215,16 +272,26 @@ function brewCore(options) {
|
|
|
215
272
|
const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, "utf-8")) : {};
|
|
216
273
|
const name = pkg.name?.replace(/^@[^/]+\//, "") ?? "my-tool";
|
|
217
274
|
const formulaAssets = releaseAssetsToFormulaAssets(releaseCtx.assets, options.assetPlatforms);
|
|
275
|
+
const token = ctx.runtime.pluginTokens?.["brew-github-token"];
|
|
276
|
+
const ghEnv = token ? { env: { ...process.env, GH_TOKEN: token } } : {};
|
|
218
277
|
try {
|
|
219
278
|
execSync2("gh repo fork homebrew/homebrew-core --clone=false", {
|
|
220
|
-
stdio: "pipe"
|
|
279
|
+
stdio: "pipe",
|
|
280
|
+
...ghEnv
|
|
221
281
|
});
|
|
222
282
|
} catch {}
|
|
223
283
|
const username = execSync2("gh api user --jq .login", {
|
|
224
|
-
encoding: "utf-8"
|
|
284
|
+
encoding: "utf-8",
|
|
285
|
+
...ghEnv
|
|
225
286
|
}).trim();
|
|
226
287
|
const tmpDir = join(tmpdir(), `pubm-brew-core-${Date.now()}`);
|
|
227
|
-
|
|
288
|
+
let cloneUrl = `https://github.com/${username}/homebrew-core.git`;
|
|
289
|
+
if (token) {
|
|
290
|
+
cloneUrl = `https://x-access-token:${token}@github.com/${username}/homebrew-core.git`;
|
|
291
|
+
}
|
|
292
|
+
execSync2(`git clone --depth 1 ${cloneUrl} ${tmpDir}`, {
|
|
293
|
+
stdio: "inherit"
|
|
294
|
+
});
|
|
228
295
|
ensureGitIdentity(tmpDir);
|
|
229
296
|
const formulaPath = join(tmpDir, "Formula", `${name}.rb`);
|
|
230
297
|
let content;
|
|
@@ -251,10 +318,21 @@ function brewCore(options) {
|
|
|
251
318
|
`git commit -m "${name} ${releaseCtx.version}"`,
|
|
252
319
|
`git push origin ${branchName}`
|
|
253
320
|
].join(" && "), { stdio: "inherit" });
|
|
254
|
-
execSync2([
|
|
321
|
+
const prUrl = execSync2([
|
|
255
322
|
`cd ${tmpDir}`,
|
|
256
323
|
`gh pr create --repo homebrew/homebrew-core --title "${name} ${releaseCtx.version}" --body "Update ${name} formula to version ${releaseCtx.version}"`
|
|
257
|
-
].join(" && "), {
|
|
324
|
+
].join(" && "), { encoding: "utf-8", ...ghEnv }).trim();
|
|
325
|
+
const prNumber = prUrl.match(/\/pull\/(\d+)/)?.[1];
|
|
326
|
+
if (prNumber) {
|
|
327
|
+
ctx.runtime.rollback.add({
|
|
328
|
+
label: `Close homebrew-core PR #${prNumber}`,
|
|
329
|
+
fn: async () => {
|
|
330
|
+
const { execSync: execSyncRb } = await import("node:child_process");
|
|
331
|
+
execSyncRb(`gh pr close ${prNumber} --repo homebrew/homebrew-core --comment "Closed by pubm rollback"`, { stdio: "inherit", ...ghEnv });
|
|
332
|
+
},
|
|
333
|
+
confirm: true
|
|
334
|
+
});
|
|
335
|
+
}
|
|
258
336
|
console.log(`PR created to homebrew/homebrew-core for ${name} ${releaseCtx.version}`);
|
|
259
337
|
}
|
|
260
338
|
}
|
|
@@ -263,6 +341,7 @@ function brewCore(options) {
|
|
|
263
341
|
// src/brew-tap.ts
|
|
264
342
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
265
343
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
344
|
+
import { resolvePhases as resolvePhases2 } from "@pubm/core";
|
|
266
345
|
function brewTap(options) {
|
|
267
346
|
return {
|
|
268
347
|
name: "@pubm/plugin-brew-tap",
|
|
@@ -299,8 +378,72 @@ function brewTap(options) {
|
|
|
299
378
|
]
|
|
300
379
|
}
|
|
301
380
|
],
|
|
381
|
+
credentials: (ctx) => {
|
|
382
|
+
if (!options.repo || ctx.options.mode !== "ci")
|
|
383
|
+
return [];
|
|
384
|
+
return [
|
|
385
|
+
{
|
|
386
|
+
key: "brew-github-token",
|
|
387
|
+
env: "PUBM_BREW_GITHUB_TOKEN",
|
|
388
|
+
label: "GitHub PAT for Homebrew tap",
|
|
389
|
+
tokenUrl: "https://github.com/settings/tokens/new?scopes=repo",
|
|
390
|
+
tokenUrlLabel: "github.com",
|
|
391
|
+
ghSecretName: "PUBM_BREW_GITHUB_TOKEN",
|
|
392
|
+
required: true
|
|
393
|
+
}
|
|
394
|
+
];
|
|
395
|
+
},
|
|
396
|
+
checks: (ctx) => {
|
|
397
|
+
const phases = resolvePhases2(ctx.options);
|
|
398
|
+
if (!phases.includes("publish") && ctx.options.mode !== "ci")
|
|
399
|
+
return [];
|
|
400
|
+
if (ctx.options.mode === "ci") {
|
|
401
|
+
if (!options.repo)
|
|
402
|
+
return [];
|
|
403
|
+
return [
|
|
404
|
+
{
|
|
405
|
+
title: "Checking Homebrew tap token availability",
|
|
406
|
+
phase: "conditions",
|
|
407
|
+
task: async (ctx2, task) => {
|
|
408
|
+
const token = ctx2.runtime.pluginTokens?.["brew-github-token"];
|
|
409
|
+
if (!token) {
|
|
410
|
+
throw new Error("PUBM_BREW_GITHUB_TOKEN is required for Homebrew tap publishing.");
|
|
411
|
+
}
|
|
412
|
+
task.output = "Homebrew tap token verified";
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
if (!options.repo)
|
|
418
|
+
return [];
|
|
419
|
+
const targetRepo = options.repo;
|
|
420
|
+
return [
|
|
421
|
+
{
|
|
422
|
+
title: "Checking git/gh access for Homebrew tap",
|
|
423
|
+
phase: "conditions",
|
|
424
|
+
task: async (_ctx, task) => {
|
|
425
|
+
const { execFileSync } = await import("node:child_process");
|
|
426
|
+
try {
|
|
427
|
+
execFileSync("gh", ["auth", "status"], { stdio: "pipe" });
|
|
428
|
+
task.output = "GitHub CLI authenticated";
|
|
429
|
+
} catch {
|
|
430
|
+
throw new Error("GitHub CLI is not authenticated. Run `gh auth login` first.");
|
|
431
|
+
}
|
|
432
|
+
const repoName = /^[^/]+\/[^/]+$/.test(targetRepo) ? targetRepo : targetRepo.match(/github\.com[/:]([^/]+\/[^/.]+)/)?.[1];
|
|
433
|
+
if (repoName) {
|
|
434
|
+
try {
|
|
435
|
+
execFileSync("gh", ["repo", "view", repoName, "--json", "name"], { stdio: "pipe" });
|
|
436
|
+
task.output = `Access to ${repoName} verified`;
|
|
437
|
+
} catch {
|
|
438
|
+
throw new Error(`Cannot access tap repository '${targetRepo}'. Check your GitHub permissions.`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
];
|
|
444
|
+
},
|
|
302
445
|
hooks: {
|
|
303
|
-
afterRelease: async (
|
|
446
|
+
afterRelease: async (ctx, releaseCtx) => {
|
|
304
447
|
if (options.packageName && releaseCtx.displayLabel !== options.packageName) {
|
|
305
448
|
return;
|
|
306
449
|
}
|
|
@@ -327,7 +470,7 @@ function brewTap(options) {
|
|
|
327
470
|
mkdirSync2(dirname2(formulaPath), { recursive: true });
|
|
328
471
|
writeFileSync2(formulaPath, content);
|
|
329
472
|
console.log(`Formula updated at ${options.formula}`);
|
|
330
|
-
|
|
473
|
+
{
|
|
331
474
|
const { execSync: execSync2 } = await import("node:child_process");
|
|
332
475
|
ensureGitIdentity();
|
|
333
476
|
execSync2(`git add ${formulaPath}`, { stdio: "inherit" });
|
|
@@ -338,16 +481,24 @@ function brewTap(options) {
|
|
|
338
481
|
const branch = `pubm/brew-formula-v${releaseCtx.version}`;
|
|
339
482
|
execSync2(`git checkout -b ${branch}`, { stdio: "inherit" });
|
|
340
483
|
execSync2(`git push origin ${branch}`, { stdio: "inherit" });
|
|
341
|
-
execSync2(`gh pr create --title "chore(brew): update formula to ${releaseCtx.version}" --body "Automated formula update by pubm"`, {
|
|
484
|
+
const prUrl = execSync2(`gh pr create --title "chore(brew): update formula to ${releaseCtx.version}" --body "Automated formula update by pubm"`, { encoding: "utf-8" }).trim();
|
|
485
|
+
const prNumber = prUrl.match(/\/pull\/(\d+)/)?.[1];
|
|
486
|
+
if (prNumber) {
|
|
487
|
+
ctx.runtime.rollback.add({
|
|
488
|
+
label: `Close Homebrew tap PR #${prNumber}`,
|
|
489
|
+
fn: async () => {
|
|
490
|
+
execSync2(`gh pr close ${prNumber} --comment "Closed by pubm rollback"`, { stdio: "inherit" });
|
|
491
|
+
},
|
|
492
|
+
confirm: true
|
|
493
|
+
});
|
|
494
|
+
}
|
|
342
495
|
console.log(`Created PR on branch ${branch}`);
|
|
343
496
|
}
|
|
344
|
-
return;
|
|
345
497
|
}
|
|
346
498
|
if (options.repo) {
|
|
347
499
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
348
500
|
const { basename, join: join2 } = await import("node:path");
|
|
349
501
|
const { execSync: execSync2 } = await import("node:child_process");
|
|
350
|
-
const { resolveGitHubToken } = await import("@pubm/core");
|
|
351
502
|
const tmpDir = join2(tmpdir2(), `pubm-brew-tap-${Date.now()}`);
|
|
352
503
|
const formulaFile = basename(formulaPath);
|
|
353
504
|
const isShorthand = /^[^/]+\/[^/]+$/.test(options.repo);
|
|
@@ -355,10 +506,11 @@ function brewTap(options) {
|
|
|
355
506
|
const ownerRepoMatch = repoUrl.match(/github\.com[/:]([^/]+\/[^/.]+?)(?:\.git)?$/);
|
|
356
507
|
const ownerRepo = ownerRepoMatch?.[1] ?? options.repo;
|
|
357
508
|
let cloneUrl = repoUrl;
|
|
358
|
-
const
|
|
359
|
-
if (
|
|
360
|
-
cloneUrl = repoUrl.replace("https://github.com/", `https://x-access-token:${
|
|
509
|
+
const token = ctx.runtime.pluginTokens?.["brew-github-token"];
|
|
510
|
+
if (token && repoUrl.startsWith("https://github.com/")) {
|
|
511
|
+
cloneUrl = repoUrl.replace("https://github.com/", `https://x-access-token:${token}@github.com/`);
|
|
361
512
|
}
|
|
513
|
+
const ghEnv = token ? { env: { ...process.env, GH_TOKEN: token } } : {};
|
|
362
514
|
execSync2(`git clone --depth 1 ${cloneUrl} ${tmpDir}`, {
|
|
363
515
|
stdio: "inherit"
|
|
364
516
|
});
|
|
@@ -381,7 +533,18 @@ function brewTap(options) {
|
|
|
381
533
|
execSync2(`cd ${tmpDir} && git push origin ${branch}`, {
|
|
382
534
|
stdio: "inherit"
|
|
383
535
|
});
|
|
384
|
-
execSync2(`gh pr create --repo ${ownerRepo} --title "chore(brew): update formula to ${releaseCtx.version}" --body "Automated formula update by pubm"`, {
|
|
536
|
+
const prUrl = execSync2(`gh pr create --repo ${ownerRepo} --title "chore(brew): update formula to ${releaseCtx.version}" --body "Automated formula update by pubm"`, { encoding: "utf-8", ...ghEnv }).trim();
|
|
537
|
+
const prNumber = prUrl.match(/\/pull\/(\d+)/)?.[1];
|
|
538
|
+
if (prNumber) {
|
|
539
|
+
const repoFlag = ownerRepo;
|
|
540
|
+
ctx.runtime.rollback.add({
|
|
541
|
+
label: `Close Homebrew tap PR #${prNumber} (${repoFlag})`,
|
|
542
|
+
fn: async () => {
|
|
543
|
+
execSync2(`gh pr close ${prNumber} --repo ${repoFlag} --comment "Closed by pubm rollback"`, { stdio: "inherit", ...ghEnv });
|
|
544
|
+
},
|
|
545
|
+
confirm: true
|
|
546
|
+
});
|
|
547
|
+
}
|
|
385
548
|
console.log(`Created PR on branch ${branch}`);
|
|
386
549
|
}
|
|
387
550
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubm/plugin-brew",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "pubm plugin for Homebrew formula publishing workflows",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "Apache-2.0",
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@pubm/core": ">=0.4.
|
|
26
|
+
"@pubm/core": ">=0.4.8"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|