diff --git a/internal/pkg/labels/labels.go b/internal/pkg/labels/labels.go index a069b1f..8c6ff78 100644 --- a/internal/pkg/labels/labels.go +++ b/internal/pkg/labels/labels.go @@ -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 "" } diff --git a/internal/pkg/labels/labels_test.go b/internal/pkg/labels/labels_test.go index bad16d9..9429a9b 100644 --- a/internal/pkg/labels/labels_test.go +++ b/internal/pkg/labels/labels_test.go @@ -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) {