Batch-load related data in actions run, job, and task API endpoints (#37032)
Avoid per-item DB queries in ListRuns, ListJobs, and ListActionTasks by batch-loading trigger users, repositories, and task attributes before the conversion loop. Remove ReferencesGitRepo from the /actions route group since no task/run endpoints use it. Added tests for these endpoints as well. --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -71,11 +71,11 @@ func init() {
|
|||||||
db.RegisterModel(new(ActionRunIndex))
|
db.RegisterModel(new(ActionRunIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) HTMLURL() string {
|
func (run *ActionRun) HTMLURL(ctxOpt ...context.Context) string {
|
||||||
if run.Repo == nil {
|
if run.Repo == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(), run.ID)
|
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(ctxOpt...), run.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) Link() string {
|
func (run *ActionRun) Link() string {
|
||||||
@@ -120,11 +120,7 @@ func (run *ActionRun) RefTooltip() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadAttributes load Repo TriggerUser if not loaded
|
// LoadAttributes load Repo TriggerUser if not loaded
|
||||||
func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) {
|
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||||
if run == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := run.LoadRepo(ctx); err != nil {
|
if err := run.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -133,18 +129,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if run.TriggerUser == nil {
|
return run.LoadTriggerUser(ctx)
|
||||||
run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
func (run *ActionRun) LoadTriggerUser(ctx context.Context) (err error) {
|
||||||
|
if run.TriggerUser != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) LoadRepo(ctx context.Context) error {
|
func (run *ActionRun) LoadRepo(ctx context.Context) error {
|
||||||
if run == nil || run.Repo != nil {
|
if run.Repo != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ func (attempt *ActionRunAttempt) Duration() time.Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (attempt *ActionRunAttempt) LoadAttributes(ctx context.Context) (err error) {
|
func (attempt *ActionRunAttempt) LoadAttributes(ctx context.Context) (err error) {
|
||||||
if attempt == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if attempt.Run == nil {
|
if attempt.Run == nil {
|
||||||
run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID)
|
run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -120,10 +120,6 @@ func (job *ActionRunJob) LoadRepo(ctx context.Context) error {
|
|||||||
|
|
||||||
// LoadAttributes load Run if not loaded
|
// LoadAttributes load Run if not loaded
|
||||||
func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
|
func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
|
||||||
if job == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := job.LoadRun(ctx); err != nil {
|
if err := job.LoadRun(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, j := range jobs {
|
for _, j := range jobs {
|
||||||
if j.RunID > 0 && j.Run == nil {
|
if j.Run == nil {
|
||||||
j.Run = runs[j.RunID]
|
j.Run = runs[j.RunID]
|
||||||
|
}
|
||||||
|
if j.Run != nil {
|
||||||
j.Run.Repo = j.Repo
|
j.Run.Repo = j.Repo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
@@ -17,27 +18,39 @@ import (
|
|||||||
|
|
||||||
type RunList []*ActionRun
|
type RunList []*ActionRun
|
||||||
|
|
||||||
// GetUserIDs returns a slice of user's id
|
|
||||||
func (runs RunList) GetUserIDs() []int64 {
|
|
||||||
return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
|
|
||||||
return run.TriggerUserID, true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runs RunList) LoadTriggerUser(ctx context.Context) error {
|
func (runs RunList) LoadTriggerUser(ctx context.Context) error {
|
||||||
userIDs := runs.GetUserIDs()
|
userIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
|
||||||
|
return run.TriggerUserID, run.TriggerUser == nil
|
||||||
|
})
|
||||||
users := make(map[int64]*user_model.User, len(userIDs))
|
users := make(map[int64]*user_model.User, len(userIDs))
|
||||||
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil {
|
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, run := range runs {
|
for _, run := range runs {
|
||||||
if run.TriggerUserID == user_model.ActionsUserID {
|
if run.TriggerUser != nil {
|
||||||
run.TriggerUser = user_model.NewActionsUser()
|
continue
|
||||||
} else {
|
}
|
||||||
run.TriggerUser = users[run.TriggerUserID]
|
run.TriggerUser = users[run.TriggerUserID]
|
||||||
if run.TriggerUser == nil {
|
if run.TriggerUserID < 0 {
|
||||||
run.TriggerUser = user_model.NewGhostUser()
|
run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
||||||
}
|
} else if run.TriggerUser == nil {
|
||||||
|
run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, user_model.GhostUserID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runs RunList) LoadRepos(ctx context.Context) error {
|
||||||
|
repoIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
|
||||||
|
return run.RepoID, run.Repo == nil
|
||||||
|
})
|
||||||
|
repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, run := range runs {
|
||||||
|
if run.Repo == nil {
|
||||||
|
run.Repo = repos[run.RepoID]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -125,9 +125,6 @@ func (task *ActionTask) LoadJob(ctx context.Context) error {
|
|||||||
|
|
||||||
// LoadAttributes load Job Steps if not loaded
|
// LoadAttributes load Job Steps if not loaded
|
||||||
func (task *ActionTask) LoadAttributes(ctx context.Context) error {
|
func (task *ActionTask) LoadAttributes(ctx context.Context) error {
|
||||||
if task == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := task.LoadJob(ctx); err != nil {
|
if err := task.LoadJob(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -376,8 +376,9 @@ func (repo *Repository) CommitLink(commitID string) (result string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// APIURL returns the repository API URL
|
// APIURL returns the repository API URL
|
||||||
func (repo *Repository) APIURL() string {
|
func (repo *Repository) APIURL(ctxOpt ...context.Context) string {
|
||||||
return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
|
ctx := util.OptionalArg(ctxOpt, context.TODO())
|
||||||
|
return httplib.MakeAbsoluteURL(ctx, setting.AppSubURL+"/api/v1/repos/"+url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommitsCountCacheKey returns cache key used for commits count caching.
|
// GetCommitsCountCacheKey returns cache key used for commits count caching.
|
||||||
|
|||||||
@@ -1272,7 +1272,7 @@ func Routes() *web.Router {
|
|||||||
m.Delete("", reqRepoWriter(unit.TypeActions), repo.DeleteArtifact)
|
m.Delete("", reqRepoWriter(unit.TypeActions), repo.DeleteArtifact)
|
||||||
})
|
})
|
||||||
m.Get("/artifacts/{artifact_id}/zip", repo.DownloadArtifact)
|
m.Get("/artifacts/{artifact_id}/zip", repo.DownloadArtifact)
|
||||||
}, reqRepoReader(unit.TypeActions), context.ReferencesGitRepo(true))
|
}, reqRepoReader(unit.TypeActions))
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Combo("").Get(repo.ListDeployKeys).
|
m.Combo("").Get(repo.ListDeployKeys).
|
||||||
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
|
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
|
||||||
|
|||||||
@@ -848,6 +848,12 @@ func ListActionTasks(ctx *context.APIContext) {
|
|||||||
res := new(api.ActionTaskResponse)
|
res := new(api.ActionTaskResponse)
|
||||||
res.TotalCount = total
|
res.TotalCount = total
|
||||||
|
|
||||||
|
taskList := actions_model.TaskList(tasks)
|
||||||
|
if err := taskList.LoadAttributes(ctx); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
res.Entries = make([]*api.ActionTask, len(tasks))
|
res.Entries = make([]*api.ActionTask, len(tasks))
|
||||||
for i := range tasks {
|
for i := range tasks {
|
||||||
convertedTask, err := convert.ToActionTask(ctx, tasks[i])
|
convertedTask, err := convert.ToActionTask(ctx, tasks[i])
|
||||||
@@ -859,7 +865,7 @@ func ListActionTasks(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetLinkHeader(total, listOptions.PageSize)
|
ctx.SetLinkHeader(total, listOptions.PageSize)
|
||||||
ctx.SetTotalCountHeader(total) // Duplicates api response field but it's better to set it for consistency
|
ctx.SetTotalCountHeader(total) // Duplicates api response field, but it's better to set it for consistency
|
||||||
ctx.JSON(http.StatusOK, &res)
|
ctx.JSON(http.StatusOK, &res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1155,6 +1161,7 @@ func getCurrentRepoActionRunByID(ctx *context.APIContext) *actions_model.ActionR
|
|||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
run.Repo = ctx.Repo.Repository
|
||||||
return run
|
return run
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1226,7 +1233,7 @@ func GetWorkflowRun(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, nil)
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return
|
return
|
||||||
@@ -1275,7 +1282,7 @@ func GetWorkflowRunAttempt(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, attempt)
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, attempt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return
|
return
|
||||||
@@ -1330,7 +1337,7 @@ func RerunWorkflowRun(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, nil)
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -62,6 +63,12 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64, runAttemptI
|
|||||||
res := new(api.ActionWorkflowJobsResponse)
|
res := new(api.ActionWorkflowJobsResponse)
|
||||||
res.TotalCount = total
|
res.TotalCount = total
|
||||||
|
|
||||||
|
jobList := actions_model.ActionJobList(jobs)
|
||||||
|
if err := jobList.LoadAttributes(ctx, true); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
res.Entries = make([]*api.ActionWorkflowJob, len(jobs))
|
res.Entries = make([]*api.ActionWorkflowJob, len(jobs))
|
||||||
|
|
||||||
isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
|
isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
|
||||||
@@ -70,11 +77,11 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64, runAttemptI
|
|||||||
if isRepoLevel {
|
if isRepoLevel {
|
||||||
repository = ctx.Repo.Repository
|
repository = ctx.Repo.Repository
|
||||||
} else {
|
} else {
|
||||||
repository, err = repo_model.GetRepositoryByID(ctx, jobs[i].RepoID)
|
if jobs[i].Run == nil || jobs[i].Run.Repo == nil {
|
||||||
if err != nil {
|
ctx.APIErrorInternal(fmt.Errorf("job %d is missing its run or repository", jobs[i].ID))
|
||||||
ctx.APIErrorInternal(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
repository = jobs[i].Run.Repo
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, repository, nil, jobs[i])
|
convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, repository, nil, jobs[i])
|
||||||
@@ -169,21 +176,28 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
|
|||||||
res := new(api.ActionWorkflowRunsResponse)
|
res := new(api.ActionWorkflowRunsResponse)
|
||||||
res.TotalCount = total
|
res.TotalCount = total
|
||||||
|
|
||||||
res.Entries = make([]*api.ActionWorkflowRun, len(runs))
|
runList := actions_model.RunList(runs)
|
||||||
isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
|
if err := runList.LoadTriggerUser(ctx); err != nil {
|
||||||
for i := range runs {
|
ctx.APIErrorInternal(err)
|
||||||
var repository *repo_model.Repository
|
return
|
||||||
if isRepoLevel {
|
}
|
||||||
repository = ctx.Repo.Repository
|
|
||||||
} else {
|
|
||||||
repository, err = repo_model.GetRepositoryByID(ctx, runs[i].RepoID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.APIErrorInternal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, repository, runs[i], nil)
|
if err := runList.LoadRepos(ctx); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
repos := repo_model.RepositoryList(container.FilterSlice(runs, func(r *actions_model.ActionRun) (*repo_model.Repository, bool) {
|
||||||
|
return r.Repo, r.Repo != nil
|
||||||
|
}))
|
||||||
|
if err := repos.LoadOwners(ctx); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Entries = make([]*api.ActionWorkflowRun, len(runs))
|
||||||
|
for i := range runs {
|
||||||
|
// TODO: load run attempts in batch
|
||||||
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, runs[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -815,7 +815,8 @@ func (n *actionsNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *rep
|
|||||||
log.Error("GetActionWorkflow: %v", err)
|
log.Error("GetActionWorkflow: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil)
|
run.Repo = repo
|
||||||
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("ToActionWorkflowRun: %v", err)
|
log.Error("ToActionWorkflowRun: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -115,12 +115,12 @@ func TestToActionWorkflowRun_UsesTriggerEvent(t *testing.T) {
|
|||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 803})
|
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 803})
|
||||||
|
run.Repo = repo
|
||||||
// Scheduled runs keep Event as the registration event (push) and use TriggerEvent as the real trigger.
|
// Scheduled runs keep Event as the registration event (push) and use TriggerEvent as the real trigger.
|
||||||
run.Event = "push"
|
run.Event = "push"
|
||||||
run.TriggerEvent = "schedule"
|
run.TriggerEvent = "schedule"
|
||||||
|
|
||||||
apiRun, err := ToActionWorkflowRun(t.Context(), repo, run, nil)
|
apiRun, err := ToActionWorkflowRun(t.Context(), run, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "schedule", apiRun.Event)
|
assert.Equal(t, "schedule", apiRun.Event)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/actions"
|
"code.gitea.io/gitea/modules/actions"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@@ -222,14 +223,18 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToActionTask convert a actions_model.ActionTask to an api.ActionTask
|
// ToActionTask convert an actions_model.ActionTask to an api.ActionTask
|
||||||
func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) {
|
func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) {
|
||||||
if err := t.LoadAttributes(ctx); err != nil {
|
// don't need Steps here, only need to load job and its run
|
||||||
|
if err := t.LoadJob(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := t.Job.LoadRun(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := t.Job.Run.LoadRepo(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
url := strings.TrimSuffix(setting.AppURL, "/") + t.GetRunLink()
|
|
||||||
|
|
||||||
return &api.ActionTask{
|
return &api.ActionTask{
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
Name: t.Job.Name,
|
Name: t.Job.Name,
|
||||||
@@ -240,23 +245,25 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action
|
|||||||
DisplayTitle: t.Job.Run.Title,
|
DisplayTitle: t.Job.Run.Title,
|
||||||
Status: t.Status.String(),
|
Status: t.Status.String(),
|
||||||
WorkflowID: t.Job.Run.WorkflowID,
|
WorkflowID: t.Job.Run.WorkflowID,
|
||||||
URL: url,
|
URL: httplib.MakeAbsoluteURL(ctx, t.Job.Run.Link()),
|
||||||
CreatedAt: t.Created.AsLocalTime(),
|
CreatedAt: t.Created.AsLocalTime(),
|
||||||
UpdatedAt: t.Updated.AsLocalTime(),
|
UpdatedAt: t.Updated.AsLocalTime(),
|
||||||
RunStartedAt: t.Started.AsLocalTime(),
|
RunStartedAt: t.Started.AsLocalTime(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (*api.ActionWorkflowRun, error) {
|
func ToActionWorkflowRun(ctx context.Context, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (_ *api.ActionWorkflowRun, err error) {
|
||||||
if err := run.LoadAttributes(ctx); err != nil {
|
if err := run.LoadRepo(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := run.LoadTriggerUser(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if attempt == nil {
|
if attempt == nil {
|
||||||
if latestAttempt, has, err := run.GetLatestAttempt(ctx); err != nil {
|
attempt, _, err = run.GetLatestAttempt(ctx)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if has {
|
|
||||||
attempt = latestAttempt
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +279,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
|
|||||||
var previousAttemptURL *string
|
var previousAttemptURL *string
|
||||||
|
|
||||||
if attempt != nil {
|
if attempt != nil {
|
||||||
|
attempt.Run = run
|
||||||
if err := attempt.LoadAttributes(ctx); err != nil {
|
if err := attempt.LoadAttributes(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -281,16 +289,15 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
|
|||||||
completedAt = attempt.Stopped.AsLocalTime()
|
completedAt = attempt.Stopped.AsLocalTime()
|
||||||
triggerUser = attempt.TriggerUser
|
triggerUser = attempt.TriggerUser
|
||||||
if attempt.Attempt > 1 {
|
if attempt.Attempt > 1 {
|
||||||
url := fmt.Sprintf("%s/actions/runs/%d/attempts/%d", repo.APIURL(), run.ID, attempt.Attempt-1)
|
previousAttemptURL = new(fmt.Sprintf("%s/actions/runs/%d/attempts/%d", run.Repo.APIURL(ctx), run.ID, attempt.Attempt-1))
|
||||||
previousAttemptURL = &url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ActionWorkflowRun{
|
return &api.ActionWorkflowRun{
|
||||||
ID: run.ID,
|
ID: run.ID,
|
||||||
URL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), run.ID),
|
URL: fmt.Sprintf("%s/actions/runs/%d", run.Repo.APIURL(ctx), run.ID),
|
||||||
PreviousAttemptURL: previousAttemptURL,
|
PreviousAttemptURL: previousAttemptURL,
|
||||||
HTMLURL: run.HTMLURL(),
|
HTMLURL: run.HTMLURL(ctx),
|
||||||
RunNumber: run.Index,
|
RunNumber: run.Index,
|
||||||
RunAttempt: runAttempt,
|
RunAttempt: runAttempt,
|
||||||
StartedAt: startedAt,
|
StartedAt: startedAt,
|
||||||
@@ -302,7 +309,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
|
|||||||
Status: status,
|
Status: status,
|
||||||
Conclusion: conclusion,
|
Conclusion: conclusion,
|
||||||
Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref),
|
Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref),
|
||||||
Repository: ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
|
Repository: ToRepo(ctx, run.Repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
|
||||||
TriggerActor: ToUser(ctx, triggerUser, nil),
|
TriggerActor: ToUser(ctx, triggerUser, nil),
|
||||||
Actor: ToUser(ctx, actor, nil),
|
Actor: ToUser(ctx, actor, nil),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -400,11 +407,11 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
|
|||||||
return &api.ActionWorkflowJob{
|
return &api.ActionWorkflowJob{
|
||||||
ID: job.ID,
|
ID: job.ID,
|
||||||
// missing api endpoint for this location
|
// missing api endpoint for this location
|
||||||
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID),
|
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(ctx), job.ID),
|
||||||
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), job.ID),
|
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(ctx), job.ID),
|
||||||
RunID: job.RunID,
|
RunID: job.RunID,
|
||||||
// Missing api endpoint for this location, artifacts are available under a nested url
|
// Missing api endpoint for this location, artifacts are available under a nested url
|
||||||
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
|
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(ctx), job.RunID),
|
||||||
Name: job.Name,
|
Name: job.Name,
|
||||||
Labels: job.RunsOn,
|
Labels: job.RunsOn,
|
||||||
RunAttempt: job.Attempt,
|
RunAttempt: job.Attempt,
|
||||||
|
|||||||
@@ -1043,7 +1043,8 @@ func (*webhookNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil)
|
run.Repo = repo
|
||||||
|
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("ToActionWorkflowRun: %v", err)
|
log.Error("ToActionWorkflowRun: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -15,17 +15,35 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPIActionsGetWorkflowRun(t *testing.T) {
|
func TestAPIActionsWorkflowRun(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
defer prepareTestEnvActionsArtifacts(t)()
|
||||||
|
t.Run("GetWorkflowRun", testAPIActionsGetWorkflowRun)
|
||||||
|
t.Run("GetWorkflowJob", testAPIActionsGetWorkflowJob)
|
||||||
|
t.Run("ListUserWorkflows", testAPIActionsListUserWorkflows)
|
||||||
|
t.Run("ListRepoWorkflows", testAPIActionsListRepoWorkflows)
|
||||||
|
t.Run("DeleteRunCheckPermission", testAPIActionsDeleteRunCheckPermission)
|
||||||
|
t.Run("DeleteRunRunning", testAPIActionsDeleteRunRunning)
|
||||||
|
t.Run("DeleteRunGeneral", testAPIActionsDeleteRunGeneral)
|
||||||
|
|
||||||
|
t.Run("RerunWorkflowRun", func(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
testAPIActionsRerunWorkflowRun(t)
|
||||||
|
})
|
||||||
|
t.Run("RerunWorkflowJob", func(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
testAPIActionsRerunWorkflowJob(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIActionsGetWorkflowRun(t *testing.T) {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
session := loginUser(t, user.Name)
|
session := loginUser(t, user.Name)
|
||||||
@@ -56,13 +74,9 @@ func TestAPIActionsGetWorkflowRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs", repo.FullName())).
|
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs", repo.FullName())).AddTokenAuth(token)
|
||||||
AddTokenAuth(token)
|
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
jobList := DecodeJSON(t, resp, &api.ActionWorkflowJobsResponse{})
|
||||||
var jobList api.ActionWorkflowJobsResponse
|
|
||||||
err = json.Unmarshal(resp.Body.Bytes(), &jobList)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
job198Idx := slices.IndexFunc(jobList.Entries, func(job *api.ActionWorkflowJob) bool { return job.ID == 198 })
|
job198Idx := slices.IndexFunc(jobList.Entries, func(job *api.ActionWorkflowJob) bool { return job.ID == 198 })
|
||||||
require.NotEqual(t, -1, job198Idx, "expected to find job 198 in run 795 jobs list")
|
require.NotEqual(t, -1, job198Idx, "expected to find job 198 in run 795 jobs list")
|
||||||
@@ -72,9 +86,7 @@ func TestAPIActionsGetWorkflowRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsGetWorkflowJob(t *testing.T) {
|
func testAPIActionsGetWorkflowJob(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
session := loginUser(t, user.Name)
|
session := loginUser(t, user.Name)
|
||||||
@@ -91,9 +103,7 @@ func TestAPIActionsGetWorkflowJob(t *testing.T) {
|
|||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsDeleteRunCheckPermission(t *testing.T) {
|
func testAPIActionsDeleteRunCheckPermission(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
session := loginUser(t, user.Name)
|
session := loginUser(t, user.Name)
|
||||||
@@ -101,9 +111,7 @@ func TestAPIActionsDeleteRunCheckPermission(t *testing.T) {
|
|||||||
testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
|
testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsDeleteRun(t *testing.T) {
|
func testAPIActionsDeleteRunGeneral(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
session := loginUser(t, user.Name)
|
session := loginUser(t, user.Name)
|
||||||
@@ -118,9 +126,7 @@ func TestAPIActionsDeleteRun(t *testing.T) {
|
|||||||
testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
|
testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsDeleteRunRunning(t *testing.T) {
|
func testAPIActionsDeleteRunRunning(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
session := loginUser(t, user.Name)
|
session := loginUser(t, user.Name)
|
||||||
@@ -138,22 +144,17 @@ func testAPIActionsDeleteRun(t *testing.T, repo *repo_model.Repository, token st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testAPIActionsDeleteRunListArtifacts(t *testing.T, repo *repo_model.Repository, token string, artifacts int) {
|
func testAPIActionsDeleteRunListArtifacts(t *testing.T, repo *repo_model.Repository, token string, artifacts int) {
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/artifacts", repo.FullName())).
|
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/artifacts", repo.FullName())).AddTokenAuth(token)
|
||||||
AddTokenAuth(token)
|
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
var listResp api.ActionArtifactsResponse
|
listResp := DecodeJSON(t, resp, &api.ActionArtifactsResponse{})
|
||||||
err := json.Unmarshal(resp.Body.Bytes(), &listResp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, listResp.Entries, artifacts)
|
assert.Len(t, listResp.Entries, artifacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository, token string, expected bool) {
|
func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository, token string, expected bool) {
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/tasks", repo.FullName())).
|
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/tasks", repo.FullName())).AddTokenAuth(token)
|
||||||
AddTokenAuth(token)
|
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
var listResp api.ActionTaskResponse
|
listResp := DecodeJSON(t, resp, &api.ActionTaskResponse{})
|
||||||
err := json.Unmarshal(resp.Body.Bytes(), &listResp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
findTask1 := false
|
findTask1 := false
|
||||||
findTask2 := false
|
findTask2 := false
|
||||||
for _, entry := range listResp.Entries {
|
for _, entry := range listResp.Entries {
|
||||||
@@ -170,9 +171,7 @@ func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository,
|
|||||||
assert.Equal(t, expected, findTask2)
|
assert.Equal(t, expected, findTask2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsRerunWorkflowRun(t *testing.T) {
|
func testAPIActionsRerunWorkflowRun(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
t.Run("NotDone", func(t *testing.T) {
|
t.Run("NotDone", func(t *testing.T) {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
@@ -192,13 +191,10 @@ func TestAPIActionsRerunWorkflowRun(t *testing.T) {
|
|||||||
readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||||
|
|
||||||
t.Run("Success", func(t *testing.T) {
|
t.Run("Success", func(t *testing.T) {
|
||||||
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/rerun", repo.FullName())).
|
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/rerun", repo.FullName())).AddTokenAuth(writeToken)
|
||||||
AddTokenAuth(writeToken)
|
|
||||||
resp := MakeRequest(t, req, http.StatusCreated)
|
resp := MakeRequest(t, req, http.StatusCreated)
|
||||||
|
rerunResp := DecodeJSON(t, resp, &api.ActionWorkflowRun{})
|
||||||
|
|
||||||
var rerunResp api.ActionWorkflowRun
|
|
||||||
err := json.Unmarshal(resp.Body.Bytes(), &rerunResp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, int64(795), rerunResp.ID)
|
assert.Equal(t, int64(795), rerunResp.ID)
|
||||||
assert.Equal(t, "queued", rerunResp.Status)
|
assert.Equal(t, "queued", rerunResp.Status)
|
||||||
assert.Equal(t, "c2d72f548424103f01ee1dc02889c1e2bff816b0", rerunResp.HeadSha)
|
assert.Equal(t, "c2d72f548424103f01ee1dc02889c1e2bff816b0", rerunResp.HeadSha)
|
||||||
@@ -236,9 +232,7 @@ func TestAPIActionsRerunWorkflowRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIActionsRerunWorkflowJob(t *testing.T) {
|
func testAPIActionsRerunWorkflowJob(t *testing.T) {
|
||||||
defer prepareTestEnvActionsArtifacts(t)()
|
|
||||||
|
|
||||||
t.Run("NotDone", func(t *testing.T) {
|
t.Run("NotDone", func(t *testing.T) {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
@@ -258,13 +252,10 @@ func TestAPIActionsRerunWorkflowJob(t *testing.T) {
|
|||||||
readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||||
|
|
||||||
t.Run("Success", func(t *testing.T) {
|
t.Run("Success", func(t *testing.T) {
|
||||||
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs/199/rerun", repo.FullName())).
|
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs/199/rerun", repo.FullName())).AddTokenAuth(writeToken)
|
||||||
AddTokenAuth(writeToken)
|
|
||||||
resp := MakeRequest(t, req, http.StatusCreated)
|
resp := MakeRequest(t, req, http.StatusCreated)
|
||||||
|
rerunResp := DecodeJSON(t, resp, &api.ActionWorkflowJob{})
|
||||||
|
|
||||||
var rerunResp api.ActionWorkflowJob
|
|
||||||
err := json.Unmarshal(resp.Body.Bytes(), &rerunResp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
job199Rerun := getLatestAttemptJobByTemplateJobID(t, 795, 199)
|
job199Rerun := getLatestAttemptJobByTemplateJobID(t, 795, 199)
|
||||||
assert.Equal(t, job199Rerun.ID, rerunResp.ID)
|
assert.Equal(t, job199Rerun.ID, rerunResp.ID)
|
||||||
assert.Equal(t, "queued", rerunResp.Status)
|
assert.Equal(t, "queued", rerunResp.Status)
|
||||||
@@ -301,3 +292,59 @@ func TestAPIActionsRerunWorkflowJob(t *testing.T) {
|
|||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAPIActionsListUserWorkflows(t *testing.T) {
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
session := loginUser(t, user.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser)
|
||||||
|
|
||||||
|
t.Run("Runs", func(t *testing.T) {
|
||||||
|
req := NewRequest(t, "GET", "/api/v1/user/actions/runs").AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
runs := DecodeJSON(t, resp, &api.ActionWorkflowRunsResponse{})
|
||||||
|
|
||||||
|
assert.Positive(t, runs.TotalCount)
|
||||||
|
assert.NotEmpty(t, runs.Entries)
|
||||||
|
|
||||||
|
for _, run := range runs.Entries {
|
||||||
|
assert.NotEmpty(t, run.DisplayTitle, "display_title should be populated")
|
||||||
|
assert.NotNil(t, run.Repository, "repository should be populated via batch loading")
|
||||||
|
assert.NotEmpty(t, run.Repository.FullName, "repository full_name should be populated")
|
||||||
|
assert.NotNil(t, run.TriggerActor, "trigger_actor should be populated via batch loading")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Jobs", func(t *testing.T) {
|
||||||
|
req := NewRequest(t, "GET", "/api/v1/user/actions/jobs").AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
jobs := DecodeJSON(t, resp, &api.ActionWorkflowJobsResponse{})
|
||||||
|
|
||||||
|
assert.Positive(t, jobs.TotalCount)
|
||||||
|
assert.NotEmpty(t, jobs.Entries)
|
||||||
|
|
||||||
|
for _, job := range jobs.Entries {
|
||||||
|
assert.NotEmpty(t, job.Name, "job name should be populated")
|
||||||
|
assert.NotEmpty(t, job.HTMLURL, "html_url should be populated via batch-loaded repo")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIActionsListRepoWorkflows(t *testing.T) {
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
|
session := loginUser(t, user.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs", repo.FullName())).AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
runs := DecodeJSON(t, resp, &api.ActionWorkflowRunsResponse{})
|
||||||
|
|
||||||
|
assert.Positive(t, runs.TotalCount)
|
||||||
|
assert.NotEmpty(t, runs.Entries)
|
||||||
|
|
||||||
|
for _, run := range runs.Entries {
|
||||||
|
assert.NotNil(t, run.Repository, "repository should be populated from ctx.Repo")
|
||||||
|
assert.Equal(t, repo.FullName(), run.Repository.FullName, "repository full_name should match")
|
||||||
|
assert.NotNil(t, run.TriggerActor, "trigger_actor should be populated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user