@wipcomputer/wip-release 1.9.72 → 1.9.73
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/core.mjs +143 -18
- package/package.json +1 -1
package/core.mjs
CHANGED
|
@@ -1507,6 +1507,125 @@ function checkTagCollision(repoPath, newVersion) {
|
|
|
1507
1507
|
return { ok: true };
|
|
1508
1508
|
}
|
|
1509
1509
|
|
|
1510
|
+
/**
|
|
1511
|
+
* Push the release commit + tag, handling protected-main rejection via
|
|
1512
|
+
* automatic PR flow.
|
|
1513
|
+
*
|
|
1514
|
+
* If `git push origin main` succeeds directly, we also push tags and return.
|
|
1515
|
+
* If it fails with a "protected branch" error (GH006), create a temporary
|
|
1516
|
+
* release branch from the current HEAD, push the branch, open a PR targeting
|
|
1517
|
+
* main, auto-merge it via `gh pr merge --merge --delete-branch`, then push
|
|
1518
|
+
* the tag separately (tags bypass branch protection).
|
|
1519
|
+
*
|
|
1520
|
+
* Phase 4 of the release-pipeline master plan. Earlier today this entire
|
|
1521
|
+
* flow was done manually every time, adding 2-3 minutes per release.
|
|
1522
|
+
*
|
|
1523
|
+
* Returns `{ ok: true, via }` on success where `via` is `"direct"` or `"pr"`.
|
|
1524
|
+
* Returns `{ ok: false, reason, detail }` on failure; caller logs and
|
|
1525
|
+
* continues (matches prior non-fatal push behavior).
|
|
1526
|
+
*/
|
|
1527
|
+
function pushReleaseWithAutoPr(repoPath, newVersion, level) {
|
|
1528
|
+
const tag = `v${newVersion}`;
|
|
1529
|
+
// 1. Try direct push first. Most repos allow it.
|
|
1530
|
+
try {
|
|
1531
|
+
execFileSync('git', ['push'], { cwd: repoPath, stdio: 'pipe' });
|
|
1532
|
+
execFileSync('git', ['push', 'origin', tag], { cwd: repoPath, stdio: 'pipe' });
|
|
1533
|
+
return { ok: true, via: 'direct' };
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
const msg = String(err?.stderr ?? err?.message ?? err);
|
|
1536
|
+
const isProtected = /protected branch|GH006|Changes must be made through a pull request/i.test(msg);
|
|
1537
|
+
if (!isProtected) {
|
|
1538
|
+
return { ok: false, reason: 'push-failed', detail: msg };
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// 2. Protected main. Open auto-PR flow.
|
|
1543
|
+
const releaseBranch = `cc-mini/release-${tag}`;
|
|
1544
|
+
console.log(` - Direct push to main refused (protected). Opening release PR...`);
|
|
1545
|
+
try {
|
|
1546
|
+
// Create branch at current HEAD
|
|
1547
|
+
execFileSync('git', ['branch', releaseBranch], { cwd: repoPath, stdio: 'pipe' });
|
|
1548
|
+
execFileSync('git', ['push', '-u', 'origin', releaseBranch], {
|
|
1549
|
+
cwd: repoPath, stdio: 'pipe'
|
|
1550
|
+
});
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
return {
|
|
1553
|
+
ok: false,
|
|
1554
|
+
reason: 'branch-push-failed',
|
|
1555
|
+
detail: String(err?.stderr ?? err?.message ?? err),
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// 3. Create and merge PR via gh CLI
|
|
1560
|
+
try {
|
|
1561
|
+
const prTitle = `release: ${tag}`;
|
|
1562
|
+
const prBody = `Release commit for ${tag} (${level}). Auto-generated by wip-release.`;
|
|
1563
|
+
execFileSync('gh', [
|
|
1564
|
+
'pr', 'create',
|
|
1565
|
+
'--base', 'main',
|
|
1566
|
+
'--head', releaseBranch,
|
|
1567
|
+
'--title', prTitle,
|
|
1568
|
+
'--body', prBody,
|
|
1569
|
+
], { cwd: repoPath, stdio: 'pipe' });
|
|
1570
|
+
execFileSync('gh', [
|
|
1571
|
+
'pr', 'merge', releaseBranch,
|
|
1572
|
+
'--merge', '--delete-branch',
|
|
1573
|
+
], { cwd: repoPath, stdio: 'pipe' });
|
|
1574
|
+
console.log(` ✓ Release PR merged`);
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
return {
|
|
1577
|
+
ok: false,
|
|
1578
|
+
reason: 'gh-pr-failed',
|
|
1579
|
+
detail: String(err?.stderr ?? err?.message ?? err),
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// 4. Push the tag separately. Tags bypass branch protection on most
|
|
1584
|
+
// GitHub setups, but if this fails the user can push the tag manually.
|
|
1585
|
+
try {
|
|
1586
|
+
execFileSync('git', ['push', 'origin', tag], { cwd: repoPath, stdio: 'pipe' });
|
|
1587
|
+
console.log(` ✓ Pushed tag ${tag}`);
|
|
1588
|
+
} catch (err) {
|
|
1589
|
+
return {
|
|
1590
|
+
ok: false,
|
|
1591
|
+
reason: 'tag-push-failed',
|
|
1592
|
+
detail: String(err?.stderr ?? err?.message ?? err),
|
|
1593
|
+
partialSuccess: 'pr-merged',
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// 5. Pull latest main so local HEAD reflects the merge commit. This
|
|
1598
|
+
// keeps subsequent git operations (like deploy-public) happy.
|
|
1599
|
+
try {
|
|
1600
|
+
execFileSync('git', ['fetch', 'origin', 'main'], { cwd: repoPath, stdio: 'pipe' });
|
|
1601
|
+
execFileSync('git', ['merge', '--ff-only', 'origin/main'], {
|
|
1602
|
+
cwd: repoPath, stdio: 'pipe'
|
|
1603
|
+
});
|
|
1604
|
+
} catch {
|
|
1605
|
+
// Non-fatal: main may have diverged (unlikely here). Deploy-public
|
|
1606
|
+
// will handle its own state.
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
return { ok: true, via: 'pr' };
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function logPushFailure(result, tag) {
|
|
1613
|
+
console.log(` ! Push failed: ${result.reason}`);
|
|
1614
|
+
if (result.detail) {
|
|
1615
|
+
console.log(` ${result.detail.split('\n')[0]}`);
|
|
1616
|
+
}
|
|
1617
|
+
console.log(` Manual recovery:`);
|
|
1618
|
+
if (result.reason === 'push-failed' || result.reason === 'branch-push-failed') {
|
|
1619
|
+
console.log(` git push && git push origin ${tag}`);
|
|
1620
|
+
} else if (result.reason === 'gh-pr-failed') {
|
|
1621
|
+
console.log(` Open a PR for cc-mini/release-${tag} targeting main, merge, then:`);
|
|
1622
|
+
console.log(` git push origin ${tag}`);
|
|
1623
|
+
} else if (result.reason === 'tag-push-failed') {
|
|
1624
|
+
console.log(` PR already merged. Just push the tag:`);
|
|
1625
|
+
console.log(` git push origin ${tag}`);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1510
1629
|
function logMainBranchGuardFailure(result) {
|
|
1511
1630
|
if (result.reason === 'linked-worktree') {
|
|
1512
1631
|
console.log(` \u2717 wip-release must run from the main working tree, not a worktree.`);
|
|
@@ -1898,12 +2017,14 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1898
2017
|
gitCommitAndTag(repoPath, newVersion, notes);
|
|
1899
2018
|
console.log(` ✓ Committed and tagged v${newVersion}`);
|
|
1900
2019
|
|
|
1901
|
-
// 5. Push commit + tag
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2020
|
+
// 5. Push commit + tag (with auto-PR fallback on protected main, Phase 4)
|
|
2021
|
+
{
|
|
2022
|
+
const pushResult = pushReleaseWithAutoPr(repoPath, newVersion, level);
|
|
2023
|
+
if (pushResult.ok) {
|
|
2024
|
+
console.log(` ✓ Pushed to remote (${pushResult.via})`);
|
|
2025
|
+
} else {
|
|
2026
|
+
logPushFailure(pushResult, `v${newVersion}`);
|
|
2027
|
+
}
|
|
1907
2028
|
}
|
|
1908
2029
|
|
|
1909
2030
|
// Distribution results collector (#104)
|
|
@@ -2260,12 +2381,14 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
|
|
|
2260
2381
|
execFileSync('git', ['tag', `v${newVersion}`], { cwd: repoPath, stdio: 'pipe' });
|
|
2261
2382
|
console.log(` \u2713 Committed and tagged v${newVersion}`);
|
|
2262
2383
|
|
|
2263
|
-
// 4. Push commit + tag
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2384
|
+
// 4. Push commit + tag (with auto-PR fallback on protected main, Phase 4)
|
|
2385
|
+
{
|
|
2386
|
+
const pushResult = pushReleaseWithAutoPr(repoPath, newVersion, track);
|
|
2387
|
+
if (pushResult.ok) {
|
|
2388
|
+
console.log(` \u2713 Pushed to remote (${pushResult.via})`);
|
|
2389
|
+
} else {
|
|
2390
|
+
logPushFailure(pushResult, `v${newVersion}`);
|
|
2391
|
+
}
|
|
2269
2392
|
}
|
|
2270
2393
|
|
|
2271
2394
|
const distResults = [];
|
|
@@ -2470,12 +2593,14 @@ export async function releaseHotfix({ repoPath, notes, notesSource, dryRun, noPu
|
|
|
2470
2593
|
gitCommitAndTag(repoPath, newVersion, notes);
|
|
2471
2594
|
console.log(` \u2713 Committed and tagged v${newVersion}`);
|
|
2472
2595
|
|
|
2473
|
-
// 5. Push commit + tag
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2596
|
+
// 5. Push commit + tag (with auto-PR fallback on protected main, Phase 4)
|
|
2597
|
+
{
|
|
2598
|
+
const pushResult = pushReleaseWithAutoPr(repoPath, newVersion, 'hotfix');
|
|
2599
|
+
if (pushResult.ok) {
|
|
2600
|
+
console.log(` \u2713 Pushed to remote (${pushResult.via})`);
|
|
2601
|
+
} else {
|
|
2602
|
+
logPushFailure(pushResult, `v${newVersion}`);
|
|
2603
|
+
}
|
|
2479
2604
|
}
|
|
2480
2605
|
|
|
2481
2606
|
const distResults = [];
|
package/package.json
CHANGED