GitHub/Workflow: Difference between revisions

From Omnia
Jump to navigation Jump to search
 
(21 intermediate revisions by the same user not shown)
Line 1: Line 1:
== 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 Run ==


Line 18: 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 47: 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 60: 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 71: 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 126: 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 156: 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 234: 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:


--- 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 }}

ref: https://stackoverflow.com/questions/75987303/how-to-pass-success-or-failure-to-a-github-action-from-a-workflow

keywords