Skip to content

Commit 41c74a3

Browse files
author
cubic Bot
committed
Preserve per-tag directory structure when constructing bundle paths (merges into #96)
1 parent 71cefe7 commit 41c74a3

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

evidence/packager.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,17 @@ def create_bundle(inputs: BundleInputs) -> dict[str, Any]:
261261
"evaluations": evaluations,
262262
}
263263

264-
manifest_path = resolve_within_root(manifest_dir, f"{tag}.yaml")
264+
tag_path = Path(tag)
265+
tag_manifest_dir = resolve_within_root(manifest_dir, str(tag_path.parent)) if tag_path.parent != Path(".") else manifest_dir
266+
tag_bundle_dir = resolve_within_root(bundle_dir, str(tag_path.parent)) if tag_path.parent != Path(".") else bundle_dir
267+
tag_manifest_dir.mkdir(parents=True, exist_ok=True)
268+
tag_bundle_dir.mkdir(parents=True, exist_ok=True)
269+
270+
manifest_path = tag_manifest_dir / f"{tag_path.name}.yaml"
265271
with manifest_path.open("w", encoding="utf-8") as handle:
266272
yaml.safe_dump(manifest, handle, sort_keys=False)
267273

268-
bundle_path = resolve_within_root(bundle_dir, f"{tag}.zip")
274+
bundle_path = tag_bundle_dir / f"{tag_path.name}.zip"
269275
with ZipFile(bundle_path, "w") as archive:
270276
for source, arcname in bundle_files:
271277
archive.write(source, arcname)

tests/test_evidence_bundle.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
load_policy,
1414
)
1515
from services.evidence.store import EvidenceStore
16-
from evidence.packager import _collect_files
16+
from evidence.packager import _collect_files, create_bundle
1717

1818

1919
def _write_json(path: Path, payload: dict) -> Path:
@@ -190,6 +190,63 @@ def test_evaluate_policy_warn_and_fail() -> None:
190190
assert evaluations["checks"]["provenance_attestations"]["status"] == "fail"
191191

192192

193+
def test_create_bundle_with_namespaced_tag(tmp_path: Path) -> None:
194+
tag = "namespace/repo:v1.0.0"
195+
normalized = _write_json(
196+
tmp_path / "artifacts/sbom/normalized.json", {"components": []}
197+
)
198+
quality_json = _write_json(
199+
tmp_path / "analysis/sbom_quality_report.json",
200+
{"metrics": {"coverage_percent": 95.0, "license_coverage_percent": 90.0}},
201+
)
202+
quality_html = tmp_path / "reports/sbom_quality_report.html"
203+
quality_html.parent.mkdir(parents=True, exist_ok=True)
204+
quality_html.write_text("<html>quality</html>", encoding="utf-8")
205+
risk_report = _write_json(
206+
tmp_path / "artifacts/risk.json",
207+
{"summary": {"component_count": 2, "cve_count": 1, "max_risk_score": 60.0}},
208+
)
209+
provenance_dir = tmp_path / "artifacts/attestations"
210+
provenance_dir.mkdir(parents=True, exist_ok=True)
211+
(provenance_dir / "build.json").write_text("{}", encoding="utf-8")
212+
repro_attestation = _write_json(
213+
tmp_path / "artifacts/repro/attestations" / f"{tag.replace('/', '_').replace(':', '_')}.json",
214+
{"match": True},
215+
)
216+
217+
inputs = BundleInputs(
218+
tag=tag,
219+
normalized_sbom=normalized,
220+
sbom_quality_json=quality_json,
221+
sbom_quality_html=quality_html,
222+
risk_report=risk_report,
223+
provenance_dir=provenance_dir,
224+
repro_attestation=repro_attestation,
225+
output_dir=tmp_path / "evidence",
226+
)
227+
manifest = create_bundle(inputs)
228+
229+
# Verify the bundle and manifest are created in the namespace directories
230+
bundle_path = Path(manifest["bundle_path"])
231+
manifest_path = Path(manifest["manifest_path"])
232+
233+
# Check that the paths preserve the namespace structure
234+
assert bundle_path.parent.name == "repo"
235+
assert bundle_path.parent.parent.name == "namespace"
236+
assert manifest_path.parent.name == "repo"
237+
assert manifest_path.parent.parent.name == "namespace"
238+
239+
# Check that the files exist
240+
assert bundle_path.is_file()
241+
assert manifest_path.is_file()
242+
243+
# Verify manifest contains the full tag
244+
import yaml
245+
with manifest_path.open("r", encoding="utf-8") as f:
246+
manifest_data = yaml.safe_load(f)
247+
assert manifest_data["tag"] == tag
248+
249+
193250
def test_collect_files_handles_nested_directories(tmp_path: Path) -> None:
194251
extras = tmp_path / "extras"
195252
extras.mkdir()

0 commit comments

Comments
 (0)