@devdogsuga/devtools 0.1.26 → 0.1.28

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 (271) hide show
  1. package/README.md +72 -11
  2. package/bin/devtools.mjs +4 -4
  3. package/dist/cli-CZKkNHK5.js +6619 -0
  4. package/dist/failure-log-BBPGejQv.js +256 -0
  5. package/dist/launch.js +267 -281
  6. package/dist/local-env-D8HC2JK_.js +351 -0
  7. package/dist/local-env-DrBCXqD8.js +2 -0
  8. package/dist/menu-CQBsQRgH.js +1684 -0
  9. package/dist/peer-redirect-hooks.js +15 -0
  10. package/dist/peers-RBplwovA.js +229 -0
  11. package/dist/root-C2OmZDwD.js +94 -0
  12. package/dist/stack-Bx-55oi_.js +2 -0
  13. package/dist/stack-BynCgRVS.js +181 -0
  14. package/dist/supabase-project-0knM0mqd.js +426 -0
  15. package/dist/telemetry-C4foD8MD.js +249 -0
  16. package/dist/telemetry.js +2 -148
  17. package/package.json +10 -29
  18. package/bin/devtools-ci-bare.mjs +0 -48
  19. package/bin/devtools-ci.mjs +0 -31
  20. package/dist/args.d.ts +0 -26
  21. package/dist/args.js +0 -78
  22. package/dist/bws/bw.d.ts +0 -18
  23. package/dist/bws/bw.js +0 -70
  24. package/dist/bws/client.d.ts +0 -43
  25. package/dist/bws/client.js +0 -363
  26. package/dist/bws/environments.d.ts +0 -96
  27. package/dist/bws/environments.js +0 -111
  28. package/dist/bws/pace.d.ts +0 -25
  29. package/dist/bws/pace.js +0 -43
  30. package/dist/bws/retry.d.ts +0 -12
  31. package/dist/bws/retry.js +0 -39
  32. package/dist/bws/store.d.ts +0 -1
  33. package/dist/bws/store.js +0 -32
  34. package/dist/bws/token.d.ts +0 -31
  35. package/dist/bws/token.js +0 -88
  36. package/dist/bws/vault.d.ts +0 -29
  37. package/dist/bws/vault.js +0 -200
  38. package/dist/catalog.d.ts +0 -57
  39. package/dist/catalog.js +0 -57
  40. package/dist/cf/build.d.ts +0 -13
  41. package/dist/cf/build.js +0 -65
  42. package/dist/cf/commands.d.ts +0 -1
  43. package/dist/cf/commands.js +0 -172
  44. package/dist/cf/local-env.d.ts +0 -58
  45. package/dist/cf/local-env.js +0 -177
  46. package/dist/ci.d.ts +0 -1
  47. package/dist/ci.js +0 -328
  48. package/dist/cli.d.ts +0 -11
  49. package/dist/cli.js +0 -1094
  50. package/dist/commands.d.ts +0 -261
  51. package/dist/commands.js +0 -1318
  52. package/dist/completions.d.ts +0 -4
  53. package/dist/completions.js +0 -110
  54. package/dist/cron/commands.d.ts +0 -47
  55. package/dist/cron/commands.js +0 -372
  56. package/dist/cron/discovery.d.ts +0 -55
  57. package/dist/cron/discovery.js +0 -85
  58. package/dist/cron/schema.d.ts +0 -45
  59. package/dist/cron/schema.js +0 -35
  60. package/dist/db/config-push.d.ts +0 -8
  61. package/dist/db/config-push.js +0 -11
  62. package/dist/db/connection.d.ts +0 -79
  63. package/dist/db/connection.js +0 -114
  64. package/dist/db/exec.d.ts +0 -1
  65. package/dist/db/exec.js +0 -11
  66. package/dist/db/generate-types.d.ts +0 -1
  67. package/dist/db/generate-types.js +0 -4
  68. package/dist/db/generated-env.d.ts +0 -69
  69. package/dist/db/generated-env.js +0 -113
  70. package/dist/db/introspect.d.ts +0 -1
  71. package/dist/db/introspect.js +0 -122
  72. package/dist/db/local-env.d.ts +0 -65
  73. package/dist/db/local-env.js +0 -23
  74. package/dist/db/new-migration.d.ts +0 -8
  75. package/dist/db/new-migration.js +0 -69
  76. package/dist/db/pooler.d.ts +0 -23
  77. package/dist/db/pooler.js +0 -55
  78. package/dist/db/run.d.ts +0 -68
  79. package/dist/db/run.js +0 -156
  80. package/dist/db/seed-buckets.d.ts +0 -2
  81. package/dist/db/seed-buckets.js +0 -4
  82. package/dist/db/seed-production.d.ts +0 -8
  83. package/dist/db/seed-production.js +0 -79
  84. package/dist/db/seed-roles.d.ts +0 -7
  85. package/dist/db/seed-roles.js +0 -33
  86. package/dist/db/session-refresh.d.ts +0 -43
  87. package/dist/db/session-refresh.js +0 -40
  88. package/dist/deploy/migrations.d.ts +0 -20
  89. package/dist/deploy/migrations.js +0 -68
  90. package/dist/deploy/orphans.d.ts +0 -20
  91. package/dist/deploy/orphans.js +0 -177
  92. package/dist/deploy/preflight.d.ts +0 -53
  93. package/dist/deploy/preflight.js +0 -189
  94. package/dist/deploy/report.d.ts +0 -26
  95. package/dist/deploy/report.js +0 -70
  96. package/dist/deploy/require-planner.d.ts +0 -2
  97. package/dist/deploy/require-planner.js +0 -60
  98. package/dist/deploy/require-token.d.ts +0 -15
  99. package/dist/deploy/require-token.js +0 -78
  100. package/dist/deploy/secrets-file.d.ts +0 -17
  101. package/dist/deploy/secrets-file.js +0 -166
  102. package/dist/deploy/write-env.d.ts +0 -40
  103. package/dist/deploy/write-env.js +0 -395
  104. package/dist/docs/index-pages.d.ts +0 -38
  105. package/dist/docs/index-pages.js +0 -186
  106. package/dist/emails/commands.d.ts +0 -15
  107. package/dist/emails/commands.js +0 -130
  108. package/dist/emails/email-types.d.ts +0 -13
  109. package/dist/emails/email-types.js +0 -1
  110. package/dist/emails/fixtures.d.ts +0 -20
  111. package/dist/emails/fixtures.js +0 -20
  112. package/dist/env/audit.d.ts +0 -206
  113. package/dist/env/audit.js +0 -436
  114. package/dist/env/cloudflare.d.ts +0 -21
  115. package/dist/env/cloudflare.js +0 -61
  116. package/dist/env/commands.d.ts +0 -99
  117. package/dist/env/commands.js +0 -613
  118. package/dist/env/discovery.d.ts +0 -15
  119. package/dist/env/discovery.js +0 -239
  120. package/dist/env/document.d.ts +0 -119
  121. package/dist/env/document.js +0 -323
  122. package/dist/env/example.d.ts +0 -68
  123. package/dist/env/example.js +0 -605
  124. package/dist/env/selection.d.ts +0 -105
  125. package/dist/env/selection.js +0 -228
  126. package/dist/env-entry.d.ts +0 -49
  127. package/dist/env-entry.js +0 -204
  128. package/dist/environment-doctor.d.ts +0 -45
  129. package/dist/environment-doctor.js +0 -429
  130. package/dist/environment.d.ts +0 -92
  131. package/dist/environment.js +0 -185
  132. package/dist/fingerprint.d.ts +0 -10
  133. package/dist/fingerprint.js +0 -18
  134. package/dist/gen/campus-map.d.ts +0 -1
  135. package/dist/gen/campus-map.js +0 -473
  136. package/dist/gen/commands.d.ts +0 -1
  137. package/dist/gen/commands.js +0 -25
  138. package/dist/gen/hypno.d.ts +0 -1
  139. package/dist/gen/hypno.js +0 -134
  140. package/dist/gh/client.d.ts +0 -80
  141. package/dist/gh/client.js +0 -259
  142. package/dist/gh/environments.d.ts +0 -83
  143. package/dist/gh/environments.js +0 -178
  144. package/dist/gh/rulesets/actors.d.ts +0 -14
  145. package/dist/gh/rulesets/actors.js +0 -69
  146. package/dist/gh/rulesets/api.d.ts +0 -15
  147. package/dist/gh/rulesets/api.js +0 -104
  148. package/dist/gh/rulesets/commands.d.ts +0 -3
  149. package/dist/gh/rulesets/commands.js +0 -203
  150. package/dist/gh/rulesets/desired.d.ts +0 -112
  151. package/dist/gh/rulesets/desired.js +0 -271
  152. package/dist/gh/rulesets/diff.d.ts +0 -65
  153. package/dist/gh/rulesets/diff.js +0 -170
  154. package/dist/gh/rulesets/types.d.ts +0 -76
  155. package/dist/gh/rulesets/types.js +0 -13
  156. package/dist/gh/settings/api.d.ts +0 -40
  157. package/dist/gh/settings/api.js +0 -180
  158. package/dist/gh/settings/commands.d.ts +0 -1
  159. package/dist/gh/settings/commands.js +0 -254
  160. package/dist/gh/settings/desired.d.ts +0 -53
  161. package/dist/gh/settings/desired.js +0 -43
  162. package/dist/gh/settings/diff.d.ts +0 -57
  163. package/dist/gh/settings/diff.js +0 -161
  164. package/dist/gh/settings/types.d.ts +0 -92
  165. package/dist/gh/settings/types.js +0 -11
  166. package/dist/gh/settings/workflows.d.ts +0 -41
  167. package/dist/gh/settings/workflows.js +0 -128
  168. package/dist/grantRoot.d.ts +0 -42
  169. package/dist/grantRoot.js +0 -89
  170. package/dist/help.d.ts +0 -18
  171. package/dist/help.js +0 -169
  172. package/dist/images/commands.d.ts +0 -32
  173. package/dist/images/commands.js +0 -231
  174. package/dist/images/events.d.ts +0 -47
  175. package/dist/images/events.js +0 -116
  176. package/dist/images/graphics.d.ts +0 -70
  177. package/dist/images/graphics.js +0 -234
  178. package/dist/images/open-graph-types.d.ts +0 -75
  179. package/dist/images/open-graph-types.js +0 -1
  180. package/dist/images/prompts.d.ts +0 -34
  181. package/dist/images/prompts.js +0 -100
  182. package/dist/images/render.d.ts +0 -21
  183. package/dist/images/render.js +0 -61
  184. package/dist/images/select.d.ts +0 -64
  185. package/dist/images/select.js +0 -96
  186. package/dist/instance.d.ts +0 -69
  187. package/dist/instance.js +0 -102
  188. package/dist/invocation.d.ts +0 -49
  189. package/dist/invocation.js +0 -78
  190. package/dist/launch-ci.d.ts +0 -1
  191. package/dist/launch-ci.js +0 -90
  192. package/dist/launch.d.ts +0 -18
  193. package/dist/menu.d.ts +0 -43
  194. package/dist/menu.js +0 -300
  195. package/dist/moderation.d.ts +0 -70
  196. package/dist/moderation.js +0 -128
  197. package/dist/oauth/browser.d.ts +0 -1
  198. package/dist/oauth/browser.js +0 -13
  199. package/dist/oauth/config.d.ts +0 -24
  200. package/dist/oauth/config.js +0 -24
  201. package/dist/oauth/connect-url.d.ts +0 -17
  202. package/dist/oauth/connect-url.js +0 -23
  203. package/dist/oauth/db.d.ts +0 -37
  204. package/dist/oauth/db.js +0 -108
  205. package/dist/oauth/device.d.ts +0 -93
  206. package/dist/oauth/device.js +0 -198
  207. package/dist/oauth/discovery.d.ts +0 -28
  208. package/dist/oauth/discovery.js +0 -54
  209. package/dist/oauth/env-file.d.ts +0 -6
  210. package/dist/oauth/env-file.js +0 -35
  211. package/dist/oauth/exchange.d.ts +0 -43
  212. package/dist/oauth/exchange.js +0 -93
  213. package/dist/oauth/label.d.ts +0 -1
  214. package/dist/oauth/label.js +0 -25
  215. package/dist/oauth/loopback.d.ts +0 -52
  216. package/dist/oauth/loopback.js +0 -168
  217. package/dist/oauth/pkce.d.ts +0 -6
  218. package/dist/oauth/pkce.js +0 -34
  219. package/dist/oauth/target.d.ts +0 -54
  220. package/dist/oauth/target.js +0 -102
  221. package/dist/oauth/transport.d.ts +0 -41
  222. package/dist/oauth/transport.js +0 -32
  223. package/dist/oauth/wizard.d.ts +0 -40
  224. package/dist/oauth/wizard.js +0 -578
  225. package/dist/persona.d.ts +0 -18
  226. package/dist/persona.js +0 -221
  227. package/dist/pick.d.ts +0 -6
  228. package/dist/pick.js +0 -64
  229. package/dist/pipes.d.ts +0 -1
  230. package/dist/pipes.js +0 -25
  231. package/dist/planner/checks.d.ts +0 -26
  232. package/dist/planner/checks.js +0 -104
  233. package/dist/planner/commands.d.ts +0 -10
  234. package/dist/planner/commands.js +0 -367
  235. package/dist/planner/db.d.ts +0 -15
  236. package/dist/planner/db.js +0 -33
  237. package/dist/planner/role.d.ts +0 -33
  238. package/dist/planner/role.js +0 -68
  239. package/dist/repo/peer-redirect-hooks.d.ts +0 -16
  240. package/dist/repo/peer-redirect-hooks.js +0 -10
  241. package/dist/repo/peer-redirect.d.ts +0 -4
  242. package/dist/repo/peer-redirect.js +0 -26
  243. package/dist/repo/peers.d.ts +0 -39
  244. package/dist/repo/peers.js +0 -139
  245. package/dist/repo/resolve.d.ts +0 -41
  246. package/dist/repo/resolve.js +0 -152
  247. package/dist/repo/root.d.ts +0 -23
  248. package/dist/repo/root.js +0 -91
  249. package/dist/repo/source.d.ts +0 -14
  250. package/dist/repo/source.js +0 -54
  251. package/dist/repo/supabase-project.d.ts +0 -60
  252. package/dist/repo/supabase-project.js +0 -124
  253. package/dist/repo/tsx-loader.d.ts +0 -6
  254. package/dist/repo/tsx-loader.js +0 -41
  255. package/dist/run/pick.d.ts +0 -90
  256. package/dist/run/pick.js +0 -664
  257. package/dist/setup.d.ts +0 -1
  258. package/dist/setup.js +0 -280
  259. package/dist/stack.d.ts +0 -48
  260. package/dist/stack.js +0 -403
  261. package/dist/telemetry.d.ts +0 -59
  262. package/dist/tier.d.ts +0 -40
  263. package/dist/tier.js +0 -96
  264. package/dist/ui.d.ts +0 -40
  265. package/dist/ui.js +0 -71
  266. package/dist/version.d.ts +0 -7
  267. package/dist/version.js +0 -17
  268. package/dist/workers.d.ts +0 -5
  269. package/dist/workers.js +0 -33
  270. package/dist/workflows/commands.d.ts +0 -54
  271. package/dist/workflows/commands.js +0 -763
@@ -1,60 +0,0 @@
1
- /**
2
- * `pnpm devtools deploy require-planner`
3
- *
4
- * Refuses the §3.5 stage-1 dry run when its `DB_URL` is anything more than
5
- * the `migration_planner` role.
6
- *
7
- * The preflight credential is the one narrowed value NOTHING can verify at
8
- * rest: a full-access connection string pasted into `.env.preflight` works
9
- * identically. The dry run succeeds, `env push` uploads it cleanly, and
10
- * `env audit` reports no drift forever, because the stored value matches the
11
- * file it came from. Only a live connection shows the difference, so this
12
- * guard opens one: it must authenticate as the planner, it must FAIL to read
13
- * `platform.profile`, and it must succeed in reading the migrations table the
14
- * dry run is about to plan against. See `planner/checks.ts` for the three
15
- * checks; `planner create` runs the same ones, so a credential that passed at
16
- * minting time passes here.
17
- *
18
- * Runs in the `main-plan` job only. `production-plan` runs the same dry run
19
- * with the PRODUCTION environment's `DB_URL`, which is the full connection
20
- * string by design, since apps boot from it, so this guard would (correctly)
21
- * refuse there. The narrowing belongs to the preflight tier, not to the dry
22
- * run.
23
- *
24
- * Like the rest of the group: `cli:no-env` (the job holds one credential in
25
- * the step's `env:` block and composes no file), reports through `say()`
26
- * (stderr), refuses by `DeployError`. One line per check on success. This
27
- * runs as its own step, and three green lines are the evidence the §3.5
28
- * argument leans on.
29
- */
30
- import { checkPlanner } from "../planner/checks.js";
31
- import { connectDb } from "../planner/db.js";
32
- import { DeployError, say } from "./report.js";
33
- export async function runRequirePlanner(env = process.env, connect = connectDb) {
34
- const url = env.DB_URL;
35
- if (!url) {
36
- throw new DeployError("DB_URL is not set — refusing to plan.", [
37
- "This step runs with the preflight environment's DB_URL: the",
38
- "migration_planner role, pushed by `env push --target preflight`.",
39
- "An empty value usually means the environment secret was never",
40
- "pushed, or the workflow step lost its env: block.",
41
- ]);
42
- }
43
- const db = connect(url);
44
- try {
45
- const verdict = await checkPlanner(db);
46
- for (const line of verdict.lines)
47
- say([`require-planner: ${line}`]);
48
- if (!verdict.ok) {
49
- throw new DeployError(`refusing to plan: ${verdict.problem}`, [
50
- "The preflight DB_URL must be the migration_planner role and no",
51
- "more — this environment is reachable from `main`. Mint or repair",
52
- "it with `pnpm devtools planner create` (or `planner status` to",
53
- "see what is wrong), then `env push --target preflight`.",
54
- ]);
55
- }
56
- }
57
- finally {
58
- await db.end();
59
- }
60
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Refuses the deploy when `CLOUDFLARE_API_TOKEN` is absent, and says nothing
3
- * when it is set.
4
- *
5
- * The silence is deliberate: it stands in front of a deploy inside an `&&`
6
- * chain, and a guard that announces itself on every run is a guard people stop
7
- * reading. The refusal is the only thing it has to say.
8
- *
9
- * It refuses by throwing rather than by returning a code so that `cli.ts`
10
- * renders it like every other deploy failure, through `say()`, which is
11
- * stderr, never `explain()`, which is clack and therefore stdout.
12
- *
13
- * @throws {DeployError} when `CLOUDFLARE_API_TOKEN` is absent or empty.
14
- */
15
- export declare function runRequireToken(env?: Record<string, string | undefined>): void;
@@ -1,78 +0,0 @@
1
- /**
2
- * `pnpm devtools deploy require-token`
3
- *
4
- * Refuses a Cloudflare deploy when `CLOUDFLARE_API_TOKEN` is absent. Without
5
- * it, wrangler falls back to an interactive browser OAuth prompt, which on a
6
- * contributor's machine reads as an invitation to authenticate. The policy
7
- * (design doc §9) is that only devops hold the token and nobody else deploys.
8
- *
9
- * ## ⚠️ Invoke it through `cli:no-env`, never through `cli`
10
- *
11
- * This is a GUARD, not a command anybody types. It runs in two shapes, and
12
- * `cli` (`with-env tsx src/cli.ts`) is wrong for both:
13
- *
14
- * * **Inside the deploy's own `with-env -c '...'` string**, in
15
- * `apps/{platform,schedule-builder,sandbox}/package.json`:
16
- *
17
- * DEPLOY_ENV=production with-env -c '<guard> && wrangler deploy -e production'
18
- *
19
- * The guard must run after with-env loads the env files, because the token
20
- * lives in a devops `.env.production` and never in the ambient
21
- * environment. `guard && with-env deploy` would check the wrong
22
- * environment; `with-env guard && deploy` would drop the loaded env for
23
- * the deploy itself. `-c` gives both commands the same loaded env, and it
24
- * evaluates through @yarnpkg/shell, so the `&&` chain works identically on
25
- * Windows and POSIX. The env is therefore ALREADY LOADED by the time the
26
- * guard runs; a second `with-env` inside it re-loads the same files to no
27
- * effect and prints a second `with-env: loaded ...` line, which makes the
28
- * deploy log claim the environment was selected twice.
29
- *
30
- * * **Bare, in CI**, in the orphan-audit and prune jobs, which run with no
31
- * env file at all, on purpose: they read secret NAMES from Cloudflare and
32
- * need exactly one credential to do it. Here `cli` is an outright bug.
33
- * With no `.env*` present, `with-env` raises `MissingEnvFileError` and
34
- * exits 1 BEFORE this code runs, so the guard would refuse the deploy
35
- * while reporting a missing file instead of a missing token. The wrong
36
- * diagnosis, on the failure path, which is the only path anybody reads.
37
- *
38
- * `cli:no-env` (`tsx --conditions=devdogs-source src/cli.ts`) is the seam for
39
- * both: it loads no env of its own and reads whatever the caller already put
40
- * in `process.env`. Verified by running `pnpm --filter @devdogsuga/devtools
41
- * run cli:no-env` inside a real `with-env -c` string, where it resolves
42
- * through @yarnpkg/shell and adds no second env-selection line.
43
- *
44
- * Nesting `with-env` is otherwise HARMLESS: dotenvx leaves values already in
45
- * the environment alone, so the inner load re-reads the same files and changes
46
- * nothing. The objection is the missing-file case above and the duplicated
47
- * `with-env: loaded ...` line, not a wrong value.
48
- *
49
- * `cf:build:*` scripts deliberately do NOT run this: building needs no token.
50
- */
51
- import { DeployError } from "./report.js";
52
- /**
53
- * Refuses the deploy when `CLOUDFLARE_API_TOKEN` is absent, and says nothing
54
- * when it is set.
55
- *
56
- * The silence is deliberate: it stands in front of a deploy inside an `&&`
57
- * chain, and a guard that announces itself on every run is a guard people stop
58
- * reading. The refusal is the only thing it has to say.
59
- *
60
- * It refuses by throwing rather than by returning a code so that `cli.ts`
61
- * renders it like every other deploy failure, through `say()`, which is
62
- * stderr, never `explain()`, which is clack and therefore stdout.
63
- *
64
- * @throws {DeployError} when `CLOUDFLARE_API_TOKEN` is absent or empty.
65
- */
66
- export function runRequireToken(env = process.env) {
67
- if (env.CLOUDFLARE_API_TOKEN)
68
- return;
69
- throw new DeployError("CLOUDFLARE_API_TOKEN is not set — refusing to deploy.", [
70
- "Cloudflare access is devops-only (design doc §9). Without this guard",
71
- "wrangler falls back to an interactive browser login, which looks like",
72
- "an invitation to authenticate rather than the refusal it should be.",
73
- "",
74
- "Ask the devops team to run this deploy. If you are joining them, the",
75
- "token lives in the devops .env.production:",
76
- " pnpm devtools env pull --target production",
77
- ]);
78
- }
@@ -1,17 +0,0 @@
1
- export interface SecretsFileOptions {
2
- /** `--app`: the workspace app whose manifest names the Worker's secrets. */
3
- app: string;
4
- /** Defaults to the ambient environment; a parameter so tests need not mutate it. */
5
- env?: NodeJS.ProcessEnv;
6
- }
7
- export interface SecretsFileResult {
8
- /** The 0700 directory holding the file; the caller removes it. */
9
- dir: string;
10
- /** The 0600 file itself. */
11
- file: string;
12
- /** Names sent, in registry order. Never values. */
13
- keys: string[];
14
- /** Declared, optional, and absent from the environment. Reported, not fatal. */
15
- omitted: string[];
16
- }
17
- export declare function runDeploySecretsFile(options: SecretsFileOptions): Promise<SecretsFileResult>;
@@ -1,166 +0,0 @@
1
- /**
2
- * `devtools deploy secrets-file --app <app>`
3
- *
4
- * Composes the `--secrets-file` a `wrangler deploy` uploads with one Worker.
5
- *
6
- * Runs INSIDE `with-env`, invoked as the ordinary
7
- * `pnpm devtools deploy secrets-file …`, so `process.env` is the environment
8
- * `devtools deploy write-env` composed a few steps earlier. `write-env` is the
9
- * one command in this group that has to bypass the wrapper. This one reads no
10
- * GitHub context of its own: one place in the pipeline touches `secrets` and
11
- * `vars`, and this is not it.
12
- *
13
- * ## Which keys, and why they are derived rather than listed
14
- *
15
- * `storableKeys()` intersected with the keys the app's own manifest declares.
16
- * A hand-written list per Worker is the exact failure the registry was built
17
- * to remove: a variable added to `src/env.ts` and forgotten in the list is
18
- * simply absent from the deployed environment, and the first thing that reads
19
- * it fails at run time in production.
20
- *
21
- * Two exclusions follow from that rule rather than being special cases:
22
- *
23
- * * `:tooling` sources — declared for the deploy pipeline itself, not for
24
- * the Worker. `study-group-finder:tooling` declares `SECRET_KEY` for
25
- * supadart, which runs on a laptop. A key the deploy needs is not
26
- * automatically a key the WORKER needs, and sending one anyway hands an
27
- * internet-facing Worker a credential it never asks for.
28
- * * `client: true` declarations, inlined into the browser bundle at build
29
- * time and not Worker secrets at all (§A.6.3).
30
- *
31
- * ## ⚠️ Always the complete set
32
- *
33
- * `wrangler deploy --secrets-file` applies ADDITIVELY: it preserves every
34
- * secret it does not mention, which may be one another environment set. A
35
- * partial file inherits stale values silently rather than failing. So this
36
- * sends everything the app declares, every time, and `devtools deploy orphans`
37
- * reports whatever the Worker is still holding that nothing declares any more.
38
- *
39
- * ## Minted keys are refused, not filled
40
- *
41
- * A `secrecy: "secret"` key `storableKeys()` excludes is "minted": signed at
42
- * deploy time rather than stored anywhere, which is what `SANDBOX_PROXY_TOKEN`
43
- * is declared as. This command has no minter of its own — there is nothing in
44
- * this repository that produces one — so a declared minted key is always a
45
- * hard failure here rather than a silent omission: a Worker that expects a
46
- * value substituted fails loudly at the file it never got, not at whatever
47
- * reads it.
48
- *
49
- * ## Interface
50
- *
51
- * DEPLOY_ENV=production pnpm devtools deploy secrets-file --app platform
52
- *
53
- * Writes `dir=` and `file=` to `$GITHUB_OUTPUT`. The caller removes `dir` in an
54
- * `if: always()` step. The runner is ephemeral, so this matters on a
55
- * self-hosted one and costs nothing on a hosted one.
56
- */
57
- import { mkdtempSync, writeFileSync } from "node:fs";
58
- import { tmpdir } from "node:os";
59
- import { join } from "node:path";
60
- import { assertRegistryLoaded } from "../env/discovery.js";
61
- import { getEnvSync } from "../repo/peers.js";
62
- import { DeployError, say, summary } from "./report.js";
63
- /**
64
- * Declared by this app, for this app's Worker.
65
- *
66
- * `source` is the manifest that declared it, and a `:` suffix is the repo's
67
- * existing convention for "declared here, but not part of the running thing".
68
- * See `sectionOf()` in devtools' `env/example.ts`, which folds the two together
69
- * for display and keeps them distinct everywhere else.
70
- */
71
- function declaredBy(entries, app) {
72
- return entries.some((e) => e.source === app && !e.client);
73
- }
74
- export async function runDeploySecretsFile(options) {
75
- assertRegistryLoaded();
76
- const { app } = options;
77
- const env = options.env ?? process.env;
78
- const storable = new Set(getEnvSync().storableKeys());
79
- const send = new Map();
80
- const absent = [];
81
- const minted = [];
82
- for (const [key, entries] of getEnvSync().variables()) {
83
- if (!declaredBy(entries, app))
84
- continue;
85
- const meta = entries[0].meta;
86
- // Public server keys ship alongside the secrets because a value that was
87
- // only in the composed .env file at build time is absent from the deployed
88
- // Worker's runtime environment. The first staging deploy proved it: the
89
- // schedule-builder Worker booted without API_URL, REST_URL,
90
- // PUBLISHABLE_KEY, STORAGE_S3_URL or S3_PROTOCOL_REGION and answered 500
91
- // on every route. Client (NEXT_PUBLIC) keys are excluded by `declaredBy`
92
- // (inlined at build); never-store keys stay excluded because a Worker
93
- // secret is a remote copy, the thing they forbid.
94
- if (meta.secrecy === "secret") {
95
- if (!storable.has(key)) {
96
- minted.push(key);
97
- continue;
98
- }
99
- }
100
- else if (meta.secrecy !== "public") {
101
- continue;
102
- }
103
- const value = env[key];
104
- if (value === undefined || value === "") {
105
- absent.push(key);
106
- continue;
107
- }
108
- send.set(key, value);
109
- }
110
- // Absent is reported, not fatal. `deploy write-env` already failed the job
111
- // for anything an app's schema requires, so what reaches here is optional by
112
- // declaration, `OAUTH_CLIENT_SECRET` on a deployment that uses the built-in
113
- // provider, say. Sending an empty string instead would be worse than omitting
114
- // it: every consumer that checks for presence would read it as configured.
115
- if (absent.length > 0) {
116
- say([
117
- `devtools deploy secrets-file: ${absent.length} optional key(s) have ` +
118
- `no value and are omitted: ${absent.join(", ")}`,
119
- ]);
120
- }
121
- if (minted.length > 0) {
122
- throw new DeployError(`${app} declares minted secret(s) with nothing to mint them: ${minted.join(", ")}.`, [
123
- "This command has no minter — nothing in this repository mints a",
124
- "secret. Drop the `minted: true` declaration from the app's",
125
- "env.ts, or the Worker deploys without it and keeps whatever the",
126
- "previous deploy left — --secrets-file preserves omissions.",
127
- ]);
128
- }
129
- if (send.size === 0) {
130
- throw new DeployError(`${app} has no Worker secrets to send.`, [
131
- "That is almost certainly wrong: a --secrets-file with nothing in it",
132
- "would leave every previously set secret in place, unexamined.",
133
- ]);
134
- }
135
- // mkdtemp gives 0700 on every platform Node supports; the mode is repeated
136
- // anyway so the intent survives a reader who does not know that. RUNNER_TEMP
137
- // is cleared between jobs on a hosted runner and is the documented place for
138
- // this on a self-hosted one.
139
- const dir = mkdtempSync(join(env.RUNNER_TEMP ?? tmpdir(), "deploy-secrets-"));
140
- const file = join(dir, `${app}.json`);
141
- writeFileSync(file, `${JSON.stringify(Object.fromEntries(send), null, 2)}\n`, { mode: 0o600 });
142
- const out = env.GITHUB_OUTPUT;
143
- if (out)
144
- writeFileSync(out, `dir=${dir}\nfile=${file}\n`, { flag: "a" });
145
- const keys = [...send.keys()];
146
- say([
147
- `devtools deploy secrets-file: ${send.size} secret(s) for ${app} -> ${file}`,
148
- ...keys.map((k) => ` ${k}`),
149
- ]);
150
- // Names go to the job summary as well as the log, because this list IS every
151
- // credential the Worker holds. Reading it should not require expanding a
152
- // step: "why does this Worker hold that?" is exactly the question a
153
- // per-deploy record makes answerable, and the answer lives in the app's
154
- // manifest rather than anywhere in this pipeline.
155
- summary([
156
- `### Worker secrets sent to \`${app}\``,
157
- "",
158
- ...keys.map((key) => `* \`${key}\``),
159
- "",
160
- "Names only. The complete set is sent every deploy, because",
161
- "`--secrets-file` preserves what it omits.",
162
- "",
163
- "",
164
- ], env);
165
- return { dir, file, keys, omitted: absent };
166
- }
@@ -1,40 +0,0 @@
1
- /** Where a value came from, for the provenance table. */
2
- export type Provenance = "secret" | "variable" | "derived" | "committed";
3
- export interface WriteEnvOptions {
4
- /** `--source`: compose only what this manifest declares. */
5
- source?: string | null;
6
- /**
7
- * Directory the env file is written into.
8
- *
9
- * The CLI always passes the project root, which is what the workflow means.
10
- * It is a parameter so the tests can compose into a temporary directory:
11
- * the real destinations are `.env.staging` and `.env.production`, which on a
12
- * maintainer's laptop are the files being filled in with live credentials.
13
- */
14
- root?: string;
15
- /** Defaults to the ambient environment; a parameter so tests need not mutate it. */
16
- env?: NodeJS.ProcessEnv;
17
- }
18
- export interface WriteEnvResult {
19
- /** The file that was written, relative to `root`. */
20
- file: string;
21
- /** Key → where its value came from, in the order they were written. */
22
- provenance: Map<string, Provenance>;
23
- }
24
- /**
25
- * Composes the file and writes it.
26
- *
27
- * Every refusal is a `DeployError`, and every one of them ALSO lands in the job
28
- * summary, via the wrapper below. That is deliberate here and not for the other
29
- * commands in the group: the failure mode this exists to catch is a missing
30
- * credential, and the person who has to add it is reading the run page rather
31
- * than a step's log.
32
- */
33
- export declare function runDeployWriteEnv(options?: WriteEnvOptions): Promise<WriteEnvResult>;
34
- /**
35
- * The line the job log gets: names and provenance only.
36
- *
37
- * Never a value, and never a fingerprint either. A fingerprint helps when
38
- * comparing two stores by hand, and there is nothing here to compare against.
39
- */
40
- export declare function renderWriteEnvReport(result: WriteEnvResult): string[];