chore: initialize Gitea v1.27.2 fork
giteabot backport / giteabot (push) Canceled after 0s
giteabot / giteabot (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
cache-seeder / gobuild (push) Canceled after 0s
cache-seeder / lint (bindata, lint-backend) (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s
giteabot backport / giteabot (push) Canceled after 0s
giteabot / giteabot (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
cache-seeder / gobuild (push) Canceled after 0s
cache-seeder / lint (bindata, lint-backend) (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s
Includes direct password setup links in registration emails. Assisted-by: Codex:GPT-5
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ActionScopedWorkflowSource registers a repository as a source of scoped workflows, either for an owner (user/org) or for the whole instance.
|
||||
type ActionScopedWorkflowSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
||||
// OwnerID is the scope the source applies to: a user/org ID (applies to that owner's repos), or 0 for instance-level (applies to every repo).
|
||||
OwnerID int64 `xorm:"UNIQUE(owner_repo) NOT NULL DEFAULT 0"`
|
||||
// SourceRepoID is the source repository providing the workflow files; always non-zero.
|
||||
SourceRepoID int64 `xorm:"INDEX UNIQUE(owner_repo) NOT NULL DEFAULT 0"`
|
||||
|
||||
// WorkflowConfigs maps a workflow ID (entry name) to its merge-gate config.
|
||||
WorkflowConfigs map[string]*ScopedWorkflowConfig `xorm:"JSON TEXT 'workflow_configs'"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
// ScopedWorkflowConfig is one scoped workflow's config within a source registration.
|
||||
type ScopedWorkflowConfig struct {
|
||||
Required bool `json:"required"`
|
||||
Patterns []string `json:"patterns"` // the status-check patterns that must be present and pass, only effective when Required is true
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(ActionScopedWorkflowSource))
|
||||
}
|
||||
|
||||
// IsWorkflowRequired reports whether the given workflow ID (entry name) is marked required in this source.
|
||||
func (s *ActionScopedWorkflowSource) IsWorkflowRequired(workflowID string) bool {
|
||||
c, ok := s.WorkflowConfigs[workflowID]
|
||||
return ok && c.Required
|
||||
}
|
||||
|
||||
type FindScopedWorkflowSourceOpts struct {
|
||||
db.ListOptions
|
||||
OwnerIDs []int64
|
||||
SourceRepoID int64
|
||||
}
|
||||
|
||||
func (opts FindScopedWorkflowSourceOpts) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if len(opts.OwnerIDs) > 0 {
|
||||
cond = cond.And(builder.In("owner_id", opts.OwnerIDs))
|
||||
}
|
||||
if opts.SourceRepoID != 0 {
|
||||
cond = cond.And(builder.Eq{"source_repo_id": opts.SourceRepoID})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
// GetEffectiveScopedWorkflowSources returns the scoped-workflow sources effective for a repo owned by repoOwnerID:
|
||||
// the owner's own sources plus instance-level (owner_id=0) sources.
|
||||
func GetEffectiveScopedWorkflowSources(ctx context.Context, repoOwnerID int64) ([]*ActionScopedWorkflowSource, error) {
|
||||
owners := []int64{0}
|
||||
if repoOwnerID != 0 {
|
||||
owners = append(owners, repoOwnerID)
|
||||
}
|
||||
return db.Find[ActionScopedWorkflowSource](ctx, FindScopedWorkflowSourceOpts{OwnerIDs: owners})
|
||||
}
|
||||
|
||||
// IsScopedWorkflowSourceEffective reports whether sourceRepoID is a scoped-workflow source effective for a repo owned by repoOwnerID.
|
||||
func IsScopedWorkflowSourceEffective(ctx context.Context, repoOwnerID, sourceRepoID int64) (bool, error) {
|
||||
owners := []int64{0}
|
||||
if repoOwnerID != 0 {
|
||||
owners = append(owners, repoOwnerID)
|
||||
}
|
||||
return db.Exist[ActionScopedWorkflowSource](ctx, FindScopedWorkflowSourceOpts{OwnerIDs: owners, SourceRepoID: sourceRepoID}.ToConds())
|
||||
}
|
||||
|
||||
// IsWorkflowRequiredInSources reports whether workflowID from sourceRepoID is required by any of the given sources.
|
||||
func IsWorkflowRequiredInSources(sources []*ActionScopedWorkflowSource, sourceRepoID int64, workflowID string) bool {
|
||||
for _, s := range sources {
|
||||
if s.SourceRepoID == sourceRepoID && s.IsWorkflowRequired(workflowID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ScopedStatusContextPrefix returns the source-repo prefix that makes a scoped run's commit-status context distinct from same-named workflows.
|
||||
func ScopedStatusContextPrefix(ctx context.Context, sourceRepoID int64) string {
|
||||
if sourceRepo, err := repo_model.GetRepositoryByID(ctx, sourceRepoID); err == nil {
|
||||
return sourceRepo.FullName()
|
||||
}
|
||||
return fmt.Sprintf("scoped:%d", sourceRepoID)
|
||||
}
|
||||
|
||||
// IsScopedWorkflowRequired reports whether workflowID from sourceRepoID is required for a repo owned by consumerOwnerID.
|
||||
func IsScopedWorkflowRequired(ctx context.Context, consumerOwnerID, sourceRepoID int64, workflowID string) (bool, error) {
|
||||
sources, err := GetEffectiveScopedWorkflowSources(ctx, consumerOwnerID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return IsWorkflowRequiredInSources(sources, sourceRepoID, workflowID), nil
|
||||
}
|
||||
|
||||
// IsScopedWorkflowOptedOutloads the consumer's effective sources then calls ScopedWorkflowOptedOut
|
||||
func IsScopedWorkflowOptedOut(ctx context.Context, cfg *repo_model.ActionsConfig, consumerOwnerID, sourceRepoID int64, workflowID string) (bool, error) {
|
||||
if !cfg.IsScopedWorkflowDisabled(sourceRepoID, workflowID) {
|
||||
return false, nil
|
||||
}
|
||||
sources, err := GetEffectiveScopedWorkflowSources(ctx, consumerOwnerID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ScopedWorkflowOptedOut(cfg, sources, sourceRepoID, workflowID), nil
|
||||
}
|
||||
|
||||
// ScopedWorkflowOptedOut reports whether a consumer's opt-out of (sourceRepoID, workflowID) is in effect.
|
||||
func ScopedWorkflowOptedOut(cfg *repo_model.ActionsConfig, sources []*ActionScopedWorkflowSource, sourceRepoID int64, workflowID string) bool {
|
||||
return !IsWorkflowRequiredInSources(sources, sourceRepoID, workflowID) && cfg.IsScopedWorkflowDisabled(sourceRepoID, workflowID)
|
||||
}
|
||||
|
||||
// GetScopedWorkflowSourcesByOwner returns the sources an owner (user/org, or 0 for instance) registered.
|
||||
func GetScopedWorkflowSourcesByOwner(ctx context.Context, ownerID int64) ([]*ActionScopedWorkflowSource, error) {
|
||||
return db.Find[ActionScopedWorkflowSource](ctx, FindScopedWorkflowSourceOpts{OwnerIDs: []int64{ownerID}})
|
||||
}
|
||||
|
||||
// GetScopedWorkflowSource returns the (owner, repo) source registration or a NotExist error.
|
||||
func GetScopedWorkflowSource(ctx context.Context, ownerID, repoID int64) (*ActionScopedWorkflowSource, error) {
|
||||
src := &ActionScopedWorkflowSource{}
|
||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND source_repo_id = ?", ownerID, repoID).Get(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, util.NewNotExistErrorf("scoped workflow source (owner %d, repo %d) does not exist", ownerID, repoID)
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// AddScopedWorkflowSource registers repoID as a source for ownerID (no-op if already registered).
|
||||
func AddScopedWorkflowSource(ctx context.Context, ownerID, repoID int64) error {
|
||||
exists, err := db.GetEngine(ctx).Where("owner_id = ? AND source_repo_id = ?", ownerID, repoID).Exist(new(ActionScopedWorkflowSource))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
if err := db.Insert(ctx, &ActionScopedWorkflowSource{OwnerID: ownerID, SourceRepoID: repoID}); err != nil {
|
||||
// Re-check and treat an already-present row as the intended no-op.
|
||||
if exists, existErr := db.GetEngine(ctx).Where("owner_id = ? AND source_repo_id = ?", ownerID, repoID).Exist(new(ActionScopedWorkflowSource)); existErr == nil && exists {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetScopedWorkflowSourceConfigs replaces the per-workflow merge-gate configs (workflow ID -> config).
|
||||
func SetScopedWorkflowSourceConfigs(ctx context.Context, ownerID, repoID int64, configs map[string]*ScopedWorkflowConfig) error {
|
||||
_, err := db.GetEngine(ctx).Where("owner_id = ? AND source_repo_id = ?", ownerID, repoID).
|
||||
Cols("workflow_configs").
|
||||
Update(&ActionScopedWorkflowSource{WorkflowConfigs: configs})
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveScopedWorkflowSource removes the (owner, repo) source registration.
|
||||
func RemoveScopedWorkflowSource(ctx context.Context, ownerID, repoID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("owner_id = ? AND source_repo_id = ?", ownerID, repoID).Delete(new(ActionScopedWorkflowSource))
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user