From 6f7653ef57954646fba78e95e166fd14de90a16f Mon Sep 17 00:00:00 2001 From: Wally Guzman <3623426+WallyGuzman@users.noreply.github.com> Date: Thu, 23 Jul 2026 20:43:31 -0500 Subject: [PATCH] Fix incorrect workflow_id type for workflow_run table - Remove unecessary strconv to match other workflow-related tables - Add optional quals for headSha, actorLogin, and createdAt - Fix minor bug in workflow_job to handle missing CompletedAt fields - Also minor typo fix in docs and new query example with dates --- .../github_actions_repository_workflow_run.md | 43 +++++++++ github/table_github_actions_cache.go | 2 +- ..._github_actions_repository_workflow_job.go | 4 +- ..._github_actions_repository_workflow_run.go | 87 ++++++++++++------- 4 files changed, 102 insertions(+), 34 deletions(-) diff --git a/docs/tables/github_actions_repository_workflow_run.md b/docs/tables/github_actions_repository_workflow_run.md index c2c1036..7c3366c 100644 --- a/docs/tables/github_actions_repository_workflow_run.md +++ b/docs/tables/github_actions_repository_workflow_run.md @@ -43,6 +43,49 @@ where repository_full_name = 'turbot/steampipe'; ``` +### List workflow runs between dates +Identify workflow runs between given dates within the 'turbot/steampipe' repository. This can be useful for tracking workflows over time. + +```sql+postgres +select + id, + event, + workflow_id, + conclusion, + status, + run_number, + workflow_url, + head_commit, + head_branch, + created_at +from + github_actions_repository_workflow_run +where + repository_full_name = 'turbot/steampipe' + and created_at >= '2026-01-01' + and created_at <= '2026-02-01'; +``` + +```sql+sqlite +select + id, + event, + workflow_id, + conclusion, + status, + run_number, + workflow_url, + head_commit, + head_branch, + created_at +from + github_actions_repository_workflow_run +where + repository_full_name = 'turbot/steampipe' + and created_at >= '2026-01-01' + and created_at <= '2026-02-01'; +``` + ### List failed workflow runs Identify instances where workflow runs have failed within the 'turbot/steampipe' repository. This can be useful for debugging and identifying problematic workflows. diff --git a/github/table_github_actions_cache.go b/github/table_github_actions_cache.go index de4e8d0..04b1104 100644 --- a/github/table_github_actions_cache.go +++ b/github/table_github_actions_cache.go @@ -32,7 +32,7 @@ func tableGitHubActionsCache() *plugin.Table { // Other columns {Name: "ref", Type: proto.ColumnType_STRING, Description: "The git reference of the cache."}, {Name: "version", Type: proto.ColumnType_STRING, Description: "Hash generated from combination of compression tool, runner OS, and path."}, - {Name: "last_accessed_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("LastAccessedAt").Transform(convertTimestamp), Description: "Time of the most recent cache access."}, + {Name: "last_accessed_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("LastAccessedAt").NullIfZero().Transform(convertTimestamp), Description: "Time of the most recent cache access."}, {Name: "created_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("CreatedAt").Transform(convertTimestamp), Description: "Time when the cache was created."}, {Name: "size_in_bytes", Type: proto.ColumnType_INT, Description: "Size of the cache in bytes."}, }), diff --git a/github/table_github_actions_repository_workflow_job.go b/github/table_github_actions_repository_workflow_job.go index c5c787b..718df2d 100644 --- a/github/table_github_actions_repository_workflow_job.go +++ b/github/table_github_actions_repository_workflow_job.go @@ -51,8 +51,8 @@ func tableGitHubActionsRepositoryWorkflowJob() *plugin.Table { {Name: "steps", Type: proto.ColumnType_JSON, Description: "The list of step details for the workflow job."}, {Name: "labels", Type: proto.ColumnType_JSON, Description: "The list of labels for the workflow job."}, {Name: "created_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("CreatedAt").Transform(convertTimestamp), Description: "Time when the workflow job was created."}, - {Name: "started_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("StartedAt").Transform(convertTimestamp), Description: "Time when the workflow job was started."}, - {Name: "completed_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("CompletedAt").Transform(convertTimestamp), Description: "Time when the workflow job was completed."}, + {Name: "started_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("StartedAt").NullIfZero().Transform(convertTimestamp), Description: "Time when the workflow job was started."}, + {Name: "completed_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("CompletedAt").NullIfZero().Transform(convertTimestamp), Description: "Time when the workflow job was completed."}, {Name: "run_attempt", Type: proto.ColumnType_INT, Description: "The attempt number of the workflow run."}, {Name: "runner_id", Type: proto.ColumnType_INT, Description: "The unique identifier of the workflow job runner."}, {Name: "runner_name", Type: proto.ColumnType_STRING, Description: "The name of the workflow job runner."}, diff --git a/github/table_github_actions_repository_workflow_run.go b/github/table_github_actions_repository_workflow_run.go index a8f9cfd..d498739 100644 --- a/github/table_github_actions_repository_workflow_run.go +++ b/github/table_github_actions_repository_workflow_run.go @@ -2,7 +2,7 @@ package github import ( "context" - "strconv" + "time" "github.com/google/go-github/v55/github" @@ -24,8 +24,11 @@ func tableGitHubActionsRepositoryWorkflowRun() *plugin.Table { {Name: "workflow_id", Require: plugin.Optional}, {Name: "event", Require: plugin.Optional}, {Name: "head_branch", Require: plugin.Optional}, + {Name: "head_sha", Require: plugin.Optional}, {Name: "status", Require: plugin.Optional}, {Name: "conclusion", Require: plugin.Optional}, + {Name: "actor_login", Require: plugin.Optional}, + {Name: "created_at", Require: plugin.Optional, Operators: []string{">", ">=", "<", "<=", "="}}, }, }, Get: &plugin.GetConfig{ @@ -40,9 +43,9 @@ func tableGitHubActionsRepositoryWorkflowRun() *plugin.Table { Columns: commonColumns([]*plugin.Column{ // Top columns {Name: "repository_full_name", Type: proto.ColumnType_STRING, Transform: transform.FromQual("repository_full_name"), Description: "Full name of the repository that specifies the workflow run."}, - {Name: "id", Type: proto.ColumnType_INT, Description: "The unque identifier of the workflow run."}, + {Name: "id", Type: proto.ColumnType_INT, Description: "The unique identifier of the workflow run."}, {Name: "event", Type: proto.ColumnType_STRING, Description: "The event for which workflow triggered off."}, - {Name: "workflow_id", Type: proto.ColumnType_STRING, Description: "The workflow id of the workflow run."}, + {Name: "workflow_id", Type: proto.ColumnType_INT, Description: "The workflow id of the workflow run."}, {Name: "node_id", Type: proto.ColumnType_STRING, Description: "The node id of the workflow run."}, {Name: "conclusion", Type: proto.ColumnType_STRING, Description: "The conclusion for workflow run."}, {Name: "status", Type: proto.ColumnType_STRING, Description: "The status of the workflow run."}, @@ -67,7 +70,7 @@ func tableGitHubActionsRepositoryWorkflowRun() *plugin.Table { {Name: "pull_requests", Type: proto.ColumnType_JSON, Description: "The pull request details for the workflow run."}, {Name: "repository", Type: proto.ColumnType_JSON, Description: "The repository info for the workflow run."}, {Name: "run_attempt", Type: proto.ColumnType_INT, Description: "The attempt number of the workflow run."}, - {Name: "run_started_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("RunStartedAt").Transform(convertTimestamp), Description: "Time when the workflow run was started."}, + {Name: "run_started_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("RunStartedAt").NullIfZero().Transform(convertTimestamp), Description: "Time when the workflow run was started."}, {Name: "updated_at", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("UpdatedAt").Transform(convertTimestamp), Description: "Time when the workflow run was updated."}, {Name: "actor", Type: proto.ColumnType_JSON, Description: "The user whom initiated the first instance of this workflow run."}, {Name: "actor_login", Type: proto.ColumnType_STRING, Description: "The login of the user whom initiated the first instance of the workflow run.", Transform: transform.FromField("Actor.Login")}, @@ -85,29 +88,60 @@ func tableGitHubRepoWorkflowRunList(ctx context.Context, d *plugin.QueryData, h opts := &github.ListWorkflowRunsOptions{ ListOptions: github.ListOptions{PerPage: 100}, } - equalQuals := d.EqualsQuals - if equalQuals["event"] != nil { - if equalQuals["event"].GetStringValue() != "" { - opts.Event = equalQuals["event"].GetStringValue() - } + if event := d.EqualsQualString("event"); event != "" { + opts.Event = event } - if equalQuals["head_branch"] != nil { - if equalQuals["head_branch"].GetStringValue() != "" { - opts.Branch = equalQuals["head_branch"].GetStringValue() - } + if branch := d.EqualsQualString("head_branch"); branch != "" { + opts.Branch = branch } - if equalQuals["status"] != nil { - if equalQuals["status"].GetStringValue() != "" { - opts.Status = equalQuals["status"].GetStringValue() - } + if headSha := d.EqualsQualString("head_sha"); headSha != "" { + opts.HeadSHA = headSha + } + if status := d.EqualsQualString("status"); status != "" { + opts.Status = status + } + if actorLogin := d.EqualsQualString("actor_login"); actorLogin != "" { + opts.Actor = actorLogin } // Status param can take the value from both status and conclusion column - // https://docs.github.com/en/rest/reference/actions#workflow-runs - if equalQuals["conclusion"] != nil { + // https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository + if conclusion := d.EqualsQualString("conclusion"); conclusion != "" { if opts.Status == "" { - if equalQuals["conclusion"].GetStringValue() != "" { - opts.Status = equalQuals["conclusion"].GetStringValue() + opts.Status = conclusion + } + } + + // Convert quals into GitHub search syntax + // https://docs.github.com/en/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax#query-for-dates + if createdAt := d.Quals["created_at"]; createdAt != nil { + var lowerBound, upperBound time.Time + for _, q := range createdAt.Quals { + t := q.Value.GetTimestampValue().AsTime() + // Note: This logic returns boundary rows regardless of operator, but qual recheck will filter those out client-side + // Keep the _latest_ lower bound and _earliest_ upper bound in case of overlapping filters + switch q.Operator { + case "=": + opts.Created = t.Format(time.DateOnly) + case ">", ">=": + if lowerBound.IsZero() || t.After(lowerBound) { + lowerBound = t + } + case "<", "<=": + if upperBound.IsZero() || t.Before(upperBound) { + upperBound = t + } + } + } + if opts.Created == "" { + var lower, upper = lowerBound.Format(time.RFC3339), upperBound.Format(time.RFC3339) + switch { + case !lowerBound.IsZero() && !upperBound.IsZero(): + opts.Created = lower + ".." + upper + case !lowerBound.IsZero(): + opts.Created = ">=" + lower + case !upperBound.IsZero(): + opts.Created = "<=" + upper } } } @@ -119,16 +153,7 @@ func tableGitHubRepoWorkflowRunList(ctx context.Context, d *plugin.QueryData, h } } - var workflowId int64 - if equalQuals["workflow_id"] != nil { - if equalQuals["workflow_id"].GetStringValue() != "" { - workflowId_, err := strconv.ParseInt(equalQuals["workflow_id"].GetStringValue(), 10, 64) - if err != nil { - panic(err) - } - workflowId = workflowId_ - } - } + workflowId := d.EqualsQuals["workflow_id"].GetInt64Value() for { var (