Skip to content

Commit 5d9a23f

Browse files
authored
Merge pull request #74 from github/nodeselector-noop-hosted-runner-flags
Remove hosted/self-hosted runner restrictions
2 parents f473586 + 1656597 commit 5d9a23f

22 files changed

Lines changed: 51 additions & 1082 deletions

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,5 @@ Finally, locked actions must have a branch that the commit being locked exists w
3131
## Limitations
3232

3333
There are currently eligibility limitations for workflows that can be onboarded to lockfiles:
34-
- Workflows in the lockfile must use a hosted runner. In the meantime, we will skip onboarding self-hosted runners. Discerning a self-hosted runner from a Larger runner requires the `manage_runners:org` which users are unlikely to have on their CLI in the typical case, if workflows are skipped during onboarding, the cli will output the command to run to allow onboarding those workflows. The requirement for hosted runners is enforced at runtime. This is a short-term gap.
3534
- Workflows in the lockfile cannot use local-path actions, these will be skipped for onboarding. This is also a short-term gap.
3635

actions/lint/action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ description: Check that actions.lock is in sync with workflow files
33

44
inputs:
55
extra-flags:
6-
description: Extra flags passed to gh actions-lock (e.g. "--allow-runners foo")
6+
description: Extra flags passed to gh actions-lock
77
required: false
88
default: ""
99
ignore-categories:
1010
description: Comma-separated finding categories to treat as warnings instead of errors (e.g. "local-action")
1111
required: false
12-
default: "self-hosted-runner"
12+
default: ""
1313

1414
runs:
1515
using: composite

cmd/gh-actions-lock/format/json.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ import (
1111
)
1212

1313
// skipFindingInJSON reports whether a finding should be omitted from JSON
14-
// output. Run-only and valid-OK workflows are noise; local-action and
15-
// self-hosted-runner warnings are informational skips (errors are kept
16-
// because they mean an already-tracked workflow became ineligible).
14+
// output. Run-only and valid-OK workflows are noise; local-action
15+
// warnings are informational skips (errors are kept because they mean
16+
// an already-tracked workflow became ineligible).
1717
func skipFindingInJSON(f checks.Finding) bool {
1818
if f.Category == checks.RunOnly {
1919
return true
2020
}
2121
if f.Category == checks.Valid && f.Severity == checks.SeverityOK {
2222
return true
2323
}
24-
if (f.Category == checks.LocalAction || f.Category == checks.SelfHostedRunner || f.Category == checks.ExpressionRunner) && f.Severity != checks.SeverityError {
24+
if f.Category == checks.LocalAction && f.Severity != checks.SeverityError {
2525
return true
2626
}
2727
return false

cmd/gh-actions-lock/format/terminal.go

Lines changed: 3 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -90,30 +90,21 @@ func renderErrorFindings(out *ui.UI, report *checks.Report, failedCount, checked
9090
continue
9191
}
9292

93-
// Self-hosted-runner findings share the same empty dep key;
94-
// render them as a deduplicated group showing affected workflows.
95-
var selfHostedFindings []checks.Finding
93+
// Self-hosted-runner findings are no longer generated; render
94+
// remaining non-excluded, non-not-pinned findings directly.
9695
for _, f := range dg.findings {
9796
if f.Category == checks.NotPinned || exclude[f.Category] {
9897
continue
9998
}
100-
if f.Category == checks.SelfHostedRunner {
101-
selfHostedFindings = append(selfHostedFindings, f)
102-
continue
103-
}
10499
renderFindingDetail(out, f, dep)
105100
}
106-
if len(selfHostedFindings) > 0 {
107-
renderSelfHostedGroup(out, selfHostedFindings)
108-
}
109101
}
110102

111103
parts := []string{}
112104
for _, cat := range []checks.Category{
113105
checks.LockfileForgery,
114106
checks.RefChanged, checks.NotPinned, checks.OnboardingRequired,
115107
checks.LocalAction,
116-
checks.SelfHostedRunner, checks.ExpressionRunner,
117108
checks.Stale, checks.MisleadingSHA,
118109
} {
119110
if n, ok := catCounts[cat]; ok {
@@ -163,38 +154,6 @@ func renderFindingDetail(out *ui.UI, f checks.Finding, dep string) {
163154
}
164155
}
165156

166-
// renderSelfHostedGroup prints a deduplicated block for self-hosted-runner
167-
// findings, listing each affected workflow and its non-hosted labels.
168-
func renderSelfHostedGroup(out *ui.UI, findings []checks.Finding) {
169-
label := "SELF-HOSTED-RUNNER"
170-
icon := "!"
171-
if IsAlertedCategory(checks.SelfHostedRunner) {
172-
icon = "✗"
173-
}
174-
out.Detail("%s %s", icon, out.Dim(label))
175-
for _, f := range findings {
176-
wfName := workflowName(f.WorkflowPath)
177-
out.Detail(" %s: %s", out.Bold(wfName), f.Detail)
178-
}
179-
if IsAlertedCategory(checks.SelfHostedRunner) && findings[0].Remediation != "" {
180-
out.Detail(" %s %s", ui.IconWarning, findings[0].Remediation)
181-
}
182-
labelSet := map[string]bool{}
183-
for _, f := range findings {
184-
for _, l := range extractBracketedLabels(f.Detail) {
185-
labelSet[l] = true
186-
}
187-
}
188-
if len(labelSet) > 0 {
189-
var labels []string
190-
for l := range labelSet {
191-
labels = append(labels, l)
192-
}
193-
sort.Strings(labels)
194-
out.Detail(" ↳ re-run with --allow-runners %s or -A to allow all", strings.Join(labels, ","))
195-
}
196-
}
197-
198157
// workflowName extracts the workflow filename from a path like
199158
// ".github/workflows/ci.yml".
200159
func workflowName(path string) string {
@@ -204,32 +163,6 @@ func workflowName(path string) string {
204163
return path
205164
}
206165

207-
// extractBracketedLabels pulls comma-separated items from the first
208-
// [...] group in s. Returns nil if no brackets are found.
209-
// Template expressions like ${{ matrix.os }} are excluded — they can't
210-
// be passed as literal --allow-runners values.
211-
func extractBracketedLabels(s string) []string {
212-
start := strings.Index(s, "[")
213-
end := strings.Index(s, "]")
214-
if start < 0 || end <= start {
215-
return nil
216-
}
217-
inner := s[start+1 : end]
218-
var labels []string
219-
for _, l := range strings.Split(inner, ",") {
220-
l = strings.TrimSpace(l)
221-
if l != "" && !isExpression(l) {
222-
labels = append(labels, l)
223-
}
224-
}
225-
return labels
226-
}
227-
228-
// isExpression reports whether s is a GitHub Actions template expression.
229-
func isExpression(s string) bool {
230-
return strings.Contains(s, "${")
231-
}
232-
233166
type warningGroup struct {
234167
finding checks.Finding
235168
count int
@@ -266,17 +199,13 @@ func renderWarnings(out *ui.UI, report *checks.Report, willRemediate bool) {
266199
}
267200

268201
// Triage warnings into buckets.
269-
var unpinnedWorkflows, localActionWorkflows, selfHostedRunnerWorkflows, expressionRunnerWorkflows, bareSHADeps, otherDetailWarnings []string
202+
var unpinnedWorkflows, localActionWorkflows, bareSHADeps, otherDetailWarnings []string
270203
for _, key := range warnOrder {
271204
wg := warnMap[key]
272205
f := wg.finding
273206
switch {
274207
case f.Category == checks.LocalAction:
275208
localActionWorkflows = append(localActionWorkflows, f.WorkflowPath)
276-
case f.Category == checks.SelfHostedRunner:
277-
selfHostedRunnerWorkflows = append(selfHostedRunnerWorkflows, f.WorkflowPath)
278-
case f.Category == checks.ExpressionRunner:
279-
expressionRunnerWorkflows = append(expressionRunnerWorkflows, f.WorkflowPath)
280209
case f.Category == checks.NotPinned && f.ActionRef == nil:
281210
unpinnedWorkflows = append(unpinnedWorkflows, f.WorkflowPath)
282211
case f.Category == checks.ShaAsRef:
@@ -310,45 +239,6 @@ func renderWarnings(out *ui.UI, report *checks.Report, willRemediate bool) {
310239
sort.Strings(localNames)
311240
out.TermDetail("↳ %s", strings.Join(localNames, ", "))
312241
}
313-
if len(selfHostedRunnerWorkflows) > 0 {
314-
out.TermCaution("%d %s skipped — non-hosted runner labels are not supported",
315-
len(selfHostedRunnerWorkflows),
316-
ui.Pluralize(len(selfHostedRunnerWorkflows), "workflow", "workflows"))
317-
// Collect distinct labels from findings for the remediation hint.
318-
labelSet := map[string]bool{}
319-
for _, key := range warnOrder {
320-
wg := warnMap[key]
321-
if wg.finding.Category != checks.SelfHostedRunner {
322-
continue
323-
}
324-
for _, l := range extractBracketedLabels(wg.finding.Detail) {
325-
labelSet[l] = true
326-
}
327-
}
328-
if len(labelSet) > 0 {
329-
var labels []string
330-
for l := range labelSet {
331-
labels = append(labels, l)
332-
}
333-
sort.Strings(labels)
334-
out.TermDetail("↳ if these are org-hosted larger runners, re-run with --allow-runners %s or -A to allow all",
335-
strings.Join(labels, ","))
336-
} else {
337-
out.TermDetail("↳ if these are org-hosted larger runners, re-run with --allow-runners <label> or -A to allow all")
338-
}
339-
}
340-
if len(expressionRunnerWorkflows) > 0 {
341-
out.TermCaution("%d %s skipped — runs-on uses expressions that can't be resolved statically",
342-
len(expressionRunnerWorkflows),
343-
ui.Pluralize(len(expressionRunnerWorkflows), "workflow", "workflows"))
344-
var wfNames []string
345-
for _, p := range expressionRunnerWorkflows {
346-
wfNames = append(wfNames, p)
347-
}
348-
sort.Strings(wfNames)
349-
out.TermDetail("↳ %s — re-run with -A (--allow-all-runners) if the matrix resolves to hosted runners",
350-
strings.Join(wfNames, ", "))
351-
}
352242
if len(unpinnedWorkflows) > 0 {
353243
out.TermWarn("%d %s not yet pinned",
354244
len(unpinnedWorkflows),

cmd/gh-actions-lock/format/terminal_test.go

Lines changed: 0 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -327,132 +327,6 @@ func TestPresentResults_ParseWarningsSurface(t *testing.T) {
327327
}
328328
}
329329

330-
// TestPresentResults_SelfHostedRunnerHintExcludesExpressions verifies that
331-
// template expressions like ${{ matrix.os }} are excluded from the
332-
// --allow-runners remediation hint shown for self-hosted-runner warnings.
333-
func TestPresentResults_SelfHostedRunnerHintExcludesExpressions(t *testing.T) {
334-
u, buf := newTestUI()
335-
report := &checks.Report{
336-
Workflows: []checks.WorkflowReport{{
337-
Path: ".github/workflows/ci.yml",
338-
Findings: []checks.Finding{
339-
{
340-
WorkflowPath: ".github/workflows/ci.yml",
341-
Category: checks.SelfHostedRunner,
342-
Severity: checks.SeverityWarning,
343-
Confidence: checks.ConfidenceHigh,
344-
Detail: "non-hosted runner labels [${{ matrix.os }}, internal-runner]",
345-
},
346-
},
347-
}},
348-
}
349-
PresentResults(u, report, true, false)
350-
351-
got := buf.String()
352-
if strings.Contains(got, "matrix.os") {
353-
t.Errorf("--allow-runners hint should not contain matrix expressions:\n%s", got)
354-
}
355-
if !strings.Contains(got, "internal-runner") {
356-
t.Errorf("--allow-runners hint should contain literal labels:\n%s", got)
357-
}
358-
}
359-
360-
// TestPresentResults_ExpressionRunnerWarning verifies that expression-only
361-
// workflows get their own distinct warning with workflow names and guidance.
362-
func TestPresentResults_ExpressionRunnerWarning(t *testing.T) {
363-
u, buf := newTestUI()
364-
report := &checks.Report{
365-
Workflows: []checks.WorkflowReport{
366-
{
367-
Path: ".github/workflows/ci.yaml",
368-
Findings: []checks.Finding{{
369-
WorkflowPath: ".github/workflows/ci.yaml",
370-
Category: checks.ExpressionRunner,
371-
Severity: checks.SeverityWarning,
372-
Confidence: checks.ConfidenceHigh,
373-
Detail: "runs-on uses expressions [${{ matrix.os }}] that can't be resolved statically",
374-
}},
375-
},
376-
{
377-
Path: ".github/workflows/maccloud-unified.yml",
378-
Findings: []checks.Finding{{
379-
WorkflowPath: ".github/workflows/maccloud-unified.yml",
380-
Category: checks.ExpressionRunner,
381-
Severity: checks.SeverityWarning,
382-
Confidence: checks.ConfidenceHigh,
383-
Detail: "runs-on uses expressions [${{ matrix.runner }}] that can't be resolved statically",
384-
}},
385-
},
386-
},
387-
}
388-
PresentResults(u, report, true, false)
389-
390-
got := buf.String()
391-
if !strings.Contains(got, "2 workflows skipped") {
392-
t.Errorf("should show count of expression-runner workflows:\n%s", got)
393-
}
394-
if !strings.Contains(got, "expressions that can't be resolved statically") {
395-
t.Errorf("should show expression-specific message:\n%s", got)
396-
}
397-
if !strings.Contains(got, "ci.yaml") {
398-
t.Errorf("should list affected workflow names:\n%s", got)
399-
}
400-
if !strings.Contains(got, "maccloud-unified.yml") {
401-
t.Errorf("should list affected workflow names:\n%s", got)
402-
}
403-
if !strings.Contains(got, "--allow-all-runners") {
404-
t.Errorf("should show --allow-all-runners remediation:\n%s", got)
405-
}
406-
}
407-
408-
func TestExtractBracketedLabels(t *testing.T) {
409-
tests := []struct {
410-
name string
411-
s string
412-
want []string
413-
}{
414-
{
415-
name: "plain labels",
416-
s: "non-hosted runner labels [internal-runner, ubuntu-22.04-32core]",
417-
want: []string{"internal-runner", "ubuntu-22.04-32core"},
418-
},
419-
{
420-
name: "matrix expression filtered",
421-
s: "non-hosted runner labels [${{ matrix.os }}, internal-runner]",
422-
want: []string{"internal-runner"},
423-
},
424-
{
425-
name: "only expressions returns nil",
426-
s: "non-hosted runner labels [${{ matrix.os }}]",
427-
want: nil,
428-
},
429-
{
430-
name: "no brackets",
431-
s: "no brackets here",
432-
want: nil,
433-
},
434-
}
435-
for _, tt := range tests {
436-
t.Run(tt.name, func(t *testing.T) {
437-
got := extractBracketedLabels(tt.s)
438-
if len(tt.want) == 0 {
439-
if len(got) != 0 {
440-
t.Errorf("got %v, want nil/empty", got)
441-
}
442-
} else {
443-
if len(got) != len(tt.want) {
444-
t.Fatalf("got %v, want %v", got, tt.want)
445-
}
446-
for i := range tt.want {
447-
if got[i] != tt.want[i] {
448-
t.Errorf("got[%d] = %q, want %q", i, got[i], tt.want[i])
449-
}
450-
}
451-
}
452-
})
453-
}
454-
}
455-
456330
// TestPresentResults_ExcludeCategoriesSkipsImpostor verifies that excluded
457331
// categories are not rendered in the error findings block. This prevents
458332
// duplication when renderInvestigationAlerts already surfaced the same

0 commit comments

Comments
 (0)