diff --git a/meta-digi-containers/recipes-core/images/dey-image-container-fragments.inc b/meta-digi-containers/recipes-core/images/dey-image-container-fragments.inc new file mode 100644 index 000000000..dc9a4d844 --- /dev/null +++ b/meta-digi-containers/recipes-core/images/dey-image-container-fragments.inc @@ -0,0 +1,57 @@ +# +# Copyright (C) 2026, Digi International Inc. +# +######################## +# Rootfs postprocess +######################## +ROOTFS_POSTPROCESS_COMMAND += "apply_container_rootfs_overlays; " +apply_container_rootfs_overlays() { + overlay_dirs="${CONTAINER_ROOTFS_OVERLAY_DIRS}" + overlay_tarballs="${CONTAINER_ROOTFS_OVERLAY_TARBALLS}" + default_overlay_dir="${CONTAINER_DEFAULT_ROOTFS_DIR}" + + if [ -z "${overlay_dirs}" ] && [ -d "${default_overlay_dir}" ]; then + overlay_dirs="${default_overlay_dir}" + bbnote "Using default profile rootfs overlay: ${default_overlay_dir}" + fi + + for overlay_dir in ${overlay_dirs}; do + if [ ! -d "${overlay_dir}" ]; then + bbfatal "CONTAINER_ROOTFS_OVERLAY_DIRS entry does not exist or is not a directory: ${overlay_dir}" + fi + bbnote "Applying rootfs overlay directory: ${overlay_dir}" + ( + cd "${overlay_dir}" || exit 1 + tar -cf - --exclude='.keep' --exclude='*/.keep' . + ) | tar -xf - -C "${IMAGE_ROOTFS}" + + # Ensure copied shell scripts are executable inside the container rootfs. + ( + cd "${overlay_dir}" || exit 1 + find . -type f -name '*.sh' + ) | while IFS= read -r relpath; do + relpath="${relpath#./}" + chmod 0755 "${IMAGE_ROOTFS}/${relpath}" + done + done + + for overlay_tarball in ${overlay_tarballs}; do + if [ ! -f "${overlay_tarball}" ]; then + bbfatal "CONTAINER_ROOTFS_OVERLAY_TARBALLS entry does not exist: ${overlay_tarball}" + fi + bbnote "Extracting rootfs overlay tarball: ${overlay_tarball}" + tar -xzf "${overlay_tarball}" -C "${IMAGE_ROOTFS}" + done +} + +ROOTFS_POSTPROCESS_COMMAND += "apply_container_rootfs_runtime_defaults; " +apply_container_rootfs_runtime_defaults() { + if [ -n "${CONTAINER_INIT_SCRIPT}" ] && \ + [ -f "${IMAGE_ROOTFS}${CONTAINER_INIT_SCRIPT}" ] && \ + grep -q "@WAYLAND_DISPLAY@" "${IMAGE_ROOTFS}${CONTAINER_INIT_SCRIPT}"; then + sed -i \ + -e "s|@WAYLAND_DISPLAY@|${WAYLAND_DISPLAY}|g" \ + "${IMAGE_ROOTFS}${CONTAINER_INIT_SCRIPT}" + bbnote "Applied WAYLAND_DISPLAY='${WAYLAND_DISPLAY}' to ${IMAGE_ROOTFS}${CONTAINER_INIT_SCRIPT}" + fi +} \ No newline at end of file diff --git a/meta-digi-containers/recipes-core/images/dey-image-container-lxc.inc b/meta-digi-containers/recipes-core/images/dey-image-container-lxc.inc new file mode 100644 index 000000000..08780ade8 --- /dev/null +++ b/meta-digi-containers/recipes-core/images/dey-image-container-lxc.inc @@ -0,0 +1,48 @@ +# +# Copyright (C) 2026, Digi International Inc. +# +######################## +# LXC artifact +######################## +do_image_lxc_bundle() { + rootfs_tar="${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.tar.xz" + if [ ! -f "${rootfs_tar}" ]; then + rootfs_tar="${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.tar.xz" + fi + lxc_tar="${DEPLOY_DIR_IMAGE}/${LXC_OUTPUT_NAME}" + workdir="$(mktemp -d)" + trap 'rm -rf "${workdir}"' EXIT + + if [ ! -f "${rootfs_tar}" ]; then + bbfatal "Expected rootfs tarball not found: ${rootfs_tar}" + fi + + lxc_dir="${workdir}/${CONTAINER_NAME}" + mkdir -p "${lxc_dir}/rootfs" + + bbnote "Extracting rootfs for LXC bundle..." + tar -xf "${rootfs_tar}" -C "${lxc_dir}/rootfs" + + config_in="${LXC_CONFIG_FILE}" + if [ ! -f "${config_in}" ]; then + bbfatal "LXC config file not found for CONTAINER_TYPE='${CONTAINER_TYPE}' and platform='${LXC_PLATFORM}': ${config_in}" + fi + + sed \ + -e "s|@LXC_ARCH@|${TARGET_ARCH}|g" \ + -e "s|@LXC_FOLDER@|${LXC_FOLDER}|g" \ + -e "s|@CONTAINER_NAME@|${CONTAINER_NAME}|g" \ + -e "s|@CONTAINER_INIT_MANAGER@|${CONTAINER_INIT_MANAGER}|g" \ + -e "s|@CONTAINER_INIT_SCRIPT@|${CONTAINER_INIT_SCRIPT}|g" \ + "${config_in}" > "${lxc_dir}/config" + + tar -C "${workdir}" -cJf "${lxc_tar}" "${CONTAINER_NAME}" + + if [ ! -s "${lxc_tar}" ]; then + bbfatal "LXC bundle was not generated correctly: ${lxc_tar}" + fi + + bbnote "LXC bundle ready: ${lxc_tar}" +} + +addtask image_lxc_bundle after do_image_complete before do_build diff --git a/meta-digi-containers/recipes-core/images/dey-image-container-podman.inc b/meta-digi-containers/recipes-core/images/dey-image-container-podman.inc new file mode 100644 index 000000000..a4766dcd1 --- /dev/null +++ b/meta-digi-containers/recipes-core/images/dey-image-container-podman.inc @@ -0,0 +1,36 @@ +# +# Copyright (C) 2026, Digi International Inc. +# +######################## +# Podman artifact +######################## +do_image_podman_archive[depends] += "skopeo-native:do_populate_sysroot" +do_image_podman_archive() { + src_tar="${DEPLOY_DIR_IMAGE}/${IMAGE_BASENAME}-${OCI_IMAGE_TAG_SAFE}-oci.tar" + if [ ! -f "${src_tar}" ]; then + src_tar="${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.rootfs-oci.tar" + fi + dst_tar="${DEPLOY_DIR_IMAGE}/${PODMAN_OUTPUT_NAME}" + image_ref="${CONTAINER_NAME}:${PODMAN_TAG}" + + if [ ! -f "${src_tar}" ]; then + bbfatal "Expected OCI tarball not found: ${src_tar}" + fi + + # skopeo docker-archive destination must not already exist. + rm -f "${dst_tar}" + + bbnote "Converting OCI archive to docker-archive (${image_ref})..." + PATH="${STAGING_BINDIR_NATIVE}:${PATH}" \ + skopeo --insecure-policy copy \ + "oci-archive:${src_tar}" \ + "docker-archive:${dst_tar}:${image_ref}" + + if [ ! -s "${dst_tar}" ]; then + bbfatal "Podman archive not generated correctly: ${dst_tar}" + fi + + bbnote "Podman archive ready: ${dst_tar} (${image_ref})" +} + +addtask image_podman_archive after do_image_complete before do_build diff --git a/meta-digi-containers/recipes-core/images/dey-image-container.bb b/meta-digi-containers/recipes-core/images/dey-image-container.bb new file mode 100644 index 000000000..884206dde --- /dev/null +++ b/meta-digi-containers/recipes-core/images/dey-image-container.bb @@ -0,0 +1,97 @@ +# Copyright (C) 2026, Digi International Inc. + +SUMMARY = "Minimal LXC container image" +LICENSE = "MIT" + +require dey-image-container-artifact.inc +require dey-image-container-fragments.inc +require dey-image-container-lxc.inc +require dey-image-container-podman.inc + +inherit core-image image-container image-oci + +######################## +# Image base settings +######################## +IMAGE_FEATURES = "" +IMAGE_FSTYPES = "tar.xz oci" +IMAGE_LINGUAS = "en-us" +NO_RECOMMENDATIONS = "1" + +######################## +# Container profile +######################## +# Select profile in local.conf (e.g. CONTAINER_TYPE = "lvgl" or "webkit"). +CONTAINER_TYPE ?= "base" +OVERRIDES:append = ":container-${CONTAINER_TYPE}" + +######################## +# Container runtime knobs +######################## +CONTAINER_INIT_MANAGER ?= "/usr/bin/docker-init" +CONTAINER_INIT_SCRIPT ?= "sleep infinity" +CONTAINER_NAME ?= "${CONTAINER_TYPE}-container" +CONTAINER_PROFILE_DIR ?= "${CONTAINERS_DIR}/${CONTAINER_TYPE}" +CONTAINER_ROOTFS_OVERLAY_DIRS ?= "" +CONTAINER_DEFAULT_ROOTFS_DIR ?= "${CONTAINER_PROFILE_DIR}/rootfs_files" +CONTAINER_ROOTFS_OVERLAY_TARBALLS ?= "" + +######################## +# Podman output knobs +######################## +PODMAN_TAG ?= "${CONTAINER_NAME}-tag" +PODMAN_OUTPUT_NAME ?= "${CONTAINER_NAME}_podman_${MACHINE}.tar" +PODMAN_ARTIFACT_OUTPUT_NAME ?= "${CONTAINER_NAME}_artifact_podman_${MACHINE}.tar.gz" + +######################## +# LXC output knobs +######################## +CONTAINERS_DIR ?= "${THISDIR}/../../containers" +LXC_FOLDER ?= "/var/lib/lxc" +LXC_PLATFORM ?= "${@d.getVar('MACHINE').split('-')[0]}" +LXC_OUTPUT_NAME ?= "${CONTAINER_NAME}_lxc_${MACHINE}.tar.xz" +LXC_ARTIFACT_OUTPUT_NAME ?= "${CONTAINER_NAME}_artifact_lxc_${MACHINE}.tar.gz" +LXC_CONFIG_DIR ?= "${CONTAINER_PROFILE_DIR}/configs_lxc" +LXC_CONFIG_FILE ?= "${LXC_CONFIG_DIR}/config_lxc_${LXC_PLATFORM}" + +######################## +# Artifact layout knobs +######################## +CONTAINER_ARTIFACT_TEMPLATE_DIR ?= "" +CONTAINER_DEFAULT_ARTIFACT_TEMPLATE_DIR ?= "${CONTAINER_PROFILE_DIR}/artifact" +META_DIGI_REPO_DIR ?= "${THISDIR}/../../.." +CONTAINER_PACKAGE_ID ?= "${CONTAINER_NAME}" +CONTAINER_ARTIFACT_VERSION ?= "${PV}" +CONTAINER_CREATE_ARGS ?= "" +CONTAINER_CREATE_ARGS_PODMAN ?= "${CONTAINER_CREATE_ARGS}" +CONTAINER_FIRMWARE_VERSIONS ?= "" +CONTAINER_DEVICE_TYPES_JSON ?= "[\"${MACHINE}\"]" +CONTAINER_ARTIFACT_DESCRIPTION ?= "" +CONTAINER_ARTIFACT_BUILD_ID ?= "" +CONTAINER_ARTIFACT_LABELS_JSON ?= "{}" + +######################## +# Container image behavior +######################## +VIRTUAL-RUNTIME_init_manager = "" +VIRTUAL-RUNTIME_initscripts = "" +IMAGE_CONTAINER_NO_DUMMY = "1" + +######################## +# OCI metadata mapping +######################## +OCI_IMAGE_TAG ?= "${PODMAN_TAG}" +OCI_IMAGE_TAG_SAFE = "${@d.getVar('OCI_IMAGE_TAG').replace(':', '_')}" +OCI_IMAGE_ENTRYPOINT = "${CONTAINER_INIT_MANAGER}" +OCI_IMAGE_ENTRYPOINT_ARGS = "${CONTAINER_INIT_SCRIPT}" + +######################## +# Base packages +######################## +IMAGE_INSTALL = " \ + base-files \ + base-passwd \ + busybox \ + netbase \ + tini \ +"