2
0

feat(labels): support schema validation in runs-on matching
Some checks failed
CI / build-and-test (push) Has been cancelled
Release / build (amd64, darwin) (push) Successful in 53s
Release / build (amd64, linux) (push) Successful in 1m4s
Release / build (amd64, windows) (push) Successful in 50s
Release / build (arm64, darwin) (push) Successful in 48s
Release / build (arm64, linux) (push) Successful in 51s
Release / release (push) Successful in 21s

Enhances PickPlatform to validate schema when runs-on includes explicit mode (e.g., "linux:host" or "ubuntu:docker"). Parses schema suffix from runs-on values and ensures it matches the runner's configured schema for that label. Returns empty string on schema mismatch to prevent jobs from running in wrong environment (e.g., workflow requesting :host but runner only has :docker). Adds test coverage for schema matching, mismatches, and backward compatibility with schema-less runs-on values.
This commit is contained in:
2026-02-09 02:30:24 -05:00
parent 17f78a5e4c
commit ee5fd838b8
2 changed files with 58 additions and 9 deletions

View File

@@ -57,31 +57,50 @@ func (l Labels) RequireDocker() bool {
// PickPlatform selects the appropriate platform based on the runsOn requirements.
// Returns empty string if no matching label is found, which will cause the job to fail.
// If runs-on includes a schema (e.g., "linux:host" or "linux:docker"), it must match
// the runner's configured schema for that label.
func (l Labels) PickPlatform(runsOn []string) string {
// Build maps for both platform values and schemas
platforms := make(map[string]string, len(l))
schemas := make(map[string]string, len(l))
for _, label := range l {
switch label.Schema {
case SchemeDocker:
// "//" will be ignored
platforms[label.Name] = strings.TrimPrefix(label.Arg, "//")
schemas[label.Name] = SchemeDocker
case SchemeHost:
platforms[label.Name] = "-self-hosted"
schemas[label.Name] = SchemeHost
default:
// It should not happen, because Parse has checked it.
continue
}
}
for _, v := range runsOn {
if v, ok := platforms[v]; ok {
return v
name := v
requestedSchema := ""
// Parse schema if present (e.g., "germany-linux:host" -> name="germany-linux", schema="host")
if idx := strings.Index(v, ":"); idx != -1 {
name = v[:idx]
requestedSchema = v[idx+1:]
// Handle docker:// prefix
if strings.HasPrefix(requestedSchema, "docker") {
requestedSchema = SchemeDocker
}
}
if platform, ok := platforms[name]; ok {
// If schema was specified, validate it matches
if requestedSchema != "" && requestedSchema != schemas[name] {
// Schema mismatch - workflow asked for different mode than runner provides
continue
}
return platform
}
}
// No matching label found. This indicates a mismatch between server's view
// of runner labels and the runner's local configuration (e.g., labels were
// edited in Gitea admin UI after runner registered). Return empty string
// to cause the job to fail with a clear error rather than silently running
// in the wrong environment.
// No matching label found
return ""
}

View File

@@ -53,6 +53,36 @@ func TestPickPlatform(t *testing.T) {
runsOn: []string{"windows", "ubuntu"},
want: "node:18",
},
{
name: "runsOn with :host suffix matches host label",
labels: []string{"germany-linux:host", "linux:host"},
runsOn: []string{"germany-linux:host"},
want: "-self-hosted",
},
{
name: "runsOn with :docker suffix matches docker label",
labels: []string{"ubuntu:docker://node:18"},
runsOn: []string{"ubuntu:docker"},
want: "node:18",
},
{
name: "runsOn without suffix matches any schema",
labels: []string{"linux:host"},
runsOn: []string{"linux"},
want: "-self-hosted",
},
{
name: "runsOn :docker does not match host label",
labels: []string{"linux:host"},
runsOn: []string{"linux:docker"},
want: "",
},
{
name: "runsOn :host does not match docker label",
labels: []string{"ubuntu:docker://node:18"},
runsOn: []string{"ubuntu:host"},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {