Skip to content

Commit

Permalink
github: Track workflow execution duration
Browse files Browse the repository at this point in the history
The amount of time it takes for a workflow to execute is not returned by
the GitHub API when listing workflow runs. The duration must be pulled
via a separate API endpoint. This commit implements this logic.

Signed-off-by: Ryan Drew <[email protected]>
  • Loading branch information
learnitall committed Sep 17, 2024
1 parent e23c0a8 commit dd19e1a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
35 changes: 35 additions & 0 deletions pkg/github/workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ func GetWorkflowRuns(
for _, runRaw := range runs.WorkflowRuns {
run := types.NewWorkflowRunFromRaw(runRaw)

duration, err := GetWorkflowRunDuration(ctx, l, client, run)
if err != nil {
return nil, err
}

run.WorkflowDuration = duration

workflowRuns = append(workflowRuns, run)
}

Expand All @@ -122,6 +129,34 @@ func GetWorkflowRuns(
return workflowRuns, nil
}

// GetWorkflowRunDuration gets the total amount of time that a workflow run took.
// This is retrieved through GitHub's usage API and is not available in a WorkflowRun object itself.
func GetWorkflowRunDuration(
ctx context.Context,
logger *slog.Logger,
client *github.Client,
run *types.WorkflowRun,
) (time.Duration, error) {
l := logger.With("workflow-id", run.ID)

l.Debug("Pulling run duration for workflow")

usage, _, err := WrapWithRateLimitRetry[github.WorkflowRunUsage](
ctx, l,
func() (*github.WorkflowRunUsage, *github.Response, error) {
return client.Actions.GetWorkflowRunUsageByID(ctx, run.Repository.Owner.Login, run.Repository.Name, run.ID)
},
)

if err != nil {
return -1, fmt.Errorf(
"unable to pull workflow run duration for run with ID %d: %w", run.ID, err,
)
}

return time.Duration(usage.GetRunDurationMS() * 1000000), nil
}

// GetTestsForWorkflowRun checks if the given WorkflowRun contains a known JUnit artifact.
// If a JUnit file is found and is recognized, it will be downloaded and parsed into a set of TestSuite
// and Testcase objects.
Expand Down
9 changes: 5 additions & 4 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type WorkflowRun struct {
HeadSHA string `json:"head_sha,omitempty"`
HeadCommit Commit `json:"head_commit,omitempty"`
WorkflowDispatchInputs map[string]string `json:"workflow_dispatch_inputs,omitempty"`
WorkflowDuration time.Duration `json:"workflow_duration,omitempty"`
}

func NewWorkflowRunFromRaw(runRaw *github.WorkflowRun) *WorkflowRun {
Expand Down Expand Up @@ -188,9 +189,9 @@ type JobRun struct {
Name string `json:"job_name,omitempty"`
Logs string `json:"job_logs,omitempty"`
// ErrorLogs contains log lines that contain an error.
ErrorLogs []string `json:"job_error_logs,omitempty"`
Link string `json:"job_link,omitempty"`
Duration time.Duration `json:"job_duration,omitempty"`
ErrorLogs []string `json:"job_error_logs,omitempty"`
Link string `json:"job_link,omitempty"`
JobDuration time.Duration `json:"job_duration,omitempty"`
}

func NewJobRunFromRaw(parent *WorkflowRun, jobRaw *github.WorkflowJob) *JobRun {
Expand All @@ -208,7 +209,7 @@ func NewJobRunFromRaw(parent *WorkflowRun, jobRaw *github.WorkflowJob) *JobRun {
StartedAt: jobRaw.GetStartedAt().Time,
CompletedAt: jobRaw.GetCompletedAt().Time,
Name: jobRaw.GetName(),
Duration: jobRaw.CompletedAt.Sub(jobRaw.StartedAt.Time),
JobDuration: jobRaw.CompletedAt.Sub(jobRaw.StartedAt.Time),
}
job.Link = fmt.Sprintf(
"https://github.com/%s/%s/actions/runs/%d/job/%d",
Expand Down

0 comments on commit dd19e1a

Please sign in to comment.