Skip to content
quarry Artifact [28f18e1f29]
Login

Artifact [28f18e1f29]

Artifact 28f18e1f29b6d1bb2fc54f4f037efe39cdf2723760b5e57b7a2a98c07d0e2e72:


package oltp

import "strings"

// Required checks (RFC 0009 §verdicts): per-branch names that must be
// green before an accept proceeds. Stored beside RFC 0007's protection
// rules and matched the same way — a branch name or a glob — but in
// their own table, because a branch may require checks without naming
// owners, and blanking owners must keep meaning "rule removed".

const checksSchema = `
CREATE TABLE IF NOT EXISTS branch_required_checks (
	repo   TEXT NOT NULL,
	branch TEXT NOT NULL, -- a name or a glob: trunk, release-*
	checks TEXT NOT NULL, -- check names, space-separated
	PRIMARY KEY (repo, branch)
);`

// CheckRule is one row: which branches, which checks.
type CheckRule struct {
	Branch string
	Checks []string
}

// CheckRules lists a repository's rules, for the settings page.
func (d *DB) CheckRules(repo string) ([]CheckRule, error) {
	rows, err := d.db.Query(`SELECT branch, checks FROM branch_required_checks
		WHERE repo = ? ORDER BY branch`, repo)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []CheckRule
	for rows.Next() {
		var c CheckRule
		var checks string
		if err := rows.Scan(&c.Branch, &checks); err != nil {
			return nil, err
		}
		c.Checks = strings.Fields(checks)
		out = append(out, c)
	}
	return out, rows.Err()
}

// SetCheckRule writes one rule; no checks removes it, like protection.
func (d *DB) SetCheckRule(repo, branch string, checks []string) error {
	branch = strings.TrimSpace(branch)
	if branch == "" {
		return nil
	}
	names := normalizeOwners(checks)
	if len(names) == 0 {
		_, err := d.db.Exec(`DELETE FROM branch_required_checks WHERE repo = ? AND branch = ?`,
			repo, branch)
		return err
	}
	_, err := d.db.Exec(`INSERT INTO branch_required_checks (repo, branch, checks)
		VALUES (?, ?, ?)
		ON CONFLICT (repo, branch) DO UPDATE SET checks = excluded.checks`,
		repo, branch, strings.Join(names, " "))
	return err
}

// RequiredChecks answers what a target branch requires: every matching
// rule applies, exactly as protection reads (RFC 0007's matching).
func (d *DB) RequiredChecks(repo, branch string) []string {
	rules, err := d.CheckRules(repo)
	if err != nil {
		return nil
	}
	seen := map[string]bool{}
	var out []string
	for _, rule := range rules {
		if !branchMatches(rule.Branch, branch) {
			continue
		}
		for _, c := range rule.Checks {
			if !seen[c] {
				seen[c] = true
				out = append(out, c)
			}
		}
	}
	return out
}