@alchemy.run/docker 2.0.0-beta.84 → 2.0.0-beta.86
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 +16 -0
- package/lib/Bootstrap.d.ts.map +1 -1
- package/lib/Bootstrap.js +2 -1
- package/lib/Bootstrap.js.map +1 -1
- package/lib/BuildCache.d.ts +87 -0
- package/lib/BuildCache.d.ts.map +1 -0
- package/lib/BuildCache.js +39 -0
- package/lib/BuildCache.js.map +1 -0
- package/lib/BuildHash.d.ts +20 -3
- package/lib/BuildHash.d.ts.map +1 -1
- package/lib/BuildHash.js +60 -21
- package/lib/BuildHash.js.map +1 -1
- package/lib/BuildPlan.d.ts +202 -0
- package/lib/BuildPlan.d.ts.map +1 -0
- package/lib/BuildPlan.js +514 -0
- package/lib/BuildPlan.js.map +1 -0
- package/lib/Container.d.ts +122 -1
- package/lib/Container.d.ts.map +1 -1
- package/lib/Container.js +232 -4
- package/lib/Container.js.map +1 -1
- package/lib/Context.d.ts +20 -1
- package/lib/Context.d.ts.map +1 -1
- package/lib/Context.js +15 -0
- package/lib/Context.js.map +1 -1
- package/lib/Docker.d.ts +113 -1
- package/lib/Docker.d.ts.map +1 -1
- package/lib/Docker.js +50 -8
- package/lib/Docker.js.map +1 -1
- package/lib/Dockerfile.d.ts +0 -2
- package/lib/Dockerfile.d.ts.map +1 -1
- package/lib/Dockerfile.js.map +1 -1
- package/lib/DockerfileParser.d.ts +125 -0
- package/lib/DockerfileParser.d.ts.map +1 -0
- package/lib/DockerfileParser.js +1040 -0
- package/lib/DockerfileParser.js.map +1 -0
- package/lib/HealthcheckCommand.d.ts.map +1 -1
- package/lib/HealthcheckCommand.js.map +1 -1
- package/lib/Image.d.ts +69 -13
- package/lib/Image.d.ts.map +1 -1
- package/lib/Image.js +59 -9
- package/lib/Image.js.map +1 -1
- package/lib/ImageArchive.d.ts +41 -0
- package/lib/ImageArchive.d.ts.map +1 -0
- package/lib/ImageArchive.js +54 -0
- package/lib/ImageArchive.js.map +1 -0
- package/lib/ImageSource.d.ts +151 -8
- package/lib/ImageSource.d.ts.map +1 -1
- package/lib/ImageSource.js +247 -72
- package/lib/ImageSource.js.map +1 -1
- package/lib/Network.d.ts +175 -3
- package/lib/Network.d.ts.map +1 -1
- package/lib/Network.js +296 -8
- package/lib/Network.js.map +1 -1
- package/lib/Providers.d.ts.map +1 -1
- package/lib/Providers.js.map +1 -1
- package/lib/Registry.d.ts.map +1 -1
- package/lib/Registry.js.map +1 -1
- package/lib/RegistryError.d.ts.map +1 -1
- package/lib/RegistryError.js.map +1 -1
- package/lib/RemoteImage.d.ts.map +1 -1
- package/lib/RemoteImage.js +1 -1
- package/lib/RemoteImage.js.map +1 -1
- package/lib/Service.d.ts +24 -3
- package/lib/Service.d.ts.map +1 -1
- package/lib/Service.js +154 -12
- package/lib/Service.js.map +1 -1
- package/lib/ServiceImage.d.ts.map +1 -1
- package/lib/ServiceImage.js.map +1 -1
- package/lib/ShellWord.d.ts +28 -0
- package/lib/ShellWord.d.ts.map +1 -0
- package/lib/ShellWord.js +301 -0
- package/lib/ShellWord.js.map +1 -0
- package/lib/Swarm.d.ts.map +1 -1
- package/lib/Swarm.js.map +1 -1
- package/lib/Volume.d.ts +24 -1
- package/lib/Volume.d.ts.map +1 -1
- package/lib/Volume.js +16 -0
- package/lib/Volume.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/package.json +9 -9
- package/src/Bootstrap.ts +2 -0
- package/src/BuildCache.ts +111 -0
- package/src/BuildHash.ts +85 -26
- package/src/BuildPlan.ts +758 -0
- package/src/Container.ts +361 -5
- package/src/Context.ts +27 -1
- package/src/Docker.ts +157 -9
- package/src/Dockerfile.ts +0 -2
- package/src/DockerfileParser.ts +1152 -0
- package/src/Image.ts +75 -15
- package/src/ImageArchive.ts +77 -0
- package/src/ImageSource.ts +391 -99
- package/src/Network.ts +432 -10
- package/src/RemoteImage.ts +1 -1
- package/src/Service.ts +246 -62
- package/src/ShellWord.ts +326 -0
- package/src/Volume.ts +33 -1
package/src/Image.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { Resource } from "alchemy/Resource";
|
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as FileSystem from "effect/FileSystem";
|
|
7
7
|
import * as Path from "effect/Path";
|
|
8
|
+
import type * as Redacted from "effect/Redacted";
|
|
9
|
+
import { type BuildCacheOptions, buildCacheRef, resolveBuildCache } from "./BuildCache.ts";
|
|
8
10
|
import { Docker, dockerContextName, dockerPhysicalName } from "./Docker.ts";
|
|
9
11
|
import type { Providers } from "./Providers.ts";
|
|
10
12
|
import {
|
|
@@ -15,7 +17,7 @@ import {
|
|
|
15
17
|
withRegistryHost,
|
|
16
18
|
} from "./Registry.ts";
|
|
17
19
|
|
|
18
|
-
export interface DockerBuildOptions {
|
|
20
|
+
export interface DockerBuildOptions extends BuildCacheOptions {
|
|
19
21
|
/**
|
|
20
22
|
* Build context directory.
|
|
21
23
|
*
|
|
@@ -30,14 +32,20 @@ export interface DockerBuildOptions {
|
|
|
30
32
|
dockerfile?: string;
|
|
31
33
|
/** Target platform, e.g. `"linux/amd64"`. */
|
|
32
34
|
platform?: string;
|
|
33
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Docker build arguments (`--build-arg`). Never pass secrets here: build
|
|
37
|
+
* args are visible in the image history. Use {@link secrets} instead.
|
|
38
|
+
*/
|
|
34
39
|
args?: Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* BuildKit secrets, keyed by id. Mount one in a `RUN` step with
|
|
42
|
+
* `--mount=type=secret,id=<id>` and read it from `/run/secrets/<id>`, or
|
|
43
|
+
* add `env=<NAME>` to expose it as an environment variable for that step.
|
|
44
|
+
* A secret never enters the image, its build history, or `docker`'s argv.
|
|
45
|
+
*/
|
|
46
|
+
secrets?: Record<string, Redacted.Redacted<string>>;
|
|
35
47
|
/** Multi-stage build target. */
|
|
36
48
|
target?: string;
|
|
37
|
-
/** Cache sources passed as `--cache-from`. */
|
|
38
|
-
cacheFrom?: string[];
|
|
39
|
-
/** Cache destinations passed as `--cache-to`. */
|
|
40
|
-
cacheTo?: string[];
|
|
41
49
|
/** Additional Docker build options. */
|
|
42
50
|
options?: string[];
|
|
43
51
|
}
|
|
@@ -117,6 +125,23 @@ export interface Image extends Resource<
|
|
|
117
125
|
* });
|
|
118
126
|
* ```
|
|
119
127
|
*
|
|
128
|
+
* ### Build Secrets
|
|
129
|
+
* **Example:** Install from a private npm registry
|
|
130
|
+
* ```typescript
|
|
131
|
+
* // Dockerfile:
|
|
132
|
+
* // RUN --mount=type=secret,id=NPM_TOKEN,env=NPM_TOKEN npm ci
|
|
133
|
+
* const image = yield* Docker.Image("app", {
|
|
134
|
+
* name: "my-app",
|
|
135
|
+
* build: {
|
|
136
|
+
* context: "./app",
|
|
137
|
+
* secrets: { NPM_TOKEN: Config.Redacted("NPM_TOKEN") },
|
|
138
|
+
* },
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* The secret is mounted only into the `RUN` step that asks for it; it never
|
|
143
|
+
* lands in an image layer or the build history.
|
|
144
|
+
*
|
|
120
145
|
* ### Registry Push
|
|
121
146
|
* **Example:** Push with Redacted credentials
|
|
122
147
|
* ```typescript
|
|
@@ -131,18 +156,35 @@ export interface Image extends Resource<
|
|
|
131
156
|
* });
|
|
132
157
|
* ```
|
|
133
158
|
*
|
|
134
|
-
* ### Private Base Images
|
|
135
|
-
* **Example:** Build FROM a private base image
|
|
159
|
+
* ### Private Base Images
|
|
160
|
+
* **Example:** Build FROM a private base image
|
|
136
161
|
* ```typescript
|
|
137
162
|
* // Dockerfile: FROM registry.example.com/base:v1
|
|
138
163
|
* const image = yield* Docker.Image("app", {
|
|
139
164
|
* name: "registry.example.com/app",
|
|
140
|
-
* build: {
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
165
|
+
* build: { context: "./app" },
|
|
166
|
+
* // Authenticates the base-image pull, then the push.
|
|
167
|
+
* registry: {
|
|
168
|
+
* server: "registry.example.com",
|
|
169
|
+
* username: "deploy",
|
|
170
|
+
* password: Config.Redacted("REGISTRY_PASSWORD"),
|
|
144
171
|
* },
|
|
145
|
-
*
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* ### Caching Build Layers
|
|
176
|
+
* Builds reuse the local BuildKit cache of the machine they run on, and
|
|
177
|
+
* nothing but the image is pushed. A builder that has never built the image,
|
|
178
|
+
* such as a fresh CI runner, reruns every step. Set `build.cache: true` to keep
|
|
179
|
+
* every stage's layers at the image repository's `:buildcache` tag, so fresh
|
|
180
|
+
* builders skip unchanged steps. For another backend, pass BuildKit's own
|
|
181
|
+
* `--cache-from` / `--cache-to` values as `from` and `to`.
|
|
182
|
+
*
|
|
183
|
+
* **Example:** Cache layers in the image's registry
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const image = yield* Docker.Image("app", {
|
|
186
|
+
* name: "registry.example.com/app",
|
|
187
|
+
* build: { context: "./app", cache: true },
|
|
146
188
|
* registry: {
|
|
147
189
|
* server: "registry.example.com",
|
|
148
190
|
* username: "deploy",
|
|
@@ -151,6 +193,20 @@ export interface Image extends Resource<
|
|
|
151
193
|
* });
|
|
152
194
|
* ```
|
|
153
195
|
*
|
|
196
|
+
* **Example:** Cache layers in GitHub Actions
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const image = yield* Docker.Image("app", {
|
|
199
|
+
* name: "my-app",
|
|
200
|
+
* build: {
|
|
201
|
+
* context: "./app",
|
|
202
|
+
* cache: {
|
|
203
|
+
* from: ["type=gha,scope=app"],
|
|
204
|
+
* to: ["type=gha,scope=app,mode=max"],
|
|
205
|
+
* },
|
|
206
|
+
* },
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
154
210
|
* ### Docker Context
|
|
155
211
|
* **Example:** Build in a named Docker context
|
|
156
212
|
* ```typescript
|
|
@@ -192,8 +248,12 @@ export const ImageProvider = () =>
|
|
|
192
248
|
platform: props.build.platform,
|
|
193
249
|
target: props.build.target,
|
|
194
250
|
"build-arg": props.build.args,
|
|
195
|
-
|
|
196
|
-
|
|
251
|
+
secrets: props.build.secrets,
|
|
252
|
+
// The registry cache lives next to the image.
|
|
253
|
+
...resolveBuildCache(
|
|
254
|
+
props.build.cache,
|
|
255
|
+
buildCacheRef(props.registry ? withRegistryHost(ref, props.registry) : ref),
|
|
256
|
+
),
|
|
197
257
|
args: props.build.options,
|
|
198
258
|
engineContext,
|
|
199
259
|
// Base images and registry caches may live in the same registry;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image archives: a Docker build context built for one platform and saved
|
|
3
|
+
* with `docker save`, for hosts with no registry between the deploy and the
|
|
4
|
+
* machine that runs it (a VM that loads the archive into its own Docker).
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as FileSystem from "effect/FileSystem";
|
|
8
|
+
import * as Path from "effect/Path";
|
|
9
|
+
import { hashDockerBuildInputs, resolveDockerBuildPaths } from "./BuildHash.ts";
|
|
10
|
+
import { Docker } from "./Docker.ts";
|
|
11
|
+
|
|
12
|
+
export interface ImageArchiveSource {
|
|
13
|
+
/** The Docker build context directory. */
|
|
14
|
+
context: string;
|
|
15
|
+
/**
|
|
16
|
+
* The Dockerfile, relative to the context unless absolute.
|
|
17
|
+
* @default "Dockerfile"
|
|
18
|
+
*/
|
|
19
|
+
dockerfile?: string;
|
|
20
|
+
/** The platform the archive's image is built for, e.g. `linux/amd64`. */
|
|
21
|
+
platform: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const pathsOf = Effect.fn(function* (source: ImageArchiveSource) {
|
|
25
|
+
const path = yield* Path.Path;
|
|
26
|
+
const dockerfile =
|
|
27
|
+
source.dockerfile === undefined
|
|
28
|
+
? path.join(source.context, "Dockerfile")
|
|
29
|
+
: path.isAbsolute(source.dockerfile)
|
|
30
|
+
? source.dockerfile
|
|
31
|
+
: path.join(source.context, source.dockerfile);
|
|
32
|
+
return yield* resolveDockerBuildPaths({ context: source.context, dockerfile });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A content hash of the inputs an archive is built from: the context's
|
|
37
|
+
* files (after `.dockerignore`), the Dockerfile and the platform. It
|
|
38
|
+
* changes exactly when the archive's image would.
|
|
39
|
+
*/
|
|
40
|
+
export const hashImageArchive = Effect.fn(function* (source: ImageArchiveSource) {
|
|
41
|
+
const { context, dockerfile } = yield* pathsOf(source);
|
|
42
|
+
return yield* hashDockerBuildInputs(
|
|
43
|
+
{ context, dockerfile, platform: source.platform },
|
|
44
|
+
"effective",
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build `source` and save its image to an archive at `output`, tagged
|
|
50
|
+
* `tag`, the name `docker load` reports where the archive is loaded.
|
|
51
|
+
* Resolves to the archive's bytes.
|
|
52
|
+
*/
|
|
53
|
+
export const buildImageArchive = Effect.fn(function* (
|
|
54
|
+
source: ImageArchiveSource,
|
|
55
|
+
options: { readonly tag: string; readonly output: string },
|
|
56
|
+
) {
|
|
57
|
+
const docker = yield* Docker;
|
|
58
|
+
const fs = yield* FileSystem.FileSystem;
|
|
59
|
+
const path = yield* Path.Path;
|
|
60
|
+
const { context, dockerfile } = yield* pathsOf(source);
|
|
61
|
+
yield* docker.image.build({
|
|
62
|
+
context,
|
|
63
|
+
file: dockerfile,
|
|
64
|
+
tag: options.tag,
|
|
65
|
+
platform: source.platform,
|
|
66
|
+
});
|
|
67
|
+
yield* fs.makeDirectory(path.dirname(options.output), { recursive: true });
|
|
68
|
+
yield* docker.run(["save", options.tag, "-o", options.output]);
|
|
69
|
+
return yield* fs.readFile(options.output);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Shell lines that load the archive at `archive` into Docker and set the
|
|
74
|
+
* shell variable `IMAGE` to the image it holds.
|
|
75
|
+
*/
|
|
76
|
+
export const loadImageArchiveScript = (archive: string) =>
|
|
77
|
+
`IMAGE="$(docker load -i ${archive} | sed -n 's/^Loaded image: //p' | tail -1)"`;
|