Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions deploy/ci/upgrade-compatibility.sh
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,72 @@ snapshot_previous_schema_contract() {
DROP SCHEMA IF EXISTS upgrade_ci_contract CASCADE;
CREATE SCHEMA upgrade_ci_contract;

-- Contract migrations may retire compatibility-only schema once the release
-- that stopped using it is itself the rollback target. Detect that boundary
-- from the baseline schema so older application images receive no exception.
CREATE TABLE upgrade_ci_contract.compatibility_boundaries (
name text PRIMARY KEY,
reason text NOT NULL
);

INSERT INTO upgrade_ci_contract.compatibility_boundaries (name, reason)
SELECT
'fast-conversation-canonical-storage-v0.41.0',
'v0.41.0 moved Fast conversation reads and writes to canonical storage'
WHERE to_regclass('public.fast_agent_conversation_aliases') IS NOT NULL
AND to_regclass('public.slack_quick_answers') IS NOT NULL
AND to_regprocedure('public.serialize_fast_conversation_bridge_writes()') IS NOT NULL
AND to_regprocedure('public.sync_canonical_fast_conversation_to_legacy()') IS NOT NULL
AND to_regprocedure('public.sync_legacy_fast_conversation_to_canonical()') IS NOT NULL
AND EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'fast_agent_conversations'
AND column_name = 'compatibility_messages'
)
AND EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'fast_agent_conversations'
AND column_name = 'legacy_conversation_ids'
)
AND EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'slack_fast_integration_calls'
AND column_name = 'fast_agent_conversation_id'
)
AND EXISTS (
SELECT 1
FROM pg_constraint AS foreign_key
INNER JOIN pg_class AS source_table
ON source_table.oid = foreign_key.conrelid
INNER JOIN pg_namespace AS source_schema
ON source_schema.oid = source_table.relnamespace
INNER JOIN pg_class AS target_table
ON target_table.oid = foreign_key.confrelid
INNER JOIN pg_namespace AS target_schema
ON target_schema.oid = target_table.relnamespace
INNER JOIN pg_attribute AS source_column
ON source_column.attrelid = source_table.oid
AND source_column.attnum = foreign_key.conkey[1]
INNER JOIN pg_attribute AS target_column
ON target_column.attrelid = target_table.oid
AND target_column.attnum = foreign_key.confkey[1]
WHERE foreign_key.contype = 'f'
AND cardinality(foreign_key.conkey) = 1
AND cardinality(foreign_key.confkey) = 1
AND source_schema.nspname = 'public'
AND source_table.relname = 'slack_fast_integration_calls'
AND source_column.attname = 'fast_agent_conversation_id'
AND target_schema.nspname = 'public'
AND target_table.relname = 'fast_agent_conversations'
AND target_column.attname = 'id'
);

-- Feature PRs use the published develop image as their CI baseline even though
-- develop is not a supported rollback target. That image briefly shipped the
-- v0.6 usage-table rename before this contract check existed. Allow only that
Expand All @@ -213,6 +279,56 @@ SELECT
WHERE to_regclass('public.llm_usage_events') IS NOT NULL
AND to_regclass('public.task_inference_usage_events') IS NULL;

INSERT INTO upgrade_ci_contract.previous_table_exceptions (table_name, reason)
SELECT retired.table_name, boundary.reason
FROM (
VALUES
('fast_agent_conversation_aliases'),
('slack_quick_answers')
) AS retired(table_name)
CROSS JOIN upgrade_ci_contract.compatibility_boundaries AS boundary
WHERE boundary.name = 'fast-conversation-canonical-storage-v0.41.0';

CREATE TABLE upgrade_ci_contract.previous_column_exceptions (
table_name text NOT NULL,
column_name text NOT NULL,
reason text NOT NULL,
PRIMARY KEY (table_name, column_name)
);

INSERT INTO upgrade_ci_contract.previous_column_exceptions (
table_name,
column_name,
reason
)
SELECT retired.table_name, retired.column_name, boundary.reason
FROM (
VALUES
('slack_conversation_messages', 'slack_quick_answer_id'),
('slack_fast_integration_calls', 'slack_quick_answer_id')
) AS retired(table_name, column_name)
CROSS JOIN upgrade_ci_contract.compatibility_boundaries AS boundary
WHERE boundary.name = 'fast-conversation-canonical-storage-v0.41.0';

CREATE TABLE upgrade_ci_contract.previous_nullability_exceptions (
table_name text NOT NULL,
column_name text NOT NULL,
reason text NOT NULL,
PRIMARY KEY (table_name, column_name)
);

INSERT INTO upgrade_ci_contract.previous_nullability_exceptions (
table_name,
column_name,
reason
)
SELECT
'slack_fast_integration_calls',
'fast_agent_conversation_id',
boundary.reason
FROM upgrade_ci_contract.compatibility_boundaries AS boundary
WHERE boundary.name = 'fast-conversation-canonical-storage-v0.41.0';

CREATE TABLE upgrade_ci_contract.previous_tables AS
SELECT table_name, table_type
FROM information_schema.tables AS previous
Expand All @@ -236,10 +352,28 @@ WHERE table_schema = 'public'
SELECT 1
FROM upgrade_ci_contract.previous_table_exceptions AS exception
WHERE exception.table_name = previous.table_name
)
AND NOT EXISTS (
SELECT 1
FROM upgrade_ci_contract.previous_column_exceptions AS exception
WHERE exception.table_name = previous.table_name
AND exception.column_name = previous.column_name
);
SQL
}

report_schema_contract_boundaries() {
local boundaries
boundaries="$(
compose exec -T postgres psql -Atq -U postgres -d roomote \
-c "SELECT format('%s: %s', name, reason) FROM upgrade_ci_contract.compatibility_boundaries ORDER BY name"
)"

if [ -n "$boundaries" ]; then
printf 'Enabled schema contract boundaries:\n%s\n' "$boundaries"
fi
}

verify_previous_schema_contract() {
local violations
violations="$(
Expand Down Expand Up @@ -309,6 +443,12 @@ JOIN information_schema.columns AS current
AND current.column_name = previous.column_name
WHERE previous.is_nullable = 'YES'
AND current.is_nullable = 'NO'
AND NOT EXISTS (
SELECT 1
FROM upgrade_ci_contract.previous_nullability_exceptions AS exception
WHERE exception.table_name = previous.table_name
AND exception.column_name = previous.column_name
)

UNION ALL

Expand Down Expand Up @@ -360,6 +500,7 @@ migration_exit="$(docker inspect --format '{{.State.ExitCode}}' "$migration_cont
}
verify_endpoints
snapshot_previous_schema_contract
report_schema_contract_boundaries
write_marker
baseline_migrations="$(applied_migration_count)"

Expand Down
19 changes: 19 additions & 0 deletions packages/db/drizzle/0053_watery_shinobi_shaw.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
DROP TRIGGER "serialize_canonical_fast_conversation_bridge_writes" ON "fast_agent_conversations";--> statement-breakpoint
DROP TRIGGER "sync_canonical_fast_conversation_to_legacy" ON "fast_agent_conversations";--> statement-breakpoint
DROP TRIGGER "serialize_legacy_fast_conversation_bridge_writes" ON "slack_quick_answers";--> statement-breakpoint
DROP TRIGGER "sync_legacy_fast_conversation_to_canonical" ON "slack_quick_answers";--> statement-breakpoint
DROP FUNCTION "serialize_fast_conversation_bridge_writes"();--> statement-breakpoint
DROP FUNCTION "sync_canonical_fast_conversation_to_legacy"();--> statement-breakpoint
DROP FUNCTION "sync_legacy_fast_conversation_to_canonical"();--> statement-breakpoint
ALTER TABLE "slack_conversation_messages" DROP CONSTRAINT "slack_conversation_messages_slack_quick_answer_id_slack_quick_answers_id_fk";
--> statement-breakpoint
ALTER TABLE "slack_fast_integration_calls" DROP CONSTRAINT "slack_fast_integration_calls_slack_quick_answer_id_slack_quick_answers_id_fk";
--> statement-breakpoint
DROP INDEX "slack_fast_integration_calls_session_idx";--> statement-breakpoint
ALTER TABLE "slack_fast_integration_calls" ALTER COLUMN "fast_agent_conversation_id" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "slack_conversation_messages" DROP COLUMN "slack_quick_answer_id";--> statement-breakpoint
ALTER TABLE "slack_fast_integration_calls" DROP COLUMN "slack_quick_answer_id";--> statement-breakpoint
ALTER TABLE "fast_agent_conversation_aliases" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
ALTER TABLE "slack_quick_answers" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
DROP TABLE "fast_agent_conversation_aliases";--> statement-breakpoint
DROP TABLE "slack_quick_answers";
Loading
Loading