Support running commands in repositories without action.yaml (#293)

* Comment for public function

* Add git describe fallback

* spelling: github

* Set initial branch to satisfy tests for modern git

* Clarify -even- if

* Go 1.16

* Support running commands in repositories without action.yaml

Support runnings commands with only a Docker file

Co-authored-by: Casey Lee <cplee@nektos.com>
Этот коммит содержится в:
Josh Soref
2021-03-30 15:26:25 -04:00
коммит произвёл GitHub
родитель d67e282f68
Коммит 5752a03dcd
16 изменённых файлов: 96 добавлений и 23 удалений

13
.github/workflows/push.yml поставляемый
Просмотреть файл

@@ -7,6 +7,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v1
with:
go-version: 1.16
- uses: golangci/golangci-lint-action@v2
env:
CGO_ENABLED: 0
@@ -24,7 +27,7 @@ jobs:
uses: docker/setup-qemu-action@v1
- uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.16
- run: go test -cover -coverprofile=coverage.txt -covermode=atomic ./...
env:
CGO_ENABLED: 0
@@ -42,7 +45,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.16
- name: Install MacOS Docker
uses: docker-practice/actions-setup-docker@master
- run: go test -v -timeout 1h -cover ./...
@@ -58,6 +61,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v1
with:
go-version: 1.16
- name: GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
@@ -90,6 +96,9 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-go@v1
with:
go-version: 1.16
- name: GoReleaser
uses: goreleaser/goreleaser-action@v1
with:

2
.github/workflows/test-expressions.yml поставляемый
Просмотреть файл

@@ -1,5 +1,5 @@
name: "Test how expressions are handled on Github"
name: "Test how expressions are handled on GitHub"
on: push
env:

2
.github/workflows/test-if.yml поставляемый
Просмотреть файл

@@ -1,5 +1,5 @@
name: "Test what expressions result in true and false on Github"
name: "Test what expressions result in true and false on GitHub"
on: push
env:

Просмотреть файл

@@ -1,5 +1,5 @@
PREFIX ?= /usr/local
VERSION ?= $(shell git describe --tags --dirty | cut -c 2-)
VERSION ?= $(shell git describe --tags --dirty --always | cut -c 2-)
IS_SNAPSHOT = $(if $(findstring -, $(VERSION)),true,false)
MAJOR_VERSION = $(word 1, $(subst ., ,$(VERSION)))
MINOR_VERSION = $(word 2, $(subst ., ,$(VERSION)))

Просмотреть файл

@@ -102,7 +102,7 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co
-l, --list list workflows
-P, --platform stringArray custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)
--privileged use privileged mode
-p, --pull pull docker image(s) if already present
-p, --pull pull docker image(s) even if already present
-q, --quiet disable logging of output from steps
-r, --reuse reuse action containers to maintain state
-s, --secret stringArray secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)

Просмотреть файл

@@ -27,7 +27,7 @@ func Execute(ctx context.Context, version string) {
input := new(Input)
var rootCmd = &cobra.Command{
Use: "act [event name to run]\nIf no event name passed, will default to \"on: push\"",
Short: "Run Github actions locally by specifying the event name (e.g. `push`) or an action name directly.",
Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.",
Args: cobra.MaximumNArgs(1),
RunE: newRunCommand(ctx, input),
PersistentPreRun: setupLogging,
@@ -43,7 +43,7 @@ func Execute(ctx context.Context, version string) {
rootCmd.Flags().StringArrayVarP(&input.platforms, "platform", "P", []string{}, "custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)")
rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "reuse action containers to maintain state")
rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy")
rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) if already present")
rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) even if already present")
rootCmd.Flags().BoolVarP(&input.autodetectEvent, "detect-event", "", false, "Use first event type from workflow as event that triggered the workflow")
rootCmd.Flags().StringVarP(&input.eventPath, "eventpath", "e", "", "path to event JSON file")
rootCmd.Flags().StringVar(&input.defaultBranch, "defaultbranch", "", "the name of the main branch")

2
go.mod
Просмотреть файл

@@ -1,6 +1,6 @@
module github.com/nektos/act
go 1.14
go 1.16
require (
github.com/AlecAivazis/survey/v2 v2.2.7

Просмотреть файл

@@ -189,6 +189,7 @@ type NewGitCloneExecutorInput struct {
Dir string
}
// CloneIfRequired ...
func CloneIfRequired(refName plumbing.ReferenceName, input NewGitCloneExecutorInput, logger log.FieldLogger) (*git.Repository, error) {
r, err := git.PlainOpen(input.Dir)
if err != nil {

Просмотреть файл

@@ -161,7 +161,7 @@ func TestGitFindRef(t *testing.T) {
t.Run(name, func(t *testing.T) {
dir := filepath.Join(basedir, name)
require.NoError(t, os.MkdirAll(dir, 0755))
require.NoError(t, gitCmd("-C", dir, "init"))
require.NoError(t, gitCmd("-C", dir, "init", "--initial-branch=master"))
require.NoError(t, cleanGitHooks(dir))
tt.Prepare(t, dir)
ref, err := FindGitRef(dir)

Просмотреть файл

@@ -38,6 +38,16 @@ const (
ActionRunsUsingDocker = "docker"
)
// ActionRuns are a field in Action
type ActionRuns struct {
Using ActionRunsUsing `yaml:"using"`
Env map[string]string `yaml:"env"`
Main string `yaml:"main"`
Image string `yaml:"image"`
Entrypoint []string `yaml:"entrypoint"`
Args []string `yaml:"args"`
}
// Action describes a metadata file for GitHub actions. The metadata filename must be either action.yml or action.yaml. The data in the metadata file defines the inputs, outputs and main entrypoint for your action.
type Action struct {
Name string `yaml:"name"`
@@ -45,15 +55,8 @@ type Action struct {
Description string `yaml:"description"`
Inputs map[string]Input `yaml:"inputs"`
Outputs map[string]Output `yaml:"outputs"`
Runs struct {
Using ActionRunsUsing `yaml:"using"`
Env map[string]string `yaml:"env"`
Main string `yaml:"main"`
Image string `yaml:"image"`
Entrypoint []string `yaml:"entrypoint"`
Args []string `yaml:"args"`
} `yaml:"runs"`
Branding struct {
Runs ActionRuns `yaml:"runs"`
Branding struct {
Color string `yaml:"color"`
Icon string `yaml:"icon"`
} `yaml:"branding"`

Просмотреть файл

@@ -211,7 +211,7 @@ func updateTestExpressionWorkflow(t *testing.T, tables []struct {
}
workflow := fmt.Sprintf(`
name: "Test how expressions are handled on Github"
name: "Test how expressions are handled on GitHub"
on: push
env:

14
pkg/runner/res/trampoline.js Обычный файл
Просмотреть файл

@@ -0,0 +1,14 @@
const { spawnSync } = require('child_process')
const spawnArguments={
cwd: process.env['INPUT_CWD'],
stdio: [
process.stdin,
process.stdout,
process.stderr,
]
}
const child=spawnSync(
'/bin/sh',
[ '-c' ].concat(process.env['INPUT_COMMAND']),
spawnArguments)
process.exit(child.status)

Просмотреть файл

@@ -316,7 +316,7 @@ func (rc *RunContext) EvalBool(expr string) (bool, error) {
interpolatedPart, isString := rc.ExprEval.InterpolateWithStringCheck(part)
// This peculiar transformation has to be done because the Github parser
// This peculiar transformation has to be done because the GitHub parser
// treats false returned from contexts as a string, not a boolean.
// Hence env.SOMETHING will be evaluated to true in an if: expression
// regardless if SOMETHING is set to false, true or any other string.

Просмотреть файл

@@ -169,7 +169,7 @@ func updateTestIfWorkflow(t *testing.T, tables []struct {
}
workflow := fmt.Sprintf(`
name: "Test what expressions result in true and false on Github"
name: "Test what expressions result in true and false on GitHub"
on: push
env:

Просмотреть файл

@@ -24,7 +24,7 @@ type Config struct {
EventPath string // path to JSON file to use for event.json in containers
DefaultBranch string // name of the main branch for this repository
ReuseContainers bool // reuse containers to maintain state
ForcePull bool // force pulling of the image, if already present
ForcePull bool // force pulling of the image, even if already present
LogOutput bool // log the output from docker run
Env map[string]string // env for containers
Secrets map[string]string // list of secrets

Просмотреть файл

@@ -2,7 +2,10 @@ package runner
import (
"context"
// Go told me to?
_ "embed"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -296,12 +299,55 @@ func (sc *StepContext) runUsesContainer() common.Executor {
}
}
//go:embed res/trampoline.js
var trampoline []byte
func (sc *StepContext) setupAction(actionDir string, actionPath string) common.Executor {
return func(ctx context.Context) error {
f, err := os.Open(filepath.Join(actionDir, actionPath, "action.yml"))
if os.IsNotExist(err) {
f, err = os.Open(filepath.Join(actionDir, actionPath, "action.yaml"))
if err != nil {
if _, err2 := os.Stat(filepath.Join(actionDir, actionPath, "Dockerfile")); err2 == nil {
sc.Action = &model.Action{
Name: "(Synthetic)",
Runs: model.ActionRuns{
Using: "docker",
Image: "Dockerfile",
},
}
log.Debugf("Using synthetic action %v for Dockerfile", sc.Action)
return nil
}
if sc.Step.With != nil {
if val, ok := sc.Step.With["args"]; ok {
err2 := ioutil.WriteFile(filepath.Join(actionDir, actionPath, "trampoline.js"), trampoline, 0400)
if err2 != nil {
return err
}
sc.Action = &model.Action{
Name: "(Synthetic)",
Inputs: map[string]model.Input{
"cwd": {
Description: "(Actual working directory)",
Required: false,
Default: filepath.Join(actionDir, actionPath),
},
"command": {
Description: "(Actual program)",
Required: false,
Default: val,
},
},
Runs: model.ActionRuns{
Using: "node12",
Main: "trampoline.js",
},
}
log.Debugf("Using synthetic action %v", sc.Action)
return nil
}
}
return err
}
} else if err != nil {