@m-kopa/launchpad-cli 0.23.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 CHANGED
@@ -6,6 +6,71 @@ 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
+
30
+ ## 0.24.0 — 2026-06-11
31
+
32
+ ### Changed
33
+
34
+ - **`launchpad status` is truthful for ALL deploy mechanisms** (sp-devlp1 AC6).
35
+ Status now reads the app's LIVE Cloudflare Pages deployment state via the
36
+ bot's new `/apps/<slug>/deployment-status` endpoint: last deployment time,
37
+ its trigger (git push vs `launchpad deploy`), and the build outcome. An app
38
+ that deploys via CF Pages native git integration (push-to-main) reports
39
+ `live — content deployed via git push` instead of the misleading
40
+ "no content deployed" (the sp-gitst1 incident class). `--json` carries a
41
+ `deployment` block and the new `live_content_untracked` state.
42
+ - **Failed builds are visible** (sp-devlp1 AC7). When the latest production
43
+ build failed, `launchpad status` shows the failed stage, a build-log
44
+ excerpt (proxied via the bot), what is still serving, and the CLI next
45
+ step — killing the sp-reloc1 silent-failed-build class. In-progress builds
46
+ say so. `launchpad deploy` (bundle path) and `launchpad merge` end with an
47
+ explicit "build pending — run `launchpad status`" handoff.
48
+ - **`launchpad pull`'s "no content deployed" claim comes from the live
49
+ source** (AC6): an app with live git-integration content gets a truthful
50
+ "has live content; deploys outside `launchpad deploy`" message.
51
+ - **Error messages never direct you at resources you can't access** (AC8).
52
+ Reworded messages that pointed at the Cloudflare dashboard / GitHub PR
53
+ page; every failure path names a CLI command (or local file fix) as the
54
+ next step, enforced by a message-catalogue audit test.
55
+
56
+ ### Added
57
+
58
+ - **App-boundary allowlist bundling** (sp-devlp1 / AC4 + AC5). Both deploy
59
+ paths (Model A CWD bundle + the content-PR path) filter the file list
60
+ through the engine's app-boundary contract: with an `app:` block in
61
+ `launchpad.yaml`, ONLY the declared app ships; without one, the whole
62
+ tree minus the platform deny-list ships (compat) and stripped files are
63
+ warned. `.launchpadignore` (repo root, gitignore subset, no `!`) adds
64
+ excludes. The build-inputs check fails a deploy whose `build.command`
65
+ references inputs missing from the bundle (sp-reloc1 class).
66
+ - **`launchpad validate` boundary checks**: warns to declare `app:` on
67
+ pre-contract manifests, surfaces the strips the next deploy would
68
+ perform, and exits 1 on declared-contract deny-list hits or confident
69
+ build-input misses. `--json` gains an `appBoundary` block.
70
+ - **`launchpad init` emits the `app:` contract explicitly** (root `.` +
71
+ the per-appType default include list) so new manifests never rely on
72
+ the inferred fallback.
73
+
9
74
  ## 0.23.0 — 2026-06-11
10
75
 
11
76
  ### Changed
@@ -0,0 +1,26 @@
1
+ export type BoundaryFilterResult = {
2
+ kind: "ok";
3
+ /** The post-boundary bundle file list (repo-relative, sorted). */
4
+ files: readonly string[];
5
+ /** Stripped-file notices (inferred mode) — surface to the user. */
6
+ warnings: readonly string[];
7
+ /** True when the manifest declares no `app:` block. */
8
+ inferred: boolean;
9
+ } | {
10
+ kind: "error";
11
+ message: string;
12
+ };
13
+ export interface ApplyBoundaryArgs {
14
+ /** Repo root the relative `files` resolve against. */
15
+ readonly cwd: string;
16
+ /** Raw `launchpad.yaml` bytes, or null when absent/unreadable. */
17
+ readonly manifestYaml: string | null;
18
+ /** Candidate file list (from the walker / `git ls-files`). */
19
+ readonly files: readonly string[];
20
+ }
21
+ /**
22
+ * Filter a candidate bundle file list through the app-boundary
23
+ * contract. See module header for the severity policy.
24
+ */
25
+ export declare function applyBoundaryToFiles(args: ApplyBoundaryArgs): BoundaryFilterResult;
26
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/bundle/boundary.ts"],"names":[],"mappings":"AAuCA,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,IAAI,CAAC;IACX,kEAAkE;IAClE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,mEAAmE;IACnE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,EAAE,OAAO,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,CA8FlF"}
@@ -12,6 +12,9 @@ export type BundleAndDeployResult = {
12
12
  } | {
13
13
  kind: "no-manifest";
14
14
  message: string;
15
+ } | {
16
+ kind: "boundary-error";
17
+ message: string;
15
18
  } | {
16
19
  kind: "pack-error";
17
20
  message: string;
@@ -32,6 +35,8 @@ export type BundleAndDeployResult = {
32
35
  compressedBytes: number;
33
36
  /** The cron Worker script name that was built + shipped, if any. */
34
37
  workerScript: string | null;
38
+ /** App-boundary strip notices (pre-contract manifests) — surface to the user. */
39
+ boundaryWarnings: readonly string[];
35
40
  };
36
41
  /**
37
42
  * Bundle the user's CWD and ship it to the bot.
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrate.d.ts","sourceRoot":"","sources":["../../src/bundle/orchestrate.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGtE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvD;IACE,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IAChE,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEN;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAoFhC"}
1
+ {"version":3,"file":"orchestrate.d.ts","sourceRoot":"","sources":["../../src/bundle/orchestrate.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAItE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvD;IACE,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IAChE,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iFAAiF;IACjF,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC,CAAC;AAEN;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAkGhC"}
@@ -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
- /** Either 202 success shape. */
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;CAC1B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,gCAAgC;AAChC,MAAM,MAAM,mBAAmB,GAC3B,oBAAoB,GACpB,+BAA+B,CAAC;AAEpC,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,UAAU,EACvB,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAsF7B"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/bundle/upload.ts"],"names":[],"mappings":"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"}