@@ -58,7 +58,24 @@ utils.printheader("Pushing container images %s" % list(container_images.keys()))
script = [
"set -e",
"test -w /dev/kvm || { echo 'ERROR: /dev/kvm is not writable, cannot push containers'; exit 1; }",
- # Always bring up a fresh memres VM in the foreground.
+ # Two push paths share this script:
+ #
+ # - Single-arch images (the common case) are imported into
+ # a vdkr/vpdmn memres VM and pushed from inside it. The
+ # VM bring-up is expensive and needs /dev/kvm, so it is
+ # wrapped in start_vm() and only runs when the first
+ # single-arch recipe is pushed.
+ #
+ # - Multi-arch images (recipes inheriting oci-multiarch)
+ # are pushed with skopeo-native straight from the OCI
+ # Image Index layout on disk — no VM, no /dev/kvm.
+ # skopeo's recipe sysroot is populated on first use by
+ # prepare_skopeo(). Detection is per-recipe at runtime:
+ # 'bitbake -e <recipe>' only contains OCI_MULTIARCH_OUTPUT
+ # when the recipe inherits oci-multiarch, and its value is
+ # the exact OCI layout path to push.
+ #
+ # Notes on the memres VM bring-up in start_vm():
#
# 'memres status' only checks that the QEMU PID in daemon.pid
# is alive (see daemon_is_running()/daemon_status() in
@@ -92,8 +109,20 @@ script = [
# memres daemon is running, vcontainer-common.sh dispatches
# login via '--daemon-interactive' and blocks reading the
# password from stdin (see login case in vcontainer-common.sh).
- "trap '%s-$(arch) memres stop 2>/dev/null || true' EXIT" % runtime,
- "%s-$(arch) --config %s memres restart </dev/null" % (runtime, auth_config),
+ "_VM_STARTED=0",
+ "start_vm() {",
+ " if [ \"$_VM_STARTED\" = 1 ]; then return 0; fi",
+ " test -w /dev/kvm || { echo 'ERROR: /dev/kvm is not writable, cannot push containers'; exit 1; }",
+ " trap '%s-$(arch) memres stop 2>/dev/null || true' EXIT" % runtime,
+ " %s-$(arch) --config %s memres restart </dev/null" % (runtime, auth_config),
+ " _VM_STARTED=1",
+ "}",
+ "_SKOPEO_READY=0",
+ "prepare_skopeo() {",
+ " if [ \"$_SKOPEO_READY\" = 1 ]; then return 0; fi",
+ " bitbake skopeo-native -c addto_recipe_sysroot",
+ " _SKOPEO_READY=1",
+ "}",
]
tag_cmds = utils.getconfiglist("CONTAINER_TAG_CMDS", ourconfig, args.target, args.stepnum)
version_recipe = utils.getconfigvar("CONTAINER_VERSION_RECIPE", ourconfig, args.target, args.stepnum)
@@ -130,6 +159,10 @@ for recipe, image in container_images.items():
" *) _DISTRO_VERSION=\"$_DISTRO_VERSION_RAW\" ;;",
"esac",
"_DEPLOY_DIR_IMAGE=$(echo \"$_BBENV\" | awk -F'\"' '/^DEPLOY_DIR_IMAGE=/{ print $2; exit }')",
+ # Only set (non-empty) when the recipe inherits
+ # oci-multiarch; doubles as the multiarch marker and
+ # the OCI layout path for skopeo below.
+ "_OCI_MULTIARCH_OUTPUT=$(echo \"$_BBENV\" | awk -F'\"' '/^OCI_MULTIARCH_OUTPUT=/{ print $2; exit }')",
"_EXTRA_TAGS=\"\"",
]
if version_recipe:
@@ -151,29 +184,50 @@ for recipe, image in container_images.items():
script.append(
"_TAGS=\"%s $_PV $_DISTRO_CODENAME yocto-$_DISTRO_VERSION $_EXTRA_TAGS\"" % " ".join(static_tags)
)
- # Only push an image whose build actually produced an OCI artefact.
- # build-targets runs 'bitbake ... -k', so a failed image build does
- # not abort the build step or the other images, and the failure is
- # already reported there. If we let the push proceed, 'vimport' of
- # the missing ${recipe}-latest-oci would fail under 'set -e' and
- # abort the whole push step — taking down the images that *did*
- # build with it. So skip a missing image here (warn, don't fail)
- # and let the successfully-built ones publish.
- script += [
- "_OCI_IMAGE=${_DEPLOY_DIR_IMAGE}/%s-latest-oci" % recipe,
- "if [ ! -e \"$_OCI_IMAGE\" ]; then",
- " echo \"WARNING: %s did not build (no OCI image at $_OCI_IMAGE), skipping push\"" % recipe,
- "else",
- ]
+ # Detect multiarch vs single-arch per recipe and push accordingly,
+ # but only for an image whose build actually produced its OCI artefact.
+ #
+ # build-targets runs 'bitbake ... -k', so a failed image build does not
+ # abort the build step or the other images, and that failure is already
+ # reported there. If we pushed regardless, the missing artefact would make
+ # 'skopeo copy'/'vimport' fail under 'set -e' and abort the whole push
+ # step — taking the images that *did* build down with it. So skip an image
+ # whose artefact is missing (warn, don't fail) and let the
+ # successfully-built ones publish.
+ #
+ # No per-registry 'login': for the VM path, credentials are staged into
+ # the guest by '--config' on 'memres restart' (see start_vm); for the
+ # skopeo path, the same auth file is passed via --dest-authfile.
+ script.append("if [ -n \"$_OCI_MULTIARCH_OUTPUT\" ]; then")
+ # Multi-arch: push the OCI Image Index layout at $_OCI_MULTIARCH_OUTPUT
+ # straight from disk with skopeo-native (no VM, no /dev/kvm).
+ script.append(" if [ ! -e \"$_OCI_MULTIARCH_OUTPUT\" ]; then")
+ script.append(" echo \"WARNING: %s did not build (no OCI image at $_OCI_MULTIARCH_OUTPUT), skipping push\"" % recipe)
+ script.append(" else")
+ script.append(" prepare_skopeo")
+ for registry in registries:
+ script += [
+ " for _tag in $_TAGS; do",
+ " oe-run-native skopeo-native skopeo copy --all --dest-authfile %s oci:${_OCI_MULTIARCH_OUTPUT} docker://%s/%s:${_tag}" % (auth_config, registry, image),
+ " done",
+ ]
+ script.append(" fi")
+ script.append("else")
+ # Single-arch: import the ${recipe}-latest-oci artefact into the memres VM
+ # and push it from inside the guest.
+ script.append(" _OCI_IMAGE=${_DEPLOY_DIR_IMAGE}/%s-latest-oci" % recipe)
+ script.append(" if [ ! -e \"$_OCI_IMAGE\" ]; then")
+ script.append(" echo \"WARNING: %s did not build (no OCI image at $_OCI_IMAGE), skipping push\"" % recipe)
+ script.append(" else")
+ script.append(" start_vm")
for registry in registries:
- # No per-registry 'login': credentials were staged into
- # the guest by '--config' on 'memres restart' above.
script += [
- " for _tag in $_TAGS; do",
- " %s-$(arch) vimport ${_DEPLOY_DIR_IMAGE}/%s-latest-oci %s/%s:${_tag}" % (runtime, recipe, registry, image),
- " %s-$(arch) push %s/%s:${_tag}" % (runtime, registry, image),
- " done",
+ " for _tag in $_TAGS; do",
+ " %s-$(arch) vimport ${_DEPLOY_DIR_IMAGE}/%s-latest-oci %s/%s:${_tag}" % (runtime, recipe, registry, image),
+ " %s-$(arch) push %s/%s:${_tag}" % (runtime, registry, image),
+ " done",
]
+ script.append(" fi")
script.append("fi")
# Tear-down is handled by the EXIT trap installed above.
utils.flush()
Support pushing multi-architecture container images (recipes inheriting meta-virtualization's oci-multiarch class) from the 'containers-library' job. These produce an OCI Image Index on disk rather than a single-arch image in a runtime store, so they are pushed with skopeo instead of vdkr/vpdmn (which only vimport one architecture). * scripts/run-push-containers: detect multiarch recipes per-recipe at runtime: 'bitbake -e' only contains OCI_MULTIARCH_OUTPUT when the recipe inherits oci-multiarch, and its value is the exact OCI layout path to push. Multiarch images are pushed with 'oe-run-native skopeo-native skopeo copy --all' direct from the OCI layout, after populating the skopeo-native recipe sysroot via 'bitbake skopeo-native -c addto_recipe_sysroot' (once per step, prepare_skopeo). The same tag set and registries are used for both paths; auth is passed to skopeo via --dest-authfile from CONTAINER_AUTH_CONFIG. * scripts/run-push-containers: defer the memres VM bring-up (kvm check, EXIT trap, restart, image rm) into a lazy start_vm() that only runs when the first single-arch recipe is pushed, so multiarch -only steps need neither /dev/kvm nor a VM boot. AI-Generated: Claude Cowork Opus 4.8 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- scripts/run-push-containers | 100 +++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 23 deletions(-)