From 8543c8372faebf4b94c4c83a35e6804aab153728 Mon Sep 17 00:00:00 2001 From: Alex Kanitz Date: Mon, 26 Oct 2026 19:44:03 +0100 Subject: [PATCH 1/5] docs: support Read the Docs subpath builds Read the Docs serves every version, branch and pull request preview under a subpath (/en/latest/, /en//, /en//). When READTHEDOCS_CANONICAL_URL is set, derive Next's basePath from it and emit trailing-slash directories, so the static export works there. Make hard-coded root paths base-path aware: next/image sources get the prefix from a small helper, internal links use next/link and the hero button uses the router. Without the variable, the build output is unchanged apart from attribute order on the converted links. Read the Docs builds with Node.js 24, as the docs image does after the move to Node.js 22 and 24 (#466). Co-Authored-By: Claude Opus 5.5 --- .readthedocs.yaml | 13 ++++++++++++ .../components/common/Footer.tsx | 11 +++++----- apps/documentation/components/home/Hero.tsx | 4 +++- .../components/home/ServicesShowcase.tsx | 5 +++-- apps/documentation/next.config.js | 15 +++++++++++++ .../documentation/pages/docs/introduction.mdx | 21 ++++++++++--------- apps/documentation/theme.config.tsx | 3 ++- apps/documentation/utils/basePath.ts | 4 ++++ turbo.json | 3 ++- 9 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 apps/documentation/utils/basePath.ts diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..3921f2f1 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +# Read the Docs configuration for the documentation app (apps/documentation). +# See https://docs.readthedocs.io/en/stable/config-file/v2.html +version: 2 + +build: + os: ubuntu-24.04 + tools: + nodejs: "24" + commands: + - npm ci --no-audit --no-fund + - npx turbo run build --filter=ecc-docs... + - mkdir -p $READTHEDOCS_OUTPUT + - cp -r apps/documentation/out $READTHEDOCS_OUTPUT/html diff --git a/apps/documentation/components/common/Footer.tsx b/apps/documentation/components/common/Footer.tsx index bb6ba67e..14c57857 100644 --- a/apps/documentation/components/common/Footer.tsx +++ b/apps/documentation/components/common/Footer.tsx @@ -1,4 +1,5 @@ import Image from 'next/image'; +import { withBasePath } from '@/utils/basePath'; function Footer() { return ( @@ -6,17 +7,17 @@ function Footer() {
- Elixir Cloud & AAI + Elixir Cloud & AAI Cloud SDK Cloud SDK Elixir Elixir
@@ -29,7 +31,7 @@ export default function Hero() {
{ - window.location.href = '/docs/introduction'; + router.push('/docs/introduction'); }} variant='default' className='part:bg-sky-600 part:hover:bg-sky-500 part:text-white' diff --git a/apps/documentation/components/home/ServicesShowcase.tsx b/apps/documentation/components/home/ServicesShowcase.tsx index 0f528343..4a7a594b 100644 --- a/apps/documentation/components/home/ServicesShowcase.tsx +++ b/apps/documentation/components/home/ServicesShowcase.tsx @@ -1,5 +1,6 @@ 'use client'; import { useState } from 'react'; +import Link from 'next/link'; import { useTheme } from 'next-themes'; import RunsPreview from '@/components/packages/wes/RunsPreview'; import RunCreatePreview from '@/components/packages/wes/RunCreatePreview'; @@ -134,9 +135,9 @@ export default function ServicesShowcase() {
diff --git a/apps/documentation/next.config.js b/apps/documentation/next.config.js index b739e9b9..9ba204fc 100644 --- a/apps/documentation/next.config.js +++ b/apps/documentation/next.config.js @@ -7,6 +7,20 @@ const withNextra = require('nextra')({ // eslint-disable-next-line @typescript-eslint/no-var-requires const withLitSSR = require('@lit-labs/nextjs')(); +// Read the Docs serves each version under a subpath (e.g. /en/latest/ or +// /en//), given by READTHEDOCS_CANONICAL_URL at build time. +// Outside Read the Docs, the variable is unset and nothing changes. +const rtdBasePath = process.env.READTHEDOCS_CANONICAL_URL + ? new URL(process.env.READTHEDOCS_CANONICAL_URL).pathname.replace(/\/$/, '') + : ''; +const rtdConfig = process.env.READTHEDOCS_CANONICAL_URL + ? { + basePath: rtdBasePath, + trailingSlash: true, + env: { NEXT_PUBLIC_BASE_PATH: rtdBasePath }, + } + : {}; + /** @type {import('next').NextConfig} */ module.exports = withLitSSR({ ...withNextra(), @@ -14,4 +28,5 @@ module.exports = withLitSSR({ images: { unoptimized: true, }, + ...rtdConfig, }); diff --git a/apps/documentation/pages/docs/introduction.mdx b/apps/documentation/pages/docs/introduction.mdx index b23097b3..b017c2fe 100644 --- a/apps/documentation/pages/docs/introduction.mdx +++ b/apps/documentation/pages/docs/introduction.mdx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { Callout } from 'nextra/components'; import { Steps } from 'nextra/components'; import { @@ -34,9 +35,9 @@ While we are continuously expanding the ECC suite, the current version includes
- + - + @@ -55,9 +56,9 @@ While we are continuously expanding the ECC suite, the current version includes
- + - + @@ -76,9 +77,9 @@ While we are continuously expanding the ECC suite, the current version includes
- + - + @@ -97,9 +98,9 @@ While we are continuously expanding the ECC suite, the current version includes
- + - + @@ -118,9 +119,9 @@ While we are continuously expanding the ECC suite, the current version includes
- + - + diff --git a/apps/documentation/theme.config.tsx b/apps/documentation/theme.config.tsx index c6ab6b17..a7be94bc 100644 --- a/apps/documentation/theme.config.tsx +++ b/apps/documentation/theme.config.tsx @@ -1,6 +1,7 @@ import { useRouter } from 'next/router' import { DocsThemeConfig } from 'nextra-theme-docs'; import Image from 'next/image'; +import { withBasePath } from './utils/basePath'; import { RiSlackFill, RiGithubFill } from '@remixicon/react'; import Footer from './components/common/Footer'; @@ -8,7 +9,7 @@ import Footer from './components/common/Footer'; const config: DocsThemeConfig = { logo: (
- Logo + Logo ELIXIR Cloud Components
), diff --git a/apps/documentation/utils/basePath.ts b/apps/documentation/utils/basePath.ts new file mode 100644 index 00000000..88c2787e --- /dev/null +++ b/apps/documentation/utils/basePath.ts @@ -0,0 +1,4 @@ +// Prefix for files in public/ when the site is served under a subpath +// (set in next.config.js, e.g. on Read the Docs); empty otherwise. +export const withBasePath = (path: string): string => + `${process.env.NEXT_PUBLIC_BASE_PATH ?? ''}${path}`; diff --git a/turbo.json b/turbo.json index f1f6e2f2..c0f7c20d 100644 --- a/turbo.json +++ b/turbo.json @@ -7,7 +7,8 @@ }, "build": { "outputs": ["dist/**"], - "dependsOn": ["^build"] + "dependsOn": ["^build"], + "env": ["READTHEDOCS_CANONICAL_URL", "NEXT_PUBLIC_BASE_PATH"] }, "test": { "inputs": [], From 0b3374de48799c09a8e00d8b00e94062228bd0bc Mon Sep 17 00:00:00 2001 From: Alex Kanitz Date: Mon, 26 Oct 2026 19:46:15 +0100 Subject: [PATCH 2/5] fix(docs): serve the favicon under the base path On Read the Docs, the docs live under a subpath, so browsers fetched /favicon.ico from the domain root and showed the Read the Docs icon. Declare the favicon with the base path; other builds are unchanged. Co-Authored-By: Claude Opus 5.5 --- apps/documentation/theme.config.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/documentation/theme.config.tsx b/apps/documentation/theme.config.tsx index a7be94bc..2df00576 100644 --- a/apps/documentation/theme.config.tsx +++ b/apps/documentation/theme.config.tsx @@ -27,6 +27,11 @@ const config: DocsThemeConfig = { component: