From cf78141bddbcae73633cd342efcb624dba279623 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 31 Jan 2024 22:55:12 +0800 Subject: [PATCH] Fix an actions schedule bug (#28942) In #28691, schedule plans will be deleted when a repo's actions unit is disabled. But when the unit is enabled, the schedule plans won't be created again. This PR fixes the bug. The schedule plans will be created again when the actions unit is re-enabled (cherry picked from commit adc3598a755b43e3911266d7fa575c121e16613d) --- modules/actions/workflows.go | 35 +++++++++++++++++++++++++++++ routers/api/v1/repo/wiki.go | 4 ++-- services/actions/notifier_helper.go | 32 ++++++++++++++++++++++++++ services/actions/schedule_tasks.go | 12 ++++++++-- services/convert/wiki.go | 15 ------------- services/repository/setting.go | 10 +++++++++ services/wiki/wiki_path.go | 15 +++++++++++++ 7 files changed, 104 insertions(+), 19 deletions(-) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index b0d53b83c7..c103aa1928 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -149,6 +149,41 @@ func DetectWorkflows( return workflows, schedules, nil } +func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) { + entries, err := ListWorkflows(commit) + if err != nil { + return nil, err + } + + wfs := make([]*DetectedWorkflow, 0, len(entries)) + for _, entry := range entries { + content, err := GetContentFromEntry(entry) + if err != nil { + return nil, err + } + + // one workflow may have multiple events + events, err := GetEventsFromContent(content) + if err != nil { + log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) + continue + } + for _, evt := range events { + if evt.IsSchedule() { + log.Trace("detect scheduled workflow: %q", entry.Name()) + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt, + Content: content, + } + wfs = append(wfs, dwf) + } + } + } + + return wfs, nil +} + func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool { if !canGithubEventMatch(evt.Name, triggedEvent) { return false diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index 347b7539b9..f18ea087c4 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -203,7 +203,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi } return &api.WikiPage{ - WikiPageMetaData: convert.ToWikiPageMetaData(wikiName, lastCommit, ctx.Repo.Repository), + WikiPageMetaData: wiki_service.ToWikiPageMetaData(wikiName, lastCommit, ctx.Repo.Repository), ContentBase64: content, CommitCount: commitsCount, Sidebar: sidebarContent, @@ -333,7 +333,7 @@ func ListWikiPages(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "WikiFilenameToName", err) return } - pages = append(pages, convert.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository)) + pages = append(pages, wiki_service.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository)) } ctx.SetTotalCountHeader(int64(len(entries))) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 81ae97d558..33f8b00fdc 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -530,3 +530,35 @@ func handleSchedules( return actions_model.CreateScheduleTask(ctx, crons) } + +// DetectAndHandleSchedules detects the schedule workflows on the default branch and create schedule tasks +func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) error { + gitRepo, err := gitrepo.OpenRepository(context.Background(), repo) + if err != nil { + return fmt.Errorf("git.OpenRepository: %w", err) + } + defer gitRepo.Close() + + // Only detect schedule workflows on the default branch + commit, err := gitRepo.GetCommit(repo.DefaultBranch) + if err != nil { + return fmt.Errorf("gitRepo.GetCommit: %w", err) + } + scheduleWorkflows, err := actions_module.DetectScheduledWorkflows(gitRepo, commit) + if err != nil { + return fmt.Errorf("detect schedule workflows: %w", err) + } + if len(scheduleWorkflows) == 0 { + return nil + } + + // We need a notifyInput to call handleSchedules + // Here we use the commit author as the Doer of the notifyInput + commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email) + if err != nil { + return fmt.Errorf("get user by email: %w", err) + } + notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule) + + return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) +} diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index e7aa4a39ac..79dd84e0cc 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -10,6 +10,7 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -65,8 +66,15 @@ func startTasks(ctx context.Context) error { } } - cfg := row.Repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() - if cfg.IsWorkflowDisabled(row.Schedule.WorkflowID) { + cfg, err := row.Repo.GetUnit(ctx, unit.TypeActions) + if err != nil { + if repo_model.IsErrUnitTypeNotExist(err) { + // Skip the actions unit of this repo is disabled. + continue + } + return fmt.Errorf("GetUnit: %w", err) + } + if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) { continue } diff --git a/services/convert/wiki.go b/services/convert/wiki.go index 1f04843483..767bfdb88d 100644 --- a/services/convert/wiki.go +++ b/services/convert/wiki.go @@ -6,11 +6,8 @@ package convert import ( "time" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - wiki_service "code.gitea.io/gitea/services/wiki" ) // ToWikiCommit convert a git commit into a WikiCommit @@ -46,15 +43,3 @@ func ToWikiCommitList(commits []*git.Commit, total int64) *api.WikiCommitList { Count: total, } } - -// ToWikiPageMetaData converts meta information to a WikiPageMetaData -func ToWikiPageMetaData(wikiName wiki_service.WebPath, lastCommit *git.Commit, repo *repo_model.Repository) *api.WikiPageMetaData { - subURL := string(wikiName) - _, title := wiki_service.WebPathToUserTitle(wikiName) - return &api.WikiPageMetaData{ - Title: title, - HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", subURL), - SubURL: subURL, - LastCommit: ToWikiCommit(lastCommit), - } -} diff --git a/services/repository/setting.go b/services/repository/setting.go index 6496ac4014..b82f24271e 100644 --- a/services/repository/setting.go +++ b/services/repository/setting.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/log" + actions_service "code.gitea.io/gitea/services/actions" ) // UpdateRepositoryUnits updates a repository's units @@ -33,6 +34,15 @@ func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, uni } } + for _, u := range units { + if u.Type == unit.TypeActions { + if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil { + log.Error("DetectAndHandleSchedules: %v", err) + } + break + } + } + if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil { return err } diff --git a/services/wiki/wiki_path.go b/services/wiki/wiki_path.go index e51d6c630c..74c7064043 100644 --- a/services/wiki/wiki_path.go +++ b/services/wiki/wiki_path.go @@ -9,7 +9,10 @@ import ( "strings" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/convert" ) // To define the wiki related concepts: @@ -155,3 +158,15 @@ func UserTitleToWebPath(base, title string) WebPath { } return WebPath(title) } + +// ToWikiPageMetaData converts meta information to a WikiPageMetaData +func ToWikiPageMetaData(wikiName WebPath, lastCommit *git.Commit, repo *repo_model.Repository) *api.WikiPageMetaData { + subURL := string(wikiName) + _, title := WebPathToUserTitle(wikiName) + return &api.WikiPageMetaData{ + Title: title, + HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", subURL), + SubURL: subURL, + LastCommit: convert.ToWikiCommit(lastCommit), + } +}