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>
This commit is contained in:
parent
d67e282f68
commit
5752a03dcd
16 changed files with 96 additions and 23 deletions
13
.github/workflows/push.yml
vendored
13
.github/workflows/push.yml
vendored
|
@ -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
vendored
2
.github/workflows/test-expressions.yml
vendored
|
@ -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
vendored
2
.github/workflows/test-if.yml
vendored
|
@ -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:
|
||||
|
|
2
Makefile
2
Makefile
|
@ -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
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
Normal file
14
pkg/runner/res/trampoline.js
Normal file
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue