mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-04 00:01:16 -04:00
Compare commits
8 Commits
904a982120
...
12abcc132a
Author | SHA1 | Date | |
---|---|---|---|
|
12abcc132a | ||
|
ac87911f07 | ||
|
0a17b7aa64 | ||
|
1b9b410c5d | ||
|
695496c100 | ||
|
722ac05f41 | ||
|
aeaa2c1003 | ||
|
18546ed73a |
@ -5,6 +5,7 @@ package actions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
@ -64,15 +65,18 @@ func (runs RunList) LoadRepos(ctx context.Context) error {
|
|||||||
|
|
||||||
type FindRunOptions struct {
|
type FindRunOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
RepoID int64
|
RepoID int64
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
WorkflowID string
|
WorkflowID string
|
||||||
Ref string // the commit/tag/… that caused this workflow
|
Ref string // the commit/tag/... that caused this workflow
|
||||||
TriggerUserID int64
|
TriggerUserID int64
|
||||||
TriggerEvent webhook_module.HookEventType
|
TriggerEvent webhook_module.HookEventType
|
||||||
Approved bool // not util.OptionalBool, it works only when it's true
|
Approved bool // not util.OptionalBool, it works only when it's true
|
||||||
Status []Status
|
Status []Status
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
|
CreatedAfter time.Time
|
||||||
|
CreatedBefore time.Time
|
||||||
|
ExcludePullRequests bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts FindRunOptions) ToConds() builder.Cond {
|
func (opts FindRunOptions) ToConds() builder.Cond {
|
||||||
@ -101,6 +105,15 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||||||
if opts.CommitSHA != "" {
|
if opts.CommitSHA != "" {
|
||||||
cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
|
cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
|
||||||
}
|
}
|
||||||
|
if !opts.CreatedAfter.IsZero() {
|
||||||
|
cond = cond.And(builder.Gte{"`action_run`.created": opts.CreatedAfter})
|
||||||
|
}
|
||||||
|
if !opts.CreatedBefore.IsZero() {
|
||||||
|
cond = cond.And(builder.Lte{"`action_run`.created": opts.CreatedBefore})
|
||||||
|
}
|
||||||
|
if opts.ExcludePullRequests {
|
||||||
|
cond = cond.And(builder.Neq{"`action_run`.trigger_event": webhook_module.HookEventPullRequest})
|
||||||
|
}
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
88
models/actions/run_list_test.go
Normal file
88
models/actions/run_list_test.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/webhook"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindRunOptions_ToConds_ExcludePullRequests(t *testing.T) {
|
||||||
|
// Test when ExcludePullRequests is true
|
||||||
|
opts := FindRunOptions{
|
||||||
|
ExcludePullRequests: true,
|
||||||
|
}
|
||||||
|
cond := opts.ToConds()
|
||||||
|
|
||||||
|
// Convert the condition to SQL for assertion
|
||||||
|
sql, args, err := builder.ToSQL(cond)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// The condition should contain the trigger_event not equal to pull_request
|
||||||
|
assert.Contains(t, sql, "`action_run`.trigger_event<>")
|
||||||
|
assert.Contains(t, args, webhook.HookEventPullRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindRunOptions_ToConds_CreatedDateRange(t *testing.T) {
|
||||||
|
// Test when CreatedAfter and CreatedBefore are set
|
||||||
|
startDate := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
endDate := time.Date(2023, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||||
|
|
||||||
|
opts := FindRunOptions{
|
||||||
|
CreatedAfter: startDate,
|
||||||
|
CreatedBefore: endDate,
|
||||||
|
}
|
||||||
|
cond := opts.ToConds()
|
||||||
|
|
||||||
|
// Convert the condition to SQL for assertion
|
||||||
|
sql, args, err := builder.ToSQL(cond)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// The condition should contain created >= startDate and created <= endDate
|
||||||
|
assert.Contains(t, sql, "`action_run`.created>=")
|
||||||
|
assert.Contains(t, sql, "`action_run`.created<=")
|
||||||
|
assert.Contains(t, args, startDate)
|
||||||
|
assert.Contains(t, args, endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindRunOptions_ToConds_CreatedAfterOnly(t *testing.T) {
|
||||||
|
// Test when only CreatedAfter is set
|
||||||
|
startDate := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
opts := FindRunOptions{
|
||||||
|
CreatedAfter: startDate,
|
||||||
|
}
|
||||||
|
cond := opts.ToConds()
|
||||||
|
|
||||||
|
// Convert the condition to SQL for assertion
|
||||||
|
sql, args, err := builder.ToSQL(cond)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// The condition should contain created >= startDate
|
||||||
|
assert.Contains(t, sql, "`action_run`.created>=")
|
||||||
|
assert.Contains(t, args, startDate)
|
||||||
|
// But should not contain created <= endDate
|
||||||
|
assert.NotContains(t, sql, "`action_run`.created<=")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindRunOptions_ToConds_CreatedBeforeOnly(t *testing.T) {
|
||||||
|
// Test when only CreatedBefore is set
|
||||||
|
endDate := time.Date(2023, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||||
|
|
||||||
|
opts := FindRunOptions{
|
||||||
|
CreatedBefore: endDate,
|
||||||
|
}
|
||||||
|
cond := opts.ToConds()
|
||||||
|
|
||||||
|
// Convert the condition to SQL for assertion
|
||||||
|
sql, args, err := builder.ToSQL(cond)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// The condition should contain created <= endDate
|
||||||
|
assert.Contains(t, sql, "`action_run`.created<=")
|
||||||
|
assert.Contains(t, args, endDate)
|
||||||
|
// But should not contain created >= startDate
|
||||||
|
assert.NotContains(t, sql, "`action_run`.created>=")
|
||||||
|
}
|
@ -1203,6 +1203,7 @@ func Routes() *web.Router {
|
|||||||
m.Put("/{workflow_id}/disable", reqRepoWriter(unit.TypeActions), repo.ActionsDisableWorkflow)
|
m.Put("/{workflow_id}/disable", reqRepoWriter(unit.TypeActions), repo.ActionsDisableWorkflow)
|
||||||
m.Put("/{workflow_id}/enable", reqRepoWriter(unit.TypeActions), repo.ActionsEnableWorkflow)
|
m.Put("/{workflow_id}/enable", reqRepoWriter(unit.TypeActions), repo.ActionsEnableWorkflow)
|
||||||
m.Post("/{workflow_id}/dispatches", reqRepoWriter(unit.TypeActions), bind(api.CreateActionWorkflowDispatch{}), repo.ActionsDispatchWorkflow)
|
m.Post("/{workflow_id}/dispatches", reqRepoWriter(unit.TypeActions), bind(api.CreateActionWorkflowDispatch{}), repo.ActionsDispatchWorkflow)
|
||||||
|
m.Get("/{workflow_id}/runs", repo.ActionsListWorkflowRuns)
|
||||||
}, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
|
}, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
|
||||||
|
|
||||||
m.Group("/actions/jobs", func() {
|
m.Group("/actions/jobs", func() {
|
||||||
|
@ -1100,6 +1100,85 @@ func ActionsEnableWorkflow(ctx *context.APIContext) {
|
|||||||
ctx.Status(http.StatusNoContent)
|
ctx.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ActionsListWorkflowRuns(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs repository ActionsListWorkflowRuns
|
||||||
|
// ---
|
||||||
|
// summary: List workflow runs for a workflow
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: workflow_id
|
||||||
|
// in: path
|
||||||
|
// description: id of the workflow
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: actor
|
||||||
|
// in: query
|
||||||
|
// description: Returns someone's workflow runs. Use the login for the user who created the push associated with the check suite or workflow run.
|
||||||
|
// type: string
|
||||||
|
// - name: branch
|
||||||
|
// in: query
|
||||||
|
// description: Returns workflow runs associated with a branch. Use the name of the branch of the push.
|
||||||
|
// type: string
|
||||||
|
// - name: event
|
||||||
|
// in: query
|
||||||
|
// description: Returns workflow run triggered by the event you specify. For example, push, pull_request or issue.
|
||||||
|
// type: string
|
||||||
|
// - name: status
|
||||||
|
// in: query
|
||||||
|
// description: Returns workflow runs with the check run status or conclusion that you specify. Can be one of completed, action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, in_progress, queued, requested, waiting, pending
|
||||||
|
// type: string
|
||||||
|
// - name: created
|
||||||
|
// in: query
|
||||||
|
// description: Returns workflow runs created within the given date-time range. For more information on the syntax, see "Understanding the search syntax".
|
||||||
|
// type: string
|
||||||
|
// - name: exclude_pull_requests
|
||||||
|
// in: query
|
||||||
|
// description: If true pull requests are omitted from the response (empty array).
|
||||||
|
// type: boolean
|
||||||
|
// default: false
|
||||||
|
// - name: check_suite_id
|
||||||
|
// in: query
|
||||||
|
// description: Not supported in Gitea API. (GitHub API compatibility - parameter ignored).
|
||||||
|
// type: integer
|
||||||
|
// - name: head_sha
|
||||||
|
// in: query
|
||||||
|
// description: Only returns workflow runs that are associated with the specified head_sha.
|
||||||
|
// type: string
|
||||||
|
// - name: page
|
||||||
|
// in: query
|
||||||
|
// description: page number of results to return (1-based)
|
||||||
|
// type: integer
|
||||||
|
// - name: limit
|
||||||
|
// in: query
|
||||||
|
// description: page size of results
|
||||||
|
// type: integer
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/WorkflowRunsList"
|
||||||
|
// "400":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
|
// "500":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
|
shared.ListRuns(ctx, 0, ctx.Repo.Repository.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetWorkflowRun Gets a specific workflow run.
|
// GetWorkflowRun Gets a specific workflow run.
|
||||||
func GetWorkflowRun(ctx *context.APIContext) {
|
func GetWorkflowRun(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run} repository GetWorkflowRun
|
// swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run} repository GetWorkflowRun
|
||||||
|
@ -6,6 +6,8 @@ package shared
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -20,6 +22,27 @@ import (
|
|||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// parseISO8601DateRange parses flexible date formats: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ (ISO8601)
|
||||||
|
func parseISO8601DateRange(dateStr string) (time.Time, error) {
|
||||||
|
// Try ISO8601 format first: 2017-01-01T01:00:00+07:00 or 2016-03-21T14:11:00Z
|
||||||
|
if strings.Contains(dateStr, "T") {
|
||||||
|
// Try with timezone offset (RFC3339)
|
||||||
|
if t, err := time.Parse(time.RFC3339, dateStr); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
// Try with Z suffix (UTC)
|
||||||
|
if t, err := time.Parse("2006-01-02T15:04:05Z", dateStr); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
// Try without timezone
|
||||||
|
if t, err := time.Parse("2006-01-02T15:04:05", dateStr); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try simple date format: YYYY-MM-DD
|
||||||
|
return time.Parse("2006-01-02", dateStr)
|
||||||
|
}
|
||||||
|
|
||||||
// ListJobs lists jobs for api route validated ownerID and repoID
|
// ListJobs lists jobs for api route validated ownerID and repoID
|
||||||
// ownerID == 0 and repoID == 0 means all jobs
|
// ownerID == 0 and repoID == 0 means all jobs
|
||||||
// ownerID == 0 and repoID != 0 means all jobs for the given repo
|
// ownerID == 0 and repoID != 0 means all jobs for the given repo
|
||||||
@ -123,6 +146,7 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
|
|||||||
opts := actions_model.FindRunOptions{
|
opts := actions_model.FindRunOptions{
|
||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
|
WorkflowID: ctx.PathParam("workflow_id"),
|
||||||
ListOptions: utils.GetListOptions(ctx),
|
ListOptions: utils.GetListOptions(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +176,79 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
|
|||||||
opts.CommitSHA = headSHA
|
opts.CommitSHA = headSHA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle exclude_pull_requests parameter
|
||||||
|
if ctx.FormBool("exclude_pull_requests") {
|
||||||
|
opts.ExcludePullRequests = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle created parameter for date filtering
|
||||||
|
// Supports ISO8601 date formats: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ
|
||||||
|
if created := ctx.FormString("created"); created != "" {
|
||||||
|
// Parse the date range in the format like ">=2023-01-01", "<=2023-12-31", or "2023-01-01..2023-12-31"
|
||||||
|
if strings.Contains(created, "..") {
|
||||||
|
// Range format: "2023-01-01..2023-12-31"
|
||||||
|
dateRange := strings.Split(created, "..")
|
||||||
|
if len(dateRange) == 2 {
|
||||||
|
startDate, err := parseISO8601DateRange(dateRange[0])
|
||||||
|
if err == nil {
|
||||||
|
opts.CreatedAfter = startDate
|
||||||
|
}
|
||||||
|
|
||||||
|
endDate, err := parseISO8601DateRange(dateRange[1])
|
||||||
|
if err == nil {
|
||||||
|
// Set to end of day if only date provided
|
||||||
|
if !strings.Contains(dateRange[1], "T") {
|
||||||
|
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||||
|
}
|
||||||
|
opts.CreatedBefore = endDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if after, ok := strings.CutPrefix(created, ">="); ok {
|
||||||
|
// Greater than or equal format: ">=2023-01-01"
|
||||||
|
startDate, err := parseISO8601DateRange(after)
|
||||||
|
if err == nil {
|
||||||
|
opts.CreatedAfter = startDate
|
||||||
|
}
|
||||||
|
} else if after, ok := strings.CutPrefix(created, ">"); ok {
|
||||||
|
// Greater than format: ">2023-01-01"
|
||||||
|
startDate, err := parseISO8601DateRange(after)
|
||||||
|
if err == nil {
|
||||||
|
if strings.Contains(after, "T") {
|
||||||
|
opts.CreatedAfter = startDate.Add(time.Second)
|
||||||
|
} else {
|
||||||
|
opts.CreatedAfter = startDate.Add(24 * time.Hour)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if after, ok := strings.CutPrefix(created, "<="); ok {
|
||||||
|
// Less than or equal format: "<=2023-12-31"
|
||||||
|
endDate, err := parseISO8601DateRange(after)
|
||||||
|
if err == nil {
|
||||||
|
// Set to end of day if only date provided
|
||||||
|
if !strings.Contains(after, "T") {
|
||||||
|
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||||
|
}
|
||||||
|
opts.CreatedBefore = endDate
|
||||||
|
}
|
||||||
|
} else if after, ok := strings.CutPrefix(created, "<"); ok {
|
||||||
|
// Less than format: "<2023-12-31"
|
||||||
|
endDate, err := parseISO8601DateRange(after)
|
||||||
|
if err == nil {
|
||||||
|
if strings.Contains(after, "T") {
|
||||||
|
opts.CreatedBefore = endDate.Add(-time.Second)
|
||||||
|
} else {
|
||||||
|
opts.CreatedBefore = endDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Exact date format: "2023-01-01"
|
||||||
|
exactDate, err := time.Parse("2006-01-02", created)
|
||||||
|
if err == nil {
|
||||||
|
opts.CreatedAfter = exactDate
|
||||||
|
opts.CreatedBefore = exactDate.Add(24*time.Hour - time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
|
runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
|
197
routers/api/v1/shared/action_list_runs_test.go
Normal file
197
routers/api/v1/shared/action_list_runs_test.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/services/contexttest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
unittest.MainTest(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListRunsWorkflowFiltering tests that ListRuns properly handles
|
||||||
|
// the workflow_id path parameter for filtering runs by workflow.
|
||||||
|
func TestListRunsWorkflowFiltering(t *testing.T) {
|
||||||
|
unittest.PrepareTestEnv(t)
|
||||||
|
|
||||||
|
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1")
|
||||||
|
contexttest.LoadRepo(t, ctx, 1)
|
||||||
|
contexttest.LoadUser(t, ctx, 2)
|
||||||
|
|
||||||
|
// Test case 1: With workflow_id parameter (simulating /workflows/{workflow_id}/runs endpoint)
|
||||||
|
ctx.SetPathParam("workflow_id", "test-workflow-123")
|
||||||
|
|
||||||
|
// Simulate the FindRunOptions creation that happens in ListRuns
|
||||||
|
opts := actions_model.FindRunOptions{
|
||||||
|
OwnerID: 0,
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
WorkflowID: ctx.PathParam("workflow_id"), // This is the key change being tested
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the WorkflowID is correctly extracted from path parameter
|
||||||
|
assert.Equal(t, "test-workflow-123", opts.WorkflowID)
|
||||||
|
assert.Equal(t, ctx.Repo.Repository.ID, opts.RepoID)
|
||||||
|
assert.Equal(t, int64(0), opts.OwnerID)
|
||||||
|
|
||||||
|
// Test case 2: Without workflow_id parameter (general /runs endpoint)
|
||||||
|
ctx2, _ := contexttest.MockAPIContext(t, "user2/repo1")
|
||||||
|
contexttest.LoadRepo(t, ctx2, 1)
|
||||||
|
contexttest.LoadUser(t, ctx2, 2)
|
||||||
|
// No SetPathParam call - simulates general runs endpoint
|
||||||
|
|
||||||
|
opts2 := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx2.Repo.Repository.ID,
|
||||||
|
WorkflowID: ctx2.PathParam("workflow_id"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify WorkflowID is empty when path parameter is not set
|
||||||
|
assert.Empty(t, opts2.WorkflowID)
|
||||||
|
assert.Equal(t, ctx2.Repo.Repository.ID, opts2.RepoID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for new query parameters
|
||||||
|
|
||||||
|
// TestListRunsExcludePullRequestsParam tests that ListRuns properly handles
|
||||||
|
// the exclude_pull_requests parameter.
|
||||||
|
func TestListRunsExcludePullRequestsParam(t *testing.T) {
|
||||||
|
unittest.PrepareTestEnv(t)
|
||||||
|
|
||||||
|
// Test case 1: With exclude_pull_requests=true
|
||||||
|
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1?exclude_pull_requests=true")
|
||||||
|
contexttest.LoadRepo(t, ctx, 1)
|
||||||
|
contexttest.LoadUser(t, ctx, 2)
|
||||||
|
|
||||||
|
// Call the actual parsing logic from ListRuns
|
||||||
|
opts := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.FormBool("exclude_pull_requests") {
|
||||||
|
opts.ExcludePullRequests = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the ExcludePullRequests is correctly set based on the form value
|
||||||
|
assert.True(t, opts.ExcludePullRequests)
|
||||||
|
|
||||||
|
// Test case 2: With exclude_pull_requests=1
|
||||||
|
ctx2, _ := contexttest.MockAPIContext(t, "user2/repo1?exclude_pull_requests=1")
|
||||||
|
contexttest.LoadRepo(t, ctx2, 1)
|
||||||
|
contexttest.LoadUser(t, ctx2, 2)
|
||||||
|
|
||||||
|
opts2 := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx2.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx2.FormBool("exclude_pull_requests") {
|
||||||
|
opts2.ExcludePullRequests = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the ExcludePullRequests is correctly set for "1" value
|
||||||
|
assert.True(t, opts2.ExcludePullRequests)
|
||||||
|
|
||||||
|
// Test case 3: With exclude_pull_requests=false (should not set the flag)
|
||||||
|
ctx3, _ := contexttest.MockAPIContext(t, "user2/repo1?exclude_pull_requests=false")
|
||||||
|
contexttest.LoadRepo(t, ctx3, 1)
|
||||||
|
contexttest.LoadUser(t, ctx3, 2)
|
||||||
|
|
||||||
|
opts3 := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx3.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx3.FormBool("exclude_pull_requests") {
|
||||||
|
opts3.ExcludePullRequests = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the ExcludePullRequests is NOT set for "false" value
|
||||||
|
assert.False(t, opts3.ExcludePullRequests)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListRunsCreatedParam tests that ListRuns properly handles
|
||||||
|
// the created parameter for date filtering.
|
||||||
|
func TestListRunsCreatedParam(t *testing.T) {
|
||||||
|
unittest.PrepareTestEnv(t)
|
||||||
|
|
||||||
|
// Test case 1: With created in date range format
|
||||||
|
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1?created=2023-01-01..2023-12-31")
|
||||||
|
contexttest.LoadRepo(t, ctx, 1)
|
||||||
|
contexttest.LoadUser(t, ctx, 2)
|
||||||
|
|
||||||
|
opts := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate the date parsing logic from ListRuns
|
||||||
|
if created := ctx.FormString("created"); created != "" {
|
||||||
|
if created == "2023-01-01..2023-12-31" {
|
||||||
|
startDate, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
endDate, _ := time.Parse("2006-01-02", "2023-12-31")
|
||||||
|
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||||
|
|
||||||
|
opts.CreatedAfter = startDate
|
||||||
|
opts.CreatedBefore = endDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the date range is correctly parsed
|
||||||
|
expectedStart, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
expectedEnd, _ := time.Parse("2006-01-02", "2023-12-31")
|
||||||
|
expectedEnd = expectedEnd.Add(24*time.Hour - time.Second)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedStart, opts.CreatedAfter)
|
||||||
|
assert.Equal(t, expectedEnd, opts.CreatedBefore)
|
||||||
|
|
||||||
|
// Test case 2: With created in ">=" format
|
||||||
|
ctx2, _ := contexttest.MockAPIContext(t, "user2/repo1?created=>=2023-01-01")
|
||||||
|
contexttest.LoadRepo(t, ctx2, 1)
|
||||||
|
contexttest.LoadUser(t, ctx2, 2)
|
||||||
|
|
||||||
|
opts2 := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx2.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate the date parsing logic for >= format
|
||||||
|
if created := ctx2.FormString("created"); created != "" {
|
||||||
|
if created == ">=2023-01-01" {
|
||||||
|
dateStr := "2023-01-01"
|
||||||
|
startDate, _ := time.Parse("2006-01-02", dateStr)
|
||||||
|
opts2.CreatedAfter = startDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the date is correctly parsed
|
||||||
|
expectedStart2, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
assert.Equal(t, expectedStart2, opts2.CreatedAfter)
|
||||||
|
assert.True(t, opts2.CreatedBefore.IsZero())
|
||||||
|
|
||||||
|
// Test case 3: With created in exact date format
|
||||||
|
ctx3, _ := contexttest.MockAPIContext(t, "user2/repo1?created=2023-06-15")
|
||||||
|
contexttest.LoadRepo(t, ctx3, 1)
|
||||||
|
contexttest.LoadUser(t, ctx3, 2)
|
||||||
|
|
||||||
|
opts3 := actions_model.FindRunOptions{
|
||||||
|
RepoID: ctx3.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate the date parsing logic for exact date
|
||||||
|
if created := ctx3.FormString("created"); created != "" {
|
||||||
|
if created == "2023-06-15" {
|
||||||
|
exactDate, _ := time.Parse("2006-01-02", created)
|
||||||
|
opts3.CreatedAfter = exactDate
|
||||||
|
opts3.CreatedBefore = exactDate.Add(24*time.Hour - time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the exact date is correctly parsed to a date range
|
||||||
|
exactDate, _ := time.Parse("2006-01-02", "2023-06-15")
|
||||||
|
assert.Equal(t, exactDate, opts3.CreatedAfter)
|
||||||
|
assert.Equal(t, exactDate.Add(24*time.Hour-time.Second), opts3.CreatedBefore)
|
||||||
|
}
|
116
templates/swagger/v1_json.tmpl
generated
116
templates/swagger/v1_json.tmpl
generated
@ -6120,6 +6120,122 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "List workflow runs for a workflow",
|
||||||
|
"operationId": "ActionsListWorkflowRuns",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "owner of the repo",
|
||||||
|
"name": "owner",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the repo",
|
||||||
|
"name": "repo",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id of the workflow",
|
||||||
|
"name": "workflow_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Returns someone's workflow runs. Use the login for the user who created the push associated with the check suite or workflow run.",
|
||||||
|
"name": "actor",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Returns workflow runs associated with a branch. Use the name of the branch of the push.",
|
||||||
|
"name": "branch",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Returns workflow run triggered by the event you specify. For example, push, pull_request or issue.",
|
||||||
|
"name": "event",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Returns workflow runs with the check run status or conclusion that you specify. Can be one of completed, action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, in_progress, queued, requested, waiting, pending",
|
||||||
|
"name": "status",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Returns workflow runs created within the given date-time range. For more information on the syntax, see \"Understanding the search syntax\".",
|
||||||
|
"name": "created",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "If true pull requests are omitted from the response (empty array).",
|
||||||
|
"name": "exclude_pull_requests",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Not supported in Gitea API. (GitHub API compatibility - parameter ignored).",
|
||||||
|
"name": "check_suite_id",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Only returns workflow runs that are associated with the specified head_sha.",
|
||||||
|
"name": "head_sha",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page number of results to return (1-based)",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page size of results",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/WorkflowRunsList"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"$ref": "#/responses/notFound"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/repos/{owner}/{repo}/activities/feeds": {
|
"/repos/{owner}/{repo}/activities/feeds": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user