diff --git a/mage/download/download.go b/mage/download/download.go index 26a6116..8eabc16 100644 --- a/mage/download/download.go +++ b/mage/download/download.go @@ -20,7 +20,7 @@ import ( ) const ( - RunnerVersion = "2.333.1" + RunnerVersion = "2.335.1" CNIVersion = "1.6.2" ContainerdVersion = "2.2.2" RuncVersion = "1.3.4" diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 519e9d9..7044e0f 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" goruntime "runtime" + "strconv" "strings" ) @@ -113,7 +114,16 @@ func (m *Manager) Extract() error { return nil } -// findTarball locates the runner archive in the embedded filesystem for the current OS. +// findTarball locates the runner archive in the embedded filesystem for the +// current OS, preferring the HIGHEST embedded version. +// +// Selecting by version (rather than "first prefix match") is critical: the +// embed directory can legitimately hold more than one version — a RunnerVersion +// bump downloads the new tarball but leaves the old one behind — and ReadDir +// returns them sorted, so a naive first-match ships the OLDER runner. GitHub +// deprecates old runner versions and refuses their connections, which silently +// fails every job ("Runner version vX is deprecated and cannot receive +// messages"). Always pick the newest so a leftover tarball can't regress us. func (m *Manager) findTarball() (string, error) { // Runner archives are named: actions-runner-{os}-{arch}-{version}.{ext} // os: linux, win, osx @@ -132,12 +142,21 @@ func (m *Manager) findTarball() (string, error) { return "", fmt.Errorf("reading embedded files: %w", err) } + var best string + var bestVer []int for _, e := range entries { name := e.Name() - if strings.HasPrefix(name, osPrefix) { - return "embed/" + name, nil + if !strings.HasPrefix(name, osPrefix) { + continue + } + ver := parseRunnerVersion(name) + if best == "" || compareRunnerVersions(ver, bestVer) > 0 { + best, bestVer = name, ver } } + if best != "" { + return "embed/" + best, nil + } // Fall back to first non-gitkeep file if no OS match for _, e := range entries { @@ -148,7 +167,46 @@ func (m *Manager) findTarball() (string, error) { return "embed/" + name, nil } - return "", fmt.Errorf("no runner archive found in embedded files (did you run 'make download-runner'?)") + return "", fmt.Errorf("no runner archive found in embedded files (did you run 'mage download'?)") +} + +// parseRunnerVersion extracts the numeric version components from a runner +// archive name like "actions-runner-osx-arm64-2.335.1.tar.gz" -> [2 335 1]. +// Names it can't parse return nil, which sorts lowest. +func parseRunnerVersion(name string) []int { + base := strings.TrimSuffix(strings.TrimSuffix(name, ".tar.gz"), ".zip") + idx := strings.LastIndex(base, "-") + if idx < 0 { + return nil + } + parts := strings.Split(base[idx+1:], ".") + nums := make([]int, 0, len(parts)) + for _, p := range parts { + n, err := strconv.Atoi(p) + if err != nil { + return nil + } + nums = append(nums, n) + } + return nums +} + +// compareRunnerVersions returns >0 if a>b, <0 if a 0) != (c.want > 0) || (got < 0) != (c.want < 0) || (got == 0) != (c.want == 0) { + t.Errorf("compareRunnerVersions(%v,%v) sign = %d, want sign %d", c.a, c.b, got, c.want) + } + } +} + +// TestFindTarball_PicksHighestVersion guards the real embedded FS: whatever +// versions are present, findTarball must return the newest one for this OS so a +// leftover older tarball can never ship a GitHub-deprecated runner. +func TestFindTarball_PicksHighestVersion(t *testing.T) { + m := &Manager{} + got, err := m.findTarball() + if err != nil { + t.Fatalf("findTarball: %v", err) + } + chosen := parseRunnerVersion(got) + + entries, err := runnerFS.ReadDir("embed") + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + var osPrefix string + switch goruntime.GOOS { + case "windows": + osPrefix = "actions-runner-win-" + case "darwin": + osPrefix = "actions-runner-osx-" + default: + osPrefix = "actions-runner-linux-" + } + for _, e := range entries { + if !strings.HasPrefix(e.Name(), osPrefix) { + continue + } + if compareRunnerVersions(parseRunnerVersion(e.Name()), chosen) > 0 { + t.Errorf("findTarball chose %q but a newer tarball %q is embedded", got, e.Name()) + } + } +}