docker-devkit 0.2.0.dev37277620738__py3-none-any.whl → 0.2.1.dev37485576216__py3-none-any.whl

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.
@@ -6,7 +6,8 @@ import platform
6
6
  import re
7
7
  from pathlib import Path
8
8
  from subprocess import CalledProcessError
9
- from typing import Annotated, Any, Literal
9
+ from tempfile import TemporaryDirectory
10
+ from typing import Annotated, Any, Literal, TypedDict
10
11
 
11
12
  import typer
12
13
  from bashrun.bash import bash, bash_output
@@ -32,7 +33,16 @@ METADATA_PATH = Path("metadata.json")
32
33
 
33
34
  Mode = Literal["local", "ci"]
34
35
 
35
- IMAGES_LOCK_PLATFORM = "images-lock"
36
+ DIGEST_PROJECT = "images-digests"
37
+ DIGEST_PLATFORM = "all"
38
+ DIGEST_FILE_NAME = "images-digests.json"
39
+
40
+
41
+ class DigestEntry(TypedDict):
42
+ ref: str
43
+ digest: str
44
+ tags: list[str]
45
+
36
46
 
37
47
  app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
38
48
 
@@ -55,14 +65,12 @@ def build(
55
65
  ] = None,
56
66
  builds_registry: Annotated[
57
67
  str | None,
58
- typer.Option("--builds-registry", help="OCI registry path to push the resolved lock to (CI build artifacts)."),
59
- ] = None,
60
- lock_project: Annotated[
61
- str | None,
62
- typer.Option("--lock-project", help="Project component of the lock's builds-shelf address (CI only)."),
68
+ typer.Option(
69
+ "--builds-registry", help="OCI registry path to push the digest manifest to (CI build artifacts)."
70
+ ),
63
71
  ] = None,
64
72
  run_number: Annotated[
65
- int, typer.Option("--run-number", help="CI run number baked into the lock's builds-shelf tag.")
73
+ int, typer.Option("--run-number", help="CI run number baked into the digest manifest's builds-shelf tag.")
66
74
  ] = 0,
67
75
  ) -> None:
68
76
  run_build(
@@ -74,7 +82,6 @@ def build(
74
82
  no_cache=no_cache,
75
83
  targets_opt=targets_opt,
76
84
  builds_registry=builds_registry,
77
- lock_project=lock_project,
78
85
  run_number=run_number,
79
86
  )
80
87
 
@@ -90,7 +97,6 @@ def run_build(
90
97
  targets_opt: list[str] | None = None,
91
98
  env_overlay: dict[str, str] | None = None,
92
99
  builds_registry: str | None = None,
93
- lock_project: str | None = None,
94
100
  run_number: int = 0,
95
101
  ) -> None:
96
102
  root = Path.cwd()
@@ -223,22 +229,50 @@ def run_build(
223
229
  if not set(targets) <= baked_images.keys():
224
230
  raise RuntimeError("Bake output does not cover the requested targets")
225
231
 
226
- push_images_lock(builds_registry, lock_project, run_number, lock_file)
232
+ push_image_digests(builds_registry, run_number, baked_images, targets)
227
233
 
228
234
 
229
- def push_images_lock(builds_registry: str | None, lock_project: str | None, run_number: int, lock_file: Path) -> None:
235
+ def push_image_digests(
236
+ builds_registry: str | None,
237
+ run_number: int,
238
+ baked_images: dict[str, Any],
239
+ targets: list[str],
240
+ ) -> None:
230
241
  if builds_registry is None:
231
242
  return
232
- if not lock_project or run_number <= 0:
233
- raise typer.BadParameter("--builds-registry requires --lock-project and a positive --run-number")
234
- push_build(
235
- builds_registry,
236
- lock_project,
237
- IMAGES_LOCK_PLATFORM,
238
- f"run-{run_number}",
239
- lock_file.parent,
240
- [lock_file.name],
241
- )
243
+ if run_number <= 0:
244
+ raise typer.BadParameter("--builds-registry requires a positive --run-number")
245
+ manifest = distill_digest_manifest(baked_images, targets)
246
+ with TemporaryDirectory(prefix="digest-manifest-") as staging:
247
+ manifest_path = Path(staging) / DIGEST_FILE_NAME
248
+ manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
249
+ push_build(
250
+ builds_registry,
251
+ DIGEST_PROJECT,
252
+ DIGEST_PLATFORM,
253
+ f"run-{run_number}",
254
+ Path(staging),
255
+ [DIGEST_FILE_NAME],
256
+ )
257
+
258
+
259
+ def distill_digest_manifest(baked_images: dict[str, Any], targets: list[str]) -> dict[str, DigestEntry]:
260
+ manifest: dict[str, DigestEntry] = {}
261
+ for target in targets:
262
+ entry = baked_images[target]
263
+ image_name = entry["image.name"]
264
+ digest = entry["containerimage.digest"]
265
+ refs = [reference.strip() for reference in image_name.split(",") if reference.strip()]
266
+ image_ref = ""
267
+ tags: list[str] = []
268
+ for reference in refs:
269
+ reference = reference.split("@", 1)[0]
270
+ repository, separator, tag = reference.rpartition(":")
271
+ if separator:
272
+ image_ref = repository
273
+ tags.append(tag)
274
+ manifest[target] = {"ref": image_ref, "digest": digest, "tags": tags}
275
+ return manifest
242
276
 
243
277
 
244
278
  # Vibe code - Gemini 3
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: docker-devkit
3
- Version: 0.2.0.dev37277620738
3
+ Version: 0.2.1.dev37485576216
4
4
  Summary: Docker-stack lifecycle commands (up/down/build) driven by a pyproject-declared compose assembly
5
5
  License-File: LICENSE
6
6
  License-File: NOTICE
7
7
  Requires-Python: >=3.13
8
8
  Requires-Dist: bashrun>=0.1.0
9
- Requires-Dist: ci-devkit==0.1.15.dev36756330648
9
+ Requires-Dist: ci-devkit>=0.1.15
10
10
  Requires-Dist: packaging>=24.1
11
11
  Requires-Dist: pathspec>=1.0.4
12
12
  Requires-Dist: pydantic-settings>=2.9.1
@@ -1,5 +1,5 @@
1
1
  docker_devkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- docker_devkit/build_docker.py,sha256=g6KPTOp5TDyG550Zun_XNpWH2D19arh-PaAjgBoSyhU,11685
2
+ docker_devkit/build_docker.py,sha256=wyIvfh5v-WarKa7XCanWxhMWQUPiBBPrQJxzALtnTQ0,12748
3
3
  docker_devkit/config.py,sha256=UMAS6yF1E9PIbsRs2TVig9XwSNCvQe5w9eTLUHOPKq0,3112
4
4
  docker_devkit/context_sha.py,sha256=lhCxUcYM98xCdDum3hsFRFw5WYrjvZUSMbOu_7ZmQjE,3041
5
5
  docker_devkit/detect_gpu.py,sha256=C4e3vixvSSJS6xQiMVWp2IdWSAgMr551KrBTuIrkTk4,456
@@ -13,9 +13,9 @@ docker_devkit/modes.py,sha256=Lwhblzu5Kn7qhfy_QMqE28XuflSIJtCRBYNX7Iq8Qfo,1510
13
13
  docker_devkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  docker_devkit/score_generate.py,sha256=psbQm-MWf9A5YP0ov0rLPUiuVAQbUWn6yfUmB-CXurU,9434
15
15
  docker_devkit/up.py,sha256=32A2_6mxAF00XILecxYTzCQw0QvSjevBWCnayF5X1Xs,4284
16
- docker_devkit-0.2.0.dev37277620738.dist-info/METADATA,sha256=58BItOxc6Me7YTdcb9SeFGSi5BNxEuLKvu9bJiH-XLU,545
17
- docker_devkit-0.2.0.dev37277620738.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
18
- docker_devkit-0.2.0.dev37277620738.dist-info/entry_points.txt,sha256=6cAHqlMvuXI00uTJLf73MTqWnLd1-m5sDgJrSyHWv4U,244
19
- docker_devkit-0.2.0.dev37277620738.dist-info/licenses/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
20
- docker_devkit-0.2.0.dev37277620738.dist-info/licenses/NOTICE,sha256=jvglZza3GCdN7v4_TLuXUqX9C_uABlwAKUkcIeKwk_8,26
21
- docker_devkit-0.2.0.dev37277620738.dist-info/RECORD,,
16
+ docker_devkit-0.2.1.dev37485576216.dist-info/METADATA,sha256=Czxw-O5SIfhkg2ST2JPcC6a-7pfKbj3n682oywKLtJ4,530
17
+ docker_devkit-0.2.1.dev37485576216.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
18
+ docker_devkit-0.2.1.dev37485576216.dist-info/entry_points.txt,sha256=6cAHqlMvuXI00uTJLf73MTqWnLd1-m5sDgJrSyHWv4U,244
19
+ docker_devkit-0.2.1.dev37485576216.dist-info/licenses/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
20
+ docker_devkit-0.2.1.dev37485576216.dist-info/licenses/NOTICE,sha256=jvglZza3GCdN7v4_TLuXUqX9C_uABlwAKUkcIeKwk_8,26
21
+ docker_devkit-0.2.1.dev37485576216.dist-info/RECORD,,