diff --git a/meta-digi-containers/README.md b/meta-digi-containers/README.md index 381a4a0a4..abd7dec3c 100644 --- a/meta-digi-containers/README.md +++ b/meta-digi-containers/README.md @@ -78,13 +78,11 @@ The script generates exactly one runtime artifact per execution. Notes: - `--output-dir` is optional. If omitted, the script writes the DCP to the current directory. -- If the input manifest omits `package_id`, the generator derives the final - `package_id` from `name` and appends a unique suffix using the `created_at` - timestamp encoded in base36 milliseconds. -- If the input manifest provides `package_id`, that value is kept unchanged. -- The output file name is always derived from the generated package ID and +- The input manifest must provide `package_id`, which becomes the stable target + identifier. +- The output file name is always derived from the package ID and manifest fields as: - - `--.tar.gz` + - `--.tar.gz` - For Podman payloads, the final internal payload name is always `payload/image.tar` - For LXC payloads, the generator requires a Yocto-style `.tar.gz` bundle, validates its layout, and stores it inside the DCP using the original file name and format without re-packaging it. @@ -104,14 +102,13 @@ python3 meta-digi-containers/scripts/generate-dcp.py \ This generates a bundle named like: -- `./flutter-demo--podman-ccmp25-dvk.tar.gz` +- `./flutter-demo-podman-ccmp25-dvk.tar.gz` In those manifests: -- `name` is the stable logical container name stored on the target. +- `package_id` is the stable target identifier. - `friendly_name` is the user-facing label shown by the manager output when available. -- the final `package_id` is generated automatically from `name` unless the - input manifest provides an explicit `package_id` +- the final DCP manifest does not include a second container identifier - `registration_defaults` is limited to local manager policy such as `autostart`, `monitor`, and `restart`; this release does not generate DRM-specific defaults @@ -182,19 +179,12 @@ Outputs are generated in: Final outputs: -- `${DCP_NAME}--podman-.tar.gz` -- `${DCP_NAME}--lxc-.tar.gz` +- `${DCP_NAME}-podman-.tar.gz` +- `${DCP_NAME}-lxc-.tar.gz` Notes: -- The generator script derives `package_id` from the input `name` only when the - manifest does not provide one explicitly. In that default case it appends a - base36-encoded millisecond timestamp suffix, stores the generated `package_id` - in the final `manifest.json`, and uses it in the DCP file name. -- In Yocto builds, `dey-image-dcp` keeps that unique-name behavior and - removes older DCP artifacts with the same `${DCP_NAME}` prefix before - generating the new one, so the deploy directory does not keep accumulating - previous builds of the same container/runtime. +- In Yocto builds, `dey-image-dcp` sets `package_id` to `${DCP_NAME}`. Intermediate rootfs and OCI outputs are kept available for incremental rebuilds. The LXC bundle and Podman archive are generated as temporary payloads while @@ -281,7 +271,6 @@ Supported placeholders in LXC config fragments: The artifact manifest is generated automatically and includes: - `package_id` -- `name` [stable logical container name] - `friendly_name` [optional user-facing display name] - `version` - `runtime` diff --git a/meta-digi-containers/examples/manifest-lxc.json b/meta-digi-containers/examples/manifest-lxc.json index 3662117b7..f5b7e251e 100644 --- a/meta-digi-containers/examples/manifest-lxc.json +++ b/meta-digi-containers/examples/manifest-lxc.json @@ -1,5 +1,5 @@ { - "name": "flutter-demo", + "package_id": "flutter-demo", "friendly_name": "Flutter Demo", "version": "1.0", "runtime": "lxc", diff --git a/meta-digi-containers/examples/manifest-podman.json b/meta-digi-containers/examples/manifest-podman.json index 77467eca1..df3c3372f 100644 --- a/meta-digi-containers/examples/manifest-podman.json +++ b/meta-digi-containers/examples/manifest-podman.json @@ -1,5 +1,5 @@ { - "name": "flutter-demo", + "package_id": "flutter-demo", "friendly_name": "Flutter Demo", "version": "1.0", "runtime": "podman", diff --git a/meta-digi-containers/recipes-containers/cc-container-mng/cc-container-mng_1.0.0.bb b/meta-digi-containers/recipes-containers/cc-container-mng/cc-container-mng_1.0.0.bb index 3ee04f3de..d16b9a444 100644 --- a/meta-digi-containers/recipes-containers/cc-container-mng/cc-container-mng_1.0.0.bb +++ b/meta-digi-containers/recipes-containers/cc-container-mng/cc-container-mng_1.0.0.bb @@ -15,8 +15,8 @@ SRC_URI = " \ file://cc-containerd.service \ file://cc-containerd-shutdown.service \ " -SRC_URI[archive.md5sum] = "6d84c6f5ec9dc94d542c91001ff5fd36" -SRC_URI[archive.sha256sum] = "ce24c4fde041a69a7646eb9bad4891d2eb91291f3534e71444552d3830247aaa" +SRC_URI[archive.md5sum] = "2ae2c3c09e9bf223e7de4ec0994376f3" +SRC_URI[archive.sha256sum] = "627d90eb53a48bf978fb6993f661af3dba8e6bf091d295ff481457e4c0cb96a0" S = "${WORKDIR}/${BP}" diff --git a/meta-digi-containers/recipes-core/images/dey-image-dcp-artifact.inc b/meta-digi-containers/recipes-core/images/dey-image-dcp-artifact.inc index fe08ced51..f7e82bb6f 100644 --- a/meta-digi-containers/recipes-core/images/dey-image-dcp-artifact.inc +++ b/meta-digi-containers/recipes-core/images/dey-image-dcp-artifact.inc @@ -15,9 +15,9 @@ do_clean_old_container_artifacts() { fi for runtime in lxc podman; do - old_artifact_glob="${DEPLOY_DIR_IMAGE}/${DCP_NAME}-*-${runtime}-${primary_device_type}.tar.gz" - bbnote "Removing old ${runtime} container artifacts matching ${old_artifact_glob}" - rm -f ${old_artifact_glob} + artifact_path="${DEPLOY_DIR_IMAGE}/${DCP_NAME}-${runtime}-${primary_device_type}.tar.gz" + bbnote "Removing old ${runtime} container artifact ${artifact_path}" + rm -f "${artifact_path}" done } @@ -69,7 +69,7 @@ do_image_container_artifacts() { manifest_path="$2" create_args="$3" build_id="$4" - NAME="${DCP_NAME}" \ + PACKAGE_ID="${DCP_NAME}" \ FRIENDLY_NAME="${CONTAINER_FRIENDLY_NAME}" \ VERSION="${CONTAINER_ARTIFACT_VERSION}" \ RUNTIME="${runtime}" \ @@ -87,7 +87,7 @@ do_image_container_artifacts() { LABELS_JSON="${CONTAINER_ARTIFACT_LABELS_JSON}" \ python3 -c 'import json, os, sys; \ parse_bool = lambda name: os.environ[name].strip().lower() == "true"; \ -payload = {"name": os.environ["NAME"], "friendly_name": os.environ["FRIENDLY_NAME"], "version": os.environ["VERSION"], "runtime": os.environ["RUNTIME"], "registration_defaults": {"autostart": parse_bool("AUTOSTART"), "monitor": parse_bool("MONITOR"), "restart": {"enabled": parse_bool("RESTART_ENABLED"), "max_retries": int(os.environ["RESTART_MAX_RETRIES"]), "window": int(os.environ["RESTART_WINDOW"]), "retry_delay": int(os.environ["RESTART_RETRY_DELAY"]) } }, "device_types": json.loads(os.environ["DEVICE_TYPES_JSON"]), "firmware_versions": os.environ["FIRMWARE_VERSIONS"], "build_id": os.environ["BUILD_ID"], "description": os.environ["DESCRIPTION"], "labels": json.loads(os.environ["LABELS_JSON"])}; \ +payload = {"package_id": os.environ["PACKAGE_ID"], "friendly_name": os.environ["FRIENDLY_NAME"], "version": os.environ["VERSION"], "runtime": os.environ["RUNTIME"], "registration_defaults": {"autostart": parse_bool("AUTOSTART"), "monitor": parse_bool("MONITOR"), "restart": {"enabled": parse_bool("RESTART_ENABLED"), "max_retries": int(os.environ["RESTART_MAX_RETRIES"]), "window": int(os.environ["RESTART_WINDOW"]), "retry_delay": int(os.environ["RESTART_RETRY_DELAY"]) } }, "device_types": json.loads(os.environ["DEVICE_TYPES_JSON"]), "firmware_versions": os.environ["FIRMWARE_VERSIONS"], "build_id": os.environ["BUILD_ID"], "description": os.environ["DESCRIPTION"], "labels": json.loads(os.environ["LABELS_JSON"])}; \ create_args = os.environ["CREATE_ARGS"].strip(); \ payload.update({"create_args": create_args} if create_args else {}); \ open(sys.argv[1], "w", encoding="utf-8").write(json.dumps(payload, indent=2) + "\n")' \ diff --git a/meta-digi-containers/scripts/generate-dcp.py b/meta-digi-containers/scripts/generate-dcp.py index d9e8e30fb..72074b0df 100755 --- a/meta-digi-containers/scripts/generate-dcp.py +++ b/meta-digi-containers/scripts/generate-dcp.py @@ -15,32 +15,15 @@ import sys import tarfile import tempfile -BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz" def fail(message: str) -> "NoReturn": raise SystemExit(f"error: {message}") -def to_base36(value: int) -> str: - if value < 0: - fail("cannot convert negative values to base36") - if value == 0: - return "0" - result: list[str] = [] - while value: - value, remainder = divmod(value, 36) - result.append(BASE36_ALPHABET[remainder]) - return "".join(reversed(result)) - - def format_created_at(timestamp: datetime) -> str: return timestamp.astimezone(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z") -def build_generated_package_id(base_name: str, *, created_at_ms: int) -> str: - return f"{base_name}-{to_base36(created_at_ms)}" - - def load_manifest(path: Path) -> dict: try: data = json.loads(path.read_text(encoding="utf-8")) @@ -100,11 +83,7 @@ def validate_labels(value: object) -> dict: def validate_manifest(data: dict) -> dict: - package_id = data.get("package_id") - if package_id is not None: - if not isinstance(package_id, str) or not package_id.strip(): - fail("invalid manifest: package_id must be a non-empty string") - package_id = package_id.strip() + package_id = validate_string(data, "package_id", path="manifest") version = validate_string(data, "version", path="manifest") runtime = validate_string(data, "runtime", path="manifest") if runtime not in {"lxc", "podman"}: @@ -136,7 +115,6 @@ def validate_manifest(data: dict) -> dict: description = data.get("description", "") if description is not None and not isinstance(description, str): fail("invalid manifest: description must be a string") - name = validate_string(data, "name", path="manifest") friendly_name = data.get("friendly_name") if friendly_name is not None: if not isinstance(friendly_name, str) or not friendly_name.strip(): @@ -145,7 +123,6 @@ def validate_manifest(data: dict) -> dict: validated = { "package_id": package_id, - "name": name, "friendly_name": friendly_name, "version": version, "runtime": runtime, @@ -269,7 +246,6 @@ def build_final_manifest( ) -> dict: output = { "package_id": package_id, - "name": base["name"], "friendly_name": base["friendly_name"], "version": base["version"], "runtime": base["runtime"], @@ -332,18 +308,10 @@ def main() -> int: fail(f"cannot create output directory {output_dir}: {exc}") created_at_dt = datetime.now(timezone.utc) - created_at_ms = int(created_at_dt.timestamp() * 1000) created_at = format_created_at(created_at_dt) - generated_package_id = ( - manifest["package_id"] - if manifest["package_id"] - else build_generated_package_id( - manifest["name"], - created_at_ms=created_at_ms, - ) - ) + package_id = manifest["package_id"] output_name = build_output_name( - package_id=generated_package_id, + package_id=package_id, runtime=manifest["runtime"], device_type=manifest["device_types"][0], ) @@ -375,7 +343,7 @@ def main() -> int: final_manifest = build_final_manifest( manifest, - package_id=generated_package_id, + package_id=package_id, artifact_type=artifact_type, digest=digest, size_bytes=size_bytes, @@ -389,7 +357,7 @@ def main() -> int: lambda dst: write_default_readme( dst, manifest, - package_id=generated_package_id, + package_id=package_id, created_at=created_at, payload_name=payload_name, ),