Input (#1524)
* added input flags * added input as part of the action event and added test cases * updated readme Co-authored-by: ChristopherHX <christopher.homberger@web.de>
This commit is contained in:
parent
b2fb9e64ac
commit
767e6a8696
6 changed files with 96 additions and 11 deletions
12
README.md
12
README.md
|
@ -186,6 +186,8 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co
|
||||||
--github-instance string GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server. (default "github.com")
|
--github-instance string GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server. (default "github.com")
|
||||||
-g, --graph draw workflows
|
-g, --graph draw workflows
|
||||||
-h, --help help for act
|
-h, --help help for act
|
||||||
|
--input stringArray action input to make available to actions (e.g. --input myinput=foo)
|
||||||
|
--input-file string input file to read and use as action input (default ".input")
|
||||||
--insecure-secrets NOT RECOMMENDED! Doesn't hide secrets while printing logs.
|
--insecure-secrets NOT RECOMMENDED! Doesn't hide secrets while printing logs.
|
||||||
-j, --job string run job
|
-j, --job string run job
|
||||||
-l, --list list workflows
|
-l, --list list workflows
|
||||||
|
@ -408,7 +410,7 @@ act pull_request -e pull-request.json
|
||||||
|
|
||||||
Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected.
|
Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected.
|
||||||
|
|
||||||
## Pass Inputs to Manually Triggered Workflows
|
# Pass Inputs to Manually Triggered Workflows
|
||||||
|
|
||||||
Example workflow file
|
Example workflow file
|
||||||
|
|
||||||
|
@ -434,6 +436,14 @@ jobs:
|
||||||
echo "Hello ${{ github.event.inputs.NAME }} and ${{ github.event.inputs.SOME_VALUE }}!"
|
echo "Hello ${{ github.event.inputs.NAME }} and ${{ github.event.inputs.SOME_VALUE }}!"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## via input or input-file flag
|
||||||
|
|
||||||
|
- `act --input NAME=somevalue` - use `somevalue` as the value for `NAME` input.
|
||||||
|
- `act --input-file my.input` - load input values from `my.input` file.
|
||||||
|
- input file format is the same as `.env` format
|
||||||
|
|
||||||
|
## via JSON
|
||||||
|
|
||||||
Example JSON payload file conveniently named `payload.json`
|
Example JSON payload file conveniently named `payload.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -17,12 +17,14 @@ type Input struct {
|
||||||
bindWorkdir bool
|
bindWorkdir bool
|
||||||
secrets []string
|
secrets []string
|
||||||
envs []string
|
envs []string
|
||||||
|
inputs []string
|
||||||
platforms []string
|
platforms []string
|
||||||
dryrun bool
|
dryrun bool
|
||||||
forcePull bool
|
forcePull bool
|
||||||
forceRebuild bool
|
forceRebuild bool
|
||||||
noOutput bool
|
noOutput bool
|
||||||
envfile string
|
envfile string
|
||||||
|
inputfile string
|
||||||
secretfile string
|
secretfile string
|
||||||
insecureSecrets bool
|
insecureSecrets bool
|
||||||
defaultBranch string
|
defaultBranch string
|
||||||
|
@ -84,3 +86,8 @@ func (i *Input) WorkflowsPath() string {
|
||||||
func (i *Input) EventPath() string {
|
func (i *Input) EventPath() string {
|
||||||
return i.resolve(i.eventPath)
|
return i.resolve(i.eventPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inputfile returns the path to the input file
|
||||||
|
func (i *Input) Inputfile() string {
|
||||||
|
return i.resolve(i.inputfile)
|
||||||
|
}
|
||||||
|
|
34
cmd/root.go
34
cmd/root.go
|
@ -47,6 +47,7 @@ func Execute(ctx context.Context, version string) {
|
||||||
rootCmd.Flags().StringVar(&input.remoteName, "remote-name", "origin", "git remote name that will be used to retrieve url of git repo")
|
rootCmd.Flags().StringVar(&input.remoteName, "remote-name", "origin", "git remote name that will be used to retrieve url of git repo")
|
||||||
rootCmd.Flags().StringArrayVarP(&input.secrets, "secret", "s", []string{}, "secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)")
|
rootCmd.Flags().StringArrayVarP(&input.secrets, "secret", "s", []string{}, "secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)")
|
||||||
rootCmd.Flags().StringArrayVarP(&input.envs, "env", "", []string{}, "env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv)")
|
rootCmd.Flags().StringArrayVarP(&input.envs, "env", "", []string{}, "env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv)")
|
||||||
|
rootCmd.Flags().StringArrayVarP(&input.inputs, "input", "", []string{}, "action input to make available to actions (e.g. --input myinput=foo)")
|
||||||
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().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, "don't remove container(s) on successfully completed workflow(s) to maintain state between runs")
|
rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "don't remove container(s) on successfully completed workflow(s) to maintain state between runs")
|
||||||
rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy")
|
rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy")
|
||||||
|
@ -74,6 +75,7 @@ func Execute(ctx context.Context, version string) {
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)")
|
rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&input.inputfile, "input-file", "", ".input", "input file to read and use as action input")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.")
|
rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.containerDaemonSocket, "container-daemon-socket", "", "/var/run/docker.sock", "Path to Docker daemon socket which will be mounted to containers")
|
rootCmd.PersistentFlags().StringVarP(&input.containerDaemonSocket, "container-daemon-socket", "", "/var/run/docker.sock", "Path to Docker daemon socket which will be mounted to containers")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.containerOptions, "container-options", "", "", "Custom docker container options for the job container without an options property in the job definition")
|
rootCmd.PersistentFlags().StringVarP(&input.containerOptions, "container-options", "", "", "Custom docker container options for the job container without an options property in the job definition")
|
||||||
|
@ -249,6 +251,21 @@ func setupLogging(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseEnvs(env []string, envs map[string]string) bool {
|
||||||
|
if env != nil {
|
||||||
|
for _, envVar := range env {
|
||||||
|
e := strings.SplitN(envVar, `=`, 2)
|
||||||
|
if len(e) == 2 {
|
||||||
|
envs[e[0]] = e[1]
|
||||||
|
} else {
|
||||||
|
envs[e[0]] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func readEnvs(path string, envs map[string]string) bool {
|
func readEnvs(path string, envs map[string]string) bool {
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
env, err := godotenv.Read(path)
|
env, err := godotenv.Read(path)
|
||||||
|
@ -285,18 +302,14 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
|
|
||||||
log.Debugf("Loading environment from %s", input.Envfile())
|
log.Debugf("Loading environment from %s", input.Envfile())
|
||||||
envs := make(map[string]string)
|
envs := make(map[string]string)
|
||||||
if input.envs != nil {
|
_ = parseEnvs(input.envs, envs)
|
||||||
for _, envVar := range input.envs {
|
|
||||||
e := strings.SplitN(envVar, `=`, 2)
|
|
||||||
if len(e) == 2 {
|
|
||||||
envs[e[0]] = e[1]
|
|
||||||
} else {
|
|
||||||
envs[e[0]] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = readEnvs(input.Envfile(), envs)
|
_ = readEnvs(input.Envfile(), envs)
|
||||||
|
|
||||||
|
log.Debugf("Loading action inputs from %s", input.Inputfile())
|
||||||
|
inputs := make(map[string]string)
|
||||||
|
_ = parseEnvs(input.inputs, inputs)
|
||||||
|
_ = readEnvs(input.Inputfile(), inputs)
|
||||||
|
|
||||||
log.Debugf("Loading secrets from %s", input.Secretfile())
|
log.Debugf("Loading secrets from %s", input.Secretfile())
|
||||||
secrets := newSecrets(input.secrets)
|
secrets := newSecrets(input.secrets)
|
||||||
_ = readEnvs(input.Secretfile(), secrets)
|
_ = readEnvs(input.Secretfile(), secrets)
|
||||||
|
@ -444,6 +457,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
JSONLogger: input.jsonLogger,
|
JSONLogger: input.jsonLogger,
|
||||||
Env: envs,
|
Env: envs,
|
||||||
Secrets: secrets,
|
Secrets: secrets,
|
||||||
|
Inputs: inputs,
|
||||||
Token: secrets["GITHUB_TOKEN"],
|
Token: secrets["GITHUB_TOKEN"],
|
||||||
InsecureSecrets: input.insecureSecrets,
|
InsecureSecrets: input.insecureSecrets,
|
||||||
Platforms: input.newPlatforms(),
|
Platforms: input.newPlatforms(),
|
||||||
|
|
|
@ -2,6 +2,7 @@ package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ type Config struct {
|
||||||
LogOutput bool // log the output from docker run
|
LogOutput bool // log the output from docker run
|
||||||
JSONLogger bool // use json or text logger
|
JSONLogger bool // use json or text logger
|
||||||
Env map[string]string // env for containers
|
Env map[string]string // env for containers
|
||||||
|
Inputs map[string]string // manually passed action inputs
|
||||||
Secrets map[string]string // list of secrets
|
Secrets map[string]string // list of secrets
|
||||||
Token string // GitHub token
|
Token string // GitHub token
|
||||||
InsecureSecrets bool // switch hiding output when printing to terminal
|
InsecureSecrets bool // switch hiding output when printing to terminal
|
||||||
|
@ -81,6 +83,15 @@ func (runner *runnerImpl) configure() (Runner, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
runner.eventJSON = string(eventJSONBytes)
|
runner.eventJSON = string(eventJSONBytes)
|
||||||
|
} else if len(runner.config.Inputs) != 0 {
|
||||||
|
eventMap := map[string]map[string]string{
|
||||||
|
"inputs": runner.config.Inputs,
|
||||||
|
}
|
||||||
|
eventJSON, err := json.Marshal(eventMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
runner.eventJSON = string(eventJSON)
|
||||||
}
|
}
|
||||||
return runner, nil
|
return runner, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
|
||||||
ReuseContainers: false,
|
ReuseContainers: false,
|
||||||
Env: cfg.Env,
|
Env: cfg.Env,
|
||||||
Secrets: cfg.Secrets,
|
Secrets: cfg.Secrets,
|
||||||
|
Inputs: cfg.Inputs,
|
||||||
GitHubInstance: "github.com",
|
GitHubInstance: "github.com",
|
||||||
ContainerArchitecture: cfg.ContainerArchitecture,
|
ContainerArchitecture: cfg.ContainerArchitecture,
|
||||||
}
|
}
|
||||||
|
@ -419,6 +420,27 @@ func TestRunEventSecrets(t *testing.T) {
|
||||||
tjfi.runTest(context.Background(), t, &Config{Secrets: secrets, Env: env})
|
tjfi.runTest(context.Background(), t, &Config{Secrets: secrets, Env: env})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunActionInputs(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
workflowPath := "input-from-cli"
|
||||||
|
|
||||||
|
tjfi := TestJobFileInfo{
|
||||||
|
workdir: workdir,
|
||||||
|
workflowPath: workflowPath,
|
||||||
|
eventName: "workflow_dispatch",
|
||||||
|
errorMessage: "",
|
||||||
|
platforms: platforms,
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := map[string]string{
|
||||||
|
"SOME_INPUT": "input",
|
||||||
|
}
|
||||||
|
|
||||||
|
tjfi.runTest(context.Background(), t, &Config{Inputs: inputs})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunEventPullRequest(t *testing.T) {
|
func TestRunEventPullRequest(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
|
|
21
pkg/runner/testdata/input-from-cli/input.yml
vendored
Normal file
21
pkg/runner/testdata/input-from-cli/input.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
NAME:
|
||||||
|
description: "A random input name for the workflow"
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
SOME_VALUE:
|
||||||
|
description: "Some other input to pass"
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Test with inputs
|
||||||
|
run: |
|
||||||
|
[ -z "${{ github.event.inputs.SOME_INPUT }}" ] && exit 1 || exit 0
|
Loading…
Reference in a new issue