Skip to content

Commit ca12ab7

Browse files
Address PR review comments:
- Fix _build_platform_image docstring (timestamp applies to all commits) - Remove unused arch param from _publish_single_arch - Skip publish recap when combined image already exists - Fix copy() docstring: refs don't include docker:// prefix - Fix blob lookup order: try verbatim digest first - Forward registry auth env vars into release container - Update README: replace crane references with buildah/skopeo Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
1 parent d0aff9d commit ca12ab7

4 files changed

Lines changed: 31 additions & 16 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ Each artifact file is pushed as its own OCI layer. Deterministic tar creation (z
111111
All three images are multi-arch OCI indexes with `linux/amd64` and `linux/arm64` platform entries pointing to the same content, so any platform can pull them. Images are compatible with:
112112
113113
- **containerd** — valid `rootfs.diff_ids` in the config; Kubernetes image-volume mounts work
114-
- **crane export** — extracts individual artifact files for release workflows
114+
- **skopeo** — extracts individual artifact files for release workflows
115115
116116
### GitHub Release
117117
@@ -184,7 +184,8 @@ Each stage can be executed in one of three modes:
184184
│ ├── tools.py # Binary tool downloader
185185
│ ├── artifacts.py # Artifact collection & checksums
186186
│ ├── oci.py # OCI artifact publish/pull/tag
187-
│ ├── crane.py # crane CLI wrapper
187+
│ ├── buildah.py # buildah CLI wrapper (image construction)
188+
│ ├── skopeo.py # skopeo CLI wrapper (inspect/copy/export)
188189
│ ├── iso.py # ISO image assembly
189190
│ ├── qemu.py # QEMU boot testing
190191
│ ├── log.py # Colored logging

captain/docker.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ def run_in_release(cfg: Config, *extra_args: str) -> None:
119119
"-e",
120120
"BUILDAH_ISOLATION=chroot",
121121
]
122+
# Forward host registry credentials so buildah/skopeo can authenticate.
123+
# The caller sets these env vars on the host (e.g. via docker login or
124+
# CI secrets); they are passed through to the container as-is.
125+
for var in ("REGISTRY_AUTH_FILE", "REGISTRY_USERNAME", "REGISTRY_PASSWORD"):
126+
val = os.environ.get(var)
127+
if val:
128+
docker_args += ["-e", f"{var}={val}"]
122129
docker_args.extend(extra_args)
123130
run(docker_args)
124131

captain/oci.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ def _build_platform_image(
161161
) -> str:
162162
"""Build an OCI image locally for *platform*.
163163
164-
Each tar becomes its own OCI layer (add → commit cycle). Only the
165-
final commit carries the image metadata and timestamp.
164+
Each tar becomes its own OCI layer (add → commit cycle). All commits
165+
use the same fixed timestamp; only the final commit carries the image
166+
metadata.
166167
167168
*base* is the starting image — ``"scratch"`` for a new image, or a
168169
``docker://`` ref to extend an existing registry image. When a
@@ -207,7 +208,6 @@ def _build_platform_image(
207208

208209
def _publish_single_arch(
209210
*,
210-
arch: str,
211211
layer_tars: list[Path],
212212
ref: str,
213213
tag: str,
@@ -220,7 +220,7 @@ def _publish_single_arch(
220220
"""Build a per-arch multi-arch index and push it.
221221
222222
Both platform entries (linux/amd64 and linux/arm64) carry the same
223-
4 layers — only the artifacts for *arch*.
223+
4 layers.
224224
"""
225225
image_ids: list[str] = []
226226
for platform_arch in _ARCHES:
@@ -253,7 +253,7 @@ def _publish_combined(
253253
created: str,
254254
force: bool = False,
255255
logger: StageLogger,
256-
) -> None:
256+
) -> bool:
257257
"""Build and push the combined multi-arch image.
258258
259259
Each platform manifest has the native arch's layers first, then the
@@ -272,7 +272,7 @@ def _publish_combined(
272272
# Skip if the combined image already exists.
273273
if not force and skopeo.image_exists(combined_ref, logger=logger):
274274
logger.log(f"{combined_ref} already exists — skipping (use --force to overwrite)")
275-
return
275+
return False
276276

277277
# Ensure per-arch images exist in the registry.
278278
for arch in _ARCHES:
@@ -285,7 +285,6 @@ def _publish_combined(
285285
f"{per_arch_ref} not found in registry — building and pushing before combined image"
286286
)
287287
_publish_single_arch(
288-
arch=arch,
289288
layer_tars=arch_layer_tars[arch],
290289
ref=per_arch_ref,
291290
tag=per_arch_tag,
@@ -319,6 +318,7 @@ def _publish_combined(
319318
for image_id in image_ids:
320319
buildah.manifest_add(manifest_id, image_id, logger=logger)
321320
buildah.manifest_push(manifest_id, combined_ref, logger=logger)
321+
return True
322322

323323

324324
def publish(
@@ -375,9 +375,10 @@ def publish(
375375
for arch, files in arch_files.items():
376376
arch_layer_tars[arch] = [_deterministic_tar(f, out) for f in files]
377377

378+
pushed = True
378379
try:
379380
if target == "combined":
380-
_publish_combined(
381+
pushed = _publish_combined(
381382
arch_layer_tars=arch_layer_tars,
382383
registry=registry,
383384
repository=repository,
@@ -390,7 +391,6 @@ def publish(
390391
)
391392
else:
392393
_publish_single_arch(
393-
arch=target,
394394
layer_tars=arch_layer_tars[target],
395395
ref=final_ref,
396396
tag=full_tag,
@@ -405,6 +405,9 @@ def publish(
405405
for t in tars:
406406
t.unlink(missing_ok=True)
407407

408+
if not pushed:
409+
return
410+
408411
# Recap
409412
artifact_names: list[str] = []
410413
for arch in arches:

captain/skopeo.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,12 @@ def copy(
6060
*,
6161
logger: StageLogger | None = None,
6262
) -> None:
63-
"""Copy an image from *src* to *dest* (both ``docker://`` refs).
63+
"""Copy an image from *src* to *dest*.
6464
65-
Typically used for retagging: the source and destination differ only
66-
in the tag component.
65+
*src* and *dest* are plain image references (e.g.
66+
``ghcr.io/org/repo:tag``); the ``docker://`` transport prefix is
67+
added automatically. Typically used for retagging: the source and
68+
destination differ only in the tag component.
6769
"""
6870
_log = logger or _default_log
6971
_log.log(f"skopeo copy {src}{dest}")
@@ -126,8 +128,10 @@ def export_image(
126128

127129
for layer in layers:
128130
digest_str = layer["digest"] # e.g. "sha256:abc123..."
129-
blob_file = tmp_dir / digest_str.replace(":", "-")
130-
# Fallback: some skopeo versions use just the hash
131+
# skopeo stores blobs under several possible filenames.
132+
blob_file = tmp_dir / digest_str
133+
if not blob_file.exists():
134+
blob_file = tmp_dir / digest_str.replace(":", "-")
131135
if not blob_file.exists():
132136
blob_file = tmp_dir / digest_str.split(":")[-1]
133137
if not blob_file.exists():

0 commit comments

Comments
 (0)