Skip to content

Commit

Permalink
add support for 'reuse' mode to allow act to be used for a fast local…
Browse files Browse the repository at this point in the history
… task runner
  • Loading branch information
cplee committed Jan 17, 2019
1 parent 317a305 commit 8793c8a
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 70 deletions.
8 changes: 2 additions & 6 deletions .github/actions/check/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
FROM golang:1.11.4-stretch

RUN go get -u honnef.co/go/tools/cmd/staticcheck
RUN go get -u golang.org/x/lint/golint
RUN go get -u github.com/fzipp/gocyclo
FROM golangci/golangci-lint:v1.12.5

COPY "entrypoint.sh" "/entrypoint.sh"
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
8 changes: 1 addition & 7 deletions .github/actions/check/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
#!/bin/sh

#GOPATH=/go
#PATH=${GOPATH}/bin:/usr/local/go/bin:${PATH}

go vet ./...
golint -set_exit_status ./...
staticcheck ./...
gocyclo -over 10 .
golangci-lint run
go test -cover ./...
14 changes: 10 additions & 4 deletions .github/main.workflow
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ action "check" {
uses = "./.github/actions/check"
}

action "branch-filter" {
action "branch-filter" {
needs = ["check"]
uses = "actions/bin/filter@master"
args = "tag v*"
}
}

action "release" {
action "release" {
needs = ["branch-filter"]
uses = "docker://goreleaser/goreleaser:v0.97"
args = "release"
secrets = ["GITHUB_TOKEN"]
}
}

action "build" {
uses = "docker://goreleaser/goreleaser:v0.97"
args = "--snapshot --rm-dist"
secrets = ["SNAPSHOT_VERSION"]
}
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ linters:
- gosec
- unconvert
- dupl
- maligned
- nakedret
- prealloc
- scopelint
Expand Down
18 changes: 8 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ endif
IS_SNAPSHOT = $(if $(findstring -, $(VERSION)),true,false)
TAG_VERSION = v$(VERSION)

default: check

deps:
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $$(go env GOPATH)/bin v1.12.5
ACT ?= go run main.go

default: check

check:
golangci-lint run
go test -cover ./...
$(ACT) -ra check

build: deps check
@GO111MODULE=off go get github.com/goreleaser/goreleaser
build: check
$(eval export SNAPSHOT_VERSION=$(VERSION))
@goreleaser --snapshot --rm-dist
$(ACT) -ra build

release:
$(ACT) -ra release

install: build
@cp dist/$(shell go env GOOS)_$(shell go env GOARCH)/act /usr/local/bin/act
Expand All @@ -36,7 +35,6 @@ installer:
@GO111MODULE=off go get github.com/goreleaser/godownloader
godownloader -r nektos/act -o install.sh


promote:
@echo "VERSION:$(VERSION) IS_SNAPSHOT:$(IS_SNAPSHOT) LATEST_VERSION:$(LATEST_VERSION)"
ifeq (false,$(IS_SNAPSHOT))
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ act -a test
# Run in dry-run mode:
act -n
# Run in reuse mode to save state:
act -r
```

# Support
Expand Down
13 changes: 7 additions & 6 deletions actions/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ type ActionRunner interface {

// RunnerConfig contains the config for a new runner
type RunnerConfig struct {
Ctx context.Context // context to use for the run
Dryrun bool // don't start any of the containers
WorkingDir string // base directory to use
WorkflowPath string // path to load main.workflow file, relative to WorkingDir
EventName string // name of event to run
EventPath string // path to JSON file to use for event.json in containers, relative to WorkingDir
Ctx context.Context // context to use for the run
Dryrun bool // don't start any of the containers
WorkingDir string // base directory to use
WorkflowPath string // path to load main.workflow file, relative to WorkingDir
EventName string // name of event to run
EventPath string // path to JSON file to use for event.json in containers, relative to WorkingDir
ReuseContainers bool // reuse containers to maintain state
}

type environmentApplier interface {
Expand Down
29 changes: 15 additions & 14 deletions actions/runner_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"bytes"
"fmt"
"io"
"math/rand"
"os"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -78,11 +77,6 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
if err != nil {
return common.NewErrorExecutor(err)
}
randSuffix := randString(6)
containerName := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-")
if len(containerName)+len(randSuffix)+1 > 30 {
containerName = containerName[:(30 - (len(randSuffix) + 1))]
}

envList := make([]string, 0)
for k, v := range env {
Expand All @@ -95,13 +89,14 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
Image: image,
WorkingDir: "/github/workspace",
Env: envList,
Name: fmt.Sprintf("%s-%s", containerName, randSuffix),
Name: runner.createContainerName(actionName),
Binds: []string{
fmt.Sprintf("%s:%s", runner.config.WorkingDir, "/github/workspace"),
fmt.Sprintf("%s:%s", runner.tempDir, "/github/home"),
fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"),
},
Content: map[string]io.Reader{"/github": ghReader},
Content: map[string]io.Reader{"/github": ghReader},
ReuseContainers: runner.config.ReuseContainers,
}))

return common.NewPipelineExecutor(executors...)
Expand Down Expand Up @@ -174,12 +169,18 @@ func (runner *runnerImpl) createGithubTarball() (io.Reader, error) {

}

const letterBytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
func (runner *runnerImpl) createContainerName(actionName string) string {
containerName := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-")

prefix := fmt.Sprintf("%s-", trimToLen(filepath.Base(runner.config.WorkingDir), 10))
suffix := ""
containerName = trimToLen(containerName, 30-(len(prefix)+len(suffix)))
return fmt.Sprintf("%s%s%s", prefix, containerName, suffix)
}

func randString(slen int) string {
b := make([]byte, slen)
for i := range b {
b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
func trimToLen(s string, l int) string {
if len(s) > l {
return s[:l]
}
return string(b)
return s
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func Execute(ctx context.Context, version string) {
}
rootCmd.Flags().BoolP("list", "l", false, "list actions")
rootCmd.Flags().StringP("action", "a", "", "run action")
rootCmd.Flags().BoolVarP(&runnerConfig.ReuseContainers, "reuse", "r", false, "reuse action containers to maintain state")
rootCmd.Flags().StringVarP(&runnerConfig.EventPath, "event", "e", "", "path to event JSON file")
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output")
rootCmd.PersistentFlags().BoolVarP(&runnerConfig.Dryrun, "dryrun", "n", false, "dryrun mode")
Expand Down
79 changes: 57 additions & 22 deletions container/docker_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ import (
// NewDockerRunExecutorInput the input for the NewDockerRunExecutor function
type NewDockerRunExecutorInput struct {
DockerExecutorInput
Image string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Content map[string]io.Reader
Volumes []string
Name string
Image string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Content map[string]io.Reader
Volumes []string
Name string
ReuseContainers bool
}

// NewDockerRunExecutor function to create a run executor for the container
Expand All @@ -41,29 +42,44 @@ func NewDockerRunExecutor(input NewDockerRunExecutorInput) common.Executor {
return err
}

containerID, err := createContainer(input, cli)
// check if container exists
containerID, err := findContainer(input, cli, input.Name)
if err != nil {
return err
}
defer removeContainer(input, cli, containerID)

err = copyContentToContainer(input, cli, containerID)
if err != nil {
return err
// if we have an old container and we aren't reusing, remove it!
if !input.ReuseContainers && containerID != "" {
input.Logger.Debugf("Found existing container for %s...removing", input.Name)
removeContainer(input, cli, containerID)
containerID = ""
}

err = attachContainer(input, cli, containerID)
if err != nil {
return err
// create a new container if we don't have one to reuse
if containerID == "" {
containerID, err = createContainer(input, cli)
if err != nil {
return err
}
}

err = startContainer(input, cli, containerID)
if err != nil {
return err
// be sure to cleanup container if we aren't reusing
if !input.ReuseContainers {
defer removeContainer(input, cli, containerID)
}

return waitContainer(input, cli, containerID)

executor := common.NewPipelineExecutor(
func() error {
return copyContentToContainer(input, cli, containerID)
}, func() error {
return attachContainer(input, cli, containerID)
}, func() error {
return startContainer(input, cli, containerID)
}, func() error {
return waitContainer(input, cli, containerID)
},
)
return executor()
}

}
Expand Down Expand Up @@ -99,6 +115,25 @@ func createContainer(input NewDockerRunExecutorInput, cli *client.Client) (strin
return resp.ID, nil
}

func findContainer(input NewDockerRunExecutorInput, cli *client.Client, containerName string) (string, error) {
containers, err := cli.ContainerList(input.Ctx, types.ContainerListOptions{
All: true,
})
if err != nil {
return "", err
}

for _, container := range containers {
for _, name := range container.Names {
if name[1:] == containerName {
return container.ID, nil
}
}
}

return "", nil
}

func removeContainer(input NewDockerRunExecutorInput, cli *client.Client, containerID string) {
err := cli.ContainerRemove(context.Background(), containerID, types.ContainerRemoveOptions{
RemoveVolumes: true,
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtf
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb h1:PyjxRdW1mqCmSoxy/6uP01P7CGbsD+woX+oOWbaUPwQ=
github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
Expand Down

0 comments on commit 8793c8a

Please sign in to comment.