| … | … | @@ -1,4 +1,4 @@ |
| 1 | | /** pgh deploy <repo> [<ref>] | status | log <repo> | trigger <repo> [<ref>] */ |
| 1 | /** pgh deploy <repo> [<ref>] | status | log <repo> | trigger <repo> [<ref>] | gc [<repo>] */ |
| 2 | 2 | |
| 3 | 3 | export const usage = `pgh deploy — CI/CD config + release log |
| 4 | 4 | |
| … | … | @@ -7,7 +7,19 @@ export const usage = `pgh deploy — CI/CD config + release log |
| 7 | 7 | pgh deploy trigger <repo> [<ref>] Build a release |
| 8 | 8 | pgh deploy <repo> [<ref>] Shorthand for trigger |
| 9 | 9 | |
| 10 | | Default ref: refs/heads/master` |
| 10 | pgh deploy gc [<repo>] [--keep N] DELETE stale release versions |
| 11 | pgh deploy gc [<repo>] --dry-run List what gc WOULD delete |
| 12 | |
| 13 | Default ref: refs/heads/master |
| 14 | Default --keep: 3 newest unpinned versions per repo. |
| 15 | |
| 16 | gc never touches a version a CHANNEL POINTS AT — pinning to a channel is how you |
| 17 | say "keep this one". Everything else is a build artifact: release versions are |
| 18 | plain files with no dedup, so an app publishing a large bundle accumulates it |
| 19 | once per version, forever, unless something collects it. |
| 20 | |
| 21 | pgh deploy gc hyper-tps --dry-run # always look first |
| 22 | pgh deploy gc hyper-tps --keep 1 # keep only the newest + pinned` |
| 11 | 23 | |
| 12 | 24 | const DEFAULT_REF = 'refs/heads/master' |
| 13 | 25 | |
| … | … | @@ -30,6 +42,30 @@ export async function run (client, args) { |
| 30 | 42 | return client.call('build-release', { repo, ref }) |
| 31 | 43 | } |
| 32 | 44 | |
| 45 | if (sub === 'gc') { |
| 46 | // The daemon has had gc-releases since the release manager was written; it |
| 47 | // had no way to be invoked, so nothing ever collected anything. |
| 48 | const rest = args.slice(1) |
| 49 | const dryRun = rest.includes('--dry-run') |
| 50 | const keepIdx = rest.indexOf('--keep') |
| 51 | let keepN |
| 52 | if (keepIdx !== -1) { |
| 53 | keepN = Number(rest[keepIdx + 1]) |
| 54 | if (!Number.isInteger(keepN) || keepN < 0) { |
| 55 | throw new Error('--keep takes a non-negative integer') |
| 56 | } |
| 57 | } |
| 58 | // A bare positional, not a flag, and not the value consumed by --keep. |
| 59 | // |
| 60 | // The keepIdx === -1 guard is load-bearing: without it, ABSENT --keep gives |
| 61 | // keepIdx + 1 === 0, which excludes the first positional — so |
| 62 | // `gc hyper-tps --dry-run` dropped the repo, and an omitted repo means EVERY |
| 63 | // repo. A scoped collection would have silently become a fleet-wide one. |
| 64 | const repo = rest.find((a, i) => |
| 65 | !a.startsWith('--') && (keepIdx === -1 || i !== keepIdx + 1)) |
| 66 | return client.call('gc-releases', { repo, keepN, dryRun }) |
| 67 | } |
| 68 | |
| 33 | 69 | // Default: treat first arg as repo name → trigger build |
| 34 | 70 | const ref = args[1] || DEFAULT_REF |
| 35 | 71 | return client.call('build-release', { repo: sub, ref }) |