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 b0d53b83c..c103aa192 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 347b7539b..f18ea087c 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 81ae97d55..33f8b00fd 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 e7aa4a39a..79dd84e0c 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 1f0484348..767bfdb88 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 6496ac401..b82f24271 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 e51d6c630..74c706404 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), + } +}