package gitea import ( "context" "encoding/json" "fmt" "net/url" "strconv" "strings" ) // DispatchWorkflowArgs is the request body for a workflow_dispatch trigger. type DispatchWorkflowArgs struct { Ref string `json:"ref"` Inputs map[string]any `json:"inputs,omitempty"` } // WorkflowRunTrigger holds the run ID extracted from the Location header. type WorkflowRunTrigger struct { RunID int64 } // DispatchWorkflow triggers a workflow_dispatch event and returns the new run ID. func (c *Client) DispatchWorkflow(ctx context.Context, owner, repo, workflow string, args DispatchWorkflowArgs) (*WorkflowRunTrigger, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/actions/workflows/%s/dispatches", owner, repo, workflow) payload, err := json.Marshal(args) if err != nil { return nil, err } resp, err := c.doRaw(ctx, "POST", p, payload) if err != nil { return nil, err } if resp.Status != 204 { if mapErr := MapStatus(resp.Status, resp.Body); mapErr != nil { return nil, mapErr } return nil, fmt.Errorf("unexpected status %d", resp.Status) } location := resp.Headers.Get("Location") if location == "" { return nil, fmt.Errorf("missing Location header in dispatch response") } // Location is e.g. "/api/v1/repos/o/r/actions/runs/123" — take the last segment. parts := strings.Split(strings.TrimRight(location, "/"), "/") if len(parts) == 0 { return nil, fmt.Errorf("malformed Location: %s", location) } runID, err := strconv.ParseInt(parts[len(parts)-1], 10, 64) if err != nil { return nil, fmt.Errorf("parse run id from %q: %w", location, err) } return &WorkflowRunTrigger{RunID: runID}, nil } // WorkflowRun represents a Gitea Actions run. type WorkflowRun struct { ID int64 `json:"id"` DisplayTitle string `json:"display_title,omitempty"` Status string `json:"status"` // queued | in_progress | completed Conclusion string `json:"conclusion"` // success | failure | cancelled | skipped (only when completed) Event string `json:"event,omitempty"` HeadSHA string `json:"head_sha,omitempty"` HeadBranch string `json:"head_branch,omitempty"` WorkflowID string `json:"workflow_id,omitempty"` RunNumber int64 `json:"run_number,omitempty"` StartedAt string `json:"started_at"` UpdatedAt string `json:"updated_at,omitempty"` HTMLURL string `json:"html_url"` Actor struct { Login string `json:"login"` } `json:"actor,omitempty"` } // GetWorkflowRun fetches the status of a specific Actions run. func (c *Client) GetWorkflowRun(ctx context.Context, owner, repo string, runID int64) (*WorkflowRun, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs/%d", owner, repo, runID) body, status, err := c.GetJSON(ctx, p) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var run WorkflowRun if err := json.Unmarshal(body, &run); err != nil { return nil, err } return &run, nil } // ListWorkflowRunsArgs captures the optional query params for ListWorkflowRuns. type ListWorkflowRunsArgs struct { Branch string HeadSHA string Status string // queued | in_progress | completed | all Event string // push | pull_request | schedule | workflow_dispatch | all Workflow string Page int Limit int } type workflowRunsResponse struct { TotalCount int64 `json:"total_count"` WorkflowRuns []WorkflowRun `json:"workflow_runs"` } // ListWorkflowRuns fetches recent Actions runs for a repo with optional filters. // Status / Event of "all" or "" are treated as no-filter. func (c *Client) ListWorkflowRuns(ctx context.Context, owner, repo string, args ListWorkflowRunsArgs) (*workflowRunsResponse, error) { q := url.Values{} if args.Branch != "" { q.Set("branch", args.Branch) } if args.HeadSHA != "" { q.Set("head_sha", args.HeadSHA) } if args.Status != "" && args.Status != "all" { q.Set("status", args.Status) } if args.Event != "" && args.Event != "all" { q.Set("event", args.Event) } if args.Workflow != "" { q.Set("workflow", args.Workflow) } if args.Page > 0 { q.Set("page", strconv.Itoa(args.Page)) } if args.Limit > 0 { q.Set("limit", strconv.Itoa(args.Limit)) } p := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs?%s", owner, repo, q.Encode()) body, status, err := c.GetJSON(ctx, p) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var resp workflowRunsResponse if err := json.Unmarshal(body, &resp); err != nil { return nil, err } return &resp, nil }