Skip to content

feat(cli): canonical image source policy and declared forks #192

Description

@danielscholl

Part of Borrow, Prove, Restore (Azure/osdu-spi#158), Phase 2. Continues #130, whose trust half shipped in #174 and #179.

The source-policy half shipped in #274 (v0.21.0): items 1 to 3 and their docs. What remains is the declaration, items 4 and 5. Start it after #275 lands, since the refresh path it reconciles into gets its fork step there. It needs its own release and a run on the shared environment before it closes.

Problem

ADR-032 and ADR-033 describe a declaration format the code does not have. The declaration in src/spi/environment.py is the flat seven-key schema; it has no forks: list, spi up takes no --declaration, and the refresh and upgrade workflows reconcile nothing from it. An environment's forks exist only as the resource-group tags and federated credentials spi onboard wrote by hand, so no reviewed file says which forks an environment should follow, and nothing puts them back when they drift.

Required change

The decision records are the spec. Read ADR-032 (Declared intent wins, Onboarding plans by default), ADR-033 (Decision), ADR-029 (Identities belong to the lifecycle), and docs/design/fork-deployment.md before starting, and implement what they describe rather than redesigning it.

  1. Source policy on the CLI. Done in feat(cli): canonical image source policy and spi service refresh #274. spi onboard <service> --repo <org>/<fork> --canonical-source fork|community writes the spi-source-<service> tag; --list reports source beside trust; --remove returns the service to community before revoking trust.
  2. Resolution honors the source. Done in feat(cli): canonical image source policy and spi service refresh #274. The resolver accepts either package name a fork can publish and refuses a fork source that isn't the trusted repository. spi info --json carries the source per service; spi service list stays a list of pins. spi service refresh <service> moves one service without touching the rest.
  3. Schema stays gated. Done in feat(cli): canonical image source policy and spi service refresh #274. The loader is the schema package name plus -load, as the template's load-image action publishes it (ADR-033 amended to match).
  4. Declaration forks:. Extend EnvironmentDeclaration with an optional forks list of {service, repo, canonicalSource} (canonicalSource defaults to community). repo unique across the list, service known to IMAGE_REGISTRY, forks invalid with any profile but core. spi up --declaration <owner>/<repo>:<path> loads the reviewed file from main, records the locator in the RG tag spi-environment-declaration, and refuses explicit flags that conflict with it. A later spi up without the option reads the retained locator.
  5. Lifecycle reconciliation. On a declared environment, spi up bootstrap and the refresh path reconcile the deploy identity's federated credentials and the source tags to forks: before projecting into the lock, serially per identity with the backoff ADR-032 requires. spi onboard on a declared environment refuses an addition, removal, repository change, or source choice that disagrees with the declaration and says which file to change.
  6. Docs. The --canonical-source mechanics shipped in feat(cli): canonical image source policy and spi service refresh #274. docs/design/fork-deployment.md still needs a forks: example, and docs/design/environment-lifecycle.md the reconcile order. ADRs need no change unless the implementation deviates; if it must, amend the record in the same PR and say why. No em dashes; follow docs/STYLE.md.

Acceptance

  • A declaration with forks: and profile: minimal fails validation; a duplicate repo fails validation; spi up --declaration on a fresh environment records the locator and leaves credentials and source tags matching the file.
  • spi onboard against a declared environment refuses an undeclared repository and names the declaration file.
  • A declared fork whose source tag or credential was removed by hand is restored by the next refresh.
  • Unit tests cover the planner rows for declaration reconciliation and declaration validation, in the style of tests/test_onboard.py (canned observed state, no simulated GitHub or Azure).

Verify

uv run pre-commit run --all-files

Report the result in the PR description. The end-to-end acceptance lines above need a real environment; state in the PR which of them you ran and which a maintainer must run on the shared environment.

Out of scope

Constraints

  • Branch: feat/declared-forks. Conventional Commits; PR title feat(cli): declared forks and lifecycle reconciliation.
  • Do not merge, do not force-push, no generated-with footers or co-author trailers on commits or the PR.
  • PR description follows the shape in CONTRIBUTING.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions