@alchemy.run/docker 0.0.0-stage → 2.0.0-beta.84

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.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +50 -2
  3. package/lib/Bootstrap.d.ts +7 -0
  4. package/lib/Bootstrap.d.ts.map +1 -0
  5. package/lib/Bootstrap.js +25 -0
  6. package/lib/Bootstrap.js.map +1 -0
  7. package/lib/BuildHash.d.ts +47 -0
  8. package/lib/BuildHash.d.ts.map +1 -0
  9. package/lib/BuildHash.js +165 -0
  10. package/lib/BuildHash.js.map +1 -0
  11. package/lib/Container.d.ts +352 -0
  12. package/lib/Container.d.ts.map +1 -0
  13. package/lib/Container.js +531 -0
  14. package/lib/Container.js.map +1 -0
  15. package/lib/Context.d.ts +86 -0
  16. package/lib/Context.d.ts.map +1 -0
  17. package/lib/Context.js +146 -0
  18. package/lib/Context.js.map +1 -0
  19. package/lib/Docker.d.ts +448 -0
  20. package/lib/Docker.d.ts.map +1 -0
  21. package/lib/Docker.js +416 -0
  22. package/lib/Docker.js.map +1 -0
  23. package/lib/Dockerfile.d.ts +72 -0
  24. package/lib/Dockerfile.d.ts.map +1 -0
  25. package/lib/Dockerfile.js +12 -0
  26. package/lib/Dockerfile.js.map +1 -0
  27. package/lib/HealthcheckCommand.d.ts +17 -0
  28. package/lib/HealthcheckCommand.d.ts.map +1 -0
  29. package/lib/HealthcheckCommand.js +33 -0
  30. package/lib/HealthcheckCommand.js.map +1 -0
  31. package/lib/Image.d.ts +151 -0
  32. package/lib/Image.d.ts.map +1 -0
  33. package/lib/Image.js +192 -0
  34. package/lib/Image.js.map +1 -0
  35. package/lib/ImageSource.d.ts +234 -0
  36. package/lib/ImageSource.d.ts.map +1 -0
  37. package/lib/ImageSource.js +469 -0
  38. package/lib/ImageSource.js.map +1 -0
  39. package/lib/Network.d.ts +70 -0
  40. package/lib/Network.d.ts.map +1 -0
  41. package/lib/Network.js +111 -0
  42. package/lib/Network.js.map +1 -0
  43. package/lib/Providers.d.ts +21 -0
  44. package/lib/Providers.d.ts.map +1 -0
  45. package/lib/Providers.js +26 -0
  46. package/lib/Providers.js.map +1 -0
  47. package/lib/Registry.d.ts +47 -0
  48. package/lib/Registry.d.ts.map +1 -0
  49. package/lib/Registry.js +67 -0
  50. package/lib/Registry.js.map +1 -0
  51. package/lib/RegistryError.d.ts +21 -0
  52. package/lib/RegistryError.d.ts.map +1 -0
  53. package/lib/RegistryError.js +28 -0
  54. package/lib/RegistryError.js.map +1 -0
  55. package/lib/RemoteImage.d.ts +118 -0
  56. package/lib/RemoteImage.d.ts.map +1 -0
  57. package/lib/RemoteImage.js +164 -0
  58. package/lib/RemoteImage.js.map +1 -0
  59. package/lib/Service.d.ts +478 -0
  60. package/lib/Service.d.ts.map +1 -0
  61. package/lib/Service.js +588 -0
  62. package/lib/Service.js.map +1 -0
  63. package/lib/ServiceImage.d.ts +78 -0
  64. package/lib/ServiceImage.d.ts.map +1 -0
  65. package/lib/ServiceImage.js +162 -0
  66. package/lib/ServiceImage.js.map +1 -0
  67. package/lib/Swarm.d.ts +119 -0
  68. package/lib/Swarm.d.ts.map +1 -0
  69. package/lib/Swarm.js +148 -0
  70. package/lib/Swarm.js.map +1 -0
  71. package/lib/Volume.d.ts +94 -0
  72. package/lib/Volume.d.ts.map +1 -0
  73. package/lib/Volume.js +133 -0
  74. package/lib/Volume.js.map +1 -0
  75. package/lib/index.d.ts +14 -0
  76. package/lib/index.d.ts.map +1 -0
  77. package/lib/index.js +13 -0
  78. package/lib/index.js.map +1 -0
  79. package/package.json +72 -3
  80. package/src/Bootstrap.ts +51 -0
  81. package/src/BuildHash.ts +255 -0
  82. package/src/Container.ts +792 -0
  83. package/src/Context.ts +215 -0
  84. package/src/Docker.ts +1079 -0
  85. package/src/Dockerfile.ts +81 -0
  86. package/src/HealthcheckCommand.ts +35 -0
  87. package/src/Image.ts +293 -0
  88. package/src/ImageSource.ts +769 -0
  89. package/src/Network.ts +169 -0
  90. package/src/Providers.ts +46 -0
  91. package/src/Registry.ts +91 -0
  92. package/src/RegistryError.ts +48 -0
  93. package/src/RemoteImage.ts +255 -0
  94. package/src/Service.ts +1080 -0
  95. package/src/ServiceImage.ts +252 -0
  96. package/src/Swarm.ts +221 -0
  97. package/src/Volume.ts +193 -0
  98. package/src/index.ts +17 -0
package/lib/Network.js ADDED
@@ -0,0 +1,111 @@
1
+ import { Unowned } from "alchemy/AdoptPolicy";
2
+ import { isResolved } from "alchemy/Diff";
3
+ import * as Provider from "alchemy/Provider";
4
+ import { Resource } from "alchemy/Resource";
5
+ import { createInternalTags, hasAlchemyTags, stripInternalTags } from "alchemy/Tags";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Equal from "effect/Equal";
8
+ import { Docker, dockerEngineContextName, dockerPhysicalName } from "./Docker.js";
9
+ /**
10
+ * A Docker network managed through the active Docker context.
11
+ *
12
+ * Existing same-name networks are treated as foreign unless the engine is
13
+ * explicitly allowed to adopt them with `--adopt` or `adopt(true)`.
14
+ *
15
+ *
16
+ * ### Creating Networks
17
+ * **Example:** Basic bridge network
18
+ * ```typescript
19
+ * const network = yield* Docker.Network("app-network", {
20
+ * name: "app-network",
21
+ * });
22
+ * ```
23
+ *
24
+ * ### Adoption
25
+ * **Example:** Adopt a pre-existing network
26
+ * ```typescript
27
+ * const network = yield* Docker.Network("app-network", {
28
+ * name: "shared-app-network",
29
+ * }).pipe(adopt(true));
30
+ * ```
31
+ *
32
+ * @resource
33
+ * @product Network
34
+ */
35
+ export const Network = Resource("Docker.Network");
36
+ export const NetworkProvider = () => Provider.effect(Network, Effect.gen(function* () {
37
+ const docker = yield* Docker;
38
+ return Network.Provider.of({
39
+ list: () => Effect.succeed([]),
40
+ read: Effect.fn(function* ({ id, instanceId, olds, output }) {
41
+ const context = dockerEngineContextName(olds?.context);
42
+ const name = yield* dockerPhysicalName(id, olds, instanceId);
43
+ const info = yield* docker.network
44
+ .inspect(name, context)
45
+ .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined));
46
+ if (!info)
47
+ return undefined;
48
+ const attrs = toNetworkAttributes(info);
49
+ if (output)
50
+ return attrs;
51
+ // Without prior state, only adopt a network that carries our branding;
52
+ // anything else is foreign and gated behind `--adopt`.
53
+ const owned = yield* hasAlchemyTags(id, info.Labels ?? undefined);
54
+ return owned ? attrs : Unowned(attrs);
55
+ }),
56
+ diff: Effect.fn(function* ({ id, output, instanceId, news, olds }) {
57
+ if (!isResolved(news) || !output)
58
+ return undefined;
59
+ if (dockerEngineContextName(olds?.context) !== dockerEngineContextName(news?.context)) {
60
+ return { action: "replace", deleteFirst: true };
61
+ }
62
+ const args = yield* makeNetworkArgs(id, news, instanceId);
63
+ // Auto-generated names are engine-owned: the deployed name stays
64
+ // authoritative even if the generator would name this id differently
65
+ // today. Only an explicit user-provided name can force a replace.
66
+ const desiredName = news?.name ?? output.name;
67
+ if (output.name !== desiredName ||
68
+ output.driver !== args.driver ||
69
+ output.enableIPv6 !== args.ipv6 ||
70
+ // Compare only user labels; internal `alchemy::*` branding lives on
71
+ // the observed network but must not drive replacement.
72
+ !Equal.equals(stripInternalTags(output.labels), args.label ?? {})) {
73
+ return { action: "replace", deleteFirst: true };
74
+ }
75
+ }),
76
+ reconcile: Effect.fn(function* ({ output, id, instanceId, news }) {
77
+ const context = dockerEngineContextName(news?.context);
78
+ if (output) {
79
+ const refreshed = yield* docker.network.inspect(output.id, context).pipe(Effect.map(toNetworkAttributes), Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined));
80
+ if (refreshed)
81
+ return refreshed;
82
+ }
83
+ const args = yield* makeNetworkArgs(id, news, instanceId);
84
+ const internalTags = yield* createInternalTags(id);
85
+ const { stdout: createdId } = yield* docker.network.create({
86
+ ...args,
87
+ label: { ...internalTags, ...args.label },
88
+ context,
89
+ });
90
+ return toNetworkAttributes(yield* docker.network.inspect(createdId, context));
91
+ }),
92
+ delete: Effect.fn(({ olds, output }) => docker.network
93
+ .remove(output.id, dockerEngineContextName(olds?.context))
94
+ .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.void))),
95
+ });
96
+ }));
97
+ const makeNetworkArgs = (id, props, instanceId) => dockerPhysicalName(id, props, instanceId).pipe(Effect.map((name) => ({
98
+ name,
99
+ driver: props?.driver ?? "bridge",
100
+ ipv6: props?.enableIPv6 ?? false,
101
+ label: props?.labels ?? {},
102
+ })));
103
+ export const toNetworkAttributes = (info) => ({
104
+ id: info.Id,
105
+ name: info.Name,
106
+ driver: info.Driver,
107
+ enableIPv6: info.EnableIPv6,
108
+ labels: info.Labels ?? {},
109
+ createdAt: Date.parse(info.Created) || Date.now(),
110
+ });
111
+ //# sourceMappingURL=Network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Network.js","sourceRoot":"","sources":["../src/Network.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AA8ClF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAU,gBAAgB,CAAC,CAAC;AAE3D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,EAAE,CAClC,QAAQ,CAAC,MAAM,CACb,OAAO,EACP,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC;IAE7B,OAAO,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;YACzD,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO;iBAC/B,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;iBACtB,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;YACjF,IAAI,CAAC,IAAI;gBAAE,OAAO,SAAS,CAAC;YAC5B,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,MAAM;gBAAE,OAAO,KAAK,CAAC;YACzB,uEAAuE;YACvE,uDAAuD;YACvD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;YAClE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC,CAAC;QACF,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE;YAC/D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YACnD,IAAI,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;gBACtF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAClD,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,iEAAiE;YACjE,qEAAqE;YACrE,kEAAkE;YAClE,MAAM,WAAW,GAAG,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;YAC9C,IACE,MAAM,CAAC,IAAI,KAAK,WAAW;gBAC3B,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;gBAC7B,MAAM,CAAC,UAAU,KAAK,IAAI,CAAC,IAAI;gBAC/B,oEAAoE;gBACpE,uDAAuD;gBACvD,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EACjE,CAAC;gBACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAClD,CAAC;QACH,CAAC,CAAC;QACF,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;YAC9D,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,IAAI,CACtE,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAC/B,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CACxE,CAAC;gBACF,IAAI,SAAS;oBAAE,OAAO,SAAS,CAAC;YAClC,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;gBACzD,GAAG,IAAI;gBACP,KAAK,EAAE,EAAE,GAAG,YAAY,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE;gBACzC,OAAO;aACR,CAAC,CAAC;YACH,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QAChF,CAAC,CAAC;QACF,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACrC,MAAM,CAAC,OAAO;aACX,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;aACzD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAC5E;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CACH,CAAC;AAEJ,MAAM,eAAe,GAAG,CAAC,EAAU,EAAE,KAA+B,EAAE,UAAkB,EAAE,EAAE,CAC1F,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,IAAI,CAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAyD,EAAE,CAAC,CAAC;IAC3E,IAAI;IACJ,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,QAAQ;IACjC,IAAI,EAAE,KAAK,EAAE,UAAU,IAAI,KAAK;IAChC,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE;CAC3B,CAAC,CAAC,CACJ,CAAC;AAEJ,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAoB,EAAyB,EAAE,CAAC,CAAC;IACnF,EAAE,EAAE,IAAI,CAAC,EAAE;IACX,IAAI,EAAE,IAAI,CAAC,IAAI;IACf,MAAM,EAAE,IAAI,CAAC,MAAM;IACnB,UAAU,EAAE,IAAI,CAAC,UAAU;IAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;IACzB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE;CAClD,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import * as Provider from "alchemy/Provider";
2
+ import * as Layer from "effect/Layer";
3
+ import { type DockerOptions } from "./Docker.ts";
4
+ declare const Providers_base: Provider.ProviderCollection<Providers, "Docker">;
5
+ export declare class Providers extends Providers_base {
6
+ }
7
+ export type ProviderRequirements = Layer.Services<ReturnType<typeof providers>>;
8
+ /**
9
+ * Registers all Docker resource providers.
10
+ *
11
+ * Docker providers use the active Docker CLI context and are intentionally
12
+ * separate from `Cloudflare.Container`. Pass `bin` to run a Docker-compatible
13
+ * CLI such as Podman; the `DOCKER_BIN` environment variable overrides it.
14
+ *
15
+ * ```typescript
16
+ * providers: Docker.providers({ bin: "podman" }),
17
+ * ```
18
+ */
19
+ export declare const providers: (options?: DockerOptions) => Layer.Layer<import("./Docker.ts").Docker | Providers, never, import("alchemy/AlchemyContext").AlchemyContext | import("effect/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
20
+ export {};
21
+ //# sourceMappingURL=Providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Providers.d.ts","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAE,KAAK,aAAa,EAAc,MAAM,aAAa,CAAC;;AAQ7D,qBAAa,SAAU,SAAQ,cAAkD;CAAG;AAEpF,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,aAAa,aAAa,2TAkB7C,CAAC"}
@@ -0,0 +1,26 @@
1
+ import * as Provider from "alchemy/Provider";
2
+ import * as Layer from "effect/Layer";
3
+ import { Container, ContainerProvider } from "./Container.js";
4
+ import { Context, ContextProvider } from "./Context.js";
5
+ import { dockerLive } from "./Docker.js";
6
+ import { Image, ImageProvider } from "./Image.js";
7
+ import { Network, NetworkProvider } from "./Network.js";
8
+ import { RemoteImage, RemoteImageProvider } from "./RemoteImage.js";
9
+ import { Service, ServiceProvider } from "./Service.js";
10
+ import { Swarm, SwarmProvider } from "./Swarm.js";
11
+ import { Volume, VolumeProvider } from "./Volume.js";
12
+ export class Providers extends Provider.ProviderCollection()("Docker") {
13
+ }
14
+ /**
15
+ * Registers all Docker resource providers.
16
+ *
17
+ * Docker providers use the active Docker CLI context and are intentionally
18
+ * separate from `Cloudflare.Container`. Pass `bin` to run a Docker-compatible
19
+ * CLI such as Podman; the `DOCKER_BIN` environment variable overrides it.
20
+ *
21
+ * ```typescript
22
+ * providers: Docker.providers({ bin: "podman" }),
23
+ * ```
24
+ */
25
+ export const providers = (options = {}) => Layer.effect(Providers, Provider.collection([Container, Image, Network, RemoteImage, Volume, Context, Service, Swarm])).pipe(Layer.provide(Layer.mergeAll(ContainerProvider(), ImageProvider(), NetworkProvider(), RemoteImageProvider(), VolumeProvider(), ContextProvider(), ServiceProvider(), SwarmProvider())), Layer.provideMerge(dockerLive(options)));
26
+ //# sourceMappingURL=Providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Providers.js","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAsB,UAAU,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,OAAO,SAAU,SAAQ,QAAQ,CAAC,kBAAkB,EAAa,CAAC,QAAQ,CAAC;CAAG;AAIpF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAO,GAAkB,EAAE,EAAE,EAAE,CACvD,KAAK,CAAC,MAAM,CACV,SAAS,EACT,QAAQ,CAAC,UAAU,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAC/F,CAAC,IAAI,CACJ,KAAK,CAAC,OAAO,CACX,KAAK,CAAC,QAAQ,CACZ,iBAAiB,EAAE,EACnB,aAAa,EAAE,EACf,eAAe,EAAE,EACjB,mBAAmB,EAAE,EACrB,cAAc,EAAE,EAChB,eAAe,EAAE,EACjB,eAAe,EAAE,EACjB,aAAa,EAAE,CAChB,CACF,EACD,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CACxC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type * as Redacted from "effect/Redacted";
2
+ export interface ImageRegistry {
3
+ /** Registry host, e.g. `ghcr.io`. */
4
+ server: string;
5
+ /** Registry username. */
6
+ username: string;
7
+ /** Registry password. Use `Redacted.make(...)` or `Config.Redacted(...)`. */
8
+ password: Redacted.Redacted<string>;
9
+ }
10
+ /** Strips the tag and digest from an image reference, leaving the repository. */
11
+ export declare const repositoryFromImageRef: (imageRef: string) => string;
12
+ /**
13
+ * Prefixes an image reference with the registry host unless the reference
14
+ * already carries a registry prefix (a dotted host, a host:port, or `localhost`).
15
+ */
16
+ export declare const withRegistryHost: (imageRef: string, registry: {
17
+ server: string;
18
+ }) => string;
19
+ /** Extracts the `repository@sha256:...` digest from `docker push` output. */
20
+ export declare const parseRepoDigest: (imageRef: string, output: string) => string | undefined;
21
+ /**
22
+ * Picks the `repository@digest` that inspect reports for this image.
23
+ *
24
+ * A fallback when push output has no `digest:` line. Podman pushes report the
25
+ * published digest through `--digestfile` instead, because Podman can list a
26
+ * pulled image's source digest under the pushed name here.
27
+ */
28
+ export declare const repoDigestFromInspect: (imageRef: string, repoDigests: ReadonlyArray<string> | null | undefined) => string | undefined;
29
+ /**
30
+ * Digest from push output when the engine prints one, otherwise the matching
31
+ * `RepoDigests` entry. `registryRef` is the host-qualified name when it
32
+ * differs from the local reference that was pushed.
33
+ */
34
+ export declare const publishedRepoDigest: (imageRef: string, output: {
35
+ stdout: string;
36
+ stderr: string;
37
+ }, repoDigests: ReadonlyArray<string> | null | undefined, registryRef?: string) => string | undefined;
38
+ /**
39
+ * Parses an image's RFC 3339 `Created` timestamp into epoch milliseconds.
40
+ *
41
+ * Docker only reports `Created` when the image config carries a creation time:
42
+ * API >= 1.44 omits it, older APIs backfill the year-1 zero value
43
+ * (`0001-01-01T00:00:00Z`), and 25.0.0–25.0.3 returned an empty string. In any
44
+ * of those cases there is no real build time, so we fall back to the wall clock.
45
+ */
46
+ export declare const parseCreatedAt: (created: string | undefined) => number;
47
+ //# sourceMappingURL=Registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Registry.d.ts","sourceRoot":"","sources":["../src/Registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,aAAc,MAAM,KAAG,MAOzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,aAAc,MAAM,YAAY;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,KAAG,MAOjF,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,eAAe,aAAc,MAAM,UAAU,MAAM,KAAG,MAAM,GAAG,SAI3E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,aACtB,MAAM,eACH,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,KACpD,MAAM,GAAG,SAOX,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,aACpB,MAAM,UACR;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,eAC7B,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,gBACvC,MAAM,KACnB,MAAM,GAAG,SAOX,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,YAAa,MAAM,GAAG,SAAS,KAAG,MAG5D,CAAC"}
@@ -0,0 +1,67 @@
1
+ /** Strips the tag and digest from an image reference, leaving the repository. */
2
+ export const repositoryFromImageRef = (imageRef) => {
3
+ const withoutDigest = imageRef.includes("@")
4
+ ? imageRef.slice(0, imageRef.indexOf("@"))
5
+ : imageRef;
6
+ const tagSeparator = withoutDigest.lastIndexOf(":");
7
+ const pathSeparator = withoutDigest.lastIndexOf("/");
8
+ return tagSeparator > pathSeparator ? withoutDigest.slice(0, tagSeparator) : withoutDigest;
9
+ };
10
+ /**
11
+ * Prefixes an image reference with the registry host unless the reference
12
+ * already carries a registry prefix (a dotted host, a host:port, or `localhost`).
13
+ */
14
+ export const withRegistryHost = (imageRef, registry) => {
15
+ const registryHost = registry.server.replace(/\/$/, "");
16
+ const firstSegment = imageRef.split("/")[0];
17
+ const hasRegistryPrefix = imageRef.includes("/") &&
18
+ (firstSegment.includes(".") || firstSegment.includes(":") || firstSegment === "localhost");
19
+ return hasRegistryPrefix ? imageRef : `${registryHost}/${imageRef}`;
20
+ };
21
+ /** Extracts the `repository@sha256:...` digest from `docker push` output. */
22
+ export const parseRepoDigest = (imageRef, output) => {
23
+ const match = /digest:\s+([a-z0-9]+:[a-f0-9]{64})/i.exec(output);
24
+ if (!match)
25
+ return undefined;
26
+ return `${repositoryFromImageRef(imageRef)}@${match[1]}`;
27
+ };
28
+ /**
29
+ * Picks the `repository@digest` that inspect reports for this image.
30
+ *
31
+ * A fallback when push output has no `digest:` line. Podman pushes report the
32
+ * published digest through `--digestfile` instead, because Podman can list a
33
+ * pulled image's source digest under the pushed name here.
34
+ */
35
+ export const repoDigestFromInspect = (imageRef, repoDigests) => {
36
+ const repository = repositoryFromImageRef(imageRef);
37
+ for (const digest of repoDigests ?? []) {
38
+ const at = digest.lastIndexOf("@");
39
+ if (at > 0 && digest.slice(0, at) === repository)
40
+ return digest;
41
+ }
42
+ return undefined;
43
+ };
44
+ /**
45
+ * Digest from push output when the engine prints one, otherwise the matching
46
+ * `RepoDigests` entry. `registryRef` is the host-qualified name when it
47
+ * differs from the local reference that was pushed.
48
+ */
49
+ export const publishedRepoDigest = (imageRef, output, repoDigests, registryRef) => {
50
+ const text = `${output.stdout}\n${output.stderr}`;
51
+ return (parseRepoDigest(imageRef, text) ??
52
+ (registryRef === undefined ? undefined : repoDigestFromInspect(registryRef, repoDigests)) ??
53
+ repoDigestFromInspect(imageRef, repoDigests));
54
+ };
55
+ /**
56
+ * Parses an image's RFC 3339 `Created` timestamp into epoch milliseconds.
57
+ *
58
+ * Docker only reports `Created` when the image config carries a creation time:
59
+ * API >= 1.44 omits it, older APIs backfill the year-1 zero value
60
+ * (`0001-01-01T00:00:00Z`), and 25.0.0–25.0.3 returned an empty string. In any
61
+ * of those cases there is no real build time, so we fall back to the wall clock.
62
+ */
63
+ export const parseCreatedAt = (created) => {
64
+ const parsed = created ? Date.parse(created) : Number.NaN;
65
+ return Number.isNaN(parsed) || parsed <= 0 ? Date.now() : parsed;
66
+ };
67
+ //# sourceMappingURL=Registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Registry.js","sourceRoot":"","sources":["../src/Registry.ts"],"names":[],"mappings":"AAWA,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAU,EAAE;IACjE,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC1C,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,CAAC,CAAC,QAAQ,CAAC;IACb,MAAM,YAAY,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACrD,OAAO,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;AAC7F,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAE,QAA4B,EAAU,EAAE;IACzF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,iBAAiB,GACrB,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QACtB,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC;IAC7F,OAAO,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,YAAY,IAAI,QAAQ,EAAE,CAAC;AACtE,CAAC,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAE,MAAc,EAAsB,EAAE;IACtF,MAAM,KAAK,GAAG,qCAAqC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,GAAG,sBAAsB,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3D,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,QAAgB,EAChB,WAAqD,EACjC,EAAE;IACtB,MAAM,UAAU,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,MAAM,MAAM,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,UAAU;YAAE,OAAO,MAAM,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,QAAgB,EAChB,MAA0C,EAC1C,WAAqD,EACrD,WAAoB,EACA,EAAE;IACtB,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;IAClD,OAAO,CACL,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC;QAC/B,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACzF,qBAAqB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAC7C,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAA2B,EAAU,EAAE;IACpE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC1D,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AACnE,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { PlatformError } from "effect/PlatformError";
2
+ declare const DockerRegistryBlobUnknown_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
+ readonly _tag: "DockerRegistryBlobUnknown";
4
+ } & Readonly<A>;
5
+ export declare class DockerRegistryBlobUnknown extends DockerRegistryBlobUnknown_base<{
6
+ readonly cause: PlatformError;
7
+ }> {
8
+ get message(): string;
9
+ }
10
+ declare const DockerRegistryUnavailable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
11
+ readonly _tag: "DockerRegistryUnavailable";
12
+ } & Readonly<A>;
13
+ export declare class DockerRegistryUnavailable extends DockerRegistryUnavailable_base<{
14
+ readonly cause: PlatformError;
15
+ }> {
16
+ get message(): string;
17
+ }
18
+ export type DockerImagePublicationError = PlatformError | DockerRegistryBlobUnknown | DockerRegistryUnavailable;
19
+ export declare const classifyDockerRegistryError: (error: PlatformError) => DockerImagePublicationError;
20
+ export {};
21
+ //# sourceMappingURL=RegistryError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RegistryError.d.ts","sourceRoot":"","sources":["../src/RegistryError.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;;;;AAE1D,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B,CAAC;IACA,IAAa,OAAO,WAEnB;CACF;;;;AAED,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B,CAAC;IACA,IAAa,OAAO,WAEnB;CACF;AAED,MAAM,MAAM,2BAA2B,GACnC,aAAa,GACb,yBAAyB,GACzB,yBAAyB,CAAC;AAE9B,eAAO,MAAM,2BAA2B,UAAW,aAAa,KAAG,2BAuBlE,CAAC"}
@@ -0,0 +1,28 @@
1
+ import * as Data from "effect/Data";
2
+ export class DockerRegistryBlobUnknown extends Data.TaggedError("DockerRegistryBlobUnknown") {
3
+ get message() {
4
+ return this.cause.message;
5
+ }
6
+ }
7
+ export class DockerRegistryUnavailable extends Data.TaggedError("DockerRegistryUnavailable") {
8
+ get message() {
9
+ return this.cause.message;
10
+ }
11
+ }
12
+ export const classifyDockerRegistryError = (error) => {
13
+ // Build logs can contain arbitrary Dockerfile output before the final error.
14
+ const message = error.reason.description
15
+ ?.split("\n")
16
+ .map((line) => line.trim())
17
+ .filter((line) => line.length > 0 &&
18
+ !line.startsWith("View build details: docker-desktop://dashboard/build/"))
19
+ .at(-1) ?? "";
20
+ if (/(?:^|:\s*)(?:unknown:\s*)?blob unknown to registry$/i.test(message)) {
21
+ return new DockerRegistryBlobUnknown({ cause: error });
22
+ }
23
+ if (/(?:unexpected (?:http )?status(?:[^\n]*:\s*|\s+)5\d\d(?:\s|$)|(?:^|:\s*)(?:500 internal server error|502 bad gateway|503 service unavailable|504 gateway timeout)$)/i.test(message)) {
24
+ return new DockerRegistryUnavailable({ cause: error });
25
+ }
26
+ return error;
27
+ };
28
+ //# sourceMappingURL=RegistryError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RegistryError.js","sourceRoot":"","sources":["../src/RegistryError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,yBAA0B,SAAQ,IAAI,CAAC,WAAW,CAAC,2BAA2B,CAEzF;IACA,IAAa,OAAO;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,IAAI,CAAC,WAAW,CAAC,2BAA2B,CAEzF;IACA,IAAa,OAAO;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;IAC5B,CAAC;CACF;AAOD,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,KAAoB,EAA+B,EAAE;IAC/F,6EAA6E;IAC7E,MAAM,OAAO,GACX,KAAK,CAAC,MAAM,CAAC,WAAW;QACtB,EAAE,KAAK,CAAC,IAAI,CAAC;SACZ,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CACL,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,IAAI,CAAC,UAAU,CAAC,uDAAuD,CAAC,CAC5E;SACA,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClB,IAAI,sDAAsD,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,yBAAyB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,IACE,sKAAsK,CAAC,IAAI,CACzK,OAAO,CACR,EACD,CAAC;QACD,OAAO,IAAI,yBAAyB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -0,0 +1,118 @@
1
+ import * as Provider from "alchemy/Provider";
2
+ import { Resource } from "alchemy/Resource";
3
+ import { Docker } from "./Docker.ts";
4
+ import type { Providers } from "./Providers.ts";
5
+ import { type ImageRegistry } from "./Registry.ts";
6
+ export interface RemoteImageProps {
7
+ /** Docker image name to use as the source, without tag. */
8
+ name: string;
9
+ /** Docker source image tag. @default "latest" */
10
+ tag?: string;
11
+ /** Pull for this platform when the source is not already local or alwaysPull is enabled. */
12
+ platform?: string;
13
+ /**
14
+ * Always pull the source image, even when that reference exists locally.
15
+ *
16
+ * @default true
17
+ */
18
+ alwaysPull?: boolean;
19
+ /**
20
+ * Re-tag the pulled image under this repository/name. When omitted the pulled
21
+ * `name` is kept.
22
+ */
23
+ targetName?: string;
24
+ /**
25
+ * Tag applied to the re-tagged image.
26
+ *
27
+ * @default The pulled `tag`.
28
+ */
29
+ targetTag?: string;
30
+ /** Registry credentials. When set, the (re-tagged) image is pushed. */
31
+ registry?: ImageRegistry;
32
+ /**
33
+ * Skip registry push even when `registry` is set.
34
+ *
35
+ * @default false
36
+ */
37
+ skipPush?: boolean;
38
+ /** Docker context name or context resource. */
39
+ context?: Docker.ContextRef;
40
+ }
41
+ export interface RemoteImage extends Resource<"Docker.RemoteImage", RemoteImageProps, {
42
+ /** Final image reference. Includes the registry host when pushed there. */
43
+ imageRef: string;
44
+ /** Local image id after reuse or pull. */
45
+ imageId: string;
46
+ /** Source image id used for the last reconcile, preserved across refresh. */
47
+ sourceImageId?: string;
48
+ /** Pull timestamp in milliseconds since epoch. */
49
+ createdAt: number;
50
+ /** Final image repository/name. */
51
+ name: string;
52
+ /** Final image tag. */
53
+ tag: string;
54
+ /** Registry digest after push when available. */
55
+ repoDigest?: string;
56
+ }, never, Providers> {
57
+ }
58
+ /**
59
+ * Makes a source Docker image available through the active Docker context,
60
+ * optionally re-tagging it and pushing it to a registry.
61
+ *
62
+ * The image is available to other Docker resources by `imageRef`. Use
63
+ * `alwaysPull: false` reuses the source tag when it exists in the configured
64
+ * Docker daemon, pulling only when it is missing. Set `targetName`/`targetTag`
65
+ * to re-tag the source image, and `registry` to push it (mirroring it from a
66
+ * source registry into your own, for example).
67
+ *
68
+ *
69
+ * ### Pulling Images
70
+ * **Example:** Pull nginx
71
+ * ```typescript
72
+ * const nginx = yield* Docker.RemoteImage("nginx", {
73
+ * name: "nginx",
74
+ * tag: "alpine",
75
+ * });
76
+ * ```
77
+ *
78
+ * **Example:** Reuse an existing daemon tag
79
+ * ```typescript
80
+ * const postgres = yield* Docker.RemoteImage("postgres", {
81
+ * name: "postgres",
82
+ * tag: "18-alpine",
83
+ * alwaysPull: false,
84
+ * });
85
+ * ```
86
+ *
87
+ * ### Re-tagging and Pushing
88
+ * **Example:** Mirror a public image into your registry
89
+ * ```typescript
90
+ * const mirrored = yield* Docker.RemoteImage("nginx-mirror", {
91
+ * name: "nginx",
92
+ * tag: "alpine",
93
+ * targetName: "acme/nginx",
94
+ * targetTag: "alpine",
95
+ * registry: {
96
+ * server: "ghcr.io",
97
+ * username: "octocat",
98
+ * password: Config.Redacted("GITHUB_TOKEN"),
99
+ * },
100
+ * });
101
+ * ```
102
+ *
103
+ * ### Docker Context
104
+ * **Example:** Pull through a named Docker context
105
+ * ```typescript
106
+ * const nginx = yield* Docker.RemoteImage("nginx", {
107
+ * name: "nginx",
108
+ * tag: "alpine",
109
+ * context: "remote-build",
110
+ * });
111
+ * ```
112
+ *
113
+ * @resource
114
+ * @product Image
115
+ */
116
+ export declare const RemoteImage: import("alchemy/Resource").ResourceClass<RemoteImage>;
117
+ export declare const RemoteImageProvider: () => import("effect/Layer").Layer<Provider.Provider<RemoteImage>, never, Docker>;
118
+ //# sourceMappingURL=RemoteImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteImage.d.ts","sourceRoot":"","sources":["../src/RemoteImage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EACL,KAAK,aAAa,EAKnB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,oBAAoB,EACpB,gBAAgB,EAChB;IACE,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,eAAO,MAAM,WAAW,uDAA8C,CAAC;AAEvE,eAAO,MAAM,mBAAmB,mFAqG7B,CAAC"}
@@ -0,0 +1,164 @@
1
+ import { isResolved } from "alchemy/Diff";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import * as Effect from "effect/Effect";
5
+ import { Docker, dockerContextName } from "./Docker.js";
6
+ import { parseCreatedAt, publishedRepoDigest, repositoryFromImageRef, withRegistryHost, } from "./Registry.js";
7
+ /**
8
+ * Makes a source Docker image available through the active Docker context,
9
+ * optionally re-tagging it and pushing it to a registry.
10
+ *
11
+ * The image is available to other Docker resources by `imageRef`. Use
12
+ * `alwaysPull: false` reuses the source tag when it exists in the configured
13
+ * Docker daemon, pulling only when it is missing. Set `targetName`/`targetTag`
14
+ * to re-tag the source image, and `registry` to push it (mirroring it from a
15
+ * source registry into your own, for example).
16
+ *
17
+ *
18
+ * ### Pulling Images
19
+ * **Example:** Pull nginx
20
+ * ```typescript
21
+ * const nginx = yield* Docker.RemoteImage("nginx", {
22
+ * name: "nginx",
23
+ * tag: "alpine",
24
+ * });
25
+ * ```
26
+ *
27
+ * **Example:** Reuse an existing daemon tag
28
+ * ```typescript
29
+ * const postgres = yield* Docker.RemoteImage("postgres", {
30
+ * name: "postgres",
31
+ * tag: "18-alpine",
32
+ * alwaysPull: false,
33
+ * });
34
+ * ```
35
+ *
36
+ * ### Re-tagging and Pushing
37
+ * **Example:** Mirror a public image into your registry
38
+ * ```typescript
39
+ * const mirrored = yield* Docker.RemoteImage("nginx-mirror", {
40
+ * name: "nginx",
41
+ * tag: "alpine",
42
+ * targetName: "acme/nginx",
43
+ * targetTag: "alpine",
44
+ * registry: {
45
+ * server: "ghcr.io",
46
+ * username: "octocat",
47
+ * password: Config.Redacted("GITHUB_TOKEN"),
48
+ * },
49
+ * });
50
+ * ```
51
+ *
52
+ * ### Docker Context
53
+ * **Example:** Pull through a named Docker context
54
+ * ```typescript
55
+ * const nginx = yield* Docker.RemoteImage("nginx", {
56
+ * name: "nginx",
57
+ * tag: "alpine",
58
+ * context: "remote-build",
59
+ * });
60
+ * ```
61
+ *
62
+ * @resource
63
+ * @product Image
64
+ */
65
+ export const RemoteImage = Resource("Docker.RemoteImage");
66
+ export const RemoteImageProvider = () => Provider.effect(RemoteImage, Effect.gen(function* () {
67
+ const docker = yield* Docker;
68
+ const inspectImage = (ref, context) => docker.image
69
+ .inspect(ref, context)
70
+ .pipe(Effect.catchIf(isMissingImageError, () => Effect.succeed(undefined)));
71
+ return RemoteImage.Provider.of({
72
+ list: () => Effect.succeed([]),
73
+ read: Effect.fn(function* ({ olds, output }) {
74
+ const context = dockerContextName(olds.context);
75
+ const ref = output?.imageRef ?? targetImageRef(olds);
76
+ return yield* inspectImage(ref, context).pipe(Effect.map((image) => image
77
+ ? {
78
+ imageRef: ref,
79
+ imageId: image.Id,
80
+ createdAt: output?.createdAt ?? parseCreatedAt(image.Created),
81
+ sourceImageId: output?.sourceImageId ?? output?.imageId ?? image.Id,
82
+ name: output?.name ?? repositoryFromImageRef(ref),
83
+ tag: output?.tag ?? targetTag(olds),
84
+ repoDigest: output?.repoDigest,
85
+ }
86
+ : undefined));
87
+ }),
88
+ diff: Effect.fn(function* ({ output, news, olds }) {
89
+ if (!isResolved(news))
90
+ return undefined;
91
+ if (dockerContextName(olds.context) !== dockerContextName(news.context) ||
92
+ !output ||
93
+ news.alwaysPull !== false ||
94
+ output.imageRef !== targetImageRef(news)) {
95
+ return { action: "update" };
96
+ }
97
+ const source = yield* inspectImage(remoteImageRef(news), dockerContextName(news.context));
98
+ const target = yield* inspectImage(targetImageRef(news), dockerContextName(news.context));
99
+ if (!source ||
100
+ !target ||
101
+ source.Id !== (output.sourceImageId ?? output.imageId) ||
102
+ source.Id !== target.Id) {
103
+ return { action: "update" };
104
+ }
105
+ }),
106
+ reconcile: Effect.fn(function* ({ news, session }) {
107
+ const context = dockerContextName(news.context);
108
+ const sourceRef = remoteImageRef(news);
109
+ let source;
110
+ if (news.alwaysPull === false) {
111
+ source = yield* inspectImage(sourceRef, context);
112
+ if (source) {
113
+ yield* session.note(`Reusing local Docker image: ${sourceRef}`);
114
+ }
115
+ }
116
+ if (!source) {
117
+ yield* session.note(`Pulling Docker image: ${sourceRef}`);
118
+ yield* docker.image.pull(sourceRef, news.platform, context);
119
+ source = yield* docker.image.inspect(sourceRef, context);
120
+ }
121
+ const finalRef = targetImageRef(news);
122
+ if (finalRef !== sourceRef || (news.registry && !news.skipPush)) {
123
+ yield* session.note(`Tagging Docker image: ${source.Id} -> ${finalRef}`);
124
+ yield* docker.image.tag(source.Id, finalRef, context);
125
+ }
126
+ let pushed;
127
+ if (news.registry && !news.skipPush) {
128
+ yield* session.note(`Pushing image to registry "${news.registry.server}"`);
129
+ pushed = yield* docker.image.push(finalRef, news.registry, undefined, context);
130
+ }
131
+ const inspected = yield* docker.image.inspect(finalRef, context);
132
+ const repoDigest = pushed === undefined
133
+ ? undefined
134
+ : publishedRepoDigest(finalRef, pushed, inspected.RepoDigests);
135
+ return {
136
+ imageRef: finalRef,
137
+ imageId: inspected.Id,
138
+ sourceImageId: source.Id,
139
+ createdAt: parseCreatedAt(inspected.Created),
140
+ name: repositoryFromImageRef(finalRef),
141
+ tag: targetTag(news),
142
+ repoDigest,
143
+ };
144
+ }),
145
+ delete: Effect.fn(function* () {
146
+ // Remote images are not removed on destroy because tags may be shared by
147
+ // unrelated local stacks or developer workflows.
148
+ }),
149
+ });
150
+ }));
151
+ /** The reference the image is pulled from. */
152
+ const remoteImageRef = (props) => `${props.name}:${props.tag ?? "latest"}`;
153
+ const isMissingImageError = (error) => error.reason._tag === "NotFound" &&
154
+ /no such image|image not known|image not found/i.test(error.reason.description ?? "");
155
+ const targetTag = (props) => props.targetTag ?? props.tag ?? "latest";
156
+ /**
157
+ * The final reference after re-tagging and registry-host prefixing. Equals the
158
+ * pulled reference when no re-tag/registry is configured.
159
+ */
160
+ const targetImageRef = (props) => {
161
+ const local = `${props.targetName ?? props.name}:${targetTag(props)}`;
162
+ return props.registry && !props.skipPush ? withRegistryHost(local, props.registry) : local;
163
+ };
164
+ //# sourceMappingURL=RemoteImage.js.map