initial commit #42
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: "Makefile test in kpango/dev" | |
| # Verify every Makefile / Makefile.d target runs without error inside the | |
| # kpango/dev container on linux/amd64, linux/arm64, and macOS (arm64). | |
| # | |
| # Three phases: | |
| # 1. syntax-check — make -n dry-run for every declared target (no container) | |
| # 2. test-linux — real execution inside kpango/dev on ubuntu runners | |
| # 3. test-macos — real execution inside kpango/dev via Docker / Colima on macOS | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "Makefile" | |
| - "Makefile.d/**" | |
| - ".github/workflows/makefile-test.yaml" | |
| pull_request: | |
| paths: | |
| - "Makefile" | |
| - "Makefile.d/**" | |
| - ".github/workflows/makefile-test.yaml" | |
| workflow_dispatch: | |
| schedule: | |
| # Daily at 02:00 UTC | |
| - cron: "0 2 * * *" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| DEV_IMAGE: kpango/dev:nightly | |
| # ── Phase 1: Makefile syntax / dry-run ──────────────────────────────────────── | |
| # Runs on a plain ubuntu runner (no container required). | |
| # Parses every target via 'make -n' to catch typos, missing includes, and bad | |
| # prerequisite chains before the more expensive container jobs start. | |
| jobs: | |
| syntax-check: | |
| name: "Dry-run syntax check" | |
| runs-on: ubuntu-latest | |
| env: | |
| # Suppress the 'pass' invocation in variables.mk; dry-run doesn't execute | |
| # shell commands but make still evaluates $(shell ...) for non-?= vars. | |
| GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Configure git safe directory | |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| - name: Dry-run — help / git / dotfiles | |
| run: | | |
| make -n help | |
| make -n echo | |
| make -n git/status git/diff git/pull git/check | |
| make -n dotfiles/install dotfiles/clean dotfiles/perm | |
| make -n link copy clean perm | |
| - name: Dry-run — format | |
| run: | | |
| make -n format \ | |
| format/zsh format/dockerfile format/nix format/yaml \ | |
| format/go format/rust format/json format/md \ | |
| format/makefile format/conf format/convert-to-zsh | |
| - name: Dry-run — arch / mac install | |
| run: | | |
| make -n arch/install arch/p1/install arch/desk/install | |
| make -n arch_link arch_copy arch_p1_link arch_p1_copy arch_desk_link arch_desk_copy | |
| make -n mac/install mac_link mac_copy | |
| - name: Dry-run — docker | |
| run: | | |
| make -n docker/build docker/build/dev \ | |
| docker/builder/init docker/builder/create \ | |
| docker/builder/remove docker/builder/add-nodes \ | |
| docker/login docker/push docker/pull docker/profile | |
| make -n build prod login push pull init_buildx create_buildx remove_buildx profile | |
| - name: Dry-run — nix | |
| run: | | |
| make -n nix/setup nix/pull nix/update \ | |
| nix/test nix/test/eval nix/test/check nix/test/dry-run \ | |
| nix/test/build nix/test/docker \ | |
| nix/fmt nix/fmt/check | |
| - name: Dry-run — devbox | |
| run: | | |
| make -n devbox/install devbox/shell devbox/setup \ | |
| devbox/update devbox/clean | |
| - name: Dry-run — update | |
| run: | | |
| make -n update update/versions \ | |
| update/versions/actions update/versions/tools | |
| # ── Phase 2: Container execution (Linux runners) ────────────────────────────── | |
| # Runs the full job inside kpango/dev so the environment exactly matches the | |
| # container. Targets that require missing optional tools emit a SKIP message | |
| # and do not fail the job. | |
| test-linux: | |
| name: "Container (${{ matrix.arch.name }})" | |
| needs: syntax-check | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: | |
| - name: linux/amd64 | |
| runner: ubuntu-latest | |
| - name: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.arch.runner }} | |
| env: | |
| GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| container: | |
| image: kpango/dev:nightly | |
| options: "--user root --privileged" | |
| steps: | |
| # When running as root the sudo binary may be absent; create a passthrough. | |
| # Also mark the workspace safe for git to avoid "dubious ownership" errors. | |
| - name: Bootstrap container | |
| shell: sh | |
| run: | | |
| git config --global --add safe.directory '*' | |
| command -v sudo >/dev/null 2>&1 || { | |
| printf '#!/bin/sh\nexec "$@"\n' > /usr/local/bin/sudo | |
| chmod +x /usr/local/bin/sudo | |
| } | |
| - uses: actions/checkout@v6 | |
| # ── Informational ────────────────────────────────────────────────────── | |
| - name: help | |
| run: make help | |
| # ── Read-only git targets ────────────────────────────────────────────── | |
| - name: git read-only (status / diff / echo) | |
| run: | | |
| make echo | |
| make git/status | |
| make git/diff | |
| # ── dotfiles install ─────────────────────────────────────────────────── | |
| # Both link and copy modes; /etc/docker is created inside the container. | |
| - name: dotfiles/install (link) | |
| run: make dotfiles/install | |
| - name: dotfiles/install (copy) | |
| run: make dotfiles/install MODE=copy | |
| # ── Format targets ───────────────────────────────────────────────────── | |
| # Formatters that are always safe (no external tool required): | |
| # format/dockerfile — intentionally skipped by the target itself | |
| # format/makefile — pure sed, always available | |
| # All others are conditional: print SKIP if the tool is absent. | |
| - name: format/dockerfile + format/makefile (unconditional) | |
| run: | | |
| make format/dockerfile | |
| make format/makefile | |
| - name: format/zsh | |
| run: | | |
| make format/zsh || echo "SKIP: shfmt / beautysh not found" | |
| - name: format/yaml | |
| run: | | |
| make format/yaml || echo "SKIP: prettier / yamlfmt not found" | |
| - name: format/go | |
| run: | | |
| make format/go || echo "SKIP: gofmt not found" | |
| - name: format/rust | |
| run: | | |
| make format/rust || echo "SKIP: rustfmt not found" | |
| - name: format/json | |
| run: | | |
| make format/json || echo "SKIP: prettier / jq not found" | |
| - name: format/md | |
| run: | | |
| make format/md || echo "SKIP: prettier not found" | |
| - name: format/nix | |
| run: | | |
| make format/nix || echo "SKIP: nixpkgs-fmt not found" | |
| - name: format/conf | |
| run: | | |
| make format/conf || echo "SKIP: shfmt not found" | |
| # ── dotfiles/perm ────────────────────────────────────────────────────── | |
| # Needs nkf for line-ending normalisation; skip gracefully if absent. | |
| - name: dotfiles/perm | |
| run: | | |
| make dotfiles/perm || echo "SKIP: nkf not found or permission error" | |
| # ── Devbox cleanup ───────────────────────────────────────────────────── | |
| - name: devbox/clean | |
| run: make devbox/clean | |
| # ── Phase 3: Container execution via Docker on macOS ───────────────────────── | |
| # macOS runners do not support the 'container:' key, so we pull the image and | |
| # invoke each target with 'docker run'. Uses Colima for the Docker daemon. | |
| # Tests the linux/arm64 image natively on Apple Silicon (macos-latest = M1/M2). | |
| test-macos: | |
| name: "Container (macOS → linux/arm64)" | |
| needs: syntax-check | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Colima + Docker CLI | |
| run: | | |
| brew install colima docker | |
| colima start \ | |
| --cpu 3 --memory 6 --disk 40 \ | |
| --arch aarch64 --vm-type vz --vz-rosetta | |
| docker version | |
| - name: Pull kpango/dev (linux/arm64) | |
| run: docker pull --platform linux/arm64 ${{ env.DEV_IMAGE }} | |
| # Each call mounts the checkout at /dotfiles and pre-configures git + sudo. | |
| - name: Run targets in container | |
| env: | |
| IMAGE: ${{ env.DEV_IMAGE }} | |
| GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| drun() { | |
| docker run --rm \ | |
| --platform linux/arm64 \ | |
| --volume "$PWD:/dotfiles" \ | |
| --workdir /dotfiles \ | |
| --user root \ | |
| --env GITHUB_ACCESS_TOKEN="$GITHUB_ACCESS_TOKEN" \ | |
| "$IMAGE" \ | |
| sh -c ' | |
| git config --global --add safe.directory /dotfiles | |
| command -v sudo >/dev/null 2>&1 || { | |
| printf "#!/bin/sh\nexec \"\$@\"\n" > /usr/local/bin/sudo | |
| chmod +x /usr/local/bin/sudo | |
| } | |
| '"$1" | |
| } | |
| # ── Informational ──────────────────────────────────────────────── | |
| drun 'make help' | |
| # ── Read-only git ──────────────────────────────────────────────── | |
| drun 'make echo' | |
| drun 'make git/status' | |
| drun 'make git/diff' | |
| # ── dotfiles install ───────────────────────────────────────────── | |
| drun 'make dotfiles/install' | |
| drun 'make dotfiles/install MODE=copy' | |
| # ── Format targets ─────────────────────────────────────────────── | |
| drun 'make format/dockerfile' | |
| drun 'make format/makefile' | |
| drun 'make format/zsh || echo "SKIP: shfmt / beautysh not found"' | |
| drun 'make format/yaml || echo "SKIP: prettier / yamlfmt not found"' | |
| drun 'make format/go || echo "SKIP: gofmt not found"' | |
| drun 'make format/rust || echo "SKIP: rustfmt not found"' | |
| drun 'make format/json || echo "SKIP: prettier / jq not found"' | |
| drun 'make format/md || echo "SKIP: prettier not found"' | |
| drun 'make format/nix || echo "SKIP: nixpkgs-fmt not found"' | |
| drun 'make format/conf || echo "SKIP: shfmt not found"' | |
| # ── dotfiles/perm ──────────────────────────────────────────────── | |
| drun 'make dotfiles/perm || echo "SKIP: nkf not found or permission error"' | |
| # ── Devbox cleanup ─────────────────────────────────────────────── | |
| drun 'make devbox/clean' | |
| # ── Summary ───────────────────────────────────────────────────────────────── | |
| all-passed: | |
| name: "All checks passed" | |
| needs: [syntax-check, test-linux, test-macos] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: Check results | |
| run: | | |
| results='${{ toJSON(needs) }}' | |
| echo "$results" | |
| for result in $(echo "$results" | grep -o '"result":"[^"]*"' | cut -d'"' -f4); do | |
| [ "$result" = "success" ] || [ "$result" = "skipped" ] || { | |
| echo "One or more jobs failed." | |
| exit 1 | |
| } | |
| done | |
| echo "All jobs succeeded." |