tenseleyflow/shithub / ca6762b

Browse files

migration 0042: workflow_runs (S41a)

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
ca6762b2590aafd35ef0d652b865bdc6112131de
Parents
acac6fb
Tree
d2d6afe

1 changed file

StatusFile+-
A internal/migrationsfs/migrations/0042_workflow_runs.sql 94 0
internal/migrationsfs/migrations/0042_workflow_runs.sqladded
@@ -0,0 +1,94 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+--
3
+-- S41a workflow runs — top-level row per triggered workflow.
4
+--
5
+-- Each push / pull_request / schedule / workflow_dispatch event matched
6
+-- against a parsed `.shithub/workflows/*.yml` produces one row here.
7
+-- Child rows live in workflow_jobs (0043) and workflow_steps (0044).
8
+-- Status moves queued → running → completed | cancelled. conclusion is
9
+-- set on completion via the same enum values check_runs uses (S24);
10
+-- one workflow_jobs row maps to one check_runs row (S41b creates the
11
+-- mapping).
12
+--
13
+-- Per-repo run_index gives stable URLs (/owner/repo/actions/runs/42)
14
+-- without leaking global IDs across repos. Pattern cribbed from
15
+-- Forgejo's actions_run.index — see .refs/forgejo/models/actions/run.go.
16
+--
17
+-- Optimistic-lock `version` column lets the runner + the cancel path
18
+-- update status concurrently without overwriting each other (Forgejo
19
+-- pattern, same file).
20
+--
21
+-- concurrency_group is parsed in S41a but only honored from S41g; we
22
+-- carry the column from day one so retroactive backfill isn't needed.
23
+-- Fork-PR approval flow (need_approval, approved_by) is parked for
24
+-- v2 but the columns exist so the schema doesn't churn later.
25
+
26
+-- +goose Up
27
+
28
+CREATE TYPE workflow_run_status AS ENUM (
29
+    'queued', 'running', 'completed', 'cancelled'
30
+);
31
+
32
+CREATE TYPE workflow_run_event AS ENUM (
33
+    'push', 'pull_request', 'schedule', 'workflow_dispatch'
34
+);
35
+
36
+CREATE TABLE workflow_runs (
37
+    id                  bigserial             PRIMARY KEY,
38
+    repo_id             bigint                NOT NULL REFERENCES repos(id) ON DELETE CASCADE,
39
+    run_index           bigint                NOT NULL,
40
+    workflow_file       text                  NOT NULL,
41
+    workflow_name       text                  NOT NULL DEFAULT '',
42
+    head_sha            text                  NOT NULL,
43
+    head_ref            text                  NOT NULL DEFAULT '',
44
+    event               workflow_run_event    NOT NULL,
45
+    event_payload       jsonb                 NOT NULL DEFAULT '{}'::jsonb,
46
+    actor_user_id       bigint                REFERENCES users(id) ON DELETE SET NULL,
47
+    parent_run_id       bigint                REFERENCES workflow_runs(id) ON DELETE SET NULL,
48
+    concurrency_group   text                  NOT NULL DEFAULT '',
49
+    status              workflow_run_status   NOT NULL DEFAULT 'queued',
50
+    conclusion          check_conclusion,
51
+    pinned              boolean               NOT NULL DEFAULT false,
52
+    need_approval       boolean               NOT NULL DEFAULT false,
53
+    approved_by_user_id bigint                REFERENCES users(id) ON DELETE SET NULL,
54
+    started_at          timestamptz,
55
+    completed_at        timestamptz,
56
+    version             integer               NOT NULL DEFAULT 0,
57
+    created_at          timestamptz           NOT NULL DEFAULT now(),
58
+    updated_at          timestamptz           NOT NULL DEFAULT now(),
59
+
60
+    UNIQUE (repo_id, run_index),
61
+
62
+    CONSTRAINT workflow_runs_workflow_file_length CHECK (char_length(workflow_file) BETWEEN 1 AND 256),
63
+    CONSTRAINT workflow_runs_workflow_name_length CHECK (char_length(workflow_name) <= 256),
64
+    CONSTRAINT workflow_runs_head_sha_format      CHECK (char_length(head_sha) BETWEEN 7 AND 64),
65
+    CONSTRAINT workflow_runs_head_ref_length      CHECK (char_length(head_ref) <= 256),
66
+    CONSTRAINT workflow_runs_concurrency_length   CHECK (char_length(concurrency_group) <= 256),
67
+    CONSTRAINT workflow_runs_completed_has_conclusion CHECK (
68
+        status <> 'completed' OR conclusion IS NOT NULL
69
+    ),
70
+    CONSTRAINT workflow_runs_started_when_running CHECK (
71
+        status NOT IN ('running', 'completed', 'cancelled') OR started_at IS NOT NULL
72
+    ),
73
+    CONSTRAINT workflow_runs_completed_when_done CHECK (
74
+        status NOT IN ('completed', 'cancelled') OR completed_at IS NOT NULL
75
+    )
76
+);
77
+
78
+CREATE INDEX workflow_runs_repo_head_idx     ON workflow_runs (repo_id, head_sha);
79
+CREATE INDEX workflow_runs_repo_status_idx   ON workflow_runs (repo_id, status, created_at DESC);
80
+CREATE INDEX workflow_runs_actor_idx         ON workflow_runs (actor_user_id, created_at DESC);
81
+CREATE INDEX workflow_runs_concurrency_idx   ON workflow_runs (repo_id, concurrency_group, status)
82
+    WHERE concurrency_group <> '';
83
+CREATE INDEX workflow_runs_event_idx         ON workflow_runs (repo_id, event, created_at DESC);
84
+CREATE INDEX workflow_runs_parent_idx        ON workflow_runs (parent_run_id)
85
+    WHERE parent_run_id IS NOT NULL;
86
+
87
+CREATE TRIGGER set_updated_at BEFORE UPDATE ON workflow_runs
88
+    FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at();
89
+
90
+
91
+-- +goose Down
92
+DROP TABLE IF EXISTS workflow_runs;
93
+DROP TYPE IF EXISTS workflow_run_event;
94
+DROP TYPE IF EXISTS workflow_run_status;