Skip to content
GitHub actions

GitHub actions

Artifacts

Artifacts are data generated by workflows that can be passed to subsequent jobs.

Official actions

Merge multiple artifacts

Set merge-multiple: true

- name: Download notebooks
  uses: actions/download-artifact@v4
  with:
    path: path/of/artifacts
    pattern: notebook-*
    merge-multiple: true
- name: Display structure of downloaded files
  run: ls -R path/of/artifacts

Automatic Dependency Update

Updating package and GitHub actions dependencies automatically as a part of continuous integration (CI).

Dependabot

Dependabot creates a pull request once there is an update for the dependencies. The pull requests are usually tested by continuous integration (CI).

However, dependabot does not support automerging on its own due to security concerns. The good news is that we could use Kodiak to do the job. See it’s quickstart if you are interested.

For example, the dependabot file .github/dependabot.yml to track GitHub actions.

.github/dependabot.yml
version: 2

updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"
    labels:
    - "automerge"
    groups:
      gha-dependencies:
        applies-to: "version-updates"
        patterns:
          - "*"

Kodiak bot configuration file: .github/.kodiak.toml

.github/.kodiak.toml
version = 1

[merge]
method = "squash"

Renovate

Renovate bot can manage both dependency update checking and automated pull request merging. Renovate supports a variety of platforms and a variety of programming languages.

Enable the Renovate GitHub APP for GitHub repositories. Renovate bot will open an pull request for reachable repos to begin an interactive setup.

The settings file renovate.json example

renovate.json
{
  "extends": [
    "config:recommended",
  ],
  "git-submodules": {
      "enabled": true
  }

Caching

The https://github.com/actions/cache action caches data across workflow runs.

- name: Cache multiple paths
  uses: actions/cache@v6
  with:
    path: |
      ~/cache
      !~/cache/exclude
    key: ${{ runner.os }}-${{ hashFiles('**/Lockfile') }}
    restore-keys: |
      ${{ runner.os }}-
  • The key is the identifier for writing into the cache. If the key stays the same before and after the workflow, the cache will not be updated.
  • The restore-keys are the identifiers for reading the cache besides the key. If there is no matching key but a part of it (restore-keys) matches, the GitHub action will still read the cache and update it after the job. (since the key is different)

You can split caching into restore and save steps, leading to a fine-grained behavior.

- name: Restore cached Primes
      id: cache-primes-restore
      uses: actions/cache/restore@v6
      with:
        path: |
          path/to/dependencies
          some/other/dependencies
        key: ${{ runner.os }}-primes
#
# //intermediate workflow steps
#
- name: Save Primes
  id: cache-primes-save
  uses: actions/cache/save@v6
  with:
    path: |
      path/to/dependencies
      some/other/dependencies

Cleanup PR caches

Clean up PR caches after it is closed.

name: Cleanup PR caches
on:
  pull_request:
    types:
      - closed

jobs:
  cleanup:
    permissions:
      actions: write
    runs-on: ubuntu-latest
    steps:
      - name: Cleanup
        run: |
          gh extension install actions/gh-actions-cache

          echo "Fetching list of cache key"
          cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )

          ## Setting this to not fail the workflow while deleting cache keys.
          set +e
          echo "Deleting caches..."
          for cacheKey in $cacheKeysForPR
          do
              gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
          done
          echo "Done"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO: ${{ github.repository }}
          BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge

Dynamic parallel matrix

We can create a job matrix dynamically by returning a JSON array.

Here we use json and glob modules in Python as an example.

name: Show text files with dynamic parallel matrix

on:
  push:
    branches:
    - main

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: List text files as a JSON array
        id: set-matrix
        run: echo "matrix=$(python -c 'import glob, json; print(json.dumps(glob.glob("*.txt")))')" >> $GITHUB_OUTPUT
  execute:
    needs: setup
    strategy:
      fail-fast: false
      matrix:
        textfile: ${{ fromJSON(needs.setup.outputs.matrix) }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Print text file
        run: cat ${{ matrix.textfile }}

Git Operations

Git commands, such as checkout, add, create a branch, make a pull request in Github actions.

Checkout/Clone

The official https://github.com/actions/checkout action clones the repository to $GITHUB_WORKSPACE. By default it uses the built-in GITHUB_TOKEN for authentication.

In most cases, this is what you need:

- uses: actions/checkout@v7

The checkout action also supports pushing a commit to the same repo.

Warning

This may not work on protected branches that need status checks.

on: push
jobs:
  git-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: |
          date > generated.txt
          git config user.name github-actions
          git config user.email github-actions@github.com
          git add .
          git commit -m "generated"
          git push

However, the following actions are more convenient for commit and push changes back to your GitHub repo.

However, no further workflows will be triggered with the GITHUB_TOKEN. You will need the following steps to trigger workflows.

How to trigger further CI runs

You will need either a Personal access token (PAT) with repo scope access as an action secret.

- uses: actions/checkout@v7
  with:
	  token: ${{ secrets.PAT }}

Or a pair of SSH keys; the public key is the deploy key with write access, while the private key is an action secret variable SSH_PRIVATE_KEY.

- uses: actions/checkout@v7
  with:
	ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}

Create a pull request

The https://github.com/peter-evans/create-pull-request action will commit all files into a new branch and make a pull request to the target (default main) branch.

- name: Create Pull Request
  uses: peter-evans/create-pull-request@v6
  with:
  # token: ${{ secrets.PAT }} # A PAT is required for triggering pull request workflows
    token: ${{ secrets.GITHUB_TOKEN }}  # This will not trigger further workflows

Merge pull requests

Issues to a markdown file

The Python package https://github.com/mattduck/gh2md exports Github repository issues and pull requests to a single, readable markdown file.

.github/workflows/issues2md.yml
name: Issues2Markdown
on:
  # issues:
  # issue_comment:
  workflow_dispatch: # manually run this workflow
  schedule:
    - cron: "0 0 * * *"  # On 00:00 every day
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
        fetch-depth: 0             # otherwise, you will failed to push refs to dest repo.
    - uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install GitHub Issue to Markdown
      run:  pip install gh2md
    - name: Backup github issues into separate markdown files
      env:
        GITHUB_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
      run: |
        rm -rf issues/ || true
        gh2md --multiple-files -I --no-prs --no-closed-prs $GITHUB_REPOSITORY issues/
    - name: Install mmv
      run: sudo apt update && sudo apt install -y mmv
    - name: Modify markdown file names
      working-directory: issues
      run: mmv '*.*.issue.*.md' '#2.md'
    - name: Commit files
      uses: stefanzweifel/git-auto-commit-action@v5
      with:
        commit_message: Backup Issues

GitHub Pages

Publish your website to GitHub pages with GitHub actions (CI/CD).

Official workflow

Official GitHub actions

The benefit of using the official workflow is that you do not need an orphan branch to hold the webpages.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # After the website is built
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          path: ./site

  # Deployment job
  deploy:
    needs: build
    if: ${{ github.ref == 'refs/heads/main' }}
    # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
    permissions:
      pages: write # to deploy to Pages
      id-token: write # to verify the deployment originates from an appropriate source
      actions: read # to download an artifact uploaded by `actions/upload-pages-artifact@v3`
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

In the repository settings => Pages => Build and deployment => Select GitHub actions as the page source.

Publish to another branch

You need to give write permission to GITHUB_TOKEN in the workflow file for the following actions to work

permissions:
  contents: write

Use https://github.com/peaceiris/actions-gh-pages

# After the website was built
- name: Deploy
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}  # You need an SSH deploy key if deploying to another repo
    publish_dir: ./public
    force_orphan: true
    commit_message: ${{ github.event.head_commit.message }}

Or https://github.com/JamesIves/github-pages-deploy-action

# After the website was built
- name: Deploy 🚀
  uses: JamesIves/github-pages-deploy-action@v4
  with:
    folder: ./public # The folder the action should deploy.

In the repository settings => Pages => Build and deployment => Select Deploy from a branch as the page source.

Release

See also

Last updated on