Repository: nektos/act (language: go)

Issue:
Issue: Improperly formatted workflow yaml file causes "panic: runtime error: invalid memory address or nil pointer dereference"
<!--
    - Make sure you are able to reproduce it on the [latest version](https://github.com/nektos/act/releases)
    - Search the existing issues.
    - Refer to [README](https://github.com/nektos/act/blob/master/README.md).
-->

## System information

<!--
    - Operating System: < Windows | Linux | macOS | etc... >
    - Architecture: < x64 (64-bit) | x86 (32-bit) | arm64 (64-bit) | arm (32-bit) | etc... >
    - Apple M1: < yes | no >
    - Docker version: < output of `docker system info -f "{{.ServerVersion}}"` >
    - Docker image used in `act`: < can be omitted if it's included in log >
    - `act` version: < output of `act --version`, if you've built `act` yourself, please provide commit hash >
-->

- Operating System: macOS
- Architecture: 64bit
- Apple M1: no
- Docker version: 20.10.14
- Docker image used in `act`: nektos/act 
- `act` version: 0.2.26

## Expected behaviour

<!--
    - Describe how whole process should go and finish
-->

Yaml parsing syntax error or otherwise error specifying that yaml file cannot be run as a github workflow.

## Actual behaviour

<!--
    - Describe the issue
-->

Cryptic go error is given.

## Workflow and/or repository

<!--
    - Provide workflow with which we can reproduce the issue
      OR
    - Provide link to your GitHub repository that contains the workflow
-->
<details>
  <summary>workflow</summary>

```
name: bug
on:
  pull_request: [synchronize]

jobs:
  bug:
    runs-on: ubuntu-18.04
    steps:
      -
      - name: step
        run: echo "bug"
```

</details>



## Steps to reproduce

<!--
    - Make sure to include full command with parameters you used to run `act`, example:
      1. Clone example repo (https://github.com/cplee/github-actions-demo)
      2. Enter cloned repo directory
      3. Run `act -s SUPER_SECRET=im-a-value`
-->

`act pull_request -W .github/workflows/bug.yml -v -P nektos/act`

## `act` output

<!--
    - Use `act` with `-v`/`--verbose` and paste output from your terminal in code block below
-->

<details>
  <summary>Log</summary>

```
❯ act pull_request -W .github/workflows/bug.yml -v -P nektos/act
DEBU[0000] Loading environment from /Users/vito/dev/redsetter/.env
DEBU[0000] Loading secrets from /Users/vito/dev/redsetter/.secrets
DEBU[0000] Loading workflow '/Users/vito/dev/redsetter/.github/workflows/bug.yml'
DEBU[0000] Reading workflow '/Users/vito/dev/redsetter/.github/workflows/bug.yml'
DEBU[0000] Planning event: pull_request
DEBU[0000] Loading slug from git directory '/Users/vito/dev/redsetter/.git'
DEBU[0000] Found revision: f2a25115b51440edb9a44c6882044454c1b90f5f
DEBU[0000] evaluating expression 'success()'
DEBU[0000] expression 'success()' evaluated to 'true'
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x16b6fd8]

goroutine 9 [running]:
github.com/nektos/act/pkg/runner.newJobExecutor({0x1986d78?, 0xc0003bdc20})
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/runner/job_executor.go:35 +0x238
github.com/nektos/act/pkg/runner.(*RunContext).Executor.func1({0x19841f8, 0xc0000a3680})
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/runner/run_context.go:290 +0x55
github.com/nektos/act/pkg/common.Executor.Finally.func1({0x19841f8, 0xc0000a3680})
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/common/executor.go:177 +0x34
github.com/nektos/act/pkg/runner.(*runnerImpl).NewPlanExecutor.func1.1({0x19841f8, 0xc00041e5d0})
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/runner/runner.go:171 +0x2f2
github.com/nektos/act/pkg/common.NewParallelExecutor.func1.1(0x0?, 0x0?)
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/common/executor.go:102 +0x5c
created by github.com/nektos/act/pkg/common.NewParallelExecutor.func1
        /private/tmp/act-20220322-14079-18pjzcc/act-0.2.26/pkg/common/executor.go:100 +0x89
```

</details>



Discussion / hints:

Fix patch:
diff --git a/pkg/runner/job_executor.go b/pkg/runner/job_executor.go
index 7c9da4d93f7..e128aaa7aac 100644
--- a/pkg/runner/job_executor.go
+++ b/pkg/runner/job_executor.go
@@ -39,6 +39,11 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
 	preSteps = append(preSteps, info.startContainer())
 
 	for i, stepModel := range infoSteps {
+		if stepModel == nil {
+			return func(ctx context.Context) error {
+				return fmt.Errorf("invalid Step %v: missing run or uses key", i)
+			}
+		}
 		if stepModel.ID == "" {
 			stepModel.ID = fmt.Sprintf("%d", i)
 		}


Test patch:
diff --git a/pkg/runner/job_executor_test.go b/pkg/runner/job_executor_test.go
index 2af79336677..4d892feb166 100644
--- a/pkg/runner/job_executor_test.go
+++ b/pkg/runner/job_executor_test.go
@@ -21,6 +21,7 @@ func TestJobExecutor(t *testing.T) {
 		{workdir, "uses-docker-url", "push", "", platforms},
 		{workdir, "uses-github-full-sha", "push", "", platforms},
 		{workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms},
+		{workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms},
 	}
 	// These tests are sufficient to only check syntax.
 	ctx := common.WithDryrun(context.Background(), true)
diff --git a/pkg/runner/testdata/job-nil-step/push.yml b/pkg/runner/testdata/job-nil-step/push.yml
new file mode 100644
index 00000000000..b868b0472a5
--- /dev/null
+++ b/pkg/runner/testdata/job-nil-step/push.yml
@@ -0,0 +1,7 @@
+on: push
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+    -
+    - run: exit 0