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 merge | 1 | import type { RepoPath } from "./repos"; |
| 2 | import type { Result } from "./result"; | |
| 3 | import type { User, Viewer } from "./identity"; | |
| 4 | import 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 | ||
| 18 | export const DEFAULT_BRANCH = "~DEFAULT_BRANCH"; | |
| 19 | export const ALL = "~ALL"; | |
| 20 | export const MAX_RULESETS = 75; | |
| 21 | ||
| 22 | export type Enforcement = "active" | "evaluate" | "disabled"; | |
| 23 | export type Target = "branch" | "tag"; | |
| 24 | export type Level = "repository" | "workspace"; | |
| 25 | export type AppliesTo = "everyone" | "agents" | "people"; | |
| 26 | export type BypassActorKind = "role" | "team" | "user" | "token" | "g1t"; | |
| 27 | export type BypassMode = "always" | "pull_requests"; | |
| 28 | export type MergeMethod = "merge" | "squash" | "rebase"; | |
| 29 | export type Integration = "actions" | "deployments" | "security" | "g1t"; | |
| 30 | export type PatternOperator = "starts_with" | "ends_with" | "contains" | "regex"; | |
| 31 | ||
| 32 | export type Weekday = "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"; | |
| 33 | ||
| 34 | export type RefCondition = { include: string[]; exclude: string[] }; | |
| 35 | export type RepositoryCondition = { | |
| 36 | include: string[]; | |
| 37 | exclude: string[]; | |
| 38 | visibility: "any" | "public" | "private"; | |
| 39 | topics: string[]; | |
| 40 | }; | |
| 41 | export type Conditions = { ref_name: RefCondition; repository?: RepositoryCondition }; | |
| 42 | export type BypassActor = { kind: BypassActorKind; value: string; mode: BypassMode }; | |
| 43 | ||
| 44 | export 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 | }; | |
| 54 | export type RulesetCheck = { context: string; integration?: Integration }; | |
| 55 | export type StatusChecksParameters = { | |
| 56 | checks: RulesetCheck[]; | |
| 57 | strict: boolean; | |
| 58 | paths: string[]; | |
| 59 | allow_bypass_on_merge: boolean; | |
| 60 | }; | |
| 61 | export 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 | }; | |
| 68 | export type PatternParameters = { name: string; operator: PatternOperator; pattern: string; negate: boolean }; | |
| 69 | export type WeeklyWindow = { days: Weekday[]; start: string; end: string }; | |
| 70 | export type Period = { start: string; end?: string | null; reason: string }; | |
| 71 | export type MergeWindowParameters = { | |
| 72 | time_zone: string; | |
| 73 | windows: WeeklyWindow[]; | |
| 74 | freezes: Period[]; | |
| 75 | exceptions: Period[]; | |
| 76 | }; | |
| 77 | ||
| 78 | /** Every rule type, with its parameters. */ | |
| 79 | export 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 | ||
| 107 | export type RuleType = Rule["type"]; | |
| 108 | export type RuleEntry = Rule & { applies_to: AppliesTo }; | |
| 109 | ||
| 110 | /** What a ruleset says: what is created, changed, exported and imported. */ | |
| 111 | export type RulesetSpec = { | |
| 112 | name: string; | |
| 113 | enforcement: Enforcement; | |
| 114 | target: Target; | |
| 115 | conditions: Conditions; | |
| 116 | bypass_actors: BypassActor[]; | |
| 117 | rules: RuleEntry[]; | |
| 118 | }; | |
| 119 | ||
| 120 | export 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 | ||
| 134 | export type RulesetSummary = { | |
| 135 | id: string; | |
| 136 | name: string; | |
| 137 | level: Level; | |
| 138 | enforcement: Enforcement; | |
| 139 | bypass_actors: BypassActor[]; | |
| 140 | }; | |
| 141 | ||
| 142 | export type EffectiveRule = RuleEntry & { | |
| 143 | ruleset_id: string; | |
| 144 | ruleset_name: string; | |
| 145 | level: Level; | |
| 146 | enforcement: Enforcement; | |
| 147 | }; | |
| 148 | ||
| 149 | export type EffectiveRules = { | |
| 150 | name: string; | |
| 151 | target: Target; | |
| 152 | default_branch: boolean; | |
| 153 | rules: EffectiveRule[]; | |
| 154 | rulesets: RulesetSummary[]; | |
| 155 | }; | |
| 156 | ||
| 157 | export type RuleVerdict = "pass" | "fail" | "bypass"; | |
| 158 | export type Action = "push" | "merge" | "create_ref" | "delete_ref" | "rename_ref" | "commit"; | |
| 159 | ||
| 160 | export type Violation = { | |
| 161 | rule: string; | |
| 162 | ruleset_id: string; | |
| 163 | ruleset_name: string; | |
| 164 | enforcement: Enforcement; | |
| 165 | message: string; | |
| 166 | remedy: string; | |
| 167 | }; | |
| 168 | ||
| 169 | export 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 | ||
| 188 | export 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 | ||
| 207 | export 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. */ | |
| 210 | export 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. */ | |
| 223 | export type RulesetOwner = { repo: RepoPath } | { workspace: string }; | |
| 224 | ||
| 225 | export type EvaluationFilter = { | |
| 226 | ruleset_id?: string; | |
| 227 | verdict?: RuleVerdict; | |
| 228 | problems_only?: boolean; | |
| 229 | before?: string; | |
| 230 | limit?: number; | |
| 231 | }; | |
| 232 | ||
| 233 | export 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.