Skip to content

hosted runner label filter + integration test CI - #56

Merged
nodeselector merged 18 commits into
mainfrom
nodeselector/hosted-runner-label-filter
Jun 16, 2026
Merged

nodeselector merged 18 commits into
mainfrom
nodeselector/hosted-runner-label-filter

Conversation

@nodeselector

Copy link
Copy Markdown
Collaborator

Filters onboarding to hosted-runner-only workflows, fixes exit code bugs, wires impostor/forgery stub scenarios, adds integration-stub CI job.

Workflows that use self-hosted runners, custom runner labels, runner
groups, or expression-based runs-on are now excluded from lockfile
onboarding. Only workflows where every job runs on a known
GitHub-hosted runner label (ubuntu-*, macos-*, windows-*) are eligible.

The UX mirrors local-action skipping: already-onboarded workflows get
an error-severity finding telling the user to migrate; not-yet-onboarded
workflows get a warning and are silently skipped. The fast path in
pipeline.Run also skips network resolution for these workflows.

New category: self-hosted-runner (parallels local-action).
Replace prefix matching (ubuntu-*, macos-*, windows-*) with the exact
label set from github/hosted-compute-core imageconfigs/imageconfigs.go.
Prefix matching had false positives (accepting EOL labels like
ubuntu-20.04, macos-13) and false negatives (missing codespaces-prebuild,
ubuntu-slim).

The hardcoded set covers standard, ARM, Intel, large/xlarge, slim,
codespaces-prebuild, and firewall tech-preview labels. Staff/canary/beta
labels are excluded. This list has a short shelf life — update when
new images ship or old ones EOL.
The inline condition was duplicated and growing with each new skip
category. Now a named function with a doc comment.
Four scenarios covering the skip/error matrix:
- self-hosted label (skipped, and onboarded→error)
- custom runner label (skipped)
- runner group (skipped)
renderPinSummary only checked pin record state (pinned, investigated,
unresolved) to decide the exit code and 'All valid' message. Error-
severity findings from local-action and self-hosted-runner categories
on already-onboarded workflows produced no pin records, so the summary
incorrectly reported success with exit 0.

Add report.IsValid() to the allClean gate and errSilent return. When
the report is invalid, temporarily detach the narration log and re-
render findings so the error text reaches the terminal — the log sink
(io.Discard in terminal mode) was swallowing PresentResults output.
report.IsValid() catches not-pinned findings which are expected in the
pre-fix report — pinning resolves them. Using it in renderPinSummary
caused every live scenario to exit 1 after a successful pin.

Replace with reportHasUnfixableErrors which only checks for error-
severity LocalAction and SelfHostedRunner findings. These are the
categories that can't be resolved by pinning and need a workflow edit.

Also remove test-matrix Makefile target (was read-only catalog view,
not a test runner) and its --matrix flag in run.rb.
Scenarios with input_spec (like onboarded_corrupt_recovery) need a real
TTY so the binary's confirm dialog renders. The harness was using
run_captured (Open3.capture3, no TTY) for all scenarios, causing the
binary to detect no terminal and fail with exit 2.

Switch to run_pty when input_spec is present. PTYResult already
implements the same interface as Result so assertions work unchanged.
Enable dbot_impostor_blocks and dbot_forgery_blocks with full HTTP
stub wiring (GraphQL resolve + reachability, REST repo metadata +
branch listing + compare). Both need --rescan to bypass the lockfile
fast path that skips network verification for already-recorded refs.

Fix JSON exit code: impostor-commit and lockfile-forgery errors now
trigger non-zero exit in --json mode. Previously only Investigated()
records (autofix attempts) caused exit 1, but detection-only findings
like impostor/forgery have nothing to investigate — they just fail.

Add integration-stub CI job to test.yml so stub scenarios run on
every PR and push to main.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:15 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:16
Build-time version stamp differs between local go build (devel) and
CI module-aware builds (v0.0.0-timestamp-hash). Strip it from both
sides before diffing so the same golden fixtures work everywhere.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:18 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:20
Runs the full integration suite (stubs + live scenarios) with
GH_TOKEN from secrets. Marked continue-on-error so flakes don't
block merge — integration-stub remains the hard gate.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:33 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:35
The default Actions token has read access to public repos which is
all the live scenarios need (actions/checkout, etc). No custom secret
required.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:38 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:39
The binary checks CI=true to suppress interactive prompts. On GitHub
Actions this is always set, so PTY-based scenarios that need to answer
a confirm dialog would fail. Unsetting CI in the PTY subprocess env
lets the binary prompt normally.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:44 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:45
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 14:55 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 14:57
@nodeselector
nodeselector marked this pull request as ready for review June 16, 2026 15:01
Copilot AI review requested due to automatic review settings June 16, 2026 15:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

The new stub route for branches?protected=true matches req.path (no query string), and skippedRescan accounting now includes “skipped” workflows, both of which can produce incorrect behavior/output.

Pull request overview

This PR tightens onboarding so only workflows that run on GitHub-hosted runner labels are eligible for lockfile onboarding, while ensuring already-onboarded workflows that become ineligible surface as hard errors. It also expands the integration harness to support new stubbed “impostor commit” and “lockfile forgery” detection scenarios and adds CI coverage for the stub suite.

Changes:

  • Add hosted-runner label detection (runs-on) and plumb a new self-hosted-runner finding through parsing/diagnosis/rendering/JSON filtering.
  • Improve integration harness behavior (PTY for prompt scenarios, stable golden JSON) and wire shared HTTP stubs for impostor/forgery scenarios.
  • Add an integration-stub GitHub Actions job and remove the legacy --matrix/test-matrix path.
File summaries
File Description
test/scenarios/catalog.yml Adds runner-label scenarios; enables stub scenarios for impostor/forgery with --rescan.
test/integration/run.rb Adds stub helpers and constants for detection scenarios; normalizes golden JSON; removes --matrix.
test/integration/harness.rb Runs scenarios with PTY when prompt input is specified; unsets CI for interactive prompt behavior.
Makefile Removes test-matrix target now that --matrix is removed.
internal/workflowfile/runson.go Implements hosted-runner label allowlist and YAML extraction of runs-on labels.
internal/workflowfile/runson_test.go Adds unit tests for hosted label detection and runs-on parsing/flagging.
internal/pipeline/run.go Skips resolver work earlier for workflows that will be skipped at diagnose time (local actions / non-hosted runners).
internal/pipeline/parse.go Populates ParsedWorkflow.NonHostedRunner from workflow runs-on labels.
internal/pipeline/diagnose.go Emits self-hosted-runner warning/error findings depending on onboarding state.
internal/pipeline/diagnose_test.go Adds tests for the new self-hosted-runner finding behavior.
internal/pipeline/checks/parsed.go Adds NonHostedRunner field to parsed workflow model.
internal/pipeline/checks/finding.go Treats self-hosted-runner as non-warning when error severity, and as “valid” when not error.
internal/pipeline/checks/category.go Introduces SelfHostedRunner category string.
internal/pipeline/checks/category_test.go Freezes the new category string and categorization behavior in tests.
cmd/gh-actions-lock/pin_summary.go Adds reportHasUnfixableErrors and ensures unfixable errors surface on terminal runs.
cmd/gh-actions-lock/format/terminal.go Renders self-hosted-runner warnings in the terminal summary buckets.
cmd/gh-actions-lock/format/json.go Refactors JSON suppression logic; omits self-hosted-runner warnings from JSON while keeping errors.
cmd/gh-actions-lock/check.go Uses reportHasUnfixableErrors to ensure JSON mode exits non-zero on unfixable error findings.
.github/workflows/test.yml Adds integration-stub job and keeps integration-live as non-blocking.

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/integration/run.rb
Comment thread internal/pipeline/run.go
Comment thread internal/pipeline/run.go
Comment thread internal/workflowfile/runson.go Outdated
Local-action and non-hosted-runner workflows are excluded, not
trusted — they shouldn't count toward the 'Trusted lockfile for N
already-pinned workflows' summary line. Also fix misleading comment
in HasNonHostedRunnerLabels about run-only workflows.
GitHub Advanced Security started work on behalf of nodeselector June 16, 2026 15:13 View session
GitHub Advanced Security finished work on behalf of nodeselector June 16, 2026 15:15
@nodeselector
nodeselector merged commit 78a656a into main Jun 16, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants