From 25ccc1d931d3cc913282b4d119510718ab9181bb Mon Sep 17 00:00:00 2001 From: Alejandro <26930485+alejandroGM0@users.noreply.github.com> Date: Thu, 23 Apr 2026 19:04:46 +0200 Subject: [PATCH 1/5] build: migrate docs site to Zensical Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com> --- .github/workflows/docs.yml | 48 ++++++++++--------------------------- docs/assets/favicon.ico | Bin 2662 -> 15086 bytes docs/contributing.md | 10 ++++---- mkdocs.yml | 1 + requirements-docs.txt | 1 + 5 files changed, 20 insertions(+), 40 deletions(-) create mode 100644 requirements-docs.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c37d3683..01f14c7d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,12 +6,14 @@ on: paths: - "docs/**" - "mkdocs.yml" + - "requirements-docs.txt" - ".github/workflows/docs.yml" pull_request: branches: [main] paths: - "docs/**" - "mkdocs.yml" + - "requirements-docs.txt" - ".github/workflows/docs.yml" workflow_dispatch: @@ -30,30 +32,21 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v6 with: python-version: "3.x" - - name: Set cache date - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - - uses: actions/cache@v5 - with: - key: mkdocs-material-${{ env.cache_id }} - path: ~/.cache - restore-keys: | - mkdocs-material- + - name: Install docs dependencies + run: pip install -r requirements-docs.txt - - name: Install MkDocs and Material - run: pip install mkdocs-material "pymdown-extensions" + - name: Build site + run: zensical build --clean - name: Deploy to GitHub Pages - run: mkdocs gh-deploy --force + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site # PR preview: build and deploy to .../pr// deploy-preview: @@ -62,30 +55,15 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v6 with: python-version: "3.x" - - name: Set cache date - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - - uses: actions/cache@v5 - with: - key: mkdocs-material-${{ env.cache_id }} - path: ~/.cache - restore-keys: | - mkdocs-material- - - - name: Install MkDocs and Material - run: pip install mkdocs-material "pymdown-extensions" + - name: Install docs dependencies + run: pip install -r requirements-docs.txt - name: Build site - run: mkdocs build --strict + run: zensical build --clean - name: Deploy PR preview to gh-pages uses: peaceiris/actions-gh-pages@v4 diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico index 255a5cc3e984ca45bc8c6e7208c0471ae8b6184d..8428b4a06b80901ef3abcc8653fc57ab4c957928 100644 GIT binary patch literal 15086 zcmeI3ONbmr7{_a~Np|BS8y_IVLk4`UdT_HLiV!nmBnt|O5nuSqY9a(QC_a*d%8Ccw z1mZzFDA5qT7(@^s2L&;{P%k-n5j>bIUN%PE#Aj9=r~UnBrZ=@a)ji!k)6*U7hTm3q z)%Vr+)mJ^;U0?Sa(_?zg(2$`lnSB=+bGb35RB9YAG3Fq3ODNU8Wxg@bATWpwDrOW^ zU)#>F7scNq0!0Lh2ow?MP6TdQ`u;rN${cT#4lGE>ym;2!(%3!+#$XSOK+Aq51MA>b zcs&98(0Lk`y1j~4i@y9AhkwC?Z((^WZDdx$A&^c|s_5>5-d6U4+R^QYPr&!#FZS;W zlK1N+1Akk3zJAAHz4ziRM$+qlI%&-MR1v*ymVY{6BzSXD67qy!ihm{tra(|90f2tvSfb z|9kMkK9^56|F__O#foE8|L;Uc*J%GYkX8JX`o9t1?RhKwZ@zy;x&9}O|7ecmPlh3U z!}t5(AIRJJujiP>;ocjQWB)(Kx6}Uz{y)Gt{bzNZLwULWM2-K0h5skcQexqH{!eoK zj2i!kv8i=e*4LjAeE8Y&V-NcaPejJOhs<_MC(H9+^R^ZC!(O<=?eXkdYg*<8dw*u(G^{C^yB5&v*c-K|~lU)P2w-PYTyrC+UyUxGB_uf7k$ zW>^8k9YXo8N8kvg?Z5gz4$6ld?hx|ZUibQA=*08G=k)FO*X>fa+sR+`q5ocn{=bXu z$k$)jFA)73L3!JwpnGaJkS*EV;PNR>9ewn|rBH^|T|%~GGjZ-Y&KL{XA_7GOiU<@D zIQb&r)iz@imxrpR$3uUh!k^HxJW+2iAIqeyj@9jYUQk){ug~*prKVDT&Mlwfdn(np z8@GnOzSAnf)iCp&R?AE)!o^lZAma$|e0c`k3(9vnpEapJ3$$KUZbkW_Ak@AI&Wt)N zWbX&Pr~4lCxnt8t{X-3zC~G(MwY8~}fC^-Hg8KavMqFal?`p_Iu`ijOu6|r~$nI$L zE&FQ&WNiCiBliR-Ppb94P|oN8`2Ov1_2a5TCysr+7rG;$pPOj&*Y9cBSI!|QMr&yM z9TbC3+kd{%uj~h%BiGXQM?u*pFh`+8s{&x?Z9{*MJy&rai^BW}UZw6f- z(>VWn=yx&nyT-0M+HQqZ><8Jj>#=5WG{uFo?WIL&S=_xPy z0lW?GL`1S5!Y`1-{$h8u+jA4PkL?>^0)B!Y+eCe+uk~Rz?KWiR!ypWI2=x{F zJ8j#iV!wz$8WE`H8=C)Cs-`?$G0Ubat6?k}RpdOcL3P=*K_AHAl$$NoYm}!@TA~Cq zd5RqWQurO82A9FicNgqm2zu7F(L0$_8P=?gIz5wr0~f1-Qum5CLH9|053P4ZS~I9@ zdak2R&s2KHS^IwjO5K}2g0Dc&o7ccL_!ZX8GG4c*|DUk+7jdlb*k18v*&l+#5Jg}2 zIlT|H^_O!j8&UN?MfPRO{tAwd#L$=h-Io4Jj&K~mjvI3nCMS)VK*rYj4{1x0egFUf literal 2662 zcmeHIOAdlC5FJ-WU79EbO<2121RQ|{RF@vXBlJ`btaM@Gk|Sv10X#zOlvJArL5)Ti zKwdkYc0RmL3qVJ`jDc!Hi2DE|0GKH#70eY#PtU#wFdUT7injS{22z%7d5~XX4?{Q6 z#KVaDnQTHD;L5eN$Hsm+!N}H=%zYGQzP?A{+S+4d+Ze+`k$Si?Sg>quY$+Gprz@Lv z^)d3owWG1-S#$Y;7y<6u7*soDZPxBb%atybUvsro_X}s)F&S^x->TgzzqalZXVCUn zC38P7O0J(f=Q^ip9#aiJgCy9zd|#Qpf3L6qjytE#|A|NO=`OC~veo?$15!$1/` and comments the preview URL on the PR. +- **Push to `main`:** Installs `requirements-docs.txt`, builds the site with `zensical build --clean`, and deploys the production site to the GitHub Pages root. +- **Pull request:** Installs `requirements-docs.txt`, builds the site with `zensical build --clean`, deploys a preview to `/pr//`, and comments the preview URL on the PR. Ensure **GitHub Pages** is enabled and set the source to **Deploy from a branch** → branch: `gh-pages`, folder: `/ (root)`. diff --git a/mkdocs.yml b/mkdocs.yml index 783cb8bc..5420600a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ repo_url: https://github.com/hiero-ledger/hiero-enterprise-java theme: name: material + variant: classic favicon: assets/favicon.ico logo: assets/logo.svg icon: diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 00000000..4a815ff9 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1 @@ +zensical==0.0.36 From 686a86b392bec2f1b130910de29083ebb05a90a8 Mon Sep 17 00:00:00 2001 From: Alejandro <26930485+alejandroGM0@users.noreply.github.com> Date: Thu, 23 Apr 2026 19:47:44 +0200 Subject: [PATCH 2/5] build: pin Zensical in docs workflow Install Zensical directly in the docs workflow and contributor docs instead of keeping a separate requirements file. Keep the version explicit while Zensical is still in 0.0.x to avoid unexpected breaking changes. Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com> --- .github/workflows/docs.yml | 6 ++---- docs/contributing.md | 6 +++--- requirements-docs.txt | 1 - 3 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 requirements-docs.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 01f14c7d..c471fbf3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,14 +6,12 @@ on: paths: - "docs/**" - "mkdocs.yml" - - "requirements-docs.txt" - ".github/workflows/docs.yml" pull_request: branches: [main] paths: - "docs/**" - "mkdocs.yml" - - "requirements-docs.txt" - ".github/workflows/docs.yml" workflow_dispatch: @@ -37,7 +35,7 @@ jobs: python-version: "3.x" - name: Install docs dependencies - run: pip install -r requirements-docs.txt + run: pip install zensical==0.0.36 - name: Build site run: zensical build --clean @@ -60,7 +58,7 @@ jobs: python-version: "3.x" - name: Install docs dependencies - run: pip install -r requirements-docs.txt + run: pip install zensical==0.0.36 - name: Build site run: zensical build --clean diff --git a/docs/contributing.md b/docs/contributing.md index 1e48c45e..fbe5e2e8 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -18,7 +18,7 @@ The site is built with [Zensical](https://zensical.org/docs/get-started/) using From the repository root: ```bash -pip install -r requirements-docs.txt +pip install zensical==0.0.36 zensical serve ``` @@ -28,7 +28,7 @@ Then open http://127.0.0.1:8000 . The `.github/workflows/docs.yml` workflow: -- **Push to `main`:** Installs `requirements-docs.txt`, builds the site with `zensical build --clean`, and deploys the production site to the GitHub Pages root. -- **Pull request:** Installs `requirements-docs.txt`, builds the site with `zensical build --clean`, deploys a preview to `/pr//`, and comments the preview URL on the PR. +- **Push to `main`:** Installs `zensical==0.0.36`, builds the site with `zensical build --clean`, and deploys the production site to the GitHub Pages root. +- **Pull request:** Installs `zensical==0.0.36`, builds the site with `zensical build --clean`, deploys a preview to `/pr//`, and comments the preview URL on the PR. Ensure **GitHub Pages** is enabled and set the source to **Deploy from a branch** → branch: `gh-pages`, folder: `/ (root)`. diff --git a/requirements-docs.txt b/requirements-docs.txt deleted file mode 100644 index 4a815ff9..00000000 --- a/requirements-docs.txt +++ /dev/null @@ -1 +0,0 @@ -zensical==0.0.36 From 073adf74ff77173bda970160d907e9e440e04898 Mon Sep 17 00:00:00 2001 From: Alejandro <26930485+alejandroGM0@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:23:21 +0200 Subject: [PATCH 3/5] ci(docs): skip PR preview deploy for fork PRs GITHUB_TOKEN from pull_request workflows cannot write to the base repo when the PR head is a fork, so gh-pages push fails with 403. Still run the docs build; deploy and comment only for same-repo PRs. Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com> --- .github/workflows/docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c471fbf3..295b20e4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -63,7 +63,10 @@ jobs: - name: Build site run: zensical build --clean + # Fork PRs get a read-only token on the base repo, so we cannot push to gh-pages. + # Same-repo PRs still get the preview under /pr//. - name: Deploy PR preview to gh-pages + if: github.event.pull_request.head.repo.full_name == github.repository uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -72,6 +75,7 @@ jobs: keep_files: true - name: Comment PR with preview URL + if: github.event.pull_request.head.repo.full_name == github.repository uses: actions/github-script@v9 with: script: | From 31fe2d2b3dd6279a56b32216c0eba225f408f9b3 Mon Sep 17 00:00:00 2001 From: Alejandro <26930485+alejandroGM0@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:50:02 +0200 Subject: [PATCH 4/5] ci(docs): preview fork PRs via workflow_run deploy Split the Docs workflow in two. The Docs workflow now only builds the site and uploads a docs-preview artifact, which is safe for fork PRs. A new Docs Preview Deploy workflow runs on workflow_run, downloads the artifact, and publishes the preview to gh-pages/pr// and comments the URL on the PR, so fork PRs get previews too. Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com> --- .github/workflows/docs-preview.yml | 58 ++++++++++++++++++++++++++++++ .github/workflows/docs.yml | 49 ++++++++++++------------- docs/contributing.md | 9 +++-- 3 files changed, 87 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/docs-preview.yml diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml new file mode 100644 index 00000000..295c7ca5 --- /dev/null +++ b/.github/workflows/docs-preview.yml @@ -0,0 +1,58 @@ +name: Docs Preview Deploy + +# Runs after the "Docs" workflow completes. Because workflow_run runs in +# the base-repo context, it has a writable GITHUB_TOKEN even when the +# originating run was a fork PR. This lets us deploy a /pr// +# preview for any PR without giving PR-controlled code access to secrets. +on: + workflow_run: + workflows: ["Docs"] + types: [completed] + +permissions: + actions: read # download the artifact from the triggering Docs run + contents: write # push the preview to gh-pages + pull-requests: write # comment the preview URL on the PR + +concurrency: + group: "docs-preview-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}" + cancel-in-progress: true + +jobs: + deploy: + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + steps: + - name: Download preview artifact + uses: actions/download-artifact@v4 + with: + name: docs-preview + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Read PR number + id: pr + run: echo "number=$(cat pr/NR | tr -d '[:space:]')" >> "$GITHUB_OUTPUT" + + - name: Deploy PR preview to gh-pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site + destination_dir: "pr/${{ steps.pr.outputs.number }}" + keep_files: true + + - name: Comment PR with preview URL + uses: actions/github-script@v9 + with: + script: | + const prNumber = parseInt('${{ steps.pr.outputs.number }}', 10); + const url = `https://hiero-ledger.github.io/hiero-enterprise-java/pr/${prNumber}/`; + await github.rest.issues.createComment({ + issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + body: `📖 **Docs preview** for this PR: ${url}` + }); diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 295b20e4..addaad1a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,26 +7,29 @@ on: - "docs/**" - "mkdocs.yml" - ".github/workflows/docs.yml" + - ".github/workflows/docs-preview.yml" pull_request: branches: [main] paths: - "docs/**" - "mkdocs.yml" - ".github/workflows/docs.yml" + - ".github/workflows/docs-preview.yml" workflow_dispatch: permissions: - contents: write - pull-requests: write + contents: read concurrency: - group: "pages-${{ github.ref }}" + group: "docs-${{ github.ref }}" cancel-in-progress: true jobs: deploy-production: if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v6 @@ -46,8 +49,11 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./site - # PR preview: build and deploy to .../pr// - deploy-preview: + # PR preview: build only, upload the site as an artifact. + # The deploy to gh-pages/pr// runs in docs-preview.yml via + # workflow_run, so fork PRs can also get a preview without granting + # write access to PR-controlled code. + build-preview: if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: @@ -63,26 +69,17 @@ jobs: - name: Build site run: zensical build --clean - # Fork PRs get a read-only token on the base repo, so we cannot push to gh-pages. - # Same-repo PRs still get the preview under /pr//. - - name: Deploy PR preview to gh-pages - if: github.event.pull_request.head.repo.full_name == github.repository - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./site - destination_dir: "pr/${{ github.event.pull_request.number }}" - keep_files: true + - name: Save PR metadata + run: | + mkdir -p pr + echo "${{ github.event.pull_request.number }}" > pr/NR - - name: Comment PR with preview URL - if: github.event.pull_request.head.repo.full_name == github.repository - uses: actions/github-script@v9 + - name: Upload preview artifact + uses: actions/upload-artifact@v4 with: - script: | - const url = `https://hiero-ledger.github.io/hiero-enterprise-java/pr/${{ github.event.pull_request.number }}/`; - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `📖 **Docs preview** for this PR: ${url}` - }); + name: docs-preview + path: | + site + pr + if-no-files-found: error + retention-days: 7 diff --git a/docs/contributing.md b/docs/contributing.md index fbe5e2e8..633e9b18 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -26,9 +26,12 @@ Then open http://127.0.0.1:8000 . ## Publishing -The `.github/workflows/docs.yml` workflow: +Two workflows drive publication: -- **Push to `main`:** Installs `zensical==0.0.36`, builds the site with `zensical build --clean`, and deploys the production site to the GitHub Pages root. -- **Pull request:** Installs `zensical==0.0.36`, builds the site with `zensical build --clean`, deploys a preview to `/pr//`, and comments the preview URL on the PR. +- **`.github/workflows/docs.yml`** + - **Push to `main`:** builds the site with `zensical build --clean` and publishes it to the GitHub Pages root. + - **Pull request:** builds the site and uploads it (together with the PR number) as a `docs-preview` artifact. No deploy happens here, so this step is safe for PRs coming from forks. +- **`.github/workflows/docs-preview.yml`** + - Triggered by the `Docs` workflow completing. It downloads the `docs-preview` artifact, deploys it to `gh-pages/pr//`, and comments the preview URL on the PR. Because it runs in the base-repo context, fork PRs also get a preview. Ensure **GitHub Pages** is enabled and set the source to **Deploy from a branch** → branch: `gh-pages`, folder: `/ (root)`. From e9e27a8525a895f73cb0d5a362cd85424f705d00 Mon Sep 17 00:00:00 2001 From: Alejandro <26930485+alejandroGM0@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:54:08 +0200 Subject: [PATCH 5/5] ci(docs): harden preview workflow against script injection Treat the PR number from the artifact as untrusted: strictly validate it as a positive integer, and pass it to actions/github-script via env instead of an inline expression so no attacker-controlled text can be expanded into the script body. Signed-off-by: Alejandro <26930485+alejandroGM0@users.noreply.github.com> --- .github/workflows/docs-preview.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index 295c7ca5..e8ed7759 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -32,9 +32,18 @@ jobs: run-id: ${{ github.event.workflow_run.id }} github-token: ${{ secrets.GITHUB_TOKEN }} + # The artifact comes from a run on PR-controlled code (possibly a fork), + # so pr/NR must be treated as untrusted input. We only accept a plain + # positive integer to prevent script/path injection. - name: Read PR number id: pr - run: echo "number=$(cat pr/NR | tr -d '[:space:]')" >> "$GITHUB_OUTPUT" + run: | + raw=$(tr -d '[:space:]' < pr/NR) + if [[ ! "$raw" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::Invalid PR number in artifact: '$raw'" + exit 1 + fi + echo "number=$raw" >> "$GITHUB_OUTPUT" - name: Deploy PR preview to gh-pages uses: peaceiris/actions-gh-pages@v4 @@ -44,11 +53,15 @@ jobs: destination_dir: "pr/${{ steps.pr.outputs.number }}" keep_files: true + # Pass the PR number through env rather than ${{ }} interpolation so no + # attacker-controlled text can be expanded into the inline script body. - name: Comment PR with preview URL uses: actions/github-script@v9 + env: + PR_NUMBER: ${{ steps.pr.outputs.number }} with: script: | - const prNumber = parseInt('${{ steps.pr.outputs.number }}', 10); + const prNumber = Number(process.env.PR_NUMBER); const url = `https://hiero-ledger.github.io/hiero-enterprise-java/pr/${prNumber}/`; await github.rest.issues.createComment({ issue_number: prNumber,