cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

main: .github/workflows/build.yml · raw

 1name: Build
 2
 3on:
 4  pull_request:
 5  push:
 6    branches: [main]
 7
 8permissions:
 9  contents: read
10
11env:
12  # Pinned so a new ruff release adds rules on your schedule, not mid-PR.
13  RUFF_VERSION: "0.16.4"
14  # orgo is installed from crates.io, so nothing bumps this automatically —
15  # dependabot does not see `cargo install` in a workflow. Raise it by hand when
16  # a new orgo lands, which is also how you find out orgo broke the site.
17  ORGO_VERSION: "0.22.0"
18
19jobs:
20  # Separate from the build so a lint failure and a broken page are distinct
21  # signals. build.py runs `ruff check --fix` and `ruff format` on development
22  # builds; these are the verifying forms of the same two commands, reading
23  # the same ruff.toml, so CI cannot disagree with a local dev build.
24  lint:
25    runs-on: ubuntu-latest
26    steps:
27      - uses: actions/checkout@v7
28
29      - name: Install ruff
30        run: pipx install "ruff==$RUFF_VERSION"
31
32      - name: Lint
33        run: |
34          ruff --version
35          ruff check --no-fix
36
37      - name: Format
38        run: ruff format --check --diff
39
40  build:
41    runs-on: ubuntu-latest
42    steps:
43      - uses: actions/checkout@v7
44
45      # Compiling orgo takes minutes; the binary only changes when the pinned
46      # version does, so key the cache on it.
47      - name: Cache orgo
48        id: cache-orgo
49        uses: actions/cache@v6
50        with:
51          path: ~/.cargo/bin/orgo
52          key: orgo-${{ env.ORGO_VERSION }}-${{ runner.os }}
53
54      - name: Install orgo
55        if: steps.cache-orgo.outputs.cache-hit != 'true'
56        run: |
57          cargo --version
58          cargo install orgo --version "$ORGO_VERSION" --locked
59
60      - name: Show versions
61        run: |
62          orgo --version
63          python3 --version
64
65      # build.py deploys with rsync when DEPLOY=true and ENV=prod. CI passes
66      # neither, but "we did not set the variable" is an assumption, not a
67      # guarantee. Shadowing rsync with a failing stub turns it into one: if the
68      # deploy path is ever reached from CI, the job fails loudly instead of
69      # quietly reaching for a production host.
70      - name: Block the deploy path
71        run: |
72          mkdir -p "$RUNNER_TEMP/bin"
73          cat > "$RUNNER_TEMP/bin/rsync" <<'EOF'
74          #!/usr/bin/env bash
75          echo "::error::rsync was invoked from CI — the deploy path must never run here" >&2
76          exit 1
77          EOF
78          chmod +x "$RUNNER_TEMP/bin/rsync"
79          echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
80
81      # ENV=prod is deliberate. The development path in build.py runs
82      # `ruff check --fix` and `ruff format`, which rewrite files in place —
83      # CI should verify the tree, not edit it. Production is also the build
84      # that actually ships, including the onion image-URL rewrite. DEPLOY is
85      # left unset, so main() builds and stops.
86      - name: Build the site
87        env:
88          ENV: prod
89          BUILD: "true"
90        run: python3 build.py
91
92      - name: Check the build produced pages
93        run: |
94          pages=$(find .build -name '*.html' | wc -l)
95          echo "built $pages html pages"
96          if [ "$pages" -lt 100 ]; then
97            echo "::error::only $pages pages built; expected the full site"
98            exit 1
99          fi