feat: tart macOS vm's as job container (#33)

adds the tart:// protocol to platform mapping

e.g. `-P macos-14=tart://ghcr.io/cirruslabs/macos-sonoma-base:latest` if you have a mac.

`add-path` is probably broken
This commit is contained in:
ChristopherHX
2025-01-29 17:27:04 +01:00
committed by GitHub
parent 592dc4bf2c
commit 677e073448
10 changed files with 832 additions and 26 deletions

View File

@@ -655,6 +655,9 @@ func (rc *RunContext) startContainer() common.Executor {
if rc.IsHostEnv(ctx) {
return rc.startHostEnvironment()(ctx)
}
if rc.IsTartEnv(ctx) {
return rc.startTartEnvironment()(ctx)
}
return rc.startJobContainer()(ctx)
}
}
@@ -665,6 +668,12 @@ func (rc *RunContext) IsHostEnv(ctx context.Context) bool {
return image == "" && strings.EqualFold(platform, "-self-hosted")
}
func (rc *RunContext) IsTartEnv(ctx context.Context) bool {
platform := rc.runsOnImage(ctx)
image := rc.containerImage(ctx)
return image == "" && strings.HasPrefix(platform, "tart://")
}
func (rc *RunContext) stopContainer() common.Executor {
return rc.stopJobContainer()
}

View File

@@ -0,0 +1,111 @@
package runner
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/actions-oss/act-cli/pkg/common"
"github.com/actions-oss/act-cli/pkg/container"
"github.com/actions-oss/act-cli/pkg/tart"
)
func (rc *RunContext) startTartEnvironment() common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
rawLogger := logger.WithField("raw_output", true)
logWriter := common.NewLineWriter(rc.commandHandler(ctx), func(s string) bool {
if rc.Config.LogOutput {
rawLogger.Infof("%s", s)
} else {
rawLogger.Debugf("%s", s)
}
return true
})
cacheDir := rc.ActionCacheDir()
randBytes := make([]byte, 8)
_, _ = rand.Read(randBytes)
miscpath := filepath.Join(cacheDir, hex.EncodeToString(randBytes))
actPath := filepath.Join(miscpath, "act")
if err := os.MkdirAll(actPath, 0o777); err != nil {
return err
}
path := filepath.Join(miscpath, "hostexecutor")
if err := os.MkdirAll(path, 0o777); err != nil {
return err
}
runnerTmp := filepath.Join(miscpath, "tmp")
if err := os.MkdirAll(runnerTmp, 0o777); err != nil {
return err
}
toolCache := filepath.Join(cacheDir, "tool_cache")
platImage := rc.runsOnImage(ctx)
platURI, _ := url.Parse(platImage)
query := platURI.Query()
tenv := &tart.Environment{
HostEnvironment: container.HostEnvironment{
Path: path,
TmpDir: runnerTmp,
ToolCache: toolCache,
Workdir: rc.Config.Workdir,
ActPath: actPath,
CleanUp: func() {
os.RemoveAll(miscpath)
},
StdOut: logWriter,
},
Config: tart.Config{
SSHUsername: "admin",
SSHPassword: "admin",
Softnet: query.Get("softnet") == "1",
Headless: query.Get("headless") != "0",
AlwaysPull: query.Get("pull") != "0",
},
Env: &tart.Env{
JobImage: platURI.Host + platURI.EscapedPath(),
JobID: rc.jobContainerName(),
},
Miscpath: miscpath,
}
rc.JobContainer = tenv
if query.Has("sshusername") {
tenv.Config.SSHUsername = query.Get("sshusername")
}
if query.Has("sshpassword") {
tenv.Config.SSHPassword = query.Get("sshpassword")
}
rc.cleanUpJobContainer = rc.JobContainer.Remove()
for k, v := range rc.JobContainer.GetRunnerContext(ctx) {
if v, ok := v.(string); ok {
rc.Env[fmt.Sprintf("RUNNER_%s", strings.ToUpper(k))] = v
}
}
// for _, env := range os.Environ() {
// if k, v, ok := strings.Cut(env, "="); ok {
// // don't override
// if _, ok := rc.Env[k]; !ok {
// rc.Env[k] = v
// }
// }
// }
return common.NewPipelineExecutor(
// rc.JobContainer.Remove(),
rc.JobContainer.Start(false),
rc.JobContainer.Copy(rc.JobContainer.GetActPath()+"/", &container.FileEntry{
Name: "workflow/event.json",
Mode: 0o644,
Body: rc.EventJSON,
}, &container.FileEntry{
Name: "workflow/envs.txt",
Mode: 0o666,
Body: "",
}),
)(ctx)
}
}

View File

@@ -0,0 +1,16 @@
//go:build !darwin
package runner
import (
"context"
"fmt"
"github.com/actions-oss/act-cli/pkg/common"
)
func (rc *RunContext) startTartEnvironment() common.Executor {
return func(_ context.Context) error {
return fmt.Errorf("You need macOS for tart")
}
}

View File

@@ -358,6 +358,33 @@ func TestRunEvent(t *testing.T) {
}
}
func TestTartNotSupportedOnNonDarwin(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
ctx := context.Background()
tables := []TestJobFileInfo{}
if runtime.GOOS != "darwin" {
platforms := map[string]string{
"ubuntu-latest": "tart://ghcr.io/cirruslabs/macos-sonoma-base:latest",
}
tables = append(tables, []TestJobFileInfo{
// Shells
{workdir, "basic", "push", "tart not supported", platforms, secrets},
}...)
}
for _, table := range tables {
t.Run(table.workflowPath, func(t *testing.T) {
table.runTest(ctx, t, &Config{})
})
}
}
func TestRunEventHostEnvironment(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")