From 497df8651ef093566e98e9a85aa8b47b44d97b36 Mon Sep 17 00:00:00 2001 From: Not Darko <93942788+darkobas2@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:33:06 +0200 Subject: [PATCH 1/4] ci: add Swarm upload workflow Publishes the built docs to Swarm on every push to master and updates the "swarm-docs" feed, mirroring the setup already used by ethswarm-nextjs and ethswarm-blog-hugo. Runs on the self-hosted runner because the upload targets an internal bee node that GitHub-hosted runners cannot reach. Output dir is ./build (Docusaurus) and package-manager-cache is disabled because setup-node >=6.5.0 probes 'npm --version', which hangs on those runners. --- .github/workflows/swarm-upload.yaml | 66 +++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/swarm-upload.yaml diff --git a/.github/workflows/swarm-upload.yaml b/.github/workflows/swarm-upload.yaml new file mode 100644 index 00000000..f5e4fd06 --- /dev/null +++ b/.github/workflows/swarm-upload.yaml @@ -0,0 +1,66 @@ +name: Swarm Upload + +on: + push: + branches: + - 'master' + +jobs: + deploy: + # Self-hosted: the upload targets an internal bee node that is not + # reachable from GitHub-hosted runners. + runs-on: [self-hosted, Linux, bee] + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v6 + with: + node-version: '24' + # setup-node >=6.5.0 probes `npm --version` to auto-detect the package + # manager. That probe hangs indefinitely on the self-hosted runners. + # package.json declares no packageManager/devEngines field, so no + # dependency caching is lost by disabling it. + package-manager-cache: false + + - name: Build + run: | + npm ci + npm run build + + - name: Upload to Swarm + uses: ethersphere/swarm-actions/upload-dir@latest + id: upload + with: + dir: ./build + index-document: index.html + postage-batch-id: ${{ secrets.PRIVATE_POSTAGE_BATCH_ID }} + bee-url: ${{ secrets.PRIVATE_BEE_URL }} + timeout: 300000 + deferred: false + headers: | + authorization: ${{ secrets.PRIVATE_API_TOKEN }} + + - name: Setup feed + uses: ethersphere/swarm-actions/write-feed@latest + id: feed + with: + reference: ${{ steps.upload.outputs.reference }} + topic: "swarm-docs" + postage-batch-id: ${{ secrets.PRIVATE_POSTAGE_BATCH_ID }} + bee-url: ${{ secrets.PRIVATE_BEE_URL }} + signer: ${{ secrets.PRIVATE_SIGNER }} + headers: | + authorization: ${{ secrets.PRIVATE_API_TOKEN }} + + - uses: ethersphere/swarm-actions/reference-to-cid@v0 + id: cid + with: + reference: ${{ steps.feed.outputs.manifest }} + + - run: | + echo 'Chunk Reference: ${{ steps.upload.outputs.reference }}' + echo 'Feed Reference: ${{ steps.feed.outputs.reference }}' + echo 'Feed Manifest: ${{ steps.feed.outputs.manifest }}' + echo 'Feed Bzz.link: https://${{ steps.cid.outputs.cid }}.bzz.link' From 40e8d695f25cda741b1672834ad6277a4127fff9 Mon Sep 17 00:00:00 2001 From: Not Darko <93942788+darkobas2@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:36:59 +0200 Subject: [PATCH 2/4] ci: run Swarm upload on GitHub-hosted runners The bee endpoint is publicly reachable and gated by PRIVATE_API_TOKEN, so a self-hosted runner is not required. Verified via devcon-swarm-exporter, which uploads from ubuntu-latest and reaches the endpoint (its failures are HTTP 403 from a missing token, not a connectivity error). Avoids granting a public repository access to the private runner group. --- .github/workflows/swarm-upload.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/swarm-upload.yaml b/.github/workflows/swarm-upload.yaml index f5e4fd06..478efd0b 100644 --- a/.github/workflows/swarm-upload.yaml +++ b/.github/workflows/swarm-upload.yaml @@ -7,9 +7,10 @@ on: jobs: deploy: - # Self-hosted: the upload targets an internal bee node that is not - # reachable from GitHub-hosted runners. - runs-on: [self-hosted, Linux, bee] + # GitHub-hosted is sufficient: the bee endpoint is publicly reachable and + # gated by PRIVATE_API_TOKEN, so this does not need the self-hosted runners + # (and a public repo should not be granted access to them). + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: @@ -18,10 +19,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: '24' - # setup-node >=6.5.0 probes `npm --version` to auto-detect the package - # manager. That probe hangs indefinitely on the self-hosted runners. - # package.json declares no packageManager/devEngines field, so no - # dependency caching is lost by disabling it. + # No-op for caching here (package.json declares no packageManager / + # devEngines field), but it also skips setup-node's `npm --version` + # probe, which is known to hang on the self-hosted runners if this + # job is ever moved there. package-manager-cache: false - name: Build From a2dffad917fb88fdbf3140f834b5b831b6145b0b Mon Sep 17 00:00:00 2001 From: Not Darko <93942788+darkobas2@users.noreply.github.com> Date: Tue, 4 Aug 2026 10:20:09 +0200 Subject: [PATCH 3/4] fix(prebuild): awesome-swarm renamed its default branch to main ethersphere/awesome-swarm no longer has a master branch, so the prebuild fetch returned 404 and failed every build - including the gh-pages deploy, not just PR checks. Points SOURCE_URL, REPO_HTTP_BASE and RAW_HTTP_BASE at main. --- scripts/fetch-awesome-swarm.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/fetch-awesome-swarm.mjs b/scripts/fetch-awesome-swarm.mjs index fdf58f34..d6d62c52 100644 --- a/scripts/fetch-awesome-swarm.mjs +++ b/scripts/fetch-awesome-swarm.mjs @@ -5,12 +5,12 @@ import { mkdir, writeFile } from 'node:fs/promises'; import { resolve, dirname } from 'node:path'; const SOURCE_URL = - 'https://raw.githubusercontent.com/ethersphere/awesome-swarm/refs/heads/master/README.md'; + 'https://raw.githubusercontent.com/ethersphere/awesome-swarm/refs/heads/main/README.md'; const OUT_PATH = resolve(process.cwd(), 'docs/references/awesome-list.mdx'); -const REPO_HTTP_BASE = 'https://github.com/ethersphere/awesome-swarm/blob/master/'; -const RAW_HTTP_BASE = 'https://raw.githubusercontent.com/ethersphere/awesome-swarm/master/'; +const REPO_HTTP_BASE = 'https://github.com/ethersphere/awesome-swarm/blob/main/'; +const RAW_HTTP_BASE = 'https://raw.githubusercontent.com/ethersphere/awesome-swarm/main/'; function normalizeNewlines(str) { return str.replace(/\r\n/g, '\n'); From b8dce6e12ef3582b8c8d1f5bb57f2dec4da415d5 Mon Sep 17 00:00:00 2001 From: Not Darko <93942788+darkobas2@users.noreply.github.com> Date: Tue, 4 Aug 2026 10:24:56 +0200 Subject: [PATCH 4/4] fix(prebuild): make fetched awesome-swarm content MDX-safe The upstream README embeds a raw inside an . MDX requires void elements to be self-closed, so the build failed with end-tag-mismatch. Also rewrites relative paths in raw HTML src/href attributes - the existing rewriting only covered markdown link syntax, so the embedded logo would have 404'd on the rendered page. --- scripts/fetch-awesome-swarm.mjs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/scripts/fetch-awesome-swarm.mjs b/scripts/fetch-awesome-swarm.mjs index d6d62c52..cacc5f6d 100644 --- a/scripts/fetch-awesome-swarm.mjs +++ b/scripts/fetch-awesome-swarm.mjs @@ -30,6 +30,30 @@ function rewriteRelativeLinks(md) { return md; } +// MDX (Docusaurus) requires void HTML elements to be self-closed. The upstream +// README embeds a raw inside an ; left as-is the build fails with +// "end-tag-mismatch". +function selfCloseVoidTags(md) { + return md.replace( + /<(img|br|hr|input|meta|source)\b([^>]*?)\s*\/?>/gi, + (_m, tag, attrs) => `<${tag}${attrs.replace(/\s+$/, '')} />` + ); +} + +// Relative paths inside raw HTML attributes are not covered by the markdown +// link rewriting above, so they would 404 once rendered on the docs site. +function rewriteHtmlAssetPaths(md) { + md = md.replace( + /(<[^>]*\ssrc=")(?!https?:|data:|#|\/)([^"]+)(")/gi, + (_m, pre, path, post) => `${pre}${RAW_HTTP_BASE}${path}${post}` + ); + md = md.replace( + /(<[^>]*\shref=")(?!https?:|mailto:|#|\/)([^"]+)(")/gi, + (_m, pre, path, post) => `${pre}${REPO_HTTP_BASE}${path}${post}` + ); + return md; +} + async function main() { const res = await fetch(SOURCE_URL); if (!res.ok) throw new Error(`Fetch failed: ${res.status} ${res.statusText}`); @@ -39,7 +63,7 @@ async function main() { // Optional: strip upstream H1 to avoid a second big title under our frontmatter title md = md.replace(/^# .*\n+/, ''); - md = rewriteRelativeLinks(md); + md = selfCloseVoidTags(rewriteHtmlAssetPaths(rewriteRelativeLinks(md))); const header = `--- title: Awesome Swarm