@m-kopa/launchpad-cli 0.27.0 → 0.27.2
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 +40 -0
- package/dist/cli.js +48 -3
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/skills/launchpad-content-pr/SKILL.md +146 -124
- package/skills/launchpad-deploy/SKILL.md +153 -67
- package/skills/launchpad-deploy-status/SKILL.md +136 -36
- package/skills/launchpad-destroy/SKILL.md +163 -65
- package/skills/launchpad-onboard/SKILL.md +43 -14
- package/skills/launchpad-status/SKILL.md +119 -25
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,46 @@ 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.27.2 — 2026-06-15
|
|
10
|
+
|
|
11
|
+
Fix: `launchpad login --help` and `launchpad logout --help` (and `-h`)
|
|
12
|
+
now print usage and exit without side effects. Previously both verbs
|
|
13
|
+
ignored their arguments and ran the real flow, so asking for help
|
|
14
|
+
actually signed you in / revoked + cleared your session. The flags are
|
|
15
|
+
now documented on each command's docs page.
|
|
16
|
+
|
|
17
|
+
## 0.27.1 — 2026-06-12
|
|
18
|
+
|
|
19
|
+
Bundled-skills refresh (sp-s4k9wm) — no CLI code changes. Every
|
|
20
|
+
`launchpad-*` skill audited claim-by-claim against shipped behaviour
|
|
21
|
+
(141 claims: 43 corrected, 13 coverage gaps filled):
|
|
22
|
+
|
|
23
|
+
- **Auth guidance** now matches the 0.27.0 gateway login: gateway-first
|
|
24
|
+
sign-in, the ≤ 0.26.x `launchpad update && launchpad login` recovery,
|
|
25
|
+
silent rotating-refresh session model (the "~24 h Cf Access session"
|
|
26
|
+
framing is gone).
|
|
27
|
+
- **`launchpad-content-pr`** reworked to the real deploy model:
|
|
28
|
+
subsequent deploys commit directly to the app repo's `main` (no
|
|
29
|
+
content PR), the CLI returns at the bot's 202 ack, and verification
|
|
30
|
+
is its own `launchpad status` step. The fictional "stack-fit
|
|
31
|
+
pre-flight" is replaced by the real gates (bundle policy, secret
|
|
32
|
+
scan, build-command allowlist — delta-judged per ADR 0025).
|
|
33
|
+
- **`launchpad-deploy`** uses `launchpad init`'s real flag vocabulary,
|
|
34
|
+
documents group displayName|UUID resolution, pages-tier D1
|
|
35
|
+
(`d1_binding`) auto-provisioning incl. the empty-DB gotcha, and the
|
|
36
|
+
real server-side gate set.
|
|
37
|
+
- **`launchpad-deploy-status`** carries the full stage taxonomy
|
|
38
|
+
(`content_seeded`, the `tf_env_*` trio) and folds in
|
|
39
|
+
`launchpad recover` for terminal-failed-but-serving apps.
|
|
40
|
+
- **`launchpad-status`** documents the full lifecycle/live-truth state
|
|
41
|
+
union and the live Pages build-outcome rendering.
|
|
42
|
+
- **`launchpad-destroy`** documents the per-app-workspace dispatch
|
|
43
|
+
teardown (no destroy PR on that path) and that the app's D1 database
|
|
44
|
+
is never dropped.
|
|
45
|
+
|
|
46
|
+
Run `launchpad skills update` after upgrading to pick up the refreshed
|
|
47
|
+
bundle.
|
|
48
|
+
|
|
9
49
|
## 0.27.0 — 2026-06-12
|
|
10
50
|
|
|
11
51
|
`launchpad login` moves onto the platform's auth gateway (sp-cli7kq
|
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.27.
|
|
22
|
+
var CLI_VERSION = "0.27.2";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -6724,7 +6724,11 @@ function makeLoginCommand(deps = REAL_DEPS) {
|
|
|
6724
6724
|
run: (args, io) => runLogin(args, io, deps)
|
|
6725
6725
|
};
|
|
6726
6726
|
}
|
|
6727
|
-
async function runLogin(
|
|
6727
|
+
async function runLogin(args, io, deps) {
|
|
6728
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6729
|
+
printLoginHelp(io);
|
|
6730
|
+
return 0;
|
|
6731
|
+
}
|
|
6728
6732
|
try {
|
|
6729
6733
|
const cfg = loadConfig();
|
|
6730
6734
|
if (cfg.authLegacy) {
|
|
@@ -6775,6 +6779,27 @@ async function runLegacyLogin(io, botUrl, sessionPath, deps) {
|
|
|
6775
6779
|
io.out(`Access token expires in ~${expiresIn}s; refreshes silently.`);
|
|
6776
6780
|
return 0;
|
|
6777
6781
|
}
|
|
6782
|
+
function printLoginHelp(io) {
|
|
6783
|
+
io.out("launchpad login — authenticate and store a session.");
|
|
6784
|
+
io.out("");
|
|
6785
|
+
io.out("Usage:");
|
|
6786
|
+
io.out(" launchpad login Sign in via the browser and store a session");
|
|
6787
|
+
io.out("");
|
|
6788
|
+
io.out("Opens your browser to sign in with your M-KOPA Microsoft account");
|
|
6789
|
+
io.out("through the Launchpad auth gateway (loopback PKCE), then writes the");
|
|
6790
|
+
io.out("session to ~/.launchpad/session.json. The short-lived access token");
|
|
6791
|
+
io.out("refreshes silently as you use the CLI.");
|
|
6792
|
+
io.out("");
|
|
6793
|
+
io.out("Environment:");
|
|
6794
|
+
io.out(" LAUNCHPAD_AUTH_LEGACY=1 Force the legacy Cloudflare Access flow");
|
|
6795
|
+
io.out(" (deprecated; removed when the dual-auth");
|
|
6796
|
+
io.out(" window closes)");
|
|
6797
|
+
io.out(" LAUNCHPAD_AUTH_GATEWAY_URL Override the gateway base URL (testing)");
|
|
6798
|
+
io.out("");
|
|
6799
|
+
io.out("Exit codes:");
|
|
6800
|
+
io.out(" 0 signed in / session stored");
|
|
6801
|
+
io.out(" 1 login failed (network, browser, or gateway error)");
|
|
6802
|
+
}
|
|
6778
6803
|
function describe19(e) {
|
|
6779
6804
|
return e instanceof Error ? e.message : String(e);
|
|
6780
6805
|
}
|
|
@@ -6789,7 +6814,11 @@ function makeLogoutCommand(deps = REAL_DEPS2) {
|
|
|
6789
6814
|
run: (args, io) => runLogout(args, io, deps)
|
|
6790
6815
|
};
|
|
6791
6816
|
}
|
|
6792
|
-
async function runLogout(
|
|
6817
|
+
async function runLogout(args, io, deps) {
|
|
6818
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6819
|
+
printLogoutHelp(io);
|
|
6820
|
+
return 0;
|
|
6821
|
+
}
|
|
6793
6822
|
try {
|
|
6794
6823
|
const cfg = loadConfig();
|
|
6795
6824
|
let session = null;
|
|
@@ -6819,6 +6848,22 @@ async function runLogout(_args, io, deps) {
|
|
|
6819
6848
|
return 1;
|
|
6820
6849
|
}
|
|
6821
6850
|
}
|
|
6851
|
+
function printLogoutHelp(io) {
|
|
6852
|
+
io.out("launchpad logout — revoke the session server-side and clear it locally.");
|
|
6853
|
+
io.out("");
|
|
6854
|
+
io.out("Usage:");
|
|
6855
|
+
io.out(" launchpad logout Revoke server-side, then clear the local session");
|
|
6856
|
+
io.out("");
|
|
6857
|
+
io.out("Gateway (v2) sessions are revoked server-side (the refresh token dies");
|
|
6858
|
+
io.out("immediately; any in-flight access token expires within ~15 minutes),");
|
|
6859
|
+
io.out("then the local ~/.launchpad/session.json is cleared. Logout also works");
|
|
6860
|
+
io.out("offline: if the gateway is unreachable the local session is cleared");
|
|
6861
|
+
io.out("anyway with a warning, and the exit code stays 0.");
|
|
6862
|
+
io.out("");
|
|
6863
|
+
io.out("Exit codes:");
|
|
6864
|
+
io.out(" 0 logged out (or already logged out)");
|
|
6865
|
+
io.out(" 1 could not clear the local session (file IO / config error)");
|
|
6866
|
+
}
|
|
6822
6867
|
function describe20(e) {
|
|
6823
6868
|
return e instanceof Error ? e.message : String(e);
|
|
6824
6869
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.27.
|
|
1
|
+
export declare const CLI_VERSION = "0.27.2";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m-kopa/launchpad-cli",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.2",
|
|
4
4
|
"description": "Launchpad CLI — clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"homepage": "https://github.com/M-KOPA/launchpad-platform/tree/main/packages/launchpad-cli#readme",
|
|
29
29
|
"license": "UNLICENSED",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"esbuild": "0.
|
|
31
|
+
"esbuild": "0.28.1",
|
|
32
32
|
"yaml": "2.9.0",
|
|
33
33
|
"zod": "4.4.3"
|
|
34
34
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-content-pr
|
|
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)
|
|
4
|
-
version: 0.27.
|
|
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) — subsequent deploys commit directly to the app repo's main and the Pages build runs asynchronously, so verification is its own step. 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.27.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -33,17 +33,23 @@ esac
|
|
|
33
33
|
Push a content change to an already-provisioned Launchpad app, then
|
|
34
34
|
verify it shipped.
|
|
35
35
|
|
|
36
|
+
The slash-command name is historical: subsequent deploys do **not**
|
|
37
|
+
open a content PR any more. The bot commits the bundle **directly to
|
|
38
|
+
the app repo's `main`** and Cloudflare Pages builds from that commit
|
|
39
|
+
asynchronously — so "ship" and "verify" are two separate steps, and
|
|
40
|
+
this skill covers both.
|
|
41
|
+
|
|
36
42
|
Under Model A the first deploy and the first content are the same
|
|
37
43
|
event — `launchpad init` + `launchpad deploy` from the user's CWD
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
stages the working tree with the provisioning run: the workflow's
|
|
45
|
+
`content_seeded` stage commits it straight onto the app repo's
|
|
46
|
+
`main`, the bot waits for the Cloudflare Pages deployment to come up
|
|
47
|
+
green (`deployment_verified`), and the lifecycle flips to `live`.
|
|
48
|
+
There is no separate "now push content" step.
|
|
42
49
|
|
|
43
50
|
This skill is therefore the **iteration** companion to
|
|
44
51
|
`/launchpad-deploy`: once an app is live, how do you ship the next
|
|
45
|
-
change
|
|
46
|
-
pre-flight (the rules the bot enforces server-side on every bundle).
|
|
52
|
+
change — and how do you know it's actually serving.
|
|
47
53
|
|
|
48
54
|
## Pre-flight
|
|
49
55
|
|
|
@@ -65,7 +71,10 @@ Look for the slug in the `live` lifecycle bucket. If it's in
|
|
|
65
71
|
`provisioning` / `failed` / `destroying` / `destroyed`, the
|
|
66
72
|
iteration loop is not the right tool — route to
|
|
67
73
|
`/launchpad-deploy-status` (in-flight) or `/launchpad-destroy`
|
|
68
|
-
(teardown) instead.
|
|
74
|
+
(teardown) instead. If it shows `failed` but the app is actually
|
|
75
|
+
serving (e.g. a verification timed out after the real deploy
|
|
76
|
+
succeeded), `launchpad recover <slug>` reconciles the record against
|
|
77
|
+
live Cloudflare state.
|
|
69
78
|
|
|
70
79
|
## Edit and ship
|
|
71
80
|
|
|
@@ -75,92 +84,84 @@ The iteration loop is two verbs:
|
|
|
75
84
|
# 1. Edit your working tree.
|
|
76
85
|
|
|
77
86
|
# 2. Ship it.
|
|
78
|
-
launchpad deploy
|
|
87
|
+
launchpad deploy
|
|
79
88
|
```
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
What happens:
|
|
91
|
+
|
|
92
|
+
- The CLI bundles the CWD with a **pure-FS walker** (no `git`
|
|
93
|
+
needed) honouring `.gitignore` plus a default-ignore set; symlinks
|
|
94
|
+
are never followed. The bundle is gzipped and uploaded to the bot.
|
|
95
|
+
- The bot runs its server-side gates (next section), then commits
|
|
96
|
+
the bundle **directly to `main`** on `launchpad-app-<slug>` via
|
|
97
|
+
the Git Data API — you are the commit author, the bot is the
|
|
98
|
+
committer. No PR, no merge step.
|
|
99
|
+
- The CLI returns at the bot's 202 ack — `✓ Bundle accepted —
|
|
100
|
+
committed as <sha>`, then "Committed; build pending". It does
|
|
101
|
+
**not** wait for the Pages build: a successful deploy is **not** a
|
|
102
|
+
live app yet — the build runs asynchronously and can fail after
|
|
103
|
+
the commit lands. Always confirm with `launchpad status`.
|
|
104
|
+
|
|
105
|
+
Flags worth knowing:
|
|
106
|
+
|
|
107
|
+
- The slug comes from `./launchpad.yaml` (`metadata.slug` /
|
|
108
|
+
`metadata.name`). On this path `--slug` does **not** override it —
|
|
109
|
+
it only overrides directory-name inference on the legacy clone
|
|
110
|
+
flow (no local manifest).
|
|
111
|
+
- **`--message`** is sent as a forward-compat header on the legacy
|
|
112
|
+
path only, and **the bot currently ignores it**. Don't rely on it
|
|
113
|
+
for change logs or audit trails.
|
|
114
|
+
- **`--file`** is valid only with `--dry-run` / `--apply`
|
|
115
|
+
(manifest-driven modes), not with a content deploy.
|
|
97
116
|
|
|
98
117
|
Exit codes:
|
|
99
118
|
|
|
100
|
-
- **0** —
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
pm2
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
The bot's secret-scan + build-command policy rejects bundles where
|
|
148
|
-
the source tree contains:
|
|
149
|
-
|
|
150
|
-
- `process.env.X` / `process.env['X']` reads — switch to `c.env.*`
|
|
151
|
-
bindings.
|
|
152
|
-
- `setInterval` / `setTimeout` daemons — Workers do not run between
|
|
153
|
-
requests; use Cron Triggers for periodic work.
|
|
154
|
-
- High-signal secret patterns (AWS access keys, GitHub PATs / OAuth
|
|
155
|
-
/ app tokens, Slack tokens, SSH/RSA/EC/PGP keys, generic
|
|
156
|
-
`api_key` shapes). These never belong in a bundle — push them
|
|
157
|
-
through `launchpad secrets push` instead.
|
|
158
|
-
|
|
159
|
-
### `react+api` requires `nodejs_compat`
|
|
160
|
-
|
|
161
|
-
The bot checks that `wrangler.toml` declares
|
|
162
|
-
`compatibility_flags = ["nodejs_compat"]` for any `react+api` app
|
|
163
|
-
(ADR-0011 carve-out).
|
|
119
|
+
- **0** — bundle accepted and committed (or "nothing to deploy"
|
|
120
|
+
when `main` already matches the bundle). This is **not** proof the
|
|
121
|
+
app is live — verify with `launchpad status`.
|
|
122
|
+
- **non-zero** — gate rejections are rendered with per-file detail
|
|
123
|
+
on stderr; fix the bundle and re-run. For provisioning-phase
|
|
124
|
+
failures see `/launchpad-deploy-status <slug>`.
|
|
125
|
+
|
|
126
|
+
## What the bot enforces on every bundle
|
|
127
|
+
|
|
128
|
+
The canonical gate list and caps live in `/launchpad-deploy
|
|
129
|
+
§ Constants (single source of truth)`. In user-visible terms, every
|
|
130
|
+
upload passes:
|
|
131
|
+
|
|
132
|
+
- **Bundle policy** — file-count / bundle-size / per-file-size caps,
|
|
133
|
+
symlink and path-traversal/absolute-path rejection, and auth-file
|
|
134
|
+
rules (`.github/workflows`, CODEOWNERS, `.npmrc`/`.yarnrc`
|
|
135
|
+
carrying auth tokens, `.git/`).
|
|
136
|
+
- **Secret scan** — high-signal secret patterns (AWS access keys,
|
|
137
|
+
GitHub PATs / OAuth / app tokens, Slack tokens, SSH/RSA/EC/PGP
|
|
138
|
+
private keys, generic `api_key` shapes). These never belong in a
|
|
139
|
+
bundle — push them through `launchpad secrets push` instead.
|
|
140
|
+
- **Build-command allowlist** — `build.command` must match the
|
|
141
|
+
bot's allowlist.
|
|
142
|
+
- **App boundary (CLI-side, before upload)** — files outside
|
|
143
|
+
`app.root` / `app.include` are stripped with a warning, and
|
|
144
|
+
key/cert-shaped files (`*.pem`, `*.key`, `id_rsa*`, `*.p12`) are
|
|
145
|
+
denied client-side.
|
|
146
|
+
|
|
147
|
+
Gates are **delta-judged** (ADR 0025): they evaluate what your
|
|
148
|
+
deploy *changes* relative to `main`, not everything the workspace
|
|
149
|
+
contains. A pre-existing violation already live on `main` becomes a
|
|
150
|
+
non-blocking **standing exception** — recorded by the bot and
|
|
151
|
+
surfaced by both `launchpad deploy` and `launchpad status` (the bot
|
|
152
|
+
serves the inventory at `GET /apps/<slug>/exceptions`). So "my old
|
|
153
|
+
violation didn't block this deploy" is by design, not a missed gate.
|
|
154
|
+
|
|
155
|
+
There is **no** server-side scan of `package.json` dependencies,
|
|
156
|
+
top-level files, or source patterns. Express/Koa-style servers,
|
|
157
|
+
native DB drivers (`pg`, `better-sqlite3`, …), `dotenv`, and
|
|
158
|
+
pm2/Docker artefacts are not rejected at deploy time — they simply
|
|
159
|
+
won't run on the Cloudflare Pages + Workers runtime and fail at
|
|
160
|
+
build or runtime instead. Likewise `react+api`'s
|
|
161
|
+
`compatibility_flags = ["nodejs_compat"]` in `wrangler.toml`
|
|
162
|
+
(ADR-0011) is **required but not validated** — a missing flag fails
|
|
163
|
+
at runtime. Treat the stack-constraints table in `/launchpad-deploy`
|
|
164
|
+
as advisory architecture guidance, not an enforced gate.
|
|
164
165
|
|
|
165
166
|
### Verifying locally before you ship
|
|
166
167
|
|
|
@@ -172,34 +173,47 @@ launchpad validate
|
|
|
172
173
|
launchpad plan
|
|
173
174
|
```
|
|
174
175
|
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
Both are offline by default (no bot). `launchpad validate
|
|
177
|
+
--strict-groups` opts in to an online check that resolves
|
|
178
|
+
`access.allowed_entra_group` (needs a session).
|
|
177
179
|
|
|
178
|
-
## Verify
|
|
180
|
+
## Verify the deploy
|
|
179
181
|
|
|
180
182
|
```bash
|
|
181
183
|
launchpad status <slug>
|
|
182
184
|
```
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
`launchpad status` reports manifest drift **and** the live
|
|
187
|
+
Cloudflare Pages build truth — last deployment, what triggered it,
|
|
188
|
+
build outcome, and a failure-log excerpt when the build broke. (See
|
|
189
|
+
`/launchpad-status` for the full state list.) What you're looking
|
|
190
|
+
for after a deploy:
|
|
191
|
+
|
|
192
|
+
- **`live, in sync`** with `last deployment: build success` — your
|
|
193
|
+
commit landed and the build is serving. You're done.
|
|
194
|
+
- **`last deployment: build FAILED at stage "<stage>"`** (+ log
|
|
195
|
+
excerpt) — the commit landed but the build broke; the previous
|
|
196
|
+
successful deployment is still serving. Fix the cause and re-run
|
|
197
|
+
`launchpad deploy`.
|
|
198
|
+
- **`build IN PROGRESS`** — re-run `launchpad status` shortly to
|
|
199
|
+
confirm the outcome.
|
|
200
|
+
- **`drift: <fields>`** — your local manifest diverges from what's
|
|
190
201
|
deployed. Either re-run `launchpad deploy` to ship the local, or
|
|
191
202
|
`launchpad pull <slug> --out launchpad.yaml` to bring the local
|
|
192
203
|
into line with deployed.
|
|
193
|
-
-
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
- **provisioning / failed / destroy states** — route to
|
|
205
|
+
`/launchpad-deploy-status` or `/launchpad-destroy`.
|
|
206
|
+
|
|
207
|
+
Do not declare a change shipped until `status` shows the build
|
|
208
|
+
outcome for your commit.
|
|
196
209
|
|
|
197
210
|
You can also point a browser at `https://<slug>.launchpad.m-kopa.us`
|
|
198
|
-
once status reports
|
|
199
|
-
for a gateway-fronted app (the default, `auth: gateway`)
|
|
200
|
-
redirect to the Entra-OIDC gateway (Microsoft sign-in) on
|
|
201
|
-
request; for an `auth: access` app expect a redirect to
|
|
202
|
-
`*.cloudflareaccess.com`. Either way you land on your app after
|
|
211
|
+
once status reports the build green. An SSO gate sits in front of
|
|
212
|
+
the app: for a gateway-fronted app (the default, `auth: gateway`)
|
|
213
|
+
expect a redirect to the Entra-OIDC gateway (Microsoft sign-in) on
|
|
214
|
+
the first request; for an `auth: access` app expect a redirect to
|
|
215
|
+
`*.cloudflareaccess.com`. Either way you land on your app after
|
|
216
|
+
sign-in.
|
|
203
217
|
|
|
204
218
|
If the URL serves the wrong thing:
|
|
205
219
|
|
|
@@ -218,16 +232,23 @@ If the URL serves the wrong thing:
|
|
|
218
232
|
Once you've shipped first content, the daily-use verbs are:
|
|
219
233
|
|
|
220
234
|
- **`launchpad status`** (`/launchpad-status`) — is my local
|
|
221
|
-
`launchpad.yaml` in sync with what's deployed
|
|
235
|
+
`launchpad.yaml` in sync with what's deployed, and did the last
|
|
236
|
+
build succeed?
|
|
222
237
|
- **`launchpad pull <slug>`** (`/launchpad-status`) — read the
|
|
223
238
|
currently-deployed `launchpad.yaml`.
|
|
224
|
-
- **`launchpad deploy`** —
|
|
225
|
-
|
|
239
|
+
- **`launchpad deploy`** — bundle the working tree; the bot commits
|
|
240
|
+
it directly to the app repo's `main`.
|
|
226
241
|
- **`launchpad envvars`** — list / set / remove non-secret
|
|
227
242
|
production env vars.
|
|
228
|
-
- **`launchpad secrets
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
- **`launchpad secrets template`** — emit `.env.example` from the
|
|
244
|
+
manifest's secret bindings.
|
|
245
|
+
- **`launchpad secrets push`** — push secret values from `.env`
|
|
246
|
+
(never via git).
|
|
247
|
+
- **`launchpad secrets status`** — names-only PRESENT/MISSING audit
|
|
248
|
+
per binding.
|
|
249
|
+
- **`launchpad logs --slug <slug>`** — recent Cloudflare Pages
|
|
250
|
+
deployment history (default 10 entries, max 25 via `--lines`).
|
|
251
|
+
Note: the slug is flag-only, not positional.
|
|
231
252
|
- **`launchpad rollback`** — revert manifest to a prior git SHA +
|
|
232
253
|
re-apply.
|
|
233
254
|
|
|
@@ -237,17 +258,18 @@ Once you've shipped first content, the daily-use verbs are:
|
|
|
237
258
|
skill. Every step is a `launchpad` verb. External users without
|
|
238
259
|
M-KOPA GitHub access need this skill to work end-to-end on the
|
|
239
260
|
CLI alone.
|
|
240
|
-
- Do **not** open
|
|
241
|
-
bot
|
|
242
|
-
bypasses the bundle
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
- Do **not**
|
|
249
|
-
|
|
250
|
-
|
|
261
|
+
- Do **not** push commits or open PRs by hand against the app repo.
|
|
262
|
+
The bot commits through a ruleset bypass it alone holds, and a
|
|
263
|
+
hand-rolled change bypasses the bundle policy, secret-scan,
|
|
264
|
+
build-command gates, and the standing-exception ledger. Use
|
|
265
|
+
`launchpad deploy`.
|
|
266
|
+
- Do **not** treat exit 0 from `launchpad deploy` as "live". The
|
|
267
|
+
commit landed, but the Pages build runs asynchronously and can
|
|
268
|
+
fail afterwards — always verify with `launchpad status`.
|
|
269
|
+
- Do **not** re-implement the server-side gates locally.
|
|
270
|
+
`launchpad validate` plus the CLI's own app-boundary pre-flight
|
|
271
|
+
cover the local half; the bot enforces the rest server-side and
|
|
272
|
+
renders violations verbatim — surface them and let the user fix
|
|
251
273
|
the bundle.
|
|
252
274
|
- Do **not** edit `launchpad.yaml`'s `production_env:` block to
|
|
253
275
|
contain secret values. That block is non-secret by contract;
|