redkite 0.1.6
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/LICENSE +21 -0
- package/README.md +819 -0
- package/bin/redkite.mjs +12 -0
- package/dist/build.d.ts +27 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +132 -0
- package/dist/build.js.map +1 -0
- package/dist/checks.d.ts +4 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +49 -0
- package/dist/checks.js.map +1 -0
- package/dist/cli/agent.d.ts +2 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +24 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +217 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +438 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/log.d.ts +9 -0
- package/dist/cli/log.d.ts.map +1 -0
- package/dist/cli/log.js +128 -0
- package/dist/cli/log.js.map +1 -0
- package/dist/cli/screen.d.ts +34 -0
- package/dist/cli/screen.d.ts.map +1 -0
- package/dist/cli/screen.js +298 -0
- package/dist/cli/screen.js.map +1 -0
- package/dist/cli/viewer.d.ts +9 -0
- package/dist/cli/viewer.d.ts.map +1 -0
- package/dist/cli/viewer.js +139 -0
- package/dist/cli/viewer.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +79 -0
- package/dist/config.js.map +1 -0
- package/dist/deploy.d.ts +19 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +275 -0
- package/dist/deploy.js.map +1 -0
- package/dist/docker.d.ts +96 -0
- package/dist/docker.d.ts.map +1 -0
- package/dist/docker.js +325 -0
- package/dist/docker.js.map +1 -0
- package/dist/dockerfile.d.ts +14 -0
- package/dist/dockerfile.d.ts.map +1 -0
- package/dist/dockerfile.js +160 -0
- package/dist/dockerfile.js.map +1 -0
- package/dist/health.d.ts +13 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +54 -0
- package/dist/health.js.map +1 -0
- package/dist/host.d.ts +21 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +27 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +5 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +45 -0
- package/dist/layout.js.map +1 -0
- package/dist/localHost.d.ts +7 -0
- package/dist/localHost.d.ts.map +1 -0
- package/dist/localHost.js +32 -0
- package/dist/localHost.js.map +1 -0
- package/dist/log.d.ts +14 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +15 -0
- package/dist/log.js.map +1 -0
- package/dist/nginx.d.ts +4 -0
- package/dist/nginx.d.ts.map +1 -0
- package/dist/nginx.js +53 -0
- package/dist/nginx.js.map +1 -0
- package/dist/pipeline.d.ts +103 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +142 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/presets/index.d.ts +3 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +3 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/nextApp.d.ts +15 -0
- package/dist/presets/nextApp.d.ts.map +1 -0
- package/dist/presets/nextApp.js +53 -0
- package/dist/presets/nextApp.js.map +1 -0
- package/dist/presets/nodeApp.d.ts +19 -0
- package/dist/presets/nodeApp.d.ts.map +1 -0
- package/dist/presets/nodeApp.js +29 -0
- package/dist/presets/nodeApp.js.map +1 -0
- package/dist/secrets/bitwarden.d.ts +3 -0
- package/dist/secrets/bitwarden.d.ts.map +1 -0
- package/dist/secrets/bitwarden.js +6 -0
- package/dist/secrets/bitwarden.js.map +1 -0
- package/dist/secrets/refs.d.ts +9 -0
- package/dist/secrets/refs.d.ts.map +1 -0
- package/dist/secrets/refs.js +20 -0
- package/dist/secrets/refs.js.map +1 -0
- package/dist/secrets/store.d.ts +9 -0
- package/dist/secrets/store.d.ts.map +1 -0
- package/dist/secrets/store.js +104 -0
- package/dist/secrets/store.js.map +1 -0
- package/dist/services/ensure.d.ts +18 -0
- package/dist/services/ensure.d.ts.map +1 -0
- package/dist/services/ensure.js +93 -0
- package/dist/services/ensure.js.map +1 -0
- package/dist/services/index.d.ts +19 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +27 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/planned.d.ts +20 -0
- package/dist/services/planned.d.ts.map +1 -0
- package/dist/services/planned.js +77 -0
- package/dist/services/planned.js.map +1 -0
- package/dist/shell.d.ts +11 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +65 -0
- package/dist/shell.js.map +1 -0
- package/dist/source.d.ts +16 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +108 -0
- package/dist/source.js.map +1 -0
- package/dist/sshHost.d.ts +15 -0
- package/dist/sshHost.d.ts.map +1 -0
- package/dist/sshHost.js +91 -0
- package/dist/sshHost.js.map +1 -0
- package/dist/steps.d.ts +27 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +69 -0
- package/dist/steps.js.map +1 -0
- package/dist/topology.d.ts +40 -0
- package/dist/topology.d.ts.map +1 -0
- package/dist/topology.js +113 -0
- package/dist/topology.js.map +1 -0
- package/dist/types.d.ts +100 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/src/build.ts +236 -0
- package/src/checks.ts +65 -0
- package/src/cli/agent.ts +28 -0
- package/src/cli/config.ts +275 -0
- package/src/cli/index.ts +559 -0
- package/src/cli/log.ts +168 -0
- package/src/cli/screen.ts +409 -0
- package/src/cli/viewer.ts +192 -0
- package/src/config.ts +90 -0
- package/src/deploy.ts +409 -0
- package/src/docker.ts +433 -0
- package/src/dockerfile.ts +228 -0
- package/src/health.ts +83 -0
- package/src/host.ts +54 -0
- package/src/index.ts +25 -0
- package/src/layout.ts +41 -0
- package/src/localHost.ts +50 -0
- package/src/log.ts +38 -0
- package/src/nginx.ts +63 -0
- package/src/pipeline.ts +335 -0
- package/src/presets/index.ts +2 -0
- package/src/presets/nextApp.ts +70 -0
- package/src/presets/nodeApp.ts +46 -0
- package/src/secrets/bitwarden.ts +7 -0
- package/src/secrets/refs.ts +32 -0
- package/src/secrets/store.ts +147 -0
- package/src/services/ensure.ts +160 -0
- package/src/services/index.ts +48 -0
- package/src/services/planned.ts +119 -0
- package/src/shell.ts +95 -0
- package/src/source.ts +171 -0
- package/src/sshHost.ts +145 -0
- package/src/steps.ts +101 -0
- package/src/topology.ts +199 -0
- package/src/types.ts +243 -0
package/dist/docker.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
const RUNNING = new Set(["running", "restarting"]);
|
|
2
|
+
// What a container was created from, recorded on the container itself. Docker
|
|
3
|
+
// cannot change a label without recreating, which is exactly when it changes
|
|
4
|
+
export const SPEC_LABEL = "redkite.spec";
|
|
5
|
+
// One command in place of an inspect per object. Every guard in this file used
|
|
6
|
+
// to be its own round trip, and a round trip here is a container exec
|
|
7
|
+
const SNAPSHOT = [
|
|
8
|
+
`ps -a --format '{{.Names}}\t{{.State}}\t{{.Label "${SPEC_LABEL}"}}'`,
|
|
9
|
+
"docker image ls --format '{{.Repository}}:{{.Tag}}'",
|
|
10
|
+
"docker network ls --format '{{.Name}}'",
|
|
11
|
+
].join("; echo --- ; ");
|
|
12
|
+
export class Docker {
|
|
13
|
+
host;
|
|
14
|
+
network;
|
|
15
|
+
image;
|
|
16
|
+
container;
|
|
17
|
+
// The promise, not the state. Two builds and the infrastructure step all ask
|
|
18
|
+
// for this at once, and caching the result alone lets every one of them miss
|
|
19
|
+
state;
|
|
20
|
+
constructor(host) {
|
|
21
|
+
this.host = host;
|
|
22
|
+
this.network = new DockerNetwork(this);
|
|
23
|
+
this.image = new DockerImage(this);
|
|
24
|
+
this.container = new DockerContainer(this);
|
|
25
|
+
}
|
|
26
|
+
// Read once, then kept current by the mutations below. A deploy owns the
|
|
27
|
+
// host for its duration, so nothing else is moving underneath it
|
|
28
|
+
async snapshot() {
|
|
29
|
+
this.state ??= this.read();
|
|
30
|
+
return await this.state;
|
|
31
|
+
}
|
|
32
|
+
async read() {
|
|
33
|
+
const { stdout } = await this.run(SNAPSHOT);
|
|
34
|
+
const [containers = "", images = "", networks = ""] = stdout.split("---");
|
|
35
|
+
return {
|
|
36
|
+
containers: new Map(lines(containers).map((line) => {
|
|
37
|
+
const [name = "", state = "unknown", spec = ""] = line.split("\t");
|
|
38
|
+
return [name, { state, spec: spec || undefined }];
|
|
39
|
+
})),
|
|
40
|
+
// Both spellings, so a lookup by bare name and one by name:tag both hit
|
|
41
|
+
images: new Set(lines(images).flatMap((line) => [line, line.replace(/:latest$/, "")])),
|
|
42
|
+
networks: new Set(lines(networks)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async run(command, onLine) {
|
|
46
|
+
return await this.host.sh(`docker ${command}`, onLine);
|
|
47
|
+
}
|
|
48
|
+
// Called by every mutation, so the snapshot never goes stale
|
|
49
|
+
async track(change) {
|
|
50
|
+
change(await this.snapshot());
|
|
51
|
+
}
|
|
52
|
+
async runOrThrow(command, message, onLine) {
|
|
53
|
+
const result = await this.run(command, onLine);
|
|
54
|
+
if (result.code === 0)
|
|
55
|
+
return result;
|
|
56
|
+
throw new Error(`${message}: ${result.stderr || result.stdout}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class DockerNetwork {
|
|
60
|
+
docker;
|
|
61
|
+
constructor(docker) {
|
|
62
|
+
this.docker = docker;
|
|
63
|
+
}
|
|
64
|
+
async exists(name) {
|
|
65
|
+
return (await this.docker.snapshot()).networks.has(name);
|
|
66
|
+
}
|
|
67
|
+
async create(name, subnet) {
|
|
68
|
+
if (await this.exists(name))
|
|
69
|
+
return false;
|
|
70
|
+
const suffix = subnet ? ` --subnet=${subnet}` : "";
|
|
71
|
+
await this.docker.runOrThrow(`network create ${name}${suffix}`, "Network creation failed");
|
|
72
|
+
await this.docker.track((state) => state.networks.add(name));
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
async remove(name) {
|
|
76
|
+
if (!(await this.exists(name)))
|
|
77
|
+
return false;
|
|
78
|
+
await this.docker.runOrThrow(`network rm ${name}`, "Network removal failed");
|
|
79
|
+
await this.docker.track((state) => state.networks.delete(name));
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
// Disconnect can fail because the container was never attached, which is not
|
|
83
|
+
// an error. Connect failing is, the address is what nginx resolves to
|
|
84
|
+
async reconnect(network, container, ip) {
|
|
85
|
+
if (!(await this.docker.container.exists(container)))
|
|
86
|
+
return false;
|
|
87
|
+
await this.docker.run(`network disconnect ${network} ${container}`);
|
|
88
|
+
await this.docker.runOrThrow(`network connect --ip ${ip} ${network} ${container}`, "Network connect failed");
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
class DockerImage {
|
|
93
|
+
docker;
|
|
94
|
+
constructor(docker) {
|
|
95
|
+
this.docker = docker;
|
|
96
|
+
}
|
|
97
|
+
async exists(name) {
|
|
98
|
+
return (await this.docker.snapshot()).images.has(name);
|
|
99
|
+
}
|
|
100
|
+
// Every version of one image the host is holding. A deploy tags what it built
|
|
101
|
+
// by release, and without this the previous ones are never reclaimed.
|
|
102
|
+
// :latest is not one of them: it is the moving name a container is created
|
|
103
|
+
// from, and reclaiming it leaves the host unable to start the app
|
|
104
|
+
async versionsOf(repository) {
|
|
105
|
+
const images = (await this.docker.snapshot()).images;
|
|
106
|
+
return [...images].filter((name) => name.startsWith(`${repository}:`) && name !== `${repository}:latest`);
|
|
107
|
+
}
|
|
108
|
+
// BuildKit is already inside the daemon that will run the container, so the
|
|
109
|
+
// image it produces never has to be serialised, transferred or loaded
|
|
110
|
+
async build(spec, onLine) {
|
|
111
|
+
const command = [
|
|
112
|
+
"build",
|
|
113
|
+
// Provenance attestations make the result a manifest list, which is a
|
|
114
|
+
// different thing to tag and nothing here consumes them
|
|
115
|
+
"--progress plain --provenance=false --pull",
|
|
116
|
+
`-f ${spec.dockerfile}`,
|
|
117
|
+
...spec.tags.map((tag) => `-t ${tag}`),
|
|
118
|
+
...Object.entries(spec.secrets ?? {}).map(([id, path]) => `--secret id=${id},src=${path}`),
|
|
119
|
+
...(spec.target ? [`--target ${spec.target}`] : []),
|
|
120
|
+
spec.context,
|
|
121
|
+
].join(" ");
|
|
122
|
+
await this.docker.runOrThrow(command, "Image build failed", onLine);
|
|
123
|
+
await this.docker.track((state) => {
|
|
124
|
+
for (const tag of spec.tags)
|
|
125
|
+
state.images.add(tag);
|
|
126
|
+
});
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
// A second name for an image that is already local. Costs nothing, and it is
|
|
130
|
+
// what lets the next deploy recognise a commit it already holds
|
|
131
|
+
async retag(from, to) {
|
|
132
|
+
await this.docker.runOrThrow(`tag ${from} ${to}`, "Image tagging failed");
|
|
133
|
+
await this.docker.track((state) => state.images.add(to));
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
async remove(name) {
|
|
137
|
+
if (!(await this.exists(name)))
|
|
138
|
+
return false;
|
|
139
|
+
await this.docker.runOrThrow(`image remove -f ${name}`, "Image removal failed");
|
|
140
|
+
await this.docker.track((state) => state.images.delete(name));
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
class DockerContainer {
|
|
145
|
+
docker;
|
|
146
|
+
constructor(docker) {
|
|
147
|
+
this.docker = docker;
|
|
148
|
+
}
|
|
149
|
+
async exists(name) {
|
|
150
|
+
return (await this.docker.snapshot()).containers.has(name);
|
|
151
|
+
}
|
|
152
|
+
async status(name) {
|
|
153
|
+
return (await this.docker.snapshot()).containers.get(name)?.state ?? "none";
|
|
154
|
+
}
|
|
155
|
+
// What the running container was created from. Absent for one redkite did not
|
|
156
|
+
// create, or created before it started recording it
|
|
157
|
+
async specOf(name) {
|
|
158
|
+
return (await this.docker.snapshot()).containers.get(name)?.spec;
|
|
159
|
+
}
|
|
160
|
+
async isRunning(name) {
|
|
161
|
+
return RUNNING.has(await this.status(name));
|
|
162
|
+
}
|
|
163
|
+
async start(name) {
|
|
164
|
+
if (!(await this.exists(name))) {
|
|
165
|
+
throw new Error(`Cannot start ${name}, it does not exist`);
|
|
166
|
+
}
|
|
167
|
+
await this.docker.runOrThrow(`container start ${name}`, "Container start failed");
|
|
168
|
+
await this.docker.track((state) => set(state, name, "running"));
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
async stop(name) {
|
|
172
|
+
if (!(await this.exists(name)))
|
|
173
|
+
return false;
|
|
174
|
+
await this.docker.runOrThrow(`container stop ${name}`, "Container stop failed");
|
|
175
|
+
await this.docker.track((state) => set(state, name, "exited"));
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
// Refuses rather than clobbers, a rename onto an existing name would lose
|
|
179
|
+
// whichever container is already there
|
|
180
|
+
async rename(from, to) {
|
|
181
|
+
if (!(await this.exists(from)))
|
|
182
|
+
return false;
|
|
183
|
+
if (await this.exists(to))
|
|
184
|
+
return false;
|
|
185
|
+
await this.docker.runOrThrow(`container rename ${from} ${to}`, "Container rename failed");
|
|
186
|
+
await this.docker.track((state) => {
|
|
187
|
+
const held = state.containers.get(from) ?? { state: "unknown" };
|
|
188
|
+
state.containers.delete(from);
|
|
189
|
+
state.containers.set(to, held);
|
|
190
|
+
});
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
async remove(name) {
|
|
194
|
+
if (!(await this.exists(name)))
|
|
195
|
+
return false;
|
|
196
|
+
if (await this.isRunning(name)) {
|
|
197
|
+
throw new Error(`Cannot remove ${name} while it is running`);
|
|
198
|
+
}
|
|
199
|
+
await this.docker.runOrThrow(`container rm ${name}`, "Container removal failed");
|
|
200
|
+
await this.docker.track((state) => state.containers.delete(name));
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
async create(builder) {
|
|
204
|
+
await this.docker.runOrThrow(builder.parse(), "Container create failed");
|
|
205
|
+
await this.docker.track((state) => state.containers.set(builder.named(), {
|
|
206
|
+
state: "created",
|
|
207
|
+
spec: builder.labelled(SPEC_LABEL),
|
|
208
|
+
}));
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
builder() {
|
|
212
|
+
return new DockerBuilder(this);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// A state change keeps whatever the container was created from: the label is
|
|
216
|
+
// on the container, and only a recreate can move it
|
|
217
|
+
function set(state, name, next) {
|
|
218
|
+
state.containers.set(name, { ...state.containers.get(name), state: next });
|
|
219
|
+
}
|
|
220
|
+
function lines(block) {
|
|
221
|
+
return block
|
|
222
|
+
.split("\n")
|
|
223
|
+
.map((line) => line.trim())
|
|
224
|
+
.filter(Boolean);
|
|
225
|
+
}
|
|
226
|
+
export class DockerBuilder {
|
|
227
|
+
container;
|
|
228
|
+
_name = "";
|
|
229
|
+
_image = "";
|
|
230
|
+
_hostname;
|
|
231
|
+
_ip;
|
|
232
|
+
_restart;
|
|
233
|
+
_networks = [];
|
|
234
|
+
_volumes = [];
|
|
235
|
+
_hosts = [];
|
|
236
|
+
_ports = [];
|
|
237
|
+
_env = [];
|
|
238
|
+
_labels = new Map();
|
|
239
|
+
_envFile;
|
|
240
|
+
constructor(container) {
|
|
241
|
+
this.container = container;
|
|
242
|
+
}
|
|
243
|
+
name(value) {
|
|
244
|
+
this._name = value;
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
image(value) {
|
|
248
|
+
this._image = value;
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
hostname(value) {
|
|
252
|
+
this._hostname = value;
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
network(value) {
|
|
256
|
+
this._networks.push(value);
|
|
257
|
+
return this;
|
|
258
|
+
}
|
|
259
|
+
volume(from, to) {
|
|
260
|
+
this._volumes.push(`${from}:${to}`);
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
extraHost(name, ip) {
|
|
264
|
+
this._hosts.push(`${name}:${ip}`);
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
restart(value) {
|
|
268
|
+
this._restart = value;
|
|
269
|
+
return this;
|
|
270
|
+
}
|
|
271
|
+
ip(value) {
|
|
272
|
+
this._ip = value;
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
env(name, value) {
|
|
276
|
+
this._env.push(`${name}=${value}`);
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
// A path on the host rather than the values themselves, so a password is
|
|
280
|
+
// never an argument in the process list or in the shell history
|
|
281
|
+
envFile(path) {
|
|
282
|
+
this._envFile = path;
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
label(name, value) {
|
|
286
|
+
this._labels.set(name, value);
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
labelled(name) {
|
|
290
|
+
return this._labels.get(name);
|
|
291
|
+
}
|
|
292
|
+
port(from, to) {
|
|
293
|
+
this._ports.push(`${from}:${to}`);
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
named() {
|
|
297
|
+
return this._name;
|
|
298
|
+
}
|
|
299
|
+
parse() {
|
|
300
|
+
if (!this._name)
|
|
301
|
+
throw new Error("A container needs a name");
|
|
302
|
+
if (!this._image)
|
|
303
|
+
throw new Error(`${this._name} has no image`);
|
|
304
|
+
// Defaults to the container name. The original emitted "--hostname
|
|
305
|
+
// undefined", because nothing ever called the setter
|
|
306
|
+
const hostname = this._hostname ?? this._name;
|
|
307
|
+
return [
|
|
308
|
+
`container create --name ${this._name} --hostname ${hostname}`,
|
|
309
|
+
...this._volumes.map((volume) => `-v ${volume}`),
|
|
310
|
+
...this._networks.map((network) => `--network ${network}`),
|
|
311
|
+
...this._ports.map((port) => `-p ${port}`),
|
|
312
|
+
...this._hosts.map((host) => `--add-host ${host}`),
|
|
313
|
+
...[...this._labels].map(([name, value]) => `--label ${name}=${value}`),
|
|
314
|
+
...(this._envFile ? [`--env-file ${this._envFile}`] : []),
|
|
315
|
+
...this._env.map((entry) => `-e ${entry}`),
|
|
316
|
+
...(this._ip ? [`--ip ${this._ip}`] : []),
|
|
317
|
+
...(this._restart ? [`--restart ${this._restart}`] : []),
|
|
318
|
+
this._image,
|
|
319
|
+
].join(" ");
|
|
320
|
+
}
|
|
321
|
+
async create() {
|
|
322
|
+
return await this.container.create(this);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../src/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BuildSpec } from "./types.js";
|
|
2
|
+
export type DockerfileContext = {
|
|
3
|
+
caches: Record<string, string>;
|
|
4
|
+
port: number;
|
|
5
|
+
release: string;
|
|
6
|
+
environment: string;
|
|
7
|
+
envSecret: string;
|
|
8
|
+
fileSecrets: Record<string, string>;
|
|
9
|
+
dir?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const BUILDER_STAGE = "builder";
|
|
12
|
+
export declare function renderDockerignore(include?: string[]): string;
|
|
13
|
+
export declare function renderDockerfile(spec: BuildSpec, context: DockerfileContext): string;
|
|
14
|
+
//# sourceMappingURL=dockerfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerfile.d.ts","sourceRoot":"","sources":["../src/dockerfile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAOvD,MAAM,MAAM,iBAAiB,GAAG;IAG9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IAKpB,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAIpC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAIF,eAAO,MAAM,aAAa,YAAY,CAAC;AAMvC,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,UAMpD;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAQR"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { appRoot, destinationFor, mountFor, rootedAt } from "./layout.js";
|
|
2
|
+
// Named here because the build stops at this stage to keep an image a
|
|
3
|
+
// step before the swap can run in, and two spellings of it would drift
|
|
4
|
+
export const BUILDER_STAGE = "builder";
|
|
5
|
+
// What BuildKit reads beside the Dockerfile. Without an include the checkout
|
|
6
|
+
// keeps its own rules and only .git is held back. With one, everything is held
|
|
7
|
+
// back and the named paths are let through again, so what the build sees is
|
|
8
|
+
// what the release was taken over
|
|
9
|
+
export function renderDockerignore(include) {
|
|
10
|
+
if (!include)
|
|
11
|
+
return ".git\n**/.git\n";
|
|
12
|
+
// .git after the exemptions, because the last rule to match is the one that
|
|
13
|
+
// decides and an included directory would otherwise carry it back in
|
|
14
|
+
return ["*", ...include.map((path) => `!${path}`), ".git", "**/.git", ""].join("\n");
|
|
15
|
+
}
|
|
16
|
+
export function renderDockerfile(spec, context) {
|
|
17
|
+
return [
|
|
18
|
+
"# syntax=docker/dockerfile:1.7",
|
|
19
|
+
...builderStage(spec, context),
|
|
20
|
+
"",
|
|
21
|
+
...runtimeStage(spec, context),
|
|
22
|
+
"",
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
function builderStage(spec, context) {
|
|
26
|
+
const mounts = cacheMounts(spec, context);
|
|
27
|
+
const workdir = appRoot(context.dir);
|
|
28
|
+
// The repository root, not the app's own directory: a workspace resolves one
|
|
29
|
+
// lockfile for every package in it, so the install below has to see them all
|
|
30
|
+
const lines = [
|
|
31
|
+
`FROM ${spec.builderImage} AS ${BUILDER_STAGE}`,
|
|
32
|
+
"WORKDIR /app",
|
|
33
|
+
];
|
|
34
|
+
if (spec.aptPackages.length > 0) {
|
|
35
|
+
lines.push(`RUN apk add --no-cache ${spec.aptPackages.join(" ")}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push(`ENV NODE_ENV=${context.environment}`);
|
|
38
|
+
lines.push("ENV NEXT_TELEMETRY_DISABLED=1");
|
|
39
|
+
if (spec.dependencies)
|
|
40
|
+
lines.push(...dependencyLayer(spec, mounts));
|
|
41
|
+
// Submodules are already in the context: the checkout on the host resolved
|
|
42
|
+
// them, so nothing in the build needs an agent or a .git directory
|
|
43
|
+
lines.push(copy(".", "/app"));
|
|
44
|
+
// Only now, so the steps below read the app's own manifest and write beside it
|
|
45
|
+
if (context.dir)
|
|
46
|
+
lines.push(`WORKDIR ${workdir}`);
|
|
47
|
+
lines.push(`RUN --mount=type=secret,id=${context.envSecret} cp /run/secrets/${context.envSecret} ${workdir}/.env`, `ENV SENTRY_RELEASE=${context.release}`);
|
|
48
|
+
// Unquoted: the shell form hands everything after RUN to sh -c, so a step is
|
|
49
|
+
// written exactly as it would be typed. Quoting it made the whole command one
|
|
50
|
+
// word, and the shell went looking for a program by that name
|
|
51
|
+
for (const step of spec.steps) {
|
|
52
|
+
lines.push(`RUN ${mounts}${step}`);
|
|
53
|
+
}
|
|
54
|
+
if (spec.sourcemaps) {
|
|
55
|
+
// The built .env ships inside the image, and an upload token is build-time
|
|
56
|
+
const token = spec.sourcemaps.stripFromImage;
|
|
57
|
+
lines.push(`RUN sed -i '/^${token}=/d' ${rootedAt(spec.output, context.dir)}/.env`);
|
|
58
|
+
}
|
|
59
|
+
return lines;
|
|
60
|
+
}
|
|
61
|
+
function runtimeStage(spec, context) {
|
|
62
|
+
const output = rootedAt(spec.output, context.dir);
|
|
63
|
+
// Where the app lands inside the tree that was just copied to /app. An output
|
|
64
|
+
// keeping the repository's structure puts it back under dir, and everything
|
|
65
|
+
// else flattens it to the top
|
|
66
|
+
const nested = spec.keepsLayout ? context.dir : undefined;
|
|
67
|
+
const lines = [`FROM ${spec.runtimeImage}`, `WORKDIR ${appRoot(nested)}`];
|
|
68
|
+
if (spec.runtimePackages.length > 0) {
|
|
69
|
+
lines.push(`RUN apk add --no-cache ${spec.runtimePackages.join(" ")}`);
|
|
70
|
+
}
|
|
71
|
+
// Before the output, which changes every commit: what these install is a
|
|
72
|
+
// property of the image, not of the release being deployed
|
|
73
|
+
for (const step of spec.runtimeSteps) {
|
|
74
|
+
lines.push(`RUN ${step}`);
|
|
75
|
+
}
|
|
76
|
+
// The one copy nothing can be allowed to skip: an image without it starts,
|
|
77
|
+
// answers, and serves a 404 for everything the app was supposed to be
|
|
78
|
+
lines.push(copy(output, "/app", BUILDER_STAGE));
|
|
79
|
+
// Directories the output does not contain but the runtime needs, such as a
|
|
80
|
+
// generated client or the static assets a standalone build leaves behind.
|
|
81
|
+
// The two sides are rooted separately: dir is where the app was built, and
|
|
82
|
+
// nested is where the output put it
|
|
83
|
+
for (const entry of spec.carry) {
|
|
84
|
+
const { path, optional } = carried(entry);
|
|
85
|
+
const from = rootedAt(path, context.dir);
|
|
86
|
+
const to = rootedAt(destinationFor(path, spec.output), nested);
|
|
87
|
+
lines.push(optional ? copyOrSkip(from, to, BUILDER_STAGE) : copy(from, to, BUILDER_STAGE));
|
|
88
|
+
}
|
|
89
|
+
for (const [path, secret] of Object.entries(context.fileSecrets)) {
|
|
90
|
+
lines.push(`RUN --mount=type=secret,id=${secret} cp /run/secrets/${secret} ${path}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push(`EXPOSE ${context.port}`, `CMD ${JSON.stringify(spec.entrypoint)}`);
|
|
93
|
+
return lines;
|
|
94
|
+
}
|
|
95
|
+
function carried(entry) {
|
|
96
|
+
if (typeof entry === "string")
|
|
97
|
+
return { path: entry, optional: false };
|
|
98
|
+
return { path: entry.path, optional: entry.optional };
|
|
99
|
+
}
|
|
100
|
+
// A COPY whose source does not exist fails the build, which is what a source
|
|
101
|
+
// the build was supposed to produce should do
|
|
102
|
+
function copy(from, to, stage) {
|
|
103
|
+
return `COPY ${stage ? `--from=${stage} ` : ""}${from} ${to}`;
|
|
104
|
+
}
|
|
105
|
+
// Bracketing the last character makes the source a pattern, and a pattern
|
|
106
|
+
// matching nothing is skipped: the only way a Dockerfile has to say "if it
|
|
107
|
+
// exists". For a source whose absence is a fact about the repository
|
|
108
|
+
function copyOrSkip(from, to, stage) {
|
|
109
|
+
const path = from.replace(/\/+$/, "");
|
|
110
|
+
const last = path.slice(-1);
|
|
111
|
+
// An empty pattern matches nothing and would be skipped in silence, which is
|
|
112
|
+
// the one outcome this whole distinction exists to prevent
|
|
113
|
+
if (!last)
|
|
114
|
+
return copy(from, to, stage);
|
|
115
|
+
return copy(`${path.slice(0, -1)}[${last}]`, to, stage);
|
|
116
|
+
}
|
|
117
|
+
// One mount per cache, repeated on every step, because a Dockerfile scopes a
|
|
118
|
+
// cache to the RUN that asks for it rather than to the stage
|
|
119
|
+
function cacheMounts(spec, context) {
|
|
120
|
+
// Only the caches the topology derived an id for. It drops any whose target
|
|
121
|
+
// another already covers, and two mounts on one target is a build that does
|
|
122
|
+
// not start
|
|
123
|
+
return spec.caches
|
|
124
|
+
.filter((name) => name in context.caches)
|
|
125
|
+
.map((name) => {
|
|
126
|
+
const id = context.caches[name] ?? name;
|
|
127
|
+
return `--mount=type=cache,id=${id},target=${mountFor(name, context.dir)} `;
|
|
128
|
+
})
|
|
129
|
+
.join("");
|
|
130
|
+
}
|
|
131
|
+
function dependencyLayer(spec, mounts) {
|
|
132
|
+
const { files, step, stripScripts = [] } = spec.dependencies ?? {
|
|
133
|
+
files: [],
|
|
134
|
+
step: "",
|
|
135
|
+
};
|
|
136
|
+
// A lockfile the repository does not have is not an error here. The install
|
|
137
|
+
// below is what decides that, and it says so in the package manager's words
|
|
138
|
+
const lines = files.map((file) => copyOrSkip(file, `/app/${file}`));
|
|
139
|
+
// This layer is the manifest and the lockfile, nothing else. A root prepare
|
|
140
|
+
// that installs git hooks or calls into scripts/ has neither
|
|
141
|
+
if (stripScripts.length > 0) {
|
|
142
|
+
lines.push(`RUN node --input-type=commonjs -e ${quote(strip(stripScripts))}`);
|
|
143
|
+
}
|
|
144
|
+
lines.push(`RUN ${mounts}${step}`);
|
|
145
|
+
return lines;
|
|
146
|
+
}
|
|
147
|
+
function strip(names) {
|
|
148
|
+
return [
|
|
149
|
+
'const fs = require("fs"), path = "/app/package.json";',
|
|
150
|
+
'const manifest = JSON.parse(fs.readFileSync(path, "utf8"));',
|
|
151
|
+
`for (const name of ${JSON.stringify(names)}) delete manifest.scripts?.[name];`,
|
|
152
|
+
"fs.writeFileSync(path, JSON.stringify(manifest, null, 2));",
|
|
153
|
+
].join("");
|
|
154
|
+
}
|
|
155
|
+
// Only for a command passed as one argument to another. Single quotes are what
|
|
156
|
+
// the shell strips, so a script containing double quotes survives
|
|
157
|
+
function quote(command) {
|
|
158
|
+
return `'${command.replaceAll("'", `'\\''`)}'`;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=dockerfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerfile.js","sourceRoot":"","sources":["../src/dockerfile.ts"],"names":[],"mappings":"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"}
|
package/dist/health.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Log } from "./log.js";
|
|
2
|
+
import type { HealthSpec } from "./types.js";
|
|
3
|
+
export type Probe = (container: string, url: string) => Promise<{
|
|
4
|
+
code: number;
|
|
5
|
+
output: string;
|
|
6
|
+
}>;
|
|
7
|
+
export type HealthDeps = {
|
|
8
|
+
probe: Probe;
|
|
9
|
+
sleep: (ms: number) => Promise<void>;
|
|
10
|
+
log?: Log;
|
|
11
|
+
};
|
|
12
|
+
export declare function healthcheck(container: string, port: number, spec: HealthSpec, deps: HealthDeps): Promise<boolean>;
|
|
13
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAS7C,MAAM,MAAM,KAAK,GAAG,CAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,KACR,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,GAAG,CAAC,EAAE,GAAG,CAAC;CACX,CAAC;AAKF,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,OAAO,CAAC,CA6BlB"}
|
package/dist/health.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { silent } from "./log.js";
|
|
2
|
+
const RETRIES = 10;
|
|
3
|
+
// Ceiling for the backoff, not a fixed wait
|
|
4
|
+
const INTERVAL_MS = 5000;
|
|
5
|
+
// The first probe is free, a container that is already up answers it
|
|
6
|
+
const DELAY_MS = 0;
|
|
7
|
+
const FIRST_BACKOFF_MS = 250;
|
|
8
|
+
// One loop for every app. The two copies it replaces had already drifted: the
|
|
9
|
+
// frontend never incremented its counter on a failed predicate, so a container
|
|
10
|
+
// that answered but never became healthy span until the deploy was killed.
|
|
11
|
+
export async function healthcheck(container, port, spec, deps) {
|
|
12
|
+
const retries = spec.retries ?? RETRIES;
|
|
13
|
+
const ceiling = spec.intervalMs ?? INTERVAL_MS;
|
|
14
|
+
const log = deps.log ?? silent;
|
|
15
|
+
const delay = spec.delayMs ?? DELAY_MS;
|
|
16
|
+
if (delay > 0)
|
|
17
|
+
await deps.sleep(delay);
|
|
18
|
+
let backoff = FIRST_BACKOFF_MS;
|
|
19
|
+
for (let attempt = 1; attempt <= retries; attempt += 1) {
|
|
20
|
+
const url = `localhost:${port}${spec.path}`;
|
|
21
|
+
const { code, output } = await deps.probe(container, url);
|
|
22
|
+
if (code === 0 && passes(spec, output, log, container)) {
|
|
23
|
+
log.done(`${container} is healthy`);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (attempt === retries)
|
|
27
|
+
break;
|
|
28
|
+
// Doubling from a quarter second means a container that starts quickly
|
|
29
|
+
// costs almost nothing, and one that does not still gets the same budget
|
|
30
|
+
await deps.sleep(backoff);
|
|
31
|
+
backoff = Math.min(backoff * 2, ceiling);
|
|
32
|
+
}
|
|
33
|
+
log.fail(`${container} failed its health check after ${retries} attempts`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// A body that parses but fails the predicate is a retry, not a verdict. The
|
|
37
|
+
// container may still be warming up, and the caller has a retry budget for it
|
|
38
|
+
function passes(spec, output, log, container) {
|
|
39
|
+
let body;
|
|
40
|
+
try {
|
|
41
|
+
body = JSON.parse(output);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
log.warn(`${container} returned a body that is not JSON: ${output}`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (typeof body !== "object" || body === null)
|
|
48
|
+
return false;
|
|
49
|
+
if (spec.expect(body))
|
|
50
|
+
return true;
|
|
51
|
+
log.warn(`${container} is not healthy yet: ${output}`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,UAAU,CAAC;AAG5C,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,4CAA4C;AAC5C,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,qEAAqE;AACrE,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAa7B,8EAA8E;AAC9E,+EAA+E;AAC/E,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,IAAY,EACZ,IAAgB,EAChB,IAAgB;IAEhB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,WAAW,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC;IAEvC,IAAI,KAAK,GAAG,CAAC;QAAE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,OAAO,GAAG,gBAAgB,CAAC;IAE/B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,GAAG,GAAG,aAAa,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAE1D,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,aAAa,CAAC,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,OAAO,KAAK,OAAO;YAAE,MAAM;QAE/B,uEAAuE;QACvE,yEAAyE;QACzE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,kCAAkC,OAAO,WAAW,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,SAAS,MAAM,CACb,IAAgB,EAChB,MAAc,EACd,GAAQ,EACR,SAAiB;IAEjB,IAAI,IAAa,CAAC;IAElB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,sCAAsC,MAAM,EAAE,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,IAAI,IAAI,CAAC,MAAM,CAAC,IAA+B,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9D,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type Result = {
|
|
2
|
+
code: number;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
};
|
|
6
|
+
export type OnLine = (line: string) => void;
|
|
7
|
+
export type Host = {
|
|
8
|
+
sh(command: string, onLine?: OnLine): Promise<Result>;
|
|
9
|
+
write(name: string, contents: string): Promise<string>;
|
|
10
|
+
readonly directory: string;
|
|
11
|
+
readonly cache: string;
|
|
12
|
+
pipe(local: string, remote: string, onLine?: OnLine): Promise<Result>;
|
|
13
|
+
stop(signal: "TERM" | "KILL"): Promise<number>;
|
|
14
|
+
close?(): Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export declare function tail(lines: string[]): string;
|
|
17
|
+
export declare function lineReader(onLine: OnLine): {
|
|
18
|
+
push(chunk: string): void;
|
|
19
|
+
flush(): void;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAItE,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAE5C,MAAM,MAAM,IAAI,GAAG;IAGjB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAGtD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAGvB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAGtE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAMF,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,UAEnC;AAGD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM;gBAIzB,MAAM;;EAUrB"}
|