Chapter 08Lesson 02~185 minutes

GitHub-Hosted Runners, Images, Labels, Hardware, and Runtime Behavior: Guided Hands-On Workflow

Now you will build a controlled cross-OS probe. The workflow compares versioned Ubuntu, Windows and macOS labels, records image and tool metadata using each platform's native shell, and then installs an explicitly selected Python version with an immutable official action reference. The purpose is not to create a giant matrix; it is to prove which execution inputs differ and which you can control.

Hands-onCross-OSImageVersionsetup-pythonManifest

Learning objectives

  • Run a small OS matrix with explicit versioned labels and platform-appropriate shell commands.
  • Record safe runner/image metadata and selected preinstalled tool versions.
  • Use an immutable actions/setup-python reference to make the Python toolchain explicit.
  • Compare a -latest label with a versioned label without treating either as a frozen image release.
  • Create a compact reproducibility manifest containing source/run/image/tool evidence.

1. Preflight and cost boundary

Use a learner-owned disposable repository. A public repository is the simplest free-compatible path for standard hosted runners; private repositories consume included/billable Actions minutes according to the account plan. The mandatory learning works with Ubuntu and Windows. macOS is optional if you want a third OS comparison.

Do not benchmark hosted runners from this lab.

The probe records environment identity and portability behavior. It is not a statistically controlled hardware benchmark and must not be used to rank operating systems or providers.

2. Versioned OS matrix

name: chapter08-hosted-runner-probe
on: workflow_dispatch
permissions: {}

jobs:
  probe:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-24.04, windows-2025, macos-26]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Common context evidence
        shell: bash
        if: runner.os != 'Windows'
        run: |
          set -euo pipefail
          printf 'label=%s
' '${{ matrix.os }}'
          printf 'runner_os=%s
' '${{ runner.os }}'
          printf 'runner_arch=%s
' '${{ runner.arch }}'
          printf 'image_os=%s
' "${ImageOS:-unknown}"
          printf 'image_version=%s
' "${ImageVersion:-unknown}"
          printf 'run_id=%s
' "$GITHUB_RUN_ID"
          printf 'attempt=%s
' "$GITHUB_RUN_ATTEMPT"
          printf 'sha=%s
' "$GITHUB_SHA"

      - name: Windows context evidence
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          "label=${{ matrix.os }}"
          "runner_os=${{ runner.os }}"
          "runner_arch=${{ runner.arch }}"
          "image_os=$env:ImageOS"
          "image_version=$env:ImageVersion"
          "run_id=$env:GITHUB_RUN_ID"
          "attempt=$env:GITHUB_RUN_ATTEMPT"
          "sha=$env:GITHUB_SHA"

The platform split is intentional. Cross-platform automation often fails because authors assume Bash, GNU utilities, path separators, and quoting rules are universal.

3. Inspect preinstalled tools without depending on them

Add a step that records a few common versions:

      - name: Preinstalled tool snapshot
        shell: pwsh
        run: |
          git --version
          python --version
          node --version
          "workspace=$env:GITHUB_WORKSPACE"
          "temp=$env:RUNNER_TEMP"

PowerShell Core is available across the chosen hosted images, so this compact snapshot is convenient. The values are observations, not a promise that your project can rely on those versions next week.

4. Make Python an explicit dependency

The official setup action is executable code. Pin the exact reviewed commit and record the human-readable release in a comment:

      - name: Set up Python 3.13
        uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: '3.13'
          check-latest: false

      - name: Verify selected toolchain
        shell: pwsh
        run: |
          python --version
          python -c "import platform; print(platform.python_implementation(), platform.machine())"

The OS image may still change, but the workflow now expresses which Python line it requires. For even stricter reproducibility, pin a patch version that your support policy has tested.

5. Compare latest to a versioned label safely

Create a separate two-cell diagnostic matrix, not a production gate:

  label_compare:
    strategy:
      matrix:
        os: [ubuntu-latest, ubuntu-24.04]
    runs-on: ${{ matrix.os }}
    steps:
      - shell: bash
        run: |
          set -euo pipefail
          printf 'requested=%s
image_os=%s
image_version=%s
' \
            '${{ matrix.os }}' "${ImageOS:-unknown}" "${ImageVersion:-unknown}"

Today both labels map to Ubuntu 24.04, but they communicate different intent. ubuntu-latest opts into a future migration; ubuntu-24.04 opts out of the OS-generation migration while that image remains supported.

6. Create a non-sensitive reproducibility manifest

Do not upload the whole environment. Produce a small text or JSON file containing only the inputs needed for diagnosis:

$manifest = [ordered]@{
  run_id        = $env:GITHUB_RUN_ID
  run_attempt   = $env:GITHUB_RUN_ATTEMPT
  source_sha    = $env:GITHUB_SHA
  runner_os     = '${{ runner.os }}'
  runner_arch   = '${{ runner.arch }}'
  requested_os  = '${{ matrix.os }}'
  image_os      = $env:ImageOS
  image_version = $env:ImageVersion
  python        = (python --version 2>&1 | Out-String).Trim()
  git           = (git --version | Out-String).Trim()
}
$manifest | ConvertTo-Json | Set-Content "$env:RUNNER_TEMP/runner-manifest.json"
Get-Content "$env:RUNNER_TEMP/runner-manifest.json"

Chapter 13 will teach retained artifact transport. In this chapter, the manifest is inspected in the job log/local temp area so the runner concept remains isolated.

7. Challenge: choose the correct control

A project passed yesterday on ubuntu-latest but fails today because the preinstalled Python minor changed. Which correction controls the actual dependency?

Answer after investigation: changing to ubuntu-24.04 may avoid a future OS-generation migration, but an explicit setup step is the direct control for Python. Record both the image release and the selected Python version.

Next lesson

Design choices and trade-offs

Decide when to use latest/versioned OS labels, preinstalled/setup-managed tools, x64/Arm64, standard/larger hosted runners, or self-hosted control.

Knowledge check

Why use versioned OS labels in the mandatory probe?

What does pinning setup-python accomplish that ubuntu-24.04 does not?

Why is the macOS cell optional?

Should the manifest contain the entire process environment?

If ubuntu-latest and ubuntu-24.04 resolve identically today, are they semantically identical?

Official references and version notes

Version and compatibility note

Version-sensitive runner behavior was rechecked against current primary GitHub documentation on 2026-09-09. At verification time, ubuntu-latest maps to Ubuntu 24.04 x64, windows-latest to Windows Server 2025 x64, and macos-latest to macOS 26 Arm64; Ubuntu 26.04 and selected Arm64 images are available in preview. GitHub states that runner-image software is typically updated weekly and that -latest migrations are gradual, so a successful run must record the actual image version/toolchain rather than treating a label as a frozen machine. For standard hosted runners, every normal job receives a fresh hosted instance; steps inside one job share that instance, while separate jobs do not share its filesystem. The ubuntu-slim single-CPU option is a special container-on-shared-VM case and is not used in mandatory labs. Current public-repository standard Linux/Windows x64 runners provide 4 CPU/16 GB RAM/14 GB SSD, while private-repository standard Linux/Windows x64 runners provide 2 CPU/8 GB RAM/14 GB SSD; macOS and Arm64 classes have different specifications. Hardware figures are therefore plan/repository/image inputs, not universal constants. Larger runners are optional organization/enterprise features and are not required for course completion. Official actions/setup-python v7.0.0 is pinned in examples to 5fda3b95a4ea91299a34e894583c3862153e4b97. The guided matrix uses versioned GA labels and an optional macOS cell; preview images are discussed but not required.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.