From 418b0db047fd23799bf1bd04c8dd68fd079c5d2a Mon Sep 17 00:00:00 2001 From: Casey Lee Date: Thu, 20 Feb 2020 22:43:20 -0500 Subject: [PATCH] reuse containers to fix #86 --- README.md | 6 ++-- cmd/platforms.go | 6 ++-- pkg/model/workflow.go | 1 + pkg/runner/run_context.go | 35 ++++++++++++++++++---- pkg/runner/step.go | 49 ++++++++++++++++++++++++------- pkg/runner/testdata/node/push.yml | 16 ++++++++++ 6 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 pkg/runner/testdata/node/push.yml diff --git a/README.md b/README.md index bd1a460..813a3b8 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ GitHub Actions offers managed [virtual environments](https://help.github.com/en/ | GitHub Runner | Docker Image | | --------------- | ------------ | -| ubuntu-latest | [ubuntu:18.04](https://hub.docker.com/_/ubuntu) | -| ubuntu-18.04 | [ubuntu:18.04](https://hub.docker.com/_/ubuntu) | -| ubuntu-16.04 | [ubuntu:16.04](https://hub.docker.com/_/ubuntu) | +| ubuntu-latest | [node:12.6-buster-slim](https://hub.docker.com/_/buildpack-deps) | +| ubuntu-18.04 | [node:12.6-buster-slim](https://hub.docker.com/_/buildpack-deps) | +| ubuntu-16.04 | [node:12.6-stretch-slim](https://hub.docker.com/_/buildpack-deps) | | windows-latest | `unsupported` | | windows-2019 | `unsupported` | | macos-latest | `unsupported` | diff --git a/cmd/platforms.go b/cmd/platforms.go index 46d45b1..b79e8ed 100644 --- a/cmd/platforms.go +++ b/cmd/platforms.go @@ -6,9 +6,9 @@ import ( func (i *Input) newPlatforms() map[string]string { platforms := map[string]string{ - "ubuntu-latest": "ubuntu:18.04", - "ubuntu-18.04": "ubuntu:18.04", - "ubuntu-16.04": "ubuntu:16.04", + "ubuntu-latest": "node:12.6-buster-slim", + "ubuntu-18.04": "node:12.6-buster-slim", + "ubuntu-16.04": "node:12.6-stretch-slim", "windows-latest": "", "windows-2019": "", "macos-latest": "", diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go index c25cf5c..f0c2a46 100644 --- a/pkg/model/workflow.go +++ b/pkg/model/workflow.go @@ -104,6 +104,7 @@ type ContainerSpec struct { Entrypoint string Args string Name string + Reuse bool } // Step is the structure of one step in a job diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 2289847..3465367 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" "os" - "path/filepath" "regexp" "runtime" "strings" @@ -107,7 +106,19 @@ func (rc *RunContext) Executor() common.Executor { return nil } - return common.NewPipelineExecutor(steps...)(ctx) + nullLogger := logrus.New() + nullLogger.Out = ioutil.Discard + if !rc.Config.ReuseContainers { + rc.newContainerCleaner()(common.WithLogger(ctx, nullLogger)) + } + + err := common.NewPipelineExecutor(steps...)(ctx) + + if !rc.Config.ReuseContainers { + rc.newContainerCleaner()(common.WithLogger(ctx, nullLogger)) + } + + return err } } @@ -202,7 +213,7 @@ func (rc *RunContext) runContainer(containerSpec *model.ContainerSpec) common.Ex fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"), }, Content: map[string]io.Reader{"/github": ghReader}, - ReuseContainers: rc.Config.ReuseContainers, + ReuseContainers: containerSpec.Reuse, Stdout: logWriter, Stderr: logWriter, })(ctx) @@ -241,17 +252,29 @@ func (rc *RunContext) createGithubTarball() (io.Reader, error) { } -func (rc *RunContext) createContainerName(stepID string) string { - containerName := fmt.Sprintf("%s-%s", stepID, rc.Tempdir) +func (rc *RunContext) createContainerName() string { + containerName := rc.Run.String() containerName = regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(containerName, "-") - prefix := fmt.Sprintf("%s-", trimToLen(filepath.Base(rc.Config.Workdir), 10)) + prefix := "" suffix := "" containerName = trimToLen(containerName, 30-(len(prefix)+len(suffix))) return fmt.Sprintf("%s%s%s", prefix, containerName, suffix) + +} + +func (rc *RunContext) createStepContainerName(stepID string) string { + + prefix := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(rc.createContainerName(), "-") + suffix := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(stepID, "-") + containerName := trimToLen(prefix, 30-len(suffix)) + return fmt.Sprintf("%s%s%s", prefix, containerName, suffix) } func trimToLen(s string, l int) string { + if l < 0 { + l = 0 + } if len(s) > l { return s[:l] } diff --git a/pkg/runner/step.go b/pkg/runner/step.go index 408f033..e5cf61c 100644 --- a/pkg/runner/step.go +++ b/pkg/runner/step.go @@ -37,21 +37,44 @@ func (rc *RunContext) setupEnv(containerSpec *model.ContainerSpec, step *model.S } } +func (rc *RunContext) newContainerCleaner() common.Executor { + job := rc.Run.Job() + containerSpec := new(model.ContainerSpec) + containerSpec.Name = rc.createContainerName() + containerSpec.Reuse = false + + if job.Container != nil { + containerSpec.Image = job.Container.Image + } else { + platformName := rc.ExprEval.Interpolate(rc.Run.Job().RunsOn) + containerSpec.Image = rc.Config.Platforms[strings.ToLower(platformName)] + } + containerSpec.Entrypoint = "bash --noprofile --norc -o pipefail -c echo 'cleaning up'" + return common.NewPipelineExecutor( + rc.pullImage(containerSpec), + rc.runContainer(containerSpec), + ) +} + func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { job := rc.Run.Job() containerSpec := new(model.ContainerSpec) - containerSpec.Name = rc.createContainerName(step.ID) + containerSpec.Name = rc.createContainerName() + containerSpec.Reuse = true + + if job.Container != nil { + containerSpec.Image = job.Container.Image + } else { + platformName := rc.ExprEval.Interpolate(rc.Run.Job().RunsOn) + containerSpec.Image = rc.Config.Platforms[strings.ToLower(platformName)] + } switch step.Type() { case model.StepTypeRun: if job.Container != nil { - containerSpec.Image = job.Container.Image containerSpec.Ports = job.Container.Ports containerSpec.Volumes = job.Container.Volumes containerSpec.Options = job.Container.Options - } else { - platformName := rc.ExprEval.Interpolate(rc.Run.Job().RunsOn) - containerSpec.Image = rc.Config.Platforms[strings.ToLower(platformName)] } return common.NewPipelineExecutor( rc.setupEnv(containerSpec, step), @@ -62,8 +85,10 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { case model.StepTypeUsesDockerURL: containerSpec.Image = strings.TrimPrefix(step.Uses, "docker://") + containerSpec.Name = rc.createStepContainerName(step.ID) containerSpec.Entrypoint = step.With["entrypoint"] containerSpec.Args = step.With["args"] + containerSpec.Reuse = rc.Config.ReuseContainers return common.NewPipelineExecutor( rc.setupEnv(containerSpec, step), rc.pullImage(containerSpec), @@ -71,7 +96,6 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { ) case model.StepTypeUsesActionLocal: - containerSpec.Image = fmt.Sprintf("%s:%s", containerSpec.Name, "latest") return common.NewPipelineExecutor( rc.setupEnv(containerSpec, step), rc.setupAction(containerSpec, filepath.Join(rc.Config.Workdir, step.Uses)), @@ -91,7 +115,6 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { if err != nil { return common.NewErrorExecutor(err) } - containerSpec.Image = fmt.Sprintf("%s:%s", remoteAction.Repo, remoteAction.Ref) return common.NewPipelineExecutor( common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{ URL: remoteAction.CloneURL(), @@ -194,18 +217,24 @@ func (rc *RunContext) setupAction(containerSpec *model.ContainerSpec, actionDir switch action.Runs.Using { case model.ActionRunsUsingNode12: - containerSpec.Image = "node:12-alpine" if strings.HasPrefix(actionDir, rc.Config.Workdir) { - containerSpec.Args = fmt.Sprintf("node /github/workspace/%s/%s", strings.TrimPrefix(actionDir, rc.Config.Workdir), action.Runs.Main) + containerSpec.Entrypoint = fmt.Sprintf("node /github/workspace/%s/%s", strings.TrimPrefix(actionDir, rc.Config.Workdir), action.Runs.Main) } else if strings.HasPrefix(actionDir, rc.Tempdir) { - containerSpec.Args = fmt.Sprintf("node /github/home/%s/%s", strings.TrimPrefix(actionDir, rc.Tempdir), action.Runs.Main) + containerSpec.Entrypoint = fmt.Sprintf("node /github/home/%s/%s", strings.TrimPrefix(actionDir, rc.Tempdir), action.Runs.Main) } case model.ActionRunsUsingDocker: + if strings.HasPrefix(actionDir, rc.Config.Workdir) { + containerSpec.Name = rc.createStepContainerName(strings.TrimPrefix(actionDir, rc.Config.Workdir)) + } else if strings.HasPrefix(actionDir, rc.Tempdir) { + containerSpec.Name = rc.createStepContainerName(strings.TrimPrefix(actionDir, rc.Tempdir)) + } + containerSpec.Reuse = rc.Config.ReuseContainers if strings.HasPrefix(action.Runs.Image, "docker://") { containerSpec.Image = strings.TrimPrefix(action.Runs.Image, "docker://") containerSpec.Entrypoint = strings.Join(action.Runs.Entrypoint, " ") containerSpec.Args = strings.Join(action.Runs.Args, " ") } else { + containerSpec.Image = fmt.Sprintf("%s:%s", containerSpec.Name, "latest") contextDir := filepath.Join(actionDir, action.Runs.Main) return container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ ContextDir: contextDir, diff --git a/pkg/runner/testdata/node/push.yml b/pkg/runner/testdata/node/push.yml new file mode 100644 index 0000000..1be8946 --- /dev/null +++ b/pkg/runner/testdata/node/push.yml @@ -0,0 +1,16 @@ +name: NodeJS Test + +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + + - name: Install Dependencies + run: npm install