@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
package/dist/env/audit.js
DELETED
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Four-way drift detection: local `.env`, Bitwarden, GitHub, Cloudflare.
|
|
3
|
-
*
|
|
4
|
-
* Each store answers a different question, and only one of them answers it
|
|
5
|
-
* completely:
|
|
6
|
-
*
|
|
7
|
-
* | Store | Exposes | So it can be checked for |
|
|
8
|
-
* |--------------------|-------------------|--------------------------|
|
|
9
|
-
* | local .env | names AND values | value drift |
|
|
10
|
-
* | Bitwarden | names AND values | value drift (the truth) |
|
|
11
|
-
* | GitHub *secrets* | names + updatedAt | presence, staleness |
|
|
12
|
-
* | GitHub *variables* | names AND values | value drift |
|
|
13
|
-
* | Cloudflare | names only | presence, orphans |
|
|
14
|
-
*
|
|
15
|
-
* That asymmetry is the whole design. A GitHub *secret* is write-only, so a
|
|
16
|
-
* clean audit of one does not mean "the values match". It means "nothing
|
|
17
|
-
* detectable is wrong", and the report says which is which rather than implying
|
|
18
|
-
* the stronger claim.
|
|
19
|
-
*
|
|
20
|
-
* Timestamps are what rescue that row from being presence-only. Bitwarden
|
|
21
|
-
* reports a `revisionDate` per secret and `gh secret list` reports `updatedAt`,
|
|
22
|
-
* so "was GitHub updated after Bitwarden last changed?" IS answerable. That is
|
|
23
|
-
* the failure this design actually has: a credential rotated in Bitwarden and
|
|
24
|
-
* never propagated, which a name-only check calls healthy right up until the old
|
|
25
|
-
* one is revoked.
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ GitHub *variables* are the one downstream store that needs none of that
|
|
28
|
-
* reasoning: `gh variable list` returns the value, so the public per-environment
|
|
29
|
-
* keys are compared exactly the way the local file is. Staleness is deliberately
|
|
30
|
-
* NOT applied to them: a timestamp is a proxy for a comparison that could not
|
|
31
|
-
* be made, and using the proxy where the real answer is available would report a
|
|
32
|
-
* variable re-pushed with an identical value as drift. The security plan's §3.6
|
|
33
|
-
* limitation ("names only… a changed value is undetectable") now holds for the
|
|
34
|
-
* secret row alone.
|
|
35
|
-
*
|
|
36
|
-
* ⚠️ One scope sits outside that table altogether: the repository's OWN
|
|
37
|
-
* variables. Push writes environment-scoped values only, and an environment
|
|
38
|
-
* variable SHADOWS a repository one of the same name, so a repository-level
|
|
39
|
-
* copy of a managed key is read by nothing, drifts forever, and becomes the
|
|
40
|
-
* live value on the day somebody deletes the environment copy. It is checked
|
|
41
|
-
* here for the same reason §3.6's orphans are: nothing manages it, so nothing
|
|
42
|
-
* else in this system would ever mention it.
|
|
43
|
-
*/
|
|
44
|
-
export function audit(input) {
|
|
45
|
-
const ignore = input.ignore ?? new Set();
|
|
46
|
-
const neverStore = input.neverStore ?? new Set();
|
|
47
|
-
const minted = input.minted ?? new Set();
|
|
48
|
-
const commented = input.localCommented ?? new Set();
|
|
49
|
-
const variables = input.variables ?? new Set();
|
|
50
|
-
const githubVariables = input.githubVariables ?? [];
|
|
51
|
-
const findings = [];
|
|
52
|
-
const relevant = (key) => !ignore.has(key) && !neverStore.has(key) && !minted.has(key);
|
|
53
|
-
// ── must not be stored anywhere remote ─────────────────────────────────────
|
|
54
|
-
// Checked first and reported as errors. `BWS_ACCESS_TOKEN` in a Bitwarden
|
|
55
|
-
// project is a key locked inside the box it opens; in GitHub it would give CI
|
|
56
|
-
// the ability to read every secret we have.
|
|
57
|
-
for (const key of neverStore) {
|
|
58
|
-
if (input.bws.has(key)) {
|
|
59
|
-
findings.push({
|
|
60
|
-
key,
|
|
61
|
-
severity: "error",
|
|
62
|
-
store: "local",
|
|
63
|
-
summary: "must NEVER be stored in Bitwarden — delete it from the project; " +
|
|
64
|
-
"it belongs in the Password Manager vault, exported per shell",
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
for (const copy of input.github.filter((g) => g.name === key)) {
|
|
68
|
-
findings.push({
|
|
69
|
-
key,
|
|
70
|
-
severity: "error",
|
|
71
|
-
store: "github",
|
|
72
|
-
summary: `must NEVER be a GitHub secret — delete it from \`${copy.environment}\`; ` +
|
|
73
|
-
"nothing machine-shaped may authenticate to Secrets Manager",
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
for (const [worker, names] of input.cloudflare ?? []) {
|
|
77
|
-
if (!names.has(key))
|
|
78
|
-
continue;
|
|
79
|
-
findings.push({
|
|
80
|
-
key,
|
|
81
|
-
severity: "error",
|
|
82
|
-
store: "cloudflare",
|
|
83
|
-
summary: `must NEVER be a Worker secret — delete it from ${worker}`,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// ── minted at deploy time ──────────────────────────────────────────────────
|
|
88
|
-
// Cloudflare is deliberately NOT checked: that is where these belong, and
|
|
89
|
-
// saying so is the entire point of the category. What IS checked is the two
|
|
90
|
-
// stores a minted credential must never reach, because a copy in either is a
|
|
91
|
-
// long-lived token nobody rotates sitting beside one that rotates every
|
|
92
|
-
// deploy, and the stale copy is the one an operator will reach for when
|
|
93
|
-
// something breaks.
|
|
94
|
-
for (const key of minted) {
|
|
95
|
-
if (input.bws.has(key)) {
|
|
96
|
-
findings.push({
|
|
97
|
-
key,
|
|
98
|
-
severity: "error",
|
|
99
|
-
store: "local",
|
|
100
|
-
summary: "is minted at deploy time and must not be stored in Bitwarden — " +
|
|
101
|
-
"delete it from the project; the deploy signs a fresh one and " +
|
|
102
|
-
"writes it straight to the Worker",
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
for (const copy of input.github.filter((g) => g.name === key)) {
|
|
106
|
-
findings.push({
|
|
107
|
-
key,
|
|
108
|
-
severity: "error",
|
|
109
|
-
store: "github",
|
|
110
|
-
summary: `is minted at deploy time and must not be a GitHub secret — delete ` +
|
|
111
|
-
`it from \`${copy.environment}\`; what CI needs is the signing key, ` +
|
|
112
|
-
"not a token somebody minted once",
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// ── in the wrong GitHub store ──────────────────────────────────────────────
|
|
117
|
-
// Errors, and reported before the drift passes, because both directions are
|
|
118
|
-
// silent misconfigurations that a presence check calls healthy: the NAME is
|
|
119
|
-
// in GitHub either way.
|
|
120
|
-
for (const copy of input.github) {
|
|
121
|
-
if (!relevant(copy.name) || !variables.has(copy.name))
|
|
122
|
-
continue;
|
|
123
|
-
findings.push({
|
|
124
|
-
key: copy.name,
|
|
125
|
-
severity: "error",
|
|
126
|
-
store: "github",
|
|
127
|
-
summary: `is public and is a SECRET on \`${copy.environment}\` — delete it and ` +
|
|
128
|
-
"let push set it as a variable; GitHub masks a secret's value in logs " +
|
|
129
|
-
"by substring, which redacts the very links and hostnames it appears in",
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
for (const copy of githubVariables) {
|
|
133
|
-
if (!relevant(copy.name) || variables.has(copy.name))
|
|
134
|
-
continue;
|
|
135
|
-
// Only for a key the registry calls a secret. An unrecognised name in the
|
|
136
|
-
// variable store is an orphan from a rename, reported as one below.
|
|
137
|
-
// Calling it a leaked secret would be a guess, and a loud wrong one. With
|
|
138
|
-
// no declared set there is no way to tell the two apart, so neither fires.
|
|
139
|
-
if (!input.declared?.has(copy.name))
|
|
140
|
-
continue;
|
|
141
|
-
findings.push({
|
|
142
|
-
key: copy.name,
|
|
143
|
-
severity: "error",
|
|
144
|
-
store: "github",
|
|
145
|
-
summary: `is a secret and is a VARIABLE on \`${copy.environment}\` — its value ` +
|
|
146
|
-
"is readable by anyone who can see the repository's Actions config, " +
|
|
147
|
-
"and it is not masked in logs. Delete it there and rotate it",
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
// ── repository-level variables ─────────────────────────────────────────────
|
|
151
|
-
// The scope push never writes to, and the one a presence check cannot see
|
|
152
|
-
// from inside an environment.
|
|
153
|
-
//
|
|
154
|
-
// ⚠️ An ENVIRONMENT variable SHADOWS a repository variable of the same name.
|
|
155
|
-
// So a repository-level `AIRTABLE_BASE_ID`, which people were told to set by
|
|
156
|
-
// hand before push started routing that key, is not merely redundant: it is
|
|
157
|
-
// unreadable from every job, holds whatever it held the day it was set, and
|
|
158
|
-
// becomes live the moment somebody deletes the environment copy. Nothing
|
|
159
|
-
// manages it and nothing else here would ever mention it, which makes it the
|
|
160
|
-
// same failure as the §3.6 orphan audit rather than a new one.
|
|
161
|
-
const repository = input.repositoryVariables;
|
|
162
|
-
if (repository !== undefined && !repository.readable) {
|
|
163
|
-
// A finding, in the same list as the rest, rather than a line printed
|
|
164
|
-
// underneath it: the whole hazard of this check is that its absence looks
|
|
165
|
-
// like health, and a clean report with a quiet caveat below it reads as
|
|
166
|
-
// clean. The key is parenthesised so it cannot be mistaken for one.
|
|
167
|
-
findings.push({
|
|
168
|
-
key: "(repository variables)",
|
|
169
|
-
severity: "warning",
|
|
170
|
-
store: "github",
|
|
171
|
-
summary: "could not check — listing the repository's own variables failed " +
|
|
172
|
-
`(${repository.reason}). This run rules out NOTHING at that scope; a ` +
|
|
173
|
-
"repository variable shadowed by an environment copy would look " +
|
|
174
|
-
"exactly like this report. Run `gh variable list` by hand",
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
const repositoryNames = repository?.readable ? repository.names : [];
|
|
178
|
-
for (const name of repositoryNames) {
|
|
179
|
-
// Membership in `variables` IS the declaration test for this branch, since
|
|
180
|
-
// the set comes from the registry, which is why it does not also consult
|
|
181
|
-
// `declared`. A caller that passed one and forgot the other would
|
|
182
|
-
// otherwise get silence from the check it asked for.
|
|
183
|
-
if (variables.has(name)) {
|
|
184
|
-
if (!relevant(name))
|
|
185
|
-
continue;
|
|
186
|
-
findings.push({
|
|
187
|
-
key: name,
|
|
188
|
-
severity: "warning",
|
|
189
|
-
store: "github",
|
|
190
|
-
summary: "is also a REPOSITORY-level GitHub variable, which push does not " +
|
|
191
|
-
"manage — the environment copy shadows it, so every job reads the " +
|
|
192
|
-
"environment value and this one is invisible, stale, and unnoticed " +
|
|
193
|
-
"until somebody deletes that copy, whereupon it silently becomes " +
|
|
194
|
-
`the live value. Delete it: \`gh variable delete ${name}\``,
|
|
195
|
-
});
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
// A name the registry calls a SECRET, sitting in the readable store one
|
|
199
|
-
// scope up. The environment-level version of this is an error above, and
|
|
200
|
-
// this is the same finding with the shadowing sentence removed, because
|
|
201
|
-
// there is none: secrets and variables are separate namespaces, so nothing
|
|
202
|
-
// hides this one.
|
|
203
|
-
//
|
|
204
|
-
// ⚠️ Filtered on `ignore` alone, deliberately NOT on the whole of
|
|
205
|
-
// `relevant()`. An ignored key legitimately lives outside these stores.
|
|
206
|
-
// `GITHUB_ORG` is a committed constant, and a repository variable holding
|
|
207
|
-
// it is somebody making a reasonable choice. A never-store or minted
|
|
208
|
-
// credential in the READABLE store is the worst case this file knows
|
|
209
|
-
// about, not an exempt one, so those two fall through to the error.
|
|
210
|
-
//
|
|
211
|
-
// `declared` is required here for the reason it is required above: without
|
|
212
|
-
// it an unrecognised name is indistinguishable from another team's
|
|
213
|
-
// variable, and calling that a leaked secret would be a loud guess.
|
|
214
|
-
if (ignore.has(name))
|
|
215
|
-
continue;
|
|
216
|
-
if (!input.declared?.has(name))
|
|
217
|
-
continue;
|
|
218
|
-
findings.push({
|
|
219
|
-
key: name,
|
|
220
|
-
severity: "error",
|
|
221
|
-
store: "github",
|
|
222
|
-
summary: "is a secret and is a REPOSITORY-level GitHub variable — its value " +
|
|
223
|
-
"is readable by anyone who can see the repository's Actions config, " +
|
|
224
|
-
"and no environment copy hides it: secrets and variables are separate " +
|
|
225
|
-
`namespaces. Delete it (\`gh variable delete ${name}\`) and rotate it`,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
// ── undeclared keys ────────────────────────────────────────────────────────
|
|
229
|
-
// Reported once, here, and then excluded from the drift comparisons below:
|
|
230
|
-
// an undeclared key is invisible to push routing, so "in your .env, not in
|
|
231
|
-
// Bitwarden" would be true and useless, since pushing cannot fix it.
|
|
232
|
-
const undeclared = (key) => input.declared !== undefined && !input.declared.has(key);
|
|
233
|
-
for (const key of input.local.keys()) {
|
|
234
|
-
if (!relevant(key) || !undeclared(key))
|
|
235
|
-
continue;
|
|
236
|
-
findings.push({
|
|
237
|
-
key,
|
|
238
|
-
severity: "warning",
|
|
239
|
-
store: "local",
|
|
240
|
-
summary: "in your .env but declared in no env manifest — push skips it. " +
|
|
241
|
-
"Declare it with define() in the owning package's env.ts, or remove " +
|
|
242
|
-
"the line",
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
// ── local vs Bitwarden ─────────────────────────────────────────────────────
|
|
246
|
-
// The only VALUE comparison available anywhere in this system.
|
|
247
|
-
for (const [key, value] of input.local) {
|
|
248
|
-
if (!relevant(key) || undeclared(key))
|
|
249
|
-
continue;
|
|
250
|
-
const truth = input.bws.get(key);
|
|
251
|
-
if (truth === undefined) {
|
|
252
|
-
findings.push({
|
|
253
|
-
key,
|
|
254
|
-
severity: "warning",
|
|
255
|
-
store: "local",
|
|
256
|
-
summary: "in your .env, not in Bitwarden — either a local-only value or one " +
|
|
257
|
-
"somebody forgot to push",
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
else if (truth.value !== value) {
|
|
261
|
-
findings.push({
|
|
262
|
-
key,
|
|
263
|
-
severity: "error",
|
|
264
|
-
store: "local",
|
|
265
|
-
summary: "your .env disagrees with Bitwarden — pull to take theirs, push to " +
|
|
266
|
-
"take yours",
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
for (const key of input.bws.keys()) {
|
|
271
|
-
if (!relevant(key))
|
|
272
|
-
continue;
|
|
273
|
-
if (!input.local.has(key)) {
|
|
274
|
-
findings.push({
|
|
275
|
-
key,
|
|
276
|
-
severity: commented.has(key) ? "info" : "warning",
|
|
277
|
-
store: "local",
|
|
278
|
-
summary: commented.has(key)
|
|
279
|
-
? "commented out in your .env; Bitwarden has a value"
|
|
280
|
-
: "in Bitwarden, missing from your .env",
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// ── Bitwarden vs GitHub ────────────────────────────────────────────────────
|
|
285
|
-
// One pass over both stores. Which one a key is compared against is decided
|
|
286
|
-
// by the registry, not by where the key happened to turn up. Otherwise a
|
|
287
|
-
// misplaced copy would define its own correctness and never be reported.
|
|
288
|
-
for (const [key, entry] of input.bws) {
|
|
289
|
-
if (!relevant(key))
|
|
290
|
-
continue;
|
|
291
|
-
const expected = input.route(key);
|
|
292
|
-
if (expected === null)
|
|
293
|
-
continue;
|
|
294
|
-
const accepted = input.accepted ??
|
|
295
|
-
((_key, environment) => environment === expected);
|
|
296
|
-
const isVariable = variables.has(key);
|
|
297
|
-
const noun = isVariable ? "variable" : "secret";
|
|
298
|
-
const pool = isVariable
|
|
299
|
-
? githubVariables
|
|
300
|
-
: input.github;
|
|
301
|
-
const copies = pool.filter((g) => g.name === key);
|
|
302
|
-
const here = copies.find((g) => g.environment === expected);
|
|
303
|
-
// A copy somewhere it does not belong. Listed FIRST because for the
|
|
304
|
-
// apply-only credentials this is the reviewer gate failing open: the token
|
|
305
|
-
// is sitting in an environment that deploys with nobody in front of it.
|
|
306
|
-
//
|
|
307
|
-
// `accepted`, not `!== expected`: `production-apply` legitimately holds a
|
|
308
|
-
// second copy of most production keys. An apply-tier key in `production`
|
|
309
|
-
// still lands here, which is the case this finding exists for.
|
|
310
|
-
for (const stray of copies.filter((g) => !accepted(key, g.environment))) {
|
|
311
|
-
findings.push({
|
|
312
|
-
key,
|
|
313
|
-
severity: "error",
|
|
314
|
-
store: "github",
|
|
315
|
-
summary: `also set as a ${noun} on \`${stray.environment}\`, which is not ` +
|
|
316
|
-
`where it belongs (\`${expected}\`) — delete it there`,
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
if (!here) {
|
|
320
|
-
findings.push({
|
|
321
|
-
key,
|
|
322
|
-
severity: "error",
|
|
323
|
-
store: "github",
|
|
324
|
-
summary: `in Bitwarden, NOT a ${noun} on the \`${expected}\` GitHub ` +
|
|
325
|
-
"environment — the deploy cannot see it",
|
|
326
|
-
});
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
// The comparison a secret cannot have. Kept distinct from the missing case
|
|
330
|
-
// above on purpose: "absent" is fixed by a push, "drifted" means somebody
|
|
331
|
-
// edited the value in the GitHub UI and the two stores now disagree about
|
|
332
|
-
// which is real, and the fix has to start by deciding that.
|
|
333
|
-
if (isVariable) {
|
|
334
|
-
const mine = githubVariables.find((g) => g.name === key && g.environment === expected);
|
|
335
|
-
if (mine !== undefined && mine.value !== entry.value) {
|
|
336
|
-
findings.push({
|
|
337
|
-
key,
|
|
338
|
-
severity: "error",
|
|
339
|
-
store: "github",
|
|
340
|
-
summary: `the \`${expected}\` GitHub variable's VALUE disagrees with ` +
|
|
341
|
-
"Bitwarden — push to overwrite GitHub, or fix Bitwarden if the " +
|
|
342
|
-
"edit there was the deliberate one",
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (isStale(entry.revisionDate, here.updatedAt)) {
|
|
348
|
-
findings.push({
|
|
349
|
-
key,
|
|
350
|
-
severity: "error",
|
|
351
|
-
store: "github",
|
|
352
|
-
summary: "rotated in Bitwarden after GitHub was last updated — the deploy is " +
|
|
353
|
-
"still using the previous value",
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
for (const copy of input.github) {
|
|
358
|
-
if (!relevant(copy.name))
|
|
359
|
-
continue;
|
|
360
|
-
if (!input.bws.has(copy.name)) {
|
|
361
|
-
findings.push({
|
|
362
|
-
key: copy.name,
|
|
363
|
-
severity: "warning",
|
|
364
|
-
store: "github",
|
|
365
|
-
summary: `in the \`${copy.environment}\` GitHub environment, not in ` +
|
|
366
|
-
"Bitwarden — an orphan from a rename or a removal",
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
for (const copy of githubVariables) {
|
|
371
|
-
if (!relevant(copy.name))
|
|
372
|
-
continue;
|
|
373
|
-
if (!input.bws.has(copy.name)) {
|
|
374
|
-
findings.push({
|
|
375
|
-
key: copy.name,
|
|
376
|
-
severity: "warning",
|
|
377
|
-
store: "github",
|
|
378
|
-
summary: `a variable on the \`${copy.environment}\` GitHub environment, not ` +
|
|
379
|
-
"in Bitwarden — an orphan from a rename or a removal",
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
// ── Bitwarden vs Cloudflare ────────────────────────────────────────────────
|
|
384
|
-
// Presence only, and asymmetric on purpose: a Worker holding a secret nobody
|
|
385
|
-
// stores is an orphan worth reporting, but a secret NOT on a given Worker is
|
|
386
|
-
// usually correct -- DISCORD_TOKEN belongs to platform and nothing else.
|
|
387
|
-
// `--secrets-file` preserves what it omits, so orphans persist indefinitely.
|
|
388
|
-
for (const [worker, names] of input.cloudflare ?? []) {
|
|
389
|
-
for (const name of names) {
|
|
390
|
-
if (!relevant(name))
|
|
391
|
-
continue;
|
|
392
|
-
if (!input.bws.has(name)) {
|
|
393
|
-
findings.push({
|
|
394
|
-
key: name,
|
|
395
|
-
severity: "warning",
|
|
396
|
-
store: "cloudflare",
|
|
397
|
-
summary: `on ${worker}, not in Bitwarden — a renamed or dropped variable leaves its secret behind`,
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return findings.sort((a, b) => rank(a.severity) - rank(b.severity) || a.key.localeCompare(b.key));
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* True when GitHub's copy predates the Bitwarden revision.
|
|
406
|
-
*
|
|
407
|
-
* A missing or unparseable date on either side returns false. A comparison
|
|
408
|
-
* against `NaN` is false anyway, so this only makes that explicit. The reason
|
|
409
|
-
* to be explicit is that the alternative reading, "unknown means stale",
|
|
410
|
-
* turns one malformed timestamp into a report that says everything is behind,
|
|
411
|
-
* after which nobody reads it, including on the run where something really is.
|
|
412
|
-
*/
|
|
413
|
-
function isStale(revisionDate, updatedAt) {
|
|
414
|
-
if (!revisionDate || !updatedAt)
|
|
415
|
-
return false;
|
|
416
|
-
const revised = Date.parse(revisionDate);
|
|
417
|
-
const updated = Date.parse(updatedAt);
|
|
418
|
-
if (Number.isNaN(revised) || Number.isNaN(updated))
|
|
419
|
-
return false;
|
|
420
|
-
return updated < revised;
|
|
421
|
-
}
|
|
422
|
-
function rank(s) {
|
|
423
|
-
return s === "error" ? 0 : s === "warning" ? 1 : 2;
|
|
424
|
-
}
|
|
425
|
-
export function hasErrors(findings) {
|
|
426
|
-
return findings.some((f) => f.severity === "error");
|
|
427
|
-
}
|
|
428
|
-
export function renderFindings(findings) {
|
|
429
|
-
if (findings.length === 0) {
|
|
430
|
-
return "No detectable drift.";
|
|
431
|
-
}
|
|
432
|
-
const mark = { error: "✗", warning: "!", info: "·" };
|
|
433
|
-
return findings
|
|
434
|
-
.map((f) => `${mark[f.severity]} ${f.key} ${f.summary}`)
|
|
435
|
-
.join("\n");
|
|
436
|
-
}
|
package/dist/env/cloudflare.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { workerApps } from "../workers.js";
|
|
2
|
-
/** Re-exported so existing importers of this module (e.g. `deploy/orphans.ts`,
|
|
3
|
-
* this file's own tests) do not need to know the list moved to `workers.ts`.
|
|
4
|
-
* A function, not a top-level constant — see `workers.ts`'s header on why
|
|
5
|
-
* eagerly reading `workers.json` at import time broke `--help`/`setup`
|
|
6
|
-
* outside a repo. */
|
|
7
|
-
export { workerApps };
|
|
8
|
-
export declare class CloudflareError extends Error {
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Secret names per Worker for one target.
|
|
12
|
-
*
|
|
13
|
-
* A Worker that fails to read comes back as an empty set rather than a throw,
|
|
14
|
-
* and the caller is told which ones could not be read. A Worker that has never
|
|
15
|
-
* been deployed is the common case during setup, and it must not stop the audit
|
|
16
|
-
* from reporting everything it CAN see.
|
|
17
|
-
*/
|
|
18
|
-
export declare function listWorkerSecrets(target: string): Promise<{
|
|
19
|
-
secrets: Map<string, Set<string>>;
|
|
20
|
-
unreadable: string[];
|
|
21
|
-
}>;
|
package/dist/env/cloudflare.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reading the secret NAMES set on each Worker.
|
|
3
|
-
*
|
|
4
|
-
* Cloudflare never returns a secret's value, so this contributes presence only.
|
|
5
|
-
* That is still worth having: `wrangler deploy --secrets-file` **preserves what
|
|
6
|
-
* it omits**, so a renamed or dropped variable leaves its secret on the Worker
|
|
7
|
-
* indefinitely, and nothing else in the system can see it.
|
|
8
|
-
*/
|
|
9
|
-
import { execFile } from "node:child_process";
|
|
10
|
-
import { promisify } from "node:util";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { findRepoRoot } from "../repo/root.js";
|
|
13
|
-
import { workerApps } from "../workers.js";
|
|
14
|
-
const run = promisify(execFile);
|
|
15
|
-
/** Re-exported so existing importers of this module (e.g. `deploy/orphans.ts`,
|
|
16
|
-
* this file's own tests) do not need to know the list moved to `workers.ts`.
|
|
17
|
-
* A function, not a top-level constant — see `workers.ts`'s header on why
|
|
18
|
-
* eagerly reading `workers.json` at import time broke `--help`/`setup`
|
|
19
|
-
* outside a repo. */
|
|
20
|
-
export { workerApps };
|
|
21
|
-
export class CloudflareError extends Error {
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Secret names per Worker for one target.
|
|
25
|
-
*
|
|
26
|
-
* A Worker that fails to read comes back as an empty set rather than a throw,
|
|
27
|
-
* and the caller is told which ones could not be read. A Worker that has never
|
|
28
|
-
* been deployed is the common case during setup, and it must not stop the audit
|
|
29
|
-
* from reporting everything it CAN see.
|
|
30
|
-
*/
|
|
31
|
-
export async function listWorkerSecrets(target) {
|
|
32
|
-
const secrets = new Map();
|
|
33
|
-
const unreadable = [];
|
|
34
|
-
for (const app of workerApps()) {
|
|
35
|
-
const worker = `${target}-${app}`;
|
|
36
|
-
try {
|
|
37
|
-
const { stdout } = await run("pnpm", [
|
|
38
|
-
"exec",
|
|
39
|
-
"wrangler",
|
|
40
|
-
"secret",
|
|
41
|
-
"list",
|
|
42
|
-
// ⚠️ WRANGLER's `--env`, not ours. Our flag is `--target`; this one
|
|
43
|
-
// names a wrangler.jsonc environment block and must keep its spelling.
|
|
44
|
-
"--env",
|
|
45
|
-
target,
|
|
46
|
-
"--format",
|
|
47
|
-
"json",
|
|
48
|
-
], { cwd: join(findRepoRoot(), "apps", app), shell: false });
|
|
49
|
-
// wrangler prints its banner on stdout too, so take the JSON array only.
|
|
50
|
-
const start = stdout.indexOf("[");
|
|
51
|
-
const parsed = start === -1
|
|
52
|
-
? []
|
|
53
|
-
: JSON.parse(stdout.slice(start));
|
|
54
|
-
secrets.set(worker, new Set(parsed.map((s) => s.name)));
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
unreadable.push(worker);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return { secrets, unreadable };
|
|
61
|
-
}
|
package/dist/env/commands.d.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import type { EnvTarget } from "@devdogsuga/env";
|
|
2
|
-
import { type VaultTarget } from "../bws/environments.js";
|
|
3
|
-
import { EnvDocument } from "./document.js";
|
|
4
|
-
export interface EnvOptions {
|
|
5
|
-
/**
|
|
6
|
-
* Typed as any `EnvTarget` rather than a `VaultTarget`, on purpose.
|
|
7
|
-
* `development` has to REACH these commands so each can refuse it by name.
|
|
8
|
-
* Narrowing here would move the refusal back into the argument parser, which
|
|
9
|
-
* is where it was missing.
|
|
10
|
-
*/
|
|
11
|
-
target: EnvTarget;
|
|
12
|
-
/** Overrides the file the target implies. */
|
|
13
|
-
file?: string;
|
|
14
|
-
yes?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Writes the document, tidying first.
|
|
18
|
-
*
|
|
19
|
-
* `group()` runs on every write rather than as its own command: files drift a
|
|
20
|
-
* line at a time, and a tidy pass nobody remembers to run is a tidy pass that
|
|
21
|
-
* never happens.
|
|
22
|
-
*
|
|
23
|
-
* Env files contain credentials and must not be world-readable. The mode
|
|
24
|
-
* parameter only applies on creation, so existing world-readable files are
|
|
25
|
-
* tightened with chmod after each write.
|
|
26
|
-
*/
|
|
27
|
-
export declare function save(path: string, doc: EnvDocument): Promise<boolean>;
|
|
28
|
-
/**
|
|
29
|
-
* Brings Bitwarden's values into the target's env file, in place.
|
|
30
|
-
*
|
|
31
|
-
* ⚠️ With `--file .env` (or `--target development`, which is refused) this is
|
|
32
|
-
* the file `pnpm dev` reads. Pulling `production` into it points local
|
|
33
|
-
* development at production, which is why that target warns and defaults its
|
|
34
|
-
* confirmation to no.
|
|
35
|
-
*/
|
|
36
|
-
export declare function runEnvPull(options: EnvOptions): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Sends the target's env file to Bitwarden and then on to GitHub.
|
|
39
|
-
*
|
|
40
|
-
* Both, always. Bitwarden alone is the failure this design has: the source of
|
|
41
|
-
* truth moves, the deploy does not, and everything looks healthy until the old
|
|
42
|
-
* credential is revoked.
|
|
43
|
-
*
|
|
44
|
-
* ⚠️ The file comes from the TARGET. `--target staging` reads `.env.staging`,
|
|
45
|
-
* not the root `.env`. That defaulting mistake meant this command uploaded a
|
|
46
|
-
* developer's own values to a shared project and said "3 created, 12 updated"
|
|
47
|
-
* about it.
|
|
48
|
-
*
|
|
49
|
-
* ⚠️ Two GitHub stores on the far side, and only one Bitwarden project. The
|
|
50
|
-
* public per-environment values become GitHub *variables* rather than secrets
|
|
51
|
-
* (see `gh/client.ts` for why the store matters), but they are stored in
|
|
52
|
-
* Bitwarden alongside the secrets, because "Bitwarden is the source of truth"
|
|
53
|
-
* has to be true of a WHOLE target for `pull` to rebuild a working env file.
|
|
54
|
-
* Splitting them would make GitHub the only home for 27 keys and the second
|
|
55
|
-
* source of truth for their values, which is the arrangement this tool exists
|
|
56
|
-
* to remove.
|
|
57
|
-
*/
|
|
58
|
-
export declare function runEnvPush(options: EnvOptions): Promise<void>;
|
|
59
|
-
/**
|
|
60
|
-
* The second half of every push.
|
|
61
|
-
*
|
|
62
|
-
* One Bitwarden project can feed more than one GitHub environment: `production`
|
|
63
|
-
* feeds both `production` and `production-apply`, and which key goes where IS
|
|
64
|
-
* the reviewer gate. So this loops over the routed targets rather than assuming
|
|
65
|
-
* one, and confirms each separately. Agreeing to update production's ordinary
|
|
66
|
-
* secrets is not agreeing to touch the two write-capable credentials sitting
|
|
67
|
-
* behind the reviewers.
|
|
68
|
-
*
|
|
69
|
-
* The two maps stay two maps all the way down to the two `gh` calls. Merging
|
|
70
|
-
* them and branching at the bottom would put "which store does this go to?" one
|
|
71
|
-
* boolean away from being wrong, and getting it wrong in the variable direction
|
|
72
|
-
* publishes a credential's plaintext to everyone who can read the repository's
|
|
73
|
-
* Actions config.
|
|
74
|
-
*
|
|
75
|
-
* Exported ONLY so dispatch can be tested against a mocked `gh` client. The two
|
|
76
|
-
* `setSecret`/`setVariable` lines below are the one place where a swap is
|
|
77
|
-
* silent, irreversible, and invisible to `selection.ts`'s tests.
|
|
78
|
-
*/
|
|
79
|
-
export declare function pushToGithub(target: VaultTarget, secrets: Map<string, string>, publicValues: Map<string, string>, yes?: boolean): Promise<void>;
|
|
80
|
-
/** Read-only, and safe to run against anything. */
|
|
81
|
-
export declare function runEnvAudit(options: EnvOptions): Promise<void>;
|
|
82
|
-
/**
|
|
83
|
-
* Empties every value in a local env file, losing none of them.
|
|
84
|
-
*
|
|
85
|
-
* The use is handing a filled-in file back to its blank state: after pulling
|
|
86
|
-
* production onto a laptop, before passing a machine on, or when a set of keys
|
|
87
|
-
* has to be re-entered from scratch. Deleting the values would do that too, and
|
|
88
|
-
* would also delete the only copy of anything that was never pushed. So each
|
|
89
|
-
* becomes a commented line holding what it was, plus an empty active line
|
|
90
|
-
* beneath. The file still declares every key it needs, which makes it a
|
|
91
|
-
* checklist rather than a blank page.
|
|
92
|
-
*
|
|
93
|
-
* Purely local. It touches no remote store, so it takes no `--target`: asking
|
|
94
|
-
* which target to clear a file against would imply it reaches one. It works on
|
|
95
|
-
* `.env` unless `--file` says otherwise, and that default is written out here
|
|
96
|
-
* rather than inherited, so the shared `pathFor` has no "no target" case for a
|
|
97
|
-
* remote command to fall into.
|
|
98
|
-
*/
|
|
99
|
-
export declare function runEnvReset(options: Pick<EnvOptions, "file" | "yes">): Promise<void>;
|