@@ -13732,14 +13732,20 @@ def validate_bundle(
1373213732 bundle_dir: Path,
1373313733 public: bool = False,
1373413734 *,
13735+ previously_activated_public: bool = False,
1373513736 license_evidence_dir: Path | None = None,
1373613737 license_build_root: Path | None = None,
1373713738 repo_root: Path | None = None,
1373813739 qualification_repo_root: Path | None = None,
1373913740) -> dict[str, Any]:
13740- """Validate a local candidate, audited candidate , or public release tree ."""
13741+ """Validate a candidate, public release , or exact activated- public replay ."""
1374113742
1374213743 bundle = Path(bundle_dir)
13744+ _require(
13745+ not (public and previously_activated_public),
13746+ "fresh and previously activated public validation are mutually exclusive",
13747+ )
13748+ public_bundle = public or previously_activated_public
1374313749 license_inputs = (
1374413750 license_evidence_dir,
1374513751 license_build_root,
@@ -13756,7 +13762,13 @@ def validate_bundle(
1375613762 "public release validation requires fresh license evidence, build root, "
1375713763 "and repository root",
1375813764 )
13759- audited_candidate = not public and supplied_license_inputs == len(license_inputs)
13765+ _require(
13766+ not previously_activated_public or supplied_license_inputs == 0,
13767+ "previously activated public validation does not accept fresh license inputs",
13768+ )
13769+ audited_candidate = (
13770+ not public_bundle and supplied_license_inputs == len(license_inputs)
13771+ )
1376013772 if repo_root is not None and qualification_repo_root is not None:
1376113773 _require(
1376213774 Path(repo_root).resolve() == Path(qualification_repo_root).resolve(),
@@ -13942,7 +13954,7 @@ def validate_bundle(
1394213954 item["hil_status"] in ("pending", "passed"),
1394313955 "%s HIL status invalid" % profile_id,
1394413956 )
13945- if public :
13957+ if public_bundle :
1394613958 _require(
1394713959 item["hil_status"] == "passed", "%s is not HIL-passed" % profile_id
1394813960 )
@@ -14033,10 +14045,10 @@ def validate_bundle(
1403314045 document_paths["hil_report"],
1403414046 profiles,
1403514047 identity_with_source,
14036- public ,
14048+ public_bundle ,
1403714049 repo_root=effective_qualification_root,
1403814050 )
14039- if public or audited_candidate:
14051+ if public_bundle or audited_candidate:
1404014052 notices = document_paths["third_party_licenses"].read_text(
1404114053 encoding="utf-8", errors="strict"
1404214054 )
@@ -14045,6 +14057,7 @@ def validate_bundle(
1404514057 "candidate-only linked-input notices cannot qualify an audited release; "
1404614058 "run the pinned esp-idf-sbom/policy audit",
1404714059 )
14060+ if public or audited_candidate:
1404814061 _audit_verify_release_evidence(
1404914062 notice=notices,
1405014063 evidence_dir=license_evidence_dir,
@@ -15168,6 +15181,10 @@ def _main(argv: list[str] | None = None) -> int:
1516815181 validate_mode = validate_parser.add_mutually_exclusive_group()
1516915182 validate_mode.add_argument("--public", action="store_true")
1517015183 validate_mode.add_argument("--audited-candidate", action="store_true")
15184+ validate_mode.add_argument(
15185+ "--previously-activated-public",
15186+ action="store_true",
15187+ )
1517115188 validate_parser.add_argument("--license-evidence-dir", type=Path)
1517215189 validate_parser.add_argument("--license-build-root", type=Path)
1517315190 validate_parser.add_argument("--repo-root", type=Path)
@@ -15255,9 +15272,17 @@ def _main(argv: list[str] | None = None) -> int:
1525515272 validation_mode = (
1525615273 "--public"
1525715274 if args.public
15258- else "--audited-candidate" if args.audited_candidate else None
15275+ else (
15276+ "--audited-candidate"
15277+ if args.audited_candidate
15278+ else (
15279+ "--previously-activated-public"
15280+ if args.previously_activated_public
15281+ else None
15282+ )
15283+ )
1525915284 )
15260- if validation_mode is not None and any(
15285+ if validation_mode in ("--public", "--audited-candidate") and any(
1526115286 value is None for value in evidence_arguments
1526215287 ):
1526315288 option_names = (
@@ -15271,22 +15296,24 @@ def _main(argv: list[str] | None = None) -> int:
1527115296 if value is None
1527215297 ]
1527315298 parser.error("%s requires %s" % (validation_mode, ", ".join(missing)))
15274- if validation_mode is None and any(
15299+ if validation_mode not in ("--public", "--audited-candidate") and any(
1527515300 value is not None for value in evidence_arguments
1527615301 ):
1527715302 parser.error(
1527815303 "license evidence options require --public or --audited-candidate"
1527915304 )
1528015305 if (
15281- validation_mode is None
15306+ validation_mode in ( None, "--previously-activated-public")
1528215307 and args.qualification_repo_root is None
1528315308 ):
1528415309 parser.error(
15285- "plain candidate validation requires --qualification-repo-root"
15310+ "%s requires --qualification-repo-root"
15311+ % (validation_mode or "plain candidate validation")
1528615312 )
1528715313 validate_bundle(
1528815314 args.bundle,
1528915315 public=args.public,
15316+ previously_activated_public=args.previously_activated_public,
1529015317 license_evidence_dir=args.license_evidence_dir,
1529115318 license_build_root=args.license_build_root,
1529215319 repo_root=args.repo_root,
0 commit comments