feat(oci): retain app version history so bumps do not lose old versions (BUNYIP-386) #380
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/BUNYIP-386-oci-version-history"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes BUNYIP-386: Bunyip's OCI distribution served only the single
applications.pinned_image_tag, so an admin bump (v0.7 -> v0.8) overwrote the pin and made v0.7 unpullable through Bunyip, with no warning or grace. This adds an append-only version history so a bump ADDS a version and historical tags stay pullable.Changes
20260720000010_create_application_versions.sql:application_versions (application_id FK, image_tag, image_digest, published_at, release_notes_url, yanked; UNIQUE(application_id, image_tag)), backfilled from each app's currentpinned_image_tag.ApplicationRepository(bunyip-domain, shared):record_version(idempotentINSERT ... ON CONFLICT DO NOTHING) andis_pullable_version(non-yankedEXISTS). Runtime sqlx, so the offline.sqlxcache needs no regeneration.admin.rs): records the current pinned tag on every distribution update, best-effort (a history-write error never fails the update).oci_registry::get_manifest): now allows the pinned tag, any sha256 digest, OR any recorded non-yanked version.yankedpulls a bad version out of circulation without deleting history.Scope note
pinned_image_tagstays the current/default pointer. Versions bumped BEFORE this lands are already lost from Bunyip and are not recovered (a one-time upstreamdev.a8n.runtag scan could reintroduce them later). An admin "yank" endpoint / UI is a follow-up; this adds the column and honours it in serving.Verification
cargo build --workspace+clippy --workspace --all-targets -- -D warnings+fmt --all --checkall green (SQLX_OFFLINE, pinned rust-builder). A DB-backedversion_history_roundtrip_and_yanktest is added (record -> pullable, unrecorded -> not, yank -> not, idempotent count). It skips whenDATABASE_URLis unset and was not run against a live postgres here - please runjust pre-commit(or the dev-postgres test path) before merge to execute it.#BUNYIP-386
BUNYIP-386 initially covered only the OCI proxy (pinned_image_tag), but the Forgejo release/package download path had the identical single-version oversight: bumping pinned_release_tag overwrote the pin and made the old binary unreachable through Bunyip, since the download route only ever served the current pin. This generalizes the version history to both distribution paths so binaries are retained and reachable the same way images are. Model: application_versions is now artifact-agnostic. image_tag/image_digest are renamed to version_tag/artifact_digest (the digest is OCI-only and stays NULL for binary versions, which have no single digest), and the backfill records both pin columns via UNION. record_version/is_pullable_version operate on version_tag; download_source gains download_source_for(version) to resolve an arbitrary tag rather than only the pin. The migration is edited in place rather than added as a rename step because the branch is unmerged and has not been applied to any real database. Serve: a new versioned route GET /applications/{slug}/downloads/{version}/{asset_name} resolves a specific recorded release, gated by the same serve-relax allow-list the OCI proxy uses (the current pin is always servable; any other tag must be a recorded, non-yanked version; a miss returns 404, not 403, so a restricted product's tag set does not leak by status code). The default route self-heals the pinned tag into history on serve, and the admin bump append now records both the image and release pins. Verified on a throwaway postgres against the real migration file: the UNION backfill covers binary pins, record_version is idempotent and fills the digest once (COALESCE keeps it), the allow-list yanks per-version, and rows cascade-delete with the app. Green on build + clippy + fmt (SQLX_OFFLINE) via the pinned rust-builder, and all 277 bunyip-domain unit tests pass including a new download_source_for assertion. #BUNYIP-386 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>