1 of 28

2 of 28

Automate Maintainer Tasks with GitHub Actions

Severin Neumann (OpenTelemetry)

3 of 28

Being a maintainer is fun …

“Maintenance involves functional checks, servicing, repairing, or replacing necessary devices, equipment, machinery, building infrastructure, and supporting utilities"

Wikipedia: Maintenance

4 of 28

… until you do the same thing

Image by Jackie (CC BY 2.0)

… and again,

and again,

and again

and again

and again

and again

and again

and

again

and

again…

again …

5 of 28

Image by xkcd.com (CC BY-NC 2.5)

We all know the answer:

Automation!

6 of 28

Automation Without Hurting Yourself

1.

Reuse what is out there

2.

script first, workflow later

3.

Learn the basics

(and then use a LLM…)

4.

Common Pitfalls

x

too much

7 of 28

Reuse what is out there

Before you write any automation on your own, make sure you use what others have created before you:

8 of 28

CODEOWNERS

The easiest way to get PRs assigned to the right person or team*:

  1. Add a .github/CODEOWNERS file to your repository
  2. Add entries for files and owners:

  • Require that pull requests have to be approved by code owners

* Limitation: Groups and users need write permissions on the repo!

* @your-project/project-owners

**/docs @your-project/project-docs-owners

*.cpp @username

9 of 28

Dependabot

Another automation you can enable without writing any scripts or workflows!

  1. In your repository’s “Code security” settings you can enable Dependabot
  2. Add a “.github/dependabot.yml” file to your repository for detailed configuration

version: 2

updates:

- package-ecosystem: github-actions

directory: /

schedule:

interval: weekly

10 of 28

GitHub’s GitHub Actions

Common actions to add to your repo:

  • Labeler: Automatically label new pull requests
  • Stale: Warns/closes issues/PRs without activity
  • Add-to-Project: add issue/PRs to a GitHub project

name: 'Label PR'

on:

pull_request_target:

types: [opened,reopened]

jobs:

labeler:

name: 'Add component labels'

permissions: [...]

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/labeler@v5

with:

configuration-path: '.github/component-label-map.yml'

11 of 28

Docker’s GitHub Actions

All you need to build containers!

  • Login
  • Setup (qemu, buildx)
  • Metadata
  • Build and Push

steps:

- name: Docker meta

uses: docker/metadata-action@<hash>

tags: “type=semver,pattern={{ version }}”

- name: Log into GitHub Container Registry

uses: docker/login-action@<hash>

with:

registry: ghcr.io

- name: Set up QEMU

uses: docker/setup-qemu-action@<...>

- name: Set up Docker Buildx

uses: docker/setup-buildx-action@<...>

- name: Build and push images

uses: docker/build-push-action@<...>

with: …

12 of 28

OpenSSF Scorecard’s GitHub Actions

Checks for issues with software security for each commit! Easy setup:

github.com/org/repo/actions/new?category=security&query=scorecard

After running the workflow go to

securityscorecards.dev/viewer/?uri=github.com%2Forg%2Frepo

13 of 28

OpenSSF Scorecard’s GitHub Actions

14 of 28

… and more

GitHub has a marketplace for Actions (and Apps), so always check if there is something reusable.

But as with any other dependency, pay attention:

(March, 2024) Multiple tags in this action are compromised

  • Verify your actions
  • Pin to commit SHA + Dependabot/Renovate
  • Limit permissions & token usage

15 of 28

script first, workflow later

Image by chriscom (CC BY-SA 2.0)

16 of 28

script first, workflow later

If there is no “ready to use” workflow by a third party, go half the way from manual to semi-automatic:

  1. Create a script for the task and store it in your repo
  2. When needed, run the script manually
  3. Test it for a few runs, make sure it works well
  4. Add the GitHub workflow

17 of 28

Learn the basics

If you have a script that you want to run based on a trigger wrapping it into a GitHub action is (relatively) easy

name: Run my script

# what triggers the workflow?

on:

# the following allows you to manually trigger the workflow

workflow_dispatch:

schedule:

# hourly at minute 10

- cron: 10 * * * *

# a list of (parallel) jobs that this workflow will execute

jobs:

my-script:

name: run-my-script

# the name of the runner used, “ubuntu-*” is a good starting point

runs-on: ubuntu-24.04

# The sequential steps the script will take

steps:

# Checkout the repo

- uses: actions/checkout@<hash> # v4.2.x

# Run the script

- run: ./scripts/my-script.sh

18 of 28

… and then use an LLM

Writing and testing GitHub actions can be painful, but gladly most LLMs do a decent job in creating you something you can start from, e.g.

“I need a github action that writes a thank you note below a merged PR and shares a link to a survey with the contributor. Make sure that you follow security best practices for github actions, and the the gh tool is used where applicable”

name: Thank Contributor

on:

pull_request_target:

types:

- closed

permissions:

pull-requests: write # Required to comment on PRs

contents: read # Minimal required permission

jobs:

thank_contributor:

if: github.event.pull_request.merged == true

runs-on: ubuntu-latest

steps:

- name: Comment on PR

env:

GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

SURVEY_URL: "https://example.com/contributor-survey"

run: |

gh pr comment ${{ github.event.pull_request.number }} \

--body "🎉 Thank you for your contribution! We appreciate your effort. 🙌

We'd love to hear your feedback—please take a moment to fill out our survey: [$SURVEY_URL]($SURVEY_URL)"

19 of 28

common pitfalls

20 of 28

common pitfalls

21 of 28

common pitfalls

Things that can go wrong:

  1. your script runs on your local machine but not on the GH runner
  2. permission and token issues

22 of 28

Runs locally but not on the GH runner

Common problems:

  • Software versions and variants
  • Path is not resolved correctly
  • Environment variables are not resolved as expected

How to fix them:

  • Review and compare local and runner’s versions/variants, e.g. ubuntu-24.04
  • On MacOS: “date”, “sed” and others are not GNU!
  • Try out nektos/act

23 of 28

Runs locally but not on the GH runner

24 of 28

Runs locally but not on the GH runner

25 of 28

Permission & token issues

This type of issue is the hardest to solve, because often it can

not be reproduced

  • locally
  • and in your fork!

What you can do:

  • read the docs
  • set your permissions for GITHUB_TOKEN
  • make sure you use the right type of token

26 of 28

Set your permissions for GITHUB_TOKEN

name: Thank Contributor

on:

pull_request_target:

types:

- closed

permissions:

issues: read

jobs:

thank_contributor:

permissions:

contents: write

pull-requests: write

You can set global permissions, and local permissions per job.

27 of 28

Token Types for actions

  • Use GITHUB_TOKEN whenever possible for internal operations—it’s secure and short-lived
  • Cross-repo actions or bot-actions require a personal access token (PAT)
  • Trigger a workflow from a workflow run does not work with GITHUB_TOKEN, use a PAT
  • Use actions/create-github-app-token for creating a GitHub App installation access token (e.g. bots)

28 of 28

Thank you