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
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:
@@ -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 ""
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user