mirror of
https://gitea.com/gitea/act_runner
synced 2026-09-21 19:37:07 +02:00
enhance: add macOS VM executor backend
Replace the Tart-based prototype with a runner-oriented macOS VM executor backed by runner-executor-macos and Apple Virtualization. Assisted-by: Codet:MODEL_VERSION
This commit is contained in:
@@ -62,6 +62,7 @@ func TestRegisterInputsValidate(t *testing.T) {
|
||||
|
||||
func TestValidateLabels(t *testing.T) {
|
||||
require.NoError(t, validateLabels([]string{"ubuntu:host", "ubuntu:docker://node:18"}))
|
||||
require.NoError(t, validateLabels([]string{"macos-latest:macos-vm://ghcr.io/cirruslabs/macos-sonoma-base:latest"}))
|
||||
// a colon that is not a supported schema is part of the label name
|
||||
require.NoError(t, validateLabels([]string{"pool:e57e18d4-10d4-406f-93bf-60f127221bdd"}))
|
||||
require.Error(t, validateLabels([]string{"ubuntu:host", ""}))
|
||||
|
||||
@@ -144,6 +144,9 @@ func (r *Runner) Close() error {
|
||||
// removeOrphanNetworks is a variable so tests can substitute one that needs no Docker daemon.
|
||||
var removeOrphanNetworks = container.RemoveOrphanNetworks
|
||||
|
||||
// removeOrphanMacOSVMs is a variable so tests can substitute one that needs no macOS VM executor.
|
||||
var removeOrphanMacOSVMs = container.CleanupOrphanMacOSVMs
|
||||
|
||||
// OnIdle performs lightweight maintenance during polling idle windows.
|
||||
// It runs synchronously on the poller goroutine; shouldRunIdleCleanup
|
||||
// throttles invocations to runner.idle_cleanup_interval so the impact on
|
||||
@@ -166,6 +169,7 @@ func (r *Runner) OnIdle(ctx context.Context) {
|
||||
r.cleanupStaleDirs(ctx, hostRoot, isHostScratchDir)
|
||||
}
|
||||
r.cleanupOrphanNetworks(ctx)
|
||||
r.cleanupOrphanMacOSVMs(ctx)
|
||||
}
|
||||
|
||||
// cleanupOrphanNetworks reclaims the per-job networks of jobs this runner did not live to
|
||||
@@ -182,6 +186,16 @@ func (r *Runner) cleanupOrphanNetworks(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) cleanupOrphanMacOSVMs(ctx context.Context) {
|
||||
if r.uuid == "" || !r.labels.RequireMacOSVM() || r.cfg.Runner.WorkdirCleanupAge <= 0 {
|
||||
return
|
||||
}
|
||||
cutoff := r.now().Add(-r.cfg.Runner.WorkdirCleanupAge)
|
||||
if err := removeOrphanMacOSVMs(ctx, r.cfg.MacOSVM.ExecutorPath, r.uuid, cutoff); err != nil {
|
||||
log.Warnf("failed to clean up macOS VMs left behind by earlier jobs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) shouldRunIdleCleanup() bool {
|
||||
if r.cfg.Runner.WorkdirCleanupAge <= 0 || r.cfg.Runner.IdleCleanupInterval <= 0 {
|
||||
return false
|
||||
@@ -548,8 +562,16 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
Vars: task.Vars,
|
||||
ValidVolumes: r.cfg.Container.ValidVolumes,
|
||||
SharedToolCache: r.cfg.Runner.ToolCacheMode == config.ToolCacheModeShared,
|
||||
InsecureSkipTLS: r.cfg.Runner.Insecure,
|
||||
RunnerName: r.name,
|
||||
MacOSVM: runner.MacOSVMConfig{
|
||||
ExecutorPath: r.cfg.MacOSVM.ExecutorPath,
|
||||
WorkdirParent: r.cfg.MacOSVM.WorkdirParent,
|
||||
CPU: r.cfg.MacOSVM.CPU,
|
||||
Memory: r.cfg.MacOSVM.Memory,
|
||||
BootTimeout: r.cfg.MacOSVM.BootTimeout,
|
||||
},
|
||||
InsecureSkipTLS: r.cfg.Runner.Insecure,
|
||||
RunnerUUID: r.uuid,
|
||||
RunnerName: r.name,
|
||||
}
|
||||
|
||||
rr, err := runner.New(runnerConfig)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
"gitea.com/gitea/runner/internal/pkg/labels"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -331,3 +332,37 @@ func TestRunnerOnIdleRemovesOrphanNetworks(t *testing.T) {
|
||||
hostOnly.OnIdle(context.Background())
|
||||
assert.Equal(t, []string{"runner-1"}, swept)
|
||||
}
|
||||
|
||||
func TestRunnerOnIdleRemovesOrphanMacOSVMs(t *testing.T) {
|
||||
now := time.Date(2026, time.April, 29, 20, 0, 0, 0, time.UTC)
|
||||
cfg := &config.Config{
|
||||
Runner: config.Runner{
|
||||
WorkdirCleanupAge: 24 * time.Hour,
|
||||
IdleCleanupInterval: time.Minute,
|
||||
},
|
||||
}
|
||||
macOSVMLabel, err := labels.Parse("macos-latest:macos-vm://ghcr.io/cirruslabs/macos-sonoma-base:latest")
|
||||
require.NoError(t, err)
|
||||
|
||||
var swept []string
|
||||
var sweptCutoff time.Time
|
||||
origRemoveOrphanMacOSVMs := removeOrphanMacOSVMs
|
||||
removeOrphanMacOSVMs = func(_ context.Context, _, runnerUUID string, accessedBefore time.Time) error {
|
||||
swept = append(swept, runnerUUID)
|
||||
sweptCutoff = accessedBefore
|
||||
return nil
|
||||
}
|
||||
t.Cleanup(func() { removeOrphanMacOSVMs = origRemoveOrphanMacOSVMs })
|
||||
|
||||
r := &Runner{uuid: "runner-1", cfg: cfg, labels: labels.Labels{macOSVMLabel}, now: func() time.Time { return now }}
|
||||
r.OnIdle(context.Background())
|
||||
assert.Equal(t, []string{"runner-1"}, swept)
|
||||
assert.Equal(t, now.Add(-24*time.Hour), sweptCutoff)
|
||||
|
||||
// a docker-only runner has no macOS VMs to sweep
|
||||
dockerLabel, err := labels.Parse("ubuntu:docker://node:18")
|
||||
require.NoError(t, err)
|
||||
dockerOnly := &Runner{uuid: "runner-2", cfg: &config.Config{Runner: cfg.Runner}, labels: labels.Labels{dockerLabel}, now: func() time.Time { return now }}
|
||||
dockerOnly.OnIdle(context.Background())
|
||||
assert.Equal(t, []string{"runner-1"}, swept)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user