GitHub Actions Tutorial — Next Steps Exercises

4 minute read

Practice Exercises — Next Steps

Final exercises to cap off your GitHub Actions learning journey.


Exercise 1 — Monorepo Path Filter

Task: Write a workflow for a monorepo with packages/api/ and packages/web/ that:

  1. Detects which packages changed
  2. Only runs the CI job for changed packages
  3. Works on both push and pull_request events
Show Solution

name: Monorepo CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      api: ${{ steps.filter.outputs.api }}
      web: ${{ steps.filter.outputs.web }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            api:
              - 'packages/api/**'
            web:
              - 'packages/web/**'

  ci-api:
    needs: detect
    if: needs.detect.outputs.api == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: cd packages/api && npm ci && npm test

  ci-web:
    needs: detect
    if: needs.detect.outputs.web == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: cd packages/web && npm ci && npm test

If only packages/api/ files changed, only ci-api runs. If both changed, both run.


Exercise 2 — GitHub App Token Workflow

Task: Explain why the following workflow won’t trigger CI on the PR it creates, and fix it using a GitHub App token:


jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: |
          echo "update" >> deps.txt
          git add . && git commit -m "Update deps"
          git push origin automated-update
      - run: gh pr create --title "Update" --body "Auto"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Show Solution

Why it won’t trigger CI: PRs created with GITHUB_TOKEN do not trigger other workflows. This is a deliberate safety measure to prevent infinite workflow loops.

Fix with GitHub App:


jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - name: Generate App Token
        id: app-token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}

      - run: |
          git config user.name "my-bot[bot]"
          git config user.email "my-bot@users.noreply.github.com"
          echo "update" >> deps.txt
          git add . && git commit -m "Update deps"
          git push origin automated-update

      - run: gh pr create --title "Update" --body "Auto"
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}

PRs created with a GitHub App token will trigger CI workflows.


Exercise 3 — Concurrency Control

Task: Write the concurrency configuration for these scenarios:

  1. Cancel previous runs when a new push arrives on the same branch
  2. Never cancel production deployments (queue them instead)
  3. Allow only one deploy per environment at a time
Show Solution

1. Cancel previous runs per branch:


concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

2. Queue production deploys (never cancel):


jobs:
  deploy-production:
    concurrency:
      group: deploy-production
      cancel-in-progress: false         # queue, don't cancel
    environment: production
    # ...

3. One deploy per environment:


jobs:
  deploy:
    concurrency:
      group: deploy-${{ inputs.environment }}
      cancel-in-progress: false
    environment: ${{ inputs.environment }}
    # ...

cancel-in-progress: true cancels queued runs. false makes them wait.


Exercise 4 — Non-CI/CD Automation

Task: Write a workflow that:

  1. Runs every day at midnight UTC
  2. Finds all issues labeled needs-triage that are older than 7 days
  3. Adds a comment asking the author for more information
  4. Adds a stale-triage label
Show Solution

name: Triage Reminder

on:
  schedule:
    - cron: '0 0 * * *'

permissions:
  issues: write

jobs:
  remind:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);

            const { data: issues } = await github.rest.issues.listForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: 'needs-triage',
              state: 'open',
              sort: 'created',
              direction: 'asc'
            });

            for (const issue of issues) {
              if (new Date(issue.created_at) < sevenDaysAgo) {
                // Check if we already commented
                const labels = issue.labels.map(l => l.name);
                if (labels.includes('stale-triage')) continue;

                await github.rest.issues.createComment({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: issue.number,
                  body: `@${issue.user.login} — this issue has been awaiting triage for over 7 days. Could you provide more details to help us prioritize it?`
                });

                await github.rest.issues.addLabels({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: issue.number,
                  labels: ['stale-triage']
                });
              }
            }


Exercise 5 — Design Your Learning Plan

Task: Based on the learning path, create a personal 4-week plan to go from beginner to advanced. For each week, specify:

  1. Which chapters to study
  2. Which project to build
  3. What to practice
Show Solution
Week Study Build Practice
1 Introduction, Getting Started, Triggers, Jobs & Runners First CI workflow (hello world) YAML syntax, trigger types, job dependencies
2 Steps & Actions, Env & Secrets, Expressions, Artifacts & Caching Node.js CI project Matrix builds, caching, artifact upload
3 Matrix, Reusable Workflows, Custom Actions, Deployments Docker + GHCR project, Multi-Env Deploy project Reusable workflows, composite actions, OIDC
4 Self-Hosted Runners, Troubleshooting, Advanced Patterns, Next Steps Reusable Library project, Custom Action project Monorepo patterns, GitHub Apps, act local testing

Daily routine:

  • Read one sub-topic (20 minutes)
  • Complete the exercises at the end (30 minutes)
  • Iterate on your project (30 minutes)

By week 4, you should be able to architect CI/CD pipelines for real projects with reusable workflows, custom actions, and multi-environment deployments.


🎉 Congratulations! You’ve completed the GitHub Actions tutorial.

← Back to: Chapter 16: Next Steps

Back to Tutorial Home →

Updated: