feat(release): publish verified source tags
This commit is contained in:
@@ -19,6 +19,11 @@ from .contracts import collect_contracts
|
||||
from .git_state import collect_repository_snapshot
|
||||
from .model import CatalogEntryChange, SelectiveCatalogCandidate
|
||||
from .module_directory import write_module_directory
|
||||
from .source_provenance import (
|
||||
catalog_source_selection,
|
||||
selected_source_provenance,
|
||||
source_tag_provenance_issues,
|
||||
)
|
||||
from .version_alignment import selected_repository_version_issues
|
||||
from .workspace import load_repository_specs, resolve_workspace_root, website_root
|
||||
|
||||
@@ -35,6 +40,7 @@ def build_selective_catalog_candidate(
|
||||
signing_keys: tuple[str, ...] = (),
|
||||
public_base_url: str = DEFAULT_PUBLIC_BASE_URL,
|
||||
repository_base: str = GITEA_BASE,
|
||||
source_remote: str = "origin",
|
||||
expires_days: int = 90,
|
||||
sequence: int | None = None,
|
||||
check_public: bool = True,
|
||||
@@ -48,6 +54,15 @@ def build_selective_catalog_candidate(
|
||||
|
||||
workspace = resolve_workspace_root(workspace_root)
|
||||
enforce_selected_version_alignment(repo_versions=repo_versions, workspace=workspace)
|
||||
enforce_selected_source_provenance(
|
||||
repo_versions=repo_versions,
|
||||
workspace=workspace,
|
||||
remote=source_remote,
|
||||
)
|
||||
selected_provenance = selected_source_provenance(
|
||||
repo_versions=repo_versions,
|
||||
workspace=workspace,
|
||||
)
|
||||
web_root = website_root(workspace)
|
||||
source_catalog = resolve_base_catalog(base_catalog=base_catalog, web_root=web_root, channel=channel)
|
||||
payload = read_catalog(source_catalog)
|
||||
@@ -66,7 +81,15 @@ def build_selective_catalog_candidate(
|
||||
release = {}
|
||||
release["catalog_url"] = f"{public_base_url.rstrip('/')}/catalogs/v1/channels/{channel}.json"
|
||||
release["keyring_url"] = f"{public_base_url.rstrip('/')}/catalogs/v1/keyring.json"
|
||||
release["selected_units"] = [{"repo": repo, "version": version, "tag": f"v{version.removeprefix('v')}"} for repo, version in sorted(repo_versions.items())]
|
||||
release["selected_units"] = [
|
||||
{
|
||||
"repo": repo,
|
||||
"version": version,
|
||||
"tag": f"v{version.removeprefix('v')}",
|
||||
**selected_provenance[repo],
|
||||
}
|
||||
for repo, version in sorted(repo_versions.items())
|
||||
]
|
||||
candidate["release"] = release
|
||||
|
||||
repo_contracts = contracts_by_repo(workspace)
|
||||
@@ -76,6 +99,11 @@ def build_selective_catalog_candidate(
|
||||
repo_contracts=repo_contracts,
|
||||
repository_base=repository_base.rstrip("/"),
|
||||
)
|
||||
enforce_complete_catalog_source_provenance(
|
||||
payload=candidate,
|
||||
workspace=workspace,
|
||||
remote=source_remote,
|
||||
)
|
||||
|
||||
output_root = resolve_output_dir(output_dir=output_dir, channel=channel, generated_at=generated_at)
|
||||
catalog_path = output_root / "channels" / f"{channel}.json"
|
||||
@@ -175,6 +203,50 @@ def enforce_selected_version_alignment(*, repo_versions: dict[str, str], workspa
|
||||
raise ValueError("Version alignment gate failed: " + "; ".join(failures))
|
||||
|
||||
|
||||
def enforce_selected_source_provenance(
|
||||
*,
|
||||
repo_versions: dict[str, str],
|
||||
workspace: Path,
|
||||
remote: str = "origin",
|
||||
) -> None:
|
||||
failures = source_tag_provenance_issues(
|
||||
repo_versions=repo_versions,
|
||||
workspace=workspace,
|
||||
remote=remote,
|
||||
require_head_repos=repo_versions,
|
||||
)
|
||||
if failures:
|
||||
raise ValueError(
|
||||
"Source tag provenance gate failed: "
|
||||
+ "; ".join(issue.describe() for issue in failures)
|
||||
)
|
||||
|
||||
|
||||
def enforce_complete_catalog_source_provenance(
|
||||
*,
|
||||
payload: object,
|
||||
workspace: Path,
|
||||
remote: str = "origin",
|
||||
) -> None:
|
||||
selection = catalog_source_selection(payload)
|
||||
failures = [*selection.issues]
|
||||
failures.extend(
|
||||
source_tag_provenance_issues(
|
||||
repo_versions=selection.all_versions,
|
||||
workspace=workspace,
|
||||
remote=remote,
|
||||
require_head_repos=selection.selected_versions,
|
||||
expected_commits=selection.selected_commits,
|
||||
expected_tag_objects=selection.selected_tag_objects,
|
||||
)
|
||||
)
|
||||
if failures:
|
||||
raise ValueError(
|
||||
"Complete catalog source provenance gate failed: "
|
||||
+ "; ".join(issue.describe() for issue in failures)
|
||||
)
|
||||
|
||||
|
||||
def resolve_base_catalog(*, base_catalog: Path | str | None, web_root: Path, channel: str) -> Path | str:
|
||||
if base_catalog is not None:
|
||||
value = str(base_catalog)
|
||||
|
||||
Reference in New Issue
Block a user