| 1 | -- SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | -- |
| 3 | -- S24 PR-checks subsystem. |
| 4 | -- |
| 5 | -- check_suites — one row per (repo_id, head_sha, app_slug). Suites |
| 6 | -- group runs from a single CI app/integration. Status |
| 7 | -- + conclusion are derived from runs (suite_rollup.go). |
| 8 | -- app_slug='external' is the default catch-all for |
| 9 | -- generic CI integrations posting via PAT. |
| 10 | -- check_runs — individual checks (e.g. "lint", "unit-tests") for |
| 11 | -- a specific head_sha. Status moves queued → in_progress |
| 12 | -- → completed; conclusion is set on completion. The |
| 13 | -- output jsonb mirrors GitHub's {title, summary, text} |
| 14 | -- shape so existing CI adapters port cleanly. |
| 15 | -- |
| 16 | -- Branch protection: status_checks_required already exists from S20 |
| 17 | -- (text[]). This migration adds dismiss_stale_status_checks_on_push so |
| 18 | -- the optional stale-on-push behavior can be opted into. |
| 19 | |
| 20 | -- +goose Up |
| 21 | |
| 22 | CREATE TYPE check_status AS ENUM ('queued', 'in_progress', 'completed', 'pending'); |
| 23 | CREATE TYPE check_conclusion AS ENUM ( |
| 24 | 'success', 'failure', 'neutral', 'cancelled', |
| 25 | 'skipped', 'timed_out', 'action_required', 'stale' |
| 26 | ); |
| 27 | |
| 28 | CREATE TABLE check_suites ( |
| 29 | id bigserial PRIMARY KEY, |
| 30 | repo_id bigint NOT NULL REFERENCES repos(id) ON DELETE CASCADE, |
| 31 | head_sha text NOT NULL, |
| 32 | app_slug text NOT NULL DEFAULT 'external', |
| 33 | status check_status NOT NULL DEFAULT 'queued', |
| 34 | conclusion check_conclusion, |
| 35 | created_at timestamptz NOT NULL DEFAULT now(), |
| 36 | updated_at timestamptz NOT NULL DEFAULT now(), |
| 37 | |
| 38 | UNIQUE (repo_id, head_sha, app_slug), |
| 39 | CONSTRAINT check_suites_app_slug_length CHECK (char_length(app_slug) BETWEEN 1 AND 64), |
| 40 | CONSTRAINT check_suites_head_sha_format CHECK (char_length(head_sha) BETWEEN 7 AND 64) |
| 41 | ); |
| 42 | |
| 43 | CREATE INDEX check_suites_repo_head_idx ON check_suites (repo_id, head_sha); |
| 44 | CREATE INDEX check_suites_status_idx ON check_suites (repo_id, status); |
| 45 | |
| 46 | CREATE TRIGGER set_updated_at BEFORE UPDATE ON check_suites |
| 47 | FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at(); |
| 48 | |
| 49 | |
| 50 | CREATE TABLE check_runs ( |
| 51 | id bigserial PRIMARY KEY, |
| 52 | suite_id bigint NOT NULL REFERENCES check_suites(id) ON DELETE CASCADE, |
| 53 | repo_id bigint NOT NULL REFERENCES repos(id) ON DELETE CASCADE, |
| 54 | head_sha text NOT NULL, |
| 55 | name text NOT NULL, |
| 56 | status check_status NOT NULL DEFAULT 'queued', |
| 57 | conclusion check_conclusion, |
| 58 | started_at timestamptz, |
| 59 | completed_at timestamptz, |
| 60 | details_url text NOT NULL DEFAULT '', |
| 61 | output jsonb NOT NULL DEFAULT '{}'::jsonb, |
| 62 | -- external_id lets external systems dedupe POST creates: the same |
| 63 | -- (repo, head_sha, name, external_id) returns the existing run. |
| 64 | external_id text, |
| 65 | created_at timestamptz NOT NULL DEFAULT now(), |
| 66 | updated_at timestamptz NOT NULL DEFAULT now(), |
| 67 | |
| 68 | CONSTRAINT check_runs_name_length CHECK (char_length(name) BETWEEN 1 AND 200), |
| 69 | CONSTRAINT check_runs_completed_has_conclusion CHECK ( |
| 70 | status <> 'completed' OR conclusion IS NOT NULL |
| 71 | ) |
| 72 | ); |
| 73 | |
| 74 | CREATE INDEX check_runs_repo_head_idx ON check_runs (repo_id, head_sha); |
| 75 | CREATE INDEX check_runs_suite_idx ON check_runs (suite_id); |
| 76 | CREATE INDEX check_runs_external_id_idx ON check_runs (repo_id, head_sha, name, external_id) |
| 77 | WHERE external_id IS NOT NULL; |
| 78 | CREATE INDEX check_runs_required_lookup_idx ON check_runs (repo_id, head_sha, name); |
| 79 | |
| 80 | CREATE TRIGGER set_updated_at BEFORE UPDATE ON check_runs |
| 81 | FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at(); |
| 82 | |
| 83 | |
| 84 | ALTER TABLE branch_protection_rules |
| 85 | ADD COLUMN dismiss_stale_status_checks_on_push boolean NOT NULL DEFAULT false; |
| 86 | |
| 87 | |
| 88 | -- +goose Down |
| 89 | ALTER TABLE branch_protection_rules |
| 90 | DROP COLUMN IF EXISTS dismiss_stale_status_checks_on_push; |
| 91 | DROP TABLE IF EXISTS check_runs; |
| 92 | DROP TABLE IF EXISTS check_suites; |
| 93 | DROP TYPE IF EXISTS check_conclusion; |
| 94 | DROP TYPE IF EXISTS check_status; |
| 95 |