mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
3 Commits
features/c
...
users/eric
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2aff5d4d8b | ||
|
|
ec910bab3d | ||
|
|
8932ad8d94 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -164,6 +164,7 @@ jobs:
|
||||
release_name: "v${{ steps.releaseNote.outputs.version }}"
|
||||
body: |
|
||||
${{ steps.releaseNote.outputs.note }}
|
||||
prerelease: true
|
||||
|
||||
# Upload release assets
|
||||
- name: Upload Release Asset (win-x64)
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,12 +8,10 @@
|
||||
**/*.xproj
|
||||
**/*.xproj.user
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.error
|
||||
**/*.json.pretty
|
||||
.idea/
|
||||
.vscode
|
||||
!.vscode/launch.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# output
|
||||
node_modules
|
||||
|
||||
57
.vscode/launch.json
vendored
57
.vscode/launch.json
vendored
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run [build]",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build runner layout",
|
||||
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"console": "integratedTerminal",
|
||||
"requireExactSource": false,
|
||||
},
|
||||
{
|
||||
"name": "Run",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
|
||||
"args": [
|
||||
"run"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"console": "integratedTerminal",
|
||||
"requireExactSource": false,
|
||||
},
|
||||
{
|
||||
"name": "Configure",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "create runner layout",
|
||||
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
|
||||
"args": [
|
||||
"configure"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"console": "integratedTerminal",
|
||||
"requireExactSource": false,
|
||||
},
|
||||
{
|
||||
"name": "Debug Worker",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processName": "Runner.Worker",
|
||||
"requireExactSource": false,
|
||||
},
|
||||
{
|
||||
"name": "Attach Debugger",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}",
|
||||
"requireExactSource": false,
|
||||
},
|
||||
],
|
||||
}
|
||||
33
.vscode/tasks.json
vendored
33
.vscode/tasks.json
vendored
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "create runner layout",
|
||||
"detail": "Build and Copy all projects, scripts and external dependencies to _layout from src (run this the first time or after deleting _layout)",
|
||||
"command": "./dev.sh",
|
||||
"windows": {
|
||||
"command": "dev.cmd"
|
||||
},
|
||||
"args": [
|
||||
"layout"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/src"
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "build runner layout",
|
||||
"detail": "Build and Copy all projects to _layout from src (run this on code change)",
|
||||
"command": "./dev.sh",
|
||||
"windows": {
|
||||
"command": "dev.cmd"
|
||||
},
|
||||
"args": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/src"
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# ADR 263: Self-Hosted Runner Proxies
|
||||
# ADR 263: Self Hosted Runner Proxies
|
||||
|
||||
**Date**: 2019-11-13
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
## Context
|
||||
|
||||
- Proxy support is required for some enterprises and organizations to start using their own self-hosted runners
|
||||
- While there is not a standard convention, many applications support setting proxies via the environment variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, and R
|
||||
- Proxy support is required for some enterprises and organizations to start using their own self hosted runners
|
||||
- While there is not a standard convention, many applications support setting proxies via the environmental variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, R, ect
|
||||
- Some of these applications use `HTTPS_PROXY` versus `https_proxy`, but most understand or primarily support the lowercase variant
|
||||
|
||||
## Decision
|
||||
|
||||
We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy`, and `no_proxy` if they are set.
|
||||
We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy` and `no_proxy` if they are set.
|
||||
These are described in detail below:
|
||||
- `https_proxy` a proxy URL for all https traffic. It may contain basic authentication credentials. For example:
|
||||
- http://proxy.com
|
||||
@@ -22,20 +22,20 @@ These are described in detail below:
|
||||
- http://proxy.com
|
||||
- http://127.0.0.1:8080
|
||||
- http://user:password@proxy.com
|
||||
- `no_proxy` a comma-separated list of hosts that should not use the proxy. An optional port may be specified. For example:
|
||||
- `no_proxy` a comma separated list of hosts that should not use the proxy. An optional port may be specified
|
||||
- `google.com`
|
||||
- `yahoo.com:443`
|
||||
- `google.com,bing.com`
|
||||
|
||||
We won't use `http_proxy` for https traffic when `https_proxy` is not set, this behavior lines up with any libcurl based tools (curl, git) and wget.
|
||||
Otherwise, action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts.
|
||||
Otherwise action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts.
|
||||
|
||||
Example:
|
||||
Customer sets `http_proxy=http://127.0.0.1:8888` and configures the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic.
|
||||
Customer set `http_proxy=http://127.0.0.1:8888` and configure the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic.
|
||||
|
||||
> `golang`, `node.js`, and other dev tools from the Linux community use `http_proxy` for both http and https traffic based on my research.
|
||||
> `golang`, `node.js` and other dev tools from the linux community use `http_proxy` for both http and https traffic based on my research.
|
||||
|
||||
A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self-hosted runners to set up a proxy and expose it in a way users are already familiar with.
|
||||
A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self hosted runners to set up proxy, and expose it in a way users are already familiar with.
|
||||
|
||||
A password provided for a proxy will be masked in the logs.
|
||||
|
||||
@@ -43,19 +43,19 @@ We will support the lowercase and uppercase variants, with lowercase taking prio
|
||||
|
||||
### No Proxy Format
|
||||
|
||||
While exact implementations are different per application on handling `no_proxy` env, most applications accept a comma-separated list of hosts. Some accept wildcard characters (`*`). We are going to do exact case-insensitive matches, and not support wildcards at this time.
|
||||
While exact implementations are different per application on handle `no_proxy` env, most applications accept a comma separated list of hosts. Some accept wildcard characters (*). We are going to do exact case-insensitive matches, and not support wildcards at this time.
|
||||
For example:
|
||||
- `example.com` will match `example.com`, `foo.example.com`, and `foo.bar.example.com`
|
||||
- `foo.example.com` will match `bar.foo.example.com` and `foo.example.com`
|
||||
- example.com will match example.com, foo.example.com, foo.bar.example.com
|
||||
- foo.example.com will match bar.foo.example.com and foo.example.com
|
||||
|
||||
We will not support IP addresses for `no_proxy`, only hostnames.
|
||||
|
||||
## Consequences
|
||||
|
||||
1. Enterprises and organizations needing proxy support will be able to embrace self-hosted runners
|
||||
2. Users will need to set these environment variables before configuring the runner in order to use a proxy when configuring
|
||||
3. The runner will read from the environment variables during config and runtime and use the provided proxy if it exists
|
||||
4. Users may need to pass these environment variables into other applications if they do not natively take these variables
|
||||
5. Action authors may need to update their workflows to react to these environment variables
|
||||
6. We will document the way of setting environment variables for runners using the environment variables and how the runner uses them
|
||||
7. Like all other secrets, users will be able to relatively easily figure out the proxy password if they can modify a workflow file running on a self-hosted machine
|
||||
1. Enterprises and organizations needing proxy support will be able to embrace self hosted runners
|
||||
2. Users will need to set these environmental variables before configuring the runner in order to use a proxy when configuring
|
||||
3. The runner will read from the environmental variables during config and runtime and use the provided proxy if it exists
|
||||
4. Users may need to pass these environmental variables into other applications if they do not natively take these variables
|
||||
5. Action authors may need to update their workflows to react to the these environment variables
|
||||
6. We will document the way of setting environmental variables for runners using the environment variables and how the runner uses them
|
||||
7. Like all other secrets, users will be able to relatively easily figure out proxy password if they can modify a workflow file running on a self hosted machine
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
run-actions run scripts using a platform specific shell:
|
||||
`bash -eo pipefail` on non-windows, and `cmd.exe /c /d /s` on windows
|
||||
|
||||
The `shell` option overrides this to allow different flags or completely different shells/interpreters
|
||||
The `shell` option overwrites this to allow different flags or completely different shells/interpreters
|
||||
|
||||
A small example is:
|
||||
```yml
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
**Status**: Accepted
|
||||
|
||||
## Context
|
||||
First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most common `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder.
|
||||
First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most comment `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder.
|
||||
|
||||
There are serval different ways to get the hash `key` input for `actions/cache` action.
|
||||
|
||||
@@ -38,7 +38,7 @@ There are serval different ways to get the hash `key` input for `actions/cache`
|
||||
`hashFiles()` will only support hashing files under the `$GITHUB_WORKSPACE` since the expression evaluated on the runner, if customer use job container or container action, the runner won't have access to file system inside the container.
|
||||
|
||||
`hashFiles()` will only take 1 parameters:
|
||||
- `hashFiles('**/package-lock.json')` // Search files under `$GITHUB_WORKSPACE` and calculate a hash for them
|
||||
- `hashFiles('**/package-lock.json')` // Search files under $GITHUB_WORKSPACE and calculate a hash for them
|
||||
|
||||
**Question: Do we need to support more than one match patterns?**
|
||||
Ex: `hashFiles('**/package-lock.json', '!toolkit/core/package-lock.json', '!toolkit/io/package-lock.json')`
|
||||
@@ -52,7 +52,7 @@ This will help customer has better experience with the `actions/cache` action's
|
||||
key: ${{hashFiles('**/package-lock.json')}}-${{github.ref}}-${{runner.os}}
|
||||
```
|
||||
|
||||
For search pattern, we will use basic globbing (`*`, `?`, and `[]`) and globstar (`**`).
|
||||
For search pattern, we will use basic globbing (`*` `?` and `[]`) and globstar (`**`).
|
||||
|
||||
Additional pattern details:
|
||||
- Root relative paths with `github.workspace` (the main repo)
|
||||
@@ -68,4 +68,4 @@ Hashing logic:
|
||||
5. Use SHA256 to hash all stored files' hash results to get the final 64 chars hash result.
|
||||
|
||||
**Question: Should we include the folder structure info into the hash?**
|
||||
Answer: No
|
||||
Answer: No
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
## Context
|
||||
|
||||
In addition to action's regular execution, action author may wants their action to have a chance to participate in:
|
||||
- Job initialization
|
||||
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the beginning of the job.
|
||||
- Job cleanup
|
||||
In addition to action's regular execution, action author may wants their action has a chance to participate in:
|
||||
- Job initialize
|
||||
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the begin of the job.
|
||||
- Job cleanup
|
||||
My Action will dirty local workspace or machine environment during execution, we need to cleanup these changes at the end of the job.
|
||||
Ex: `actions/checkout@v2` will write `github.token` into local `.git/config` during execution, it has post job cleanup defined to undo the changes.
|
||||
|
||||
@@ -46,12 +46,12 @@ Container Action Example:
|
||||
post-if: 'success()' // Optional
|
||||
```
|
||||
|
||||
Both `pre` and `post` will have default `pre-if/post-if` set to `always()`.
|
||||
Both `pre` and `post` will has default `pre-if/post-if` sets to `always()`.
|
||||
Setting `pre` to `always()` will make sure no matter what condition evaluate result the `main` gets at runtime, the `pre` has always run already.
|
||||
`pre` executes in order of how the steps are defined.
|
||||
`pre` will always be added to job steps list during job setup.
|
||||
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checked-out during job initialization.
|
||||
> We can't use GitHub api to download the repository since there is about a 3 minute delay between `git push` and the new commit available to download using GitHub api.
|
||||
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checkout during job initialize.
|
||||
> We can't use GitHub api to download the repository since there is a about 3 mins delay between `git push` and the new commit available to download using GitHub api.
|
||||
|
||||
`post` will be pushed into a `poststeps` stack lazily when the action's `pre` or `main` execution passed `if` condition check and about to run, you can't have an action that only contains a `post`, we will pop and run each `post` after all `pre` and `main` finished.
|
||||
> Currently `post` works for both repository action (`org/repo@v1`) and local action (`./my-action`)
|
||||
@@ -60,7 +60,7 @@ Valid action:
|
||||
- only has `main`
|
||||
- has `pre` and `main`
|
||||
- has `main` and `post`
|
||||
- has `pre`, `main`, and `post`
|
||||
- has `pre`, `main` and `post`
|
||||
|
||||
Invalid action:
|
||||
- only has `pre`
|
||||
|
||||
@@ -13,13 +13,13 @@ This is another version of [ADR275](https://github.com/actions/runner/pull/275)
|
||||
|
||||
## Decision
|
||||
|
||||
This ADR proposes that we add a `--labels` option to the `config`, which could be used to add custom additional labels to the configured runner.
|
||||
This ADR proposes that we add a `--labels` option to `config`, which could be used to add custom additional labels to the configured runner.
|
||||
|
||||
For example, to add a single additional label the operator could run:
|
||||
For example, to add a single extra label the operator could run:
|
||||
```bash
|
||||
./config.sh --labels mylabel
|
||||
```
|
||||
> Note: the current runner command line parsing and envvar override algorithm only support a single argument (key).
|
||||
> Note: the current runner command line parsing and envvar override algorithm only supports a single argument (key).
|
||||
|
||||
This would add the label `mylabel` to the runner, and enable users to select the runner in their workflow using this label:
|
||||
```yaml
|
||||
@@ -39,17 +39,17 @@ runs-on: [self-hosted, mylabel, anotherlabel]
|
||||
|
||||
It would not be possible to remove labels from an existing runner using `config.sh`, instead labels would have to be removed using the GitHub UI.
|
||||
|
||||
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commas in unattended config label names. Alternatively, we could choose to escape commas but it's a nice to have.
|
||||
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commans in unattended config label names. Alternatively we could choose to escape commans but it's a nice to have.
|
||||
|
||||
## Replace
|
||||
|
||||
If an existing runner exists and the option to replace is chosen (interactively or via unattended as in this scenario), then the labels will be replaced/overwritten (not merged).
|
||||
If an existing runner exists and the option to replace is chosen (interactively of via unattend as in this scenario), then the labels will be replaced / overwritten (not merged).
|
||||
|
||||
## Overriding built-in labels
|
||||
|
||||
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org/runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
|
||||
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org / runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
|
||||
|
||||
We will also not make other restrictions such as limiting explicitly adding os/arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future-proofing / compatibility.
|
||||
We will also not make other restrictions such as limiting explicitly adding os / arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future proofing / compat.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
|
||||
Customers want to be able to compose actions from actions (ex: https://github.com/actions/runner/issues/438)
|
||||
|
||||
An important step towards meeting this goal is to build functionality for actions where users can simply execute any number of steps.
|
||||
An important step towards meeting this goal is to build in functionality for actions where users can simply execute any number of steps.
|
||||
|
||||
### Guiding Principles
|
||||
|
||||
We don't want the workflow author to need to know how the internal workings of the action work. Users shouldn't know the internal workings of the composite action (for example, `default.shell` and `default.workingDir` should not be inherited from the workflow file to the action file). When deciding how to design certain parts of composite run steps, we want to treat it as one logical step for the consumer.
|
||||
We don't want the workflow author to need to know how the internal workings of the action work. Users shouldn't know the internal workings of the composite action (for example, `default.shell` and `default.workingDir` should not be inherited from the workflow file to the action file). When deciding how to design certain parts of composite run steps, we want to think one logical step from the consumer.
|
||||
|
||||
A composite action is treated as **one** individual job step (this is known as encapsulation).
|
||||
|
||||
## Decision
|
||||
|
||||
**In this ADR, we only support running multiple run steps in an Action.** In doing so, we build in support for mapping and flowing the inputs, outputs, and env variables (ex: All nested steps should have access to their parents' input variables and nested steps can overwrite the input variables).
|
||||
**In this ADR, we only support running multiple run steps in an Action.** In doing so, we build in support for mapping and flowing the inputs, outputs, and env variables (ex: All nested steps should have access to its parents' input variables and nested steps can overwrite the input variables).
|
||||
|
||||
### Composite Run Steps Features
|
||||
This feature supports at the top action level:
|
||||
@@ -92,7 +92,7 @@ We will not support "defaults" in a composite action.
|
||||
|
||||
### Shell and Working-directory
|
||||
|
||||
For each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. The shell attribute is **required** for each run step because the action author does not know what the workflow author is using for the operating system so we need to explicitly prevent unknown behavior by making sure that each run step has an explicit shell **set by the action author.** On the other hand, `working-directory` is optional. Moreover, the composite action author can map in values from the `inputs` for its `shell` and `working-directory` attributes at the step level for an action.
|
||||
For each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. The shell attribute is **required** for each run step because the action author does not know what the workflow author is using for the operating system so we need to explicitly prevent unknown behavior by making sure that each run step has an explicit shell **set by the action author.** On the other hand, `working-directory` is optional. Moreover, the composite action author can map in values from the `inputs` for it's `shell` and `working-directory` attributes at the step level for an action.
|
||||
|
||||
For example,
|
||||
|
||||
@@ -218,9 +218,9 @@ Example Output:
|
||||
random-number 43243
|
||||
```
|
||||
|
||||
Each of the output variables from the composite action is viewable from the workflow file that uses the composite action. In other words, every child's action output(s) are only viewable by its parent using dot notation (ex `steps.foo.outputs.random-number`).
|
||||
Each of the output variables from the composite action is viewable from the workflow file that uses the composite action. In other words, every child action output(s) is viewable only by its parent using dot notation (ex `steps.foo.outputs.random-number`).
|
||||
|
||||
Moreover, the output ids are only accessible within the scope where it was defined. Note that in the example above, in our `workflow.yml` file, it should not have access to output id (i.e. `random-id`). The reason why we are doing this is that we don't want to require the workflow author to know the internal workings of the composite action.
|
||||
Moreover, the output ids are only accessible within the scope where it was defined. Note that in the example above, in our `workflow.yml` file, it should not have access to output id (i.e. `random-id`). The reason why we are doing this is because we don't want to require the workflow author to know the internal workings of the composite action.
|
||||
|
||||
### Context
|
||||
|
||||
@@ -237,9 +237,9 @@ In the Composite Action, you'll only be able to use `::set-env::` to set environ
|
||||
We'll pass the secrets from the composite action's parents (ex: the workflow file) to the composite action. Secrets can be created in the composite action with the secrets context. In the actions yaml, we'll automatically mask the secret.
|
||||
|
||||
|
||||
### If-Condition
|
||||
### If Condition
|
||||
|
||||
** `If` and `needs` conditions will not be supported in the composite run steps feature. It will be supported later on in a new feature. **
|
||||
** If and needs conditions will not be supported in the composite run steps feature. It will be supported later on in a new feature. **
|
||||
|
||||
Old reasoning:
|
||||
|
||||
@@ -248,7 +248,7 @@ Example `workflow.yml`:
|
||||
```yaml
|
||||
steps:
|
||||
- run: exit 1
|
||||
- uses: user/composite@v1 # <--- this will run, as it's marked as always running
|
||||
- uses: user/composite@v1 # <--- this will run, as it's marked as always runing
|
||||
if: always()
|
||||
```
|
||||
|
||||
@@ -269,15 +269,15 @@ runs:
|
||||
shell: bash
|
||||
```
|
||||
|
||||
**We will not support "if-condition" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
**We will not support "if Condition" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
|
||||
See the paragraph below for a rudimentary approach (thank you to @cybojenix for the idea, example, and explanation for this approach):
|
||||
|
||||
The `if` statement in the parent (in the example above, this is the `workflow.yml`) shows whether or not we should run the composite action. So, our composite action will run since the `if` condition for running the composite action is `always()`.
|
||||
|
||||
**Note that the "if-condition" on the parent does not propagate to the rest of its children though.**
|
||||
**Note that the if condition on the parent does not propagate to the rest of its children though.**
|
||||
|
||||
In the child action (in this example, this is the `action.yml`), it starts with a clean slate (in other words, no imposing if-conditions). Similar to the logic in the paragraph above, `echo "I will run, as my current scope is succeeding"` will run since the `if` condition checks if the previous steps **within this composite action** have not failed. `run: echo "I will not run, as my current scope is now failing"` will not run since the previous step resulted in an error and by default, the if expression is set to `success()` if the if-condition is not set for a step.
|
||||
In the child action (in this example, this is the `action.yml`), it starts with a clean slate (in other words, no imposing if conditions). Similar to the logic in the paragraph above, `echo "I will run, as my current scope is succeeding"` will run since the `if` condition checks if the previous steps **within this composite action** has not failed. `run: echo "I will not run, as my current scope is now failing"` will not run since the previous step resulted in an error and by default, the if expression is set to `success()` if the if condition is not set for a step.
|
||||
|
||||
|
||||
What if a step has `cancelled()`? We do the opposite of our approach above if `cancelled()` is used for any of our composite run steps. We will cancel any step that has this condition if the workflow is cancelled at all.
|
||||
@@ -314,13 +314,13 @@ runs:
|
||||
|
||||
**We will not support "timeout-minutes" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
|
||||
A composite action in its entirety is a job. You can set both timeout-minutes for the whole composite action or its steps as long as the sum of the `timeout-minutes` for each composite action step that has the attribute `timeout-minutes` is less than or equals to `timeout-minutes` for the composite action. There is no default timeout-minutes for each composite action step.
|
||||
A composite action in its entirety is a job. You can set both timeout-minutes for the whole composite action or its steps as long as the the sum of the `timeout-minutes` for each composite action step that has the attribute `timeout-minutes` is less than or equals to `timeout-minutes` for the composite action. There is no default timeout-minutes for each composite action step.
|
||||
|
||||
If the time taken for any of the steps in combination or individually exceeds the whole composite action `timeout-minutes` attribute, the whole job will fail (1). If an individual step exceeds its own `timeout-minutes` attribute but the total time that has been used including this step is below the overall composite action `timeout-minutes`, the individual step will fail but the rest of the steps will run based on their own `timeout-minutes` attribute (they will still abide by condition (1) though).
|
||||
If the time taken for any of the steps in combination or individually exceed the whole composite action `timeout-minutes` attribute, the whole job will fail (1). If an individual step exceeds its own `timeout-minutes` attribute but the total time that has been used including this step is below the overall composite action `timeout-minutes`, the individual step will fail but the rest of the steps will run based on their own `timeout-minutes` attribute (they will still abide by condition (1) though).
|
||||
|
||||
For reference, in the example above, if the composite step `foo1` takes 11 minutes to run, that step will fail but the rest of the steps, `foo1` and `foo2`, will proceed as long as their total runtime with the previous failed `foo1` action is less than the composite action's `timeout-minutes` (50 minutes). If the composite step `foo2` takes 51 minutes to run, it will cause the whole composite action job to fail.
|
||||
For reference, in the example above, if the composite step `foo1` takes 11 minutes to run, that step will fail but the rest of the steps, `foo1` and `foo2`, will proceed as long as their total runtime with the previous failed `foo1` action is less than the composite action's `timeout-minutes` (50 minutes). If the composite step `foo2` takes 51 minutes to run, it will cause the whole composite action job to fail. I
|
||||
|
||||
The rationale behind this is that users can configure their steps with the `if` condition to conditionally set how steps rely on each other. Due to the additional capabilities that are offered with combining `timeout-minutes` and/or `if`, we wanted the `timeout-minutes` condition to be as dumb as possible and not affect other steps.
|
||||
The rationale behind this is that users can configure their steps with the `if` condition to conditionally set how steps rely on each other. Due to the additional capabilities that are offered with combining `timeout-minutes` and/or `if`, we wanted the `timeout-minutes` condition to be as dumb as possible and not effect other steps.
|
||||
|
||||
[Usage limits still apply](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions?query=if%28%29#usage-limits)
|
||||
|
||||
@@ -361,7 +361,7 @@ For the composite action steps, it follows the same logic as above. In this exam
|
||||
### Visualizing Composite Action in the GitHub Actions UI
|
||||
We want all the composite action's steps to be condensed into the original composite action node.
|
||||
|
||||
Here is a visual representation of the [first example](#Steps)
|
||||
Here is a visual represenation of the [first example](#Steps)
|
||||
|
||||
```yaml
|
||||
| composite_action_node |
|
||||
|
||||
44
docs/adrs/1040-composite-supports-action-steps.md
Normal file
44
docs/adrs/1040-composite-supports-action-steps.md
Normal file
@@ -0,0 +1,44 @@
|
||||
**Date**: 2021-04-06
|
||||
|
||||
**Status**: Accepted
|
||||
|
||||
## Context
|
||||
|
||||
We will add support for actions to be referenced, with composite actions.
|
||||
|
||||
## Decision
|
||||
|
||||
TODO:
|
||||
- Decide recursion limit
|
||||
|
||||
## Consequences
|
||||
|
||||
TODO: Move these details into a tracking issue instead
|
||||
|
||||
- Launch
|
||||
- New feature flag
|
||||
- Move Action policy enforcement from workflow run, to resolve actions endpoint
|
||||
- Runner
|
||||
- New feature flag
|
||||
- Download all nested actions
|
||||
- Decide recursion limit
|
||||
- src/Runner.Worker/ActionManager.cs
|
||||
- Precursor: Remove feature flag DistributedTask.NewActionMetadata
|
||||
- Update schema to support `uses` within a composite step
|
||||
- src/Runner.Worker/action_yaml.json
|
||||
- src/Sdk/DTPipelines/Pipelines/ObjectTemplating/PipelineTemplateConverter.cs
|
||||
- Mimic server validation, generate IDs, etc
|
||||
- Handle inputs/outputs between nested layers
|
||||
- Update server to generate context names when empty
|
||||
- Fix issue with `hashFiles` within a composite action
|
||||
- https://github.com/actions/runner/issues/991
|
||||
- Support container-actions and actions with pre-step and post-step
|
||||
- Composite actions is intended double as a re-use mechanism. So pre/post support is critical, and should execute in the normal order.
|
||||
- Investigate how to deal with pre-steps. Today, nested steps are created just-in-time. However, pre-steps are generally executed before regular steps. Also, container actions implicitly have a pre-step to pull or build the image
|
||||
- Investigate whether we copy the continue-on-error setting to the pre/post steps
|
||||
- Investigate CompositeActionHandler.cs especially wrt clearing the output scopes
|
||||
- Investigate whether we need additional validation to restrict contexts available to pre-step condition and post-step condition
|
||||
- Testing
|
||||
- Support all types of action manifests: Node.js, Dockerfile, `docker://`, composite, no manifest only Dockerfile
|
||||
- Support all types of action references: `docker://`, `./`, owner/repo@ref
|
||||
- Support for actions with pre-step and post-step. For pre/post, also test intra-action-state
|
||||
@@ -23,7 +23,7 @@ export RUNNER_CFG_PAT=yourPAT
|
||||
|
||||
Run as a one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
|
||||
```bash
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo
|
||||
```
|
||||
|
||||
## Uninstall running as service
|
||||
@@ -38,7 +38,7 @@ curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/create-lat
|
||||
|
||||
Repo level one liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
|
||||
```bash
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/remove-svc.sh | bash -s yourorg/yourrepo
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/remove-svc.sh | bash -s yourorg/yourrepo
|
||||
```
|
||||
|
||||
### Delete an offline runner
|
||||
@@ -53,5 +53,5 @@ curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/remove-svc
|
||||
|
||||
Repo level one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) and replace runnername
|
||||
```bash
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/delete.sh | bash -s yourorg/yourrepo runnername
|
||||
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/delete.sh | bash -s yourorg/yourrepo runnername
|
||||
```
|
||||
|
||||
@@ -19,35 +19,12 @@ We ask that before significant effort is put into code changes, that we have agr
|
||||
|
||||
An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](adrs/README.md)
|
||||
|
||||
## Required Dev Dependencies
|
||||
## Development Life Cycle
|
||||
|
||||
### Required Dev Dependencies
|
||||
|
||||
  Git for Windows and Linux [Install Here](https://git-scm.com/downloads) (needed for dev sh script)
|
||||
|
||||
## Quickstart: Run a job from a real repository
|
||||
|
||||
If you just want to get from building the sourcecode to using it to execute an action, you will need:
|
||||
|
||||
- The url of your repository
|
||||
- A runner registration token. You can find it at `https://github.com/{your-repo}/settings/actions/runners/new`
|
||||
|
||||
|
||||
```bash
|
||||
git clone https://github.com/actions/runner
|
||||
cd runner/src
|
||||
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
|
||||
cd ../_layout
|
||||
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB # accept default name, labels and work folder
|
||||
./run.(sh/cmd)
|
||||
```
|
||||
|
||||
If you trigger a job now, you can see the runner execute it.
|
||||
|
||||
Tip: Make sure your job can run on this runner. The easiest way is to set `runs-on: self-hosted` in the workflow file.
|
||||
|
||||
|
||||
## Development Life Cycle
|
||||
If you're using VS Code, you can follow [these](contribute/vscode.md) steps instead.
|
||||
|
||||
### To Build, Test, Layout
|
||||
|
||||
Navigate to the `src` directory and run the following command:
|
||||
@@ -62,7 +39,7 @@ Navigate to the `src` directory and run the following command:
|
||||
* `build` (`b`): Build everything and update runner layout folder
|
||||
* `test` (`t`): Build runner binaries and run unit tests
|
||||
|
||||
### Sample developer flow:
|
||||
Sample developer flow:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/actions/runner
|
||||
@@ -74,81 +51,25 @@ cd ./src
|
||||
./dev.(sh/cmd) test # run all unit tests before git commit/push
|
||||
```
|
||||
|
||||
Let's break that down.
|
||||
|
||||
### Clone repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/actions/runner
|
||||
cd runner
|
||||
```
|
||||
If you want to push your changes to a remote, it is recommended you fork the repository and use that fork as your origin instead of `https://github.com/actions/runner`.
|
||||
|
||||
|
||||
### Build Layout:
|
||||
|
||||
This command will build all projects, then copies them and other dependencies into a folder called `_layout`. The binaries in this folder are then used for running, debugging the runner.
|
||||
|
||||
```bash
|
||||
cd ./src # execute the script from this folder
|
||||
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
|
||||
```
|
||||
|
||||
If you make code changes after this point, use the argument `build` to build your code in the `src` folder to keep your `_layout` folder up to date.
|
||||
|
||||
```bash
|
||||
cd ./src
|
||||
./dev.(sh/cmd) build # {root}/_layout will get updated
|
||||
```
|
||||
### Test Layout:
|
||||
|
||||
This command runs the suite of unit tests in the project
|
||||
|
||||
```bash
|
||||
cd ./src
|
||||
./dev.(sh/cmd) test # run all unit tests before git commit/push
|
||||
```
|
||||
|
||||
### Configure Runner:
|
||||
|
||||
If you want to manually test your runner and run actions from a real repository, you'll have to configure it before running it.
|
||||
|
||||
```bash
|
||||
cd runner/_layout
|
||||
./config.(sh/cmd) # configure your custom runner
|
||||
```
|
||||
|
||||
You will need your the name of your repository and a runner registration token.
|
||||
Check [Quickstart](##Quickstart:-Run-a-job-from-a-real-repository) if you don't know how to get this token.
|
||||
|
||||
These can also be passed down as arguments to `config.(sh/cmd)`:
|
||||
```bash
|
||||
cd runner/_layout
|
||||
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB
|
||||
```
|
||||
|
||||
### Run Runner
|
||||
|
||||
All that's left to do is to start the runner:
|
||||
```bash
|
||||
cd runner/_layout
|
||||
./run.(sh/cmd) # run your custom runner
|
||||
```
|
||||
|
||||
### View logs:
|
||||
|
||||
View logs:
|
||||
```bash
|
||||
cd runner/_layout/_diag
|
||||
ls
|
||||
cat (Runner/Worker)_TIMESTAMP.log # view your log file
|
||||
```
|
||||
|
||||
## Editors
|
||||
Run Runner:
|
||||
```bash
|
||||
cd runner/_layout
|
||||
./run.sh # run your custom runner
|
||||
```
|
||||
|
||||
### Editors
|
||||
|
||||
[Using Visual Studio Code](https://code.visualstudio.com/)
|
||||
[Using Visual Studio](https://code.visualstudio.com/docs)
|
||||
|
||||
## Styling
|
||||
### Styling
|
||||
|
||||
We use the .NET Foundation and CoreCLR style guidelines [located here](
|
||||
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# Development Life Cycle using VS Code:
|
||||
|
||||
These examples use VS Code, but the idea should be similar across all IDEs as long as you attach to the same processes in the right folder.
|
||||
## Configure
|
||||
|
||||
To successfully start the runner, you need to register it using a repository and a runner registration token.
|
||||
Run `Configure` first to build the source code and set up the runner in `_layout`.
|
||||
Once it's done creating `_layout`, it asks for the url of your repository and your token in the terminal.
|
||||
|
||||
Check [Quickstart](../contribute.md#quickstart-run-a-job-from-a-real-repository) if you don't know how to get this token.
|
||||
|
||||
## Debugging
|
||||
|
||||
Debugging the full lifecycle of a job can be tricky, because there are multiple processes involved.
|
||||
All the configs below can be found in `.vscode/launch.json`.
|
||||
|
||||
## Debug the Listener
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Run [build]",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build runner layout", // use the config called "Run" to launch without rebuild
|
||||
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
|
||||
"args": [
|
||||
"run" // run without args to print usage
|
||||
],
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"console": "integratedTerminal",
|
||||
"requireExactSource": false,
|
||||
}
|
||||
```
|
||||
|
||||
If you launch `Run` or `Run [build]`, it starts a process called `Runner.Listener`.
|
||||
This process will receive any job queued on this repository if the job runs on matching labels (e.g `runs-on: self-hosted`).
|
||||
Once a job is received, a `Runner.Listener` starts a new process of `Runner.Worker`.
|
||||
Since this is a diferent process, you can't use the same debugger session debug it.
|
||||
Instead, a parallel debugging session has to be started, using a different launch config.
|
||||
Luckily, VS Code supports multiple parallel debugging sessions.
|
||||
|
||||
## Debug the Worker
|
||||
|
||||
Because the worker process is usually started by the listener instead of an IDE, debugging it from start to finish can be tricky.
|
||||
For this reason, `Runner.Worker` can be configured to wait for a debugger to be attached before it begins any actual work.
|
||||
|
||||
Set the environment variable `GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER` to `true` or `1` to enable this wait.
|
||||
All worker processes now will wait 20 seconds before they start working on their task.
|
||||
|
||||
This gives enough time to attach a debugger by running `Debug Worker`.
|
||||
If for some reason you have multiple workers running, run the launch config `Attach` instead.
|
||||
Select `Runner.Worker` from the running processes when VS Code prompts for it.
|
||||
@@ -5,12 +5,6 @@
|
||||
## Supported Versions
|
||||
|
||||
- macOS High Sierra (10.13) and later versions
|
||||
|
||||
## Apple Silicon M1
|
||||
|
||||
The runner is currently not supported on devices with an Apple M1 chip.
|
||||
We are waiting for official .NET support. You can read more here about the [current state of support here](https://github.com/orgs/dotnet/projects/18#card-56812463).
|
||||
Current .NET project board about M1 support:
|
||||
https://github.com/orgs/dotnet/projects/18#card-56812463
|
||||
|
||||
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
## Features
|
||||
|
||||
- Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided (#990)
|
||||
|
||||
## Bugs
|
||||
|
||||
- Do not trucate error message from template evaluation (#1038)
|
||||
- Make FileShare ReadWrite (#1033)
|
||||
- Mask secrets with double-quotes when passed to docker command line (#1002)
|
||||
- Delete script files before replacing during update (#984)
|
||||
|
||||
- Fixed an issue where docker containers failed to initialize (#977)
|
||||
|
||||
## Misc
|
||||
|
||||
@@ -49,7 +43,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
|
||||
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
||||
```
|
||||
|
||||
## Linux arm64
|
||||
## Linux arm64 (Pre-release)
|
||||
|
||||
``` bash
|
||||
# Create a folder
|
||||
@@ -60,7 +54,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
|
||||
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
||||
```
|
||||
|
||||
## Linux arm
|
||||
## Linux arm (Pre-release)
|
||||
|
||||
``` bash
|
||||
# Create a folder
|
||||
|
||||
@@ -24,10 +24,10 @@ if /i "%~1" equ "localRun" (
|
||||
rem ********************************************************************************
|
||||
"%~dp0bin\Runner.Listener.exe" run %*
|
||||
|
||||
rem Return codes 3 and 4 mean the runner received an update message.
|
||||
rem Return code 4 means the run once runner received an update message.
|
||||
rem Sleep 5 seconds to wait for the update process finish and run the runner again.
|
||||
if ERRORLEVEL 3 (
|
||||
if ERRORLEVEL 4 (
|
||||
timeout /t 5 /nobreak > NUL
|
||||
"%~dp0run.cmd" %*
|
||||
"%~dp0bin\Runner.Listener.exe" run %*
|
||||
)
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
Trace.Info(nameof(LoadSettings));
|
||||
if (!IsConfigured())
|
||||
{
|
||||
throw new InvalidOperationException("Not configured. Run config.(sh/cmd) to configure the runner.");
|
||||
throw new InvalidOperationException("Not configured");
|
||||
}
|
||||
|
||||
RunnerSettings settings = _store.GetSettings();
|
||||
|
||||
@@ -240,15 +240,6 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
request = await runnerServer.GetAgentRequestAsync(_poolId, jobDispatch.RequestId, CancellationToken.None);
|
||||
}
|
||||
catch (TaskAgentJobNotFoundException ex)
|
||||
{
|
||||
Trace.Error($"Catch job-not-found exception while checking jobrequest {jobDispatch.JobId} status. Cancel running worker right away.");
|
||||
Trace.Error(ex);
|
||||
jobDispatch.WorkerCancellationTokenSource.Cancel();
|
||||
// make sure worker process exit before we return, otherwise we might leave orphan worker process behind.
|
||||
await jobDispatch.WorkerDispatch;
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// we can't even query for the jobrequest from server, something totally busted, stop runner/worker.
|
||||
|
||||
@@ -396,14 +396,7 @@ namespace GitHub.Runner.Listener
|
||||
autoUpdateInProgress = true;
|
||||
var runnerUpdateMessage = JsonUtility.FromString<AgentRefreshMessage>(message.Body);
|
||||
var selfUpdater = HostContext.GetService<ISelfUpdater>();
|
||||
|
||||
#if OS_WINDOWS
|
||||
var restartInteractiveRunner = false;
|
||||
#else
|
||||
var restartInteractiveRunner = !runOnce;
|
||||
#endif
|
||||
|
||||
selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, restartInteractiveRunner, HostContext.RunnerShutdownToken);
|
||||
selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, !runOnce && HostContext.StartupType != StartupType.Service, HostContext.RunnerShutdownToken);
|
||||
Trace.Info("Refresh message received, kick-off selfupdate background process.");
|
||||
}
|
||||
else
|
||||
|
||||
@@ -61,15 +61,14 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
bool EchoOnActionCommand { get; set; }
|
||||
|
||||
bool IsEmbedded { get; }
|
||||
bool InsideComposite { get; }
|
||||
|
||||
ExecutionContext Root { get; }
|
||||
|
||||
// Initialize
|
||||
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
||||
void CancelToken();
|
||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null);
|
||||
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName);
|
||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null);
|
||||
|
||||
// logging
|
||||
long Write(string tag, string message);
|
||||
@@ -100,6 +99,7 @@ namespace GitHub.Runner.Worker
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(IStep step);
|
||||
IStep CreateCompositeStep(string scopeName, IActionRunner step, DictionaryContextData inputsData, Dictionary<string, string> envData);
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -157,9 +157,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public bool EchoOnActionCommand { get; set; }
|
||||
|
||||
// An embedded execution context shares the same record ID, record name, and logger
|
||||
// as its enclosing execution context.
|
||||
public bool IsEmbedded { get; private set; }
|
||||
public bool InsideComposite { get; private set; }
|
||||
|
||||
public TaskResult? Result
|
||||
{
|
||||
@@ -255,7 +253,36 @@ namespace GitHub.Runner.Worker
|
||||
Root.PostJobSteps.Push(step);
|
||||
}
|
||||
|
||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null)
|
||||
/// <summary>
|
||||
/// Helper function used in CompositeActionHandler::RunAsync to
|
||||
/// add a child node, aka a step, to the current job to the Root.JobSteps based on the location.
|
||||
/// </summary>
|
||||
public IStep CreateCompositeStep(
|
||||
string scopeName,
|
||||
IActionRunner step,
|
||||
DictionaryContextData inputsData,
|
||||
Dictionary<string, string> envData)
|
||||
{
|
||||
step.ExecutionContext = Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, insideComposite: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||
step.ExecutionContext.ExpressionValues["steps"] = Global.StepsContext.GetScope(step.ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
// Add the composite action environment variables to each step.
|
||||
#if OS_WINDOWS
|
||||
var envContext = new DictionaryContextData();
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
foreach (var pair in envData)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null)
|
||||
{
|
||||
Trace.Entering();
|
||||
|
||||
@@ -302,20 +329,11 @@ namespace GitHub.Runner.Worker
|
||||
child._logger.Setup(_mainTimelineId, recordId);
|
||||
}
|
||||
|
||||
child.IsEmbedded = isEmbedded;
|
||||
child.InsideComposite = insideComposite;
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An embedded execution context shares the same record ID, record name, logger,
|
||||
/// and a linked cancellation token.
|
||||
/// </summary>
|
||||
public IExecutionContext CreateEmbeddedChild(string scopeName, string contextName)
|
||||
{
|
||||
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
}
|
||||
|
||||
public void Start(string currentOperation = null)
|
||||
{
|
||||
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
|
||||
|
||||
@@ -26,60 +26,65 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
public async Task RunAsync(ActionRunStage stage)
|
||||
{
|
||||
// Validate args
|
||||
// Validate args.
|
||||
Trace.Entering();
|
||||
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
|
||||
ArgUtil.NotNull(Inputs, nameof(Inputs));
|
||||
ArgUtil.NotNull(Data.Steps, nameof(Data.Steps));
|
||||
|
||||
// Resolve action steps
|
||||
var actionSteps = Data.Steps;
|
||||
|
||||
// Create Context Data to reuse for each composite action step
|
||||
var inputsData = new DictionaryContextData();
|
||||
foreach (var i in Inputs)
|
||||
{
|
||||
inputsData[i.Key] = new StringContextData(i.Value);
|
||||
}
|
||||
|
||||
// Initialize Composite Steps List of Steps
|
||||
var compositeSteps = new List<IStep>();
|
||||
|
||||
// Temporary hack until after M271-ish. After M271-ish the server will never send an empty
|
||||
// context name. Generated context names start with "__"
|
||||
var childScopeName = ExecutionContext.GetFullyQualifiedContextName();
|
||||
if (string.IsNullOrEmpty(childScopeName))
|
||||
{
|
||||
childScopeName = $"__{Guid.NewGuid()}";
|
||||
}
|
||||
|
||||
foreach (Pipelines.ActionStep actionStep in actionSteps)
|
||||
{
|
||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
actionRunner.Action = actionStep;
|
||||
actionRunner.Stage = stage;
|
||||
actionRunner.Condition = actionStep.Condition;
|
||||
|
||||
var step = ExecutionContext.CreateCompositeStep(childScopeName, actionRunner, inputsData, Environment);
|
||||
|
||||
// Shallow copy github context
|
||||
var gitHubContext = step.ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
||||
ArgUtil.NotNull(gitHubContext, nameof(gitHubContext));
|
||||
gitHubContext = gitHubContext.ShallowCopy();
|
||||
step.ExecutionContext.ExpressionValues["github"] = gitHubContext;
|
||||
|
||||
// Set GITHUB_ACTION_PATH
|
||||
step.ExecutionContext.SetGitHubContext("action_path", ActionDirectory);
|
||||
|
||||
compositeSteps.Add(step);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Inputs of the composite step
|
||||
var inputsData = new DictionaryContextData();
|
||||
foreach (var i in Inputs)
|
||||
{
|
||||
inputsData[i.Key] = new StringContextData(i.Value);
|
||||
}
|
||||
// This is where we run each step.
|
||||
await RunStepsAsync(compositeSteps);
|
||||
|
||||
// Temporary hack until after M271-ish. After M271-ish the server will never send an empty
|
||||
// context name. Generated context names start with "__"
|
||||
var childScopeName = ExecutionContext.GetFullyQualifiedContextName();
|
||||
if (string.IsNullOrEmpty(childScopeName))
|
||||
{
|
||||
childScopeName = $"__{Guid.NewGuid()}";
|
||||
}
|
||||
|
||||
// Create embedded steps
|
||||
var embeddedSteps = new List<IStep>();
|
||||
foreach (Pipelines.ActionStep stepData in Data.Steps)
|
||||
{
|
||||
var step = HostContext.CreateService<IActionRunner>();
|
||||
step.Action = stepData;
|
||||
step.Stage = stage;
|
||||
step.Condition = stepData.Condition;
|
||||
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepData.ContextName);
|
||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||
step.ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(childScopeName);
|
||||
|
||||
// Shallow copy github context
|
||||
var gitHubContext = step.ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
||||
ArgUtil.NotNull(gitHubContext, nameof(gitHubContext));
|
||||
gitHubContext = gitHubContext.ShallowCopy();
|
||||
step.ExecutionContext.ExpressionValues["github"] = gitHubContext;
|
||||
|
||||
// Set GITHUB_ACTION_PATH
|
||||
step.ExecutionContext.SetGitHubContext("action_path", ActionDirectory);
|
||||
|
||||
embeddedSteps.Add(step);
|
||||
}
|
||||
|
||||
// Run embedded steps
|
||||
await RunStepsAsync(embeddedSteps);
|
||||
|
||||
// Set outputs
|
||||
// Get the pointer of the correct "steps" object and pass it to the ExecutionContext so that we can process the outputs correctly
|
||||
ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(childScopeName);
|
||||
ProcessOutputs();
|
||||
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
ProcessCompositeActionOutputs();
|
||||
|
||||
ExecutionContext.Global.StepsContext.ClearScope(childScopeName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -91,7 +96,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessOutputs()
|
||||
private void ProcessCompositeActionOutputs()
|
||||
{
|
||||
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
|
||||
|
||||
@@ -108,57 +113,69 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
evaluateContext[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
// Evaluate outputs
|
||||
// Get the evluated composite outputs' values mapped to the outputs named
|
||||
DictionaryContextData actionOutputs = actionManifestManager.EvaluateCompositeOutputs(ExecutionContext, Data.Outputs, evaluateContext);
|
||||
|
||||
// Set outputs
|
||||
//
|
||||
// Each pair is structured like:
|
||||
// {
|
||||
// "the-output-name": {
|
||||
// "description": "",
|
||||
// "value": "the value"
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
// Set the outputs for the outputs object in the whole composite action
|
||||
// Each pair is structured like this
|
||||
// We ignore "description" for now
|
||||
// {
|
||||
// "the-output-name": {
|
||||
// "description": "",
|
||||
// "value": "the value"
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
foreach (var pair in actionOutputs)
|
||||
{
|
||||
var outputName = pair.Key;
|
||||
var outputDefinition = pair.Value as DictionaryContextData;
|
||||
if (outputDefinition.TryGetValue("value", out var val))
|
||||
var outputsName = pair.Key;
|
||||
var outputsAttributes = pair.Value as DictionaryContextData;
|
||||
outputsAttributes.TryGetValue("value", out var val);
|
||||
|
||||
if (val != null)
|
||||
{
|
||||
var outputValue = val.AssertString("output value");
|
||||
ExecutionContext.SetOutput(outputName, outputValue.Value, out _);
|
||||
var outputsValue = val as StringContextData;
|
||||
// Set output in the whole composite scope.
|
||||
if (!String.IsNullOrEmpty(outputsValue))
|
||||
{
|
||||
ExecutionContext.SetOutput(outputsName, outputsValue, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecutionContext.SetOutput(outputsName, "", out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunStepsAsync(List<IStep> embeddedSteps)
|
||||
private async Task RunStepsAsync(List<IStep> compositeSteps)
|
||||
{
|
||||
ArgUtil.NotNull(embeddedSteps, nameof(embeddedSteps));
|
||||
ArgUtil.NotNull(compositeSteps, nameof(compositeSteps));
|
||||
|
||||
foreach (IStep step in embeddedSteps)
|
||||
// The parent StepsRunner of the whole Composite Action Step handles the cancellation stuff already.
|
||||
foreach (IStep step in compositeSteps)
|
||||
{
|
||||
Trace.Info($"Processing embedded step: DisplayName='{step.DisplayName}'");
|
||||
Trace.Info($"Processing composite step: DisplayName='{step.DisplayName}'");
|
||||
|
||||
// Initialize env context
|
||||
Trace.Info("Initialize Env context for embedded step");
|
||||
step.ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
|
||||
|
||||
// Populate env context for each step
|
||||
Trace.Info("Initialize Env context for step");
|
||||
#if OS_WINDOWS
|
||||
var envContext = new DictionaryContextData();
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
// Merge global env
|
||||
// Global env
|
||||
foreach (var pair in ExecutionContext.Global.EnvironmentVariables)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
|
||||
// Merge composite-step env
|
||||
if (ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
|
||||
// Stomps over with outside step env
|
||||
if (step.ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
|
||||
{
|
||||
#if OS_WINDOWS
|
||||
var dict = envContextData as DictionaryContextData;
|
||||
@@ -171,11 +188,13 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
var actionStep = step as IActionRunner;
|
||||
|
||||
try
|
||||
{
|
||||
// Evaluate and merge embedded-step env
|
||||
// Evaluate and merge action's env block to env context
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, Common.Util.VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var env in actionEnvironment)
|
||||
@@ -185,28 +204,39 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Evaluation error
|
||||
Trace.Info("Caught exception from expression for embedded step.env");
|
||||
// fail the step since there is an evaluate error.
|
||||
Trace.Info("Caught exception in Composite Steps Runner from expression for step.env");
|
||||
// evaluateStepEnvFailed = true;
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Complete(TaskResult.Failed);
|
||||
}
|
||||
|
||||
await RunStepAsync(step);
|
||||
|
||||
// Check failed or canceled
|
||||
// Directly after the step, check if the step has failed or cancelled
|
||||
// If so, return that to the output
|
||||
if (step.ExecutionContext.Result == TaskResult.Failed || step.ExecutionContext.Result == TaskResult.Canceled)
|
||||
{
|
||||
ExecutionContext.Result = step.ExecutionContext.Result;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Add compat for other types of steps.
|
||||
}
|
||||
// Completion Status handled by StepsRunner for the whole Composite Action Step
|
||||
}
|
||||
|
||||
private async Task RunStepAsync(IStep step)
|
||||
{
|
||||
Trace.Info($"Starting: {step.DisplayName}");
|
||||
// Start the step.
|
||||
Trace.Info("Starting the step.");
|
||||
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
|
||||
|
||||
// TODO: Fix for Step Level Timeout Attributes for an individual Composite Run Step
|
||||
// For now, we are not going to support this for an individual composite run step
|
||||
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
|
||||
await Common.Util.EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);
|
||||
|
||||
try
|
||||
@@ -231,7 +261,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the error and fail the step
|
||||
// Log the error and fail the step.
|
||||
Trace.Error($"Caught exception from step: {ex}");
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Result = TaskResult.Failed;
|
||||
@@ -244,7 +274,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
|
||||
Trace.Info($"Step result: {step.ExecutionContext.Result}");
|
||||
step.ExecutionContext.Debug($"Finished: {step.DisplayName}");
|
||||
|
||||
// Complete the step context.
|
||||
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +97,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
Encoding outputEncoding = null;
|
||||
#endif
|
||||
|
||||
// Remove environment variable that may cause conflicts with the node within the runner.
|
||||
Environment.Remove("NODE_ICU_DATA"); // https://github.com/actions/runner/issues/795
|
||||
|
||||
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
||||
using (var stderrManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// We don't want to display the internal workings if composite (similar/equivalent information can be found in debug)
|
||||
void writeDetails(string message)
|
||||
{
|
||||
if (ExecutionContext.IsEmbedded)
|
||||
if (ExecutionContext.InsideComposite)
|
||||
{
|
||||
ExecutionContext.Debug(message);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
firstLine = firstLine.Substring(0, firstNewLine);
|
||||
}
|
||||
|
||||
writeDetails(ExecutionContext.IsEmbedded ? $"Run {firstLine}" : $"##[group]Run {firstLine}");
|
||||
writeDetails(ExecutionContext.InsideComposite ? $"Run {firstLine}" : $"##[group]Run {firstLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -138,7 +138,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
writeDetails(ExecutionContext.IsEmbedded ? "" : "##[endgroup]");
|
||||
writeDetails(ExecutionContext.InsideComposite ? "" : "##[endgroup]");
|
||||
}
|
||||
|
||||
public async Task RunAsync(ActionRunStage stage)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -19,16 +19,11 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public static async Task<int> MainAsync(IHostContext context, string[] args)
|
||||
{
|
||||
Tracing trace = context.GetTrace(nameof(GitHub.Runner.Worker));
|
||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER")))
|
||||
{
|
||||
await WaitForDebugger(trace);
|
||||
}
|
||||
|
||||
// We may want to consider registering this handler in Worker.cs, similiar to the unloading/SIGTERM handler
|
||||
//ITerminal registers a CTRL-C handler, which keeps the Runner.Worker process running
|
||||
//and lets the Runner.Listener handle gracefully the exit.
|
||||
var term = context.GetService<ITerminal>();
|
||||
Tracing trace = context.GetTrace(nameof(GitHub.Runner.Worker));
|
||||
try
|
||||
{
|
||||
trace.Info($"Version: {BuildConstants.RunnerPackage.Version}");
|
||||
@@ -69,25 +64,5 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Runner.Worker is started by Runner.Listener in a separate process,
|
||||
/// so the two can't be debugged in the same session.
|
||||
/// This method halts the Runner.Worker process until a debugger is attached,
|
||||
/// allowing a developer to debug Runner.Worker from start to finish.
|
||||
/// </summary>
|
||||
private static async Task WaitForDebugger(Tracing trace)
|
||||
{
|
||||
trace.Info($"Waiting for a debugger to be attached. Edit the 'GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER' environment variable to toggle this feature.");
|
||||
int waitInSeconds = 20;
|
||||
while (!Debugger.IsAttached && waitInSeconds-- > 0)
|
||||
{
|
||||
trace.Info($"Waiting for a debugger to be attached. {waitInSeconds} seconds left.");
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
Debugger.Break();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,11 @@ using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the "steps" context. The "steps" context is used to track individual steps
|
||||
/// "outcome", "conclusion", and "outputs".
|
||||
/// </summary>
|
||||
public sealed class StepsContext
|
||||
{
|
||||
private static readonly Regex _propertyRegex = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled);
|
||||
private readonly DictionaryContextData _contextData = new DictionaryContextData();
|
||||
|
||||
/// <summary>
|
||||
/// Clears memory for a composite action's isolated "steps" context, after the action
|
||||
/// is finished executing.
|
||||
/// </summary>
|
||||
public void ClearScope(string scopeName)
|
||||
{
|
||||
if (_contextData.TryGetValue(scopeName, out _))
|
||||
@@ -31,14 +23,6 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the "steps" context for a given scope. The root steps in a workflow use the
|
||||
/// default "steps" context (i.e. scopeName="").
|
||||
///
|
||||
/// An isolated "steps" context is created for each composite action. All child steps
|
||||
/// within a composite action, share an isolated "steps" context. The scope name matches
|
||||
/// the composite action's fully qualified context name.
|
||||
/// </summary>
|
||||
public DictionaryContextData GetScope(string scopeName)
|
||||
{
|
||||
if (scopeName == null)
|
||||
|
||||
@@ -82,21 +82,24 @@ namespace GitHub.Runner.Worker
|
||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
|
||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<HashFilesFunction>(PipelineTemplateConstants.HashFiles, 1, byte.MaxValue));
|
||||
|
||||
// Expression values
|
||||
step.ExecutionContext.ExpressionValues["steps"] = step.ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
|
||||
|
||||
// Populate env context for each step
|
||||
Trace.Info("Initialize Env context for step");
|
||||
#if OS_WINDOWS
|
||||
var envContext = new DictionaryContextData();
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
// Merge global env
|
||||
// Global env
|
||||
foreach (var pair in step.ExecutionContext.Global.EnvironmentVariables)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
bool evaluateStepEnvFailed = false;
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
@@ -105,7 +108,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
try
|
||||
{
|
||||
// Evaluate and merge step env
|
||||
// Evaluate and merge action's env block to env context
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var env in actionEnvironment)
|
||||
@@ -115,7 +118,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fail the step since there is an evaluate error
|
||||
// fail the step since there is an evaluate error.
|
||||
Trace.Info("Caught exception from expression for step.env");
|
||||
evaluateStepEnvFailed = true;
|
||||
step.ExecutionContext.Error(ex);
|
||||
@@ -133,7 +136,7 @@ namespace GitHub.Runner.Worker
|
||||
// Test the condition again. The job was canceled after the condition was originally evaluated.
|
||||
jobCancelRegister = jobContext.CancellationToken.Register(() =>
|
||||
{
|
||||
// Mark job as cancelled
|
||||
// mark job as cancelled
|
||||
jobContext.Result = TaskResult.Canceled;
|
||||
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
||||
|
||||
@@ -154,7 +157,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Cancel the step since we get exception while re-evaluate step condition
|
||||
// Cancel the step since we get exception while re-evaluate step condition.
|
||||
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
||||
step.ExecutionContext.Error(ex);
|
||||
}
|
||||
@@ -162,7 +165,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (!conditionReTestResult)
|
||||
{
|
||||
// Cancel the step
|
||||
// Cancel the step.
|
||||
Trace.Info("Cancel current running step.");
|
||||
step.ExecutionContext.CancelToken();
|
||||
}
|
||||
@@ -172,13 +175,13 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (jobContext.Result != TaskResult.Canceled)
|
||||
{
|
||||
// Mark job as cancelled
|
||||
// mark job as cancelled
|
||||
jobContext.Result = TaskResult.Canceled;
|
||||
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate condition
|
||||
// Evaluate condition.
|
||||
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
||||
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
||||
var conditionResult = false;
|
||||
@@ -203,21 +206,22 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
// no evaluate error but condition is false
|
||||
if (!conditionResult && conditionEvaluateError == null)
|
||||
{
|
||||
// Condition is false
|
||||
// Condition == false
|
||||
Trace.Info("Skipping step due to condition evaluation.");
|
||||
CompleteStep(step, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
|
||||
}
|
||||
else if (conditionEvaluateError != null)
|
||||
{
|
||||
// Condition error
|
||||
// fail the step since there is an evaluate error.
|
||||
step.ExecutionContext.Error(conditionEvaluateError);
|
||||
CompleteStep(step, TaskResult.Failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run the step
|
||||
// Run the step.
|
||||
await RunStepAsync(step, jobContext.CancellationToken);
|
||||
CompleteStep(step);
|
||||
}
|
||||
@@ -232,7 +236,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
// Update the job result
|
||||
// Update the job result.
|
||||
if (step.ExecutionContext.Result == TaskResult.Failed)
|
||||
{
|
||||
Trace.Info($"Update job result with current step result '{step.ExecutionContext.Result}'.");
|
||||
@@ -258,7 +262,7 @@ namespace GitHub.Runner.Worker
|
||||
step.ExecutionContext.UpdateTimelineRecordDisplayName(actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
// Start the step
|
||||
// Start the step.
|
||||
Trace.Info("Starting the step.");
|
||||
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
|
||||
|
||||
@@ -299,7 +303,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log the exception and cancel the step
|
||||
// Log the exception and cancel the step.
|
||||
Trace.Error($"Caught cancellation exception from step: {ex}");
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Result = TaskResult.Canceled;
|
||||
@@ -307,7 +311,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the error and fail the step
|
||||
// Log the error and fail the step.
|
||||
Trace.Error($"Caught exception from step: {ex}");
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Result = TaskResult.Failed;
|
||||
@@ -319,7 +323,7 @@ namespace GitHub.Runner.Worker
|
||||
step.ExecutionContext.Result = TaskResultUtil.MergeTaskResults(step.ExecutionContext.Result, step.ExecutionContext.CommandResult.Value);
|
||||
}
|
||||
|
||||
// Fixup the step result if ContinueOnError
|
||||
// Fixup the step result if ContinueOnError.
|
||||
if (step.ExecutionContext.Result == TaskResult.Failed)
|
||||
{
|
||||
var continueOnError = false;
|
||||
@@ -344,7 +348,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
Trace.Info($"Step result: {step.ExecutionContext.Result}");
|
||||
|
||||
// Complete the step context
|
||||
// Complete the step context.
|
||||
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
"outputs": {
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "output-definition"
|
||||
"loose-value-type": "outputs-attributes"
|
||||
}
|
||||
},
|
||||
"output-definition": {
|
||||
"outputs-attributes": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"description": "string",
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.278.0
|
||||
2.277.1
|
||||
|
||||
Reference in New Issue
Block a user