@@ -1,4 +1,4 @@
-{
+ {
"BASE_HOMEDIR" : "/home/pokybuild",
"BASE_SHAREDDIR" : "/srv/autobuilder/autobuilder.yocto.io",
"BASE_PUBLISHDIR" : "/srv/autobuilder/downloads.yoctoproject.org",
@@ -40,6 +40,10 @@
"SDKEXTRAS" : ["SSTATE_MIRRORS += '\\", "file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH'", "BB_HASHSERVE = 'auto'", "BB_HASHSERVE_UPSTREAM = '${AUTOBUILDER_HASHSERV}'"],
"BUILDINFO" : false,
"BUILDHISTORY" : false,
+ "CONTAINER_RUNTIME" : "vdkr",
+ "CONTAINER_REGISTRIES" : [],
+ "CONTAINER_TAGS" : ["latest"],
+ "CONTAINER_TAG_CMDS" : [],
"BUILDINFOVARS" : ["INHERIT += 'image-buildinfo'", "IMAGE_BUILDINFO_VARS:append = ' IMAGE_BASENAME IMAGE_NAME'"],
"WRITECONFIG" : true,
"SENDERRORS" : true,
@@ -1908,6 +1912,7 @@
"step1" : {
"shortname" : "Build 'base' container",
"BBTARGETS" : "container-base",
+ "CONTAINER_IMAGES" : {"container-base": "base"},
"extravars" : [
"DISTRO_FEATURES:append = ' virtualization vcontainer'"
]
@@ -1915,6 +1920,7 @@
"step2" : {
"shortname" : "Build 'curl' container",
"BBTARGETS" : "app-container-curl",
+ "CONTAINER_IMAGES" : {"app-container-curl": "curl"},
"extravars" : [
"DISTRO_FEATURES:append = ' virtualization vcontainer'"
]
@@ -1934,9 +1940,16 @@
"extravars" : [
"DISTRO_FEATURES:append = ' virtualization vcontainer'"
],
+ "CONTAINER_TAG_CMDS" : [
+ "_PV_MAJOR=$(echo $_PV | cut -d. -f1)",
+ "_PV_MAJOR_MINOR=$(echo $_PV | cut -d. -f1,2)",
+ "_EXTRA_TAGS=\"$_PV_MAJOR $_PV_MAJOR_MINOR\""
+ ],
"step1" : {
"shortname" : "Build 'python' container",
- "BBTARGETS" : "app-container-python"
+ "BBTARGETS" : "app-container-python",
+ "CONTAINER_IMAGES" : {"app-container-python": "python"},
+ "CONTAINER_VERSION_RECIPE" : "python3"
}
},
"vcontainer-tests": {
@@ -198,6 +198,7 @@ utils.mkdir(args.builddir)
revision = "unknown"
report = utils.ErrorReport(ourconfig, args.target, args.builddir, properties['branch_oecore'], revision)
+push_containers = properties.get("push_containers", False)
errordir = utils.errorreportdir(args.builddir)
utils.mkdir(errordir)
@@ -321,6 +322,133 @@ def handle_stepnum(stepnum):
hp.printheader("Step %s/%s: Running bitbake %s" % (stepnum, maxsteps, sanitytargets))
bitbakecmd(args.builddir, "bitbake %s -k" % (sanitytargets), report, stepnum, args.stepname)
+ # Push container images to registries when push_containers is enabled
+ container_images = utils.getconfigdict("CONTAINER_IMAGES", ourconfig, args.target, stepnum)
+ if container_images and push_containers:
+ if jcfg:
+ addstepentry("push-containers", "Push containers", shortdesc, desc, str(container_images), str(stepnum))
+ elif args.stepname == "push-containers":
+ runtime = utils.getconfigvar("CONTAINER_RUNTIME", ourconfig, args.target, stepnum) or "vdkr"
+ registries = utils.getconfiglist("CONTAINER_REGISTRIES", ourconfig, args.target, stepnum)
+ if not registries:
+ hp.printheader("Step %s/%s: push-containers skipped — CONTAINER_REGISTRIES is empty, no containers pushed" % (stepnum, maxsteps))
+ else:
+ static_tags = utils.getconfiglist("CONTAINER_TAGS", ourconfig, args.target, stepnum)
+ auth_config = utils.getconfigvar("CONTAINER_AUTH_CONFIG", ourconfig, args.target, stepnum)
+ if not auth_config:
+ if runtime == "vpdmn":
+ auth_config = "${HOME}/.config/containers/auth.json"
+ else:
+ auth_config = "${HOME}/.docker/config.json"
+ hp.printheader("Step %s/%s: Pushing container images %s" % (stepnum, maxsteps, 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.
+ #
+ # 'memres status' only checks that the QEMU PID in daemon.pid
+ # is alive (see daemon_is_running()/daemon_status() in
+ # meta-virtualization's vrunner.sh); it returns 0 as soon as
+ # QEMU forks, so a hung/partially-booted VM from a previous
+ # run — or a VM in mid-boot — is reported as healthy. The
+ # subsequent 'login'/'vimport'/'push' commands then hang on
+ # the unresponsive daemon socket.
+ #
+ # 'memres restart' is synchronous: it does stop+start and
+ # runs a PING/PONG readiness probe against the daemon socket
+ # (120s timeout), exiting non-zero if the VM never answers.
+ # Running it in the foreground gives us a trustworthy ready
+ # signal via its exit code, so we can drop the status-poll
+ # loop entirely.
+ #
+ # Install an EXIT trap first so we always tear the daemon
+ # down, even if bitbake -e / vimport / push fails mid-step
+ # under 'set -e'. The trap is armed before the restart so
+ # a restart failure also triggers cleanup.
+ #
+ # Registry auth is staged into the guest at VM boot via
+ # the global '--config' flag — vrunner.sh's setup_auth_share()
+ # copies $AUTH_CONFIG onto a read-only 9p share, and
+ # vdkr-init.sh / vpdmn-init.sh's install_auth_config()
+ # installs it at /root/.docker/config.json (vdkr) or
+ # /run/containers/0/auth.json (vpdmn) inside the guest.
+ # Subsequent 'push' calls use those creds directly, so no
+ # explicit 'login' step is needed. Calling 'login' would
+ # actually hang under the autobuilder (no PTY): when the
+ # 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),
+ ]
+ tag_cmds = utils.getconfiglist("CONTAINER_TAG_CMDS", ourconfig, args.target, stepnum)
+ version_recipe = utils.getconfigvar("CONTAINER_VERSION_RECIPE", ourconfig, args.target, stepnum)
+ for recipe, image in container_images.items():
+ # Extract version metadata from the recipe and distro via
+ # bitbake -e. Steps that need additional derived tags (e.g.
+ # major, major.minor) populate _EXTRA_TAGS via
+ # CONTAINER_TAG_CMDS in their step config.
+ #
+ # PV is sanitized with 'sed s/+.*//' to drop Yocto's
+ # '+git<sha>' suffix on AUTOREV/dev recipes — Docker
+ # reference format does not allow '+' in tags, and the
+ # base PV is what consumers expect.
+ #
+ # DISTRO_VERSION needs context-sensitive handling. Poky's
+ # DISTRO_VERSION resolves to '${PV}+snapshot-${METADATA_REVISION}'
+ # off a tag and just '${PV}' on a release tag. The '+' in
+ # the snapshot form is illegal in a Docker tag, but more
+ # importantly the patch level on a snapshot build (e.g.
+ # '6.0.99' between 6.0 and 6.1) is a moving target that
+ # doesn't correspond to any real release — only the
+ # major.minor line is meaningful. So:
+ # - snapshot build (DISTRO_VERSION contains '+') → tag
+ # with major.minor only, e.g. 'yocto-6.0'.
+ # - release-tag build (no '+') → tag with the full
+ # version, e.g. 'yocto-5.0.5' from the yocto-5.0.5 tag.
+ script += [
+ "_BBENV=$(bitbake -e %s 2>/dev/null) || true" % recipe,
+ "_PV=$(echo \"$_BBENV\" | awk -F'\"' '/^PV=/{ print $2; exit }' | sed 's/+.*//')",
+ "_DISTRO_CODENAME=$(echo \"$_BBENV\" | awk -F'\"' '/^DISTRO_CODENAME=/{ print $2; exit }')",
+ "_DISTRO_VERSION_RAW=$(echo \"$_BBENV\" | awk -F'\"' '/^DISTRO_VERSION=/{ print $2; exit }')",
+ "case \"$_DISTRO_VERSION_RAW\" in",
+ " *+*) _DISTRO_VERSION=$(echo \"${_DISTRO_VERSION_RAW%%+*}\" | cut -d. -f1,2) ;;",
+ " *) _DISTRO_VERSION=\"$_DISTRO_VERSION_RAW\" ;;",
+ "esac",
+ "_DEPLOY_DIR_IMAGE=$(echo \"$_BBENV\" | awk -F'\"' '/^DEPLOY_DIR_IMAGE=/{ print $2; exit }')",
+ "_EXTRA_TAGS=\"\"",
+ ]
+ if version_recipe:
+ # When the image recipe's PV is a wrapper-style
+ # placeholder (e.g. app-container-python_1.0.0.bb,
+ # whose 1.0.0 is meaningless to a downstream user),
+ # CONTAINER_VERSION_RECIPE points at the recipe whose
+ # PV is actually meaningful for the resulting tag —
+ # typically the language runtime or app being packaged
+ # (e.g. python3 -> 3.14.x). Override _PV from that
+ # recipe; image-recipe state still drives
+ # DEPLOY_DIR_IMAGE and DISTRO_* since those are
+ # environment-wide.
+ script += [
+ "_VBBENV=$(bitbake -e %s 2>/dev/null) || true" % version_recipe,
+ "_PV=$(echo \"$_VBBENV\" | awk -F'\"' '/^PV=/{ print $2; exit }' | sed 's/+.*//')",
+ ]
+ script += tag_cmds
+ script.append(
+ "_TAGS=\"%s $_PV $_DISTRO_CODENAME yocto-$_DISTRO_VERSION $_EXTRA_TAGS\"" % " ".join(static_tags)
+ )
+ 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",
+ ]
+ # Tear-down is handled by the EXIT trap installed above.
+ bitbakecmd(args.builddir, "\n".join(script), report, stepnum, args.stepname)
+
# Run any extra commands specified
cmds = utils.getconfiglist("EXTRACMDS", ourconfig, args.target, stepnum)
if jcfg: