mirror of
https://code.forgejo.org/forgejo/runner.git
synced 2025-08-06 17:40:58 +00:00
add support for 'reuse' mode to allow act to be used for a fast local task runner
This commit is contained in:
parent
f26a1a3f0c
commit
ff392444a4
7 changed files with 95 additions and 61 deletions
8
.github/actions/check/Dockerfile
vendored
8
.github/actions/check/Dockerfile
vendored
|
@ -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
.github/actions/check/entrypoint.sh
vendored
8
.github/actions/check/entrypoint.sh
vendored
|
@ -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
.github/main.workflow
vendored
14
.github/main.workflow
vendored
|
@ -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"]
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
@ -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 {
|
||||
|
@ -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...)
|
||||
|
@ -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, "-")
|
||||
|
||||
func randString(slen int) string {
|
||||
b := make([]byte, slen)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
|
||||
}
|
||||
return string(b)
|
||||
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 trimToLen(s string, l int) string {
|
||||
if len(s) > l {
|
||||
return s[:l]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
@ -41,29 +42,44 @@ func NewDockerRunExecutor(input NewDockerRunExecutorInput) common.Executor {
|
|||
return err
|
||||
}
|
||||
|
||||
containerID, err := createContainer(input, cli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer removeContainer(input, cli, containerID)
|
||||
|
||||
err = copyContentToContainer(input, cli, containerID)
|
||||
// check if container exists
|
||||
containerID, err := findContainer(input, cli, input.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = attachContainer(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 = startContainer(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
|
||||
}
|
||||
}
|
||||
|
||||
return waitContainer(input, cli, containerID)
|
||||
// be sure to cleanup container if we aren't reusing
|
||||
if !input.ReuseContainers {
|
||||
defer removeContainer(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()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue