From 0b015193785ad473182da2b60cff5f3dead9cdb2 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 28 Apr 2026 15:24:30 +0200 Subject: [PATCH] =?UTF-8?q?feat(deps):=20OCI=20Akua-package=20deps=20?= =?UTF-8?q?=E2=80=94=20accept=20package.k=20as=20KclModule=20marker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `find_package_root` now accepts either `kcl.mod` (kpm-published) or `package.k` (Akua-published) as a `KclModule` marker. Annotation detection (`detect_package`) and filesystem detection (`detect_kind`) already recognize `dev.akua.*` annotations + `akua.toml + package.k` directories from PR #34; the missing piece was the cache-root descent step that finalizes a fetched OCI artifact. End result: `[dependencies] upstream = { oci = "...", version = "..." }` where the artifact is an Akua-published OCI package now resolves through to the cached directory and lands as a `KclModule` dep. Also fixes a pre-existing test-only build break in `verbs/render.rs` where the `args(...)` test helper was missing the `debug` field added in PR #33. Test plan: - `cargo test -p akua-core --features oci-fetch --lib oci_fetcher::tests` — 12/12 (new: `extract_blob_unpacks_akua_published_plain_tar`) - `cargo test --workspace --all-features` — green - `cargo clippy --workspace --all-features` — clean --- crates/akua-cli/src/verbs/render.rs | 1 + crates/akua-core/src/oci_fetcher.rs | 67 ++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/akua-cli/src/verbs/render.rs b/crates/akua-cli/src/verbs/render.rs index 06b9165c..02f75438 100644 --- a/crates/akua-cli/src/verbs/render.rs +++ b/crates/akua-cli/src/verbs/render.rs @@ -606,6 +606,7 @@ resources = [{ stdout_mode: false, strict: false, offline: false, + debug: false, } } diff --git a/crates/akua-core/src/oci_fetcher.rs b/crates/akua-core/src/oci_fetcher.rs index 11c2c569..ae67a414 100644 --- a/crates/akua-core/src/oci_fetcher.rs +++ b/crates/akua-core/src/oci_fetcher.rs @@ -498,18 +498,25 @@ fn cache_dir_for(root: &Path, digest: &str) -> PathBuf { root.join("sha256").join(hex) } -/// Return the directory inside `cache_dir` that holds the -/// kind-appropriate marker (`Chart.yaml` for Helm; `kcl.mod` for KCL). +/// Return the directory inside `cache_dir` that holds a +/// kind-appropriate marker. Helm packages carry `Chart.yaml`. KCL +/// packages carry either `kcl.mod` (kpm-published) or `package.k` +/// (Akua-published) — both shapes share `PackageKind::KclModule` +/// and either marker is a valid root. +/// /// Handles both tar layouts: marker at root *or* in a single /// `/` wrapper subdirectory. Fails with [`OciFetchError::Extract`] -/// when the cached tree is missing the expected marker entirely — -/// usually means the cache slot was clobbered by a different artifact. +/// when the cached tree is missing every expected marker — usually +/// means the cache slot was clobbered by a different artifact. fn find_package_root(cache_dir: &Path, kind: PackageKind) -> Result { - let marker = match kind { - PackageKind::HelmChart => "Chart.yaml", - PackageKind::KclModule => "kcl.mod", + let markers: &[&str] = match kind { + PackageKind::HelmChart => &["Chart.yaml"], + PackageKind::KclModule => &["kcl.mod", "package.k"], }; - if cache_dir.join(marker).is_file() { + let has_marker = + |dir: &Path| -> bool { markers.iter().any(|m| dir.join(m).is_file()) }; + + if has_marker(cache_dir) { return Ok(cache_dir.to_path_buf()); } let rd = std::fs::read_dir(cache_dir).map_err(|source| OciFetchError::Io { @@ -518,12 +525,13 @@ fn find_package_root(cache_dir: &Path, kind: PackageKind) -> Result/` directory using `package.k` as the marker; + /// `detect_kind` recognizes the pair as `KclModule`. + #[test] + fn extract_blob_unpacks_akua_published_plain_tar() { + let mut buf = Vec::new(); + { + let mut tar_b = tar::Builder::new(&mut buf); + let mut hdr = tar::Header::new_gnu(); + + let toml_body = b"[package]\nname = \"webapp\"\nversion = \"0.1.0\"\nedition = \"akua.dev/v1alpha1\"\n"; + hdr.set_size(toml_body.len() as u64); + hdr.set_mode(0o644); + hdr.set_cksum(); + tar_b + .append_data(&mut hdr.clone(), "webapp/akua.toml", &toml_body[..]) + .unwrap(); + + let kcl_body = b"resources = [{apiVersion = \"v1\", kind = \"ConfigMap\", metadata.name = \"x\"}]\n"; + hdr.set_size(kcl_body.len() as u64); + hdr.set_cksum(); + tar_b + .append_data(&mut hdr, "webapp/package.k", &kcl_body[..]) + .unwrap(); + + tar_b.finish().unwrap(); + } + + let tmp = tempfile::tempdir().unwrap(); + let dest = tmp.path().join("sha256").join("akua01"); + extract_blob(&buf, &dest, ArchiveFormat::PlainTar).unwrap(); + assert!(dest.join("webapp/akua.toml").is_file()); + assert!(dest.join("webapp/package.k").is_file()); + let root = find_package_root(&dest, PackageKind::KclModule).unwrap(); + assert!(root.ends_with("webapp"), "got root {:?}", root); + assert_eq!(detect_kind(&dest), Some(PackageKind::KclModule)); + } + #[test] fn detect_package_prefers_helm_then_kcl_via_annotation() { // Manifest with both Helm + KCL layers + KCL annotation —