refactor: make volume controls consistent #338
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Flake Builder 🏗️ | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ────────────────────────────────────────────── | |
| # Inventory: discover all flake outputs | |
| # ────────────────────────────────────────────── | |
| inventory: | |
| name: Inventory 📋 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| outputs: | |
| devshells: ${{ steps.inventory.outputs.devshells }} | |
| packages: ${{ steps.inventory.outputs.packages }} | |
| nixos: ${{ steps.inventory.outputs.nixos }} | |
| darwin: ${{ steps.inventory.outputs.darwin }} | |
| orphan_homes: ${{ steps.inventory.outputs.orphan_homes }} | |
| has_devshells: ${{ steps.inventory.outputs.has_devshells }} | |
| has_packages: ${{ steps.inventory.outputs.has_packages }} | |
| has_nixos: ${{ steps.inventory.outputs.has_nixos }} | |
| has_darwin: ${{ steps.inventory.outputs.has_darwin }} | |
| has_orphan_homes: ${{ steps.inventory.outputs.has_orphan_homes }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: wimpysworld/nothing-but-nix@main | |
| with: | |
| hatchet-protocol: "holster" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Inventory | |
| id: inventory | |
| run: bash home-manager/_mixins/scripts/flake-inventory/flake-inventory.sh | |
| # ────────────────────────────────────────────── | |
| # DevShells & Formatter (per-platform) | |
| # ────────────────────────────────────────────── | |
| devshells: | |
| name: DevShells 🐚 (${{ matrix.target.system }}) | |
| needs: inventory | |
| if: needs.inventory.outputs.has_devshells == 'true' | |
| runs-on: ${{ matrix.target.runner }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.inventory.outputs.devshells) }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: wimpysworld/nothing-but-nix@main | |
| if: contains(matrix.target.system, 'linux') | |
| with: | |
| hatchet-protocol: "holster" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Build devShells & formatter | |
| env: | |
| SYSTEM: ${{ matrix.target.system }} | |
| SHELLS_JSON: ${{ toJSON(matrix.target.shells) }} | |
| HAS_FORMATTER: ${{ matrix.target.formatter }} | |
| run: | | |
| FAILED=() | |
| BUILT=0 | |
| SKIPPED=0 | |
| # Build each devShell, checking evaluability first. | |
| while IFS= read -r shell; do | |
| [ -z "${shell}" ] && continue | |
| ATTR="devShells.${SYSTEM}.${shell}" | |
| if nix eval ".#${ATTR}.drvPath" --raw --no-write-lock-file >/dev/null 2>&1; then | |
| echo "🔨 Building ${ATTR}" | |
| if nix build ".#${ATTR}" --no-link -L; then | |
| echo "✅ Built ${ATTR}" | |
| BUILT=$((BUILT + 1)) | |
| else | |
| echo "❌ Failed to build ${ATTR}" >&2 | |
| FAILED+=("${ATTR}") | |
| fi | |
| else | |
| echo "⏭️ Skipping ${ATTR} (not evaluable for ${SYSTEM})" | |
| SKIPPED=$((SKIPPED + 1)) | |
| fi | |
| done < <(echo "${SHELLS_JSON}" | jq -r '.[]') | |
| # Build formatter if present. | |
| if [ "${HAS_FORMATTER}" = "true" ]; then | |
| ATTR="formatter.${SYSTEM}" | |
| echo "🔨 Building ${ATTR}" | |
| if nix build ".#${ATTR}" --no-link -L; then | |
| echo "✅ Built ${ATTR}" | |
| BUILT=$((BUILT + 1)) | |
| else | |
| echo "❌ Failed to build ${ATTR}" >&2 | |
| FAILED+=("${ATTR}") | |
| fi | |
| fi | |
| echo "" | |
| echo "Built: ${BUILT}, Skipped: ${SKIPPED}, Failed: ${#FAILED[@]}" | |
| if [ "${#FAILED[@]}" -gt 0 ]; then | |
| echo "Failed outputs:" | |
| for f in "${FAILED[@]}"; do | |
| echo " ✗ ${f}" | |
| done | |
| exit 1 | |
| fi | |
| # ────────────────────────────────────────────── | |
| # Packages (per-platform) | |
| # ────────────────────────────────────────────── | |
| packages: | |
| name: Packages 📦 (${{ matrix.target.system }}) | |
| needs: inventory | |
| if: needs.inventory.outputs.has_packages == 'true' | |
| runs-on: ${{ matrix.target.runner }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.inventory.outputs.packages) }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: wimpysworld/nothing-but-nix@main | |
| if: contains(matrix.target.system, 'linux') | |
| with: | |
| hatchet-protocol: "holster" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Prefetch proprietary packages 📦 | |
| if: contains(matrix.target.system, 'linux') | |
| env: | |
| PICO8_DOWNLOAD_URL: ${{ secrets.PICO8_DOWNLOAD_URL }} | |
| run: | | |
| if [ -n "${PICO8_DOWNLOAD_URL}" ]; then | |
| nix-prefetch-url --name "pico-8_0.2.7_amd64.zip" "${PICO8_DOWNLOAD_URL}" || echo "⚠️ PICO-8 prefetch failed" | |
| fi | |
| - name: Build packages | |
| env: | |
| SYSTEM: ${{ matrix.target.system }} | |
| PACKAGES_JSON: ${{ toJSON(matrix.target.packages) }} | |
| run: | | |
| FAILED=() | |
| BUILT=0 | |
| SKIPPED=0 | |
| SUMMARY_ROWS=() | |
| while IFS= read -r name; do | |
| [ -z "${name}" ] && continue | |
| ATTR="packages.${SYSTEM}.${name}" | |
| # Check whether the package evaluates for this platform. | |
| if ! nix eval ".#${ATTR}.drvPath" --raw --no-write-lock-file >/dev/null 2>&1; then | |
| echo "⏭️ Skipping ${ATTR} (not evaluable for ${SYSTEM})" | |
| SKIPPED=$((SKIPPED + 1)) | |
| SUMMARY_ROWS+=("| ${name} | ⏭️ Skipped |") | |
| continue | |
| fi | |
| # Check hydraPlatforms; skip if explicitly set to empty. | |
| hydra_platforms=$(nix eval ".#${ATTR}.meta.hydraPlatforms" --json --no-write-lock-file 2>/dev/null || echo "null") | |
| if [ "${hydra_platforms}" = "[]" ]; then | |
| echo "⏭️ Skipping ${ATTR} (excluded from CI via hydraPlatforms)" | |
| SKIPPED=$((SKIPPED + 1)) | |
| SUMMARY_ROWS+=("| ${name} | ⏭️ Skipped |") | |
| continue | |
| fi | |
| echo "🔨 Building ${ATTR}" | |
| if nix build ".#${ATTR}" --no-link -L; then | |
| echo "✅ Built ${ATTR}" | |
| BUILT=$((BUILT + 1)) | |
| SUMMARY_ROWS+=("| ${name} | ✅ Built |") | |
| else | |
| echo "❌ Failed to build ${ATTR}" >&2 | |
| FAILED+=("${ATTR}") | |
| SUMMARY_ROWS+=("| ${name} | ❌ Failed |") | |
| fi | |
| done < <(echo "${PACKAGES_JSON}" | jq -r '.[]') | |
| # Write Job Summary | |
| { | |
| printf "## Packages 📦 (%s)\n\n" "${SYSTEM}" | |
| printf "| Package | Status |\n" | |
| printf "|---------|--------|\n" | |
| for row in "${SUMMARY_ROWS[@]}"; do | |
| printf "%s\n" "${row}" | |
| done | |
| printf "\n**Built: %d, Skipped: %d, Failed: %d**\n" "${BUILT}" "${SKIPPED}" "${#FAILED[@]}" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| echo "" | |
| echo "Built: ${BUILT}, Skipped: ${SKIPPED}, Failed: ${#FAILED[@]}" | |
| if [ "${#FAILED[@]}" -gt 0 ]; then | |
| echo "Failed outputs:" | |
| for f in "${FAILED[@]}"; do | |
| echo " ✗ ${f}" | |
| done | |
| exit 1 | |
| fi | |
| # ────────────────────────────────────────────── | |
| # NixOS Configurations (per-host) | |
| # ────────────────────────────────────────────── | |
| nixos: | |
| name: NixOS 🐧 ${{ matrix.target.name }} | |
| needs: inventory | |
| if: needs.inventory.outputs.has_nixos == 'true' | |
| runs-on: ${{ matrix.target.runner }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.inventory.outputs.nixos) }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: wimpysworld/nothing-but-nix@main | |
| with: | |
| hatchet-protocol: "holster" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Prefetch proprietary packages 📦 | |
| env: | |
| PICO8_DOWNLOAD_URL: ${{ secrets.PICO8_DOWNLOAD_URL }} | |
| run: | | |
| if [ -n "${PICO8_DOWNLOAD_URL}" ]; then | |
| nix-prefetch-url --name "pico-8_0.2.7_amd64.zip" "${PICO8_DOWNLOAD_URL}" || echo "⚠️ PICO-8 prefetch failed" | |
| fi | |
| - name: Build NixOS ${{ matrix.target.name }} | |
| run: | | |
| nix build ".#nixosConfigurations.${{ matrix.target.name }}.config.system.build.toplevel" --no-link -L | |
| - name: Build Home ${{ matrix.target.home }} | |
| if: matrix.target.home != '' | |
| run: | | |
| nix build ".#homeConfigurations.\"${{ matrix.target.home }}\".activationPackage" --no-link -L | |
| # ────────────────────────────────────────────── | |
| # Darwin Configurations (per-host) | |
| # ────────────────────────────────────────────── | |
| darwin: | |
| name: Darwin 🍏 ${{ matrix.target.name }} | |
| needs: inventory | |
| if: needs.inventory.outputs.has_darwin == 'true' | |
| runs-on: ${{ matrix.target.runner }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.inventory.outputs.darwin) }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Build Darwin ${{ matrix.target.name }} | |
| run: | | |
| nix build ".#darwinConfigurations.${{ matrix.target.name }}.config.system.build.toplevel" --no-link -L | |
| - name: Build Home ${{ matrix.target.home }} | |
| if: matrix.target.home != '' | |
| run: | | |
| nix build ".#homeConfigurations.\"${{ matrix.target.home }}\".activationPackage" --no-link -L | |
| # ────────────────────────────────────────────── | |
| # Orphan Home Configurations (lima, wsl, gaming) | |
| # ────────────────────────────────────────────── | |
| orphan-homes: | |
| name: Home 🏠 ${{ matrix.target.name }} | |
| needs: inventory | |
| if: needs.inventory.outputs.has_orphan_homes == 'true' | |
| runs-on: ${{ matrix.target.runner }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.inventory.outputs.orphan_homes) }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: wimpysworld/nothing-but-nix@main | |
| with: | |
| hatchet-protocol: "holster" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| - name: Prefetch proprietary packages 📦 | |
| if: contains(matrix.target.runner, 'ubuntu') | |
| env: | |
| PICO8_DOWNLOAD_URL: ${{ secrets.PICO8_DOWNLOAD_URL }} | |
| run: | | |
| if [ -n "${PICO8_DOWNLOAD_URL}" ]; then | |
| nix-prefetch-url --name "pico-8_0.2.7_amd64.zip" "${PICO8_DOWNLOAD_URL}" || echo "⚠️ PICO-8 prefetch failed" | |
| fi | |
| - name: Build Home ${{ matrix.target.name }} | |
| run: | | |
| nix build ".#homeConfigurations.\"${{ matrix.target.name }}\".activationPackage" --no-link -L | |
| # ────────────────────────────────────────────── | |
| # Sentinel: gate builds and derive release version | |
| # ────────────────────────────────────────────── | |
| sentinel: | |
| name: Sentinel 👁️ | |
| needs: [devshells, packages, nixos, darwin, orphan-homes] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| outputs: | |
| ver: ${{ steps.version.outputs.ver }} | |
| publish_ver: ${{ steps.version.outputs.publish_ver }} | |
| steps: | |
| - name: Check build results | |
| run: | | |
| results='${{ toJSON(needs.*.result) }}' | |
| echo "Job results: ${results}" | |
| if echo "${results}" | jq -e 'any(. == "failure" or . == "cancelled")' > /dev/null 2>&1; then | |
| echo "❌ One or more build jobs failed or were cancelled" | |
| exit 1 | |
| fi | |
| echo "✅ All build jobs passed (or were skipped)" | |
| - uses: actions/checkout@v6 | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| - name: Derive version | |
| id: version | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| run: | | |
| # Extract NixOS version (e.g. 25.11.20260211.6c5e707) and take first 3 | |
| # dot-separated components, then strip leading zeros for semver compliance | |
| NIXOS_VER=$(nix eval .#nixosConfigurations.nihilus.config.system.nixos.version --raw) | |
| VER=$(echo "${NIXOS_VER}" | cut -d'.' -f1-3 | sed -E 's/\b0+([1-9])/\1/g') | |
| PUBLISH_VER="${VER}$(date -u +%H)" | |
| echo "ver=${VER}" >> "$GITHUB_OUTPUT" | |
| echo "publish_ver=${PUBLISH_VER}" >> "$GITHUB_OUTPUT" | |
| echo "📌 Version: ${VER}" | |
| echo "📌 Publish version: ${PUBLISH_VER}" | |
| # ────────────────────────────────────────────── | |
| # Publish to FlakeHub (after all builds pass) | |
| # ────────────────────────────────────────────── | |
| publish: | |
| name: Publish FlakeHub ❄️ | |
| needs: [sentinel] | |
| if: github.repository_owner == 'wimpysworld' && needs.sentinel.outputs.publish_ver != '' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actionhippie/swap-space@v1 | |
| with: | |
| size: 16G | |
| - uses: actions/checkout@v6 | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| # Use fork with OIDC fix until upstream merges: | |
| # https://github.com/DeterminateSystems/flakehub-push/pull/275 | |
| - name: Build flakehub-push 🔨 | |
| run: | | |
| nix build github:flexiondotorg/flakehub-push/oidc-order#packages.x86_64-linux.default -L | |
| - uses: DeterminateSystems/flakehub-push@v6 | |
| with: | |
| tag: v${{ needs.sentinel.outputs.publish_ver }} | |
| visibility: public | |
| include-output-paths: true | |
| log-directives: flakehub_push=debug | |
| source-binary: result/bin/flakehub-push | |
| # ────────────────────────────────────────────── | |
| # Release ISO (after NixOS builds pass) | |
| # ────────────────────────────────────────────── | |
| release: | |
| name: Release ISO 📀 | |
| needs: [sentinel] | |
| if: github.repository_owner == 'wimpysworld' && needs.sentinel.outputs.ver != '' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check existing release 🔍 | |
| id: check-release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if gh release view "v${{ needs.sentinel.outputs.ver }}" --repo "${{ github.repository }}" >/dev/null 2>&1; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| echo "Release v${{ needs.sentinel.outputs.ver }} already exists; skipping ISO build" | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| echo "Release v${{ needs.sentinel.outputs.ver }} not found; proceeding with ISO build" | |
| fi | |
| - uses: wimpysworld/nothing-but-nix@main | |
| if: steps.check-release.outputs.exists == 'false' | |
| with: | |
| hatchet-protocol: "holster" | |
| root-safe-haven: "10240" | |
| mnt-safe-haven: "10240" | |
| - uses: DeterminateSystems/determinate-nix-action@v3 | |
| if: steps.check-release.outputs.exists == 'false' | |
| with: | |
| extra-conf: | | |
| eval-cores = 0 | |
| - uses: DeterminateSystems/flakehub-cache-action@v3 | |
| if: steps.check-release.outputs.exists == 'false' | |
| - name: Build ISO 💿️ | |
| if: steps.check-release.outputs.exists == 'false' | |
| id: build-iso | |
| run: | | |
| nix build .#nixosConfigurations.nihilus.config.system.build.isoImage -L | |
| ISO=$(head -n1 result/nix-support/hydra-build-products | cut -d'/' -f6) | |
| # Stage ISO for release upload; GitHub auto-generates sha256 digests | |
| mkdir -p iso | |
| sudo mv "result/iso/${ISO}" "iso/${ISO}" | |
| - name: Release ISO 🎁 | |
| if: steps.check-release.outputs.exists == 'false' | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.sentinel.outputs.ver }} | |
| files: iso/* | |
| generate_release_notes: true | |
| make_latest: true |