diff --git a/go.mod b/go.mod index dfd9a95ea3f53..9b70a191ce57e 100644 --- a/go.mod +++ b/go.mod @@ -302,7 +302,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.2.51 +replace github.com/nektos/act => gitea.com/gitea/act v0.259.1 replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 diff --git a/go.sum b/go.sum index 0d8e7dc699796..a44809dde5810 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.2.51 h1:gXc/B4OlTciTTzAx9cmNyw04n2SDO7exPjAsR5Idu+c= -gitea.com/gitea/act v0.2.51/go.mod h1:CoaX2053jqBlD6JMgu4d4UgFL/rp2I14Kt5mMqcs0Z0= +gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw= +gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= diff --git a/models/actions/variable.go b/models/actions/variable.go index 12717e0ae4614..14ded60fac1d4 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -82,3 +83,35 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) }) return count != 0, err } + +func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { + variables := map[string]string{} + + // Global + globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) + if err != nil { + log.Error("find global variables: %v", err) + return nil, err + } + + // Org / User level + ownerVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID}) + if err != nil { + log.Error("find variables of org: %d, error: %v", run.Repo.OwnerID, err) + return nil, err + } + + // Repo level + repoVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{RepoID: run.RepoID}) + if err != nil { + log.Error("find variables of repo: %d, error: %v", run.RepoID, err) + return nil, err + } + + // Level precedence: Repo > Org / User > Global + for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { + variables[v.Name] = v.Data + } + + return variables, nil +} diff --git a/models/issues/pull.go b/models/issues/pull.go index 7d299eac271be..80b149da5c790 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -901,12 +901,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque } defer repo.Close() - branch, err := repo.GetDefaultBranch() - if err != nil { - return err - } - - commit, err := repo.GetBranchCommit(branch) + commit, err := repo.GetBranchCommit(pr.BaseRepo.DefaultBranch) if err != nil { return err } diff --git a/models/secret/secret.go b/models/secret/secret.go index 41e860d7f6640..35bed500b937d 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -9,7 +9,10 @@ import ( "fmt" "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/log" secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -112,3 +115,39 @@ func UpdateSecret(ctx context.Context, secretID int64, data string) error { } return err } + +func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { + secrets := map[string]string{} + + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { + // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated. + // for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch + // see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target + return secrets, nil + } + + ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) + if err != nil { + log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) + return nil, err + } + repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID}) + if err != nil { + log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) + return nil, err + } + + for _, secret := range append(ownerSecrets, repoSecrets...) { + v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) + if err != nil { + log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) + return nil, err + } + secrets[secret.Name] = v + } + + return secrets, nil +} diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 979c5dec9175a..552ae2bb8c6c1 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -55,15 +55,8 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) { }, nil } -// SetDefaultBranch sets default branch of repository. -func (repo *Repository) SetDefaultBranch(name string) error { - _, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path}) - return err -} - -// GetDefaultBranch gets default branch of repository. -func (repo *Repository) GetDefaultBranch() (string, error) { - stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path}) +func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) { + stdout, _, err := NewCommand(ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repoPath}) if err != nil { return "", err } diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index dcaf92668d158..e13a4c82e1c25 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -30,3 +30,20 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str return gitRepo.GetBranchCommitID(branch) } + +// SetDefaultBranch sets default branch of repository. +func SetDefaultBranch(ctx context.Context, repo Repository, name string) error { + _, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD"). + AddDynamicArguments(git.BranchPrefix + name). + RunStdString(&git.RunOpts{Dir: repoPath(repo)}) + return err +} + +// GetDefaultBranch gets default branch of repository. +func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) { + return git.GetDefaultBranch(ctx, repoPath(repo)) +} + +func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) { + return git.GetDefaultBranch(ctx, wikiPath(repo)) +} diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go index 7af34a6cbc2d4..12458e954ade9 100644 --- a/modules/markup/csv/csv.go +++ b/modules/markup/csv/csv.go @@ -93,8 +93,10 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil { return err } - _, err = tmpBlock.WriteString("") - return err + if _, err := tmpBlock.WriteString(""); err != nil { + return err + } + return tmpBlock.Flush() } rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes)) diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index c4b23e66fc510..67817ce27bf30 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -219,21 +219,18 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa v.SetAttributeString("class", []byte("gt-py-3 attention attention-"+attentionType)) // create an emphasis to make it bold + attentionParagraph := ast.NewParagraph() emphasis := ast.NewEmphasis(2) emphasis.SetAttributeString("class", []byte("attention-"+attentionType)) - firstParagraph.InsertBefore(firstParagraph, firstTextNode, emphasis) // capitalize first letter attentionText := ast.NewString([]byte(strings.ToUpper(string(attentionType[0])) + attentionType[1:])) - // replace the ![TYPE] with icon+Type + // replace the ![TYPE] with a dedicated paragraph of icon+Type emphasis.AppendChild(emphasis, attentionText) - for i := 0; i < 2; i++ { - lineBreak := ast.NewText() - lineBreak.SetSoftLineBreak(true) - firstParagraph.InsertAfter(firstParagraph, emphasis, lineBreak) - } - firstParagraph.InsertBefore(firstParagraph, emphasis, NewAttention(attentionType)) + attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType)) + attentionParagraph.AppendChild(attentionParagraph, emphasis) + firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph) firstParagraph.RemoveChild(firstParagraph, firstTextNode) firstParagraph.RemoveChild(firstParagraph, secondTextNode) firstParagraph.RemoveChild(firstParagraph, thirdTextNode) diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index a7cb31288cab1..ff6ec5bd54c62 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/actions" @@ -32,14 +31,24 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return nil, false, nil } + secrets, err := secret_model.GetSecretsOfTask(ctx, t) + if err != nil { + return nil, false, fmt.Errorf("GetSecretsOfTask: %w", err) + } + + vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run) + if err != nil { + return nil, false, fmt.Errorf("GetVariablesOfRun: %w", err) + } + actions.CreateCommitStatus(ctx, t.Job) task := &runnerv1.Task{ Id: t.ID, WorkflowPayload: t.Job.WorkflowPayload, Context: generateTaskContext(t), - Secrets: getSecretsOfTask(ctx, t), - Vars: getVariablesOfTask(ctx, t), + Secrets: secrets, + Vars: vars, } if needs, err := findTaskNeeds(ctx, t); err != nil { @@ -55,71 +64,6 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return task, true, nil } -func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - secrets := map[string]string{} - - secrets["GITHUB_TOKEN"] = task.Token - secrets["GITEA_TOKEN"] = task.Token - - if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { - // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated. - // for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch - // see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target - return secrets - } - - ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) - // go on - } - repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) - // go on - } - - for _, secret := range append(ownerSecrets, repoSecrets...) { - if v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data); err != nil { - log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) - // go on - } else { - secrets[secret.Name] = v - } - } - - return secrets -} - -func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - variables := map[string]string{} - - // Global - globalVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{}) - if err != nil { - log.Error("find global variables: %v", err) - } - - // Org / User level - ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) - } - - // Repo level - repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) - } - - // Level precedence: Repo > Org / User > Global - for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { - variables[v.Name] = v.Data - } - - return variables -} - func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { event := map[string]any{} _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 5f1af920414fb..80504b9c330d4 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -720,7 +720,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err if ctx.Repo.GitRepo == nil && !repo.IsEmpty { var err error - ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository) + ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, repo) if err != nil { ctx.Error(http.StatusInternalServerError, "Unable to OpenRepository", err) return err @@ -731,7 +731,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err // Default branch only updated if changed and exist or the repository is empty if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) { if !repo.IsEmpty { - if err := ctx.Repo.GitRepo.SetDefaultBranch(*opts.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err) return err diff --git a/routers/private/default_branch.go b/routers/private/default_branch.go index 2e323129ef028..33890be6a9277 100644 --- a/routers/private/default_branch.go +++ b/routers/private/default_branch.go @@ -9,6 +9,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/private" gitea_context "code.gitea.io/gitea/services/context" ) @@ -20,7 +21,7 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) { branch := ctx.Params(":branch") ctx.Repo.Repository.DefaultBranch = branch - if err := ctx.Repo.GitRepo.SetDefaultBranch(ctx.Repo.Repository.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { ctx.JSON(http.StatusInternalServerError, private.Response{ Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err), diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 88b63da88d986..df15f61b173ff 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -102,7 +102,7 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err commit, errCommit := wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch) if git.IsErrNotExist(errCommit) { // if the default branch recorded in database is out of sync, then re-sync it - gitRepoDefaultBranch, errBranch := wikiGitRepo.GetDefaultBranch() + gitRepoDefaultBranch, errBranch := gitrepo.GetWikiDefaultBranch(ctx, ctx.Repo.Repository) if errBranch != nil { return wikiGitRepo, nil, errBranch } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index b0d848b5add15..d84191dca2b17 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -296,7 +296,18 @@ func handleWorkflows( run.NeedApproval = need } - jobs, err := jobparser.Parse(dwf.Content) + if err := run.LoadAttributes(ctx); err != nil { + log.Error("LoadAttributes: %v", err) + continue + } + + vars, err := actions_model.GetVariablesOfRun(ctx, run) + if err != nil { + log.Error("GetVariablesOfRun: %v", err) + continue + } + + jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) if err != nil { log.Error("jobparser.Parse: %v", err) continue diff --git a/services/context/repo.go b/services/context/repo.go index 0b15c95e59fbc..56e9fada0e935 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -681,7 +681,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch } else { - ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch() + ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository) if ctx.Repo.BranchName == "" { // If it still can't get a default branch, fall back to default branch from setting. // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug. diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 3418cf90dfe38..de4a58f27b787 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -593,7 +593,7 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gi m.Repo.DefaultBranch = firstName } // Update the git repository default branch - if err := gitRepo.SetDefaultBranch(m.Repo.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) desc := fmt.Sprintf("Failed to update default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 7ca68776b55ab..0ac3c774b7ae9 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -127,24 +127,17 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r repo.IsEmpty = false - // Don't bother looking this repo in the context it won't be there - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if len(defaultBranch) > 0 { repo.DefaultBranch = defaultBranch - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } else { - repo.DefaultBranch, err = gitRepo.GetDefaultBranch() + repo.DefaultBranch, err = gitrepo.GetDefaultBranch(ctx, repo) if err != nil { repo.DefaultBranch = setting.Repository.DefaultBranch - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } @@ -188,7 +181,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r repo.DefaultBranch = setting.Repository.DefaultBranch } - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } @@ -197,6 +190,13 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r return fmt.Errorf("updateRepository: %w", err) } + // Don't bother looking this repo in the context it won't be there + gitRepo, err := gitrepo.OpenRepository(ctx, repo) + if err != nil { + return fmt.Errorf("openRepository: %w", err) + } + defer gitRepo.Close() + if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return fmt.Errorf("SyncReleasesWithTags: %w", err) } diff --git a/services/repository/branch.go b/services/repository/branch.go index 402814fb9a343..8d8cfa2d19d82 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -16,9 +16,11 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/queue" @@ -99,7 +101,6 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git if err != nil { return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err) } - branches = append(branches, branch) } @@ -109,10 +110,44 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git if err != nil { return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err) } - return defaultBranch, branches, totalNumOfBranches, nil } +func getDivergenceCacheKey(repoID int64, branchName string) string { + return fmt.Sprintf("%d-%s", repoID, branchName) +} + +// getDivergenceFromCache gets the divergence from cache +func getDivergenceFromCache(repoID int64, branchName string) (*git.DivergeObject, bool) { + data := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName)) + res := git.DivergeObject{ + Ahead: -1, + Behind: -1, + } + s, ok := data.([]byte) + if !ok || len(s) == 0 { + return &res, false + } + + if err := json.Unmarshal(s, &res); err != nil { + log.Error("json.UnMarshal failed: %v", err) + return &res, false + } + return &res, true +} + +func putDivergenceFromCache(repoID int64, branchName string, divergence *git.DivergeObject) error { + bs, err := json.Marshal(divergence) + if err != nil { + return err + } + return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), bs, 30*24*60*60) +} + +func DelDivergenceFromCache(repoID int64, branchName string) error { + return cache.GetCache().Delete(getDivergenceCacheKey(repoID, branchName)) +} + func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *git_model.Branch, protectedBranches *git_model.ProtectedBranchRules, repoIDToRepo map[int64]*repo_model.Repository, repoIDToGitRepo map[int64]*git.Repository, @@ -130,10 +165,18 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g // it's not default branch if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted { - var err error - divergence, err = files_service.CountDivergingCommits(ctx, repo, git.BranchPrefix+branchName) - if err != nil { - log.Error("CountDivergingCommits: %v", err) + var cached bool + divergence, cached = getDivergenceFromCache(repo.ID, dbBranch.Name) + if !cached { + var err error + divergence, err = files_service.CountDivergingCommits(ctx, repo, git.BranchPrefix+branchName) + if err != nil { + log.Error("CountDivergingCommits: %v", err) + } else { + if err = putDivergenceFromCache(repo.ID, dbBranch.Name, divergence); err != nil { + log.Error("putDivergenceFromCache: %v", err) + } + } } } @@ -375,7 +418,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m log.Error("CancelRunningJobs: %v", err) } - err2 = gitRepo.SetDefaultBranch(to) + err2 = gitrepo.SetDefaultBranch(ctx, repo, to) if err2 != nil { return err2 } @@ -540,7 +583,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR log.Error("CancelRunningJobs: %v", err) } - if err := gitRepo.SetDefaultBranch(newBranchName); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, repo, newBranchName); err != nil { if !git.IsErrUnsupportedVersion(err) { return err } diff --git a/services/repository/create.go b/services/repository/create.go index 8d8c39197dece..971793bcc6e35 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -177,12 +177,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re if len(opts.DefaultBranch) > 0 { repo.DefaultBranch = opts.DefaultBranch - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } diff --git a/services/repository/generate.go b/services/repository/generate.go index c444b60b2c50a..9b09e271ab41a 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -272,12 +272,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r repo.DefaultBranch = templateRepo.DefaultBranch } - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } if err = UpdateRepository(ctx, repo, false); err != nil { diff --git a/services/repository/migrate.go b/services/repository/migrate.go index aae2ddc1200ea..df5cc67ae1cd6 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -57,14 +57,7 @@ func cloneWiki(ctx context.Context, u *user_model.User, opts migration.MigrateOp return "", err } - wikiRepo, err := git.OpenRepository(ctx, wikiPath) - if err != nil { - cleanIncompleteWikiPath() - return "", fmt.Errorf("failed to open wiki repo %q, err: %w", wikiPath, err) - } - defer wikiRepo.Close() - - defaultBranch, err := wikiRepo.GetDefaultBranch() + defaultBranch, err := git.GetDefaultBranch(ctx, wikiPath) if err != nil { cleanIncompleteWikiPath() return "", fmt.Errorf("failed to get wiki repo default branch for %q, err: %w", wikiPath, err) diff --git a/services/repository/push.go b/services/repository/push.go index 89a3127902a59..39843249a53a7 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -182,7 +182,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { repo.DefaultBranch = refName repo.IsEmpty = false if repo.DefaultBranch != setting.Repository.DefaultBranch { - if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { return err } @@ -220,6 +220,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } } + // delete cache for divergence + if err := DelDivergenceFromCache(repo.ID, branch); err != nil { + log.Error("DelDivergenceFromCache: %v", err) + } + commits := repo_module.GitToPushCommits(l) commits.HeadCommit = repo_module.CommitToPushCommit(newCommit) diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 6f1ca120b0a27..1b921a44bdbc5 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -370,6 +370,14 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n return fmt.Errorf("unable to update database: %w", err) } + oldDefBranch, err := gitrepo.GetWikiDefaultBranch(ctx, repo) + if err != nil { + return fmt.Errorf("unable to get default branch: %w", err) + } + if oldDefBranch == newBranch { + return nil + } + gitRepo, err := gitrepo.OpenWikiRepository(ctx, repo) if errors.Is(err, util.ErrNotExist) { return nil // no git repo on storage, no need to do anything else @@ -378,14 +386,6 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n } defer gitRepo.Close() - oldDefBranch, err := gitRepo.GetDefaultBranch() - if err != nil { - return fmt.Errorf("unable to get default branch: %w", err) - } - if oldDefBranch == newBranch { - return nil - } - err = gitRepo.RenameBranch(oldDefBranch, newBranch) if err != nil { return fmt.Errorf("unable to rename default branch: %w", err) diff --git a/web_src/css/base.css b/web_src/css/base.css index 77359b36e5001..3db9cd894ca8f 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1448,7 +1448,9 @@ strong.attention-caution, span.attention-caution { } .ui.label, -.ui.menu .item > .label { +.ui.menu .item > .label, +.ui.grey.labels .label, +.ui.ui.ui.grey.label { background: var(--color-label-bg); color: var(--color-label-text); } diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css index 5137e0774c298..fbe2458ed6d30 100644 --- a/web_src/css/themes/theme-gitea-light.css +++ b/web_src/css/themes/theme-gitea-light.css @@ -228,9 +228,9 @@ --color-nav-hover-bg: #ebebeb; --color-nav-text: var(--color-text); --color-label-text: var(--color-text); - --color-label-bg: #cacaca5b; - --color-label-hover-bg: #cacacaa0; - --color-label-active-bg: #cacacaff; + --color-label-bg: #9d9d9d4b; + --color-label-hover-bg: #9d9d9da0; + --color-label-active-bg: #9d9d9dff; --color-accent: var(--color-primary-light-1); --color-small-accent: var(--color-primary-light-6); --color-active-line: #fffbdd; diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 97cd05b45bcc4..de9625b143bab 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -3,7 +3,7 @@ import {SvgIcon} from '../svg.js'; import ActionRunStatus from './ActionRunStatus.vue'; import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; -import {getCurrentLocale} from '../utils.js'; +import {formatDatetime} from '../utils/time.js'; import {renderAnsi} from '../render/ansi.js'; import {POST, DELETE} from '../modules/fetch.js'; @@ -167,7 +167,7 @@ const sfc = { const logTimeStamp = document.createElement('span'); logTimeStamp.className = 'log-time-stamp'; const date = new Date(parseFloat(line.timestamp * 1000)); - const timeStamp = date.toLocaleString(getCurrentLocale(), {timeZoneName: 'short'}); + const timeStamp = formatDatetime(date); logTimeStamp.textContent = timeStamp; toggleElem(logTimeStamp, this.timeVisible['log-time-stamp']); // for "Show seconds" diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 211253ef9aa28..ee4ade1f04745 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -105,8 +105,10 @@ async function fetchActionDoRequest(actionElem, url, opt) { showErrorToast(`server error: ${resp.status}`); } } catch (e) { - console.error('error when doRequest', e); - showErrorToast(`${i18n.network_error} ${e}`); + if (e.name !== 'AbortError') { + console.error('error when doRequest', e); + showErrorToast(`${i18n.network_error} ${e}`); + } } actionElem.classList.remove('is-loading', 'small-loading-icon'); } diff --git a/web_src/js/markup/anchors.js b/web_src/js/markup/anchors.js index 53dfa2980c0a1..03934ea215fab 100644 --- a/web_src/js/markup/anchors.js +++ b/web_src/js/markup/anchors.js @@ -2,6 +2,7 @@ import {svg} from '../svg.js'; const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6'; +// scroll to anchor while respecting the `user-content` prefix that exists on the target function scrollToAnchor(hash, initial) { // abort if the browser has already scrolled to another anchor during page load if (initial && document.querySelector(':target')) return; @@ -19,6 +20,7 @@ function scrollToAnchor(hash, initial) { export function initMarkupAnchors() { if (!document.querySelector('.markup')) return; + // create link icons for markup headings, the resulting link href will remove `user-content-` for (const heading of document.querySelectorAll(headingSelector)) { const originalId = heading.id.replace(/^user-content-/, ''); const a = document.createElement('a'); @@ -31,5 +33,18 @@ export function initMarkupAnchors() { heading.prepend(a); } + // handle user-defined `name` anchors like `[Link](#link)` linking to `Link` + for (const a of document.querySelectorAll('.markup a[href^="#"]')) { + const href = a.getAttribute('href'); + if (!href.startsWith('#user-content-')) continue; + const originalId = href.replace(/^#user-content-/, ''); + a.setAttribute('href', `#${encodeURIComponent(originalId)}`); + if (document.getElementsByName(originalId).length !== 1) { + a.addEventListener('click', (e) => { + scrollToAnchor(e.currentTarget.getAttribute('href'), false); + }); + } + } + scrollToAnchor(window.location.hash, true); } diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 27f371fd88659..489afc0ae1cfa 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -1,5 +1,6 @@ import tippy, {followCursor} from 'tippy.js'; import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; +import {formatDatetime} from '../utils/time.js'; const visibleInstances = new Set(); @@ -93,8 +94,15 @@ function attachTooltip(target, content = null) { } function switchTitleToTooltip(target) { - const title = target.getAttribute('title'); + let title = target.getAttribute('title'); if (title) { + // apply custom formatting to relative-time's tooltips + if (target.tagName.toLowerCase() === 'relative-time') { + const datetime = target.getAttribute('datetime'); + if (datetime) { + title = formatDatetime(new Date(datetime)); + } + } target.setAttribute('data-tooltip-content', title); target.setAttribute('aria-label', title); // keep the attribute, in case there are some other "[title]" selectors diff --git a/web_src/js/utils/time.js b/web_src/js/utils/time.js index 3284e893e1377..1848792c984c9 100644 --- a/web_src/js/utils/time.js +++ b/web_src/js/utils/time.js @@ -1,4 +1,5 @@ import dayjs from 'dayjs'; +import {getCurrentLocale} from '../utils.js'; // Returns an array of millisecond-timestamps of start-of-week days (Sundays) export function startDaysBetween(startDate, endDate) { @@ -44,3 +45,23 @@ export function fillEmptyStartDaysWithZeroes(startDays, data) { return Object.values(result); } + +let dateFormat; + +// format a Date object to document's locale, but with 24h format from user's current locale because this +// option is a personal preference of the user, not something that the document's locale should dictate. +export function formatDatetime(date) { + if (!dateFormat) { + // TODO: replace `hour12` with `Intl.Locale.prototype.getHourCycles` once there is broad browser support + dateFormat = new Intl.DateTimeFormat(getCurrentLocale(), { + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + hour12: !Number.isInteger(Number(new Intl.DateTimeFormat([], {hour: 'numeric'}).format())), + minute: '2-digit', + timeZoneName: 'short', + }); + } + return dateFormat.format(date); +}