#!/usr/bin/env nu # CLI entry for the catalog contract content-addressing process (ADR-046). # Wrapped by justfiles/catalog-contract.just. Subcommands automate the producer # side (publish anchor + per-version referrer) and the consumer side (resolve a # catalog's requires.contract to an immutable digest pinned in catalog.lock.ncl). use platform/oci/contract_resolver.nu * use platform/oci/push_registry.nu * # Select the push registry from a pluggable source and emit shell export lines. # Apply with: eval "$(... select-registry --kind sops --value ...)" def "main select-registry" [ --kind: string = "" # sops | api | literal | env --value: string = "" # literal endpoint | env var | sops path | api url --endpoint-key: string = "endpoint" --user-key: string = "username" --pass-key: string = "password" --vault-id: string = "" --age-key: string = "" --auth-header: string = "" ] { select-push-registry --kind $kind --value $value --endpoint-key $endpoint_key --user-key $user_key --pass-key $pass_key --vault-id $vault_id --age-key $age_key --auth-header $auth_header } # Publish the immutable referrers anchor for a contract id (run once per id). # Prints the subject digest to record in requires.contract.subject. def "main publish-anchor" [ id: string # contract id, e.g. catalog-manifest --participant: string = "provisioning" --registry: string = "" --registry-id: string = "" ] { let r = (publish-contract-anchor $participant $id --registry $registry --registry-id $registry_id) print $"anchor published: ($r.ref)" print $"subject digest → set requires.contract.subject = \"($r.digest)\"" $r } # Attach a contract version as a referrer to the anchor. def "main publish-version" [ contract_file: string # path to contract.ncl (the manifest schema) id: string version: string subject: string # anchor digest from publish-anchor --participant: string = "provisioning" --registry: string = "" --registry-id: string = "" ] { let r = (publish-contract-version $contract_file $participant $id $version $subject --registry $registry --registry-id $registry_id) print $"attached ($id) v($version) → ($r.digest)" $r } # Resolve a catalog's requires.contract → digest and pin catalog.lock.ncl. def "main resolve" [ catalog_root: string # catalog dir to write catalog.lock.ncl into manifest: string # manifest file declaring requires.contract ] { let decl = (read-contract-decl $manifest) if $decl == null { print "no requires.contract declared — nothing to resolve" return } let r = (resolve-contract-and-lock $catalog_root $decl) print $"resolved ($r.id) → ($r.version) @ ($r.digest)" print $"lock: ($r.lock)" $r } # Validate a manifest against its declared contract (resolve + typecheck). # Intended as a CI gate before any automated load. def "main validate" [ manifest: string --catalog-root: string = "" ] { let result = (validate-manifest-against-contract $manifest --catalog-root $catalog_root) print $result if not $result.typechecks { exit 1 } $result } def main [] { print "catalog contract process (ADR-046)." print "subcommands: publish-anchor | publish-version | resolve | validate" }