Skip to content

g1t/packages/contracts/src/rules.ts

241 lines8,730 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1import type { RepoPath } from "./repos";
2import type { Result } from "./result";
3import type { User, Viewer } from "./identity";
4import type { ConfidenceLevel } from "./work";
5
6/**
7 * Rulesets: what may happen to a repository's branches and tags, and what a
8 * pull request needs before it merges. A ruleset belongs to a repository or
9 * to a workspace (and through it to the repositories it selects), targets
10 * branches or tags by name, is `active`, `evaluate` (a dry run that records
11 * what it would have refused) or `disabled`, lists who may bypass it, and
12 * holds rules. Rulesets stack: every rule of each holds.
13 *
14 * Mirrors `crates/contracts/src/rules.rs`. Rulesets travel in the shape the
15 * API shows them, `snake_case`, so an export imports anywhere unchanged.
16 */
17
18export const DEFAULT_BRANCH = "~DEFAULT_BRANCH";
19export const ALL = "~ALL";
20export const MAX_RULESETS = 75;
21
22export type Enforcement = "active" | "evaluate" | "disabled";
23export type Target = "branch" | "tag";
24export type Level = "repository" | "workspace";
25export type AppliesTo = "everyone" | "agents" | "people";
26export type BypassActorKind = "role" | "team" | "user" | "token" | "g1t";
27export type BypassMode = "always" | "pull_requests";
28export type MergeMethod = "merge" | "squash" | "rebase";
Merge checks: statuses and check runs on every commit29export type Integration = "actions" | "deployments" | "security" | "g1t" | "api";
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge30export type PatternOperator = "starts_with" | "ends_with" | "contains" | "regex";
31
32export type Weekday = "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun";
33
34export type RefCondition = { include: string[]; exclude: string[] };
35export type RepositoryCondition = {
36 include: string[];
37 exclude: string[];
38 visibility: "any" | "public" | "private";
39 topics: string[];
40};
41export type Conditions = { ref_name: RefCondition; repository?: RepositoryCondition };
42export type BypassActor = { kind: BypassActorKind; value: string; mode: BypassMode };
43
44export type PullRequestParameters = {
45 required_approvals: number;
46 count_agent_approvals: boolean;
47 dismiss_stale_reviews_on_push: boolean;
48 require_code_owner_review: boolean;
49 require_last_push_approval: boolean;
50 allowed_merge_methods: MergeMethod[];
51 /** Only the ruleset made from branch protection that did not refuse pushes. */
52 allow_direct_pushes?: boolean;
53};
54export type RulesetCheck = { context: string; integration?: Integration };
55export type StatusChecksParameters = {
56 checks: RulesetCheck[];
57 strict: boolean;
58 paths: string[];
59 allow_bypass_on_merge: boolean;
60};
61export type MergeQueueParameters = {
62 merge_method: MergeMethod;
63 max_entries_to_build: number;
64 min_entries_to_merge: number;
65 min_entries_wait_minutes: number;
66 check_response_timeout_minutes: number;
67};
68export type PatternParameters = { name: string; operator: PatternOperator; pattern: string; negate: boolean };
69export type WeeklyWindow = { days: Weekday[]; start: string; end: string };
70export type Period = { start: string; end?: string | null; reason: string };
71export type MergeWindowParameters = {
72 time_zone: string;
73 windows: WeeklyWindow[];
74 freezes: Period[];
75 exceptions: Period[];
76};
77
78/** Every rule type, with its parameters. */
79export type Rule =
80 | { type: "creation"; parameters: Record<string, never> }
81 | { type: "update"; parameters: Record<string, never> }
82 | { type: "deletion"; parameters: Record<string, never> }
83 | { type: "non_fast_forward"; parameters: Record<string, never> }
84 | { type: "required_linear_history"; parameters: Record<string, never> }
85 | { type: "required_signatures"; parameters: Record<string, never> }
86 | { type: "pull_request"; parameters: PullRequestParameters }
87 | { type: "required_status_checks"; parameters: StatusChecksParameters }
88 | { type: "merge_queue"; parameters: MergeQueueParameters }
89 | { type: "required_deployments"; parameters: { environments: string[] } }
90 | { type: "commit_message_pattern"; parameters: PatternParameters }
91 | { type: "commit_author_email_pattern"; parameters: PatternParameters }
92 | { type: "committer_email_pattern"; parameters: PatternParameters }
93 | { type: "branch_name_pattern"; parameters: PatternParameters }
94 | { type: "tag_name_pattern"; parameters: PatternParameters }
95 | { type: "file_path_restriction"; parameters: { restricted_file_paths: string[] } }
96 | { type: "file_extension_restriction"; parameters: { restricted_file_extensions: string[] } }
97 | { type: "max_file_size"; parameters: { max_file_size_mb: number } }
98 | { type: "max_file_path_length"; parameters: { max_file_path_length: number } }
99 | { type: "max_files_changed"; parameters: { max_files: number } }
100 | { type: "secret_scanning"; parameters: Record<string, never> }
101 | { type: "confidence_threshold"; parameters: { minimum: ConfidenceLevel; required_approvals: number } }
102 | { type: "cost_cap"; parameters: { max_usd: number } }
103 | { type: "path_review"; parameters: { paths: string[]; required_approvals: number; team?: string | null } }
104 | { type: "merge_window"; parameters: MergeWindowParameters }
105 | { type: "agent_auto_merge"; parameters: { allowed: boolean; minimum_confidence?: ConfidenceLevel | null } };
106
107export type RuleType = Rule["type"];
108export type RuleEntry = Rule & { applies_to: AppliesTo };
109
110/** What a ruleset says: what is created, changed, exported and imported. */
111export type RulesetSpec = {
112 name: string;
113 enforcement: Enforcement;
114 target: Target;
115 conditions: Conditions;
116 bypass_actors: BypassActor[];
117 rules: RuleEntry[];
118};
119
120export type Ruleset = RulesetSpec & {
121 id: string;
122 level: Level;
123 workspace: string;
124 repo_id?: string;
125 repository?: string;
126 /** `branch_protection` for the one made from branch protection settings. */
127 source?: string;
128 created_by: string;
129 created_at: string;
130 updated_by: string;
131 updated_at: string;
132};
133
134export type RulesetSummary = {
135 id: string;
136 name: string;
137 level: Level;
138 enforcement: Enforcement;
139 bypass_actors: BypassActor[];
140};
141
142export type EffectiveRule = RuleEntry & {
143 ruleset_id: string;
144 ruleset_name: string;
145 level: Level;
146 enforcement: Enforcement;
147};
148
149export type EffectiveRules = {
150 name: string;
151 target: Target;
152 default_branch: boolean;
153 rules: EffectiveRule[];
154 rulesets: RulesetSummary[];
155};
156
157export type RuleVerdict = "pass" | "fail" | "bypass";
158export type Action = "push" | "merge" | "create_ref" | "delete_ref" | "rename_ref" | "commit";
159
160export type Violation = {
161 rule: string;
162 ruleset_id: string;
163 ruleset_name: string;
164 enforcement: Enforcement;
165 message: string;
166 remedy: string;
167};
168
169export type Evaluation = {
170 id: string;
171 repo_id: string;
172 workspace: string;
173 ruleset_id: string;
174 ruleset_name: string;
175 enforcement: Enforcement;
176 action: Action;
177 git_ref: string;
178 actor: string;
179 actor_kind: string;
180 verdict: RuleVerdict;
181 violations: Violation[];
182 number: number | null;
183 sha: string | null;
184 repository: string;
185 created_at: string;
186};
187
188export type Insights = {
189 days: number;
190 total: number;
191 passed: number;
192 blocked: number;
193 would_block: number;
194 bypassed: number;
195 by_ruleset: {
196 ruleset_id: string;
197 ruleset_name: string;
198 enforcement: Enforcement;
199 total: number;
200 blocked: number;
201 would_block: number;
202 bypassed: number;
203 }[];
204 by_rule: { rule: string; count: number }[];
205};
206
207export type EvaluationPage = { evaluations: Evaluation[]; next: string | null; insights: Insights };
208
209/** What a pull request's merge box shows of the rules of its base. */
210export type MergeRules = {
211 unmet: Violation[];
212 bypassable: Violation[];
213 evaluate: Violation[];
214 rulesets: RulesetSummary[];
215 merge_queue: boolean;
216 /** What the active rules ask, as they stack. */
217 required_approvals: number;
218 strict: boolean;
219 allow_bypass_on_merge: boolean;
220};
221
222/** Whose rulesets: a repository's or a workspace's. */
223export type RulesetOwner = { repo: RepoPath } | { workspace: string };
224
225export type EvaluationFilter = {
226 ruleset_id?: string;
227 verdict?: RuleVerdict;
228 problems_only?: boolean;
229 before?: string;
230 limit?: number;
231};
232
233export interface RulesApi {
234 listRulesets(owner: RulesetOwner, viewer: Viewer, includeParents?: boolean): Promise<Result<Ruleset[]>>;
235 getRuleset(owner: RulesetOwner, id: string, viewer: Viewer): Promise<Result<Ruleset>>;
236 /** Creates one (no `id`) or replaces one. */
237 saveRuleset(actor: User, owner: RulesetOwner, ruleset: RulesetSpec, id?: string): Promise<Result<Ruleset>>;
238 deleteRuleset(actor: User, owner: RulesetOwner, id: string): Promise<Result<boolean>>;
239 effectiveRules(repo: RepoPath, name: string, viewer: Viewer, target?: Target): Promise<Result<EffectiveRules>>;
240 ruleEvaluations(owner: RulesetOwner, viewer: Viewer, filter?: EvaluationFilter): Promise<Result<EvaluationPage>>;
241}

This file's history is long; its oldest lines are credited to the oldest commit read.