Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input #1524

Merged
merged 4 commits into from
Jan 13, 2023
Merged

Input #1524

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")
-g, --graph draw workflows
-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.
-j, --job string run job
-l, --list list workflows
Expand Down Expand Up @@ -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.

## Pass Inputs to Manually Triggered Workflows
# Pass Inputs to Manually Triggered Workflows

Example workflow file

Expand All @@ -434,6 +436,14 @@ jobs:
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`

```json
Expand Down
7 changes: 7 additions & 0 deletions cmd/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ type Input struct {
bindWorkdir bool
secrets []string
envs []string
inputs []string
platforms []string
dryrun bool
forcePull bool
forceRebuild bool
noOutput bool
envfile string
inputfile string
secretfile string
insecureSecrets bool
defaultBranch string
Expand Down Expand Up @@ -84,3 +86,8 @@ func (i *Input) WorkflowsPath() string {
func (i *Input) EventPath() string {
return i.resolve(i.eventPath)
}

// Inputfile returns the path to the input file
func (i *Input) Inputfile() string {
return i.resolve(i.inputfile)
}
34 changes: 24 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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().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.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().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")
Expand Down Expand Up @@ -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().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.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.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")
Expand Down Expand Up @@ -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 {
if _, err := os.Stat(path); err == nil {
env, err := godotenv.Read(path)
Expand Down Expand Up @@ -285,18 +302,14 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str

log.Debugf("Loading environment from %s", input.Envfile())
envs := make(map[string]string)
if input.envs != nil {
for _, envVar := range input.envs {
e := strings.SplitN(envVar, `=`, 2)
if len(e) == 2 {
envs[e[0]] = e[1]
} else {
envs[e[0]] = ""
}
}
}
_ = parseEnvs(input.envs, 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())
secrets := newSecrets(input.secrets)
_ = readEnvs(input.Secretfile(), secrets)
Expand Down Expand Up @@ -444,6 +457,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
JSONLogger: input.jsonLogger,
Env: envs,
Secrets: secrets,
Inputs: inputs,
Token: secrets["GITHUB_TOKEN"],
InsecureSecrets: input.insecureSecrets,
Platforms: input.newPlatforms(),
Expand Down
11 changes: 11 additions & 0 deletions pkg/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package runner

import (
"context"
"encoding/json"
"fmt"
"os"

Expand Down Expand Up @@ -31,6 +32,7 @@ type Config struct {
LogOutput bool // log the output from docker run
JSONLogger bool // use json or text logger
Env map[string]string // env for containers
Inputs map[string]string // manually passed action inputs
Secrets map[string]string // list of secrets
Token string // GitHub token
InsecureSecrets bool // switch hiding output when printing to terminal
Expand Down Expand Up @@ -81,6 +83,15 @@ func (runner *runnerImpl) configure() (Runner, error) {
return nil, err
}
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
}
Expand Down
22 changes: 22 additions & 0 deletions pkg/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
ReuseContainers: false,
Env: cfg.Env,
Secrets: cfg.Secrets,
Inputs: cfg.Inputs,
GitHubInstance: "github.com",
ContainerArchitecture: cfg.ContainerArchitecture,
}
Expand Down Expand Up @@ -419,6 +420,27 @@ func TestRunEventSecrets(t *testing.T) {
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) {
if testing.Short() {
t.Skip("skipping integration test")
Expand Down
21 changes: 21 additions & 0 deletions pkg/runner/testdata/input-from-cli/input.yml
Original file line number Diff line number Diff line change
@@ -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