mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-03-20 20:06:16 +08:00
233 lines
5.7 KiB
Go
233 lines
5.7 KiB
Go
package tart
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/actions-oss/act-cli/pkg/common"
|
|
"github.com/actions-oss/act-cli/pkg/container"
|
|
"github.com/kballard/go-shellquote"
|
|
)
|
|
|
|
type Environment struct {
|
|
container.HostEnvironment
|
|
vm *VM
|
|
Config Config
|
|
Env *Env
|
|
Miscpath string
|
|
}
|
|
|
|
// "/Volumes/My Shared Files/act/"
|
|
func (e *Environment) ToHostPath(path string) string {
|
|
return e.translatePath(path, false)
|
|
}
|
|
|
|
func (e *Environment) ToContainerPath(path string) string {
|
|
path = e.HostEnvironment.ToContainerPath(path)
|
|
return e.translatePath(path, true)
|
|
}
|
|
|
|
func (e *Environment) translatePath(path string, reverse bool) string {
|
|
mounts := map[string]string{
|
|
"/private/tmp/act": e.Miscpath,
|
|
"/private/tmp/tool_cache": e.ToolCache,
|
|
}
|
|
altPath := filepath.Clean(path)
|
|
for k, v := range mounts {
|
|
if reverse {
|
|
v, k = k, v
|
|
}
|
|
actPath := filepath.Clean(k)
|
|
add := 0
|
|
if strings.HasPrefix(altPath, actPath+"/") {
|
|
add = 1
|
|
}
|
|
if altPath == actPath || add > 0 {
|
|
return filepath.Join(v, altPath[len(actPath)+add:])
|
|
}
|
|
}
|
|
return altPath
|
|
}
|
|
|
|
func (e *Environment) Exec(command []string /*cmdline string, */, env map[string]string, user, workdir string) common.Executor {
|
|
return e.ExecWithCmdLine(command, "", env, user, workdir)
|
|
}
|
|
|
|
func (e *Environment) ExecWithCmdLine(command []string, cmdline string, env map[string]string, user, workdir string) common.Executor {
|
|
return func(ctx context.Context) error {
|
|
if err := e.exec(ctx, command, cmdline, env, user, workdir); err != nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("this step has been cancelled: %w", err)
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (e *Environment) Start(b bool) common.Executor {
|
|
return e.HostEnvironment.Start(b).Then(func(ctx context.Context) error {
|
|
return e.start(ctx)
|
|
})
|
|
}
|
|
|
|
func (e *Environment) start(ctx context.Context) error {
|
|
actEnv := e.Env
|
|
|
|
config := e.Config
|
|
|
|
config.Writer = e.StdOut
|
|
|
|
if config.AlwaysPull {
|
|
common.Logger(ctx).Infof("Pulling the latest version of %s...\n", actEnv.JobImage)
|
|
_, _, err := ExecWithEnv(ctx, nil,
|
|
"pull", actEnv.JobImage)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
common.Logger(ctx).Info("Cloning and configuring a new VM...")
|
|
vm, err := CreateNewVM(ctx, *actEnv, 0, 0)
|
|
if err != nil {
|
|
_ = e.Stop(ctx)
|
|
return err
|
|
}
|
|
var customDirectoryMounts []string
|
|
_ = os.MkdirAll(e.Miscpath, 0777)
|
|
_ = os.MkdirAll(e.ToolCache, 0777)
|
|
customDirectoryMounts = append(customDirectoryMounts, "act:"+e.Miscpath)
|
|
customDirectoryMounts = append(customDirectoryMounts, "tool_cache:"+e.ToolCache)
|
|
e.vm = vm
|
|
err = vm.Start(ctx, config, actEnv, customDirectoryMounts)
|
|
if err != nil {
|
|
_ = e.Stop(ctx)
|
|
return err
|
|
}
|
|
|
|
return e.execRaw(ctx, "ln -sf '/Volumes/My Shared Files/act' /private/tmp/act && ln -sf '/Volumes/My Shared Files/tool_cache' /private/tmp/tool_cache")
|
|
}
|
|
func (e *Environment) Stop(ctx context.Context) error {
|
|
common.Logger(ctx).Debug("Preparing stopping VM")
|
|
|
|
actEnv := e.Env
|
|
|
|
var vm *VM
|
|
if e.vm != nil {
|
|
vm = e.vm
|
|
} else {
|
|
vm = ExistingVM(*actEnv)
|
|
}
|
|
|
|
if err := vm.Stop(ctx); err != nil {
|
|
common.Logger(ctx).Errorf("Failed to stop VM: %v", err)
|
|
}
|
|
|
|
if err := vm.Delete(ctx); err != nil {
|
|
common.Logger(ctx).Error("Failed to delete VM: %v", err)
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *Environment) Remove() common.Executor {
|
|
return func(ctx context.Context) error {
|
|
_ = e.Stop(ctx)
|
|
common.Logger(ctx).Debug("Stopped VM, removing...")
|
|
if e.CleanUp != nil {
|
|
e.CleanUp()
|
|
}
|
|
_ = os.RemoveAll(e.Path)
|
|
return e.Close()(ctx)
|
|
}
|
|
}
|
|
func (e *Environment) exec(ctx context.Context, command []string, _ string, env map[string]string, _, workdir string) error {
|
|
var wd string
|
|
if workdir != "" {
|
|
if filepath.IsAbs(workdir) {
|
|
wd = filepath.Clean(workdir)
|
|
} else {
|
|
wd = filepath.Clean(filepath.Join(e.Path, workdir))
|
|
}
|
|
} else {
|
|
wd = e.ToContainerPath(e.Path)
|
|
}
|
|
envs := ""
|
|
for k, v := range env {
|
|
envs += shellquote.Join(k) + "=" + shellquote.Join(v) + " "
|
|
}
|
|
return e.execRaw(ctx, "cd "+shellquote.Join(wd)+"\nenv "+envs+shellquote.Join(command...)+"\nexit $?")
|
|
}
|
|
|
|
func (e *Environment) execRaw(ctx context.Context, script string) error {
|
|
actEnv := e.Env
|
|
|
|
var vm *VM
|
|
if e.vm != nil {
|
|
vm = e.vm
|
|
} else {
|
|
vm = ExistingVM(*actEnv)
|
|
}
|
|
|
|
config := e.Config
|
|
|
|
ssh, err := vm.OpenSSH(ctx, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer ssh.Close()
|
|
|
|
session, err := ssh.NewSession()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer session.Close()
|
|
|
|
common.Logger(ctx).Debug(script)
|
|
|
|
session.Stdin = strings.NewReader(
|
|
script,
|
|
)
|
|
session.Stdout = e.StdOut
|
|
session.Stderr = e.StdOut
|
|
|
|
err = session.Shell()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return session.Wait()
|
|
}
|
|
|
|
func (e *Environment) GetActPath() string {
|
|
return e.ToContainerPath(e.HostEnvironment.GetActPath())
|
|
}
|
|
|
|
func (e *Environment) Copy(destPath string, files ...*container.FileEntry) common.Executor {
|
|
return e.HostEnvironment.Copy(e.ToHostPath(destPath), files...)
|
|
}
|
|
func (e *Environment) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
|
|
return e.HostEnvironment.CopyTarStream(ctx, e.ToHostPath(destPath), tarStream)
|
|
}
|
|
func (e *Environment) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
|
|
return e.HostEnvironment.CopyDir(e.ToHostPath(destPath), srcPath, useGitIgnore)
|
|
}
|
|
func (e *Environment) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) {
|
|
return e.HostEnvironment.GetContainerArchive(ctx, e.ToHostPath(srcPath))
|
|
}
|
|
|
|
func (e *Environment) GetRunnerContext(ctx context.Context) map[string]interface{} {
|
|
rctx := e.HostEnvironment.GetRunnerContext(ctx)
|
|
rctx["temp"] = e.ToContainerPath(e.TmpDir)
|
|
rctx["tool_cache"] = e.ToContainerPath(e.ToolCache)
|
|
return rctx
|
|
}
|