Repository navigation
Add workflow run ignore list to logs #59697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
807c111
c27961b
a4dba37
8eb6cf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # ADR-59697: Add ignore-workflow-runs to logs | ||
|
|
||
| **Date**: 2026-09-09 | ||
| **Status**: Draft | ||
| **Deciders**: gh-aw maintainers | ||
|
|
||
| --- | ||
|
|
||
| ### Context | ||
|
|
||
| The `gh aw logs` command collects workflow runs and currently lets users constrain results with workflow, date, ref, and run ID filters, but it cannot explicitly skip known unwanted runs while still returning the requested count of useful results. This PR adds a new exclusion input that accepts workflow run IDs in plain numeric form or qualified `slug/ID` form, applies the exclusion before artifact processing, and preserves it in continuation state for resumed downloads. The implementation evidence shows a need to omit specific runs without shrinking the effective result set or forcing users to narrow broader filters. The repository needs an explicit decision on whether run exclusion should be treated as a first-class logs query parameter across CLI parsing, pagination, orchestration, and reporting. | ||
|
|
||
| ### Decision | ||
|
|
||
| We will add an `--ignore-workflow-runs` option to `gh aw logs` that accepts positive workflow run database IDs, including qualified `slug/ID` inputs, normalizes them to unique numeric IDs, and excludes matching runs from collection. We will apply the ignore list before downstream artifact processing and persist it through orchestration and continuation payloads so resumed or paginated log downloads keep honoring the same exclusion set. We chose this approach because it gives users precise control over unwanted runs without changing the requested result count or overloading existing date, ref, or before/after run filters. | ||
|
|
||
| ### Alternatives Considered | ||
|
|
||
| #### Alternative 1: Require users to refine existing filters such as `--ref`, `--before-run-id`, or `--after-run-id` | ||
|
|
||
| The project could continue relying on broader query filters and ask users to manually narrow the candidate run set. This was considered because it avoids adding a new option and reuses existing filtering semantics. It was not chosen because the PR evidence explicitly addresses the need to skip specific known runs while still preserving the overall count and broader search scope. | ||
|
|
||
| #### Alternative 2: Support exclusion only for raw numeric run IDs | ||
|
|
||
| Another option would be to accept only integer run IDs and reject qualified `slug/ID` inputs. This was considered because it would simplify parsing and validation. It was not chosen because the PR explicitly supports both numeric IDs and qualified values, which improves usability when users copy workflow run references from repository-qualified contexts. | ||
|
|
||
| #### Alternative 3: Exclude runs only in the final output after artifacts are processed | ||
|
|
||
| The implementation could defer exclusion until after run data is downloaded and assembled. This was considered because it would minimize changes to earlier pagination and orchestration code paths. It was not chosen because the PR description and code both indicate exclusions should happen before artifact processing so ignored runs do not consume result slots or processing work. | ||
|
|
||
| ### Consequences | ||
|
|
||
| #### Positive | ||
| - Users can omit known irrelevant or problematic workflow runs without reducing the requested number of collected results. | ||
| - The same exclusion behavior is preserved across pagination and continuation data, making resumed downloads more predictable. | ||
| - Supporting both numeric and `slug/ID` inputs reduces friction when specifying runs from copied GitHub references. | ||
|
|
||
| #### Negative | ||
| - The logs query path becomes more complex because ignore-list parsing, deduplication, filtering, and continuation serialization must all stay in sync. | ||
| - Invalid or non-positive run identifiers now introduce an additional user-facing validation failure mode. | ||
| - Exclusion behavior must be covered in multiple test layers to avoid regressions across CLI and orchestration flows. | ||
|
|
||
| #### Neutral | ||
| - `LogsDownloadOptions`, pagination options, and continuation payloads gain an `IgnoreWorkflowRuns` field. | ||
| - Run filtering semantics now include explicit exclusions in addition to inclusive workflow, date, ref, and run ID bounds. | ||
| - The implementation uses run database IDs as the stable identifier for exclusion across query and resume boundaries. | ||
|
|
||
| --- | ||
|
|
||
| *ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,8 +31,7 @@ func TestWorkflowRunUnmarshal(t *testing.T) { | |
| "startedAt": "2026-01-01T00:00:01Z", | ||
| "updatedAt": "2026-01-01T00:01:00Z", | ||
| "attempt": 2 | ||
| } | ||
| ]` | ||
| }]` | ||
|
|
||
| var runs []WorkflowRun | ||
| require.NoError(t, json.Unmarshal([]byte(rawJSON), &runs), "unmarshal should succeed") | ||
|
|
@@ -44,6 +43,16 @@ func TestWorkflowRunUnmarshal(t *testing.T) { | |
| assert.Equal(t, 2, runs[0].Attempt, "Attempt should be populated") | ||
| } | ||
|
|
||
| func TestFilterIgnoredWorkflowRuns(t *testing.T) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
💡 Why this needs to be fixed before mergeIn Move |
||
| runs := []WorkflowRun{{DatabaseID: 100}, {DatabaseID: 200}, {DatabaseID: 300}} | ||
|
|
||
| filtered := filterIgnoredWorkflowRuns(runs, []int64{100, 300}) | ||
|
|
||
| require.Len(t, filtered, 1) | ||
| assert.Equal(t, int64(200), filtered[0].DatabaseID) | ||
| assert.Equal(t, []WorkflowRun{{DatabaseID: 100}, {DatabaseID: 200}, {DatabaseID: 300}}, runs) | ||
| } | ||
|
|
||
| func TestApplyWorkflowRunListRepository(t *testing.T) { | ||
| runs := []WorkflowRun{ | ||
| {DatabaseID: 1, Repository: ""}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,19 +48,20 @@ type LogsData struct { | |
|
|
||
| // ContinuationData provides parameters to continue an incomplete logs query. | ||
| type ContinuationData struct { | ||
| Message string `json:"message"` | ||
| WorkflowName string `json:"workflow_name,omitempty"` | ||
| Count int `json:"count,omitempty"` | ||
| StartDate string `json:"start_date,omitempty"` | ||
| EndDate string `json:"end_date,omitempty"` | ||
| Engine string `json:"engine,omitempty"` | ||
| Branch string `json:"branch,omitempty"` | ||
| AfterRunID int64 `json:"after_run_id,omitempty"` | ||
| BeforeRunID int64 `json:"before_run_id,omitempty"` | ||
| Timeout int `json:"timeout,omitempty"` | ||
| MaxGitHubAPIRateLimit int `json:"max_github_api_rate_limit,omitempty"` | ||
| MaxStorageMB int `json:"max_storage,omitempty"` | ||
| PruneOlderRuns bool `json:"prune_older_runs,omitempty"` | ||
| Message string `json:"message"` | ||
| WorkflowName string `json:"workflow_name,omitempty"` | ||
| Count int `json:"count,omitempty"` | ||
| StartDate string `json:"start_date,omitempty"` | ||
| EndDate string `json:"end_date,omitempty"` | ||
| Engine string `json:"engine,omitempty"` | ||
| Branch string `json:"branch,omitempty"` | ||
| AfterRunID int64 `json:"after_run_id,omitempty"` | ||
| BeforeRunID int64 `json:"before_run_id,omitempty"` | ||
| IgnoreWorkflowRuns []int64 `json:"ignore_workflow_runs,omitempty"` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The continuation payload now persists 💡 Why this blocks the featureThe CLI path wires A minimal fix is to add an |
||
| Timeout int `json:"timeout,omitempty"` | ||
| MaxGitHubAPIRateLimit int `json:"max_github_api_rate_limit,omitempty"` | ||
| MaxStorageMB int `json:"max_storage,omitempty"` | ||
| PruneOlderRuns bool `json:"prune_older_runs,omitempty"` | ||
| } | ||
|
|
||
| // WorkflowContinuation identifies a per-target cursor in a combined | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/codebase-design]
filtered := runs[:0]reuses the caller's backing array in place.runshere isagenticRuns, which may itself be an alias of the originalrunsslice returned by the GitHub API call earlier in this function (whenopts.WorkflowName != "",agenticRuns = runswith no copy). Overwriting elements in place is safe for this specific call site today, but it's a fragile invariant future callers offilterIgnoredWorkflowRunscould easily violate (any caller passing a slice they still hold a live reference to will see it silently mutated).💡 Suggested fix
The existing before/after-run-id filter a few lines above already follows this safer append-to-new-slice pattern — worth being consistent with it here too.
@copilot please address this.