redkite 0.1.7 → 0.1.9
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 +343 -26
- package/dist/build.d.ts +2 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +11 -1
- package/dist/build.js.map +1 -1
- package/dist/checks.d.ts.map +1 -1
- package/dist/checks.js +4 -0
- package/dist/checks.js.map +1 -1
- package/dist/cli/agent.d.ts +2 -0
- package/dist/cli/agent.d.ts.map +1 -1
- package/dist/cli/agent.js +23 -1
- package/dist/cli/agent.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +15 -2
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +98 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -1
- package/dist/config.js.map +1 -1
- package/dist/deploy.d.ts +4 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +77 -23
- package/dist/deploy.js.map +1 -1
- package/dist/dockerfile.js +6 -7
- package/dist/dockerfile.js.map +1 -1
- package/dist/environment.d.ts +5 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +20 -0
- package/dist/environment.js.map +1 -0
- package/dist/host.d.ts +2 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +6 -0
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/localHost.d.ts.map +1 -1
- package/dist/localHost.js +2 -0
- package/dist/localHost.js.map +1 -1
- package/dist/plugin.d.ts +15 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +21 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins/bitwarden.d.ts +11 -0
- package/dist/plugins/bitwarden.d.ts.map +1 -0
- package/dist/plugins/bitwarden.js +40 -0
- package/dist/plugins/bitwarden.js.map +1 -0
- package/dist/plugins/digitalOcean.d.ts +19 -0
- package/dist/plugins/digitalOcean.d.ts.map +1 -0
- package/dist/plugins/digitalOcean.js +71 -0
- package/dist/plugins/digitalOcean.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/rds.d.ts +14 -0
- package/dist/plugins/rds.d.ts.map +1 -0
- package/dist/plugins/rds.js +86 -0
- package/dist/plugins/rds.js.map +1 -0
- package/dist/plugins/snapshot.d.ts +7 -0
- package/dist/plugins/snapshot.d.ts.map +1 -0
- package/dist/plugins/snapshot.js +29 -0
- package/dist/plugins/snapshot.js.map +1 -0
- package/dist/presets/nodeApp.d.ts +1 -2
- package/dist/presets/nodeApp.d.ts.map +1 -1
- package/dist/presets/nodeApp.js +0 -1
- package/dist/presets/nodeApp.js.map +1 -1
- package/dist/recover.d.ts +19 -0
- package/dist/recover.d.ts.map +1 -0
- package/dist/recover.js +58 -0
- package/dist/recover.js.map +1 -0
- package/dist/secrets/store.d.ts +5 -2
- package/dist/secrets/store.d.ts.map +1 -1
- package/dist/secrets/store.js +25 -14
- package/dist/secrets/store.js.map +1 -1
- package/dist/source.d.ts +6 -1
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +12 -4
- package/dist/source.js.map +1 -1
- package/dist/sshHost.d.ts +2 -0
- package/dist/sshHost.d.ts.map +1 -1
- package/dist/sshHost.js +29 -11
- package/dist/sshHost.js.map +1 -1
- package/dist/steps.d.ts +0 -7
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +6 -3
- package/dist/steps.js.map +1 -1
- package/dist/types.d.ts +7 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/build.ts +12 -1
- package/src/checks.ts +4 -0
- package/src/cli/agent.ts +28 -1
- package/src/cli/config.ts +19 -2
- package/src/cli/index.ts +118 -29
- package/src/config.ts +27 -1
- package/src/deploy.ts +101 -29
- package/src/dockerfile.ts +7 -11
- package/src/environment.ts +24 -0
- package/src/host.ts +11 -0
- package/src/index.ts +4 -1
- package/src/localHost.ts +3 -0
- package/src/plugin.ts +50 -0
- package/src/plugins/bitwarden.ts +57 -0
- package/src/plugins/digitalOcean.ts +111 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/rds.ts +126 -0
- package/src/plugins/snapshot.ts +44 -0
- package/src/presets/nodeApp.ts +1 -3
- package/src/recover.ts +90 -0
- package/src/secrets/store.ts +39 -20
- package/src/source.ts +20 -5
- package/src/sshHost.ts +35 -11
- package/src/steps.ts +7 -6
- package/src/types.ts +29 -12
- package/dist/secrets/bitwarden.d.ts +0 -3
- package/dist/secrets/bitwarden.d.ts.map +0 -1
- package/dist/secrets/bitwarden.js +0 -6
- package/dist/secrets/bitwarden.js.map +0 -1
- package/src/secrets/bitwarden.ts +0 -7
package/dist/steps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnyStep } from "./pipeline.js";
|
|
2
|
+
import type { Plugin } from "./plugin.js";
|
|
2
3
|
export type Environment = {
|
|
3
4
|
buildOn?: "host" | "local";
|
|
4
5
|
branch: string;
|
|
@@ -10,9 +11,11 @@ export type Environment = {
|
|
|
10
11
|
export type StepNetwork = "host" | "deployment" | "none" | {
|
|
11
12
|
named: string;
|
|
12
13
|
};
|
|
14
|
+
export type HostKeys = "accept-new" | "strict" | "off";
|
|
13
15
|
export type DeployHost = {
|
|
14
16
|
bastion: string;
|
|
15
17
|
socket?: string;
|
|
18
|
+
hostKeys?: HostKeys;
|
|
16
19
|
};
|
|
17
20
|
export type SecretRef = {
|
|
18
21
|
provider: string;
|
|
@@ -53,11 +56,6 @@ export type BuildSpec = {
|
|
|
53
56
|
aptPackages: string[];
|
|
54
57
|
runtimePackages: string[];
|
|
55
58
|
runtimeSteps: string[];
|
|
56
|
-
sourcemaps?: SourcemapSpec;
|
|
57
|
-
};
|
|
58
|
-
export type SourcemapSpec = {
|
|
59
|
-
provider: "sentry";
|
|
60
|
-
stripFromImage: string;
|
|
61
59
|
};
|
|
62
60
|
export type VerifySpec = {
|
|
63
61
|
steps: string[];
|
|
@@ -79,6 +77,9 @@ export type AppSpec = {
|
|
|
79
77
|
route: string;
|
|
80
78
|
port: number;
|
|
81
79
|
secrets?: SecretRefs;
|
|
80
|
+
branch?: string;
|
|
81
|
+
tag?: string;
|
|
82
|
+
commit?: string;
|
|
82
83
|
dir?: string;
|
|
83
84
|
build: BuildSpec;
|
|
84
85
|
health: HealthSpec;
|
|
@@ -96,5 +97,6 @@ export type Deployment = {
|
|
|
96
97
|
services: ServiceSpec[];
|
|
97
98
|
apps: AppSpec[];
|
|
98
99
|
steps?: AnyStep[];
|
|
100
|
+
plugins?: Plugin[];
|
|
99
101
|
};
|
|
100
102
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/build.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import type { Docker } from "./docker.js";
|
|
5
5
|
import { BUILDER_STAGE, renderDockerfile, renderDockerignore } from "./dockerfile.js";
|
|
6
|
+
import type { Ref } from "./source.js";
|
|
6
7
|
import type { Host } from "./host.js";
|
|
7
8
|
import { prepareSource } from "./source.js";
|
|
8
9
|
import type { AppTopology } from "./topology.js";
|
|
@@ -48,6 +49,16 @@ export type BuildResult = {
|
|
|
48
49
|
cached: boolean;
|
|
49
50
|
};
|
|
50
51
|
|
|
52
|
+
// The app's own, or the environment's branch when it names none. A branch is
|
|
53
|
+
// the only thing an environment can say, because a tag or a commit is a claim
|
|
54
|
+
// about one repository and an environment spans every app
|
|
55
|
+
export function refOf(app: AppSpec, branch: string): Ref {
|
|
56
|
+
if (app.tag) return { kind: "tag", name: app.tag };
|
|
57
|
+
if (app.commit) return { kind: "commit", name: app.commit };
|
|
58
|
+
|
|
59
|
+
return { kind: "branch", name: app.branch ?? branch };
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
export async function build(
|
|
52
63
|
app: AppSpec,
|
|
53
64
|
topology: AppTopology,
|
|
@@ -64,7 +75,7 @@ export async function build(
|
|
|
64
75
|
// against the deployment file rather than wherever this was run
|
|
65
76
|
path: app.path && resolve(app.path),
|
|
66
77
|
include: app.include,
|
|
67
|
-
|
|
78
|
+
ref: refOf(app, context.branch),
|
|
68
79
|
submodules: spec.submodules,
|
|
69
80
|
detail,
|
|
70
81
|
});
|
package/src/checks.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { envFlags } from "./environment.js";
|
|
1
2
|
import { appRoot } from "./layout.js";
|
|
2
3
|
import type { Built, Context, Plan, Released } from "./pipeline.js";
|
|
3
4
|
import { quote } from "./shell.js";
|
|
@@ -31,6 +32,9 @@ async function checkApp(app: AppSpec, spec: VerifySpec, input: Built, context: C
|
|
|
31
32
|
const flags = [
|
|
32
33
|
"run --rm",
|
|
33
34
|
...attachment(spec.network ?? "deployment", context.topology),
|
|
35
|
+
// Before the named settings, so a check can override what the vault says
|
|
36
|
+
// without editing the vault
|
|
37
|
+
...(await envFlags(app, context)),
|
|
34
38
|
`--workdir ${appRoot(app.dir)}`,
|
|
35
39
|
...settings({ ...app.environment, ...spec.environment }),
|
|
36
40
|
builderOf(input, app.name),
|
package/src/cli/agent.ts
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
|
|
3
|
+
import { environmentOf } from "../config.js";
|
|
4
|
+
import type { Deployment } from "../types.js";
|
|
5
|
+
|
|
3
6
|
// Cloning private repositories and opening the tunnel both go through the
|
|
4
7
|
// agent, so a deploy without one fails halfway rather than at the start
|
|
5
8
|
|
|
9
|
+
// Asked before one is demanded. A runner has no keys and no way to be prompted
|
|
10
|
+
// for one, and a deployment that reaches no other machine and clones nothing
|
|
11
|
+
// has no use for an agent: insisting on one there is a green run turned red
|
|
12
|
+
export function needsAgent(config: Deployment, environment: string) {
|
|
13
|
+
if (environmentOf(config, environment)?.host?.bastion) return true;
|
|
14
|
+
|
|
15
|
+
return config.apps.some((app) => app.repo !== undefined && overSsh(app.repo));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// A clone URL that names a transport of its own carries its own credentials.
|
|
19
|
+
// Anything else is the scp-like form, which is ssh and wants a key
|
|
20
|
+
function overSsh(repo: string) {
|
|
21
|
+
return !/^(https?|git|file):\/\//.test(repo);
|
|
22
|
+
}
|
|
23
|
+
|
|
6
24
|
// A plain writer rather than a Log, because this runs before the view opens:
|
|
7
25
|
// ssh-add may ask for a passphrase, and it cannot ask through a screen
|
|
8
26
|
// something else is drawing
|
|
@@ -22,7 +40,16 @@ export function requireAgent(warn: (message: string) => void) {
|
|
|
22
40
|
if (pid) process.env["SSH_AGENT_PID"] = pid;
|
|
23
41
|
|
|
24
42
|
// Inherits stdio so a passphrase prompt reaches the person running this
|
|
25
|
-
|
|
43
|
+
try {
|
|
44
|
+
execFileSync("ssh-add", [], { stdio: "inherit" });
|
|
45
|
+
} catch {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"ssh-add found no key to load. This deployment reaches another machine " +
|
|
48
|
+
"or clones over ssh, so it needs an agent holding a key that can. On a " +
|
|
49
|
+
"runner, set one up before this step and leave SSH_AUTH_SOCK in the " +
|
|
50
|
+
"environment",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
26
53
|
|
|
27
54
|
return socket;
|
|
28
55
|
}
|
package/src/cli/config.ts
CHANGED
|
@@ -16,8 +16,16 @@ const EXTENSIONS = ["ts", "mts", "js", "mjs"] as const;
|
|
|
16
16
|
|
|
17
17
|
const CANDIDATES = EXTENSIONS.map((extension) => `redkite.config.${extension}`);
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
|
|
19
|
+
// Anything beside the deployment that starts with redkite and is a module it
|
|
20
|
+
// could read. The .config in the middle is what people write and what the docs
|
|
21
|
+
// say, and it is optional here because a file that was meant to be an
|
|
22
|
+
// environment and is named slightly differently should be read and refused
|
|
23
|
+
// rather than passed over as though it were not there
|
|
24
|
+
const PER_ENVIRONMENT = /^redkite[.\-_](.+?)(?:\.config)?\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
25
|
+
|
|
26
|
+
// What an environment may be called. It becomes part of an image tag, and an
|
|
27
|
+
// image tag cannot hold a capital, so this is docker's limit rather than ours
|
|
28
|
+
const ENVIRONMENT_NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
21
29
|
|
|
22
30
|
// Node strips types itself from 22.18 on, which is what lets redkite ship with
|
|
23
31
|
// no dependencies. These are the ways that can fail on a config it cannot read.
|
|
@@ -121,9 +129,18 @@ export async function loadEnvironments(directory: string) {
|
|
|
121
129
|
const seen: Record<string, string> = {};
|
|
122
130
|
|
|
123
131
|
for (const entry of readdirSync(directory).sort()) {
|
|
132
|
+
if (CANDIDATES.includes(entry)) continue;
|
|
133
|
+
|
|
124
134
|
const name = PER_ENVIRONMENT.exec(entry)?.[1];
|
|
125
135
|
if (!name) continue;
|
|
126
136
|
|
|
137
|
+
if (!ENVIRONMENT_NAME.test(name)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`${join(directory, entry)} reads as the environment ${name}, which cannot ` +
|
|
140
|
+
"name one: it becomes part of an image tag, and a tag is lower case",
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
127
144
|
const first = seen[name];
|
|
128
145
|
if (first) {
|
|
129
146
|
throw new Error(`${name} is defined by both ${first} and ${entry}`);
|
package/src/cli/index.ts
CHANGED
|
@@ -7,13 +7,14 @@ import { silent, type Log } from "../log.js";
|
|
|
7
7
|
import { renderNginx } from "../nginx.js";
|
|
8
8
|
import { addressOf, RUNS, SLOTS, type Run } from "../pipeline.js";
|
|
9
9
|
import { listRefs, type SecretStores } from "../secrets/refs.js";
|
|
10
|
-
import {
|
|
10
|
+
import { pluginSteps, storeFor } from "../plugin.js";
|
|
11
|
+
import { down, rollback } from "../recover.js";
|
|
11
12
|
import { driftOf, plannedServices, type Drift } from "../services/planned.js";
|
|
12
13
|
import { sshHost } from "../sshHost.js";
|
|
13
14
|
import { topologyFor, type Topology } from "../topology.js";
|
|
14
15
|
import type { Deployment, DeployHost } from "../types.js";
|
|
15
16
|
|
|
16
|
-
import { requireAgent } from "./agent.js";
|
|
17
|
+
import { needsAgent, requireAgent } from "./agent.js";
|
|
17
18
|
import { loadConfig } from "./config.js";
|
|
18
19
|
import { createLog, describeFailure } from "./log.js";
|
|
19
20
|
|
|
@@ -25,6 +26,8 @@ const USAGE = `redkite <command> [environment]
|
|
|
25
26
|
plan [environment] Print the derived topology, the pipeline and the nginx
|
|
26
27
|
deploy [environment] Build, swap, health check, and revert on failure
|
|
27
28
|
verify [environment] Bring the services up, build, and run each app's checks
|
|
29
|
+
rollback [environment] Put back any app a run moved and did not finish
|
|
30
|
+
down [environment] Stop everything this environment named
|
|
28
31
|
|
|
29
32
|
--config <path> Defaults to redkite.config.ts at the root of the project
|
|
30
33
|
--local Build the images here and ship them to the host
|
|
@@ -46,6 +49,7 @@ function measured(host: Host, log?: Log) {
|
|
|
46
49
|
cache: host.cache,
|
|
47
50
|
pipe: host.pipe.bind(host),
|
|
48
51
|
stop: host.stop.bind(host),
|
|
52
|
+
final: host.final.bind(host),
|
|
49
53
|
|
|
50
54
|
sh: async (command, onLine) => {
|
|
51
55
|
const started = Date.now();
|
|
@@ -143,6 +147,9 @@ async function dispatch(argv: string[]) {
|
|
|
143
147
|
const configPath = flag(argv, "--config");
|
|
144
148
|
|
|
145
149
|
if (command === "plan") return await plan(environment, configPath);
|
|
150
|
+
if (command === "rollback" || command === "down") {
|
|
151
|
+
return await recover(command, environment, configPath);
|
|
152
|
+
}
|
|
146
153
|
if (command === "deploy" || command === "verify") {
|
|
147
154
|
return await run(command, environment, configPath, {
|
|
148
155
|
verbose: argv.includes("--verbose"),
|
|
@@ -155,6 +162,49 @@ async function dispatch(argv: string[]) {
|
|
|
155
162
|
process.exitCode = command ? 1 : 0;
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
// Neither runs a pipeline. Both exist for the run that was killed rather than
|
|
166
|
+
// asked to stop, so they read what the host is in rather than what a deploy
|
|
167
|
+
// remembered, and both are safe where there is nothing to do
|
|
168
|
+
async function recover(
|
|
169
|
+
command: "rollback" | "down",
|
|
170
|
+
environment: string,
|
|
171
|
+
configPath?: string,
|
|
172
|
+
) {
|
|
173
|
+
const config = await loadConfig(configPath);
|
|
174
|
+
const topology = topologyFor(config, environment);
|
|
175
|
+
const deployHost = environmentOf(config, environment)?.host;
|
|
176
|
+
|
|
177
|
+
const say = (message = "") => process.stdout.write(`${message}\n`);
|
|
178
|
+
if (needsAgent(config, environment)) requireAgent(say);
|
|
179
|
+
|
|
180
|
+
const host = await hostFor(deployHost, silent);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
if (command === "down") {
|
|
184
|
+
const { stopped } = await down({ config, environment, host, log: asLog(say) });
|
|
185
|
+
|
|
186
|
+
say(stopped.length === 0 ? "Nothing was running" : `Stopped ${stopped.join(", ")}`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const { restored } = await rollback({ config, environment, host, log: asLog(say) });
|
|
191
|
+
|
|
192
|
+
if (restored.length === 0) {
|
|
193
|
+
return say(`Nothing to put back in ${topology.environment}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
say(`Put back ${restored.join(", ")}`);
|
|
197
|
+
} finally {
|
|
198
|
+
await host.close?.();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// These print rather than draw: a recovery runs after the view has gone, and
|
|
203
|
+
// often in a job that is already being torn down
|
|
204
|
+
function asLog(say: (message: string) => void): Log {
|
|
205
|
+
return Object.assign(say, { warn: say, fail: say, done: say, step: () => silent.step("") });
|
|
206
|
+
}
|
|
207
|
+
|
|
158
208
|
async function plan(environment: string, configPath?: string) {
|
|
159
209
|
const config = await loadConfig(configPath);
|
|
160
210
|
const topology = topologyFor(config, environment);
|
|
@@ -203,6 +253,7 @@ async function plan(environment: string, configPath?: string) {
|
|
|
203
253
|
// run it supports. Nothing declares that: the missing port is what says it
|
|
204
254
|
const serves = Boolean(topology.publicPort);
|
|
205
255
|
|
|
256
|
+
sayPlugins(config, say);
|
|
206
257
|
sayPipeline(config, serves, say);
|
|
207
258
|
await sayDrift(config, topology, serves ? "deploy" : "verify", say);
|
|
208
259
|
|
|
@@ -250,6 +301,29 @@ const DRIFT: Record<Drift["reason"], string> = {
|
|
|
250
301
|
unrecognised: "not created by redkite, will be recreated",
|
|
251
302
|
};
|
|
252
303
|
|
|
304
|
+
// Everything this deployment opted into. A vault that is missing from here is
|
|
305
|
+
// the reason its refs will not resolve, and that is worth seeing before a run
|
|
306
|
+
function sayPlugins(config: Deployment, say: (message?: string) => void) {
|
|
307
|
+
const plugins = config.plugins ?? [];
|
|
308
|
+
if (plugins.length === 0) return;
|
|
309
|
+
|
|
310
|
+
say("\nplugins");
|
|
311
|
+
|
|
312
|
+
for (const plugin of plugins) {
|
|
313
|
+
const stores = Object.keys(plugin.stores ?? {});
|
|
314
|
+
const resolves = stores.length > 0 ? ` resolves ${stores.join(", ")}` : "";
|
|
315
|
+
const count = plugin.steps?.length ?? 0;
|
|
316
|
+
|
|
317
|
+
say(` ${plugin.name}${gap(plugin.name.length)}${count} ${count === 1 ? "step" : "steps"}${resolves}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const COLUMN = 26;
|
|
322
|
+
|
|
323
|
+
function gap(width: number) {
|
|
324
|
+
return " ".repeat(Math.max(2, COLUMN - width));
|
|
325
|
+
}
|
|
326
|
+
|
|
253
327
|
// What each run will actually do, in the order it will do it. A step is
|
|
254
328
|
// addressed rather than called, so this is the only place a sequence is visible
|
|
255
329
|
function sayPipeline(
|
|
@@ -257,9 +331,19 @@ function sayPipeline(
|
|
|
257
331
|
serves: boolean,
|
|
258
332
|
say: (message?: string) => void,
|
|
259
333
|
) {
|
|
260
|
-
|
|
334
|
+
// What the run will walk, which is the plugins' steps and then the
|
|
335
|
+
// deployment's own. A plan that showed only one of them would be a plan of a
|
|
336
|
+
// different deploy
|
|
337
|
+
const steps = [...pluginSteps(config.plugins), ...(config.steps ?? [])];
|
|
261
338
|
const added = new Set(steps.map((step) => step.point));
|
|
262
339
|
|
|
340
|
+
// Says which plugin a step came from, because a point on its own does not
|
|
341
|
+
const from = new Map(
|
|
342
|
+
(config.plugins ?? []).flatMap((plugin) =>
|
|
343
|
+
(plugin.steps ?? []).map((step) => [step.point, plugin.name] as const),
|
|
344
|
+
),
|
|
345
|
+
);
|
|
346
|
+
|
|
263
347
|
const at = (phase: string, slot: string) =>
|
|
264
348
|
steps.filter((step) => {
|
|
265
349
|
const address = addressOf(step.point);
|
|
@@ -294,7 +378,12 @@ function sayPipeline(
|
|
|
294
378
|
say(` ${phase.padEnd(26)}${who}${what}`);
|
|
295
379
|
}
|
|
296
380
|
|
|
297
|
-
for (const step of at(phase, slot))
|
|
381
|
+
for (const step of at(phase, slot)) {
|
|
382
|
+
const owner = from.get(step.point);
|
|
383
|
+
if (!owner) say(` ${step.point}`);
|
|
384
|
+
// A point long enough to fill the column still gets its two spaces
|
|
385
|
+
else say(` ${step.point}${gap(step.point.length)}${owner}`);
|
|
386
|
+
}
|
|
298
387
|
}
|
|
299
388
|
}
|
|
300
389
|
}
|
|
@@ -395,7 +484,7 @@ async function hostFor(host: DeployHost | undefined, log: Log, signal?: AbortSig
|
|
|
395
484
|
const task = log.step(`Opening a connection to ${host.bastion}`);
|
|
396
485
|
|
|
397
486
|
try {
|
|
398
|
-
const opened = await sshHost(host.bastion, { signal });
|
|
487
|
+
const opened = await sshHost(host.bastion, { signal, hostKeys: host.hostKeys });
|
|
399
488
|
task.done();
|
|
400
489
|
return opened;
|
|
401
490
|
} catch (error) {
|
|
@@ -406,6 +495,9 @@ async function hostFor(host: DeployHost | undefined, log: Log, signal?: AbortSig
|
|
|
406
495
|
|
|
407
496
|
// Only the stores the config actually names. A deployment that keeps its
|
|
408
497
|
// environment somewhere else, or nowhere, needs no credentials to deploy
|
|
498
|
+
// One store per provider a ref names, opened from the plugins the deployment
|
|
499
|
+
// registered. Nothing is opened for a deployment that names no refs, and a ref
|
|
500
|
+
// no plugin answers for is refused rather than reaching for a vault by name
|
|
409
501
|
async function openStores(config: Deployment, log: Log): Promise<SecretStores> {
|
|
410
502
|
// Services name refs too, and a deployment whose only secret is a database
|
|
411
503
|
// password opened no store at all until this counted them
|
|
@@ -417,35 +509,30 @@ async function openStores(config: Deployment, log: Log): Promise<SecretStores> {
|
|
|
417
509
|
...config.services.flatMap((service) => listRefs(service.secrets)),
|
|
418
510
|
];
|
|
419
511
|
|
|
420
|
-
const
|
|
512
|
+
const opened: SecretStores = {};
|
|
421
513
|
|
|
422
|
-
|
|
514
|
+
for (const provider of new Set(refs.map((ref) => ref.provider))) {
|
|
515
|
+
const open = storeFor(config.plugins, provider);
|
|
423
516
|
|
|
424
|
-
|
|
517
|
+
if (!open) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`Nothing resolves ${provider} secrets. Add the plugin that does to ` +
|
|
520
|
+
"the plugins this deployment registers",
|
|
521
|
+
);
|
|
522
|
+
}
|
|
425
523
|
|
|
426
|
-
|
|
427
|
-
const bitwarden = await bitwardenStore({
|
|
428
|
-
detail: vault.detail,
|
|
429
|
-
clientId: required("BW_CLIENT_ID"),
|
|
430
|
-
clientSecret: required("BW_CLIENT_SECRET"),
|
|
431
|
-
password: required("BW_PASSWORD"),
|
|
432
|
-
});
|
|
524
|
+
const task = log.step(`Reading the ${provider} vault`);
|
|
433
525
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
526
|
+
try {
|
|
527
|
+
opened[provider] = await open({ detail: task.detail });
|
|
528
|
+
task.done();
|
|
529
|
+
} catch (error) {
|
|
530
|
+
task.fail(`Could not read the ${provider} vault`);
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
439
533
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function required(name: string) {
|
|
443
|
-
const value = process.env[name];
|
|
444
|
-
if (value) return value;
|
|
445
534
|
|
|
446
|
-
|
|
447
|
-
`${name} is not set, and this deployment reads its environment from Bitwarden`,
|
|
448
|
-
);
|
|
535
|
+
return opened;
|
|
449
536
|
}
|
|
450
537
|
|
|
451
538
|
type RunOptions = { verbose: boolean; full: boolean; local: boolean };
|
|
@@ -465,7 +552,9 @@ async function run(
|
|
|
465
552
|
// The host clones the repositories over this, so it has to exist before the
|
|
466
553
|
// connection that forwards it is opened. Before the view too: ssh-add asks
|
|
467
554
|
// for a passphrase on the terminal, and by then the view owns it
|
|
468
|
-
|
|
555
|
+
if (needsAgent(config, environment)) {
|
|
556
|
+
requireAgent((message) => process.stderr.write(`${message}\n`));
|
|
557
|
+
}
|
|
469
558
|
|
|
470
559
|
// Whatever is in flight is killed, the pipeline unwinds through its own
|
|
471
560
|
// failure path, and the finally below removes the scratch directory
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertSteps } from "./pipeline.js";
|
|
2
|
+
import { pluginSteps } from "./plugin.js";
|
|
2
3
|
import type { Deployment, Environment } from "./types.js";
|
|
3
4
|
|
|
4
5
|
// The one selected, from the one place worth looking. A deployment that carries
|
|
@@ -19,7 +20,10 @@ export function defineDeployment<const T extends Deployment & { environments?: n
|
|
|
19
20
|
assertOneSource(config);
|
|
20
21
|
assertRoutesResolvable(config);
|
|
21
22
|
assertDirsRelative(config);
|
|
22
|
-
|
|
23
|
+
assertUniquePlugins(config);
|
|
24
|
+
// Together, because a plugin's step and the deployment's own share the same
|
|
25
|
+
// points and two of them at one point is the collision worth catching
|
|
26
|
+
assertSteps([...pluginSteps(config.plugins), ...(config.steps ?? [])]);
|
|
23
27
|
return config;
|
|
24
28
|
}
|
|
25
29
|
|
|
@@ -29,6 +33,20 @@ export function defineEnvironment<const T extends Environment>(environment: T):
|
|
|
29
33
|
return environment;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
// Registering one twice is either a mistake or two configurations of the same
|
|
37
|
+
// thing, and neither is something to pick a winner for
|
|
38
|
+
function assertUniquePlugins(config: Deployment) {
|
|
39
|
+
const seen = new Set<string>();
|
|
40
|
+
|
|
41
|
+
for (const plugin of config.plugins ?? []) {
|
|
42
|
+
if (seen.has(plugin.name)) {
|
|
43
|
+
throw new Error(`The plugin ${plugin.name} is registered twice`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
seen.add(plugin.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
32
50
|
// Cloned or already here, and the two are built differently enough that
|
|
33
51
|
// guessing between them is worse than being told
|
|
34
52
|
function assertOneSource(config: Deployment) {
|
|
@@ -47,6 +65,14 @@ function assertOneSource(config: Deployment) {
|
|
|
47
65
|
throw new Error(`${app.name} says what to include, but is cloned rather than built from a path`);
|
|
48
66
|
}
|
|
49
67
|
|
|
68
|
+
const refs = ["branch", "tag", "commit"].filter((kind) => app[kind as "branch"]);
|
|
69
|
+
|
|
70
|
+
if (refs.length > 1) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`${app.name} names ${refs.join(" and ")} to build from, and they are different commits`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
50
76
|
if (app.include?.length === 0) {
|
|
51
77
|
throw new Error(`${app.name} includes nothing, so there would be no build context`);
|
|
52
78
|
}
|