From f8a537e12dc52a03b38965ca1c7c78f32272acbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:35:21 -0300 Subject: [PATCH 1/8] ci(docs): publish mdbook to GitHub Pages Adds a workflow that builds the mdbook on docs/book.toml changes, runs lychee link-check on PRs, and deploys to the gh-pages branch on push to main. Closes #123. Note: GitHub Pages must be manually configured in repo settings to serve from the gh-pages branch after the first successful run. --- .github/workflows/pr-main_mdbook.yml | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/pr-main_mdbook.yml diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml new file mode 100644 index 0000000..5c3110e --- /dev/null +++ b/.github/workflows/pr-main_mdbook.yml @@ -0,0 +1,78 @@ +name: Publish ethlambda docs to GitHub Pages + +on: + push: + branches: + - main + paths: + - "docs/**" + - "book.toml" + - ".github/workflows/pr-main_mdbook.yml" + pull_request: + paths: + - "docs/**" + - "book.toml" + - ".github/workflows/pr-main_mdbook.yml" + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + name: Build + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v6 + + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: "0.5.2" + + - name: Build documentation + run: make docs + + - name: Upload docs artifact + if: ${{ github.ref == 'refs/heads/main' }} + uses: actions/upload-artifact@v4 + with: + name: docs + path: book/html + retention-days: 1 + + link-check: + name: Link Check + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v6 + + - name: Check links + uses: lycheeverse/lychee-action@v2 + with: + args: --no-progress --exclude 'localhost' --exclude '127\.0\.0\.1' docs/ + fail: true + + deploy: + name: Deploy + runs-on: ubuntu-latest + needs: build + if: ${{ github.ref == 'refs/heads/main' }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - name: Download docs artifact + uses: actions/download-artifact@v4 + with: + name: docs + path: book/html + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: book/html + destination_dir: . From 0598f228481bc03972303c14413e1de39b925065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:39:57 -0300 Subject: [PATCH 2/8] ci(docs): use linkcheck2 instead of lychee for link checks The linkcheck2 preprocessor is already configured in book.toml and matches what `make docs-deps` installs locally. Drop the lychee job and let `mdbook build` fail on broken internal links by setting `warning-policy = "error"`. CI installs mdbook-linkcheck2 so the optional renderer actually runs. Verified locally: `mdbook build` exits 101 when SUMMARY.md is missing an entry referenced from another page. --- .github/workflows/pr-main_mdbook.yml | 28 +++++++++++++++------------- book.toml | 6 ++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index 5c3110e..ec516fe 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -32,6 +32,21 @@ jobs: with: mdbook-version: "0.5.2" + - name: Setup Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Cache cargo install + uses: Swatinem/rust-cache@v2 + with: + cache-targets: false + + # mdbook-linkcheck2 runs as part of `mdbook build` and fails the build + # on broken internal links (see book.toml `warning-policy = "error"`). + - name: Install mdbook-linkcheck2 + run: cargo install --version 0.12.0 --locked mdbook-linkcheck2 + - name: Build documentation run: make docs @@ -43,19 +58,6 @@ jobs: path: book/html retention-days: 1 - link-check: - name: Link Check - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/checkout@v6 - - - name: Check links - uses: lycheeverse/lychee-action@v2 - with: - args: --no-progress --exclude 'localhost' --exclude '127\.0\.0\.1' docs/ - fail: true - deploy: name: Deploy runs-on: ubuntu-latest diff --git a/book.toml b/book.toml index 373b4f7..a34ab2e 100644 --- a/book.toml +++ b/book.toml @@ -18,4 +18,10 @@ fold = { enable = true, level = 1 } # https://github.com/marxin/mdbook-linkcheck2 # Needs to be installed with `cargo install mdbook-linkcheck2` [output.linkcheck2] +# Skip silently if the binary isn't installed, so contributors can build the +# site without installing the link checker. optional = true +# Fail the build on broken links (default is "warn"), so CI catches them. +# Only internal/relative links are checked because follow-web-links defaults +# to false. +warning-policy = "error" From c370f8438adc5c7c4d9e294c537af743b0b99632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 15:56:49 -0300 Subject: [PATCH 3/8] chore(docs-deps): pin transitive deps with --locked `cargo install` without --locked regenerates the lockfile from semver constraints, so a transitive dep can update overnight and break the install. Use the lockfile published with each tool for reproducible installs locally and in CI. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 07d9c4b..40c29f3 100644 --- a/Makefile +++ b/Makefile @@ -50,8 +50,8 @@ run-devnet: docker-build lean-quickstart ## 🚀 Run a local devnet using lean-q && NETWORK_DIR=local-devnet ./spin-node.sh --node all --generateGenesis --metrics > ../devnet.log 2>&1 docs-deps: ## 📦 Install dependencies for generating the documentation - cargo install --version 0.5.2 mdbook - cargo install --version 0.12.0 mdbook-linkcheck2 + cargo install --version 0.5.2 --locked mdbook + cargo install --version 0.12.0 --locked mdbook-linkcheck2 docs: ## 📚 Generate the documentation site under ./book mdbook build From 13c68156e455f0c9861b1033dca95cb9fc93564b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:05:19 -0300 Subject: [PATCH 4/8] ci(docs): re-add lychee for external URL checking mdbook-linkcheck2 only validates internal links (follow-web-links is off so contributors don't get blocked by transient external failures or rate limits). Lychee covers the external side: bitrot in linked papers, blog posts, and other clients' READMEs. Lychee runs alongside deploy on main; deploy doesn't depend on it, so a flaky external server can't block a release. --- .github/workflows/pr-main_mdbook.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index ec516fe..e049b8a 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -58,6 +58,22 @@ jobs: path: book/html retention-days: 1 + # Internal links are validated by mdbook-linkcheck2 inside the build step. + # This job validates external URLs, which linkcheck2 skips by default + # (follow-web-links = false) to keep local builds fast and offline-friendly. + link-check: + name: Link Check + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v6 + + - name: Check links + uses: lycheeverse/lychee-action@v2 + with: + args: --no-progress --exclude 'localhost' --exclude '127\.0\.0\.1' docs/ + fail: true + deploy: name: Deploy runs-on: ubuntu-latest From 52cf715f726f8713a4ce21c17c8f6a4af0b9a8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:08:22 -0300 Subject: [PATCH 5/8] ci(docs): tighten permissions and concurrency on mdbook workflow Addresses bot-review feedback on PR #331: - Default workflow permissions to `contents: read`, scope `contents: write` to the deploy job only. Limits blast radius of any compromise in the build/link-check toolchain. - Add `cancel-in-progress: true` to the build concurrency group so a new push drops the stale build. Keep deploy as `cancel-in-progress: false` and split the group key so a fresh build does not cancel an in-flight deploy mid-push. - Add `Makefile` to path filters so changes to `make docs` retrigger the workflow. - Add a header comment linking the ethrex workflow used as a model. Skipped feedback (intentional): - `actions/checkout@v6` is current (v6.0.2) and matches `ci.yml`. - `deploy needs: build` (not link-check) is by design: a flaky external URL should not gate a docs deploy. Internal links are enforced inside the build via `warning-policy = "error"`. --- .github/workflows/pr-main_mdbook.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index e049b8a..70be1e7 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -1,3 +1,5 @@ +# Modeled on ethrex's mdbook publish workflow: +# https://github.com/lambdaclass/ethrex/blob/728dc7ded560c665ab1ff2cf7f3eeb197b5bc40f/.github/workflows/pr-main_mdbook.yml name: Publish ethlambda docs to GitHub Pages on: @@ -7,23 +9,28 @@ on: paths: - "docs/**" - "book.toml" + - "Makefile" - ".github/workflows/pr-main_mdbook.yml" pull_request: paths: - "docs/**" - "book.toml" + - "Makefile" - ".github/workflows/pr-main_mdbook.yml" workflow_dispatch: +# Default to least privilege; only the deploy job upgrades to write to push the +# gh-pages branch. permissions: - contents: write + contents: read jobs: build: name: Build runs-on: ubuntu-latest concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-build-${{ github.ref }} + cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -79,8 +86,11 @@ jobs: runs-on: ubuntu-latest needs: build if: ${{ github.ref == 'refs/heads/main' }} + permissions: + contents: write concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-deploy-${{ github.ref }} + cancel-in-progress: false steps: - name: Download docs artifact uses: actions/download-artifact@v4 From a877ef44fa7c6e8825660fb96ed403c56a5e31f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:10:48 -0300 Subject: [PATCH 6/8] ci(docs): drop Makefile from path filters --- .github/workflows/pr-main_mdbook.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index 70be1e7..57929cf 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -9,13 +9,11 @@ on: paths: - "docs/**" - "book.toml" - - "Makefile" - ".github/workflows/pr-main_mdbook.yml" pull_request: paths: - "docs/**" - "book.toml" - - "Makefile" - ".github/workflows/pr-main_mdbook.yml" workflow_dispatch: From 301bd62c4e6ac39b190838578f3bdb7ab04f0727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:13:35 -0300 Subject: [PATCH 7/8] ci(docs): bump upload/download-artifact to current majors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pinned `@v4` tags were three (upload) and four (download) major versions behind. No breaking changes affect our usage: - We download by `name:`, not `artifact-ids:`, so the v5 download-artifact path consistency change is irrelevant. - The v6/v7 Node.js 24 bumps require runner ≥ 2.327.1, which GitHub-hosted runners already satisfy. - The v7 upload-artifact direct-upload feature is opt-in (`archive: false`); default behavior is unchanged. - The v8 download-artifact hash-mismatch default of `error` is a security improvement, transparent on successful runs. --- .github/workflows/pr-main_mdbook.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index 57929cf..5eeb3ea 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -57,7 +57,7 @@ jobs: - name: Upload docs artifact if: ${{ github.ref == 'refs/heads/main' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: docs path: book/html @@ -91,7 +91,7 @@ jobs: cancel-in-progress: false steps: - name: Download docs artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: docs path: book/html From 27cb0e0af70e7b97de73503482edce74d19d449b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:16:00 -0300 Subject: [PATCH 8/8] ci(docs): exclude eprint.iacr.org from lychee The IACR preprint server returns 403 to lychee's default User-Agent (anti-bot protection); links work fine in a browser. Treating these as broken adds noise without catching real bitrot. --- .github/workflows/pr-main_mdbook.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-main_mdbook.yml b/.github/workflows/pr-main_mdbook.yml index 5eeb3ea..d419c90 100644 --- a/.github/workflows/pr-main_mdbook.yml +++ b/.github/workflows/pr-main_mdbook.yml @@ -76,7 +76,15 @@ jobs: - name: Check links uses: lycheeverse/lychee-action@v2 with: - args: --no-progress --exclude 'localhost' --exclude '127\.0\.0\.1' docs/ + # eprint.iacr.org returns 403 to non-browser User-Agents (anti-bot + # protection on the IACR preprint server); the URLs themselves are + # valid in a real browser. + args: >- + --no-progress + --exclude 'localhost' + --exclude '127\.0\.0\.1' + --exclude 'eprint\.iacr\.org' + docs/ fail: true deploy: