Files
gitea/services/actions/scoped_workflow_cache.go
T
wxiaoguangandGitHub 82edc3da01 refactor: decouple git.Repository(ctx) from git.Commit & git.Tree (#38464)
1. Storing "ctx" in a long-living object is wrong
2. Make the commit & tree cacheable (for the future performance
optimization)
3. Also fix some bad designs like `// FIXME: bad design, this field can
be nil if the commit is from "last commit cache"`

ref:
* #33893
2026-07-15 17:30:01 +00:00

85 lines
2.9 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"fmt"
git_model "gitea.dev/models/git"
repo_model "gitea.dev/models/repo"
actions_module "gitea.dev/modules/actions"
"gitea.dev/modules/gitrepo"
"gitea.dev/modules/log"
lru "github.com/hashicorp/golang-lru/v2"
)
// cachedScopedWorkflows is one source repo's parsed scoped workflows together with the default-branch SHA they were parsed at.
type cachedScopedWorkflows struct {
sha string
parsed []*actions_module.ParsedScopedWorkflow
}
// scopedWorkflowCache caches each scoped-workflow source repo's parsed workflows, keyed by source repo ID.
// There is exactly one entry per source: a default-branch update is detected by SHA mismatch and overwrites the entry, so stale parses never accumulate.
var scopedWorkflowCache *lru.Cache[int64, *cachedScopedWorkflows]
const defaultScopedWorkflowCacheSize = 1024
func init() {
c, err := lru.New[int64, *cachedScopedWorkflows](defaultScopedWorkflowCacheSize)
if err != nil {
log.Fatal("failed to new scopedWorkflowCache, err: %v", err)
}
scopedWorkflowCache = c
}
// LoadParsedScopedWorkflows returns the source repo's parsed scoped workflows at its current default-branch HEAD.
func LoadParsedScopedWorkflows(ctx context.Context, sourceRepo *repo_model.Repository) (sha string, parsed []*actions_module.ParsedScopedWorkflow, err error) {
branch, err := git_model.GetBranch(ctx, sourceRepo.ID, sourceRepo.DefaultBranch)
if err != nil {
return "", nil, fmt.Errorf("get source default branch: %w", err)
}
sha = branch.CommitID
if v, ok := scopedWorkflowCache.Get(sourceRepo.ID); ok && v.sha == sha {
// cache hit at the current default-branch HEAD
return sha, v.parsed, nil
}
// cache miss: open the source repo at the exact SHA we keyed on
sourceGitRepo, err := gitrepo.OpenRepository(ctx, sourceRepo)
if err != nil {
return "", nil, fmt.Errorf("open source repo: %w", err)
}
defer sourceGitRepo.Close()
sourceCommit, err := sourceGitRepo.GetCommit(sha)
if err != nil {
return "", nil, fmt.Errorf("get source commit %s: %w", sha, err)
}
parsed, err = actions_module.ParseScopedWorkflows(ctx, sourceGitRepo, sourceCommit)
if err != nil {
return "", nil, err
}
// overwrite this source's single entry (a stale entry from a previous HEAD is replaced, not accumulated)
scopedWorkflowCache.Add(sourceRepo.ID, &cachedScopedWorkflows{sha: sha, parsed: parsed})
return sha, parsed, nil
}
// ScopedWorkflowContent returns one scoped workflow's raw content (by entry name) at the source repo's current default-branch HEAD, or nil if no such workflow exists there.
func ScopedWorkflowContent(ctx context.Context, sourceRepo *repo_model.Repository, entryName string) ([]byte, error) {
_, parsed, err := LoadParsedScopedWorkflows(ctx, sourceRepo)
if err != nil {
return nil, err
}
for _, p := range parsed {
if p.EntryName == entryName {
return p.Content, nil
}
}
return nil, nil
}