g1t/services/work/migrations/0028_rulesets.sql
| 1 | -- Rulesets: what may happen to a repository's branches and tags, and what |
| 2 | -- a pull request needs before it merges. See crates/rules and |
| 3 | -- src/rulesets.rs. Every timestamp is RFC 3339 UTC. |
| 4 | |
| 5 | -- A repository's rulesets (level 'repository', with repo_id) and a |
| 6 | -- workspace's (level 'workspace'). `spec` is the ruleset as the API shows |
| 7 | -- it (g1t_contracts::rules::RulesetSpec, JSON); name, enforcement and |
| 8 | -- target are copied out of it for listing. |
| 9 | CREATE TABLE rulesets ( |
| 10 | id TEXT PRIMARY KEY, |
| 11 | level TEXT NOT NULL, |
| 12 | -- The workspace's slug; for a repository's, its workspace when saved. |
| 13 | workspace TEXT NOT NULL, |
| 14 | repo_id TEXT, |
| 15 | name TEXT NOT NULL, |
| 16 | enforcement TEXT NOT NULL, |
| 17 | target TEXT NOT NULL, |
| 18 | spec TEXT NOT NULL, |
| 19 | -- 'branch_protection' for the one made from branch protection settings. |
| 20 | source TEXT, |
| 21 | created_by TEXT NOT NULL, |
| 22 | created_at TEXT NOT NULL, |
| 23 | updated_by TEXT NOT NULL, |
| 24 | updated_at TEXT NOT NULL |
| 25 | ); |
| 26 | CREATE INDEX rulesets_by_repo ON rulesets (repo_id); |
| 27 | CREATE INDEX rulesets_by_workspace ON rulesets (workspace, level); |
| 28 | |
| 29 | -- Every evaluation of a ruleset on a push, a merge or another change to a |
| 30 | -- branch or tag: the audit trail, and what `evaluate` rulesets would have |
| 31 | -- refused. `violations` is JSON (Vec<Violation>). Ids sort by time. |
| 32 | CREATE TABLE rule_evaluations ( |
| 33 | id TEXT PRIMARY KEY, |
| 34 | repo_id TEXT NOT NULL, |
| 35 | workspace TEXT NOT NULL, |
| 36 | ruleset_id TEXT NOT NULL, |
| 37 | ruleset_name TEXT NOT NULL, |
| 38 | enforcement TEXT NOT NULL, |
| 39 | -- push, merge, create_ref, delete_ref, rename_ref or commit. |
| 40 | action TEXT NOT NULL, |
| 41 | git_ref TEXT NOT NULL, |
| 42 | actor TEXT NOT NULL, |
| 43 | -- person, agent, g1t or token. |
| 44 | actor_kind TEXT NOT NULL, |
| 45 | -- pass, fail or bypass. |
| 46 | verdict TEXT NOT NULL, |
| 47 | violations TEXT NOT NULL DEFAULT '[]', |
| 48 | number INTEGER, |
| 49 | sha TEXT, |
| 50 | created_at TEXT NOT NULL |
| 51 | ); |
| 52 | CREATE INDEX rule_evaluations_by_repo ON rule_evaluations (repo_id, id); |
| 53 | CREATE INDEX rule_evaluations_by_workspace ON rule_evaluations (workspace, id); |
| 54 | CREATE INDEX rule_evaluations_by_ruleset ON rule_evaluations (ruleset_id, id); |
| 55 | |
| 56 | -- The repos service kept whether pushes to the default branch were refused |
| 57 | -- (its `protected` flag). The first time g1t reads a repository's rulesets |
| 58 | -- after this, it folds that flag into its branch protection ruleset, once. |
| 59 | CREATE TABLE ruleset_adoptions ( |
| 60 | repo_id TEXT PRIMARY KEY, |
| 61 | adopted_at TEXT NOT NULL |
| 62 | ); |
| 63 | |
| 64 | -- Who moved a pull request's head last, and when: for approving the most |
| 65 | -- recent push and dismissing approvals that came before it. |
| 66 | ALTER TABLE pulls ADD COLUMN head_pushed_by TEXT; |
| 67 | ALTER TABLE pulls ADD COLUMN head_pushed_at TEXT; |
| 68 | |
| 69 | -- The integration that reported a status: actions, deployments, security |
| 70 | -- or g1t. A required check can insist on one. |
| 71 | ALTER TABLE commit_statuses ADD COLUMN source TEXT; |
| 72 | |
| 73 | -- Branch protection becomes a ruleset, holding exactly what it held: the |
| 74 | -- default branch's pull request rule (approvals, code owners), required |
| 75 | -- status checks and merge queue. Pushes stay allowed here; repositories |
| 76 | -- that refused them are adopted as described above. Only for repositories |
| 77 | -- whose settings protect anything. |
| 78 | INSERT INTO rulesets (id, level, workspace, repo_id, name, enforcement, target, spec, source, created_by, created_at, updated_by, updated_at) |
| 79 | SELECT |
| 80 | 'rs_' || lower(hex(randomblob(12))), |
| 81 | 'repository', |
| 82 | '', |
| 83 | s.repo_id, |
| 84 | 'Default branch protection', |
| 85 | 'active', |
| 86 | 'branch', |
| 87 | json_object( |
| 88 | 'name', 'Default branch protection', |
| 89 | 'enforcement', 'active', |
| 90 | 'target', 'branch', |
| 91 | 'conditions', json_object('ref_name', json_object('include', json_array('~DEFAULT_BRANCH'), 'exclude', json_array())), |
| 92 | 'bypass_actors', json_array(), |
| 93 | 'rules', ( |
| 94 | SELECT json_group_array(json(rule.value)) |
| 95 | FROM json_each(json_array( |
| 96 | CASE WHEN s.required_approvals > 0 OR s.require_code_owner_review != 0 THEN json_object( |
| 97 | 'type', 'pull_request', |
| 98 | 'parameters', json_object( |
| 99 | 'required_approvals', s.required_approvals, |
| 100 | 'count_agent_approvals', json(CASE WHEN s.count_agent_approvals != 0 THEN 'true' ELSE 'false' END), |
| 101 | 'dismiss_stale_reviews_on_push', json('false'), |
| 102 | 'require_code_owner_review', json(CASE WHEN s.require_code_owner_review != 0 THEN 'true' ELSE 'false' END), |
| 103 | 'require_last_push_approval', json('false'), |
| 104 | 'allowed_merge_methods', json_array(), |
| 105 | 'allow_direct_pushes', json('true') |
| 106 | ), |
| 107 | 'applies_to', 'everyone' |
| 108 | ) END, |
| 109 | CASE WHEN s.required_checks != '[]' OR s.require_up_to_date != 0 THEN json_object( |
| 110 | 'type', 'required_status_checks', |
| 111 | 'parameters', json_object( |
| 112 | 'checks', (SELECT json_group_array(json_object('context', checks.value)) FROM json_each(s.required_checks) AS checks), |
| 113 | 'strict', json(CASE WHEN s.require_up_to_date != 0 THEN 'true' ELSE 'false' END), |
| 114 | 'paths', json_array(), |
| 115 | 'allow_bypass_on_merge', json(CASE WHEN s.allow_ignoring_checks != 0 THEN 'true' ELSE 'false' END) |
| 116 | ), |
| 117 | 'applies_to', 'everyone' |
| 118 | ) END, |
| 119 | CASE WHEN s.merge_queue != 0 THEN json_object( |
| 120 | 'type', 'merge_queue', |
| 121 | 'parameters', json_object( |
| 122 | 'merge_method', 'merge', |
| 123 | 'max_entries_to_build', 4, |
| 124 | 'min_entries_to_merge', 1, |
| 125 | 'min_entries_wait_minutes', 0, |
| 126 | 'check_response_timeout_minutes', 45 |
| 127 | ), |
| 128 | 'applies_to', 'everyone' |
| 129 | ) END |
| 130 | )) AS rule |
| 131 | WHERE rule.type != 'null' |
| 132 | ) |
| 133 | ), |
| 134 | 'branch_protection', |
| 135 | s.updated_by, |
| 136 | s.updated_at, |
| 137 | s.updated_by, |
| 138 | s.updated_at |
| 139 | FROM repo_settings s |
| 140 | WHERE s.required_approvals > 0 |
| 141 | OR s.require_code_owner_review != 0 |
| 142 | OR s.required_checks != '[]' |
| 143 | OR s.require_up_to_date != 0 |
| 144 | OR s.merge_queue != 0; |