@smoothbricks/cli 0.11.19 → 0.11.20
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 +68 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +28 -4
- package/dist/lib/json.d.ts +22 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +15 -3
- package/dist/monorepo/cargo-policy.d.ts +17 -0
- package/dist/monorepo/cargo-policy.d.ts.map +1 -1
- package/dist/monorepo/cargo-policy.js +73 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +4 -2
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +6 -1
- package/dist/nx/index.d.ts +6 -0
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +14 -0
- package/dist/secrets/commands.d.ts +9 -5
- package/dist/secrets/commands.d.ts.map +1 -1
- package/dist/secrets/commands.js +122 -49
- package/dist/secrets/index.d.ts +42 -56
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +52 -79
- package/dist/secrets/resolver.d.ts +59 -0
- package/dist/secrets/resolver.d.ts.map +1 -0
- package/dist/secrets/resolver.js +100 -0
- package/dist/secrets/run.d.ts +23 -0
- package/dist/secrets/run.d.ts.map +1 -0
- package/dist/secrets/run.js +130 -0
- package/dist/secrets/status.d.ts +177 -0
- package/dist/secrets/status.d.ts.map +1 -0
- package/dist/secrets/status.js +724 -0
- package/dist/wrangler/deploy-stage.d.ts +1 -1
- package/dist/wrangler/deploy-stage.d.ts.map +1 -1
- package/dist/wrangler/deploy-stage.js +22 -20
- package/dist/wrangler/deployed-version.d.ts.map +1 -1
- package/dist/wrangler/deployed-version.js +7 -12
- package/dist/wrangler/flat-config.d.ts +1 -4
- package/dist/wrangler/flat-config.d.ts.map +1 -1
- package/dist/wrangler/flat-config.js +98 -44
- package/dist/wrangler/prepare-env.d.ts +4 -3
- package/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +7 -5
- package/dist/wrangler/source-config.d.ts +24 -0
- package/dist/wrangler/source-config.d.ts.map +1 -0
- package/dist/wrangler/source-config.js +110 -0
- package/dist/wrangler/stage.d.ts +55 -23
- package/dist/wrangler/stage.d.ts.map +1 -1
- package/dist/wrangler/stage.js +81 -162
- package/managed/raw/tooling/direnv/devenv.smoo.nix +19 -3
- package/managed/raw/tooling/direnv/secret-references.ts +280 -76
- package/managed/raw/tooling/direnv/setup-environment.ts +58 -2
- package/managed/raw/tsconfig.lib.json +28 -0
- package/package.json +7 -2
- package/src/cli.ts +34 -5
- package/src/lib/json.ts +23 -1
- package/src/monorepo/cargo-policy.test.ts +91 -1
- package/src/monorepo/cargo-policy.ts +87 -1
- package/src/monorepo/index.ts +8 -2
- package/src/monorepo/packs/index.ts +10 -1
- package/src/monorepo/secret-references.test.ts +412 -6
- package/src/monorepo/setup-environment.test.ts +181 -0
- package/src/nx/index.test.ts +8 -0
- package/src/nx/index.ts +20 -0
- package/src/secrets/commands.test.ts +186 -4
- package/src/secrets/commands.ts +142 -51
- package/src/secrets/index.test.ts +4 -10
- package/src/secrets/index.ts +54 -115
- package/src/secrets/resolver.ts +130 -0
- package/src/secrets/run.test.ts +359 -0
- package/src/secrets/run.ts +148 -0
- package/src/secrets/status.test.ts +164 -0
- package/src/secrets/status.ts +297 -0
- package/src/wrangler/deploy-stage.test.ts +95 -2
- package/src/wrangler/deploy-stage.ts +26 -23
- package/src/wrangler/deployed-version.ts +7 -11
- package/src/wrangler/flat-config.test.ts +9 -4
- package/src/wrangler/flat-config.ts +1 -20
- package/src/wrangler/format-parity.test.ts +433 -0
- package/src/wrangler/prepare-env.ts +7 -5
- package/src/wrangler/source-config.test.ts +102 -0
- package/src/wrangler/source-config.ts +110 -0
- package/src/wrangler/stage.test.ts +61 -32
- package/src/wrangler/stage.ts +124 -173
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bootstrap secret resolver, as the CLI sees it.
|
|
3
|
+
*
|
|
4
|
+
* `managed/raw/tooling/direnv/secret-references.ts` holds the one
|
|
5
|
+
* implementation of THE RULE — which group resolves where, and what CI and a
|
|
6
|
+
* cowshed workspace refuse instead. A managed repository runs the copy smoo
|
|
7
|
+
* writes into its own `tooling/direnv/secret-references.ts` at shell entry;
|
|
8
|
+
* `smoo secrets run` runs THIS package's copy of the same file. One
|
|
9
|
+
* implementation, so the command and the shell cannot disagree about a group.
|
|
10
|
+
*
|
|
11
|
+
* It is loaded, not statically imported, and that is forced rather than
|
|
12
|
+
* chosen: the file is a raw bootstrap script that must load before any
|
|
13
|
+
* workspace package and before the Typia transform exists, so it lives
|
|
14
|
+
* outside `src` — outside this package's compiled `rootDir`, which a static
|
|
15
|
+
* import may not cross. A dynamic `import()` reaches it in both layouts
|
|
16
|
+
* (`src/secrets/` and `dist/secrets/` are each two directories below the
|
|
17
|
+
* package root), and typia validates what comes back: the same boundary
|
|
18
|
+
* ../monorepo/packed-package.ts puts around a foreign module. `require` is
|
|
19
|
+
* not the alternative — under a Bun loader plugin, which this package's own
|
|
20
|
+
* tests preload, every module is async and `require` of one throws.
|
|
21
|
+
*/
|
|
22
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
23
|
+
import typia from 'typia';
|
|
24
|
+
const isSecretReferencesModule = (() => {
|
|
25
|
+
const _io0 = input => true && true;
|
|
26
|
+
return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
|
|
27
|
+
})();
|
|
28
|
+
const isGroupedSecrets = (() => {
|
|
29
|
+
const _io0 = input => "string" === typeof input.name && "string" === typeof input.group && "string" === typeof input.derivedGroup;
|
|
30
|
+
return input => Array.isArray(input) && input.every(elem => "object" === typeof elem && null !== elem && _io0(elem));
|
|
31
|
+
})();
|
|
32
|
+
const isSecretResolution = (() => {
|
|
33
|
+
const _io0 = input => "object" === typeof input.values && null !== input.values && false === Array.isArray(input.values) && _io1(input.values) && (Array.isArray(input.deferred) && input.deferred.every(elem => "object" === typeof elem && null !== elem && _io2(elem)));
|
|
34
|
+
const _io1 = input => Object.keys(input).every(key => {
|
|
35
|
+
const value = input[key];
|
|
36
|
+
if (undefined === value)
|
|
37
|
+
return true;
|
|
38
|
+
return "string" === typeof value;
|
|
39
|
+
});
|
|
40
|
+
const _io2 = input => "string" === typeof input.name && "string" === typeof input.group && "string" === typeof input.guidance;
|
|
41
|
+
return input => "object" === typeof input && null !== input && _io0(input);
|
|
42
|
+
})();
|
|
43
|
+
const RESOLVER_PATH = fileURLToPath(new URL('../../managed/raw/tooling/direnv/secret-references.ts', import.meta.url));
|
|
44
|
+
/**
|
|
45
|
+
* A loaded export, narrowed to the signature this file calls it with. typia
|
|
46
|
+
* validates data, not functions — it reports a module missing both of these
|
|
47
|
+
* as valid — so presence is checked here, and what each one RETURNS is
|
|
48
|
+
* validated below, where it is data again.
|
|
49
|
+
*/
|
|
50
|
+
function isSecretGroupsReader(value) {
|
|
51
|
+
return typeof value === 'function';
|
|
52
|
+
}
|
|
53
|
+
function isSecretGroupResolver(value) {
|
|
54
|
+
return typeof value === 'function';
|
|
55
|
+
}
|
|
56
|
+
let loaded;
|
|
57
|
+
async function secretReferences() {
|
|
58
|
+
if (loaded !== undefined) {
|
|
59
|
+
return loaded;
|
|
60
|
+
}
|
|
61
|
+
// Dynamic by necessity: the target is a managed raw script outside this
|
|
62
|
+
// package's compiled rootDir, so no static import can name it.
|
|
63
|
+
const imported = await import(pathToFileURL(RESOLVER_PATH).href);
|
|
64
|
+
if (!isSecretReferencesModule(imported)) {
|
|
65
|
+
throw new Error(`${RESOLVER_PATH} did not load as a module`);
|
|
66
|
+
}
|
|
67
|
+
const { readSecretGroups: read, resolveSecretEnvironment: resolve } = imported;
|
|
68
|
+
if (!isSecretGroupsReader(read) || !isSecretGroupResolver(resolve)) {
|
|
69
|
+
throw new Error(`${RESOLVER_PATH} does not expose the expected secret resolver API`);
|
|
70
|
+
}
|
|
71
|
+
loaded = { readSecretGroups: read, resolveSecretEnvironment: resolve };
|
|
72
|
+
return loaded;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Every `smoo.secrets` entry of the repository at `root`, each with the group
|
|
76
|
+
* that resolves it. Rejects with the offending declaration named — never a
|
|
77
|
+
* value — when the manifest or `.npmrc` cannot be read.
|
|
78
|
+
*/
|
|
79
|
+
export async function readSecretGroups(root) {
|
|
80
|
+
const references = await secretReferences();
|
|
81
|
+
const groups = references.readSecretGroups(root);
|
|
82
|
+
if (!isGroupedSecrets(groups)) {
|
|
83
|
+
throw new Error(`${RESOLVER_PATH} returned an unexpected group listing`);
|
|
84
|
+
}
|
|
85
|
+
return groups;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve exactly one group's secrets for one child process. Everything
|
|
89
|
+
* outside the group comes back deferred rather than resolved, so a run for
|
|
90
|
+
* one group never triggers another group's provider command. Rejects with the
|
|
91
|
+
* resolver's aggregated refusal, which names variables and groups only.
|
|
92
|
+
*/
|
|
93
|
+
export async function resolveSecretGroup(root, group) {
|
|
94
|
+
const references = await secretReferences();
|
|
95
|
+
const resolution = await references.resolveSecretEnvironment({ root, group });
|
|
96
|
+
if (!isSecretResolution(resolution)) {
|
|
97
|
+
throw new Error(`${RESOLVER_PATH} returned an unexpected resolution`);
|
|
98
|
+
}
|
|
99
|
+
return resolution;
|
|
100
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `smoo secrets run <group> <command...>` — the half of THE RULE that shell
|
|
3
|
+
* entry is not.
|
|
4
|
+
*
|
|
5
|
+
* The rule lives in tooling/direnv/secret-references.ts: shell entry resolves
|
|
6
|
+
* the `shell` group and nothing else, because a provider authorises per
|
|
7
|
+
* requesting process lineage and every direnv reload is a new one. Every
|
|
8
|
+
* other group belongs to the command that needs it, and this is that command.
|
|
9
|
+
* One deliberate `smoo secrets run registry bun install` instead of a
|
|
10
|
+
* credential prompt on every shell.
|
|
11
|
+
*
|
|
12
|
+
* Exactly one group is resolved. A repository that declares a registry
|
|
13
|
+
* credential and a cache token runs one provider command here, not two: a
|
|
14
|
+
* wrapper that resolved everything declared would fire provider commands for
|
|
15
|
+
* credentials the command never touches, which is the prompt this exists to
|
|
16
|
+
* remove.
|
|
17
|
+
*
|
|
18
|
+
* The value reaches the child the only way that leaves no trace: its
|
|
19
|
+
* environment. Never a file, never argv — argv is world-readable through
|
|
20
|
+
* `ps` — and never this process's stdout.
|
|
21
|
+
*/
|
|
22
|
+
export declare function secretsRun(root: string, group: string | undefined, command: readonly string[]): Promise<number>;
|
|
23
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/secrets/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAiBH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0CrH"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `smoo secrets run <group> <command...>` — the half of THE RULE that shell
|
|
3
|
+
* entry is not.
|
|
4
|
+
*
|
|
5
|
+
* The rule lives in tooling/direnv/secret-references.ts: shell entry resolves
|
|
6
|
+
* the `shell` group and nothing else, because a provider authorises per
|
|
7
|
+
* requesting process lineage and every direnv reload is a new one. Every
|
|
8
|
+
* other group belongs to the command that needs it, and this is that command.
|
|
9
|
+
* One deliberate `smoo secrets run registry bun install` instead of a
|
|
10
|
+
* credential prompt on every shell.
|
|
11
|
+
*
|
|
12
|
+
* Exactly one group is resolved. A repository that declares a registry
|
|
13
|
+
* credential and a cache token runs one provider command here, not two: a
|
|
14
|
+
* wrapper that resolved everything declared would fire provider commands for
|
|
15
|
+
* credentials the command never touches, which is the prompt this exists to
|
|
16
|
+
* remove.
|
|
17
|
+
*
|
|
18
|
+
* The value reaches the child the only way that leaves no trace: its
|
|
19
|
+
* environment. Never a file, never argv — argv is world-readable through
|
|
20
|
+
* `ps` — and never this process's stdout.
|
|
21
|
+
*/
|
|
22
|
+
import { spawn } from 'node:child_process';
|
|
23
|
+
import { constants } from 'node:os';
|
|
24
|
+
import { readSecretGroups, resolveSecretGroup } from './resolver.js';
|
|
25
|
+
/**
|
|
26
|
+
* Nothing ran, and the listing is the answer: no group named, a group this
|
|
27
|
+
* repository does not declare, or a group with no command to run. Distinct
|
|
28
|
+
* from 1, which is a resolution that refused, and from any other code, which
|
|
29
|
+
* is the child's own.
|
|
30
|
+
*/
|
|
31
|
+
const REFUSED = 2;
|
|
32
|
+
/** A command that could not be started at all, as a shell reports it. */
|
|
33
|
+
const NOT_EXECUTABLE = 127;
|
|
34
|
+
export async function secretsRun(root, group, command) {
|
|
35
|
+
let declared;
|
|
36
|
+
try {
|
|
37
|
+
declared = await readSecretGroups(root);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
if (group === undefined) {
|
|
44
|
+
console.error('smoo secrets run <group> <command...> runs one command with one group of secrets in its environment.');
|
|
45
|
+
printGroups(declared);
|
|
46
|
+
return REFUSED;
|
|
47
|
+
}
|
|
48
|
+
const members = declared.filter((secret) => secret.group === group);
|
|
49
|
+
if (members.length === 0) {
|
|
50
|
+
console.error(`smoo secrets run: no declared secret is in group \`${group}\`.`);
|
|
51
|
+
printGroups(declared);
|
|
52
|
+
return REFUSED;
|
|
53
|
+
}
|
|
54
|
+
const [program, ...args] = command;
|
|
55
|
+
if (program === undefined) {
|
|
56
|
+
console.error(`smoo secrets run ${group} <command...>: name the command to run with ` +
|
|
57
|
+
`${members.map((secret) => secret.name).join(', ')} in its environment.`);
|
|
58
|
+
return REFUSED;
|
|
59
|
+
}
|
|
60
|
+
let values;
|
|
61
|
+
try {
|
|
62
|
+
({ values } = await resolveSecretGroup(root, group));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
return await runWithSecrets(program, args, values);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The child's exit status, reproduced: a code as itself, death by signal as
|
|
72
|
+
* 128+signum, and a program that could not be started as 127. It shares this
|
|
73
|
+
* process group, so terminal signals reach it directly; it is spawned rather
|
|
74
|
+
* than exec'd only because neither Bun nor Node exposes `execve`.
|
|
75
|
+
*/
|
|
76
|
+
async function runWithSecrets(program, args, values) {
|
|
77
|
+
return await new Promise((resolve) => {
|
|
78
|
+
const child = spawn(program, [...args], { stdio: 'inherit', env: { ...process.env, ...values } });
|
|
79
|
+
child.on('error', (error) => {
|
|
80
|
+
console.error(`smoo secrets run: cannot run \`${program}\`: ${error.message}`);
|
|
81
|
+
resolve(NOT_EXECUTABLE);
|
|
82
|
+
});
|
|
83
|
+
child.on('close', (code, signal) => {
|
|
84
|
+
if (signal !== null) {
|
|
85
|
+
resolve(128 + constants.signals[signal]);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
resolve(code ?? 1);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The groups this repository declares and the secrets in each. Most people
|
|
94
|
+
* meet this command exactly once, at a 401, after reading a message that
|
|
95
|
+
* named it — so the refusal answers "which group?" from this checkout's own
|
|
96
|
+
* declarations rather than printing a usage line that sends them to the
|
|
97
|
+
* manifest to work it out.
|
|
98
|
+
*/
|
|
99
|
+
function printGroups(declared) {
|
|
100
|
+
if (declared.length === 0) {
|
|
101
|
+
console.error('This repository declares no smoo.secrets, so there is no group to run.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const members = new Map();
|
|
105
|
+
for (const secret of declared) {
|
|
106
|
+
const named = members.get(secret.group);
|
|
107
|
+
if (named === undefined) {
|
|
108
|
+
members.set(secret.group, [secret.name]);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
named.push(secret.name);
|
|
112
|
+
}
|
|
113
|
+
const groups = [...members.keys()].sort((left, right) => left.localeCompare(right));
|
|
114
|
+
const width = Math.max(...groups.map((group) => group.length));
|
|
115
|
+
console.error('');
|
|
116
|
+
console.error('Groups this repository declares:');
|
|
117
|
+
for (const group of groups) {
|
|
118
|
+
console.error(` ${group.padEnd(width)} ${(members.get(group) ?? []).join(', ')}`);
|
|
119
|
+
}
|
|
120
|
+
// An override is stated, never left for the next reader to discover by
|
|
121
|
+
// wondering why a `.npmrc` credential resolves at shell entry.
|
|
122
|
+
for (const secret of declared) {
|
|
123
|
+
if (secret.group === secret.derivedGroup)
|
|
124
|
+
continue;
|
|
125
|
+
console.error(` note: ${secret.name} declares group \`${secret.group}\`, overriding the \`${secret.derivedGroup}\` ` +
|
|
126
|
+
"this repository's declarations derive.");
|
|
127
|
+
}
|
|
128
|
+
console.error('');
|
|
129
|
+
console.error(` smoo secrets run ${groups[0] ?? '<group>'} <command...>`);
|
|
130
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The value model behind `smoo secrets`: one row per secret name, reconciled
|
|
3
|
+
* from every source that has an opinion about it, plus the machine-readable
|
|
4
|
+
* document `smoo secrets status --json` emits.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is pure and depends on nothing but typia, which is what lets
|
|
7
|
+
* `@smoothbricks/cli/secrets` be imported for the types alone — a consumer
|
|
8
|
+
* driving a UI or a repository's own operations CLI gets the document type and
|
|
9
|
+
* a validator without pulling in `gh`, the filesystem readers, or commander.
|
|
10
|
+
* The readers that produce these facts live in ./index.ts; the operator
|
|
11
|
+
* commands that print them live in ./commands.ts.
|
|
12
|
+
*/
|
|
13
|
+
import typia from 'typia';
|
|
14
|
+
import type { SecretStageMap, SecretStageScope } from '../wrangler/stage-secrets.js';
|
|
15
|
+
export type { SecretStageScope };
|
|
16
|
+
/** Where an env name is described, which repository secret carries it, and whether that exists. */
|
|
17
|
+
export interface SecretRow {
|
|
18
|
+
name: string;
|
|
19
|
+
/** The repository secret this env name reads from, by convention or declaration. */
|
|
20
|
+
repositorySecret: string;
|
|
21
|
+
/** Wrangler projects whose `.dev.vars.example` declares it. */
|
|
22
|
+
declaredByWorkers: string[];
|
|
23
|
+
/** True when a managed workflow renders `secrets.<name>` into a job. */
|
|
24
|
+
suppliedByWorkflow: boolean;
|
|
25
|
+
/** True when `smoo.secrets` can fetch it for a developer shell; equivalently, `localGroup` is set. */
|
|
26
|
+
fetchableLocally: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* How `smoo.secrets` resolves it locally: the group that resolves it, and
|
|
29
|
+
* the group this repository's own declarations derive. They differ exactly
|
|
30
|
+
* when the entry overrides the derivation, which is the one thing a reader
|
|
31
|
+
* must never have to infer. Absent when nothing fetches the name locally.
|
|
32
|
+
*/
|
|
33
|
+
localGroup?: {
|
|
34
|
+
resolves: string;
|
|
35
|
+
derived: string;
|
|
36
|
+
};
|
|
37
|
+
/** True when the repository holds a secret of this name. */
|
|
38
|
+
onRepository: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* GitHub Environments holding their own value for this name. A job bound to
|
|
41
|
+
* an environment reads that value in preference to the repository's, which
|
|
42
|
+
* is how one name carries test credentials on a preview stage and live ones
|
|
43
|
+
* in production.
|
|
44
|
+
*/
|
|
45
|
+
heldByEnvironment: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One `smoo.secrets` entry as the reconciliation needs it: the name, the
|
|
49
|
+
* group that resolves it, and the group this repository's declarations
|
|
50
|
+
* derive. ../secrets/resolver.ts produces these from the same file shell
|
|
51
|
+
* entry routes with.
|
|
52
|
+
*/
|
|
53
|
+
export interface LocalSecret {
|
|
54
|
+
name: string;
|
|
55
|
+
group: string;
|
|
56
|
+
derivedGroup: string;
|
|
57
|
+
}
|
|
58
|
+
export interface SecretSources {
|
|
59
|
+
/** Worker label -> env names it declares. */
|
|
60
|
+
workerSecrets: Record<string, readonly string[]>;
|
|
61
|
+
/** Env names a managed workflow passes into a job. */
|
|
62
|
+
workflowSecrets: readonly string[];
|
|
63
|
+
/** Env name -> repository secret, by convention with declared exceptions. */
|
|
64
|
+
secretNames: Readonly<Record<string, string>>;
|
|
65
|
+
/** Every `smoo.secrets` entry with the group that resolves it. */
|
|
66
|
+
localSecrets: readonly LocalSecret[];
|
|
67
|
+
/** Repository secret names GitHub currently holds. */
|
|
68
|
+
repositorySecrets: readonly string[];
|
|
69
|
+
/** Environment name -> secret names that environment holds. */
|
|
70
|
+
environmentSecrets?: Readonly<Record<string, readonly string[]>>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One row per name known to any source, sorted, so a reader sees the whole
|
|
74
|
+
* picture rather than one source's view of it.
|
|
75
|
+
*/
|
|
76
|
+
export declare function reconcileSecrets(sources: SecretSources): SecretRow[];
|
|
77
|
+
/**
|
|
78
|
+
* The rows a CI deploy cannot satisfy: a Worker declares the name, a workflow
|
|
79
|
+
* promises to pass it, and the repository has no value to pass. Reported
|
|
80
|
+
* separately from "declared but not wired into any workflow", because the
|
|
81
|
+
* remedies differ — set a secret, versus declare it in `smoo.github`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function unsatisfiedSecrets(rows: readonly SecretRow[], boundEnvironments?: readonly string[]): SecretRow[];
|
|
84
|
+
/** Bound environments that lack their own value and cannot fall back to the repository. */
|
|
85
|
+
export declare function environmentsMissing(row: SecretRow, boundEnvironments: readonly string[]): string[];
|
|
86
|
+
/** Worker-declared names no managed workflow passes: a CI deploy will run without them. */
|
|
87
|
+
export declare function unwiredSecrets(rows: readonly SecretRow[]): SecretRow[];
|
|
88
|
+
/** The repository the status was read from, and how that repository was chosen. */
|
|
89
|
+
export interface SecretsStatusRepository {
|
|
90
|
+
/** `owner/name`, the form `gh --repo` takes. */
|
|
91
|
+
repo: string;
|
|
92
|
+
/** Why this repository — the checkout fact that decided it, verbatim from the resolver. */
|
|
93
|
+
source: string;
|
|
94
|
+
/** How many secrets it holds at repository scope. */
|
|
95
|
+
secretCount: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One GitHub Environment the status looked at. Discriminated on `readable`
|
|
99
|
+
* because an environment `gh` refused to list is not an empty one: reporting
|
|
100
|
+
* it as empty would call values missing that may well be set, and the reason
|
|
101
|
+
* is the only thing that lets an operator tell the two apart.
|
|
102
|
+
*/
|
|
103
|
+
export type SecretsStatusEnvironment = {
|
|
104
|
+
name: string;
|
|
105
|
+
/** True when a managed workflow binds a job to it, so a job reads it before the repository. */
|
|
106
|
+
bound: boolean;
|
|
107
|
+
readable: true;
|
|
108
|
+
secretCount: number;
|
|
109
|
+
} | {
|
|
110
|
+
name: string;
|
|
111
|
+
bound: boolean;
|
|
112
|
+
readable: false;
|
|
113
|
+
/** What `gh` said, so nothing below claims what this environment holds. */
|
|
114
|
+
reason: string;
|
|
115
|
+
};
|
|
116
|
+
/** A reconciled row, plus which deployment stages require the name to have a value. */
|
|
117
|
+
export interface SecretsStatusRow extends SecretRow {
|
|
118
|
+
/**
|
|
119
|
+
* Stages that refuse to deploy without a value, from `smoo.wrangler.secretStages`
|
|
120
|
+
* unioned over every wrangler project declaring the name: absent from the map
|
|
121
|
+
* means every stage, mapped to `[]` means none. A name no wrangler project
|
|
122
|
+
* declares is `[]` too — `declaredByWorkers` tells those two apart.
|
|
123
|
+
*/
|
|
124
|
+
requiredByStages: SecretStageScope[];
|
|
125
|
+
}
|
|
126
|
+
/** A name a workflow passes that no scope the job reads can supply: this is what fails a deploy. */
|
|
127
|
+
export interface UnsatisfiedSecret {
|
|
128
|
+
name: string;
|
|
129
|
+
repositorySecret: string;
|
|
130
|
+
/**
|
|
131
|
+
* Bound environments holding no value for it. Empty means no environment is
|
|
132
|
+
* bound and the repository itself is the scope to set it in.
|
|
133
|
+
*/
|
|
134
|
+
missingIn: string[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Everything `smoo secrets status` learned, in one document.
|
|
138
|
+
*
|
|
139
|
+
* `unsatisfied` is the exit code's reason and not a convenience: the rule that
|
|
140
|
+
* decides whether a value exists in a scope a bound job reads is smoo's, and a
|
|
141
|
+
* consumer re-deriving it from `secrets` would be maintaining a second copy of
|
|
142
|
+
* it.
|
|
143
|
+
*/
|
|
144
|
+
export interface SecretsStatusDocument {
|
|
145
|
+
/** Document shape, so a consumer refuses a document it does not understand instead of misreading it. */
|
|
146
|
+
version: 1;
|
|
147
|
+
repository: SecretsStatusRepository;
|
|
148
|
+
/** Every environment looked at: those the workflows bind, then any asked for with `--env`. */
|
|
149
|
+
environments: SecretsStatusEnvironment[];
|
|
150
|
+
/** One row per name any source knows, sorted by name. */
|
|
151
|
+
secrets: SecretsStatusRow[];
|
|
152
|
+
/** Empty exactly when the command exits 0. */
|
|
153
|
+
unsatisfied: UnsatisfiedSecret[];
|
|
154
|
+
}
|
|
155
|
+
/** The facts a document is projected from, all gathered before anything is printed. */
|
|
156
|
+
export interface SecretsStatusFacts {
|
|
157
|
+
repository: SecretsStatusRepository;
|
|
158
|
+
environments: readonly SecretsStatusEnvironment[];
|
|
159
|
+
rows: readonly SecretRow[];
|
|
160
|
+
/** Worker label -> its `smoo.wrangler.secretStages` declaration; a worker with no block is absent. */
|
|
161
|
+
stageScopes: Readonly<Record<string, SecretStageMap>>;
|
|
162
|
+
}
|
|
163
|
+
/** The document, from facts. Pure: every read happened before this was called. */
|
|
164
|
+
export declare function projectSecretsStatus(facts: SecretsStatusFacts): SecretsStatusDocument;
|
|
165
|
+
/**
|
|
166
|
+
* The consumer's boundary: text off a `smoo secrets status --json` run into a
|
|
167
|
+
* validated document, or typia's own account of every field that disagreed.
|
|
168
|
+
*/
|
|
169
|
+
export declare const parseSecretsStatusDocument: (input: string) => typia.IValidation<SecretsStatusDocument>;
|
|
170
|
+
/**
|
|
171
|
+
* The emitter's boundary: the projection is checked against its declared type
|
|
172
|
+
* before it becomes text. A shape bug then fails here, naming the field, rather
|
|
173
|
+
* than reaching a consumer as a document its validator rejects with no idea who
|
|
174
|
+
* produced it.
|
|
175
|
+
*/
|
|
176
|
+
export declare const stringifySecretsStatusDocument: (input: unknown, errorFactory?: undefined | ((props: typia.TypeGuardError.IProps) => Error)) => string;
|
|
177
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/secrets/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,mGAAmG;AACnG,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,wEAAwE;IACxE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sGAAsG;IACtG,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,UAAU,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,4DAA4D;IAC5D,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACjD,sDAAsD;IACtD,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,6EAA6E;IAC7E,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,kEAAkE;IAClE,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IACrC,sDAAsD;IACtD,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,CAyCpE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,EAAE,iBAAiB,GAAE,SAAS,MAAM,EAAO,GAAG,SAAS,EAAE,CAUrH;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAGlG;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAEtE;AASD,mFAAmF;AACnF,MAAM,WAAW,uBAAuB;IACtC,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAChC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;IAChB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,uFAAuF;AACvF,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD;;;;;OAKG;IACH,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;CACtC;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,wGAAwG;IACxG,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,uBAAuB,CAAC;IACpC,8FAA8F;IAC9F,YAAY,EAAE,wBAAwB,EAAE,CAAC;IACzC,yDAAyD;IACzD,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,8CAA8C;IAC9C,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC;AAED,uFAAuF;AACvF,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,uBAAuB,CAAC;IACpC,YAAY,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAClD,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3B,sGAAsG;IACtG,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;CACvD;AAsBD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB,CAqBrF;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,6DAA0D,CAAC;AAElG;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,wGAA4D,CAAC"}
|