C.W.K.
Stream
Lesson 03 of 08 · published

GitHub Actions — Workflows, Jobs, Steps

~12 min · yaml, github-actions, ci

Level 0Plaintext
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

YAML that becomes your CI

A GitHub Actions workflow lives at .github/workflows/<name>.yaml. It declares when to run (events), where to run (runner), and what to run (steps). Three nesting levels: workflow → jobs → steps.

The skeleton

  • on: — trigger events. push, pull_request, workflow_dispatch (manual), schedule (cron), workflow_call (reusable).
  • jobs: — named jobs that run in parallel by default. Each job picks a runs-on: runner.
  • steps: — ordered list within a job. Each step is either uses: (a published action) or run: (a shell command).

Secrets and permissions

Secrets live at the repo or org level; reference as ${{ secrets.NAME }}. The default GITHUB_TOKEN has read-only contents permission since 2023; if you need writes (push tags, create releases), declare permissions: contents: write on the job.

The 'pin actions to SHAs' rule: uses: actions/checkout@v4 tracks v4's moving tag. A compromised maintainer could ship malicious code under v4 and your CI runs it. For supply-chain safety, pin to a SHA: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11. Dependabot updates these automatically.

Code

Minimal CI workflow·yaml
name: ci
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -e '.[dev]'
      - run: pytest -q
Matrix builds (run the same job N times with different params)·yaml
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        python: ['3.10', '3.11', '3.12']
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - run: pip install -e .
      - run: pytest
Job depending on another job + uploading an artifact·yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build           # wait for build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - run: echo deploying...
        env:
          API_TOKEN: ${{ secrets.API_TOKEN }}

External links

Exercise

Pick one workflow in your repo. Run actionlint against it locally — fix every error. Then convert at least one uses: vendor/action@v3 to a SHA pin (Dependabot will keep it updated). Diff before/after and notice your supply-chain footprint just shrank.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.