@alchemy.run/docker 2.0.0-beta.84 → 2.0.0-beta.85
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/lib/BuildHash.d.ts +12 -3
- package/lib/BuildHash.d.ts.map +1 -1
- package/lib/BuildHash.js +9 -2
- package/lib/BuildHash.js.map +1 -1
- package/lib/Container.d.ts +98 -0
- package/lib/Container.d.ts.map +1 -1
- package/lib/Container.js +218 -4
- package/lib/Container.js.map +1 -1
- package/lib/Docker.d.ts +80 -1
- package/lib/Docker.d.ts.map +1 -1
- package/lib/Docker.js +27 -6
- package/lib/Docker.js.map +1 -1
- package/lib/Image.d.ts +29 -1
- package/lib/Image.d.ts.map +1 -1
- package/lib/Image.js +18 -0
- package/lib/Image.js.map +1 -1
- package/lib/ImageSource.d.ts +57 -3
- package/lib/ImageSource.d.ts.map +1 -1
- package/lib/ImageSource.js +32 -3
- package/lib/ImageSource.js.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/package.json +7 -7
- package/src/BuildHash.ts +12 -2
- package/src/Container.ts +330 -4
- package/src/Docker.ts +101 -7
- package/src/Image.ts +30 -1
- package/src/ImageSource.ts +78 -5
- package/src/RemoteImage.ts +1 -1
- package/src/Service.ts +246 -62
package/src/Docker.ts
CHANGED
|
@@ -85,6 +85,42 @@ export class Docker extends Context.Service<
|
|
|
85
85
|
network?: string | undefined;
|
|
86
86
|
/** Linux capabilities to add. */
|
|
87
87
|
"cap-add"?: Array<string> | undefined;
|
|
88
|
+
/** Linux capabilities to drop. */
|
|
89
|
+
"cap-drop"?: Array<string> | undefined;
|
|
90
|
+
/** Hard memory limit in Docker's byte-suffix format. */
|
|
91
|
+
memory?: string | undefined;
|
|
92
|
+
/** Combined memory-and-swap limit in Docker's byte-suffix format. */
|
|
93
|
+
"memory-swap"?: string | undefined;
|
|
94
|
+
/** Soft memory reservation for memory contention. */
|
|
95
|
+
"memory-reservation"?: string | undefined;
|
|
96
|
+
/** CPU quota, in cores. */
|
|
97
|
+
cpus?: number | undefined;
|
|
98
|
+
/** Per-container process and thread limit. */
|
|
99
|
+
"pids-limit"?: number | undefined;
|
|
100
|
+
/** Mount the root filesystem read-only. */
|
|
101
|
+
"read-only"?: boolean | undefined;
|
|
102
|
+
/** Security options such as `no-new-privileges` and seccomp/AppArmor profiles. */
|
|
103
|
+
"security-opt"?: Array<string> | undefined;
|
|
104
|
+
/** Per-container user namespace; "host" disables daemon user remapping. */
|
|
105
|
+
userns?: string | undefined;
|
|
106
|
+
/** Supplemental Linux groups. */
|
|
107
|
+
"group-add"?: Array<string> | undefined;
|
|
108
|
+
/** Per-process soft/hard resource limits. */
|
|
109
|
+
ulimit?: Array<string> | undefined;
|
|
110
|
+
/** Docker log driver. */
|
|
111
|
+
"log-driver"?: string | undefined;
|
|
112
|
+
/** Driver-specific Docker logging options. */
|
|
113
|
+
"log-opt"?: Array<string> | undefined;
|
|
114
|
+
/** Kernel OOM score adjustment. */
|
|
115
|
+
"oom-score-adj"?: number | undefined;
|
|
116
|
+
/** Signal used on container stop. */
|
|
117
|
+
"stop-signal"?: string | undefined;
|
|
118
|
+
/** Run Docker's tiny init process. */
|
|
119
|
+
init?: boolean | undefined;
|
|
120
|
+
/** Runtime UID/GID or named user. */
|
|
121
|
+
user?: string | undefined;
|
|
122
|
+
/** Docker tmpfs mount expressions (target[:options]). */
|
|
123
|
+
tmpfs?: Array<string> | undefined;
|
|
88
124
|
/** Host devices in Docker's `host:container[:permissions]` form. */
|
|
89
125
|
device?: Array<string> | undefined;
|
|
90
126
|
command: Array<string> | undefined;
|
|
@@ -130,6 +166,12 @@ export class Docker extends Context.Service<
|
|
|
130
166
|
platform?: string;
|
|
131
167
|
target?: string;
|
|
132
168
|
"build-arg"?: Record<string, string>;
|
|
169
|
+
/**
|
|
170
|
+
* BuildKit secrets, keyed by id (`RUN --mount=type=secret,id=<id>`).
|
|
171
|
+
* Values reach `docker` through its environment, never its argv,
|
|
172
|
+
* and are not written to the image.
|
|
173
|
+
*/
|
|
174
|
+
secrets?: Record<string, string | Redacted.Redacted<string>>;
|
|
133
175
|
"cache-from"?: Array<string>;
|
|
134
176
|
"cache-to"?: Array<string>;
|
|
135
177
|
args?: Array<string>;
|
|
@@ -327,10 +369,12 @@ export class Docker extends Context.Service<
|
|
|
327
369
|
context?: string;
|
|
328
370
|
replicas?: number;
|
|
329
371
|
"endpoint-mode"?: "vip" | "dnsrr";
|
|
372
|
+
/** Networks to detach, by name or id; applied before `network-add`. */
|
|
373
|
+
"network-rm"?: string[];
|
|
374
|
+
"network-add"?: string[];
|
|
330
375
|
"constraint-add"?: string[];
|
|
331
376
|
"constraint-rm"?: string[];
|
|
332
377
|
"replicas-max-per-node"?: number;
|
|
333
|
-
"placement-pref"?: string[];
|
|
334
378
|
"update-parallelism"?: number;
|
|
335
379
|
"update-delay"?: string;
|
|
336
380
|
"update-monitor"?: string;
|
|
@@ -356,13 +400,23 @@ export class Docker extends Context.Service<
|
|
|
356
400
|
"health-start-period"?: string;
|
|
357
401
|
"stop-grace-period"?: string;
|
|
358
402
|
"mount-add"?: string[];
|
|
403
|
+
/** Mount targets to remove. */
|
|
404
|
+
"mount-rm"?: string[];
|
|
359
405
|
"secret-add"?: string[];
|
|
406
|
+
/** Secret names to remove; applied before `secret-add`. */
|
|
407
|
+
"secret-rm"?: string[];
|
|
360
408
|
"config-add"?: string[];
|
|
409
|
+
/** Config names to remove; applied before `config-add`. */
|
|
410
|
+
"config-rm"?: string[];
|
|
361
411
|
"read-only"?: boolean;
|
|
362
412
|
"publish-add"?: string[];
|
|
413
|
+
/** Published ports to remove, as `target=<port>,protocol=<p>,mode=<m>`. */
|
|
414
|
+
"publish-rm"?: string[];
|
|
363
415
|
"label-add"?: Record<string, string>;
|
|
364
416
|
"label-rm"?: string[];
|
|
365
417
|
"env-add"?: Record<string, string>;
|
|
418
|
+
/** Environment variable names to remove. */
|
|
419
|
+
"env-rm"?: string[];
|
|
366
420
|
args?: string;
|
|
367
421
|
}) => Effect.Effect<CommandOutput, PlatformError>;
|
|
368
422
|
/** Inspects a service. */
|
|
@@ -423,6 +477,8 @@ export declare namespace Docker {
|
|
|
423
477
|
Image: string;
|
|
424
478
|
Cmd: string[] | null;
|
|
425
479
|
Env: string[] | null;
|
|
480
|
+
User?: string;
|
|
481
|
+
StopSignal?: string;
|
|
426
482
|
Labels: Record<string, string> | null;
|
|
427
483
|
StopTimeout?: number;
|
|
428
484
|
Healthcheck?: {
|
|
@@ -442,12 +498,28 @@ export declare namespace Docker {
|
|
|
442
498
|
AutoRemove: boolean;
|
|
443
499
|
NetworkMode?: string;
|
|
444
500
|
CapAdd?: string[] | null;
|
|
501
|
+
CapDrop?: string[] | null;
|
|
502
|
+
Memory?: number;
|
|
503
|
+
MemorySwap?: number;
|
|
504
|
+
MemoryReservation?: number;
|
|
505
|
+
NanoCpus?: number;
|
|
506
|
+
PidsLimit?: number;
|
|
507
|
+
ReadonlyRootfs?: boolean;
|
|
508
|
+
SecurityOpt?: string[] | null;
|
|
509
|
+
UsernsMode?: string;
|
|
510
|
+
GroupAdd?: string[] | null;
|
|
511
|
+
Ulimits?: Array<{ Name: string; Soft: number; Hard: number }> | null;
|
|
512
|
+
LogConfig?: { Type: string; Config: Record<string, string> | null };
|
|
513
|
+
OomScoreAdj?: number;
|
|
514
|
+
Init?: boolean | null;
|
|
515
|
+
Tmpfs?: Record<string, string> | null;
|
|
445
516
|
Devices?: Array<{
|
|
446
517
|
PathOnHost: string;
|
|
447
518
|
PathInContainer: string;
|
|
448
519
|
CgroupPermissions: string;
|
|
449
520
|
}> | null;
|
|
450
521
|
};
|
|
522
|
+
AppArmorProfile?: string;
|
|
451
523
|
NetworkSettings: {
|
|
452
524
|
Networks: Record<string, { NetworkID: string; Aliases: string[] | null }> | null;
|
|
453
525
|
Ports?: Record<string, Array<{ HostIp: string; HostPort: string }> | null> | null;
|
|
@@ -832,17 +904,21 @@ const makeDocker = (options: DockerOptions) =>
|
|
|
832
904
|
},
|
|
833
905
|
image: {
|
|
834
906
|
build: Effect.fn("Docker.image.build")(function* (
|
|
835
|
-
{ context: buildContext, engineContext, args, credentials, ...options },
|
|
907
|
+
{ context: buildContext, engineContext, args, credentials, secrets, ...options },
|
|
836
908
|
session,
|
|
837
909
|
registry,
|
|
838
910
|
) {
|
|
839
911
|
const tap = outputTap(session);
|
|
840
|
-
const
|
|
912
|
+
const secret = buildSecrets(secrets);
|
|
913
|
+
const buildArgs = [buildContext, ...formatArgs(options), ...secret.args, ...(args ?? [])];
|
|
841
914
|
const engine = formatArgs({ context: engineContext });
|
|
842
915
|
if (registry === undefined) {
|
|
843
916
|
return yield* run(
|
|
844
917
|
[...engine, "image", "build", ...buildArgs],
|
|
845
|
-
|
|
918
|
+
{
|
|
919
|
+
...(credentials ? yield* registryEnvironment(credentials) : undefined),
|
|
920
|
+
...secret.env,
|
|
921
|
+
},
|
|
846
922
|
tap,
|
|
847
923
|
);
|
|
848
924
|
}
|
|
@@ -853,7 +929,7 @@ const makeDocker = (options: DockerOptions) =>
|
|
|
853
929
|
const env = yield* registryEnvironment(registry);
|
|
854
930
|
return yield* run(
|
|
855
931
|
[...engine, "buildx", "build", "--push", ...buildArgs],
|
|
856
|
-
env,
|
|
932
|
+
{ ...env, ...secret.env },
|
|
857
933
|
tap,
|
|
858
934
|
).pipe(Effect.mapError(classifyDockerRegistryError));
|
|
859
935
|
}
|
|
@@ -869,7 +945,7 @@ const makeDocker = (options: DockerOptions) =>
|
|
|
869
945
|
...(mode === "load" ? ["buildx", "build", "--load"] : ["image", "build"]),
|
|
870
946
|
...buildArgs,
|
|
871
947
|
],
|
|
872
|
-
|
|
948
|
+
secret.env,
|
|
873
949
|
tap,
|
|
874
950
|
);
|
|
875
951
|
const [tag, ...tags] =
|
|
@@ -1048,6 +1124,22 @@ const systemError = (input: {
|
|
|
1048
1124
|
}),
|
|
1049
1125
|
);
|
|
1050
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* `--secret id=<id>,env=<var>` per secret, with each value under a generated
|
|
1129
|
+
* variable name: an id is the Dockerfile author's choice and may collide with
|
|
1130
|
+
* the environment Docker itself reads (`PATH`, `DOCKER_HOST`).
|
|
1131
|
+
*/
|
|
1132
|
+
const buildSecrets = (secrets: Record<string, string | Redacted.Redacted<string>> | undefined) => {
|
|
1133
|
+
const args: Array<string> = [];
|
|
1134
|
+
const env: Record<string, string> = {};
|
|
1135
|
+
Object.entries(secrets ?? {}).forEach(([id, value], index) => {
|
|
1136
|
+
const variable = `ALCHEMY_BUILD_SECRET_${index}`;
|
|
1137
|
+
args.push("--secret", `id=${id},env=${variable}`);
|
|
1138
|
+
env[variable] = Redacted.isRedacted(value) ? Redacted.value(value) : value;
|
|
1139
|
+
});
|
|
1140
|
+
return { args, env };
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1051
1143
|
/** Formats a set of options into a list of command-line arguments. */
|
|
1052
1144
|
const formatArgs = (
|
|
1053
1145
|
options: Record<
|
|
@@ -1057,7 +1149,9 @@ const formatArgs = (
|
|
|
1057
1149
|
) => {
|
|
1058
1150
|
const args: Array<string> = [];
|
|
1059
1151
|
for (const [key, value] of Object.entries(options)) {
|
|
1060
|
-
|
|
1152
|
+
// `0` is meaningful (`--replicas 0`, `--update-parallelism 0`), so only
|
|
1153
|
+
// absent, `false` and empty values are omitted.
|
|
1154
|
+
if (value === undefined || value === false || value === "") continue;
|
|
1061
1155
|
const prefix = key.length > 1 ? `--${key}` : `-${key}`;
|
|
1062
1156
|
if (value === true) {
|
|
1063
1157
|
args.push(prefix);
|
package/src/Image.ts
CHANGED
|
@@ -5,6 +5,7 @@ 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";
|
|
8
9
|
import { Docker, dockerContextName, dockerPhysicalName } from "./Docker.ts";
|
|
9
10
|
import type { Providers } from "./Providers.ts";
|
|
10
11
|
import {
|
|
@@ -30,8 +31,18 @@ export interface DockerBuildOptions {
|
|
|
30
31
|
dockerfile?: string;
|
|
31
32
|
/** Target platform, e.g. `"linux/amd64"`. */
|
|
32
33
|
platform?: string;
|
|
33
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Docker build arguments (`--build-arg`). Never pass secrets here: build
|
|
36
|
+
* args are visible in the image history. Use {@link secrets} instead.
|
|
37
|
+
*/
|
|
34
38
|
args?: Record<string, string>;
|
|
39
|
+
/**
|
|
40
|
+
* BuildKit secrets, keyed by id. Mount one in a `RUN` step with
|
|
41
|
+
* `--mount=type=secret,id=<id>` and read it from `/run/secrets/<id>`, or
|
|
42
|
+
* add `env=<NAME>` to expose it as an environment variable for that step.
|
|
43
|
+
* A secret never enters the image, its build history, or `docker`'s argv.
|
|
44
|
+
*/
|
|
45
|
+
secrets?: Record<string, Redacted.Redacted<string>>;
|
|
35
46
|
/** Multi-stage build target. */
|
|
36
47
|
target?: string;
|
|
37
48
|
/** Cache sources passed as `--cache-from`. */
|
|
@@ -117,6 +128,23 @@ export interface Image extends Resource<
|
|
|
117
128
|
* });
|
|
118
129
|
* ```
|
|
119
130
|
*
|
|
131
|
+
* ### Build Secrets
|
|
132
|
+
* **Example:** Install from a private npm registry
|
|
133
|
+
* ```typescript
|
|
134
|
+
* // Dockerfile:
|
|
135
|
+
* // RUN --mount=type=secret,id=NPM_TOKEN,env=NPM_TOKEN npm ci
|
|
136
|
+
* const image = yield* Docker.Image("app", {
|
|
137
|
+
* name: "my-app",
|
|
138
|
+
* build: {
|
|
139
|
+
* context: "./app",
|
|
140
|
+
* secrets: { NPM_TOKEN: Config.Redacted("NPM_TOKEN") },
|
|
141
|
+
* },
|
|
142
|
+
* });
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* The secret is mounted only into the `RUN` step that asks for it; it never
|
|
146
|
+
* lands in an image layer or the build history.
|
|
147
|
+
*
|
|
120
148
|
* ### Registry Push
|
|
121
149
|
* **Example:** Push with Redacted credentials
|
|
122
150
|
* ```typescript
|
|
@@ -192,6 +220,7 @@ export const ImageProvider = () =>
|
|
|
192
220
|
platform: props.build.platform,
|
|
193
221
|
target: props.build.target,
|
|
194
222
|
"build-arg": props.build.args,
|
|
223
|
+
secrets: props.build.secrets,
|
|
195
224
|
"cache-from": props.build.cacheFrom,
|
|
196
225
|
"cache-to": props.build.cacheTo,
|
|
197
226
|
args: props.build.options,
|
package/src/ImageSource.ts
CHANGED
|
@@ -3,9 +3,11 @@ import * as Bundle from "alchemy/Bundle/Bundle";
|
|
|
3
3
|
import { findCwdForBundle, getStableContextDir, resolveMainPath } from "alchemy/Bundle/TempRoot";
|
|
4
4
|
import { hashDirectory } from "alchemy/Command/Memo";
|
|
5
5
|
import { sha256Object } from "alchemy/Util/sha256";
|
|
6
|
+
import * as Data from "effect/Data";
|
|
6
7
|
import * as Effect from "effect/Effect";
|
|
7
8
|
import * as FileSystem from "effect/FileSystem";
|
|
8
9
|
import * as Path from "effect/Path";
|
|
10
|
+
import type * as Redacted from "effect/Redacted";
|
|
9
11
|
import * as Schedule from "effect/Schedule";
|
|
10
12
|
import type * as rolldown from "rolldown";
|
|
11
13
|
import { Docker, type RegistryCredentials } from "./Docker.ts";
|
|
@@ -82,11 +84,41 @@ export interface BundledImageSource {
|
|
|
82
84
|
build?: Bundle.BundleConfig;
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Build arguments and BuildKit secrets for an image built from the user's
|
|
89
|
+
* own Dockerfile (`context` / `dockerfile`).
|
|
90
|
+
*/
|
|
91
|
+
export interface DockerfileBuild {
|
|
92
|
+
/**
|
|
93
|
+
* Docker build arguments (`--build-arg`), read by `ARG` instructions.
|
|
94
|
+
* They are part of the image's content hash, so changing one rebuilds the
|
|
95
|
+
* image. Never pass secrets here: build args are visible in the image
|
|
96
|
+
* history. Use {@link secrets} instead.
|
|
97
|
+
*/
|
|
98
|
+
args?: Record<string, string>;
|
|
99
|
+
/**
|
|
100
|
+
* BuildKit secrets, keyed by id. Mount one in a `RUN` step with
|
|
101
|
+
* `--mount=type=secret,id=<id>` and read it from `/run/secrets/<id>`, or
|
|
102
|
+
* add `env=<NAME>` to expose it as an environment variable for that step.
|
|
103
|
+
*
|
|
104
|
+
* A secret never enters the image, its build history, or `docker`'s argv.
|
|
105
|
+
* Secrets are left out of the image's content hash, so rotating one does
|
|
106
|
+
* not rebuild the image.
|
|
107
|
+
*/
|
|
108
|
+
secrets?: Record<string, Redacted.Redacted<string>>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The `build` prop of a source built from the user's own Dockerfile. */
|
|
112
|
+
export interface DockerfileBuildOptions {
|
|
113
|
+
/** Build arguments and BuildKit secrets for the Dockerfile build. */
|
|
114
|
+
build?: DockerfileBuild;
|
|
115
|
+
}
|
|
116
|
+
|
|
85
117
|
/**
|
|
86
118
|
* Build the image from the user's own Dockerfile and build context — no
|
|
87
119
|
* Effect program is bundled.
|
|
88
120
|
*/
|
|
89
|
-
export interface DockerfileImageSource {
|
|
121
|
+
export interface DockerfileImageSource extends DockerfileBuildOptions {
|
|
90
122
|
/**
|
|
91
123
|
* Docker build context directory. Optional when {@link dockerfile} is
|
|
92
124
|
* inline content (which builds with an empty context).
|
|
@@ -123,7 +155,8 @@ export type ImageSourceProps = BundledImageSource | DockerfileImageSource | Regi
|
|
|
123
155
|
export interface ImageSourceLike {
|
|
124
156
|
main?: string;
|
|
125
157
|
handler?: string;
|
|
126
|
-
build
|
|
158
|
+
/** Bundler config for `main`; Docker build args and secrets for `context`. */
|
|
159
|
+
build?: Bundle.BundleConfig & DockerfileBuild;
|
|
127
160
|
context?: string;
|
|
128
161
|
dockerfile?: string | InlineDockerfile;
|
|
129
162
|
image?: string;
|
|
@@ -146,9 +179,41 @@ export const imageSourceKind = (source: ImageSourceLike): ImageSourceKind | unde
|
|
|
146
179
|
? "context"
|
|
147
180
|
: undefined;
|
|
148
181
|
|
|
182
|
+
/**
|
|
183
|
+
* `build.args` or `build.secrets` set on an image that is not built from the
|
|
184
|
+
* user's own Dockerfile: a `main` program or a pre-built `image`.
|
|
185
|
+
*/
|
|
186
|
+
export class DockerfileBuildConflict extends Data.TaggedError("DockerfileBuildConflict")<{
|
|
187
|
+
readonly id: string;
|
|
188
|
+
readonly source: "main" | "image";
|
|
189
|
+
}> {
|
|
190
|
+
override get message() {
|
|
191
|
+
return `'${this.id}': 'build.args' and 'build.secrets' apply to an image built from your own Dockerfile ('context' / 'dockerfile'), not to '${this.source}'`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Dies with {@link DockerfileBuildConflict} when `build.args` or
|
|
197
|
+
* `build.secrets` are set alongside `main` or `image`.
|
|
198
|
+
*/
|
|
199
|
+
export const validateDockerfileBuild = (
|
|
200
|
+
id: string,
|
|
201
|
+
source: { main?: string; image?: string; build?: DockerfileBuild },
|
|
202
|
+
): Effect.Effect<void> =>
|
|
203
|
+
(source.main !== undefined || source.image !== undefined) &&
|
|
204
|
+
(source.build?.args !== undefined || source.build?.secrets !== undefined)
|
|
205
|
+
? Effect.die(
|
|
206
|
+
new DockerfileBuildConflict({
|
|
207
|
+
id,
|
|
208
|
+
source: source.main !== undefined ? "main" : "image",
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
211
|
+
: Effect.void;
|
|
212
|
+
|
|
149
213
|
/**
|
|
150
214
|
* Validate environment-source exclusivity. Dies (plan-time defect) on
|
|
151
|
-
* `image`+`dockerfile`, `image`+`context`,
|
|
215
|
+
* `image`+`dockerfile`, `image`+`context`, inline-`dockerfile`+`context`, or
|
|
216
|
+
* build args/secrets without a Dockerfile build.
|
|
152
217
|
*/
|
|
153
218
|
export const validateImageSource = (id: string, source: ImageSourceLike): Effect.Effect<void> => {
|
|
154
219
|
if (source.image !== undefined && source.dockerfile !== undefined) {
|
|
@@ -176,7 +241,7 @@ export const validateImageSource = (id: string, source: ImageSourceLike): Effect
|
|
|
176
241
|
),
|
|
177
242
|
);
|
|
178
243
|
}
|
|
179
|
-
return
|
|
244
|
+
return validateDockerfileBuild(id, source);
|
|
180
245
|
};
|
|
181
246
|
|
|
182
247
|
/**
|
|
@@ -287,6 +352,8 @@ export const computeStaticSourceHash = Effect.fn(function* (
|
|
|
287
352
|
return (yield* sha256Object({
|
|
288
353
|
dockerfile: source.dockerfile.content,
|
|
289
354
|
platform: platform ?? "linux/amd64",
|
|
355
|
+
// Absent when unset, so existing hashes hold. Secrets are excluded.
|
|
356
|
+
buildArgs: source.build?.args,
|
|
290
357
|
})).slice(0, 16);
|
|
291
358
|
}
|
|
292
359
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -300,6 +367,7 @@ export const computeStaticSourceHash = Effect.fn(function* (
|
|
|
300
367
|
contextHash,
|
|
301
368
|
dockerfile: dockerfileContent,
|
|
302
369
|
platform: platform ?? "linux/amd64",
|
|
370
|
+
buildArgs: source.build?.args,
|
|
303
371
|
})).slice(0, 16);
|
|
304
372
|
}
|
|
305
373
|
return undefined;
|
|
@@ -460,7 +528,7 @@ export const makeContainerImageSource = Effect.gen(function* () {
|
|
|
460
528
|
envFrom !== undefined
|
|
461
529
|
? `FROM ${envFrom}`
|
|
462
530
|
: source.dockerfile !== undefined && isInlineDockerfile(source.dockerfile)
|
|
463
|
-
?
|
|
531
|
+
? (source.dockerfile.content as string).trimEnd()
|
|
464
532
|
: `FROM ${source.image ?? "oven/bun:1"}`;
|
|
465
533
|
const lines = [
|
|
466
534
|
preamble,
|
|
@@ -514,6 +582,7 @@ export const makeContainerImageSource = Effect.gen(function* () {
|
|
|
514
582
|
context: string;
|
|
515
583
|
dockerfile?: string;
|
|
516
584
|
platform?: string;
|
|
585
|
+
build?: DockerfileBuild;
|
|
517
586
|
},
|
|
518
587
|
) {
|
|
519
588
|
const credentials = yield* target.credentials;
|
|
@@ -522,6 +591,8 @@ export const makeContainerImageSource = Effect.gen(function* () {
|
|
|
522
591
|
context: options.context,
|
|
523
592
|
file: options.dockerfile,
|
|
524
593
|
platform: options.platform,
|
|
594
|
+
"build-arg": options.build?.args,
|
|
595
|
+
secrets: options.build?.secrets,
|
|
525
596
|
});
|
|
526
597
|
yield* push(options.imageUri, credentials, options.platform);
|
|
527
598
|
return options.imageUri;
|
|
@@ -676,6 +747,7 @@ export const makeContainerImageSource = Effect.gen(function* () {
|
|
|
676
747
|
imageUri,
|
|
677
748
|
context: contextDir,
|
|
678
749
|
platform,
|
|
750
|
+
build: source.build,
|
|
679
751
|
});
|
|
680
752
|
yield* session.note(`Pushed ${imageUri}`);
|
|
681
753
|
return { imageUri, repositoryUri, codeHash };
|
|
@@ -698,6 +770,7 @@ export const makeContainerImageSource = Effect.gen(function* () {
|
|
|
698
770
|
context,
|
|
699
771
|
dockerfile,
|
|
700
772
|
platform,
|
|
773
|
+
build: source.build,
|
|
701
774
|
});
|
|
702
775
|
yield* session.note(`Pushed ${imageUri}`);
|
|
703
776
|
return { imageUri, repositoryUri, codeHash };
|
package/src/RemoteImage.ts
CHANGED
|
@@ -140,7 +140,7 @@ export const RemoteImageProvider = () =>
|
|
|
140
140
|
const inspectImage = (ref: string, context: string | undefined) =>
|
|
141
141
|
docker.image
|
|
142
142
|
.inspect(ref, context)
|
|
143
|
-
.pipe(Effect.catchIf(isMissingImageError, () => Effect.
|
|
143
|
+
.pipe(Effect.catchIf(isMissingImageError, () => Effect.undefined));
|
|
144
144
|
|
|
145
145
|
return RemoteImage.Provider.of({
|
|
146
146
|
list: () => Effect.succeed([]),
|