knowledge/technology/dev/GitHub Actions.md
2024-02-12 15:32:44 +01:00

26 KiB

obj website
concept https://github.com/features/actions

GitHub Actions

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged pull requests to production.

GitHub Actions goes beyond just DevOps and lets you run workflows when other events happen in your repository. For example, you can run a workflow to automatically add the appropriate labels whenever someone creates a new issue in your repository.

You can configure a GitHub Actions workflow to be triggered when an event occurs in your repository, such as a pull request being opened or an issue being created. Your workflow contains one or more jobs which can run in sequential order or in parallel. Each job will run inside its own virtual machine runner, or inside a container, and has one or more steps that either run a script that you define or run an action, which is a reusable extension that can simplify your workflow.

Example:

name: Rust

on:
  push:
  pull_request:

env:
  CARGO_TERM_COLOR: always

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Build
      run: cargo build --verbose
    - name: Run tests
      run: cargo test --verbose

Expressions

You can use expressions to programmatically set environment variables in workflow files and access contexts. An expression can be any combination of literal values, references to a context, or functions. You can combine literals, context references, and functions using operators.

Expressions are commonly used with the conditional if keyword in a workflow file to determine whether a step should run. When an if conditional is true, the step will run.

You need to use specific syntax to tell GitHub to evaluate an expression rather than treat it as a string.
${{ <expression> }}

Secrets passed to GitHub Actions can be used:
${{ secrets.MYSECRET }}

Functions

contains

contains( search, item )

Returns true if search contains item. If search is an array, this function returns true if the item is an element in the array. If search is a string, this function returns true if the item is a substring of search. This function is not case sensitive. Casts values to a string.

startsWith

startsWith( searchString, searchValue )

Returns true when searchString starts with searchValue. This function is not case sensitive. Casts values to a string.

endsWith

endsWith( searchString, searchValue )

Returns true if searchString ends with searchValue. This function is not case sensitive. Casts values to a string.

format

format( string, replaceValue0, replaceValue1, ..., replaceValueN)

Replaces values in the string, with the variable replaceValueN. Variables in the string are specified using the {N} syntax, where N is an integer. You must specify at least one replaceValue and string. There is no maximum for the number of variables (replaceValueN) you can use. Escape curly braces using double braces.

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. For example, you can use always to send logs even when a job is canceled.

Example of always:

if: ${{ always() }}

failure

Returns true when any previous step of a job fails. If you have a chain of dependent jobs, failure() returns true if any ancestor job fails.

Example of failure:

steps:
  ...
  - name: The job has failed
    if: ${{ failure() }}

Workflows

A workflow is a configurable automated process that will run one or more jobs. Workflows are defined by a YAML file checked in to your repository and will run when triggered by an event in your repository, or they can be triggered manually, or at a defined schedule.

Workflows are defined in the .github/workflows directory in a repository, and a repository can have multiple workflows, each of which can perform a different set of tasks. For example, you can have one workflow to build and test pull requests, another workflow to deploy your application every time a release is created, and still another workflow that adds a label every time someone opens a new issue.

Syntax

name

The name of the workflow. GitHub displays the names of your workflows under your repository's "Actions" tab. If you omit name, GitHub displays the workflow file path relative to the root of the repository.

on

To automatically trigger a workflow, use on to define which events can cause the workflow to run.

You can define single or multiple events that can trigger a workflow, or set a time schedule. You can also restrict the execution of a workflow to only occur for specific files, tags, or branch changes. These options are described in the following sections.

Using a single event:
For example, a workflow with the following on value will run when a push is made to any branch in the workflow's repository:

on: push

Using multiple events:
You can specify a single event or multiple events. For example, a workflow with the following on value will run when a push is made to any branch in the repository or when someone forks the repository:

on: [push, fork]

If you specify multiple events, only one of those events needs to occur to trigger your workflow. If multiple triggering events for your workflow occur at the same time, multiple workflow runs will be triggered.

Using activity types:
Some events have activity types that give you more control over when your workflow should run. Use on.<event_name>.types to define the type of event activity that will trigger a workflow run.

For example, the issue_comment event has the created, edited, and deleted activity types. If your workflow triggers on the label event, it will run whenever a label is created, edited, or deleted. If you specify the created activity type for the label event, your workflow will run when a label is created but not when a label is edited or deleted.

on:
  label:
    types:
      - created

If you specify multiple activity types, only one of those event activity types needs to occur to trigger your workflow. If multiple triggering event activity types for your workflow occur at the same time, multiple workflow runs will be triggered. For example, the following workflow triggers when an issue is opened or labeled. If an issue with two labels is opened, three workflow runs will start: one for the issue opened event and two for the two issue labeled events.

on:
  issues:
    types:
      - opened
      - labeled

Using filters:
Some events have filters that give you more control over when your workflow should run.

For example, the push event has a branches filter that causes your workflow to run only when a push to a branch that matches the branches filter occurs, instead of when any push occurs.

on:
  push:
    branches:
      - main
      - 'releases/**'

on.<event_name>.types

Use on.<event_name>.types to define the type of activity that will trigger a workflow run. Most GitHub events are triggered by more than one type of activity. For example, the label is triggered when a label is created, edited, or deleted. The types keyword enables you to narrow down activity that causes the workflow to run. When only one activity type triggers a webhook event, the types keyword is unnecessary.

on:
  label:
    types: [created, edited]

on.schedule

You can use on.schedule to define a time schedule for your workflows. You can schedule a workflow to run at specific UTC times using POSIX cron syntax. Scheduled workflows run on the latest commit on the default or base branch. The shortest interval you can run scheduled workflows is once every 5 minutes.

This example triggers the workflow every day at 5:30 and 17:30 UTC:

on:
  schedule:
    # * is a special character in YAML so you have to quote this string
    - cron:  '30 5,17 * * *'

A single workflow can be triggered by multiple schedule events. You can access the schedule event that triggered the workflow through the github.event.schedule context. This example triggers the workflow to run at 5:30 UTC every Monday-Thursday, but skips the Not on Monday or Wednesday step on Monday and Wednesday.

on:
  schedule:
    - cron: '30 5 * * 1,3'
    - cron: '30 5 * * 2,4'

jobs:
  test_schedule:
    runs-on: ubuntu-latest
    steps:
      - name: Not on Monday or Wednesday
        if: github.event.schedule != '30 5 * * 1,3'
        run: echo "This step will be skipped on Monday and Wednesday"
      - name: Every time
        run: echo "This step will always run"

on.workflow_dispatch

When using the workflow_dispatch event, you can manually run this workflow from the UI.

env

A map of variables that are available to the steps of all jobs in the workflow. You can also set variables that are only available to the steps of a single job or to a single step. For more information, see jobs.<job_id>.env and jobs.<job_id>.steps[*].env.

Variables in the env map cannot be defined in terms of other variables in the map.

Example of env:

env:
  SERVER: production

jobs

A workflow run is made up of one or more jobs, which run in parallel by default. To run jobs sequentially, you can define dependencies on other jobs using the jobs.<job_id>.needs keyword.

Each job runs in a runner environment specified by runs-on.

jobs.<job_id>

Use jobs.<job_id> to give your job a unique identifier. The key job_id is a string and its value is a map of the job's configuration data. You must replace <job_id> with a string that is unique to the jobs object. The <job_id> must start with a letter or _ and contain only alphanumeric characters, -, or _.

Use jobs.<job_id>.name to set a name for the job, which is displayed in the GitHub UI.

Example: Creating jobs:
In this example, two jobs have been created, and their job_id values are my_first_job and my_second_job.

jobs:
  my_first_job:
    name: My first job
  my_second_job:
    name: My second job
jobs.<job_id>.container

Use jobs.<job_id>.container to create a container to run any steps in a job that don't already specify a container. If you have steps that use both script and container actions, the container actions will run as sibling containers on the same network with the same volume mounts.

If you do not set a container, all steps will run directly on the host specified by runs-on unless a step refers to an action configured to run in a container.

Example: Running a job within a container:

name: CI
on:
  push:
    branches: [ main ]
jobs:
  container-test-job:
    runs-on: ubuntu-latest
    container:
      image: node:18
      env:
        NODE_ENV: development
      ports:
        - 80
      volumes:
        - my_docker_volume:/volume_mount
      options: --cpus 1
    steps:
      - name: Check for dockerenv file
        run: (ls /.dockerenv && echo Found dockerenv) || (echo No dockerenv)

When you only specify a container image, you can omit the image keyword.

jobs:
  container-test-job:
    runs-on: ubuntu-latest
    container: node:18
jobs.<job_id>.needs

Use jobs.<job_id>.needs to identify any jobs that must complete successfully before this job will run. It can be a string or array of strings. If a job fails or is skipped, all jobs that need it are skipped unless the jobs use a conditional expression that causes the job to continue. If a run contains a series of jobs that need each other, a failure or skip applies to all jobs in the dependency chain from the point of failure or skip onwards. If you would like a job to run even if a job it is dependent on did not succeed, use the always() conditional expression in jobs.<job_id>.if.

Example: Requiring successful dependent jobs:

jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]
jobs.<job_id>.if

You can use the jobs.<job_id>.if conditional to prevent a job from running unless a condition is met. You can use any supported context and expression to create a conditional.

When you use expressions in an if conditional, you can, optionally, omit the ${{ }} expression syntax because GitHub Actions automatically evaluates the if conditional as an expression. However, this exception does not apply everywhere.

You must always use the ${{ }} expression syntax or escape with '', "", or () when the expression starts with !, since ! is reserved notation in YAML format. For example:

if: ${{ ! startsWith(github.ref, 'refs/tags/') }}

For more information, see "Expressions."

jobs.<job_id>.runs-on

Use jobs.<job_id>.runs-on to define the type of machine to run the job on.

jobs.<job_id>.env

A map of variables that are available to all steps in the job. You can set variables for the entire workflow or an individual step.

jobs.<job_id>.steps

A job contains a sequence of tasks called steps. Steps can run commands, run setup tasks, or run an action in your repository, a public repository, or an action published in a Docker registry. Not all steps run actions, but all actions run as a step. Each step runs in its own process in the runner environment and has access to the workspace and filesystem. Because steps run in their own process, changes to environment variables are not preserved between steps. GitHub provides built-in steps to set up and complete a job.

Example of jobs.<job_id>.steps:

name: Greeting from Mona

on: push

jobs:
  my-job:
    name: My Job
    runs-on: ubuntu-latest
    steps:
      - name: Print a greeting
        env:
          MY_VAR: Hi there! My name is
          FIRST_NAME: Mona
          MIDDLE_NAME: The
          LAST_NAME: Octocat
        run: |
          echo $MY_VAR $FIRST_NAME $MIDDLE_NAME $LAST_NAME.          
  • jobs.<job_id>.steps[*].if
    You can use the if conditional to prevent a step from running unless a condition is met. You can use any supported context and expression to create a conditional.
  • jobs.<job_id>.steps[*].name
    A name for your step to display on GitHub.
  • jobs.<job_id>.steps[*].uses
    Selects an action to run as part of a step in your job. An action is a reusable unit of code. You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image.

Some actions require inputs that you must set using the with keyword. Review the action's README file to determine the inputs required.

Example: Using versioned actions

steps:
  # Reference a specific commit
  - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
  # Reference the major version of a release
  - uses: actions/checkout@v4
  # Reference a specific version
  - uses: actions/checkout@v4.2.0
  # Reference a branch
  - uses: actions/checkout@main
  • jobs.<job_id>.steps[*].run
    Runs command-line programs using the operating system's shell. If you do not provide a name, the step name will default to the text specified in the run command. Commands run using non-login shells by default.

Each run keyword represents a new process and shell in the runner environment. When you provide multi-line commands, each line runs in the same shell. For example:

A single-line command:

    - name: Install Dependencies
      run: npm install
    ```  

A multi-line command:
```yaml
    - name: Clean install dependencies and build
      run: |
        npm ci
        npm run build        
    ```
- `jobs.<job_id>.steps[*].working-directory`
Using the `working-directory` keyword, you can specify the working directory of where to run the command.

```yaml
- name: Clean temp directory
  run: rm -rf *
  working-directory: ./temp
  • jobs.<job_id>.steps[*].with
    A map of the input parameters defined by the action. Each input parameter is a key/value pair. Input parameters are set as environment variables. The variable is prefixed with INPUT_ and converted to upper case.

Input parameters defined for a Docker container must use args. For more information, see "jobs.<job_id>.steps[*].with.args."

**Example of jobs.<job_id>.steps[*].with

Defines the three input parameters (first_name, middle_name, and last_name) defined by the hello_world action. These input variables will be accessible to the hello-world action as INPUT_FIRST_NAME, INPUT_MIDDLE_NAME, and INPUT_LAST_NAME environment variables.

jobs:
  my_first_job:
    steps:
      - name: My first step
        uses: actions/hello_world@main
        with:
          first_name: Mona
          middle_name: The
          last_name: Octocat
  • jobs.<job_id>.steps[*].with.args
    A string that defines the inputs for a Docker container. GitHub passes the args to the container's ENTRYPOINT when the container starts up. An array of strings is not supported by this parameter. A single argument that includes spaces should be surrounded by double quotes "".
  • jobs.<job_id>.steps[*].env
    Sets variables for steps to use in the runner environment. You can also set variables for the entire workflow or a job. For more information, see env and jobs.<job_id>.env.

When more than one environment variable is defined with the same name, GitHub uses the most specific variable. For example, an environment variable defined in a step will override job and workflow environment variables with the same name, while the step executes. An environment variable defined for a job will override a workflow variable with the same name, while the job executes.

Public actions may specify expected variables in the README file. If you are setting a secret or sensitive value, such as a password or token, you must set secrets using the secrets context.

Example of jobs.<job_id>.steps[*].env

steps:
  - name: My first action
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      FIRST_NAME: Mona
      LAST_NAME: Octocat

Events

An event is a specific activity in a repository that triggers a workflow run. For example, activity can originate from GitHub when someone creates a pull request, opens an issue, or pushes a commit to a repository. You can also trigger a workflow to run on a schedule, by posting to a REST API, or manually.

create

Runs your workflow when someone creates a Git reference (Git branch or tag) in the workflow's repository.

For example, you can run a workflow when the create event occurs.

on:
  create

delete

Runs your workflow when someone deletes a Git reference (Git branch or tag) in the workflow's repository.

For example, you can run a workflow when the delete event occurs.

on:
  delete

discussion

Runs your workflow when a discussion in the workflow's repository is created or modified.

Activity Types:

  • created
  • edited
  • deleted
  • transferred
  • pinned
  • unpinned
  • labeled
  • unlabeled
  • locked
  • unlocked
  • category_changed
  • answered
  • unanswered

For example, you can run a workflow when a discussion has been created, edited, or answered.

on:
  discussion:
    types: [created, edited, answered]

discussion_comment

Runs your workflow when a comment on a discussion in the workflow's repository is created or modified.

Activity Types:

  • created
  • edited
  • deleted

For example, you can run a workflow when a discussion comment has been created or deleted.

on:
  discussion_comment:
    types: [created, deleted]

fork

Runs your workflow when someone forks a repository.

For example, you can run a workflow when the fork event occurs.

on:
  fork

issue_comment

Runs your workflow when an issue or pull request comment is created, edited, or deleted.

Activity Types:

  • created
  • edited
  • deleted

For example, you can run a workflow when an issue or pull request comment has been created or deleted.

on:
  issue_comment:
    types: [created, deleted]

issues

Runs your workflow when an issue in the workflow's repository is created or modified.

Activity Types:

  • opened
  • edited
  • deleted
  • transferred
  • pinned
  • unpinned
  • closed
  • reopened
  • assigned
  • unassigned
  • labeled
  • unlabeled
  • locked
  • unlocked
  • milestoned
  • demilestoned

For example, you can run a workflow when an issue has been opened, edited, or milestoned.

on:
  issues:
    types: [opened, edited, milestoned]

milestone

Runs your workflow when a milestone in the workflow's repository is created or modified.

Activity Types:

  • created
  • closed
  • opened
  • edited
  • deleted

For example, you can run a workflow when a milestone has been opened or deleted.

on:
  milestone:
    types: [opened, deleted]

pull_request

Runs your workflow when activity on a pull request in the workflow's repository occurs. For example, if no activity types are specified, the workflow runs when a pull request is opened or reopened or when the head branch of the pull request is updated.

Activity Types:

  • assigned
  • unassigned
  • labeled
  • unlabeled
  • opened
  • edited
  • closed
  • reopened
  • synchronize
  • converted_to_draft
  • ready_for_review
  • locked
  • unlocked
  • milestoned
  • demilestoned
  • review_requested
  • review_request_removed
  • auto_merge_enabled
  • auto_merge_disabled

For example, you can run a workflow when a pull request has been opened or reopened.

on:
  pull_request:
    types: [opened, reopened]

push

Runs your workflow when you push a commit or tag, or when you create a repository from a template.

You can use the branches or branches-ignore filter to configure your workflow to only run when specific branches are pushed.

You can use the tags or tags-ignore filter to configure your workflow to only run when specific tags are pushed.

If you use both the branches filter and the paths filter, the workflow will only run when both filters are satisfied. For example, the following workflow will only run when a push that includes a change to a JavaScript (.js) file is made to a branch whose name starts with releases/:

on:
  push:
    branches:
      - 'releases/**'
    paths:
      - '**.js'

For example, you can run a workflow when the push event occurs.

on:
  push

release

Runs your workflow when release activity in your repository occurs.

Activity Types:

  • published
  • unpublished
  • created
  • edited
  • deleted
  • prereleased
  • released

For example, you can run a workflow when a release has been published.

on:
  release:
    types: [published]

schedule

The schedule event allows you to trigger a workflow at a scheduled time.

You can schedule a workflow to run at specific UTC times using POSIX cron syntax. Scheduled workflows run on the latest commit on the default or base branch. The shortest interval you can run scheduled workflows is once every 5 minutes. A single workflow can be triggered by multiple schedule events. You can access the schedule event that triggered the workflow through the github.event.schedule context.

This example triggers the workflow every day at 5:30 and 17:30 UTC:

on:
  schedule:
    # * is a special character in YAML so you have to quote this string
    - cron:  '30 5,17 * * *'

workflow_dispatch

To enable a workflow to be triggered manually, you need to configure the workflow_dispatch event. You can manually trigger a workflow run using the GitHub API, GitHub CLI, or GitHub browser interface.

on: workflow_dispatch

Jobs

A job is a set of steps in a workflow that is executed on the same runner. Each step is either a shell script that will be executed, or an action that will be run. Steps are executed in order and are dependent on each other. Since each step is executed on the same runner, you can share data from one step to another. For example, you can have a step that builds your application followed by a step that tests the application that was built.

You can configure a job's dependencies with other jobs; by default, jobs have no dependencies and run in parallel with each other. When a job takes a dependency on another job, it will wait for the dependent job to complete before it can run. For example, you may have multiple build jobs for different architectures that have no dependencies, and a packaging job that is dependent on those jobs. The build jobs will run in parallel, and when they have all completed successfully, the packaging job will run.

Actions

An action is a custom application for the GitHub Actions platform that performs a complex but frequently repeated task. Use an action to help reduce the amount of repetitive code that you write in your workflow files. An action can pull your git repository from GitHub, set up the correct toolchain for your build environment, or set up the authentication to your cloud provider.

You can write your own actions, or you can find actions to use in your workflows in the GitHub Marketplace.