From 0822225df72f9b619d8d75c505595afa964d0394 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Wed, 3 Jun 2026 17:10:55 +0100 Subject: [PATCH 1/3] feat: add per-PR documentation preview deployments Build every pull request and publish it to a unique, navigable GitHub Pages URL so reviewers can see the rendered docs before merging. Production (AWS Amplify at docs.cartesi.io) is unaffected; previews are served from a separate origin (https://.github.io//pr-preview/pr-/). Fork-safe by design (no PAT or secrets), via a two-workflow split: - .github/workflows/preview-build.yml (pull_request): builds the PR with a read-only token and NO access to secrets, then uploads the static site as an artifact. Replaces the build-only build.yml so each PR builds exactly once. - .github/workflows/preview-deploy.yml (workflow_run): runs from the default branch in the trusted base-repo context, downloads the artifact, publishes it to the gh-pages branch under pr-preview/pr-/, posts a sticky comment with the URL, and removes the preview when the PR is closed. It never executes PR code, so previews work for pull requests from forks without exposing any secret. - docusaurus.config.js: url/baseUrl are now env-overridable (DOCS_URL/DOCS_BASE_URL) and default to docs.cartesi.io and "/", so local builds and Amplify are unchanged. - src/pages/index.js and src/components/SelectVersion/DropdownVersion.js: resolve the homepage redirect and the version dropdown against baseUrl so they work under the preview sub-path. Both are no-ops in production (baseUrl "/"). One-time setup (maintainer): - Settings -> Actions -> General -> Workflow permissions: "Read and write". - After the first preview deploy creates the gh-pages branch, set Settings -> Pages source to the gh-pages branch (root). - If branch protection requires the old "Build" check, update it to "preview-build". --- .github/workflows/build.yml | 26 --- .github/workflows/preview-build.yml | 82 ++++++++ .github/workflows/preview-deploy.yml | 179 ++++++++++++++++++ docusaurus.config.js | 8 +- .../SelectVersion/DropdownVersion.js | 15 +- src/pages/index.js | 6 +- 6 files changed, 285 insertions(+), 31 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/preview-build.yml create mode 100644 .github/workflows/preview-deploy.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 6cce4096..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - main - - "release/*" - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: "22" - - - name: Install dependencies - run: yarn install - - - name: Build Docusaurus site - run: yarn build diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml new file mode 100644 index 00000000..94a50f12 --- /dev/null +++ b/.github/workflows/preview-build.yml @@ -0,0 +1,82 @@ +name: preview-build + +# Builds the docs for every PR and uploads the result as an artifact. +# +# SECURITY: this is triggered by `pull_request` (NOT `pull_request_target`), so code +# from forked PRs runs with a read-only token and NO access to repository secrets. This +# job only ever produces a build artifact. All privileged work (publishing to GitHub +# Pages, commenting on the PR) happens in the separate `preview-deploy` workflow, which +# runs in the trusted base-repo context and never executes PR code. + +on: + pull_request: + branches: [main, "release/*"] + types: [opened, synchronize, reopened, closed] + +permissions: + contents: read + +# One build per PR; newer pushes cancel in-flight builds for the same PR. +concurrency: + group: preview-build-${{ github.event.number }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout PR code + uses: actions/checkout@v4 + with: + # Untrusted PR code runs in this job; don't leave the auth token behind in + # .git/config where a build script could read it. + persist-credentials: false + + # Carry the PR number + event action across to the trusted deploy workflow. + # The `workflow_run` event does not expose the PR number for forked PRs, so we + # pass it explicitly via this artifact. + - name: Save PR metadata + run: | + mkdir -p pr-meta + echo "${{ github.event.number }}" > pr-meta/number + echo "${{ github.event.action }}" > pr-meta/action + - name: Upload PR metadata + uses: actions/upload-artifact@v4 + with: + name: pr-meta + path: pr-meta/ + retention-days: 1 + if-no-files-found: error + + # On `closed` the deploy workflow only needs the metadata above to tear the + # preview down, so skip the (slow) build entirely. + - name: Set up Node.js + if: github.event.action != 'closed' + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: yarn + + # Plain `yarn install` to mirror the upstream build (build.yml / amplify.yml). + - name: Install dependencies + if: github.event.action != 'closed' + run: yarn install + + - name: Build Docusaurus site + if: github.event.action != 'closed' + env: + # Point this build at the GitHub Pages sub-path the PR will be published to. + # The defaults in docusaurus.config.js keep production (Amplify) unchanged. + DOCS_URL: https://${{ github.repository_owner }}.github.io + DOCS_BASE_URL: /${{ github.event.repository.name }}/pr-preview/pr-${{ github.event.number }}/ + run: yarn build + + - name: Upload site artifact + if: github.event.action != 'closed' + uses: actions/upload-artifact@v4 + with: + name: pr-preview-site + path: build/ + retention-days: 1 + if-no-files-found: error diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml new file mode 100644 index 00000000..479e3bba --- /dev/null +++ b/.github/workflows/preview-deploy.yml @@ -0,0 +1,179 @@ +name: preview-deploy + +# Publishes (and tears down) the per-PR preview produced by `preview-build`. +# +# This workflow runs from the DEFAULT branch in the trusted base-repo context, so it has +# write access to the repository. It NEVER executes PR code: it only downloads the +# already-built static artifact and publishes it to the `gh-pages` branch under +# pr-preview/pr-/. This is the GitHub-recommended pattern for safely handling +# artifacts that were built from forked pull requests. + +on: + workflow_run: + workflows: ["preview-build"] + types: [completed] + +permissions: + contents: write # push to gh-pages + pull-requests: write # post/update the preview comment + issues: write # the PR comment is posted via the issues endpoint + actions: read # download artifacts from the triggering run + +# Serialize all writes to the shared gh-pages branch across every PR. +concurrency: + group: gh-pages-deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 10 + # Only act on successful builds that originated from a pull_request. + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Download PR metadata + uses: actions/download-artifact@v4 + with: + name: pr-meta + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: pr-meta + + - name: Read and validate PR metadata + id: meta + run: | + set -euo pipefail + number="$(cat pr-meta/number)" + action="$(cat pr-meta/action)" + # pr-meta is produced in the untrusted build context, so validate before + # using it in filesystem paths / API calls (a forked PR could tamper with it). + if ! [[ "$number" =~ ^[0-9]+$ ]]; then + echo "::error::Refusing to deploy: PR number '$number' is not a positive integer." + exit 1 + fi + echo "number=$number" >> "$GITHUB_OUTPUT" + echo "action=$action" >> "$GITHUB_OUTPUT" + + - name: Download site artifact + if: steps.meta.outputs.action != 'closed' + uses: actions/download-artifact@v4 + with: + name: pr-preview-site + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: site + + - name: Publish or remove preview on gh-pages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ steps.meta.outputs.number }} + PR_ACTION: ${{ steps.meta.outputs.action }} + SRC_SHA: ${{ github.event.workflow_run.head_sha }} + run: | + set -euo pipefail + REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + TARGET="pr-preview/pr-${PR_NUMBER}" + + # A successful (non-close) build must contain a homepage; bail rather than + # publish a broken/empty preview. + if [ "$PR_ACTION" != "closed" ] && [ ! -f "${GITHUB_WORKSPACE}/site/index.html" ]; then + echo "::error::Built site artifact is missing index.html; aborting." + exit 1 + fi + + # Shallow clone of just gh-pages (fast even as its history grows), or start an + # orphan branch on the very first deploy. + if git clone --quiet --depth 1 --branch gh-pages --single-branch "$REPO_URL" gh-pages 2>/dev/null; then + cd gh-pages + else + mkdir gh-pages && cd gh-pages + git init --quiet + git remote add origin "$REPO_URL" + git checkout --quiet --orphan gh-pages + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Re-apply only this PR's directory onto the current tree, leaving every other + # PR's preview intact. Idempotent, so it is safe to replay on a push retry. + apply_change() { + rm -rf "$TARGET" + if [ "$PR_ACTION" != "closed" ]; then + mkdir -p "$TARGET" + cp -R "${GITHUB_WORKSPACE}/site/." "$TARGET/" + fi + touch .nojekyll # serve Docusaurus output verbatim (skip GitHub Pages' Jekyll) + git add -A + } + + if [ "$PR_ACTION" != "closed" ]; then + MSG="Deploy preview for PR #${PR_NUMBER} (${SRC_SHA})" + else + MSG="Remove preview for PR #${PR_NUMBER}" + fi + + apply_change + if git diff --quiet --cached; then + echo "Nothing to publish." + exit 0 + fi + git commit --quiet -m "$MSG" + + # Deploys are serialized by the workflow concurrency group, so a plain push + # should succeed. If the branch moved anyway, adopt the latest tree and + # re-apply our isolated change (never clobbering other PRs) before retrying. + if ! git push --quiet origin gh-pages; then + echo "Push rejected; re-syncing gh-pages and retrying." + git fetch --quiet --depth 1 origin gh-pages + git reset --hard origin/gh-pages + apply_change + git diff --quiet --cached || git commit --quiet -m "$MSG" + git push --quiet origin gh-pages + fi + + - name: Comment the preview URL on the PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ steps.meta.outputs.number }} + PR_ACTION: ${{ steps.meta.outputs.action }} + SRC_SHA: ${{ github.event.workflow_run.head_sha }} + run: | + set -euo pipefail + OWNER="${GITHUB_REPOSITORY%%/*}" + REPO="${GITHUB_REPOSITORY##*/}" + OWNER_LC="$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')" + URL="https://${OWNER_LC}.github.io/${REPO}/pr-preview/pr-${PR_NUMBER}/" + SHORT_SHA="${SRC_SHA:0:7}" + MARKER="" + + if [ "$PR_ACTION" != "closed" ]; then + { + echo "$MARKER" + echo "### 📖 Docs preview is ready" + echo "" + echo "**Preview:** $URL" + echo "" + echo "Built from commit \`$SHORT_SHA\` and published to GitHub Pages. Updates on every push; removed automatically when this PR is closed." + } > body.md + else + { + echo "$MARKER" + echo "### 📖 Docs preview removed" + echo "" + echo "The preview for this PR was taken down because the PR is now closed." + } > body.md + fi + + # Sticky behaviour: update our existing marked comment if present, else create it. + existing="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ + --jq "[.[] | select(.body | contains(\"${MARKER}\"))][0].id // empty")" + if [ -n "$existing" ]; then + gh api -X PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${existing}" -f body="$(cat body.md)" >/dev/null + echo "Updated comment $existing" + else + gh api -X POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$(cat body.md)" >/dev/null + echo "Created preview comment" + fi + echo "Preview URL: $URL" diff --git a/docusaurus.config.js b/docusaurus.config.js index 0ffcc7af..b3edf0a8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -56,8 +56,12 @@ const openApiDocsConfig = versions.reduce((config, version) => { const config = { title: "Cartesi Documentation", tagline: "Application-specific rollups with a Linux runtime.", - url: "https://docs.cartesi.io", - baseUrl: "/", + // url/baseUrl default to production (AWS Amplify @ docs.cartesi.io). PR-preview + // builds override them via env vars to publish under a GitHub Pages sub-path + // (https://.github.io//pr-preview/pr-/). The vars are unset for + // local builds and on Amplify, so production behavior is unchanged. + url: process.env.DOCS_URL || "https://docs.cartesi.io", + baseUrl: process.env.DOCS_BASE_URL || "/", trailingSlash: true, onBrokenLinks: "ignore", onBrokenMarkdownLinks: "throw", diff --git a/src/components/SelectVersion/DropdownVersion.js b/src/components/SelectVersion/DropdownVersion.js index df00bf79..1004e1a9 100644 --- a/src/components/SelectVersion/DropdownVersion.js +++ b/src/components/SelectVersion/DropdownVersion.js @@ -5,6 +5,7 @@ import { } from "@docusaurus/plugin-content-docs/client"; import { useLocation } from "@docusaurus/router"; import useGlobalData from "@docusaurus/useGlobalData"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import clsx from "clsx"; @@ -14,11 +15,21 @@ import NavbarItem from "@theme/NavbarItem"; const useVersionsForDropdown = () => { const global = useGlobalData(); const location = useLocation(); + const { + siteConfig: { baseUrl }, + } = useDocusaurusContext(); const sidebars = Object.keys(global["docusaurus-plugin-content-docs"]); + // Detect the active docs plugin from the first path segment *after* the site + // baseUrl. Using the raw pathname breaks when the site is served from a + // sub-path (e.g. GitHub Pages PR previews at //pr-preview/pr-/), + // where the first segment is the baseUrl prefix instead of the docs route. const docsPluginId = sidebars.find((sidebar) => { - const path = location.pathname.split("/")[1]; - return sidebar.includes(path); + const relativePath = location.pathname.startsWith(baseUrl) + ? location.pathname.slice(baseUrl.length) + : location.pathname.replace(/^\/+/, ""); + const path = relativePath.split("/")[0]; + return path && sidebar.includes(path); }); const versions = useVersions(docsPluginId); diff --git a/src/pages/index.js b/src/pages/index.js index 8f58658e..4cab78f0 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,10 +1,14 @@ import React from "react"; import { Redirect } from "@docusaurus/router"; +import useBaseUrl from "@docusaurus/useBaseUrl"; function Home() { // return ; // return ; - return ; + // useBaseUrl keeps the redirect correct under any baseUrl. Production uses "/" + // (target unchanged), but PR previews are served from a sub-path where a bare + // "/get-started" would resolve outside the preview and 404. + return ; } export default Home; From 400c5ab4301ec4e635ed8ba4d41e0fa8c42d800a Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 4 Jun 2026 17:56:06 +0100 Subject: [PATCH 2/3] fix(serve-markdown): write .md files baseUrl-relative so PR previews aren't empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin keyed its URL map on doc.permalink, which includes the site baseUrl. Under a sub-path baseUrl (GitHub Pages PR previews at //pr-preview/pr-/), postBuild wrote each page's full .md to a baseUrl-nested dead path, so the URL people request (.../page.md) only got the title-only generated-index stub. Normalize permalinks to baseUrl-relative in buildUrlMap — one change that fixes the .md output paths, the llms.txt index/alias route matching, and the llms-full.txt page URLs. No-op at baseUrl "/", so production is unchanged. --- plugins/serve-markdown.js | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/plugins/serve-markdown.js b/plugins/serve-markdown.js index 309b48f2..a2439f19 100644 --- a/plugins/serve-markdown.js +++ b/plugins/serve-markdown.js @@ -46,6 +46,26 @@ function resolveSource(siteDir, source) { return source; } +/** + * Strip the site baseUrl from a permalink so paths are baseUrl-relative. + * + * Docusaurus permalinks include the configured baseUrl, but the build output + * (and the URLs people request) are relative to it. Under a sub-path baseUrl — + * e.g. GitHub Pages PR previews at //pr-preview/pr-/ — using the raw + * permalink writes .md files to a baseUrl-nested dead path and breaks the + * llms.txt index/alias matching. At baseUrl "/" this is a no-op. + * + * @param {string} permalink + * @param {string} baseUrl e.g. "/" or "/repo/pr-preview/pr-12/" + * @returns {string} + */ +function stripBaseUrl(permalink, baseUrl) { + if (baseUrl && baseUrl !== '/' && permalink.startsWith(baseUrl)) { + return '/' + permalink.slice(baseUrl.length); + } + return permalink; +} + /** * Walk the allContent object and collect every { permalink → absFilePath } * pair from all @docusaurus/plugin-content-docs instances. @@ -58,9 +78,10 @@ function resolveSource(siteDir, source) { * * @param {Record>} allContent * @param {string} siteDir + * @param {string} baseUrl site baseUrl; keys are stored baseUrl-relative * @returns {Map} permalink → absolute source path */ -function buildUrlMap(allContent, siteDir) { +function buildUrlMap(allContent, siteDir, baseUrl) { const map = new Map(); for (const instancesById of Object.values(allContent)) { @@ -75,9 +96,13 @@ function buildUrlMap(allContent, siteDir) { for (const doc of docs) { const { permalink, source } = doc; if (!permalink || !source) continue; + // Store keys baseUrl-relative so the .md output paths and the llms + // index/alias routes line up under any baseUrl (incl. sub-path + // previews). No-op when baseUrl is "/". + const sitePermalink = stripBaseUrl(permalink, baseUrl); // Normalize: always store with trailing slash so toPermalink lookups // are consistent regardless of how Docusaurus emits the permalink. - const normalised = permalink.endsWith('/') ? permalink : permalink + '/'; + const normalised = sitePermalink.endsWith('/') ? sitePermalink : sitePermalink + '/'; map.set(normalised, resolveSource(siteDir, source)); } } @@ -386,7 +411,7 @@ module.exports = function serveMarkdownPlugin(context) { // contentLoaded, and before configureWebpack / postBuild. // async allContentLoaded({ allContent }) { - const discovered = buildUrlMap(allContent, siteDir); + const discovered = buildUrlMap(allContent, siteDir, siteConfig.baseUrl); for (const [permalink, absPath] of discovered) { urlMap.set(permalink, absPath); } From 210a6f6c748d92c5bbe4eb9e3be47926ff7b8200 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Fri, 5 Jun 2026 01:33:47 +0100 Subject: [PATCH 3/3] ci: production deploy to GitHub Pages + domain-aware previews - deploy-prod.yml: build on push to main and publish to the gh-pages root, preserving pr-preview/ so PR previews keep working on the same branch. - preview workflows: when a root custom domain is configured (the PAGES_CNAME repo variable), previews build at /pr-preview/pr-N/ and the comment links there; otherwise unchanged. Environment (URL / baseUrl / CNAME) is driven by repo Actions variables, so the same workflows serve the github.io test, a staging domain, and production with no edits. --- .github/workflows/deploy-prod.yml | 93 ++++++++++++++++++++++++++++ .github/workflows/preview-build.yml | 11 ++-- .github/workflows/preview-deploy.yml | 11 +++- 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/deploy-prod.yml diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 00000000..56f787ca --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,93 @@ +name: deploy-prod + +# Builds the docs on every push to the default branch and publishes them to the +# ROOT of the gh-pages branch — the production site — while preserving any +# pr-preview/ directories so PR previews are never clobbered. +# +# Runs in the trusted base-repo context (push to the default branch), so it has +# write access and needs no fork-safety split. +# +# Environment is driven by repo Actions variables, so the SAME workflow serves the +# github.io sub-path, a staging custom domain, and production with no code edits: +# vars.DOCS_URL default: https://.github.io +# vars.DOCS_BASE_URL default: // (set to "/" for a root custom domain) +# vars.PAGES_CNAME optional: when set, written as the gh-pages CNAME each deploy + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + +# Shared with preview-deploy so prod and preview writes to gh-pages never race. +concurrency: + group: gh-pages-deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: yarn + + - run: yarn install + + - name: Build production site + env: + DOCS_URL: ${{ vars.DOCS_URL || format('https://{0}.github.io', github.repository_owner) }} + DOCS_BASE_URL: ${{ vars.DOCS_BASE_URL || format('/{0}/', github.event.repository.name) }} + run: yarn build + + - name: Publish to gh-pages root (preserve pr-preview/) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PAGES_CNAME: ${{ vars.PAGES_CNAME }} + run: | + set -euo pipefail + REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + + if git clone --quiet --depth 1 --branch gh-pages --single-branch "$REPO_URL" gh-pages 2>/dev/null; then + cd gh-pages + else + mkdir gh-pages && cd gh-pages + git init --quiet + git remote add origin "$REPO_URL" + git checkout --quiet --orphan gh-pages + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Replace the production site at the root while keeping pr-preview/ (and .git). + # Defined as a function so it can be replayed verbatim on a push retry. + publish() { + find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'pr-preview' -exec rm -rf {} + + cp -R "${GITHUB_WORKSPACE}/build/." . + touch .nojekyll # serve Docusaurus output verbatim (skip GitHub Pages' Jekyll) + if [ -n "${PAGES_CNAME:-}" ]; then echo "$PAGES_CNAME" > CNAME; fi + git add -A + } + + publish + if git diff --quiet --cached; then + echo "No changes to publish." + exit 0 + fi + git commit --quiet -m "Deploy production ($GITHUB_SHA)" + + # Serialized by the concurrency group; if the branch moved anyway, adopt the + # latest tree and replay (never touching pr-preview/) before retrying. + if ! git push --quiet origin gh-pages; then + echo "Push rejected; re-syncing gh-pages and retrying." + git fetch --quiet --depth 1 origin gh-pages + git reset --hard origin/gh-pages + publish + git diff --quiet --cached || git commit --quiet -m "Deploy production ($GITHUB_SHA)" + git push --quiet origin gh-pages + fi diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 94a50f12..4ecadb69 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -66,10 +66,13 @@ jobs: - name: Build Docusaurus site if: github.event.action != 'closed' env: - # Point this build at the GitHub Pages sub-path the PR will be published to. - # The defaults in docusaurus.config.js keep production (Amplify) unchanged. - DOCS_URL: https://${{ github.repository_owner }}.github.io - DOCS_BASE_URL: /${{ github.event.repository.name }}/pr-preview/pr-${{ github.event.number }}/ + # Point this build at the path the PR will be published to. When a root custom + # domain is configured (the PAGES_CNAME repo variable), previews live at + # /pr-preview/pr-/; otherwise at the github.io project sub-path + # .github.io//pr-preview/pr-/. Defaults in docusaurus.config.js + # keep production builds unchanged. + DOCS_URL: ${{ vars.PAGES_CNAME && format('https://{0}', vars.PAGES_CNAME) || format('https://{0}.github.io', github.repository_owner) }} + DOCS_BASE_URL: ${{ vars.PAGES_CNAME && format('/pr-preview/pr-{0}/', github.event.number) || format('/{0}/pr-preview/pr-{1}/', github.event.repository.name, github.event.number) }} run: yarn build - name: Upload site artifact diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml index 479e3bba..4ed6f1b1 100644 --- a/.github/workflows/preview-deploy.yml +++ b/.github/workflows/preview-deploy.yml @@ -139,12 +139,19 @@ jobs: PR_NUMBER: ${{ steps.meta.outputs.number }} PR_ACTION: ${{ steps.meta.outputs.action }} SRC_SHA: ${{ github.event.workflow_run.head_sha }} + PAGES_CNAME: ${{ vars.PAGES_CNAME }} run: | set -euo pipefail OWNER="${GITHUB_REPOSITORY%%/*}" REPO="${GITHUB_REPOSITORY##*/}" - OWNER_LC="$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')" - URL="https://${OWNER_LC}.github.io/${REPO}/pr-preview/pr-${PR_NUMBER}/" + # When a root custom domain is configured, previews are served at + # /pr-preview/pr-/; otherwise at the github.io project sub-path. + if [ -n "${PAGES_CNAME:-}" ]; then + URL="https://${PAGES_CNAME}/pr-preview/pr-${PR_NUMBER}/" + else + OWNER_LC="$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')" + URL="https://${OWNER_LC}.github.io/${REPO}/pr-preview/pr-${PR_NUMBER}/" + fi SHORT_SHA="${SRC_SHA:0:7}" MARKER=""