Files
MokoGitea/models/git/org_protected_branch.go
Jonathan Miller c572fcfe04
PR RC Release / Build RC Release (pull_request) Failing after 0s
Branch Policy Check / Verify merge target (pull_request) Failing after 0s
chore(core): rename Go module from code.gitea.io/gitea to MokoGitea namespace
Rename the Go module path from code.gitea.io/gitea to
git.mokoconsulting.tech/MokoConsulting/MokoGitea across the entire
codebase.

Scope:
- go.mod module declaration
- 2,235 Go source files (import paths)
- Dockerfile WORKDIR and COPY paths
- Swagger API templates
- golangci.yml linter config

External dependencies (code.gitea.io/gitea-vet, code.gitea.io/sdk/gitea,
gitea.com/gitea/act, etc.) are intentionally NOT renamed — they are
separate upstream modules.

Closes #132

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 00:22:38 -05:00

198 lines
8.0 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"context"
"fmt"
"strings"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/glob"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
"xorm.io/builder"
)
// OrgProtectedBranch represents an org-level branch protection ruleset.
// These rules cascade to all repositories within the organization.
type OrgProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"UNIQUE(s) index"`
RuleName string `xorm:"UNIQUE(s)"` // branch glob pattern
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
globRule glob.Glob `xorm:"-"`
isPlainName bool `xorm:"-"`
CanPush bool `xorm:"NOT NULL DEFAULT false"`
EnableWhitelist bool `xorm:"NOT NULL DEFAULT false"`
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
CanForcePush bool `xorm:"NOT NULL DEFAULT false"`
EnableForcePushAllowlist bool `xorm:"NOT NULL DEFAULT false"`
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
StatusCheckContexts []string `xorm:"JSON TEXT"`
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
ProtectedFilePatterns string `xorm:"TEXT"`
UnprotectedFilePatterns string `xorm:"TEXT"`
BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
func init() {
db.RegisterModel(new(OrgProtectedBranch))
}
func (o *OrgProtectedBranch) loadGlob() {
if o.isPlainName || o.globRule != nil {
return
}
if !IsRuleNameSpecial(o.RuleName) {
o.isPlainName = true
return
}
var err error
o.globRule, err = glob.Compile(o.RuleName, '/')
if err != nil {
log.Warn("Invalid glob rule for OrgProtectedBranch[%d]: %s %v", o.ID, o.RuleName, err)
o.globRule = glob.MustCompile(glob.QuoteMeta(o.RuleName), '/')
}
}
// Match tests if branchName matches this org rule
func (o *OrgProtectedBranch) Match(branchName string) bool {
o.loadGlob()
if o.isPlainName {
return strings.EqualFold(o.RuleName, branchName)
}
return o.globRule.Match(branchName)
}
// ToProtectedBranch converts an org-level rule to a ProtectedBranch for use
// in the standard enforcement path. Fields that are user-scoped (WhitelistUserIDs etc.)
// are left empty because org rules only reference teams.
func (o *OrgProtectedBranch) ToProtectedBranch() *ProtectedBranch {
return &ProtectedBranch{
ID: o.ID,
RuleName: o.RuleName,
Priority: o.Priority,
CanPush: o.CanPush,
EnableWhitelist: o.EnableWhitelist,
WhitelistTeamIDs: o.WhitelistTeamIDs,
EnableMergeWhitelist: o.EnableMergeWhitelist,
MergeWhitelistTeamIDs: o.MergeWhitelistTeamIDs,
CanForcePush: o.CanForcePush,
EnableForcePushAllowlist: o.EnableForcePushAllowlist,
ForcePushAllowlistTeamIDs: o.ForcePushAllowlistTeamIDs,
EnableStatusCheck: o.EnableStatusCheck,
StatusCheckContexts: o.StatusCheckContexts,
RequiredApprovals: o.RequiredApprovals,
EnableApprovalsWhitelist: o.EnableApprovalsWhitelist,
ApprovalsWhitelistTeamIDs: o.ApprovalsWhitelistTeamIDs,
BlockOnRejectedReviews: o.BlockOnRejectedReviews,
BlockOnOfficialReviewRequests: o.BlockOnOfficialReviewRequests,
BlockOnOutdatedBranch: o.BlockOnOutdatedBranch,
DismissStaleApprovals: o.DismissStaleApprovals,
IgnoreStaleApprovals: o.IgnoreStaleApprovals,
RequireSignedCommits: o.RequireSignedCommits,
ProtectedFilePatterns: o.ProtectedFilePatterns,
UnprotectedFilePatterns: o.UnprotectedFilePatterns,
BlockAdminMergeOverride: o.BlockAdminMergeOverride,
CreatedUnix: o.CreatedUnix,
UpdatedUnix: o.UpdatedUnix,
}
}
// GetOrgProtectedBranchByName retrieves a single org rule by org ID and rule name
func GetOrgProtectedBranchByName(ctx context.Context, orgID int64, ruleName string) (*OrgProtectedBranch, error) {
rule, exist, err := db.Get[OrgProtectedBranch](ctx, builder.Eq{"org_id": orgID, "rule_name": ruleName})
if err != nil {
return nil, err
} else if !exist {
return nil, nil //nolint:nilnil
}
return rule, nil
}
// GetOrgProtectedBranchByID retrieves a single org rule by ID
func GetOrgProtectedBranchByID(ctx context.Context, orgID, id int64) (*OrgProtectedBranch, error) {
rule, exist, err := db.Get[OrgProtectedBranch](ctx, builder.Eq{"org_id": orgID, "id": id})
if err != nil {
return nil, err
} else if !exist {
return nil, nil //nolint:nilnil
}
return rule, nil
}
// FindOrgProtectedBranchRules loads all org-level branch protection rules
func FindOrgProtectedBranchRules(ctx context.Context, orgID int64) ([]*OrgProtectedBranch, error) {
var rules []*OrgProtectedBranch
err := db.GetEngine(ctx).Where("org_id = ?", orgID).Asc("priority", "created_unix").Find(&rules)
return rules, err
}
// CreateOrgProtectedBranch creates a new org-level branch protection rule
func CreateOrgProtectedBranch(ctx context.Context, rule *OrgProtectedBranch) error {
if rule.Priority == 0 {
var maxPrio int64
if _, err := db.GetEngine(ctx).SQL(`SELECT MAX(priority) FROM org_protected_branch WHERE org_id = ?`, rule.OrgID).
Get(&maxPrio); err != nil {
return err
}
rule.Priority = maxPrio + 1
}
_, err := db.GetEngine(ctx).Insert(rule)
if err != nil {
return fmt.Errorf("Insert OrgProtectedBranch: %v", err)
}
return nil
}
// UpdateOrgProtectedBranch updates an existing org-level branch protection rule
func UpdateOrgProtectedBranch(ctx context.Context, rule *OrgProtectedBranch) error {
_, err := db.GetEngine(ctx).ID(rule.ID).AllCols().Update(rule)
if err != nil {
return fmt.Errorf("Update OrgProtectedBranch: %v", err)
}
return nil
}
// DeleteOrgProtectedBranch deletes an org-level branch protection rule
func DeleteOrgProtectedBranch(ctx context.Context, orgID, id int64) error {
affected, err := db.GetEngine(ctx).Where("org_id = ? AND id = ?", orgID, id).Delete(new(OrgProtectedBranch))
if err != nil {
return err
}
if affected == 0 {
return fmt.Errorf("org branch protection rule ID(%d) not found", id)
}
return nil
}
// FindOrgBranchRuleForBranch finds the first matching org rule for a given branch name
func FindOrgBranchRuleForBranch(ctx context.Context, orgID int64, branchName string) (*OrgProtectedBranch, error) {
rules, err := FindOrgProtectedBranchRules(ctx, orgID)
if err != nil {
return nil, err
}
for _, rule := range rules {
if rule.Match(branchName) {
return rule, nil
}
}
return nil, nil //nolint:nilnil
}