feat: add repository rename lifecycle API
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a049a4-ee9f-78e1-9d66-2cb0f9bea3e3
This commit is contained in:
tegwick 2026-08-29 03:17:37 +02:00
parent 9e77a4a9a2
commit 82ea38b180
11 changed files with 2464 additions and 3 deletions

View file

@ -0,0 +1,89 @@
"""allow only operation-guarded alias restoration during rollback
Revision ID: a4d5e6f7b8c9
Revises: f3c4d5e6a7b8
"""
from alembic import op
import sqlalchemy as sa
revision = "a4d5e6f7b8c9"
down_revision = "f3c4d5e6a7b8"
branch_labels = None
depends_on = None
def upgrade() -> None:
# T02 made aliases immutable. Rollback needs exactly one controlled
# exception: the alias created by this operation may become canonical while
# that same operation is in rollback-preflight. Ownership, spelling,
# protection, and provenance remain immutable.
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION guard_repository_slug_history()
RETURNS trigger AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
RAISE EXCEPTION 'repository slug rows are durable';
END IF;
IF NEW.repo_id IS DISTINCT FROM OLD.repo_id
OR NEW.slug IS DISTINCT FROM OLD.slug THEN
RAISE EXCEPTION 'repository slug ownership is immutable';
END IF;
IF OLD.kind = 'alias' AND ROW(
NEW.kind, NEW.protected, NEW.source_operation_id
) IS DISTINCT FROM ROW(
OLD.kind, OLD.protected, OLD.source_operation_id
) THEN
IF NOT (
NEW.kind = 'canonical'
AND NEW.protected
AND NEW.source_operation_id = OLD.source_operation_id
AND EXISTS (
SELECT 1
FROM repository_rename_operations operation
WHERE operation.id = OLD.source_operation_id
AND operation.repo_id = OLD.repo_id
AND operation.phase = 'rollback-preflight'
)
) THEN
RAISE EXCEPTION 'protected repository alias is immutable';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
"""
)
)
def downgrade() -> None:
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION guard_repository_slug_history()
RETURNS trigger AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
RAISE EXCEPTION 'repository slug rows are durable';
END IF;
IF NEW.repo_id IS DISTINCT FROM OLD.repo_id
OR NEW.slug IS DISTINCT FROM OLD.slug THEN
RAISE EXCEPTION 'repository slug ownership is immutable';
END IF;
IF OLD.kind = 'alias' AND ROW(
NEW.kind, NEW.protected, NEW.source_operation_id
) IS DISTINCT FROM ROW(
OLD.kind, OLD.protected, OLD.source_operation_id
) THEN
RAISE EXCEPTION 'protected repository alias is immutable';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
"""
)
)