2
0
Files
gitcaddy-runner/internal/pkg/labels/labels.go
logikonline ee5fd838b8
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
feat(labels): support schema validation in runs-on matching
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.
2026-02-09 02:30:24 -05:00

131 lines
3.2 KiB
Go

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Package labels provides utilities for parsing and managing runner labels.
package labels
import (
"fmt"
"strings"
)
// Label scheme constants define the execution environments.
const (
SchemeHost = "host"
SchemeDocker = "docker"
)
// Label represents a parsed runner label with name, schema, and optional argument.
type Label struct {
Name string
Schema string
Arg string
}
// Parse parses a label string in the format "name:schema:arg" and returns a Label.
func Parse(str string) (*Label, error) {
splits := strings.SplitN(str, ":", 3)
label := &Label{
Name: splits[0],
Schema: "host",
Arg: "",
}
if len(splits) >= 2 {
label.Schema = splits[1]
}
if len(splits) >= 3 {
label.Arg = splits[2]
}
if label.Schema != SchemeHost && label.Schema != SchemeDocker {
return nil, fmt.Errorf("unsupported schema: %s", label.Schema)
}
return label, nil
}
// Labels is a slice of Label pointers.
type Labels []*Label
// RequireDocker returns true if any label uses the docker schema.
func (l Labels) RequireDocker() bool {
for _, label := range l {
if label.Schema == SchemeDocker {
return true
}
}
return false
}
// 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:
platforms[label.Name] = strings.TrimPrefix(label.Arg, "//")
schemas[label.Name] = SchemeDocker
case SchemeHost:
platforms[label.Name] = "-self-hosted"
schemas[label.Name] = SchemeHost
default:
continue
}
}
for _, v := range runsOn {
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
return ""
}
// Names returns the names of all labels.
func (l Labels) Names() []string {
names := make([]string, 0, len(l))
for _, label := range l {
names = append(names, label.Name)
}
return names
}
// ToStrings converts labels back to their string representation.
func (l Labels) ToStrings() []string {
ls := make([]string, 0, len(l))
for _, label := range l {
lbl := label.Name
if label.Schema != "" {
lbl += ":" + label.Schema
if label.Arg != "" {
lbl += ":" + label.Arg
}
}
ls = append(ls, lbl)
}
return ls
}