Skip to content
zit Artifact [80a880e821]
Login

Artifact [80a880e821]

Artifact 80a880e821d612a07324b106c48b81b1896e301e3cbe73d8d56cbdd7ed6e5017:


name: soak

# The nightly bug hunt. CI runs the deterministic simulation and the fuzz
# corpus shallow, for regression detection; this runs the same harnesses at
# depth on a ReleaseSafe build (asserts on), every night, on one runner, and
# fails loudly with a reproduce line when either finds something. It is
# scripts/soak.sh with a schedule, which the script's own README leaves to
# the operator. Budgets are the script's nightly defaults and can be
# overridden from the dispatch form for a bounded manual run.
on:
  schedule:
    - cron: '0 3 * * *'
  workflow_dispatch:
    inputs:
      dst_seeds:
        description: "seeds swept by the deterministic simulation"
        default: "200"
      dst_steps:
        description: "steps per seed"
        default: "10000"
      fuzz_target_budget:
        description: "wall-clock seconds granted to each fuzz target"
        default: "300"
      fuzz_seeds:
        description: "fresh-seed invocations per fuzz target"
        default: "20"

permissions:
  contents: read

# One soak at a time; a second trigger waits rather than doubling the load
# on a single-slot runner.
concurrency:
  group: soak
  cancel-in-progress: false

jobs:
  soak:
    runs-on: [self-hosted, Linux, X64, linux-x64-zit]
    # 200 seeds x 10000 steps plus 27 targets x 300 s is a few hours; the
    # ceiling keeps a hang from holding the runner into the working day.
    timeout-minutes: 420
    env:
      # Kept in lockstep with src/ci_test.zig's `pinned_zig_version` and
      # `pinned_zig_shasums`, which fail `zig build test` if this file drifts.
      ZIG_VERSION: 0.17.0
      ZIG_TARGET: x86_64-linux
      ZIG_SHASUM: 1cbe9df9f27e6b78d14ccbca43b6703a404ef79ef1c463de901d7f088d4e2026
    steps:
      - uses: actions/checkout@v5

      - name: Install Zig ${{ env.ZIG_VERSION }}
        run: |
          set -euo pipefail
          cache="${HOME}/.cache/zit-ci"
          # Zig never prunes its compilation cache: every source change leaves
          # a generation behind, and these runners grew 9-14 GB of it in a week.
          # Past the cap it is cleared WHOLE, at job start, when nothing else is
          # building on this single-slot runner. Not pruned by age: Zig does not
          # touch an entry it reuses, so an old entry may still be live, and a
          # manifest naming a deleted output costs more than one cold compile.
          cap_kb=$((8 * 1024 * 1024))
          if [ -d "${cache}/zig-local" ]; then
            used_kb=$(du -sk "${cache}/zig-local" | awk '{print $1}')
            if [ "${used_kb}" -gt "${cap_kb}" ]; then
              echo "zig cache is ${used_kb} KB, over the ${cap_kb} KB cap; clearing it"
              rm -rf "${cache}/zig-local"
            fi
          fi
          mkdir -p "${cache}/zig-local" "${cache}/zig-global" "${cache}/toolchains"
          echo "ZIG_LOCAL_CACHE_DIR=${cache}/zig-local" >> "$GITHUB_ENV"
          echo "ZIG_GLOBAL_CACHE_DIR=${cache}/zig-global" >> "$GITHUB_ENV"
          tarball="zig-${ZIG_TARGET}-${ZIG_VERSION}.tar.xz"
          kept="${cache}/toolchains/${tarball}"
          digest() { sha256sum "$1" | awk '{print $1}'; }
          if [ ! -f "$kept" ] || [ "$(digest "$kept")" != "${ZIG_SHASUM}" ]; then
            rm -f "$kept"
            curl -fsSL -o "${kept}.part" "https://ziglang.org/download/${ZIG_VERSION}/${tarball}"
            mv "${kept}.part" "$kept"
          fi
          actual="$(digest "$kept")"
          if [ "$actual" != "${ZIG_SHASUM}" ]; then
            echo "zig tarball digest mismatch: expected ${ZIG_SHASUM}, got ${actual}" >&2
            rm -f "$kept"
            exit 1
          fi
          tar -xf "$kept"
          echo "${GITHUB_WORKSPACE}/zig-${ZIG_TARGET}-${ZIG_VERSION}" >> "$GITHUB_PATH"

      - name: Deep DST and fuzz soak
        env:
          ZIT_SOAK_DIR: ${{ runner.temp }}/soak
          DST_SEEDS: ${{ inputs.dst_seeds || '200' }}
          DST_STEPS: ${{ inputs.dst_steps || '10000' }}
          FUZZ_TARGET_BUDGET: ${{ inputs.fuzz_target_budget || '300' }}
          FUZZ_SEEDS: ${{ inputs.fuzz_seeds || '20' }}
        run: |
          set -euo pipefail
          rm -rf "${ZIT_SOAK_DIR}"
          mkdir -p "${ZIT_SOAK_DIR}"
          bash scripts/soak.sh

      # The log and, on a failure, the flag with its reproduce line, printed
      # into the job's own output first. Job logs are kept 90 days and do not
      # count against the artifact storage quota, so the record survives a
      # full quota — which is shared with every other repository on the
      # account and failed this upload on two nights the soak itself passed.
      - name: Print the soak log
        if: always()
        run: |
          for f in "${{ runner.temp }}"/soak/*; do
            [ -f "$f" ] || continue
            echo "===== $(basename "$f")"
            cat "$f"
          done

      # The same files as an artifact, for convenience. Its failure is not the
      # soak's: the verdict is the step above, and the log is already printed.
      - name: Keep the soak log
        if: always()
        continue-on-error: true
        uses: actions/upload-artifact@v4
        with:
          name: soak-${{ github.run_id }}
          path: ${{ runner.temp }}/soak
          if-no-files-found: warn
          retention-days: 30