feat: specify matrix on command line (#1675)
* added matrix option * select the correct subset of matrix configuration after producing all the matrix configuration * add tests * update readme * lint fix * remove matrix from readme --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
09de42f067
commit
636c8a34ae
5 changed files with 144 additions and 35 deletions
|
@ -47,6 +47,7 @@ type Input struct {
|
||||||
remoteName string
|
remoteName string
|
||||||
replaceGheActionWithGithubCom []string
|
replaceGheActionWithGithubCom []string
|
||||||
replaceGheActionTokenWithGithubCom string
|
replaceGheActionTokenWithGithubCom string
|
||||||
|
matrix []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) resolve(path string) string {
|
func (i *Input) resolve(path string) string {
|
||||||
|
|
23
cmd/root.go
23
cmd/root.go
|
@ -66,6 +66,7 @@ func Execute(ctx context.Context, version string) {
|
||||||
rootCmd.Flags().BoolVar(&input.autoRemove, "rm", false, "automatically remove container(s)/volume(s) after a workflow(s) failure")
|
rootCmd.Flags().BoolVar(&input.autoRemove, "rm", false, "automatically remove container(s)/volume(s) after a workflow(s) failure")
|
||||||
rootCmd.Flags().StringArrayVarP(&input.replaceGheActionWithGithubCom, "replace-ghe-action-with-github-com", "", []string{}, "If you are using GitHub Enterprise Server and allow specified actions from GitHub (github.com), you can set actions on this. (e.g. --replace-ghe-action-with-github-com =github/super-linter)")
|
rootCmd.Flags().StringArrayVarP(&input.replaceGheActionWithGithubCom, "replace-ghe-action-with-github-com", "", []string{}, "If you are using GitHub Enterprise Server and allow specified actions from GitHub (github.com), you can set actions on this. (e.g. --replace-ghe-action-with-github-com =github/super-linter)")
|
||||||
rootCmd.Flags().StringVar(&input.replaceGheActionTokenWithGithubCom, "replace-ghe-action-token-with-github-com", "", "If you are using replace-ghe-action-with-github-com and you want to use private actions on GitHub, you have to set personal access token")
|
rootCmd.Flags().StringVar(&input.replaceGheActionTokenWithGithubCom, "replace-ghe-action-token-with-github-com", "", "If you are using replace-ghe-action-with-github-com and you want to use private actions on GitHub, you have to set personal access token")
|
||||||
|
rootCmd.Flags().StringArrayVarP(&input.matrix, "matrix", "", []string{}, "specify which matrix configuration to include (e.g. --matrix java:13")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.actor, "actor", "a", "nektos/act", "user that triggered the event")
|
rootCmd.PersistentFlags().StringVarP(&input.actor, "actor", "a", "nektos/act", "user that triggered the event")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.workflowsPath, "workflows", "W", "./.github/workflows/", "path to workflow file(s)")
|
rootCmd.PersistentFlags().StringVarP(&input.workflowsPath, "workflows", "W", "./.github/workflows/", "path to workflow file(s)")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&input.noWorkflowRecurse, "no-recurse", "", false, "Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag")
|
rootCmd.PersistentFlags().BoolVarP(&input.noWorkflowRecurse, "no-recurse", "", false, "Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag")
|
||||||
|
@ -295,6 +296,24 @@ func readEnvs(path string, envs map[string]string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseMatrix(matrix []string) map[string]map[string]bool {
|
||||||
|
// each matrix entry should be of the form - string:string
|
||||||
|
r := regexp.MustCompile(":")
|
||||||
|
matrixes := make(map[string]map[string]bool)
|
||||||
|
for _, m := range matrix {
|
||||||
|
matrix := r.Split(m, 2)
|
||||||
|
if len(matrix) < 2 {
|
||||||
|
log.Fatalf("Invalid matrix format. Failed to parse %s", m)
|
||||||
|
} else {
|
||||||
|
if _, ok := matrixes[matrix[0]]; !ok {
|
||||||
|
matrixes[matrix[0]] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
matrixes[matrix[0]][matrix[1]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrixes
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
|
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
|
@ -329,6 +348,9 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
secrets := newSecrets(input.secrets)
|
secrets := newSecrets(input.secrets)
|
||||||
_ = readEnvs(input.Secretfile(), secrets)
|
_ = readEnvs(input.Secretfile(), secrets)
|
||||||
|
|
||||||
|
matrixes := parseMatrix(input.matrix)
|
||||||
|
log.Debugf("Evaluated matrix inclusions: %v", matrixes)
|
||||||
|
|
||||||
planner, err := model.NewWorkflowPlanner(input.WorkflowsPath(), input.noWorkflowRecurse)
|
planner, err := model.NewWorkflowPlanner(input.WorkflowsPath(), input.noWorkflowRecurse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -508,6 +530,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
RemoteName: input.remoteName,
|
RemoteName: input.remoteName,
|
||||||
ReplaceGheActionWithGithubCom: input.replaceGheActionWithGithubCom,
|
ReplaceGheActionWithGithubCom: input.replaceGheActionWithGithubCom,
|
||||||
ReplaceGheActionTokenWithGithubCom: input.replaceGheActionTokenWithGithubCom,
|
ReplaceGheActionTokenWithGithubCom: input.replaceGheActionTokenWithGithubCom,
|
||||||
|
Matrix: matrixes,
|
||||||
}
|
}
|
||||||
r, err := runner.New(config)
|
r, err := runner.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -20,40 +20,41 @@ type Runner interface {
|
||||||
|
|
||||||
// Config contains the config for a new runner
|
// Config contains the config for a new runner
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Actor string // the user that triggered the event
|
Actor string // the user that triggered the event
|
||||||
Workdir string // path to working directory
|
Workdir string // path to working directory
|
||||||
BindWorkdir bool // bind the workdir to the job container
|
BindWorkdir bool // bind the workdir to the job container
|
||||||
EventName string // name of event to run
|
EventName string // name of event to run
|
||||||
EventPath string // path to JSON file to use for event.json in containers
|
EventPath string // path to JSON file to use for event.json in containers
|
||||||
DefaultBranch string // name of the main branch for this repository
|
DefaultBranch string // name of the main branch for this repository
|
||||||
ReuseContainers bool // reuse containers to maintain state
|
ReuseContainers bool // reuse containers to maintain state
|
||||||
ForcePull bool // force pulling of the image, even if already present
|
ForcePull bool // force pulling of the image, even if already present
|
||||||
ForceRebuild bool // force rebuilding local docker image action
|
ForceRebuild bool // force rebuilding local docker image action
|
||||||
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
|
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
|
||||||
Platforms map[string]string // list of platforms
|
Platforms map[string]string // list of platforms
|
||||||
Privileged bool // use privileged mode
|
Privileged bool // use privileged mode
|
||||||
UsernsMode string // user namespace to use
|
UsernsMode string // user namespace to use
|
||||||
ContainerArchitecture string // Desired OS/architecture platform for running containers
|
ContainerArchitecture string // Desired OS/architecture platform for running containers
|
||||||
ContainerDaemonSocket string // Path to Docker daemon socket
|
ContainerDaemonSocket string // Path to Docker daemon socket
|
||||||
ContainerOptions string // Options for the job container
|
ContainerOptions string // Options for the job container
|
||||||
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
|
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
|
||||||
GitHubInstance string // GitHub instance to use, default "github.com"
|
GitHubInstance string // GitHub instance to use, default "github.com"
|
||||||
ContainerCapAdd []string // list of kernel capabilities to add to the containers
|
ContainerCapAdd []string // list of kernel capabilities to add to the containers
|
||||||
ContainerCapDrop []string // list of kernel capabilities to remove from the containers
|
ContainerCapDrop []string // list of kernel capabilities to remove from the containers
|
||||||
AutoRemove bool // controls if the container is automatically removed upon workflow completion
|
AutoRemove bool // controls if the container is automatically removed upon workflow completion
|
||||||
ArtifactServerPath string // the path where the artifact server stores uploads
|
ArtifactServerPath string // the path where the artifact server stores uploads
|
||||||
ArtifactServerAddr string // the address the artifact server binds to
|
ArtifactServerAddr string // the address the artifact server binds to
|
||||||
ArtifactServerPort string // the port the artifact server binds to
|
ArtifactServerPort string // the port the artifact server binds to
|
||||||
NoSkipCheckout bool // do not skip actions/checkout
|
NoSkipCheckout bool // do not skip actions/checkout
|
||||||
RemoteName string // remote name in local git repo config
|
RemoteName string // remote name in local git repo config
|
||||||
ReplaceGheActionWithGithubCom []string // Use actions from GitHub Enterprise instance to GitHub
|
ReplaceGheActionWithGithubCom []string // Use actions from GitHub Enterprise instance to GitHub
|
||||||
ReplaceGheActionTokenWithGithubCom string // Token of private action repo on GitHub.
|
ReplaceGheActionTokenWithGithubCom string // Token of private action repo on GitHub.
|
||||||
|
Matrix map[string]map[string]bool // Matrix config to run
|
||||||
}
|
}
|
||||||
|
|
||||||
type caller struct {
|
type caller struct {
|
||||||
|
@ -116,7 +117,10 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||||
log.Errorf("Error while evaluating matrix: %v", err)
|
log.Errorf("Error while evaluating matrix: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
matrixes := job.GetMatrixes()
|
|
||||||
|
matrixes := selectMatrixes(job.GetMatrixes(), runner.config.Matrix)
|
||||||
|
log.Debugf("Final matrix after applying user inclusions '%v'", matrixes)
|
||||||
|
|
||||||
maxParallel := 4
|
maxParallel := 4
|
||||||
if job.Strategy != nil {
|
if job.Strategy != nil {
|
||||||
maxParallel = job.Strategy.MaxParallel
|
maxParallel = job.Strategy.MaxParallel
|
||||||
|
@ -171,6 +175,25 @@ func handleFailure(plan *model.Plan) common.Executor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func selectMatrixes(originalMatrixes []map[string]interface{}, targetMatrixValues map[string]map[string]bool) []map[string]interface{} {
|
||||||
|
matrixes := make([]map[string]interface{}, 0)
|
||||||
|
for _, original := range originalMatrixes {
|
||||||
|
flag := true
|
||||||
|
for key, val := range original {
|
||||||
|
if allowedVals, ok := targetMatrixValues[key]; ok {
|
||||||
|
valToString := fmt.Sprintf("%v", val)
|
||||||
|
if _, ok := allowedVals[valToString]; !ok {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flag {
|
||||||
|
matrixes = append(matrixes, original)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrixes
|
||||||
|
}
|
||||||
|
|
||||||
func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, matrix map[string]interface{}) *RunContext {
|
func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, matrix map[string]interface{}) *RunContext {
|
||||||
rc := &RunContext{
|
rc := &RunContext{
|
||||||
Config: runner.config,
|
Config: runner.config,
|
||||||
|
|
|
@ -186,6 +186,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
|
||||||
Inputs: cfg.Inputs,
|
Inputs: cfg.Inputs,
|
||||||
GitHubInstance: "github.com",
|
GitHubInstance: "github.com",
|
||||||
ContainerArchitecture: cfg.ContainerArchitecture,
|
ContainerArchitecture: cfg.ContainerArchitecture,
|
||||||
|
Matrix: cfg.Matrix,
|
||||||
}
|
}
|
||||||
|
|
||||||
runner, err := New(runnerConfig)
|
runner, err := New(runnerConfig)
|
||||||
|
@ -584,3 +585,30 @@ func TestRunEventPullRequest(t *testing.T) {
|
||||||
|
|
||||||
tjfi.runTest(context.Background(), t, &Config{EventPath: filepath.Join(workdir, workflowPath, "event.json")})
|
tjfi.runTest(context.Background(), t, &Config{EventPath: filepath.Join(workdir, workflowPath, "event.json")})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunMatrixWithUserDefinedInclusions(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
workflowPath := "matrix-with-user-inclusions"
|
||||||
|
|
||||||
|
tjfi := TestJobFileInfo{
|
||||||
|
workdir: workdir,
|
||||||
|
workflowPath: workflowPath,
|
||||||
|
eventName: "push",
|
||||||
|
errorMessage: "",
|
||||||
|
platforms: platforms,
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix := map[string]map[string]bool{
|
||||||
|
"node": {
|
||||||
|
"8": true,
|
||||||
|
"8.x": true,
|
||||||
|
},
|
||||||
|
"os": {
|
||||||
|
"ubuntu-18.04": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tjfi.runTest(context.Background(), t, &Config{Matrix: matrix})
|
||||||
|
}
|
||||||
|
|
34
pkg/runner/testdata/matrix-with-user-inclusions/push.yml
vendored
Normal file
34
pkg/runner/testdata/matrix-with-user-inclusions/push.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: matrix-with-user-inclusions
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: PHP ${{ matrix.os }} ${{ matrix.node}}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo ${NODE_VERSION} | grep 8
|
||||||
|
echo ${OS_VERSION} | grep ubuntu-18.04
|
||||||
|
env:
|
||||||
|
NODE_VERSION: ${{ matrix.node }}
|
||||||
|
OS_VERSION: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-18.04, macos-latest]
|
||||||
|
node: [4, 6, 8, 10]
|
||||||
|
exclude:
|
||||||
|
- os: macos-latest
|
||||||
|
node: 4
|
||||||
|
include:
|
||||||
|
- os: ubuntu-16.04
|
||||||
|
node: 10
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: [8.x, 10.x, 12.x, 13.x]
|
||||||
|
steps:
|
||||||
|
- run: echo ${NODE_VERSION} | grep 8.x
|
||||||
|
env:
|
||||||
|
NODE_VERSION: ${{ matrix.node }}
|
Loading…
Add table
Reference in a new issue