@devdogsuga/devtools 0.1.25 → 0.1.27
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/README.md +72 -11
- package/bin/devtools.mjs +4 -4
- package/dist/cli-CZKkNHK5.js +6619 -0
- package/dist/failure-log-BBPGejQv.js +256 -0
- package/dist/launch.js +267 -281
- package/dist/local-env-D8HC2JK_.js +351 -0
- package/dist/local-env-DrBCXqD8.js +2 -0
- package/dist/menu-CQBsQRgH.js +1684 -0
- package/dist/peer-redirect-hooks.js +15 -0
- package/dist/peers-RBplwovA.js +229 -0
- package/dist/root-C2OmZDwD.js +94 -0
- package/dist/stack-Bx-55oi_.js +2 -0
- package/dist/stack-BynCgRVS.js +181 -0
- package/dist/supabase-project-0knM0mqd.js +426 -0
- package/dist/telemetry-C4foD8MD.js +249 -0
- package/dist/telemetry.js +2 -148
- package/package.json +10 -29
- package/bin/devtools-ci-bare.mjs +0 -48
- package/bin/devtools-ci.mjs +0 -31
- package/dist/args.d.ts +0 -26
- package/dist/args.js +0 -78
- package/dist/bws/bw.d.ts +0 -18
- package/dist/bws/bw.js +0 -70
- package/dist/bws/client.d.ts +0 -43
- package/dist/bws/client.js +0 -363
- package/dist/bws/environments.d.ts +0 -96
- package/dist/bws/environments.js +0 -111
- package/dist/bws/pace.d.ts +0 -25
- package/dist/bws/pace.js +0 -43
- package/dist/bws/retry.d.ts +0 -12
- package/dist/bws/retry.js +0 -39
- package/dist/bws/store.d.ts +0 -1
- package/dist/bws/store.js +0 -32
- package/dist/bws/token.d.ts +0 -31
- package/dist/bws/token.js +0 -88
- package/dist/bws/vault.d.ts +0 -29
- package/dist/bws/vault.js +0 -200
- package/dist/catalog.d.ts +0 -57
- package/dist/catalog.js +0 -57
- package/dist/cf/build.d.ts +0 -13
- package/dist/cf/build.js +0 -65
- package/dist/cf/commands.d.ts +0 -1
- package/dist/cf/commands.js +0 -172
- package/dist/cf/local-env.d.ts +0 -58
- package/dist/cf/local-env.js +0 -177
- package/dist/ci.d.ts +0 -1
- package/dist/ci.js +0 -328
- package/dist/cli.d.ts +0 -11
- package/dist/cli.js +0 -1094
- package/dist/commands.d.ts +0 -261
- package/dist/commands.js +0 -1318
- package/dist/completions.d.ts +0 -4
- package/dist/completions.js +0 -110
- package/dist/cron/commands.d.ts +0 -47
- package/dist/cron/commands.js +0 -372
- package/dist/cron/discovery.d.ts +0 -55
- package/dist/cron/discovery.js +0 -85
- package/dist/cron/schema.d.ts +0 -45
- package/dist/cron/schema.js +0 -35
- package/dist/db/config-push.d.ts +0 -8
- package/dist/db/config-push.js +0 -11
- package/dist/db/connection.d.ts +0 -79
- package/dist/db/connection.js +0 -114
- package/dist/db/exec.d.ts +0 -1
- package/dist/db/exec.js +0 -11
- package/dist/db/generate-types.d.ts +0 -1
- package/dist/db/generate-types.js +0 -4
- package/dist/db/generated-env.d.ts +0 -69
- package/dist/db/generated-env.js +0 -113
- package/dist/db/introspect.d.ts +0 -1
- package/dist/db/introspect.js +0 -122
- package/dist/db/local-env.d.ts +0 -65
- package/dist/db/local-env.js +0 -23
- package/dist/db/new-migration.d.ts +0 -8
- package/dist/db/new-migration.js +0 -69
- package/dist/db/pooler.d.ts +0 -23
- package/dist/db/pooler.js +0 -55
- package/dist/db/run.d.ts +0 -68
- package/dist/db/run.js +0 -156
- package/dist/db/seed-buckets.d.ts +0 -2
- package/dist/db/seed-buckets.js +0 -4
- package/dist/db/seed-production.d.ts +0 -8
- package/dist/db/seed-production.js +0 -79
- package/dist/db/seed-roles.d.ts +0 -7
- package/dist/db/seed-roles.js +0 -33
- package/dist/db/session-refresh.d.ts +0 -43
- package/dist/db/session-refresh.js +0 -40
- package/dist/deploy/migrations.d.ts +0 -20
- package/dist/deploy/migrations.js +0 -68
- package/dist/deploy/orphans.d.ts +0 -20
- package/dist/deploy/orphans.js +0 -177
- package/dist/deploy/preflight.d.ts +0 -53
- package/dist/deploy/preflight.js +0 -189
- package/dist/deploy/report.d.ts +0 -26
- package/dist/deploy/report.js +0 -70
- package/dist/deploy/require-planner.d.ts +0 -2
- package/dist/deploy/require-planner.js +0 -60
- package/dist/deploy/require-token.d.ts +0 -15
- package/dist/deploy/require-token.js +0 -78
- package/dist/deploy/secrets-file.d.ts +0 -17
- package/dist/deploy/secrets-file.js +0 -166
- package/dist/deploy/write-env.d.ts +0 -40
- package/dist/deploy/write-env.js +0 -395
- package/dist/docs/index-pages.d.ts +0 -38
- package/dist/docs/index-pages.js +0 -186
- package/dist/emails/commands.d.ts +0 -15
- package/dist/emails/commands.js +0 -130
- package/dist/emails/email-types.d.ts +0 -13
- package/dist/emails/email-types.js +0 -1
- package/dist/emails/fixtures.d.ts +0 -20
- package/dist/emails/fixtures.js +0 -20
- package/dist/env/audit.d.ts +0 -206
- package/dist/env/audit.js +0 -436
- package/dist/env/cloudflare.d.ts +0 -21
- package/dist/env/cloudflare.js +0 -61
- package/dist/env/commands.d.ts +0 -99
- package/dist/env/commands.js +0 -613
- package/dist/env/discovery.d.ts +0 -15
- package/dist/env/discovery.js +0 -239
- package/dist/env/document.d.ts +0 -119
- package/dist/env/document.js +0 -323
- package/dist/env/example.d.ts +0 -68
- package/dist/env/example.js +0 -605
- package/dist/env/selection.d.ts +0 -105
- package/dist/env/selection.js +0 -228
- package/dist/env-entry.d.ts +0 -49
- package/dist/env-entry.js +0 -204
- package/dist/environment-doctor.d.ts +0 -45
- package/dist/environment-doctor.js +0 -429
- package/dist/environment.d.ts +0 -92
- package/dist/environment.js +0 -185
- package/dist/fingerprint.d.ts +0 -10
- package/dist/fingerprint.js +0 -18
- package/dist/gen/campus-map.d.ts +0 -1
- package/dist/gen/campus-map.js +0 -473
- package/dist/gen/commands.d.ts +0 -1
- package/dist/gen/commands.js +0 -25
- package/dist/gen/hypno.d.ts +0 -1
- package/dist/gen/hypno.js +0 -134
- package/dist/gh/client.d.ts +0 -80
- package/dist/gh/client.js +0 -259
- package/dist/gh/environments.d.ts +0 -83
- package/dist/gh/environments.js +0 -178
- package/dist/gh/rulesets/actors.d.ts +0 -14
- package/dist/gh/rulesets/actors.js +0 -69
- package/dist/gh/rulesets/api.d.ts +0 -15
- package/dist/gh/rulesets/api.js +0 -104
- package/dist/gh/rulesets/commands.d.ts +0 -3
- package/dist/gh/rulesets/commands.js +0 -203
- package/dist/gh/rulesets/desired.d.ts +0 -112
- package/dist/gh/rulesets/desired.js +0 -271
- package/dist/gh/rulesets/diff.d.ts +0 -65
- package/dist/gh/rulesets/diff.js +0 -170
- package/dist/gh/rulesets/types.d.ts +0 -76
- package/dist/gh/rulesets/types.js +0 -13
- package/dist/gh/settings/api.d.ts +0 -40
- package/dist/gh/settings/api.js +0 -180
- package/dist/gh/settings/commands.d.ts +0 -1
- package/dist/gh/settings/commands.js +0 -254
- package/dist/gh/settings/desired.d.ts +0 -53
- package/dist/gh/settings/desired.js +0 -43
- package/dist/gh/settings/diff.d.ts +0 -57
- package/dist/gh/settings/diff.js +0 -161
- package/dist/gh/settings/types.d.ts +0 -92
- package/dist/gh/settings/types.js +0 -11
- package/dist/gh/settings/workflows.d.ts +0 -41
- package/dist/gh/settings/workflows.js +0 -128
- package/dist/grantRoot.d.ts +0 -42
- package/dist/grantRoot.js +0 -89
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -169
- package/dist/images/commands.d.ts +0 -32
- package/dist/images/commands.js +0 -231
- package/dist/images/events.d.ts +0 -47
- package/dist/images/events.js +0 -116
- package/dist/images/graphics.d.ts +0 -70
- package/dist/images/graphics.js +0 -234
- package/dist/images/open-graph-types.d.ts +0 -75
- package/dist/images/open-graph-types.js +0 -1
- package/dist/images/prompts.d.ts +0 -34
- package/dist/images/prompts.js +0 -100
- package/dist/images/render.d.ts +0 -21
- package/dist/images/render.js +0 -61
- package/dist/images/select.d.ts +0 -64
- package/dist/images/select.js +0 -96
- package/dist/instance.d.ts +0 -69
- package/dist/instance.js +0 -102
- package/dist/invocation.d.ts +0 -49
- package/dist/invocation.js +0 -78
- package/dist/launch-ci.d.ts +0 -1
- package/dist/launch-ci.js +0 -90
- package/dist/launch.d.ts +0 -18
- package/dist/menu.d.ts +0 -43
- package/dist/menu.js +0 -300
- package/dist/moderation.d.ts +0 -70
- package/dist/moderation.js +0 -128
- package/dist/oauth/browser.d.ts +0 -1
- package/dist/oauth/browser.js +0 -13
- package/dist/oauth/config.d.ts +0 -24
- package/dist/oauth/config.js +0 -24
- package/dist/oauth/connect-url.d.ts +0 -17
- package/dist/oauth/connect-url.js +0 -23
- package/dist/oauth/db.d.ts +0 -37
- package/dist/oauth/db.js +0 -108
- package/dist/oauth/device.d.ts +0 -93
- package/dist/oauth/device.js +0 -198
- package/dist/oauth/discovery.d.ts +0 -28
- package/dist/oauth/discovery.js +0 -54
- package/dist/oauth/env-file.d.ts +0 -6
- package/dist/oauth/env-file.js +0 -35
- package/dist/oauth/exchange.d.ts +0 -43
- package/dist/oauth/exchange.js +0 -93
- package/dist/oauth/label.d.ts +0 -1
- package/dist/oauth/label.js +0 -25
- package/dist/oauth/loopback.d.ts +0 -52
- package/dist/oauth/loopback.js +0 -168
- package/dist/oauth/pkce.d.ts +0 -6
- package/dist/oauth/pkce.js +0 -34
- package/dist/oauth/target.d.ts +0 -54
- package/dist/oauth/target.js +0 -102
- package/dist/oauth/transport.d.ts +0 -41
- package/dist/oauth/transport.js +0 -32
- package/dist/oauth/wizard.d.ts +0 -40
- package/dist/oauth/wizard.js +0 -578
- package/dist/persona.d.ts +0 -18
- package/dist/persona.js +0 -221
- package/dist/pick.d.ts +0 -6
- package/dist/pick.js +0 -64
- package/dist/pipes.d.ts +0 -1
- package/dist/pipes.js +0 -25
- package/dist/planner/checks.d.ts +0 -26
- package/dist/planner/checks.js +0 -104
- package/dist/planner/commands.d.ts +0 -10
- package/dist/planner/commands.js +0 -367
- package/dist/planner/db.d.ts +0 -15
- package/dist/planner/db.js +0 -33
- package/dist/planner/role.d.ts +0 -33
- package/dist/planner/role.js +0 -68
- package/dist/repo/peer-redirect-hooks.d.ts +0 -16
- package/dist/repo/peer-redirect-hooks.js +0 -10
- package/dist/repo/peer-redirect.d.ts +0 -4
- package/dist/repo/peer-redirect.js +0 -26
- package/dist/repo/peers.d.ts +0 -39
- package/dist/repo/peers.js +0 -139
- package/dist/repo/resolve.d.ts +0 -41
- package/dist/repo/resolve.js +0 -152
- package/dist/repo/root.d.ts +0 -23
- package/dist/repo/root.js +0 -91
- package/dist/repo/source.d.ts +0 -14
- package/dist/repo/source.js +0 -54
- package/dist/repo/supabase-project.d.ts +0 -60
- package/dist/repo/supabase-project.js +0 -124
- package/dist/repo/tsx-loader.d.ts +0 -6
- package/dist/repo/tsx-loader.js +0 -41
- package/dist/run/pick.d.ts +0 -90
- package/dist/run/pick.js +0 -664
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -280
- package/dist/stack.d.ts +0 -48
- package/dist/stack.js +0 -403
- package/dist/telemetry.d.ts +0 -59
- package/dist/tier.d.ts +0 -40
- package/dist/tier.js +0 -96
- package/dist/ui.d.ts +0 -40
- package/dist/ui.js +0 -71
- package/dist/version.d.ts +0 -7
- package/dist/version.js +0 -17
- package/dist/workers.d.ts +0 -5
- package/dist/workers.js +0 -33
- package/dist/workflows/commands.d.ts +0 -54
- 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[];
|