GitHub/Workflow

From Omnia
Jump to navigation Jump to search

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

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"

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

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