TheFocus.AI TheFocus.AI
04 cloud and system tests → Lesson 11

Chapter 11: CI and Caches

Make pull requests prove the lab on Mac and Linux, and add cache trust deliberately.

~45–90 minutes

TUTOR WITH THEFOCUS.AI

Agent Integration

Copy this prompt into Claude, ChatGPT, or any external AI assistant. It points the assistant to the course instructions and links it to your student profile to track your progress and customize observations.

Please tutor me in this lesson using the following context. First, read the instructions at: https://courses.thefocus.ai/llms.txt My Student ID is: <none> The lesson markdown source is at: https://courses.thefocus.ai/nix-agent-lab/04-cloud-and-system-tests/11-ci-and-caches.md

You are not enrolled yet. Enroll to generate a Student ID to track lesson completions and store learning notes.

Chapter 11: CI and Caches

Objective

Make pull requests prove the lab on Mac and Linux, and add cache trust deliberately.

Your tutor should stop at each gate and wait for your evidence before continuing.

Concept

CI should call the same acceptance command that you use locally. The runner still needs bootstrap software, and that software must be accounted for. We derive the devenv CLI’s Nixpkgs source from the project lock instead of installing an unrelated moving registry version on every job.

A binary cache saves rebuilding a store path. It does not cache “correctness,” bypass tests, or make Darwin and Linux outputs interchangeable. Only configure caches and signing keys whose publishers you trust. Private source and secrets can become public if you build them into a closure and upload it to a public cache.

Concept check

Q1. Why keep tests running when every dependency is a cache hit?

Q2. Should an untrusted pull request receive a cache-upload credential?

Concept answer key — attempt first

Answer key (concept)

Q1. Cached tools do not validate changed application code. Pass criteria: distinguish dependency reuse from acceptance.

Q2. No. Pass criteria: trusted publication path, read-only untrusted checks.

Gate: Prepare the CLI bootstrap

From the lab shell, extract the Nixpkgs revision:

jq -r '.nodes.nixpkgs.locked.rev' devenv.lock

Inspect devenv.lock: in the supplied input layout, the nixpkgs node has a GitHub owner, repo, and revision. If your layout is different, adapt the lookup explicitly; never accept the literal output null as a version.

Gate: Add a workflow

Create .github/workflows/lab.yml in your learning repository:

name: Agent lab
on: [push, pull_request]
permissions:
  contents: read
jobs:
  check:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - uses: cachix/install-nix-action@v31
      - name: Install CLI from project Nixpkgs
        shell: bash
        run: |
          set -euo pipefail
          owner=$(jq -er '.nodes.nixpkgs.locked.owner' devenv.lock)
          repo=$(jq -er '.nodes.nixpkgs.locked.repo' devenv.lock)
          rev=$(jq -er '.nodes.nixpkgs.locked.rev' devenv.lock)
          nix profile install "github:$owner/$repo/$rev#devenv"
          nix --version
          devenv version
      - name: Validate
        run: devenv test
      - name: Check lock did not drift
        run: git diff --exit-code -- devenv.lock

The action tags follow the current devenv guide. For a production workflow, resolve and review full action commit SHAs, and update them deliberately. Hosted runner labels can move too: record the actual architecture in logs. An Intel and an Apple Silicon runner are not equivalent coverage. jq is a bootstrap dependency on these hosted images; verify it if choosing another runner.

Gate: Prove the workflow can reject a change

Push the green, reviewed lab commit to your own learning repository. Check both jobs. In a separate exercise PR, reintroduce the title-boundary defect from Chapter 03 and observe failure. Restore it and require green checks before merging.

Pass: both operating-system jobs run the actual suite; one intentional defect makes CI red. The workflow never starts a paid agent and receives no model credential.

Gate: Add caching after correctness

Begin with the official Nix cache. If builds are slow, measure the missing store paths first. A custom cache is useful for overlays or packages not available upstream. Add your cache name to cachix.pull only after reviewing its signing key and daemon configuration. Avoid granting a worker blanket Nix trusted-users authority merely to speed downloads; configure approved substituters through the administrator.

For cache uploads, use a trusted post-merge workflow and a narrowly scoped token. Fork PRs should read approved public caches without receiving upload credentials. Compare cold and warm setup times on the same platform and lock. Do not compare a Mac cache warm-up with a Linux cache miss as though it were one artifact.

Known failure modes

  • A passing job that runs zero tests is still a broken gate.
  • A source-only Nix build can be slow because the daemon ignored an unapproved cache; inspect configuration rather than weakening trust.
  • Never embed a secret into builtins.readFile, derivation environment variables, or generated files intended for the store.
  • A NixOS VM test may need KVM unavailable on your chosen hosted runner. Keep that capability explicit in the next lesson.

Check your understanding

Q3. What should a lock drift failure cause?

Q4. When should a cache publisher credential be available?

Answer key — attempt every question first

Answer key

Q3. Stop, inspect why evaluation changed inputs, and review a separate update. Pass criteria: do not commit CI-generated changes blindly.

Q4. Only in the trusted publication job, not arbitrary PR code. Pass criteria: scoped credential and trusted context.

References


← Chapter 10 · Next: Chapter 12 →

Previous Lesson 11 of 12 Next