import { all } from "./db"; import { matchesGlob } from "~/lib/glob"; import type { Permission } from "~/lib/types"; export type BranchProtectionRow = { id: string; repo_id: string; pattern: string; require_pull_request: number; required_approvals: number; required_checks: string; dismiss_stale_reviews: number; allow_deletions: number; enforce_admins: number; created_at: number; }; export type BranchProtection = { id: string; pattern: string; requirePullRequest: boolean; requiredApprovals: number; requiredChecks: string[]; dismissStaleReviews: boolean; allowDeletions: boolean; enforceAdmins: boolean; }; export function toProtection(row: BranchProtectionRow): BranchProtection { return { id: row.id, pattern: row.pattern, requirePullRequest: row.require_pull_request === 1, requiredApprovals: row.required_approvals, requiredChecks: JSON.parse(row.required_checks), dismissStaleReviews: row.dismiss_stale_reviews === 1, allowDeletions: row.allow_deletions === 1, enforceAdmins: row.enforce_admins === 1, }; } export async function loadProtections(repoId: string): Promise { const rows = await all( "SELECT * FROM branch_protections WHERE repo_id = ? ORDER BY created_at", repoId, ); return rows.map(toProtection); } export function protectionFor(protections: BranchProtection[], branch: string): BranchProtection | null { return ( protections.find((protection) => protection.pattern === branch) ?? protections.find((protection) => matchesGlob(protection.pattern, branch)) ?? null ); } export function bypassesProtection(protection: BranchProtection, permission: Permission): boolean { return permission === "admin" && !protection.enforceAdmins; } /** Returns a rejection reason if a direct (non-PR) ref update to a branch is not allowed. */ export function directUpdateViolation( protection: BranchProtection | null, permission: Permission, isDeletion: boolean, ): string | null { if (!protection || bypassesProtection(protection, permission)) return null; if (isDeletion && !protection.allowDeletions) return "protected branch: deletion is not allowed"; if (!isDeletion && protection.requirePullRequest) return "protected branch: changes must be made through a pull request"; return null; }