GitHub/Workflow: Difference between revisions
(19 intermediate revisions by the same user not shown) | |||
Line 29: | Line 29: | ||
steps: | steps: | ||
- run: hostname | - run: hostname | ||
</pre> | |||
== Change Run Name with Input == | |||
<pre> | |||
name: Nodecheck | |||
run-name: Nodecheck for ${{ inputs.RUNNER_LABEL }} | |||
on: | |||
workflow_dispatch: | |||
inputs: | |||
RUNNER_LABEL: | |||
description: Name of runner to target | |||
required: true | |||
type: string | |||
workflow_call: | |||
inputs: | |||
RUNNER_LABEL: | |||
required: true | |||
type: string | |||
description: Name of runner to target | |||
jobs: | |||
run: | |||
uses: some-org/some-repo/.github/workflows/nodecheck.yaml@latest | |||
with: | |||
RUNNER_LABEL: ${{ inputs.RUNNER_LABEL }} | |||
secrets: inherit | |||
... | |||
</pre> | |||
== On Pull Request == | |||
<pre> | |||
name: On Pull Request | |||
on: | |||
pull_request: | |||
types: [opened, reopened] | |||
jobs: | |||
test: | |||
runs-on: [ubuntu-latest] | |||
steps: | |||
- name: do something | |||
shell: bash | |||
run: | | |||
echo "hi" | |||
</pre> | |||
== On Label Change == | |||
Note: Must have event in | |||
<pre> | |||
name: On Label | |||
on: | |||
pull_request: | |||
types: | |||
- labeled | |||
jobs: | |||
label-job: | |||
if: "github.event.label.name == 'run-mylabel'" | |||
uses: myorg/myrepo/.github/workflows/dolabel.yaml@v2 | |||
with: | |||
VAR1: VAR1 | |||
secrets: inherit | |||
</pre> | </pre> | ||
Line 58: | Line 122: | ||
== Using Job Output == | == Using Job Output == | ||
if you want to export the job output from the workflow: | |||
<pre> | |||
name: CI Precommit Automation Pipeline - Get Builds | |||
on: | |||
workflow_call: | |||
inputs: | |||
... | |||
outputs: | |||
output1: ${{ jobs.job1.outputs.output1 }} | |||
</pre> | |||
job outputs: | |||
<pre> | <pre> | ||
jobs: | jobs: | ||
Line 71: | Line 147: | ||
- id: step2 | - id: step2 | ||
run: echo "test=world" >> "$GITHUB_OUTPUT" | run: echo "test=world" >> "$GITHUB_OUTPUT" | ||
- id: step3 | |||
run: echo "I see step1: ${{ steps.step1.outputs.test }} | |||
job2: | job2: | ||
runs-on: ubuntu-latest | runs-on: ubuntu-latest | ||
Line 82: | Line 160: | ||
ref: Defining outputs for jobs - GitHub Enterprise Cloud Docs - https://docs.github.com/en/enterprise-cloud@latest/actions/using-jobs/defining-outputs-for-jobs | ref: Defining outputs for jobs - GitHub Enterprise Cloud Docs - https://docs.github.com/en/enterprise-cloud@latest/actions/using-jobs/defining-outputs-for-jobs | ||
Multi-line output: <ref>https://github.com/github/docs/issues/21529</ref> | |||
<pre> | |||
run: | | |||
echo 'JSON_RESPONSE<<EOF' >> $GITHUB_OUTPUT | |||
curl https://example.lab >> $GITHUB_OUTPUT | |||
echo 'EOF' >> $GITHUB_OUTPUT | |||
</pre> | |||
=== Python version job output === | |||
Python version: <ref>https://github.com/orgs/community/discussions/28146</ref> | |||
<pre> | |||
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: | |||
print(f'{SOME}={THING}', file=fh) | |||
</pre> | |||
Function version: | |||
<pre> | |||
def set_output(name, value): | |||
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: | |||
print(f'{name}={value}', file=fh) | |||
</pre> | |||
Multi lined version: | |||
<pre> | |||
import uuid | |||
def set_multiline_output(name, value): | |||
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: | |||
delimiter = uuid.uuid1() | |||
print(f'{name}<<{delimiter}', file=fh) | |||
print(value, file=fh) | |||
print(delimiter, file=fh) | |||
# call example: | |||
set_multiline_output("value", my_multiline_string) | |||
</pre> | |||
== Dynamic Runs-on == | == Dynamic Runs-on == | ||
Line 137: | Line 253: | ||
* How to Dynamically assign the runs-on value in the github actions - Stack Overflow - https://stackoverflow.com/questions/73241812/how-to-dynamically-assign-the-runs-on-value-in-the-github-actions | * How to Dynamically assign the runs-on value in the github actions - Stack Overflow - https://stackoverflow.com/questions/73241812/how-to-dynamically-assign-the-runs-on-value-in-the-github-actions | ||
* Github Actions: How use strategy/matrix with script - Stack Overflow - https://stackoverflow.com/questions/59977364/github-actions-how-use-strategy-matrix-with-script | * Github Actions: How use strategy/matrix with script - Stack Overflow - https://stackoverflow.com/questions/59977364/github-actions-how-use-strategy-matrix-with-script | ||
--- todo --- | |||
ideas: | |||
runs-on: ${{ fromJson(format('[{0}]', github.event.inputs.LABELS)) }} | |||
this will work if labels is a string of quoted labels: "a","b" | |||
Sadness - if the actions expressions supported a "replace()" function, could have the comma replace with a "," | |||
== Dynamic Jobs == | == Dynamic Jobs == | ||
<pre> | |||
{ "include": [ | |||
{"runson": ["CI", "MORE"], | |||
"task": "joe"}, | |||
{"runson": ["CI", "LESS"], | |||
"task": "bob"} | |||
] | |||
} | |||
</pre> | |||
<pre> | <pre> | ||
Line 167: | Line 302: | ||
- run: echo ${{ matrix.runson }} | - run: echo ${{ matrix.runson }} | ||
- run: echo ${{ matrix.task }} | - run: echo ${{ matrix.task }} | ||
</pre> | |||
== Official Dynamic Job Examples == | |||
Reference: https://docs.github.com/en/actions/learn-github-actions/expressions#example-returning-a-json-object | |||
<pre> | |||
name: build | |||
on: push | |||
jobs: | |||
job1: | |||
runs-on: ubuntu-latest | |||
outputs: | |||
matrix: ${{ steps.set-matrix.outputs.matrix }} | |||
steps: | |||
- id: set-matrix | |||
run: echo "matrix={\"include\":[{\"project\":\"foo\",\"config\":\"Debug\"},{\"project\":\"bar\",\"config\":\"Release\"}]}" >> $GITHUB_OUTPUT | |||
job2: | |||
needs: job1 | |||
runs-on: ubuntu-latest | |||
strategy: | |||
matrix: ${{ fromJSON(needs.job1.outputs.matrix) }} | |||
steps: | |||
- run: echo "Matrix - Project ${{ matrix.project }}, Config ${{ matrix.config }}" | |||
</pre> | |||
and | |||
<pre> | |||
jobs: | |||
example_matrix: | |||
strategy: | |||
matrix: | |||
os: [ubuntu-22.04, ubuntu-20.04] | |||
version: [10, 12, 14] | |||
runs-on: ${{ matrix.os }} | |||
steps: | |||
- uses: actions/setup-node@v4 | |||
with: | |||
node-version: ${{ matrix.version }} | |||
</pre> | </pre> | ||
Line 245: | Line 420: | ||
run: echo $MESSAGE | run: echo $MESSAGE | ||
</pre> | </pre> | ||
== Job Timeout == | |||
Workflow syntax - jobs - timeout-minutes https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes | |||
<pre> | |||
my-job: | |||
runs-on: ubuntu-latest | |||
timeout-minutes: 30 | |||
</pre> | |||
jobs.<job_id>.timeout-minutes | |||
The maximum number of minutes to let a job run before GitHub automatically cancels it. Default: 360 | |||
If the timeout exceeds the job execution time limit for the runner, the job will be canceled when the execution time limit is met instead. For more information about job execution time limits, see "Usage limits, billing, and administration" for GitHub-hosted runners and "About self-hosted runners" for self-hosted runner usage limits. | |||
Usage Limits: | |||
There are some limits on GitHub Actions usage when using GitHub-hosted runners. These limits are subject to change. <ref>https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits</ref> | |||
* Job execution time - Each job in a workflow can run for up to 6 hours of execution time. If a job reaches this limit, the job is terminated and fails to complete. | |||
There are some limits on GitHub Actions usage when using self-hosted runners. These limits are subject to change. <ref>https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits</ref> | |||
* Job execution time - Each job in a workflow can run for up to 5 days of execution time. If a job reaches this limit, the job is terminated and fails to complete. | |||
=== Step Timeout === | |||
jobs.<job_id>.steps[*].timeout-minutes | |||
The maximum number of minutes to run the step before killing the process. | |||
== Status Check Functions == | |||
Expressions - GitHub Docs | |||
https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions | |||
=== success === | |||
<pre> | |||
steps: | |||
... | |||
- name: The job has succeeded | |||
if: ${{ success() }} | |||
</pre> | |||
=== failure === | |||
Returns true when any previous step of a job fails. | |||
<pre> | |||
steps: | |||
... | |||
- name: The job has failed | |||
if: ${{ failure() }} | |||
</pre> | |||
with step conclusion check: | |||
<pre> | |||
steps: | |||
... | |||
- name: Failing step | |||
id: demo | |||
run: exit 1 | |||
- name: The demo step has failed | |||
if: ${{ failure() && steps.demo.conclusion == 'failure' }} | |||
</pre> | |||
=== always === | |||
Causes the step to always execute, and returns true, even when canceled. The always expression is best used at the step level or on tasks that you expect to run even when a job is canceled. | |||
<pre> | |||
if: ${{ always() }} | |||
</pre> | |||
=== cancelled === | |||
Returns true if the workflow was canceled. | |||
<pre> | |||
if: ${{ cancelled() }} | |||
</pre> | |||
=== Pass Status to Step === | |||
Use: | |||
steps.<id>.outcome != 'success' | |||
<pre> | |||
- name: Check status step | |||
id: check-step | |||
if: success() | |||
shell: bash | |||
run: | | |||
echo "Workflow successful" | |||
- name: The step to call the action | |||
if: always() | |||
uses: ./path/to/action | |||
with: | |||
some-token: ${{ inputs.some-token }} | |||
some-value: ${{ inputs.some-value }} | |||
workflow-status: ${{ steps.check-step.outcome }} | |||
</pre> | |||
ref: https://stackoverflow.com/questions/75987303/how-to-pass-success-or-failure-to-a-github-action-from-a-workflow | |||
== keywords == | == keywords == |
Latest revision as of 21:05, 26 July 2024
Starter Workflows
Quickstart for GitHub Actions - GitHub Docs https://docs.github.com/en/actions/quickstart
actions/starter-workflows: Accelerating new GitHub Actions workflows https://github.com/actions/starter-workflows
GitHub Marketplace - Actions https://github.com/marketplace?category=&query=&type=actions&verification=
Manual Run
manual.yaml:
name: Manual Run run-name: Test on: workflow_dispatch: inputs: LABEL: required: true type: string description: label jobs: run: runs-on: ${{ inputs.LABEL }} steps: - run: hostname
Change Run Name with Input
name: Nodecheck run-name: Nodecheck for ${{ inputs.RUNNER_LABEL }} on: workflow_dispatch: inputs: RUNNER_LABEL: description: Name of runner to target required: true type: string workflow_call: inputs: RUNNER_LABEL: required: true type: string description: Name of runner to target jobs: run: uses: some-org/some-repo/.github/workflows/nodecheck.yaml@latest with: RUNNER_LABEL: ${{ inputs.RUNNER_LABEL }} secrets: inherit ...
On Pull Request
name: On Pull Request on: pull_request: types: [opened, reopened] jobs: test: runs-on: [ubuntu-latest] steps: - name: do something shell: bash run: | echo "hi"
On Label Change
Note: Must have event in
name: On Label on: pull_request: types: - labeled jobs: label-job: if: "github.event.label.name == 'run-mylabel'" uses: myorg/myrepo/.github/workflows/dolabel.yaml@v2 with: VAR1: VAR1 secrets: inherit
Basic Workflow
name: GitHub Actions Demo run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 on: [push] jobs: Explore-GitHub-Actions: runs-on: ubuntu-latest steps: - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." - name: Check out repository code uses: actions/checkout@v3 - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." - run: echo "🖥️ The workflow is now ready to test your code on the runner." - name: List files in the repository run: | ls ${{ github.workspace }} - run: echo "🍏 This job's status is ${{ job.status }}."
ref: https://docs.github.com/en/actions/quickstart
Using Job Output
if you want to export the job output from the workflow:
name: CI Precommit Automation Pipeline - Get Builds on: workflow_call: inputs: ... outputs: output1: ${{ jobs.job1.outputs.output1 }}
job outputs:
jobs: job1: runs-on: ubuntu-latest # Map a step output to a job output outputs: output1: ${{ steps.step1.outputs.test }} output2: ${{ steps.step2.outputs.test }} steps: - id: step1 run: echo "test=hello" >> "$GITHUB_OUTPUT" - id: step2 run: echo "test=world" >> "$GITHUB_OUTPUT" - id: step3 run: echo "I see step1: ${{ steps.step1.outputs.test }} job2: runs-on: ubuntu-latest needs: job1 steps: - env: OUTPUT1: ${{needs.job1.outputs.output1}} OUTPUT2: ${{needs.job1.outputs.output2}} run: echo "$OUTPUT1 $OUTPUT2"
ref: Defining outputs for jobs - GitHub Enterprise Cloud Docs - https://docs.github.com/en/enterprise-cloud@latest/actions/using-jobs/defining-outputs-for-jobs
Multi-line output: [1]
run: | echo 'JSON_RESPONSE<<EOF' >> $GITHUB_OUTPUT curl https://example.lab >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT
Python version job output
Python version: [2]
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: print(f'{SOME}={THING}', file=fh)
Function version:
def set_output(name, value): with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: print(f'{name}={value}', file=fh)
Multi lined version:
import uuid def set_multiline_output(name, value): with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: delimiter = uuid.uuid1() print(f'{name}<<{delimiter}', file=fh) print(value, file=fh) print(delimiter, file=fh) # call example: set_multiline_output("value", my_multiline_string)
Dynamic Runs-on
Dynamic from Input:
name: Dynamic Labels run-name: Dynamic Labels on: workflow_dispatch: inputs: LABELS: required: false type: string jobs: run: # runs-on: ${{ fromJSON( '["CI"]' ) }} # This works # runs-on: ${{ fromJSON( '["CI", "MORE"]' ) }} # This works runs-on: ${{ fromJSON( inputs.LABELS ) }} # This works if formatted like: ["CI", "MORE"] # runs-on: ${{ inputs.LABELS }} # this works for single label # runs-on: [ CI, MORE ] # this works steps: - run: hostname
Dynamic from script:
name: Dynamic Script run-name: Test on: workflow_dispatch: inputs: Go: required: false type: string jobs: job1: runs-on: ubuntu-latest outputs: RUNS: ${{ steps.set-RUNS.outputs.RUNS }} steps: - id: set-RUNS run: echo "RUNS=[\"CI\",\"MORE\"]" >> $GITHUB_OUTPUT job2: needs: job1 runs-on: ${{fromJSON(needs.job1.outputs.RUNS)}} steps: - run: hostname
ref:
- How to Dynamically assign the runs-on value in the github actions - Stack Overflow - https://stackoverflow.com/questions/73241812/how-to-dynamically-assign-the-runs-on-value-in-the-github-actions
- Github Actions: How use strategy/matrix with script - Stack Overflow - https://stackoverflow.com/questions/59977364/github-actions-how-use-strategy-matrix-with-script
--- todo ---
ideas:
runs-on: ${{ fromJson(format('[{0}]', github.event.inputs.LABELS)) }}
this will work if labels is a string of quoted labels: "a","b"
Sadness - if the actions expressions supported a "replace()" function, could have the comma replace with a ","
Dynamic Jobs
{ "include": [ {"runson": ["CI", "MORE"], "task": "joe"}, {"runson": ["CI", "LESS"], "task": "bob"} ] }
name: Dynamic Jobs run-name: Test on: workflow_dispatch: inputs: Go: required: false type: string jobs: generate: runs-on: ubuntu-latest outputs: MATRIX: ${{ steps.set-MATRIX.outputs.MATRIX }} steps: - id: set-MATRIX run: echo "MATRIX={\"include\":[{\"runson\":[\"CI\",\"MORE\"],\"task\":\"joe\"},{\"runson\":[\"CI\",\"LESS\"],\"task\":\"bob\"}]}" >> $GITHUB_OUTPUT jober: needs: generate runs-on: ${{ matrix.runson }} strategy: matrix: ${{fromJson(needs.generate.outputs.matrix)}} steps: - run: hostname - run: echo ${{ matrix.runson }} - run: echo ${{ matrix.task }}
Official Dynamic Job Examples
Reference: https://docs.github.com/en/actions/learn-github-actions/expressions#example-returning-a-json-object
name: build on: push jobs: job1: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix run: echo "matrix={\"include\":[{\"project\":\"foo\",\"config\":\"Debug\"},{\"project\":\"bar\",\"config\":\"Release\"}]}" >> $GITHUB_OUTPUT job2: needs: job1 runs-on: ubuntu-latest strategy: matrix: ${{ fromJSON(needs.job1.outputs.matrix) }} steps: - run: echo "Matrix - Project ${{ matrix.project }}, Config ${{ matrix.config }}"
and
jobs: example_matrix: strategy: matrix: os: [ubuntu-22.04, ubuntu-20.04] version: [10, 12, 14] runs-on: ${{ matrix.os }} steps: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.version }}
Remotely Trigger Workflow - repository_dispatch
on: repository_dispatch:
---
on: repository_dispatch: types: [some_event_type]
curl -X POST https://api.github.com/repos/OWNER/REPO/dispatches -H "Authorization: Bearer ${GH_TOKEN}" -d '{"event_type":"some_event_type"}'
---
curl -X POST https://api.github.com/repos/ORG/REPO/dispatches -d '{"event_type":"some_event_type"}' # -d or --data, -X or --request # -H "Accept: application/vnd.github.v3+json" # -H "Accept: application/vnd.github+json" # You will most likely need to also pass along auth: (unless you are silly and on a publicly executable workflow?) # -H "Authorization: Bearer $GITHUB_TOKEN"
curl -L \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer <YOUR-TOKEN>" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/OWNER/REPO/dispatches \ -d '{"event_type":"on-demand-test","client_payload":{"unit":false,"integration":true}}'
---
if you want to trigger from some other branch: {...,'ref':'other_branch_or_ref'},...}
---
repository_dispatch - Events that trigger workflows - GitHub Docs https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch
repository_dispatch - Repositories - GitHub Docs https://docs.github.com/rest/repos/repos#create-a-repository-dispatch-event
---
If you pass this payload through, you can limit the workflow, and access it in the workflow:
{ "event_type": "test_result", "client_payload": { "passed": false, "message": "Error: timeout" } }
workflow:
on: repository_dispatch: types: [test_result] jobs: run_if_failure: if: ${{ !github.event.client_payload.passed }} runs-on: ubuntu-latest steps: - env: MESSAGE: ${{ github.event.client_payload.message }} run: echo $MESSAGE
Job Timeout
Workflow syntax - jobs - timeout-minutes https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes
my-job: runs-on: ubuntu-latest timeout-minutes: 30
jobs.<job_id>.timeout-minutes The maximum number of minutes to let a job run before GitHub automatically cancels it. Default: 360
If the timeout exceeds the job execution time limit for the runner, the job will be canceled when the execution time limit is met instead. For more information about job execution time limits, see "Usage limits, billing, and administration" for GitHub-hosted runners and "About self-hosted runners" for self-hosted runner usage limits.
Usage Limits:
There are some limits on GitHub Actions usage when using GitHub-hosted runners. These limits are subject to change. [3]
- Job execution time - Each job in a workflow can run for up to 6 hours of execution time. If a job reaches this limit, the job is terminated and fails to complete.
There are some limits on GitHub Actions usage when using self-hosted runners. These limits are subject to change. [4]
- Job execution time - Each job in a workflow can run for up to 5 days of execution time. If a job reaches this limit, the job is terminated and fails to complete.
Step Timeout
jobs.<job_id>.steps[*].timeout-minutes The maximum number of minutes to run the step before killing the process.
Status Check Functions
Expressions - GitHub Docs https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions
success
steps: ... - name: The job has succeeded if: ${{ success() }}
failure
Returns true when any previous step of a job fails.
steps: ... - name: The job has failed if: ${{ failure() }}
with step conclusion check:
steps: ... - name: Failing step id: demo run: exit 1 - name: The demo step has failed if: ${{ failure() && steps.demo.conclusion == 'failure' }}
always
Causes the step to always execute, and returns true, even when canceled. The always expression is best used at the step level or on tasks that you expect to run even when a job is canceled.
if: ${{ always() }}
cancelled
Returns true if the workflow was canceled.
if: ${{ cancelled() }}
Pass Status to Step
Use:
steps.<id>.outcome != 'success'
- name: Check status step id: check-step if: success() shell: bash run: | echo "Workflow successful" - name: The step to call the action if: always() uses: ./path/to/action with: some-token: ${{ inputs.some-token }} some-value: ${{ inputs.some-value }} workflow-status: ${{ steps.check-step.outcome }}
keywords
- ↑ https://github.com/github/docs/issues/21529
- ↑ https://github.com/orgs/community/discussions/28146
- ↑ https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits
- ↑ https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits