@wipcomputer/wip-release 1.9.73 → 1.9.74

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.
Files changed (3) hide show
  1. package/cli.js +4 -0
  2. package/core.mjs +122 -2
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -24,6 +24,7 @@ const skipWorktreeCheck = args.includes('--skip-worktree-check');
24
24
  const skipTechDocsCheck = args.includes('--skip-tech-docs-check');
25
25
  const skipCoverageCheck = args.includes('--skip-coverage-check');
26
26
  const allowSubToolDrift = args.includes('--allow-sub-tool-drift');
27
+ const noDeployPublic = args.includes('--no-deploy-public');
27
28
  const wantReleaseNotes = args.includes('--release-notes');
28
29
  const noReleaseNotes = args.includes('--no-release-notes');
29
30
  const notesFilePath = flag('notes-file');
@@ -174,6 +175,7 @@ Flags:
174
175
  --skip-stale-check Skip stale remote branch check
175
176
  --skip-worktree-check Skip main-branch + worktree guard (break-glass only)
176
177
  --allow-sub-tool-drift Allow release even if a sub-tool's files changed since the last tag without a version bump (error by default)
178
+ --no-deploy-public Skip the deploy-public.sh step at the end of stable and prerelease flows (runs by default for -private repos)
177
179
 
178
180
  Release notes (REQUIRED for stable, optional for other tracks):
179
181
  1. --notes-file=path Explicit file path
@@ -224,6 +226,7 @@ if (level === 'alpha' || level === 'beta') {
224
226
  publishReleaseNotes: level === 'alpha' ? wantReleaseNotes : !noReleaseNotes,
225
227
  skipWorktreeCheck,
226
228
  allowSubToolDrift,
229
+ noDeployPublic,
227
230
  }).catch(err => {
228
231
  console.error(` \u2717 ${err.message}`);
229
232
  process.exit(1);
@@ -258,6 +261,7 @@ if (level === 'alpha' || level === 'beta') {
258
261
  skipTechDocsCheck,
259
262
  skipCoverageCheck,
260
263
  allowSubToolDrift,
264
+ noDeployPublic,
261
265
  }).catch(err => {
262
266
  console.error(` \u2717 ${err.message}`);
263
267
  process.exit(1);
package/core.mjs CHANGED
@@ -1626,6 +1626,79 @@ function logPushFailure(result, tag) {
1626
1626
  }
1627
1627
  }
1628
1628
 
1629
+ /**
1630
+ * Resolve the public GitHub repo name for a private-to-public mirror setup.
1631
+ *
1632
+ * Strategy:
1633
+ * 1. Read `origin` remote URL (e.g. git@github.com:owner/repo-private.git)
1634
+ * 2. Strip `-private` suffix to get the public repo name
1635
+ * 3. Return `owner/repo` format suitable for gh CLI
1636
+ *
1637
+ * Returns null if the remote URL cannot be parsed or the repo name does not
1638
+ * end in `-private` (in which case the caller should skip deploy-public as a
1639
+ * no-op rather than error).
1640
+ */
1641
+ function resolvePublicRepoName(repoPath) {
1642
+ try {
1643
+ const remoteUrl = execFileSync('git', ['remote', 'get-url', 'origin'], {
1644
+ cwd: repoPath, encoding: 'utf8'
1645
+ }).trim();
1646
+ // Match owner/repo from either SSH (git@github.com:owner/repo.git) or HTTPS
1647
+ const match = remoteUrl.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
1648
+ if (!match) return null;
1649
+ const [, owner, repoName] = match;
1650
+ if (!repoName.endsWith('-private')) {
1651
+ return null;
1652
+ }
1653
+ return `${owner}/${repoName.replace(/-private$/, '')}`;
1654
+ } catch {
1655
+ return null;
1656
+ }
1657
+ }
1658
+
1659
+ /**
1660
+ * Run deploy-public.sh as the final step of the release pipeline.
1661
+ *
1662
+ * Previously deploy-public was a separate manual step. Every release that
1663
+ * forgot it left the public mirror stale, so `ldm install` pulled old code
1664
+ * from the public repo. Now wip-release invokes it automatically for stable
1665
+ * and prerelease tracks (hotfix still skips, per prior convention).
1666
+ *
1667
+ * Callable with `--no-deploy-public` to opt out.
1668
+ *
1669
+ * Skips silently if:
1670
+ * - The repo has no tools/deploy-public/deploy-public.sh (not a toolbox-
1671
+ * style repo, or deploy-public is provided by a parent install)
1672
+ * - The origin remote is not a -private repo (no public mirror to sync)
1673
+ *
1674
+ * Related: `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phase 6.
1675
+ */
1676
+ function runDeployPublic(repoPath, { skip } = {}) {
1677
+ if (skip) {
1678
+ return { ok: true, skipped: true, reason: 'flag' };
1679
+ }
1680
+ const scriptPath = join(repoPath, 'tools', 'deploy-public', 'deploy-public.sh');
1681
+ if (!existsSync(scriptPath)) {
1682
+ return { ok: true, skipped: true, reason: 'no-script' };
1683
+ }
1684
+ const publicRepo = resolvePublicRepoName(repoPath);
1685
+ if (!publicRepo) {
1686
+ return { ok: true, skipped: true, reason: 'not-private-repo' };
1687
+ }
1688
+ try {
1689
+ execFileSync('bash', [scriptPath, repoPath, publicRepo], {
1690
+ cwd: repoPath, stdio: 'inherit',
1691
+ });
1692
+ return { ok: true, publicRepo };
1693
+ } catch (err) {
1694
+ return {
1695
+ ok: false,
1696
+ detail: String(err?.stderr ?? err?.message ?? err),
1697
+ publicRepo,
1698
+ };
1699
+ }
1700
+ }
1701
+
1629
1702
  function logMainBranchGuardFailure(result) {
1630
1703
  if (result.reason === 'linked-worktree') {
1631
1704
  console.log(` \u2717 wip-release must run from the main working tree, not a worktree.`);
@@ -1645,7 +1718,7 @@ function logMainBranchGuardFailure(result) {
1645
1718
  /**
1646
1719
  * Run the full release pipeline.
1647
1720
  */
1648
- export async function release({ repoPath, level, notes, notesSource, dryRun, noPublish, skipProductCheck, skipStaleCheck, skipWorktreeCheck, skipTechDocsCheck, skipCoverageCheck, allowSubToolDrift }) {
1721
+ export async function release({ repoPath, level, notes, notesSource, dryRun, noPublish, skipProductCheck, skipStaleCheck, skipWorktreeCheck, skipTechDocsCheck, skipCoverageCheck, allowSubToolDrift, noDeployPublic }) {
1649
1722
  repoPath = repoPath || process.cwd();
1650
1723
  const currentVersion = detectCurrentVersion(repoPath);
1651
1724
  const newVersion = bumpSemver(currentVersion, level);
@@ -2288,6 +2361,29 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
2288
2361
  }) + '\n');
2289
2362
  } catch {}
2290
2363
 
2364
+ // 12. deploy-public: sync private -> public mirror (Phase 6)
2365
+ // Runs at the end of every stable release unless --no-deploy-public is set
2366
+ // or the repo has no deploy-public.sh script. Skips silently for non-
2367
+ // -private repos (no public mirror to sync).
2368
+ {
2369
+ const dp = runDeployPublic(repoPath, { skip: noDeployPublic });
2370
+ if (dp.skipped) {
2371
+ if (dp.reason === 'flag') {
2372
+ console.log(` - deploy-public: skipped (--no-deploy-public)`);
2373
+ } else if (dp.reason === 'no-script') {
2374
+ console.log(` - deploy-public: skipped (no tools/deploy-public/deploy-public.sh)`);
2375
+ } else if (dp.reason === 'not-private-repo') {
2376
+ console.log(` - deploy-public: skipped (origin is not a -private repo)`);
2377
+ }
2378
+ } else if (dp.ok) {
2379
+ console.log(` \u2713 deploy-public: synced to ${dp.publicRepo}`);
2380
+ } else {
2381
+ console.log(` \u2717 deploy-public failed for ${dp.publicRepo}`);
2382
+ if (dp.detail) console.log(` ${dp.detail.split('\n')[0]}`);
2383
+ console.log(` Manual recovery: bash tools/deploy-public/deploy-public.sh ${repoPath} ${dp.publicRepo}`);
2384
+ }
2385
+ }
2386
+
2291
2387
  console.log('');
2292
2388
  console.log(` Done. ${repoName} v${newVersion} released.`);
2293
2389
  console.log('');
@@ -2306,7 +2402,7 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
2306
2402
  * No deploy-public. No code sync. No CHANGELOG gate. No product docs gate.
2307
2403
  * Lightweight: bump version, npm publish with tag, optional GitHub prerelease.
2308
2404
  */
2309
- export async function releasePrerelease({ repoPath, track, notes, dryRun, noPublish, publishReleaseNotes, skipWorktreeCheck, allowSubToolDrift }) {
2405
+ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPublish, publishReleaseNotes, skipWorktreeCheck, allowSubToolDrift, noDeployPublic }) {
2310
2406
  repoPath = repoPath || process.cwd();
2311
2407
  const currentVersion = detectCurrentVersion(repoPath);
2312
2408
  const newVersion = bumpPrerelease(currentVersion, track);
@@ -2430,6 +2526,30 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
2430
2526
  }
2431
2527
  }
2432
2528
 
2529
+ // deploy-public: sync private -> public mirror (Phase 6)
2530
+ // Prerelease tracks (alpha/beta) also run deploy-public so the public
2531
+ // mirror stays current. Flagged in the bridge master plan and the
2532
+ // release-pipeline master plan: forgetting deploy-public left the public
2533
+ // repo stale across multiple days of alpha releases.
2534
+ {
2535
+ const dp = runDeployPublic(repoPath, { skip: noDeployPublic });
2536
+ if (dp.skipped) {
2537
+ if (dp.reason === 'flag') {
2538
+ console.log(` - deploy-public: skipped (--no-deploy-public)`);
2539
+ } else if (dp.reason === 'no-script') {
2540
+ console.log(` - deploy-public: skipped (no deploy-public.sh)`);
2541
+ } else if (dp.reason === 'not-private-repo') {
2542
+ console.log(` - deploy-public: skipped (origin is not a -private repo)`);
2543
+ }
2544
+ } else if (dp.ok) {
2545
+ console.log(` \u2713 deploy-public: synced to ${dp.publicRepo}`);
2546
+ } else {
2547
+ console.log(` \u2717 deploy-public failed for ${dp.publicRepo}`);
2548
+ if (dp.detail) console.log(` ${dp.detail.split('\n')[0]}`);
2549
+ console.log(` Manual recovery: bash tools/deploy-public/deploy-public.sh ${repoPath} ${dp.publicRepo}`);
2550
+ }
2551
+ }
2552
+
2433
2553
  console.log('');
2434
2554
  console.log(` Done. ${repoName} v${newVersion} (${track}) released.`);
2435
2555
  console.log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.9.73",
3
+ "version": "1.9.74",
4
4
  "type": "module",
5
5
  "description": "One-command release pipeline. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.",
6
6
  "main": "core.mjs",