go.temporal.io/server/tools/common/github/runs.go
269 LOC · 5 covered · 264 uncovered · 3 ranges · 3 concepts · 3 introducers · 3 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
package github
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
)
// Conclusion represents the conclusion status of a workflow run or job.
type Conclusion string
const (
ConclusionSuccess Conclusion = "success"
ConclusionFailure Conclusion = "failure"
)
// Run represents a GitHub Actions workflow run returned by the gh CLI.
type Run struct {
DatabaseID int64 `json:"databaseId"`
Number int `json:"number"`
Name string `json:"name"`
WorkflowName string `json:"workflowName"`
WorkflowDatabaseID int64 `json:"workflowDatabaseId"`
Status string `json:"status"`
Conclusion Conclusion `json:"conclusion"`
HeadBranch string `json:"headBranch"`
HeadSHA string `json:"headSha"`
URL string `json:"url"`
DisplayTitle string `json:"displayTitle"`
Event string `json:"event"`
CreatedAt time.Time `json:"createdAt"`
StartedAt time.Time `json:"startedAt"`
UpdatedAt time.Time `json:"updatedAt"`
Duration time.Duration `json:"-"`
Jobs []Job `json:"jobs"`
}
// ShortSHA returns the first seven characters of the run's commit SHA.
if len(r.HeadSHA) > 7 {
}
}
type workflowRunResponse struct {
ID int64 `json:"id"`
Number int `json:"run_number"`
Name string `json:"name"`
Status string `json:"status"`
Conclusion Conclusion `json:"conclusion"`
HeadBranch string `json:"head_branch"`
HeadSHA string `json:"head_sha"`
URL string `json:"html_url"`
Event string `json:"event"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
WorkflowID int64 `json:"workflow_id"`
}
// Job represents a single job in a GitHub Actions workflow run returned by the gh CLI.
type Job struct {
Name string `json:"name"`
Conclusion Conclusion `json:"conclusion"`
Status string `json:"status"`
StartedAt string `json:"startedAt"`
CompletedAt string `json:"completedAt"`
URL string `json:"url"`
}
// ViewRun retrieves workflow run details through the gh CLI.
func ViewRun(ctx context.Context, runID string) (Run, error) {
fields := []string{
"databaseId",
"number",
"conclusion",
"name",
"workflowName",
"workflowDatabaseId",
"status",
"headBranch",
"headSha",
"url",
"displayTitle",
"event",
"createdAt",
"startedAt",
"updatedAt",
"jobs",
}
var run Run
if err := runJSON(ctx, []string{"view", runID}, fields, &run); err != nil {
return Run{}, fmt.Errorf("failed to get workflow run: %w", err)
}
return run, nil
}
func runJSON(ctx context.Context, args []string, fields []string, out any) error {
cmdArgs := append([]string{"run"}, args...)
if len(fields) > 0 {
cmdArgs = append(cmdArgs, "--json", strings.Join(fields, ","))
}
output, err := commandOutput(ctx, defaultTimeout, cmdArgs...)
if err != nil {
return err
}
if err := json.Unmarshal(output, out); err != nil {
return fmt.Errorf("failed to parse gh run response: %w", err)
}
return nil
}
// RunListOptions configures workflow-run listing.
type RunListOptions struct {
Repo string
WorkflowID int64
Branch string
Workflow string
Event string
Status string
Created string
Limit int
All bool
}
// ListRuns retrieves workflow runs through the gh CLI.
func ListRuns(ctx context.Context, opts RunListOptions) ([]Run, error) {
if opts.WorkflowID != 0 {
return listWorkflowRuns(ctx, opts)
}
fields := []string{
"databaseId",
"number",
"conclusion",
"name",
"workflowName",
"workflowDatabaseId",
"status",
"event",
"createdAt",
"startedAt",
"updatedAt",
"headSha",
"displayTitle",
"url",
}
var runs []Run
if err := runJSON(ctx, append([]string{"list"}, runListArgs(opts)...), fields, &runs); err != nil {
return nil, fmt.Errorf("failed to list workflow runs: %w", err)
}
return runs, nil
}
func listWorkflowRuns(ctx context.Context, opts RunListOptions) ([]Run, error) {
var allRuns []Run
page := 1
for {
var response struct {
WorkflowRuns []workflowRunResponse `json:"workflow_runs"`
}
path := fmt.Sprintf(
"/repos/%s/actions/workflows/%d/runs?branch=%s&created=%s&per_page=100&page=%d",
opts.Repo,
opts.WorkflowID,
opts.Branch,
opts.Created,
page,
)
if err := getJSON(ctx, path, &response); err != nil {
return nil, fmt.Errorf("failed to fetch workflow runs page %d: %w", page, err)
}
if len(response.WorkflowRuns) == 0 {
break
}
for _, run := range response.WorkflowRuns {
allRuns = append(allRuns, run.toRun())
}
if opts.Limit > 0 && len(allRuns) >= opts.Limit {
return allRuns[:opts.Limit], nil
}
if len(response.WorkflowRuns) < 100 {
break
}
page++
}
return allRuns, nil
}
func (r workflowRunResponse) toRun() Run {
return Run{
DatabaseID: r.ID,
Number: r.Number,
Name: r.Name,
WorkflowName: r.Name,
WorkflowDatabaseID: r.WorkflowID,
Status: r.Status,
Conclusion: r.Conclusion,
HeadBranch: r.HeadBranch,
HeadSHA: r.HeadSHA,
URL: r.URL,
Event: r.Event,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
}
}
func runListArgs(opts RunListOptions) []string {
var args []string
if opts.Repo != "" {
args = append(args, "--repo", opts.Repo)
}
if opts.Branch != "" {
args = append(args, "--branch", opts.Branch)
}
if opts.Workflow != "" {
args = append(args, "--workflow", opts.Workflow)
}
if opts.Event != "" {
args = append(args, "--event", opts.Event)
}
if opts.Status != "" {
args = append(args, "--status", opts.Status)
}
if opts.Created != "" {
args = append(args, "--created", opts.Created)
}
if opts.Limit > 0 {
args = append(args, "--limit", strconv.Itoa(opts.Limit))
}
if opts.All {
args = append(args, "--all")
}
return args
}
// RunDownloadOptions configures `gh run download`.
type RunDownloadOptions struct {
Repo string
Pattern string
Dir string
}
// RunDownload executes `gh run download`.
func RunDownload(ctx context.Context, runID string, opts RunDownloadOptions) error {
args := []string{"run", "download", runID}
if opts.Repo != "" {
args = append(args, "--repo", opts.Repo)
}
if opts.Pattern != "" {
args = append(args, "--pattern", opts.Pattern)
}
if opts.Dir != "" {
args = append(args, "--dir", opts.Dir)
}
return commandRun(ctx, defaultTimeout, args...)
}