@m-kopa/launchpad-cli 0.24.0 → 0.25.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/CHANGELOG.md +21 -0
- package/dist/bundle/upload.d.ts +34 -2
- package/dist/bundle/upload.d.ts.map +1 -1
- package/dist/cli.js +115 -38
- package/dist/commands/deploy-flags.d.ts +6 -0
- package/dist/commands/deploy-flags.d.ts.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/status.d.ts +5 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/deploy/apply.d.ts +5 -2
- package/dist/deploy/apply.d.ts.map +1 -1
- package/dist/deploy/deployment-status.d.ts +18 -0
- package/dist/deploy/deployment-status.d.ts.map +1 -1
- package/dist/deploy/rollback.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/skills/launchpad-content-pr/SKILL.md +1 -1
- package/skills/launchpad-deploy/SKILL.md +1 -1
- package/skills/launchpad-deploy-status/SKILL.md +1 -1
- package/skills/launchpad-destroy/SKILL.md +1 -1
- package/skills/launchpad-onboard/SKILL.md +1 -1
- package/skills/launchpad-status/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
|
|
7
7
|
pre-1.0 minor bumps may carry breaking changes per ADR 0005.
|
|
8
8
|
|
|
9
|
+
## 0.25.0 — 2026-06-11
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Redeploying an unchanged app is a clean no-op** (sp-devlp1 AC1 / ADR
|
|
14
|
+
0025). The bot diffs the bundle against the managed repo's `main` (git
|
|
15
|
+
tree-diff) before gating: nothing changed → `launchpad deploy` prints
|
|
16
|
+
"Nothing to deploy" and exits 0 — no policy failure, no junk commit, no
|
|
17
|
+
rebuild. Deploys that DO change files commit only the changed files.
|
|
18
|
+
- **Policy gates judge what you changed, not what you have** (AC2).
|
|
19
|
+
Already-live files are never re-flagged; their pre-existing violations
|
|
20
|
+
surface as a non-blocking drift report (and in `launchpad status`'s new
|
|
21
|
+
standing-exceptions block). Secrets are the exception: a secret anywhere
|
|
22
|
+
in the upload — including one already on `main` — always blocks, with
|
|
23
|
+
rotation guidance.
|
|
24
|
+
- **`deploy --apply` no longer requires local git state** (AC3). The bot
|
|
25
|
+
resolves `launchpad.yaml` at the managed repo's `main` HEAD; your local
|
|
26
|
+
history can share zero commits with the managed repo. New `--at <sha>`
|
|
27
|
+
pins a specific managed-repo commit; `rollback` now uses it too (fixing
|
|
28
|
+
its local-HEAD keying).
|
|
29
|
+
|
|
9
30
|
## 0.24.0 — 2026-06-11
|
|
10
31
|
|
|
11
32
|
### Changed
|
package/dist/bundle/upload.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import type { CliConfig } from "../config.js";
|
|
2
2
|
import type { WorkerArtifact } from "./cron-bundle.js";
|
|
3
|
+
/** Drift report riding on a success response (sp-devlp1 T3 / D2):
|
|
4
|
+
* policy violations observed in content already live on managed
|
|
5
|
+
* main — non-blocking, recorded server-side in the queryable
|
|
6
|
+
* standing-exception inventory. */
|
|
7
|
+
export interface StandingExceptionsSummary {
|
|
8
|
+
readonly count: number;
|
|
9
|
+
readonly entries: ReadonlyArray<{
|
|
10
|
+
readonly path: string;
|
|
11
|
+
readonly rule: string;
|
|
12
|
+
}>;
|
|
13
|
+
readonly message?: string;
|
|
14
|
+
}
|
|
3
15
|
/**
|
|
4
16
|
* Response shape from the bot's deploy-bundle endpoint when the
|
|
5
17
|
* bundle was committed to the per-app repo (HTTP 202, status="accepted").
|
|
@@ -10,6 +22,26 @@ export interface DeployBundleAccepted {
|
|
|
10
22
|
readonly commit_sha: string;
|
|
11
23
|
readonly repo: string;
|
|
12
24
|
readonly message: string;
|
|
25
|
+
/** "delta" (gates judged the changed-set) or "full" (fallback). */
|
|
26
|
+
readonly gating?: "delta" | "full";
|
|
27
|
+
readonly committed_file_count?: number;
|
|
28
|
+
/** Files the bot's server-side app-boundary stripped (inferred
|
|
29
|
+
* contract, never-shippable paths). */
|
|
30
|
+
readonly boundary_stripped?: readonly string[];
|
|
31
|
+
readonly standing_exceptions?: StandingExceptionsSummary;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Response shape when managed main already matches the upload
|
|
35
|
+
* byte-for-byte (HTTP 200, sp-devlp1 AC1): no commit was created,
|
|
36
|
+
* no CF Pages build was triggered.
|
|
37
|
+
*/
|
|
38
|
+
export interface DeployBundleNothingToDeploy {
|
|
39
|
+
readonly outcome: "nothing-to-deploy";
|
|
40
|
+
readonly slug: string;
|
|
41
|
+
readonly head_sha: string;
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly boundary_stripped?: readonly string[];
|
|
44
|
+
readonly standing_exceptions?: StandingExceptionsSummary;
|
|
13
45
|
}
|
|
14
46
|
/**
|
|
15
47
|
* Response shape when the slug was never registered before and the
|
|
@@ -27,8 +59,8 @@ export interface DeployBundleProvisioningStarted {
|
|
|
27
59
|
readonly appType: string;
|
|
28
60
|
readonly message: string;
|
|
29
61
|
}
|
|
30
|
-
/**
|
|
31
|
-
export type DeployBundleSuccess = DeployBundleAccepted | DeployBundleProvisioningStarted;
|
|
62
|
+
/** Any success shape (202 accepted / 202 provisioning / 200 no-op). */
|
|
63
|
+
export type DeployBundleSuccess = DeployBundleAccepted | DeployBundleProvisioningStarted | DeployBundleNothingToDeploy;
|
|
32
64
|
/** Structured error shape from the bot on validation failure. */
|
|
33
65
|
export interface DeployBundleError {
|
|
34
66
|
readonly error: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/bundle/upload.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAgBvD;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/bundle/upload.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var CLI_VERSION = "0.
|
|
22
|
+
var CLI_VERSION = "0.25.0";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -1432,6 +1432,9 @@ async function uploadBundle(cfg, slug, manifestYaml, bundleTarGz, workerArtifact
|
|
|
1432
1432
|
if (res.status === 202) {
|
|
1433
1433
|
return { kind: "ok", response: body };
|
|
1434
1434
|
}
|
|
1435
|
+
if (res.status === 200 && typeof body === "object" && body !== null && body.outcome === "nothing-to-deploy") {
|
|
1436
|
+
return { kind: "ok", response: body };
|
|
1437
|
+
}
|
|
1435
1438
|
return {
|
|
1436
1439
|
kind: "error",
|
|
1437
1440
|
status: res.status,
|
|
@@ -3380,6 +3383,7 @@ function parseDeployFlags(args) {
|
|
|
3380
3383
|
let yes = false;
|
|
3381
3384
|
let resumePrNumber = null;
|
|
3382
3385
|
let timeoutMinutes = null;
|
|
3386
|
+
let atSha = null;
|
|
3383
3387
|
let modeFlagsSeen = 0;
|
|
3384
3388
|
let i = 0;
|
|
3385
3389
|
while (i < args.length) {
|
|
@@ -3532,6 +3536,17 @@ function parseDeployFlags(args) {
|
|
|
3532
3536
|
i += 2;
|
|
3533
3537
|
continue;
|
|
3534
3538
|
}
|
|
3539
|
+
if (a === "--at") {
|
|
3540
|
+
const v = args[i + 1];
|
|
3541
|
+
if (v === undefined)
|
|
3542
|
+
return `missing value for ${a}`;
|
|
3543
|
+
if (!/^[0-9a-f]{40}$/.test(v)) {
|
|
3544
|
+
return `invalid --at "${v}" — expected a 40-char lowercase hex git SHA from the MANAGED repo`;
|
|
3545
|
+
}
|
|
3546
|
+
atSha = v;
|
|
3547
|
+
i += 2;
|
|
3548
|
+
continue;
|
|
3549
|
+
}
|
|
3535
3550
|
if (a === "--resume-pr") {
|
|
3536
3551
|
const v = args[i + 1];
|
|
3537
3552
|
if (v === undefined)
|
|
@@ -3601,6 +3616,7 @@ function parseDeployFlags(args) {
|
|
|
3601
3616
|
rePin,
|
|
3602
3617
|
yes,
|
|
3603
3618
|
resumePrNumber,
|
|
3619
|
+
atSha,
|
|
3604
3620
|
timeoutMinutes
|
|
3605
3621
|
},
|
|
3606
3622
|
message: null,
|
|
@@ -3610,6 +3626,9 @@ function parseDeployFlags(args) {
|
|
|
3610
3626
|
if (resumePrNumber !== null) {
|
|
3611
3627
|
return "--resume-pr is only valid with --apply";
|
|
3612
3628
|
}
|
|
3629
|
+
if (atSha !== null) {
|
|
3630
|
+
return "--at is only valid with --apply";
|
|
3631
|
+
}
|
|
3613
3632
|
if (timeoutMinutes !== null) {
|
|
3614
3633
|
return "--timeout-minutes is only valid with --apply";
|
|
3615
3634
|
}
|
|
@@ -3687,7 +3706,6 @@ function parseDeployFlags(args) {
|
|
|
3687
3706
|
// src/deploy/apply.ts
|
|
3688
3707
|
import { existsSync as existsSync3, rmSync } from "node:fs";
|
|
3689
3708
|
import { resolve as resolvePath, join as join7 } from "node:path";
|
|
3690
|
-
import { execFileSync } from "node:child_process";
|
|
3691
3709
|
|
|
3692
3710
|
// src/manifest/load.ts
|
|
3693
3711
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -3780,29 +3798,23 @@ async function runDeployApply(opts, io, deps = {}) {
|
|
|
3780
3798
|
io.err(`launchpad deploy --apply: manifest's metadata.name '${slug}' is not a valid slug`);
|
|
3781
3799
|
return 1;
|
|
3782
3800
|
}
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
} catch (e) {
|
|
3787
|
-
io.err(`launchpad deploy --apply: failed to resolve git HEAD in ${process.cwd()}: ${describe10(e)}`);
|
|
3788
|
-
io.err(" Apply needs a clean commit so portal-bot can fetch launchpad.yaml at that sha.");
|
|
3789
|
-
return 2;
|
|
3790
|
-
}
|
|
3791
|
-
if (!MANIFEST_SHA_REGEX.test(manifestSha)) {
|
|
3792
|
-
io.err(`launchpad deploy --apply: git HEAD returned an unexpected sha shape: '${manifestSha}'`);
|
|
3793
|
-
return 2;
|
|
3801
|
+
if (opts.atSha !== undefined && !MANIFEST_SHA_REGEX.test(opts.atSha)) {
|
|
3802
|
+
io.err(`launchpad deploy --apply: --at expects a 40-char lowercase hex git SHA, got '${opts.atSha}'`);
|
|
3803
|
+
return 64;
|
|
3794
3804
|
}
|
|
3795
|
-
io.out(`Planning apply for ${slug} @ ${
|
|
3805
|
+
io.out(opts.atSha !== undefined ? `Planning apply for ${slug} @ ${opts.atSha.slice(0, 7)} (managed repo) …` : `Planning apply for ${slug} (bot resolves managed main HEAD) …`);
|
|
3796
3806
|
let plan;
|
|
3797
3807
|
try {
|
|
3798
3808
|
plan = await apiJson(cfg, {
|
|
3799
3809
|
method: "POST",
|
|
3800
3810
|
path: `/apps/${slug}/manifest/plan`,
|
|
3801
|
-
jsonBody: { manifestSha }
|
|
3811
|
+
jsonBody: opts.atSha !== undefined ? { manifestSha: opts.atSha } : {}
|
|
3802
3812
|
}, fetcher);
|
|
3803
3813
|
} catch (e) {
|
|
3804
3814
|
return mapHttpError(e, slug, io);
|
|
3805
3815
|
}
|
|
3816
|
+
const manifestSha = plan.manifestSha;
|
|
3817
|
+
io.out(`Manifest pinned @ ${manifestSha.slice(0, 7)} (managed repo provenance).`);
|
|
3806
3818
|
for (const w of plan.warnings)
|
|
3807
3819
|
io.err(`! ${w}`);
|
|
3808
3820
|
io.out("");
|
|
@@ -3922,14 +3934,6 @@ function sleep(ms) {
|
|
|
3922
3934
|
return Promise.resolve();
|
|
3923
3935
|
return new Promise((res) => setTimeout(res, ms));
|
|
3924
3936
|
}
|
|
3925
|
-
function defaultGitHeadSha(cwd) {
|
|
3926
|
-
const out = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
3927
|
-
cwd,
|
|
3928
|
-
encoding: "utf8",
|
|
3929
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3930
|
-
});
|
|
3931
|
-
return out.trim();
|
|
3932
|
-
}
|
|
3933
3937
|
function deletePinIfPresent(cfg, slug, io) {
|
|
3934
3938
|
const pinPath = join7(cfg.stateDir, slug, "group.json");
|
|
3935
3939
|
if (!existsSync3(pinPath))
|
|
@@ -4012,7 +4016,7 @@ function renderManifestError(loaded, io) {
|
|
|
4012
4016
|
}
|
|
4013
4017
|
|
|
4014
4018
|
// src/deploy/dry-run.ts
|
|
4015
|
-
import { execFileSync
|
|
4019
|
+
import { execFileSync } from "node:child_process";
|
|
4016
4020
|
import { resolve as resolve5 } from "node:path";
|
|
4017
4021
|
var MANIFEST_SHA_REGEX2 = /^[0-9a-f]{40}$/;
|
|
4018
4022
|
async function runDeployDryRun(opts, io, deps = {}) {
|
|
@@ -4026,7 +4030,7 @@ async function runDeployDryRun(opts, io, deps = {}) {
|
|
|
4026
4030
|
const slug = loaded.manifest.metadata.name;
|
|
4027
4031
|
let manifestSha;
|
|
4028
4032
|
try {
|
|
4029
|
-
manifestSha = (deps.gitHeadSha ??
|
|
4033
|
+
manifestSha = (deps.gitHeadSha ?? defaultGitHeadSha)(process.cwd());
|
|
4030
4034
|
} catch (e) {
|
|
4031
4035
|
const msg = `failed to resolve git HEAD in ${process.cwd()}: ${describe11(e)}`;
|
|
4032
4036
|
if (opts.json) {
|
|
@@ -4069,8 +4073,8 @@ async function runDeployDryRun(opts, io, deps = {}) {
|
|
|
4069
4073
|
}
|
|
4070
4074
|
return 0;
|
|
4071
4075
|
}
|
|
4072
|
-
function
|
|
4073
|
-
const out =
|
|
4076
|
+
function defaultGitHeadSha(cwd) {
|
|
4077
|
+
const out = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
4074
4078
|
cwd,
|
|
4075
4079
|
encoding: "utf8",
|
|
4076
4080
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4402,6 +4406,7 @@ async function runDeploy(args, io) {
|
|
|
4402
4406
|
platformRepo: flags.mode.platformRepo,
|
|
4403
4407
|
rePin: flags.mode.rePin,
|
|
4404
4408
|
yes: flags.mode.yes,
|
|
4409
|
+
...flags.mode.atSha !== null ? { atSha: flags.mode.atSha } : {},
|
|
4405
4410
|
...flags.mode.resumePrNumber !== null ? { resumePrNumber: flags.mode.resumePrNumber } : {},
|
|
4406
4411
|
...flags.mode.timeoutMinutes !== null ? { timeoutMinutes: flags.mode.timeoutMinutes } : {}
|
|
4407
4412
|
}, io);
|
|
@@ -4597,6 +4602,28 @@ function formatBytes2(n) {
|
|
|
4597
4602
|
function describe12(e) {
|
|
4598
4603
|
return e instanceof Error ? e.message : String(e);
|
|
4599
4604
|
}
|
|
4605
|
+
function surfaceDeployExtras(body, io, slug) {
|
|
4606
|
+
if (body.boundary_stripped !== undefined && body.boundary_stripped.length > 0) {
|
|
4607
|
+
io.err(`warning: the bot stripped ${body.boundary_stripped.length} never-shippable file(s) server-side:`);
|
|
4608
|
+
for (const p of body.boundary_stripped.slice(0, 10)) {
|
|
4609
|
+
io.err(` - ${p}`);
|
|
4610
|
+
}
|
|
4611
|
+
if (body.boundary_stripped.length > 10) {
|
|
4612
|
+
io.err(` … and ${body.boundary_stripped.length - 10} more`);
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
const se = body.standing_exceptions;
|
|
4616
|
+
if (se !== undefined && se.count > 0) {
|
|
4617
|
+
io.out(` ${se.count} standing policy exception(s) observed in content already live on main (non-blocking):`);
|
|
4618
|
+
for (const e of se.entries.slice(0, 5)) {
|
|
4619
|
+
io.out(` - ${e.path} [${e.rule}]`);
|
|
4620
|
+
}
|
|
4621
|
+
if (se.entries.length > 5) {
|
|
4622
|
+
io.out(` … and ${se.entries.length - 5} more`);
|
|
4623
|
+
}
|
|
4624
|
+
io.out(` Full list: \`launchpad status ${slug}\`.`);
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4600
4627
|
function resolveManifestSlug(parsed) {
|
|
4601
4628
|
if (parsed === null || typeof parsed !== "object" || typeof parsed.metadata !== "object" || parsed.metadata === null) {
|
|
4602
4629
|
return null;
|
|
@@ -4694,10 +4721,19 @@ async function runModelADeploy(args) {
|
|
|
4694
4721
|
for (const w of result.boundaryWarnings) {
|
|
4695
4722
|
io.err(`warning: ${w}`);
|
|
4696
4723
|
}
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4724
|
+
const success = result.result;
|
|
4725
|
+
if ("outcome" in success) {
|
|
4726
|
+
io.out("Nothing to deploy — your app is already live at this content.");
|
|
4727
|
+
if (typeof success.head_sha === "string") {
|
|
4728
|
+
io.out(` (managed main @ ${success.head_sha.slice(0, 8)} matches the bundle)`);
|
|
4729
|
+
}
|
|
4730
|
+
surfaceDeployExtras(success, io, slug);
|
|
4731
|
+
return 0;
|
|
4732
|
+
}
|
|
4733
|
+
if (success.status === "provisioning_started") {
|
|
4734
|
+
const submissionId = typeof success.submissionId === "string" ? success.submissionId : "(missing)";
|
|
4735
|
+
const appType = typeof success.appType === "string" ? success.appType : "(missing)";
|
|
4736
|
+
const message = typeof success.message === "string" ? success.message : "Bot accepted the upload and started provisioning.";
|
|
4701
4737
|
io.out(`✓ First-time deploy — provisioning workflow started for ${slug}`);
|
|
4702
4738
|
io.out(` submission: ${submissionId}`);
|
|
4703
4739
|
io.out(` appType: ${appType}`);
|
|
@@ -4709,12 +4745,12 @@ async function runModelADeploy(args) {
|
|
|
4709
4745
|
io.out(` launchpad deploy # re-run once lifecycle is "live"`);
|
|
4710
4746
|
return 0;
|
|
4711
4747
|
}
|
|
4712
|
-
if (typeof
|
|
4748
|
+
if (typeof success.commit_sha !== "string" || typeof success.repo !== "string") {
|
|
4713
4749
|
io.err(`launchpad deploy: bot returned an unexpected 202 body — missing commit_sha or repo.`);
|
|
4714
|
-
io.err(` got: ${JSON.stringify(
|
|
4750
|
+
io.err(` got: ${JSON.stringify(success)}`);
|
|
4715
4751
|
return 1;
|
|
4716
4752
|
}
|
|
4717
|
-
io.out(`✓ Bundle accepted — committed as ${
|
|
4753
|
+
io.out(`✓ Bundle accepted — committed as ${success.commit_sha.slice(0, 8)} on ${success.repo}`);
|
|
4718
4754
|
io.out(` ${result.fileCount} files (${formatBytes2(result.compressedBytes)} gzipped)`);
|
|
4719
4755
|
if (result.workerScript !== null) {
|
|
4720
4756
|
io.out(` cron Worker '${result.workerScript}' bundled + shipped (the bot deploys it after the commit)`);
|
|
@@ -4722,9 +4758,10 @@ async function runModelADeploy(args) {
|
|
|
4722
4758
|
if (result.walk.skipped.length > 0) {
|
|
4723
4759
|
io.out(` ${result.walk.skipped.length} entries skipped (.gitignore / default-ignore / symlink)`);
|
|
4724
4760
|
}
|
|
4725
|
-
if (typeof
|
|
4726
|
-
io.out(` ${
|
|
4761
|
+
if (typeof success.message === "string") {
|
|
4762
|
+
io.out(` ${success.message}`);
|
|
4727
4763
|
}
|
|
4764
|
+
surfaceDeployExtras(success, io, slug);
|
|
4728
4765
|
io.out("");
|
|
4729
4766
|
io.out("Committed; build pending — Cloudflare Pages is building this deploy now.");
|
|
4730
4767
|
io.out(`Run \`launchpad status ${slug}\` to confirm the build outcome (success / failure + log excerpt).`);
|
|
@@ -7270,6 +7307,19 @@ async function fetchManifestStatus(cfg, slug, fetcher = fetch) {
|
|
|
7270
7307
|
}
|
|
7271
7308
|
|
|
7272
7309
|
// src/deploy/deployment-status.ts
|
|
7310
|
+
async function fetchStandingExceptions(cfg, slug, fetcher = fetch) {
|
|
7311
|
+
let raw;
|
|
7312
|
+
try {
|
|
7313
|
+
raw = await apiJson(cfg, { path: `/apps/${encodeURIComponent(slug)}/exceptions` }, fetcher);
|
|
7314
|
+
} catch (e) {
|
|
7315
|
+
if (e instanceof NotFoundError)
|
|
7316
|
+
return null;
|
|
7317
|
+
throw e;
|
|
7318
|
+
}
|
|
7319
|
+
if (!Array.isArray(raw.exceptions))
|
|
7320
|
+
return null;
|
|
7321
|
+
return raw.exceptions.filter((e) => typeof e === "object" && e !== null && typeof e.path === "string" && typeof e.rule === "string" && typeof e.detectedAt === "string" && typeof e.deployRef === "string");
|
|
7322
|
+
}
|
|
7273
7323
|
async function fetchDeploymentStatus(cfg, slug, fetcher = fetch) {
|
|
7274
7324
|
let raw;
|
|
7275
7325
|
try {
|
|
@@ -7557,6 +7607,15 @@ async function runStatus(args, io) {
|
|
|
7557
7607
|
}
|
|
7558
7608
|
io.err(`launchpad status: live deployment state unavailable (${describe24(e)}) — ` + `the report below is from the platform manifest view only.`);
|
|
7559
7609
|
}
|
|
7610
|
+
let standingExceptions = null;
|
|
7611
|
+
try {
|
|
7612
|
+
standingExceptions = await fetchStandingExceptions(cfg, parsed.slug);
|
|
7613
|
+
} catch (e) {
|
|
7614
|
+
if (e instanceof UnauthenticatedError || e instanceof ForbiddenError) {
|
|
7615
|
+
return mapBotError(e, parsed.slug, io);
|
|
7616
|
+
}
|
|
7617
|
+
io.err(`launchpad status: standing-exception inventory unavailable (${describe24(e)}).`);
|
|
7618
|
+
}
|
|
7560
7619
|
if (state.manifestYaml === null || state.manifestYaml === undefined) {
|
|
7561
7620
|
const live = deployment?.liveDeployment ?? null;
|
|
7562
7621
|
const contentIsLive = live !== null && (live.buildStatus === "success" || deployment?.lastSuccessfulDeployment != null);
|
|
@@ -7570,7 +7629,8 @@ async function runStatus(args, io) {
|
|
|
7570
7629
|
openPrNumber: state.openPr?.number ?? null,
|
|
7571
7630
|
driftFields: [],
|
|
7572
7631
|
driftDetails: [],
|
|
7573
|
-
...deploymentKnown ? { deployment } : {}
|
|
7632
|
+
...deploymentKnown ? { deployment } : {},
|
|
7633
|
+
...standingExceptions !== null ? { standingExceptions } : {}
|
|
7574
7634
|
};
|
|
7575
7635
|
emit3(out, parsed.json, io);
|
|
7576
7636
|
return 0;
|
|
@@ -7602,7 +7662,8 @@ async function runStatus(args, io) {
|
|
|
7602
7662
|
openPrNumber: state.openPr?.number ?? null,
|
|
7603
7663
|
driftFields: drift.map((d) => d.path),
|
|
7604
7664
|
driftDetails: drift,
|
|
7605
|
-
...deploymentKnown ? { deployment } : {}
|
|
7665
|
+
...deploymentKnown ? { deployment } : {},
|
|
7666
|
+
...standingExceptions !== null ? { standingExceptions } : {}
|
|
7606
7667
|
};
|
|
7607
7668
|
emit3(result, parsed.json, io);
|
|
7608
7669
|
if (result.state === "drift" && parsed.strict) {
|
|
@@ -7689,21 +7750,25 @@ function emit3(out, asJson, io) {
|
|
|
7689
7750
|
io.out(`${out.slug}: live — no content deployed yet. Run \`launchpad deploy\`.`);
|
|
7690
7751
|
surfaceHeadVsDeployed(out, io);
|
|
7691
7752
|
surfaceDeployment(out, io);
|
|
7753
|
+
surfaceExceptions(out, io);
|
|
7692
7754
|
return;
|
|
7693
7755
|
case "no_deployed_manifest":
|
|
7694
7756
|
io.out(`${out.slug}: no deployed manifest yet — run \`launchpad deploy\`.`);
|
|
7695
7757
|
surfaceHeadVsDeployed(out, io);
|
|
7696
7758
|
surfaceDeployment(out, io);
|
|
7759
|
+
surfaceExceptions(out, io);
|
|
7697
7760
|
return;
|
|
7698
7761
|
case "live_content_untracked":
|
|
7699
7762
|
io.out(`${out.slug}: live — content deployed via ` + `${triggerLabel(out.deployment?.liveDeployment?.trigger)} (no platform-tracked manifest; this app deploys outside \`launchpad deploy\`).`);
|
|
7700
7763
|
surfaceHeadVsDeployed(out, io);
|
|
7701
7764
|
surfaceDeployment(out, io);
|
|
7765
|
+
surfaceExceptions(out, io);
|
|
7702
7766
|
return;
|
|
7703
7767
|
case "in_sync":
|
|
7704
7768
|
io.out(`${out.slug}: live, in sync` + (out.deployedSha ? ` (content @ ${out.deployedSha.slice(0, 7)})` : ""));
|
|
7705
7769
|
surfaceHeadVsDeployed(out, io);
|
|
7706
7770
|
surfaceDeployment(out, io);
|
|
7771
|
+
surfaceExceptions(out, io);
|
|
7707
7772
|
return;
|
|
7708
7773
|
case "drift":
|
|
7709
7774
|
io.out(`${out.slug}: live, drift: ${out.driftFields.join(", ")}`);
|
|
@@ -7714,6 +7779,7 @@ function emit3(out, asJson, io) {
|
|
|
7714
7779
|
}
|
|
7715
7780
|
surfaceHeadVsDeployed(out, io);
|
|
7716
7781
|
surfaceDeployment(out, io);
|
|
7782
|
+
surfaceExceptions(out, io);
|
|
7717
7783
|
return;
|
|
7718
7784
|
}
|
|
7719
7785
|
}
|
|
@@ -7763,6 +7829,16 @@ function surfaceDeployment(out, io) {
|
|
|
7763
7829
|
}
|
|
7764
7830
|
}
|
|
7765
7831
|
}
|
|
7832
|
+
function surfaceExceptions(out, io) {
|
|
7833
|
+
const exceptions = out.standingExceptions;
|
|
7834
|
+
if (exceptions === undefined || exceptions.length === 0)
|
|
7835
|
+
return;
|
|
7836
|
+
io.out(` standing exceptions: ${exceptions.length} policy violation(s) in content already live on main (non-blocking):`);
|
|
7837
|
+
for (const e of exceptions) {
|
|
7838
|
+
io.out(` - ${e.path} [${e.rule}]`);
|
|
7839
|
+
}
|
|
7840
|
+
io.out(" these never block a deploy that doesn't change them, but they are never grandfathered silently — clean them up in a future deploy.");
|
|
7841
|
+
}
|
|
7766
7842
|
function surfaceHeadVsDeployed(out, io) {
|
|
7767
7843
|
if (out.headSha !== null && out.deployedSha !== null && out.headSha !== out.deployedSha) {
|
|
7768
7844
|
const short = (s) => s.slice(0, 7);
|
|
@@ -8160,6 +8236,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
8160
8236
|
file: opts.file,
|
|
8161
8237
|
platformRepo: null,
|
|
8162
8238
|
rePin: false,
|
|
8239
|
+
atSha: verifiedSha,
|
|
8163
8240
|
yes: true
|
|
8164
8241
|
}, io, applyDeps);
|
|
8165
8242
|
}
|
|
@@ -54,6 +54,12 @@ export type DeployMode = {
|
|
|
54
54
|
* so we use a distinct flag here.
|
|
55
55
|
*/
|
|
56
56
|
readonly resumePrNumber: number | null;
|
|
57
|
+
/**
|
|
58
|
+
* `--at <sha>` (sp-devlp1 AC3) — pin the manifest to a specific
|
|
59
|
+
* MANAGED-repo sha. Default (null) lets the bot resolve managed
|
|
60
|
+
* main HEAD; local git state is irrelevant either way.
|
|
61
|
+
*/
|
|
62
|
+
readonly atSha: string | null;
|
|
57
63
|
/**
|
|
58
64
|
* `--timeout-minutes <n>` — override the 90-minute default
|
|
59
65
|
* polling timeout (AC-U2). Apply-only.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-flags.d.ts","sourceRoot":"","sources":["../../src/commands/deploy-flags.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1D;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;GAOG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACpF;;;;;;;;;;GAUG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"deploy-flags.d.ts","sourceRoot":"","sources":["../../src/commands/deploy-flags.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1D;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;GAOG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACpF;;;;;;;;;;GAUG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,GAAG,MAAM,CAqU9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAgDA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAM/B,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAEjE,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAgDA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAM/B,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAEjE,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAwPD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAwBpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI3D;AA6GD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAYxC;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAalE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DeploymentStatusView } from "../deploy/deployment-status.js";
|
|
1
|
+
import { type DeploymentStatusView, type StandingExceptionView } from "../deploy/deployment-status.js";
|
|
2
2
|
import { type Manifest } from "@m-kopa/launchpad-engine";
|
|
3
3
|
import type { Command } from "../dispatcher.js";
|
|
4
4
|
export declare const statusCommand: Command;
|
|
@@ -42,6 +42,10 @@ export interface StatusJsonOutput {
|
|
|
42
42
|
* Absent when the bot pre-dates the endpoint; null when fetched but
|
|
43
43
|
* the app has no Pages surface. */
|
|
44
44
|
readonly deployment?: DeploymentStatusView | null;
|
|
45
|
+
/** Standing-exception inventory (sp-devlp1 T3 / D2): non-blocking
|
|
46
|
+
* policy violations in content already live on managed main.
|
|
47
|
+
* Absent when the bot pre-dates the endpoint. */
|
|
48
|
+
readonly standingExceptions?: readonly StandingExceptionView[];
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* Compute drift over the v1 closed field set (AC5).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAyCA,OAAO,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAyCA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC3B,MAAM,gCAAgC,CAAC;AASxC,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAEjE,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAuBD;;;uEAGuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EACV,cAAc,GACd,qBAAqB,GACrB,iBAAiB;IACnB;;wDAEoD;OAClD,wBAAwB,GACxB,SAAS,GACT,OAAO,GACP,sBAAsB,GACtB,YAAY,GACZ,WAAW,GACX,gBAAgB,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC;QACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC5B,CAAC,CAAC;IACH;;;wCAGoC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD;;sDAEkD;IAClD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAChE;AAyOD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,QAAQ,GACjB,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CA2DpE;AA4PD,2CAA2C;AAC3C,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAsB,GAC1B,UAAU,GAAG,MAAM,CAgErB"}
|
package/dist/deploy/apply.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface ApplyOptions {
|
|
|
9
9
|
* Removal in v0.13 alongside `--platform-repo`. */
|
|
10
10
|
readonly rePin: boolean;
|
|
11
11
|
readonly yes: boolean;
|
|
12
|
+
/** `--at <sha>` — pin the manifest to a specific MANAGED-repo sha
|
|
13
|
+
* instead of letting the bot resolve managed main HEAD (sp-devlp1
|
|
14
|
+
* AC3). The sha must exist in the managed repo; a local-only sha
|
|
15
|
+
* is rejected by the bot with a pointer at the no-sha path. */
|
|
16
|
+
readonly atSha?: string;
|
|
12
17
|
/** Re-attach to an in-flight apply for `--resume <prNumber>`. */
|
|
13
18
|
readonly resumePrNumber?: number;
|
|
14
19
|
/** Override the 90-minute default polling timeout (AC-U2). */
|
|
@@ -20,8 +25,6 @@ export interface ApplyDeps {
|
|
|
20
25
|
readonly config?: CliConfig;
|
|
21
26
|
/** Inject `fetch` for tests; defaults to `globalThis.fetch`. */
|
|
22
27
|
readonly fetcher?: typeof fetch;
|
|
23
|
-
/** Inject `git rev-parse` resolution for tests. */
|
|
24
|
-
readonly gitHeadSha?: (cwd: string) => string;
|
|
25
28
|
/** Polling interval seconds (defaults to 10s; tests use 0). */
|
|
26
29
|
readonly pollIntervalSeconds?: number;
|
|
27
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAU1D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;iCAC6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;oEAGgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAwCD,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CAoLnB"}
|
|
@@ -27,6 +27,24 @@ export interface DeploymentStatusView {
|
|
|
27
27
|
readonly url: string | null;
|
|
28
28
|
} | null;
|
|
29
29
|
}
|
|
30
|
+
/** One standing-exception inventory entry (sp-devlp1 T3 / D2) — a
|
|
31
|
+
* policy/boundary/build-command violation observed in content already
|
|
32
|
+
* live on managed main. Non-blocking; recorded by the bot on every
|
|
33
|
+
* deploy that observes it. Mirror of the bot's `StandingException`. */
|
|
34
|
+
export interface StandingExceptionView {
|
|
35
|
+
readonly path: string;
|
|
36
|
+
readonly rule: string;
|
|
37
|
+
readonly detectedAt: string;
|
|
38
|
+
readonly deployRef: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetch the app's standing-exception inventory via
|
|
42
|
+
* `GET /apps/<slug>/exceptions` (owner/editor/break-glass). Returns
|
|
43
|
+
* `null` when the bot pre-dates the endpoint (404) or the response is
|
|
44
|
+
* shape-less — callers omit the block in that case. Auth errors
|
|
45
|
+
* propagate.
|
|
46
|
+
*/
|
|
47
|
+
export declare function fetchStandingExceptions(cfg: CliConfig, slug: string, fetcher?: typeof fetch): Promise<readonly StandingExceptionView[] | null>;
|
|
30
48
|
/**
|
|
31
49
|
* Fetch the live deployment status. Returns `null` when the bot
|
|
32
50
|
* pre-dates the endpoint (404) or the response doesn't carry the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-status.d.ts","sourceRoot":"","sources":["../../src/deploy/deployment-status.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnD,yEAAyE;IACzE,QAAQ,CAAC,wBAAwB,EAAE;QACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,IAAI,CAAC;CACV;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuBtC"}
|
|
1
|
+
{"version":3,"file":"deployment-status.d.ts","sourceRoot":"","sources":["../../src/deploy/deployment-status.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnD,yEAAyE;IACzE,QAAQ,CAAC,wBAAwB,EAAE;QACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,IAAI,CAAC;CACV;AAED;;;wEAGwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,SAAS,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAsBlD;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuBtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/deploy/rollback.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIxD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD;;;gDAG4C;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAChC;AAUD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/deploy/rollback.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIxD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD;;;gDAG4C;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAChC;AAUD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,QAAQ,CAAC,CAyInB"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.
|
|
1
|
+
export declare const CLI_VERSION = "0.25.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-content-pr
|
|
3
3
|
description: Push a content change to a Launchpad app via `launchpad deploy` and verify it shipped via `launchpad status`. Covers the post-first-deploy iteration loop (edit → deploy → verify) and the stack-fit pre-flight that the bot enforces server-side. Use when someone says "push a content change", "ship an update", "/launchpad-content-pr", "verify my deploy", or after `/launchpad-deploy` reports `done` and they want to follow up with an edit.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy
|
|
3
3
|
description: Walk a Launchpad user through deploying an app from their local working directory (Model A — `launchpad init` + `launchpad deploy`). Wraps the CLI verbs end-to-end: detects the app shape, scaffolds `launchpad.yaml`, resolves the allowed Entra group via `launchpad groups`, bundles the CWD via `launchpad deploy`, and tails the resulting content PR. Use when someone says "deploy a new app", "ship my app to Launchpad", "/launchpad-deploy", "I have an app locally — get it on Launchpad", or any variant. Resume/abandon for legacy in-flight provisioning is at the bottom.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy-status
|
|
3
3
|
description: Show the current provisioning stage + failure reason for a Launchpad app via `launchpad status` (Model A drift + deployment_verified) and `launchpad apps` (lifecycle bucket). Renders the M-892 stage trace for legacy in-flight provisioning. Use when someone says "what's the status of demo-X", "/launchpad-deploy-status", "is my deploy stuck", or after `/launchpad-deploy` reports a non-`done` terminal stage.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-destroy
|
|
3
3
|
description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, Access app, custom hostname, platform-repo TF entries, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-onboard
|
|
3
3
|
description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the Cf Access session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-status
|
|
3
3
|
description: Show whether a Launchpad app's local launchpad.yaml matches what's deployed, and read the deployed manifest. Wraps `launchpad pull` (fetch deployed YAML) and `launchpad status` (drift report). Use when someone says "is my app in sync", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or after `launchpad deploy` to verify the change landed.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|