From de3d9b0579d96626db30e98716282860d0f7f837 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 30 Jan 2026 17:25:53 +0100 Subject: [PATCH 1/8] feat: versioned release docs deployment --- .github/workflows/docs.yml | 11 ++-- .github/workflows/release.yml | 43 +++++++++++++++ docs-site/astro.config.mjs | 3 ++ docs-site/src/components/SiteTitle.astro | 50 +++++++++++++++++ docs-site/src/components/VersionBadge.astro | 59 +++++++++++++++++++++ scripts/prepare-docs.sh | 4 +- 6 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 docs-site/src/components/SiteTitle.astro create mode 100644 docs-site/src/components/VersionBadge.astro diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4a155dd89..3aaaa7af1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,12 +3,7 @@ name: Documentation on: push: branches: - - main - paths: - - 'docs/**' - - 'docs-site/**' - - 'scripts/prepare-docs.sh' - - '.github/workflows/docs.yml' + - 'v*-docs' pull_request: paths: - 'docs/**' @@ -59,9 +54,9 @@ jobs: with: path: ./docs-site/dist - # Deployment job - only runs on main branch + # Deployment job - only runs on the active docs branch deploy: - if: github.ref == 'refs/heads/main' && github.event_name == 'push' + if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == format('refs/heads/{0}', vars.ACTIVE_DOCS_BRANCH) environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c59af532..3c7b4c3d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -278,6 +278,49 @@ jobs: gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + # Create a docs branch for stable releases (non-prerelease, non-beta) + create-docs-branch: + needs: + - plan + - host + # Only run for stable releases (not prereleases, not beta versions) + if: ${{ needs.host.result == 'success' && !fromJson(needs.host.outputs.val).announcement_is_prerelease && !contains(needs.plan.outputs.tag, 'beta') }} + runs-on: "ubuntu-22.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + submodules: recursive + fetch-depth: 0 + - name: Create docs branch + env: + TAG_NAME: "${{ needs.plan.outputs.tag }}" + RELEASE_COMMIT: "${{ github.sha }}" + run: | + # Extract major.minor from tag and create branch name (e.g., "v0.1.0" -> "v0.1-docs") + BRANCH_NAME="v$(echo "${TAG_NAME#v}" | cut -d. -f1,2)-docs" + + # Check if branch already exists + if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then + echo "Branch $BRANCH_NAME already exists, skipping creation" + echo "This is expected for patch releases (e.g., v0.1.1, v0.1.2)" + exit 0 + fi + + echo "Creating docs branch: $BRANCH_NAME from commit $RELEASE_COMMIT" + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Create and push the branch + git checkout -b "$BRANCH_NAME" "$RELEASE_COMMIT" + git push origin "$BRANCH_NAME" + + echo "Successfully created branch $BRANCH_NAME" + announce: needs: - plan diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index 43e081947..870124097 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -17,6 +17,9 @@ export default defineConfig({ title: 'ICP CLI', description: 'Command-line tool for developing and deploying applications on the Internet Computer Protocol (ICP)', favicon: '/favicon.png', + components: { + SiteTitle: './src/components/SiteTitle.astro', + }, head: [ { tag: 'script', diff --git a/docs-site/src/components/SiteTitle.astro b/docs-site/src/components/SiteTitle.astro new file mode 100644 index 000000000..827a8095d --- /dev/null +++ b/docs-site/src/components/SiteTitle.astro @@ -0,0 +1,50 @@ +--- +import type { Props } from '@astrojs/starlight/props'; +import config from 'virtual:starlight/user-config'; +import VersionBadge from './VersionBadge.astro'; + +const { hasSiteTitleLogo } = Astro.props; +// Ensure title is a string +const titleText = typeof config.title === 'string' ? config.title : 'ICP CLI'; +--- + + + { + config.logo && hasSiteTitleLogo && 'src' in config.logo ? ( + {config.logo.alt} + ) : null + } + { + config.logo?.dark && hasSiteTitleLogo && 'src' in config.logo.dark ? ( + {config.logo.dark.alt + ) : null + } + + {titleText} + + + + + diff --git a/docs-site/src/components/VersionBadge.astro b/docs-site/src/components/VersionBadge.astro new file mode 100644 index 000000000..9582f0d60 --- /dev/null +++ b/docs-site/src/components/VersionBadge.astro @@ -0,0 +1,59 @@ +--- +// Read version from Cargo.toml at build time +import { readFileSync } from 'fs'; +import { resolve } from 'path'; + +let version = 'unknown'; +try { + const cargoToml = readFileSync(resolve('../Cargo.toml'), 'utf-8'); + // Match version in [workspace.package] section or at root level + const versionMatch = cargoToml.match(/\[workspace\.package\][^\[]*version\s*=\s*"([^"]+)"/s) + || cargoToml.match(/^version\s*=\s*"([^"]+)"/m); + if (versionMatch) { + version = versionMatch[1]; + } +} catch (error) { + console.error('Failed to read version from Cargo.toml:', error); +} +--- + +
+ v{version} +
+ + diff --git a/scripts/prepare-docs.sh b/scripts/prepare-docs.sh index 58b9cf266..74b0512a2 100755 --- a/scripts/prepare-docs.sh +++ b/scripts/prepare-docs.sh @@ -109,9 +109,9 @@ find "$TARGET_DIR" -name "*.md" -type f | while read -r file; do { echo "---" echo "title: $title" - # Add banner to all pages (will be removed once versioning is introduced) + # Banner encouraging user feedback echo "banner:" - echo " content: 'This documentation reflects the latest main branch and may include features not yet in the current beta release. Feedback welcome on the Forum or Discord!'" + echo " content: 'Feedback welcome! Report issues on GitHub, ask questions on the Forum, or chat with us on Discord.'" echo "---" echo "" # Remove the first H1 heading line from content to avoid duplicates From 5ddb280254cba64a0090f02e1850d0e01a2916b6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 30 Jan 2026 18:23:42 +0100 Subject: [PATCH 2/8] ci: manual workflow for docs branch creation --- .github/workflows/release-docs-branch.yml | 68 +++++++++++++++++++++++ .github/workflows/release.yml | 43 -------------- 2 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/release-docs-branch.yml diff --git a/.github/workflows/release-docs-branch.yml b/.github/workflows/release-docs-branch.yml new file mode 100644 index 000000000..1b3a53d39 --- /dev/null +++ b/.github/workflows/release-docs-branch.yml @@ -0,0 +1,68 @@ +name: Release Docs Branch + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version tag to create docs branch from (e.g., v0.1.0, v0.1.0-beta.5)' + required: true + type: string + branch_strategy: + description: 'Branch naming strategy' + required: false + type: choice + options: + - major-minor + - full-version + default: major-minor + +jobs: + create-docs-branch: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.version }} + fetch-depth: 0 + + - name: Create docs branch + env: + TAG_NAME: ${{ inputs.version }} + BRANCH_STRATEGY: ${{ inputs.branch_strategy }} + run: | + # Extract version from tag (remove 'v' prefix if present) + VERSION="${TAG_NAME#v}" + + # Determine branch name based on strategy + if [[ "$BRANCH_STRATEGY" == "major-minor" ]]; then + # Extract major.minor (e.g., "0.1.0" -> "0.1", "0.1.0-beta.5" -> "0.1") + MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2) + BRANCH_NAME="v${MAJOR_MINOR}-docs" + else + # Use full version + BRANCH_NAME="${TAG_NAME}-docs" + fi + + echo "Creating docs branch: $BRANCH_NAME from tag $TAG_NAME" + + # Check if branch already exists + if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then + echo "⚠️ Branch $BRANCH_NAME already exists" + echo "If you want to update it, delete the branch first and re-run this workflow" + exit 1 + fi + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Create and push the branch + git checkout -b "$BRANCH_NAME" + git push origin "$BRANCH_NAME" + + echo "✅ Successfully created branch $BRANCH_NAME" + echo "Next steps:" + echo "1. Set ACTIVE_DOCS_BRANCH variable to: $BRANCH_NAME" + echo "2. Trigger the docs workflow to deploy" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c7b4c3d1..3c59af532 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -278,49 +278,6 @@ jobs: gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - # Create a docs branch for stable releases (non-prerelease, non-beta) - create-docs-branch: - needs: - - plan - - host - # Only run for stable releases (not prereleases, not beta versions) - if: ${{ needs.host.result == 'success' && !fromJson(needs.host.outputs.val).announcement_is_prerelease && !contains(needs.plan.outputs.tag, 'beta') }} - runs-on: "ubuntu-22.04" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - submodules: recursive - fetch-depth: 0 - - name: Create docs branch - env: - TAG_NAME: "${{ needs.plan.outputs.tag }}" - RELEASE_COMMIT: "${{ github.sha }}" - run: | - # Extract major.minor from tag and create branch name (e.g., "v0.1.0" -> "v0.1-docs") - BRANCH_NAME="v$(echo "${TAG_NAME#v}" | cut -d. -f1,2)-docs" - - # Check if branch already exists - if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then - echo "Branch $BRANCH_NAME already exists, skipping creation" - echo "This is expected for patch releases (e.g., v0.1.1, v0.1.2)" - exit 0 - fi - - echo "Creating docs branch: $BRANCH_NAME from commit $RELEASE_COMMIT" - - # Configure git - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Create and push the branch - git checkout -b "$BRANCH_NAME" "$RELEASE_COMMIT" - git push origin "$BRANCH_NAME" - - echo "Successfully created branch $BRANCH_NAME" - announce: needs: - plan From a99f27037ac76a7d93e9c95054ee21e13512b872 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 2 Feb 2026 11:23:52 +0100 Subject: [PATCH 3/8] feat: add versioned documentation deployment system --- .github/CONTRIBUTING.md | 16 + .github/workflows/docs.yml | 163 ++++++++- .github/workflows/release-docs-branch.yml | 68 ---- docs-site/astro.config.mjs | 6 +- docs-site/src/components/SiteTitle.astro | 4 +- .../src/components/VersionSwitcher.astro | 266 ++++++++++++++ docs-site/versions.json | 4 + docs/VERSIONED_DOCS.md | 338 ++++++++++++++++++ scripts/prepare-docs.sh | 2 +- 9 files changed, 777 insertions(+), 90 deletions(-) delete mode 100644 .github/workflows/release-docs-branch.yml create mode 100644 docs-site/src/components/VersionSwitcher.astro create mode 100644 docs-site/versions.json create mode 100644 docs/VERSIONED_DOCS.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4dd32b297..5b4860c6e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -104,6 +104,22 @@ The documentation site uses [Astro](https://astro.build/) with [Starlight](https This architecture keeps source docs clean and GitHub-friendly while providing a polished documentation site. +### Versioned Documentation Deployment + +The documentation site supports multiple versions simultaneously. When working with releases: + +- **Stable releases** (e.g., `v0.1.0`, `v0.2.0`) automatically deploy versioned docs +- **Documentation updates** can be made to old versions via `docs/v*` branches +- Users can switch between versions via the version switcher in the header + +For detailed information on the versioned documentation system, including: +- How version deployment works (tags vs branches) +- Releasing new versions +- Updating existing version docs +- Managing the version list + +See [docs/VERSIONED_DOCS.md](../docs/VERSIONED_DOCS.md). + ### Writing Documentation 1. **Create a markdown file** in the appropriate directory: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3aaaa7af1..e1e034078 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,8 +2,16 @@ name: Documentation on: push: + tags: + - 'v*' branches: - - 'v*-docs' + - main + - 'docs/v*' + paths: + - 'docs/**' + - 'docs-site/**' + - 'scripts/prepare-docs.sh' + - '.github/workflows/docs.yml' pull_request: paths: - 'docs/**' @@ -14,9 +22,7 @@ on: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: - contents: read - pages: write - id-token: write + contents: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. @@ -25,7 +31,7 @@ concurrency: cancel-in-progress: false jobs: - # Build job + # Build job - validates that docs build successfully build: runs-on: ubuntu-latest steps: @@ -49,20 +55,141 @@ jobs: env: NODE_ENV: production - - name: Upload artifact - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + # Publish root index and versions list - only runs on main branch + publish-root-files: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Setup Node + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v4.2.0 + with: + node-version: '20' + + - name: Prepare root files + run: | + mkdir -p root + + # Copy versions.json from repo + cp docs-site/versions.json root/versions.json + + # Extract latest version (first in list) from versions.json + LATEST_VERSION=$(jq -r '.versions[0].version // empty' docs-site/versions.json) + + # If no releases yet, redirect to main branch docs + if [[ -z "$LATEST_VERSION" ]]; then + echo "⚠️ No releases yet, redirecting to main branch docs" + LATEST_VERSION="main" + else + echo "✅ Redirecting to version: ${LATEST_VERSION}" + fi + + # Generate index.html that redirects to latest version + cat > root/index.html << EOF + + + + + + + + EOF + + - name: Deploy root files to GitHub Pages + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: - path: ./docs-site/dist - - # Deployment job - only runs on the active docs branch - deploy: - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == format('refs/heads/{0}', vars.ACTIVE_DOCS_BRANCH) - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./root + keep_files: true + + # Publish main branch docs for preview (always available at /main/) + publish-main-docs: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: build runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Setup Node + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v4.2.0 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: docs-site/package-lock.json + + - name: Install dependencies + working-directory: ./docs-site + run: npm ci + + - name: Build main branch docs + working-directory: ./docs-site + run: npm run build + env: + NODE_ENV: production + PUBLIC_BASE_PATH: /main/ + + - name: Deploy main docs to GitHub Pages + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs-site/dist + destination_dir: main + keep_files: true + + # Publish versioned docs - runs on tags (v*) or docs branches (docs/v*) + publish-docs: + if: github.ref != 'refs/heads/main' needs: build + runs-on: ubuntu-latest steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Setup Node + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v4.2.0 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: docs-site/package-lock.json + + - name: Install dependencies + working-directory: ./docs-site + run: npm ci + + - name: Extract version from tag or branch + run: | + if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then + # Tag: v0.1.0 -> extract major.minor -> 0.1 + VERSION=${GITHUB_REF#refs/tags/v} + # Strip patch version (0.1.0 -> 0.1) + VERSION=${VERSION%.*} + echo "DOCS_VERSION=${VERSION}" >> $GITHUB_ENV + echo "DOCS_BASE=/${VERSION}/" >> $GITHUB_ENV + elif [[ "${GITHUB_REF}" == refs/heads/docs/v* ]]; then + # Branch: docs/v0.1 -> extract version -> 0.1 + VERSION=${GITHUB_REF#refs/heads/docs/v} + echo "DOCS_VERSION=${VERSION}" >> $GITHUB_ENV + echo "DOCS_BASE=/${VERSION}/" >> $GITHUB_ENV + else + echo "❌ Docs should only be published for version tags (v*) or docs branches (docs/v*)" + echo "Current ref: ${GITHUB_REF}" + exit 1 + fi + + - name: Build documentation site + working-directory: ./docs-site + run: npm run build + env: + NODE_ENV: production + PUBLIC_BASE_PATH: ${{ env.DOCS_BASE }} + + - name: Deploy versioned docs to GitHub Pages + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs-site/dist + destination_dir: ${{ env.DOCS_VERSION }} diff --git a/.github/workflows/release-docs-branch.yml b/.github/workflows/release-docs-branch.yml deleted file mode 100644 index 1b3a53d39..000000000 --- a/.github/workflows/release-docs-branch.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Release Docs Branch - -on: - workflow_dispatch: - inputs: - version: - description: 'Release version tag to create docs branch from (e.g., v0.1.0, v0.1.0-beta.5)' - required: true - type: string - branch_strategy: - description: 'Branch naming strategy' - required: false - type: choice - options: - - major-minor - - full-version - default: major-minor - -jobs: - create-docs-branch: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.version }} - fetch-depth: 0 - - - name: Create docs branch - env: - TAG_NAME: ${{ inputs.version }} - BRANCH_STRATEGY: ${{ inputs.branch_strategy }} - run: | - # Extract version from tag (remove 'v' prefix if present) - VERSION="${TAG_NAME#v}" - - # Determine branch name based on strategy - if [[ "$BRANCH_STRATEGY" == "major-minor" ]]; then - # Extract major.minor (e.g., "0.1.0" -> "0.1", "0.1.0-beta.5" -> "0.1") - MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2) - BRANCH_NAME="v${MAJOR_MINOR}-docs" - else - # Use full version - BRANCH_NAME="${TAG_NAME}-docs" - fi - - echo "Creating docs branch: $BRANCH_NAME from tag $TAG_NAME" - - # Check if branch already exists - if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then - echo "⚠️ Branch $BRANCH_NAME already exists" - echo "If you want to update it, delete the branch first and re-run this workflow" - exit 1 - fi - - # Configure git - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Create and push the branch - git checkout -b "$BRANCH_NAME" - git push origin "$BRANCH_NAME" - - echo "✅ Successfully created branch $BRANCH_NAME" - echo "Next steps:" - echo "1. Set ACTIVE_DOCS_BRANCH variable to: $BRANCH_NAME" - echo "2. Trigger the docs workflow to deploy" diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index 870124097..a9f3f8709 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -5,7 +5,11 @@ import rehypeExternalLinks from 'rehype-external-links'; // https://astro.build/config export default defineConfig({ site: 'https://dfinity.github.io', - base: process.env.NODE_ENV === 'production' ? '/icp-cli/' : '/', + // For versioned deployments: /icp-cli/0.1/, /icp-cli/0.2/, etc. + // For non-versioned: /icp-cli/ in production, / in development + base: process.env.PUBLIC_BASE_PATH + ? `/icp-cli${process.env.PUBLIC_BASE_PATH}` + : (process.env.NODE_ENV === 'production' ? '/icp-cli/' : '/'), markdown: { rehypePlugins: [ // Open external links in new tab diff --git a/docs-site/src/components/SiteTitle.astro b/docs-site/src/components/SiteTitle.astro index 827a8095d..1a9a9041c 100644 --- a/docs-site/src/components/SiteTitle.astro +++ b/docs-site/src/components/SiteTitle.astro @@ -1,7 +1,7 @@ --- import type { Props } from '@astrojs/starlight/props'; import config from 'virtual:starlight/user-config'; -import VersionBadge from './VersionBadge.astro'; +import VersionSwitcher from './VersionSwitcher.astro'; const { hasSiteTitleLogo } = Astro.props; // Ensure title is a string @@ -34,7 +34,7 @@ const titleText = typeof config.title === 'string' ? config.title : 'ICP CLI'; {titleText} - + diff --git a/docs-site/versions.json b/docs-site/versions.json new file mode 100644 index 000000000..ad8074046 --- /dev/null +++ b/docs-site/versions.json @@ -0,0 +1,4 @@ +{ + "$comment": "Before first release: Empty array redirects to /main/. After releases: Add versions here (newest first).", + "versions": [] +} diff --git a/docs/VERSIONED_DOCS.md b/docs/VERSIONED_DOCS.md new file mode 100644 index 000000000..519879a1e --- /dev/null +++ b/docs/VERSIONED_DOCS.md @@ -0,0 +1,338 @@ +# Versioned Documentation Setup + +This document explains how the versioned documentation system works for the ICP CLI. + +## Overview + +The documentation site supports multiple versions simultaneously: +- `https://dfinity.github.io/icp-cli/` → Redirects to latest version +- `https://dfinity.github.io/icp-cli/0.1/` → Version 0.1 docs +- `https://dfinity.github.io/icp-cli/0.2/` → Version 0.2 docs + +Users can switch between versions using the version switcher in the header. + +## Architecture + +### GitHub Pages Deployment + +The site uses the `gh-pages` branch for deployment via the `peaceiris/actions-gh-pages` action, which allows multiple versions to coexist in subdirectories. + +### Directory Structure + +``` +gh-pages branch: +├── index.html # Redirects to latest version (or main if no releases) +├── versions.json # List of available versions +├── main/ # Main branch docs (always updated, for preview) +│ └── (full site) +├── 0.1/ # Version 0.1 docs +│ └── (full site) +├── 0.2/ # Version 0.2 docs +│ └── (full site) +└── ... +``` + +### Workflow + +**[.github/workflows/docs.yml](.github/workflows/docs.yml)** handles all deployment: + +1. **Tag triggers** (`v*`): + - Pushing tag `v0.1.0` automatically deploys docs to `/0.1/` (major.minor) + - Pushing tag `v0.2.0` automatically deploys docs to `/0.2/` + +2. **Branch triggers** (`docs/v*`): + - Pushing to `docs/v0.1` updates docs at `/0.1/` + - Useful for cherry-picking fixes to older versions + +3. **Main branch**: + - Deploys main branch docs to `/main/` (for preview/development) + - Updates `index.html` (redirect to latest version, or `main` if no releases) + - Updates `versions.json` (list of available versions) + +**Jobs:** +- `build`: Validates docs build on all changes +- `publish-root-files`: Updates index.html and versions.json (runs on `main`) +- `publish-main-docs`: Deploys main branch docs to `/main/` (runs on `main`) +- `publish-docs`: Publishes versioned docs (runs on tags or `docs/v*` branches) + +## Initial Setup + +### 1. GitHub Pages Settings + +Go to **Settings → Pages** in the GitHub repository and configure: + +- **Source**: Deploy from a branch +- **Branch**: `gh-pages` +- **Folder**: `/ (root)` + +⚠️ **Important**: Change from "GitHub Actions" to "Deploy from a branch" for this to work. + +### 2. Version List Configuration + +The version list is stored in [docs-site/versions.json](../docs-site/versions.json). + +**Before first release** (empty array): +```json +{ + "versions": [] +} +``` +- Redirect goes to `/main/` (main branch docs) +- Version switcher shows "main" badge + +**After releases** (add versions, newest first): +```json +{ + "versions": [ + {"version": "0.1", "label": "0.1", "latest": true} + ] +} +``` + +The workflow automatically: +- Deploys this file to the root of gh-pages +- Reads the **first version** in the list (newest) +- Generates `index.html` to redirect to that version (or `main` if empty) + +**You only need to update this file when releasing new versions** - the redirect is automated. + +### 3. First Deployment + +**Before first release:** + +1. **Push to main**: + ```bash + git push origin main + ``` + This deploys: + - Root files (index.html → redirects to `/main/`, versions.json) + - Main branch docs to `/main/` + +2. **Verify pre-release state**: + - `https://dfinity.github.io/icp-cli/` → redirects to `/main/` + - `https://dfinity.github.io/icp-cli/main/` → loads main branch docs + - Version switcher shows "main" badge + +**First release (v0.1.0):** + +1. **Push release tag** (deploys docs to `/0.1/`): + ```bash + git tag v0.1.0 + git push origin v0.1.0 + ``` + +2. **Update versions.json and push to main** (updates redirect): + ```bash + # Edit docs-site/versions.json to add: + # {"version": "0.1", "label": "0.1", "latest": true} + + git add docs-site/versions.json + git commit -m "docs: add v0.1 to version list" + git push origin main + ``` + +3. **Verify release**: + - `https://dfinity.github.io/icp-cli/` → redirects to `/0.1/` + - `https://dfinity.github.io/icp-cli/0.1/` → loads v0.1 docs + - `https://dfinity.github.io/icp-cli/main/` → still accessible for preview + - Version switcher shows "0.1 (latest)" + +## Main Branch Docs + +The `/main/` path always contains docs built from the main branch: + +**Purpose:** +- Provides live docs before the first release +- Allows contributors to preview upcoming documentation changes +- Useful for testing docs updates before creating a release + +**Behavior:** +- Always deployed when pushing to main +- Redirect points to `/main/` when versions.json is empty (pre-release) +- Redirect points to latest version after first release +- Version switcher shows "main" badge (non-interactive) +- Remains accessible at `/main/` even after releases + +**Access:** +- Direct: `https://dfinity.github.io/icp-cli/main/` +- Redirect (pre-release only): `https://dfinity.github.io/icp-cli/` + +## Releasing a New Version + +When releasing v0.2.0: + +### 1. Deploy + +**Important**: Deploy docs FIRST, then update redirect to avoid 404s. + +```bash +# Step 1: Push release tag (deploys v0.2 docs to /0.2/) +git tag v0.2.0 +git push origin v0.2.0 + +# Step 2: Edit docs-site/versions.json to add v0.2 at top (newest first): +# {"version": "0.2", "label": "0.2", "latest": true} +# And update v0.1 to remove latest flag (or omit it) + +# Step 3: Push to main (redirect now points to /0.2/, which exists) +git add docs-site/versions.json +git commit -m "docs: add v0.2 to version list" +git push origin main +``` + +### 2. Verify + +- `https://dfinity.github.io/icp-cli/` → redirects to `/0.2/` +- `https://dfinity.github.io/icp-cli/0.2/` → loads v0.2 docs +- `https://dfinity.github.io/icp-cli/0.1/` → still works (old version) +- Version switcher shows both versions, with 0.2 marked as latest + +## Updating Existing Version Docs + +To update docs for an already-released version (e.g., cherry-pick bug fixes): + +### Option 1: Create docs branch from existing tag + +```bash +git checkout v0.1.0 +git checkout -b docs/v0.1 +# Make changes to docs +git commit -m "docs: fix typo in v0.1 docs" +git push origin docs/v0.1 +``` + +The workflow will automatically rebuild and redeploy to `/0.1/`. + +### Option 2: Push a new patch release tag + +```bash +git tag v0.1.1 +git push origin v0.1.1 +``` + +Since patch version is stripped (`v0.1.1` → `0.1`), this updates the same `/0.1/` directory. + +Both approaches preserve other version directories due to `keep_files: true`. + +## Beta/Pre-release Versions + +For beta versions, you can either: + +### Option 1: Don't deploy beta docs + +Just skip deploying until stable release. Beta users can build docs locally. + +### Option 2: Deploy beta to its own version + +Create a docs branch with full version: + +```bash +git checkout v0.2.0-beta.5 +git checkout -b docs/v0.2.0-beta.5 +git push origin docs/v0.2.0-beta.5 +``` + +This deploys to `/0.2.0-beta.5/` (full version, not just major.minor). + +**Do not add beta versions to versions.json** - they won't appear in the version switcher. + +## Version Switcher Component + +The version switcher ([docs-site/src/components/VersionSwitcher.astro](docs-site/src/components/VersionSwitcher.astro)): +- Shows "main" badge when viewing main branch docs or no releases exist +- Shows "dev" badge in local development +- Shows version dropdown for released versions +- Reads current version from Cargo.toml at build time +- Fetches `/icp-cli/versions.json` at runtime +- Shows "(latest)" label for the latest version +- Highlights current version with checkmark + +## How It Works: Tag vs Branch + +**When you push a tag** (e.g., `v0.1.0`): +1. Workflow extracts major.minor: `v0.1.0` → `0.1` +2. Builds docs with base path `/icp-cli/0.1/` +3. Deploys to gh-pages branch at `/0.1/` + +**When you push to docs branch** (e.g., `docs/v0.1`): +1. Workflow extracts version: `docs/v0.1` → `0.1` +2. Builds docs with base path `/icp-cli/0.1/` +3. Deploys to gh-pages branch at `/0.1/` (replaces existing) + +**When you push to main**: +1. Deploys main branch docs to `/main/` +2. Updates `index.html` at root (redirect) +3. Updates `versions.json` at root (version list) +4. Uses `keep_files: true` to preserve version directories + +## Troubleshooting + +### Version switcher shows "Failed to load versions" +- Check that `versions.json` was deployed to `/icp-cli/versions.json` +- Check browser console for fetch errors +- Verify gh-pages branch contains `versions.json` at root + +### Tag pushed but docs not deployed +- Check GitHub Actions workflow ran successfully +- Verify tag matches pattern `v*` (e.g., `v0.1.0`, not `0.1.0`) +- Check workflow logs for errors + +### New version not appearing in switcher +- Verify `versions.json` was updated in the workflow +- Check that main branch was pushed after updating workflow +- Verify gh-pages branch contains updated `versions.json` + +### Redirect not working +- Check `index.html` in gh-pages branch root +- Verify the redirect URL matches the deployed version path +- Clear browser cache + +### Deployment replaces other versions +- Verify workflow uses `keep_files: true` in peaceiris/actions-gh-pages +- Check that `destination_dir` is set correctly for versioned deployments + +## Quick Reference + +**Pre-release (main branch only)**: +```bash +# versions.json is empty - redirect goes to /main/ +git push origin main +# → https://dfinity.github.io/icp-cli/ redirects to /main/ +``` + +**First release (v0.1.0)**: +```bash +# 1. Push tag (deploys docs to /0.1/) +git tag v0.1.0 +git push origin v0.1.0 + +# 2. Edit docs-site/versions.json, add: {"version": "0.1", "label": "0.1", "latest": true} +# 3. Push to main (updates redirect) +git add docs-site/versions.json +git commit -m "docs: add v0.1 to version list" +git push origin main +``` + +**Subsequent release (v0.2.0)**: +```bash +# 1. Push tag (deploys docs to /0.2/) +git tag v0.2.0 +git push origin v0.2.0 + +# 2. Edit docs-site/versions.json, add v0.2 at top, update v0.1 +# 3. Push to main (updates redirect) +git add docs-site/versions.json +git commit -m "docs: add v0.2 to version list" +git push origin main +``` + +**Update old version docs**: +```bash +git checkout v0.1.0 +git checkout -b docs/v0.1 +# Make doc changes +git commit -m "docs: update v0.1 docs" +git push origin docs/v0.1 +``` + +That's it! Tags automatically deploy new versions, branches update existing versions. diff --git a/scripts/prepare-docs.sh b/scripts/prepare-docs.sh index 74b0512a2..91b4eed04 100755 --- a/scripts/prepare-docs.sh +++ b/scripts/prepare-docs.sh @@ -42,7 +42,7 @@ echo "" echo "Step 1: Copying documentation files..." rm -rf "$TARGET_DIR" mkdir -p "$TARGET_DIR" -rsync -a --exclude='schemas/' --exclude='README.md' --exclude='*/README.md' "$SOURCE_DIR/" "$TARGET_DIR/" +rsync -a --exclude='schemas/' --exclude='README.md' --exclude='*/README.md' --exclude='VERSIONED_DOCS.md' "$SOURCE_DIR/" "$TARGET_DIR/" echo "✓ Files copied" # Step 2: Fix markdown links for Starlight's directory structure From 2472b539e0cd335641f1d20e208e4d34276eb549 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 2 Feb 2026 11:39:37 +0100 Subject: [PATCH 4/8] fix: wrap VersionSwitcher script in IIFE to resolve build error --- .../src/components/VersionSwitcher.astro | 162 +++++++++--------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/docs-site/src/components/VersionSwitcher.astro b/docs-site/src/components/VersionSwitcher.astro index b31993aa8..a8b04056a 100644 --- a/docs-site/src/components/VersionSwitcher.astro +++ b/docs-site/src/components/VersionSwitcher.astro @@ -53,90 +53,92 @@ const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); diff --git a/docs-site/src/components/VersionSwitcher.astro b/docs-site/src/components/VersionSwitcher.astro index a8b04056a..e267c983d 100644 --- a/docs-site/src/components/VersionSwitcher.astro +++ b/docs-site/src/components/VersionSwitcher.astro @@ -1,28 +1,21 @@ --- -import { readFileSync } from 'fs'; -import { resolve } from 'path'; - -// Get current version from Cargo.toml -let currentVersion = 'unknown'; -try { - const cargoToml = readFileSync(resolve('../Cargo.toml'), 'utf-8'); - const versionMatch = cargoToml.match(/\[workspace\.package\][^\[]*version\s*=\s*"([^"]+)"/s) - || cargoToml.match(/^version\s*=\s*"([^"]+)"/m); - if (versionMatch) { - const fullVersion = versionMatch[1]; - // Extract major.minor (e.g., "0.1.0" -> "0.1") - currentVersion = fullVersion.split('.').slice(0, 2).join('.'); - } -} catch (error) { - console.error('Failed to read version from Cargo.toml:', error); -} - // Detect environment and branch const isDev = import.meta.env.DEV; const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); + +// Extract base prefix and version from BASE_URL +// Examples: "/icp-cli/0.1/" -> prefix="/icp-cli", version="0.1" +// "/your-repo/0.2/" -> prefix="/your-repo", version="0.2" +const baseUrl = import.meta.env.BASE_URL || '/'; +const basePrefixMatch = baseUrl.match(/^(\/[^\/]+)/); +const basePrefix = basePrefixMatch ? basePrefixMatch[1] : ''; + +// Extract version from BASE_URL (e.g., "/icp-cli/0.1/" -> "0.1") +const versionMatch = baseUrl.match(/\/(\d+\.\d+)\//); +const currentVersion = versionMatch ? versionMatch[1] : 'unknown'; --- -
+
{isDev ? (
@@ -59,6 +52,7 @@ const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); if (!button) return; const dropdown = document.getElementById('version-dropdown'); + const versionSwitcher = document.querySelector('.version-switcher'); let isOpen = false; let versionsLoaded = false; @@ -68,6 +62,7 @@ const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); isOpen = !isOpen; if (isOpen) { + button.setAttribute('aria-expanded', 'true'); dropdown?.removeAttribute('hidden'); // Load versions on first open @@ -76,14 +71,17 @@ const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); versionsLoaded = true; } } else { + button.setAttribute('aria-expanded', 'false'); dropdown?.setAttribute('hidden', ''); } }); // Close dropdown when clicking outside - document.addEventListener('click', () => { - if (isOpen) { + document.addEventListener('click', (e) => { + const target = e.target as Node; + if (isOpen && !versionSwitcher?.contains(target)) { isOpen = false; + button?.setAttribute('aria-expanded', 'false'); dropdown?.setAttribute('hidden', ''); } }); @@ -98,29 +96,42 @@ const isMainBranch = import.meta.env.BASE_URL?.includes('/main/'); if (!content) return; try { - // Fetch versions.json from repo root (/icp-cli/versions.json) - const response = await fetch('/icp-cli/versions.json'); - if (!response.ok) throw new Error('Failed to fetch versions'); + // Get base prefix from data attribute (set at build time from workflow env vars) + const basePrefix = versionSwitcher?.getAttribute('data-base-prefix') || ''; + const currentPath = window.location.pathname; + + // Fetch versions.json from repo root + const fetchUrl = `${basePrefix}/versions.json`; + console.log('[VersionSwitcher] Fetching versions from:', fetchUrl); + + const response = await fetch(fetchUrl); + if (!response.ok) { + console.error('[VersionSwitcher] Fetch failed:', response.status, response.statusText); + throw new Error('Failed to fetch versions'); + } const data = await response.json(); const versions = data.versions || []; + console.log('[VersionSwitcher] Loaded versions:', versions); + if (versions.length === 0) { content.innerHTML = '
No versions available
'; return; } - // Get current path to determine current version - const currentPath = window.location.pathname; - // Match pattern: /icp-cli/0.1/ or /icp-cli/0.2/ - const currentVersionMatch = currentPath.match(/\/icp-cli\/(\d+\.\d+)\//); + // Extract current version from path (e.g., /icp-cli/0.1/ -> "0.1") + const versionPattern = new RegExp(`${basePrefix.replace(/\//g, '\\/')}\\/(\\d+\\.\\d+)\\/`); + const currentVersionMatch = currentPath.match(versionPattern); const currentVersion = currentVersionMatch ? currentVersionMatch[1] : versions.find(v => v.latest)?.version; + console.log('[VersionSwitcher] Current version:', currentVersion, 'from path:', currentPath); + // Render version list content.innerHTML = versions.map(v => { const isCurrent = v.version === currentVersion; - const label = v.latest ? `${v.label} (latest)` : v.label; - const href = `/icp-cli/${v.version}/`; + const label = v.latest ? `${v.version} (latest)` : v.version; + const href = `${basePrefix}/${v.version}/`; return ` diff --git a/docs-site/test-version-switcher.sh b/docs-site/test-version-switcher.sh new file mode 100755 index 000000000..f0b331619 --- /dev/null +++ b/docs-site/test-version-switcher.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# Local test script for version switcher - builds all versions for complete testing +# This script simulates the production deployment locally for testing purposes + +set -e + +cd "$(dirname "$0")" + +# Cleanup function to kill the server +cleanup() { + echo "" + echo "Shutting down server..." + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" 2>/dev/null || true + fi + exit 0 +} + +# Set up trap to catch Ctrl+C and other termination signals +trap cleanup SIGINT SIGTERM + +# Configuration - matches production setup +BASE_PREFIX="/icp-cli" +TEST_DIR="dist-test" +TEST_PORT=4321 + +echo "==================================================" +echo "Building all documentation versions for testing" +echo "==================================================" +echo "" + +# Clean everything for a fresh start +echo "Cleaning all previous builds and caches..." +rm -rf "$TEST_DIR" dist .astro +mkdir -p "$TEST_DIR$BASE_PREFIX" + +# Set common environment variables +export NODE_ENV=production +export PUBLIC_SITE=http://localhost:4321 +export PUBLIC_BASE_PREFIX="$BASE_PREFIX" + +# Function to build a version +build_version() { + local version=$1 + local version_path="${BASE_PREFIX}/${version}/" + + echo "" + echo "Building version $version..." + echo " Base path: $version_path" + + # Clean previous build artifacts to avoid caching issues + echo " Cleaning build cache..." + rm -rf dist .astro + + # Set environment for this specific version + export PUBLIC_BASE_PATH="$version_path" + echo " PUBLIC_BASE_PATH=$PUBLIC_BASE_PATH" + echo " Building..." + + npm run build + + # Verify build succeeded + if [ ! -d "dist" ] || [ ! -f "dist/index.html" ]; then + echo "❌ Build failed for version $version - dist/index.html not found" + exit 1 + fi + + # Check what BASE_URL was baked into the build + BUILT_BASE=$(grep -o 'import\.meta\.env\.BASE_URL[^"]*"[^"]*"' "dist/index.html" | head -1 || echo "not found") + echo " Built with BASE_URL: $BUILT_BASE" + + # Copy to test directory + mkdir -p "$TEST_DIR$BASE_PREFIX/$version" + cp -r dist/* "$TEST_DIR$BASE_PREFIX/$version/" + + # Verify copy succeeded + local file_count=$(ls "$TEST_DIR$BASE_PREFIX/$version" | wc -l) + echo " Copied $file_count files to test directory" + + echo "✓ Version $version built successfully" +} + +# Build each version +build_version "0.1" +build_version "0.2" +build_version "main" + +# Generate test versions.json +echo "" +echo "Generating test versions.json..." +cat > "$TEST_DIR$BASE_PREFIX/versions.json" << 'EOF' +{ + "$comment": "Test versions.json for local testing", + "versions": [ + { + "version": "0.2", + "latest": true + }, + { + "version": "0.1" + } + ] +} +EOF +echo "✓ versions.json created" + +# Generate redirect index.html +echo "" +echo "Generating root redirect..." +cat > "$TEST_DIR$BASE_PREFIX/index.html" << EOF + + + + + + Redirecting to latest version... + + +

Redirecting to latest version...

+ + +EOF +echo "✓ index.html created" + +echo "" +echo "==================================================" +echo "✓ All versions built successfully!" +echo "==================================================" +echo "" + +# Verify the structure +echo "Verifying test structure..." +echo "Directory: $TEST_DIR$BASE_PREFIX/" +ls -lah "$TEST_DIR$BASE_PREFIX/" +echo "" +echo "Checking version subdirectories..." +for version in 0.1 0.2 main; do + echo "" + if [ -d "$TEST_DIR$BASE_PREFIX/$version" ]; then + echo "✓ $version/ exists" + echo " Files: $(ls "$TEST_DIR$BASE_PREFIX/$version" | wc -l)" + + if [ -f "$TEST_DIR$BASE_PREFIX/$version/index.html" ]; then + echo " index.html: ✓" + + # Check BASE_URL in the built HTML + BASE_URL_IN_HTML=$(grep -o 'import\.meta\.env\.BASE_URL[^"]*"[^"]*"' "$TEST_DIR$BASE_PREFIX/$version/index.html" | head -1 || echo "not found") + echo " BASE_URL in HTML: $BASE_URL_IN_HTML" + + # Check if version switcher is present + if grep -q "version-switcher" "$TEST_DIR$BASE_PREFIX/$version/index.html"; then + echo " VersionSwitcher: ✓ present" + + # Check what's rendered (dev/main badge or button) + if grep -q "version-button" "$TEST_DIR$BASE_PREFIX/$version/index.html"; then + echo " Renders: version button (interactive dropdown)" + elif grep -q ">main<" "$TEST_DIR$BASE_PREFIX/$version/index.html"; then + echo " Renders: 'main' badge (⚠️ unexpected for $version)" + elif grep -q ">dev<" "$TEST_DIR$BASE_PREFIX/$version/index.html"; then + echo " Renders: 'dev' badge (⚠️ unexpected)" + else + echo " Renders: unknown" + fi + else + echo " VersionSwitcher: ✗ not found" + fi + else + echo " index.html: ✗ MISSING" + fi + + # Check for assets directory + if [ -d "$TEST_DIR$BASE_PREFIX/$version/_astro" ]; then + echo " _astro/ assets: ✓ ($(ls "$TEST_DIR$BASE_PREFIX/$version/_astro" | wc -l) files)" + else + echo " _astro/ assets: ✗ MISSING" + fi + else + echo "✗ $version/ MISSING" + fi +done +echo "" + +# Start Python HTTP server (most reliable for static files) +if ! command -v python3 &> /dev/null; then + echo "⚠️ Warning: python3 not found. Cannot start server." + echo "Files are built in: $TEST_DIR$BASE_PREFIX/" +else + echo "Starting local server with Python..." + echo "Server starting at: http://localhost:${TEST_PORT}" + echo "" + echo "Press Ctrl+C to stop the server when done testing" + echo "" + echo "Test URLs:" + echo " - http://localhost:${TEST_PORT}$BASE_PREFIX/ (should redirect to 0.2)" + echo " - http://localhost:${TEST_PORT}$BASE_PREFIX/0.2/ (version 0.2)" + echo " - http://localhost:${TEST_PORT}$BASE_PREFIX/0.1/ (version 0.1)" + echo " - http://localhost:${TEST_PORT}$BASE_PREFIX/main/ (main branch)" + echo "" + echo "Expected behavior:" + echo " ✓ Version 0.2: Button shows 'v0.2', dropdown shows both versions" + echo " ✓ Version 0.1: Button shows 'v0.1', dropdown shows both versions" + echo " ✓ Main: Shows 'main' badge (no dropdown)" + echo " ✓ Clicking versions navigates between them" + echo " ✓ Console shows [VersionSwitcher] logs" + echo "" + echo "If you see 404s or wrong versions:" + echo " 1. Check the structure output above" + echo " 2. Check browser DevTools Console for BASE_URL" + echo " 3. Check browser DevTools Network tab for asset paths" + echo "" + echo "Starting server in 3 seconds..." + sleep 3 + + cd "$TEST_DIR" + python3 -m http.server ${TEST_PORT} & + SERVER_PID=$! + + echo "" + echo "Server is running (PID: $SERVER_PID)" + echo "Press Ctrl+C to stop the server and exit" + echo "" + + # Wait for server process + wait "$SERVER_PID" +fi diff --git a/docs-site/versions.json b/docs-site/versions.json index ad8074046..d28705ba6 100644 --- a/docs-site/versions.json +++ b/docs-site/versions.json @@ -1,4 +1,4 @@ { - "$comment": "Before first release: Empty array redirects to /main/. After releases: Add versions here (newest first).", + "$comment": "Before first release: Empty array redirects to /main/. After releases: Add versions here (newest first). Only 'version' is required; 'latest: true' marks the current release.", "versions": [] } diff --git a/docs/VERSIONED_DOCS.md b/docs/VERSIONED_DOCS.md index ee07c15ac..3b993b25a 100644 --- a/docs/VERSIONED_DOCS.md +++ b/docs/VERSIONED_DOCS.md @@ -57,7 +57,33 @@ gh-pages branch: ## Initial Setup -### 1. GitHub Pages Settings +### 1. Configuration + +The documentation system is fully configurable via environment variables in the workflow file [`.github/workflows/docs.yml`](.github/workflows/docs.yml). + +**Required environment variables:** +- `PUBLIC_SITE`: The GitHub Pages base URL (e.g., `https://dfinity.github.io`) +- `PUBLIC_BASE_PREFIX`: The repository path prefix (e.g., `/icp-cli`) + +**For the main dfinity/icp-cli repository:** +```yaml +env: + PUBLIC_SITE: https://dfinity.github.io + PUBLIC_BASE_PREFIX: /icp-cli +``` + +**For forks:** +```yaml +env: + PUBLIC_SITE: https://your-username.github.io + PUBLIC_BASE_PREFIX: /your-repo-name +``` + +These values are set at the workflow level and used by all build jobs. No hardcoded defaults exist in the source code. + +**Validation:** The `build` job validates that both variables are set before building the docs. If either variable is missing, the workflow fails early with a clear error message. Since all publish jobs depend on `build` (via `needs: build`), this single validation point protects the entire pipeline. + +### 2. GitHub Pages Settings Go to **Settings → Pages** in the GitHub repository and configure: @@ -67,7 +93,7 @@ Go to **Settings → Pages** in the GitHub repository and configure: ⚠️ **Important**: Change from "GitHub Actions" to "Deploy from a branch" for this to work. -### 2. Version List Configuration +### 3. Version List Configuration The version list is stored in [docs-site/versions.json](../docs-site/versions.json). @@ -84,7 +110,7 @@ The version list is stored in [docs-site/versions.json](../docs-site/versions.js ```json { "versions": [ - {"version": "0.1", "label": "0.1", "latest": true} + {"version": "0.1", "latest": true} ] } ``` @@ -96,7 +122,7 @@ The workflow automatically: **You only need to update this file when releasing new versions** - the redirect is automated. -### 3. First Deployment +### 4. First Deployment **Before first release:** @@ -124,7 +150,7 @@ The workflow automatically: 2. **Update versions.json and push to main** (updates redirect): ```bash # Edit docs-site/versions.json to add: - # {"version": "0.1", "label": "0.1", "latest": true} + # {"version": "0.1", "latest": true} git add docs-site/versions.json git commit -m "docs: add v0.1 to version list" @@ -171,7 +197,7 @@ git tag v0.2.0 git push origin v0.2.0 # Step 2: Edit docs-site/versions.json to add v0.2 at top (newest first): -# {"version": "0.2", "label": "0.2", "latest": true} +# {"version": "0.2", "latest": true} # And update v0.1 to remove latest flag (or omit it) # Step 3: Push to main (redirect now points to /0.2/, which exists) @@ -267,6 +293,12 @@ The version switcher ([docs-site/src/components/VersionSwitcher.astro](docs-site ## Troubleshooting +### Workflow fails with "environment variable is not set" +- Error: `❌ Error: PUBLIC_SITE environment variable is not set` or `PUBLIC_BASE_PREFIX environment variable is not set` +- Cause: Required environment variables are missing from workflow configuration +- Fix: Add both `PUBLIC_SITE` and `PUBLIC_BASE_PREFIX` to the `env:` section at the top of [`.github/workflows/docs.yml`](.github/workflows/docs.yml) +- See [Configuration](#1-configuration) section for examples + ### Version switcher shows "Failed to load versions" - Check that `versions.json` was deployed to `/icp-cli/versions.json` - Check browser console for fetch errors @@ -306,7 +338,7 @@ git push origin main git tag v0.1.0 git push origin v0.1.0 -# 2. Edit docs-site/versions.json, add: {"version": "0.1", "label": "0.1", "latest": true} +# 2. Edit docs-site/versions.json, add: {"version": "0.1", "latest": true} # 3. Push to main (updates redirect) git add docs-site/versions.json git commit -m "docs: add v0.1 to version list" From 4ced55948ccec55bda61606c77b4cd494ca5b552 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 2 Feb 2026 16:05:01 +0100 Subject: [PATCH 7/8] docs: refactor VERSIONED_DOCS.md - fix inaccuracies, remove duplication --- docs/VERSIONED_DOCS.md | 355 +++++++++-------------------------------- 1 file changed, 76 insertions(+), 279 deletions(-) diff --git a/docs/VERSIONED_DOCS.md b/docs/VERSIONED_DOCS.md index 3b993b25a..7fc4e9f3b 100644 --- a/docs/VERSIONED_DOCS.md +++ b/docs/VERSIONED_DOCS.md @@ -7,364 +7,161 @@ This document explains how the versioned documentation system works for the ICP The documentation site supports multiple versions simultaneously: - `https://dfinity.github.io/icp-cli/` → Redirects to latest version - `https://dfinity.github.io/icp-cli/0.1/` → Version 0.1 docs -- `https://dfinity.github.io/icp-cli/0.2/` → Version 0.2 docs +- `https://dfinity.github.io/icp-cli/main/` → Main branch docs (preview) -Users can switch between versions using the version switcher in the header. +Users can switch between versions using the version switcher dropdown in the header. ## Architecture -### GitHub Pages Deployment - -The site uses the `gh-pages` branch for deployment via the `peaceiris/actions-gh-pages` action, which allows multiple versions to coexist in subdirectories. - -### Directory Structure +### Directory Structure (gh-pages branch) ``` -gh-pages branch: -├── index.html # Redirects to latest version (or main if no releases) +├── index.html # Redirects to latest version (or /main/ if no releases) ├── versions.json # List of available versions -├── main/ # Main branch docs (always updated, for preview) -│ └── (full site) -├── 0.1/ # Version 0.1 docs -│ └── (full site) -├── 0.2/ # Version 0.2 docs -│ └── (full site) +├── main/ # Main branch docs (always updated) +├── 0.1/ # Version 0.1 docs +├── 0.2/ # Version 0.2 docs └── ... ``` -### Workflow - -**[.github/workflows/docs.yml](.github/workflows/docs.yml)** handles all deployment: +### Workflow Triggers -1. **Tag triggers** (`v*`): - - Pushing tag `v0.1.0` automatically deploys docs to `/0.1/` (major.minor) - - Pushing tag `v0.2.0` automatically deploys docs to `/0.2/` +The workflow [`.github/workflows/docs.yml`](.github/workflows/docs.yml) handles deployment: -2. **Branch triggers** (`docs/v*`): - - Pushing to `docs/v0.1` updates docs at `/0.1/` - - Useful for cherry-picking fixes to older versions +| Trigger | Action | +|---------|--------| +| Tag `v*` (e.g., `v0.1.0`) | Deploys to `/0.1/` (major.minor) | +| Branch `docs/v*` (e.g., `docs/v0.1`) | Updates `/0.1/` (for cherry-picks) | +| Push to `main` | Deploys to `/main/`, updates root `index.html` and `versions.json` | -3. **Main branch**: - - Deploys main branch docs to `/main/` (for preview/development) - - Updates `index.html` (redirect to latest version, or `main` if no releases) - - Updates `versions.json` (list of available versions) +### Version Switcher -**Jobs:** -- `build`: Validates docs build on all changes -- `publish-root-files`: Updates index.html and versions.json (runs on `main`) -- `publish-main-docs`: Deploys main branch docs to `/main/` (runs on `main`) -- `publish-versioned-docs`: Publishes versioned docs (runs on tags or `docs/v*` branches) +The component ([VersionSwitcher.astro](../docs-site/src/components/VersionSwitcher.astro)): +- Extracts current version from the URL path at build time +- Fetches `versions.json` at runtime using the configured base prefix +- Shows "dev" badge in local development, "main" badge on main branch +- Shows interactive dropdown with all versions for released docs -## Initial Setup +## Configuration -### 1. Configuration +### Environment Variables -The documentation system is fully configurable via environment variables in the workflow file [`.github/workflows/docs.yml`](.github/workflows/docs.yml). +Set these in the workflow file to configure deployment: -**Required environment variables:** -- `PUBLIC_SITE`: The GitHub Pages base URL (e.g., `https://dfinity.github.io`) -- `PUBLIC_BASE_PREFIX`: The repository path prefix (e.g., `/icp-cli`) - -**For the main dfinity/icp-cli repository:** ```yaml env: - PUBLIC_SITE: https://dfinity.github.io - PUBLIC_BASE_PREFIX: /icp-cli + PUBLIC_SITE: https://dfinity.github.io # GitHub Pages base URL + PUBLIC_BASE_PREFIX: /icp-cli # Repository path prefix ``` -**For forks:** +**For forks**, update both values: ```yaml env: PUBLIC_SITE: https://your-username.github.io PUBLIC_BASE_PREFIX: /your-repo-name ``` -These values are set at the workflow level and used by all build jobs. No hardcoded defaults exist in the source code. - -**Validation:** The `build` job validates that both variables are set before building the docs. If either variable is missing, the workflow fails early with a clear error message. Since all publish jobs depend on `build` (via `needs: build`), this single validation point protects the entire pipeline. - -### 2. GitHub Pages Settings +The `build` job validates these are set before proceeding. -Go to **Settings → Pages** in the GitHub repository and configure: +### GitHub Pages Settings +In **Settings → Pages**: - **Source**: Deploy from a branch -- **Branch**: `gh-pages` -- **Folder**: `/ (root)` +- **Branch**: `gh-pages` / `/ (root)` -⚠️ **Important**: Change from "GitHub Actions" to "Deploy from a branch" for this to work. +### versions.json -### 3. Version List Configuration - -The version list is stored in [docs-site/versions.json](../docs-site/versions.json). - -**Before first release** (empty array): -```json -{ - "versions": [] -} -``` -- Redirect goes to `/main/` (main branch docs) -- Version switcher shows "main" badge +Located at [docs-site/versions.json](../docs-site/versions.json). Update when releasing: -**After releases** (add versions, newest first): ```json { "versions": [ - {"version": "0.1", "latest": true} + {"version": "0.2", "latest": true}, + {"version": "0.1"} ] } ``` -The workflow automatically: -- Deploys this file to the root of gh-pages -- Reads the **first version** in the list (newest) -- Generates `index.html` to redirect to that version (or `main` if empty) - -**You only need to update this file when releasing new versions** - the redirect is automated. - -### 4. First Deployment - -**Before first release:** - -1. **Push to main**: - ```bash - git push origin main - ``` - This deploys: - - Root files (index.html → redirects to `/main/`, versions.json) - - Main branch docs to `/main/` - -2. **Verify pre-release state**: - - `https://dfinity.github.io/icp-cli/` → redirects to `/main/` - - `https://dfinity.github.io/icp-cli/main/` → loads main branch docs - - Version switcher shows "main" badge - -**First release (v0.1.0):** - -1. **Push release tag** (deploys docs to `/0.1/`): - ```bash - git tag v0.1.0 - git push origin v0.1.0 - ``` - -2. **Update versions.json and push to main** (updates redirect): - ```bash - # Edit docs-site/versions.json to add: - # {"version": "0.1", "latest": true} - - git add docs-site/versions.json - git commit -m "docs: add v0.1 to version list" - git push origin main - ``` - -3. **Verify release**: - - `https://dfinity.github.io/icp-cli/` → redirects to `/0.1/` - - `https://dfinity.github.io/icp-cli/0.1/` → loads v0.1 docs - - `https://dfinity.github.io/icp-cli/main/` → still accessible for preview - - Version switcher shows "0.1 (latest)" +The workflow copies this to gh-pages root and generates `index.html` redirecting to the first entry. -## Main Branch Docs +## Common Tasks -The `/main/` path always contains docs built from the main branch: +### First Deployment (Pre-release) -**Purpose:** -- Provides live docs before the first release -- Allows contributors to preview upcoming documentation changes -- Useful for testing docs updates before creating a release - -**Behavior:** -- Always deployed when pushing to main -- Redirect points to `/main/` when versions.json is empty (pre-release) -- Redirect points to latest version after first release -- Version switcher shows "main" badge (non-interactive) -- Remains accessible at `/main/` even after releases - -**Access:** -- Direct: `https://dfinity.github.io/icp-cli/main/` -- Redirect (pre-release only): `https://dfinity.github.io/icp-cli/` +```bash +git push origin main +# → Deploys to /main/, redirect points to /main/ +``` -## Releasing a New Version +### First Release -When releasing v0.2.0: +```bash +# 1. Deploy docs +git tag v0.1.0 +git push origin v0.1.0 -### 1. Deploy +# 2. Update versions.json: add {"version": "0.1", "latest": true} +git add docs-site/versions.json +git commit -m "docs: add v0.1 to version list" +git push origin main +``` -**Important**: Deploy docs FIRST, then update redirect to avoid 404s. +### Subsequent Releases ```bash -# Step 1: Push release tag (deploys v0.2 docs to /0.2/) +# 1. Deploy docs git tag v0.2.0 git push origin v0.2.0 -# Step 2: Edit docs-site/versions.json to add v0.2 at top (newest first): -# {"version": "0.2", "latest": true} -# And update v0.1 to remove latest flag (or omit it) - -# Step 3: Push to main (redirect now points to /0.2/, which exists) +# 2. Update versions.json: add 0.2 at top with latest: true, remove latest from 0.1 git add docs-site/versions.json git commit -m "docs: add v0.2 to version list" git push origin main ``` -### 2. Verify - -- `https://dfinity.github.io/icp-cli/` → redirects to `/0.2/` -- `https://dfinity.github.io/icp-cli/0.2/` → loads v0.2 docs -- `https://dfinity.github.io/icp-cli/0.1/` → still works (old version) -- Version switcher shows both versions, with 0.2 marked as latest - -## Updating Existing Version Docs - -To update docs for an already-released version (e.g., cherry-pick bug fixes): +**Important**: Push the tag first, then update versions.json to avoid 404s. -### Option 1: Create docs branch from existing tag +### Update Old Version Docs ```bash git checkout v0.1.0 git checkout -b docs/v0.1 -# Make changes to docs -git commit -m "docs: fix typo in v0.1 docs" +# Make changes +git commit -m "docs: fix typo in v0.1" git push origin docs/v0.1 ``` -The workflow will automatically rebuild and redeploy to `/0.1/`. - -### Option 2: Push a new patch release tag - -```bash -git tag v0.1.1 -git push origin v0.1.1 -``` - -Since patch version is stripped (`v0.1.1` → `0.1`), this updates the same `/0.1/` directory. +Or push a patch tag (`v0.1.1`) — it deploys to the same `/0.1/` directory. -Both approaches preserve other version directories due to `keep_files: true`. +### Beta Versions -## Beta/Pre-release Versions - -For beta versions, you can either: - -### Option 1: Don't deploy beta docs - -Just skip deploying until stable release. Beta users can build docs locally. - -### Option 2: Deploy beta to its own version - -Create a docs branch with full version: +Create a docs branch with the full version: ```bash -git checkout v0.2.0-beta.5 git checkout -b docs/v0.2.0-beta.5 git push origin docs/v0.2.0-beta.5 +# → Deploys to /0.2.0-beta.5/ ``` -This deploys to `/0.2.0-beta.5/` (full version, not just major.minor). - -**Do not add beta versions to versions.json** - they won't appear in the version switcher. - -## Version Switcher Component - -The version switcher ([docs-site/src/components/VersionSwitcher.astro](docs-site/src/components/VersionSwitcher.astro)): -- Shows "main" badge when viewing main branch docs or no releases exist -- Shows "dev" badge in local development -- Shows version dropdown for released versions -- Reads current version from Cargo.toml at build time -- Fetches `/icp-cli/versions.json` at runtime -- Shows "(latest)" label for the latest version -- Highlights current version with checkmark - -## How It Works: Tag vs Branch - -**When you push a tag** (e.g., `v0.1.0`): -1. Workflow extracts major.minor: `v0.1.0` → `0.1` -2. Builds docs with base path `/icp-cli/0.1/` -3. Deploys to gh-pages branch at `/0.1/` - -**When you push to docs branch** (e.g., `docs/v0.1`): -1. Workflow extracts version: `docs/v0.1` → `0.1` -2. Builds docs with base path `/icp-cli/0.1/` -3. Deploys to gh-pages branch at `/0.1/` (replaces existing) +Don't add beta versions to `versions.json` — they won't appear in the switcher. -**When you push to main**: -1. Deploys main branch docs to `/main/` -2. Updates `index.html` at root (redirect) -3. Updates `versions.json` at root (version list) -4. Uses `keep_files: true` to preserve version directories +## Local Testing -## Troubleshooting - -### Workflow fails with "environment variable is not set" -- Error: `❌ Error: PUBLIC_SITE environment variable is not set` or `PUBLIC_BASE_PREFIX environment variable is not set` -- Cause: Required environment variables are missing from workflow configuration -- Fix: Add both `PUBLIC_SITE` and `PUBLIC_BASE_PREFIX` to the `env:` section at the top of [`.github/workflows/docs.yml`](.github/workflows/docs.yml) -- See [Configuration](#1-configuration) section for examples - -### Version switcher shows "Failed to load versions" -- Check that `versions.json` was deployed to `/icp-cli/versions.json` -- Check browser console for fetch errors -- Verify gh-pages branch contains `versions.json` at root - -### Tag pushed but docs not deployed -- Check GitHub Actions workflow ran successfully -- Verify tag matches pattern `v*` (e.g., `v0.1.0`, not `0.1.0`) -- Check workflow logs for errors - -### New version not appearing in switcher -- Verify `versions.json` was updated in the workflow -- Check that main branch was pushed after updating workflow -- Verify gh-pages branch contains updated `versions.json` - -### Redirect not working -- Check `index.html` in gh-pages branch root -- Verify the redirect URL matches the deployed version path -- Clear browser cache +Run the test script to simulate the full deployment locally: -### Deployment replaces other versions -- Verify workflow uses `keep_files: true` in peaceiris/actions-gh-pages -- Check that `destination_dir` is set correctly for versioned deployments - -## Quick Reference - -**Pre-release (main branch only)**: ```bash -# versions.json is empty - redirect goes to /main/ -git push origin main -# → https://dfinity.github.io/icp-cli/ redirects to /main/ +./docs-site/test-version-switcher.sh ``` -**First release (v0.1.0)**: -```bash -# 1. Push tag (deploys docs to /0.1/) -git tag v0.1.0 -git push origin v0.1.0 - -# 2. Edit docs-site/versions.json, add: {"version": "0.1", "latest": true} -# 3. Push to main (updates redirect) -git add docs-site/versions.json -git commit -m "docs: add v0.1 to version list" -git push origin main -``` +This builds multiple versions and serves them at `http://localhost:4321/icp-cli/`. -**Subsequent release (v0.2.0)**: -```bash -# 1. Push tag (deploys docs to /0.2/) -git tag v0.2.0 -git push origin v0.2.0 - -# 2. Edit docs-site/versions.json, add v0.2 at top, update v0.1 -# 3. Push to main (updates redirect) -git add docs-site/versions.json -git commit -m "docs: add v0.2 to version list" -git push origin main -``` - -**Update old version docs**: -```bash -git checkout v0.1.0 -git checkout -b docs/v0.1 -# Make doc changes -git commit -m "docs: update v0.1 docs" -git push origin docs/v0.1 -``` +## Troubleshooting -That's it! Tags automatically deploy new versions, branches update existing versions. +| Problem | Solution | +|---------|----------| +| Workflow fails with "environment variable is not set" | Add `PUBLIC_SITE` and `PUBLIC_BASE_PREFIX` to workflow `env:` section | +| Version switcher shows "Failed to load versions" | Check `versions.json` exists at gh-pages root, check browser console | +| Tag pushed but docs not deployed | Verify tag matches `v*` pattern, check workflow logs | +| New version not in switcher | Push `versions.json` update to main after adding the version | +| Redirect not working | Check `index.html` in gh-pages, clear browser cache | +| Deployment replaces other versions | Verify `keep_files: true` and correct `destination_dir` in workflow | From f9590c39dc2ae3695840373238c7994e7fc4b552 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 2 Feb 2026 19:50:42 +0100 Subject: [PATCH 8/8] fix: remove unused VersionBadge and use latest field in workflow --- .github/workflows/docs.yml | 4 +- docs-site/src/components/VersionBadge.astro | 59 --------------------- 2 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 docs-site/src/components/VersionBadge.astro diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3b71539f7..92ccf605d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -101,8 +101,8 @@ jobs: # Copy versions.json from repo cp docs-site/versions.json root/versions.json - # Extract latest version (first in list) from versions.json - LATEST_VERSION=$(jq -r '.versions[0].version // empty' docs-site/versions.json) + # Extract the latest version from versions.json + LATEST_VERSION=$(jq -r ".versions[] | select(.latest == true) | .version" docs-site/versions.json) # If no releases yet, redirect to main branch docs if [[ -z "$LATEST_VERSION" ]]; then diff --git a/docs-site/src/components/VersionBadge.astro b/docs-site/src/components/VersionBadge.astro deleted file mode 100644 index 9582f0d60..000000000 --- a/docs-site/src/components/VersionBadge.astro +++ /dev/null @@ -1,59 +0,0 @@ ---- -// Read version from Cargo.toml at build time -import { readFileSync } from 'fs'; -import { resolve } from 'path'; - -let version = 'unknown'; -try { - const cargoToml = readFileSync(resolve('../Cargo.toml'), 'utf-8'); - // Match version in [workspace.package] section or at root level - const versionMatch = cargoToml.match(/\[workspace\.package\][^\[]*version\s*=\s*"([^"]+)"/s) - || cargoToml.match(/^version\s*=\s*"([^"]+)"/m); - if (versionMatch) { - version = versionMatch[1]; - } -} catch (error) { - console.error('Failed to read version from Cargo.toml:', error); -} ---- - -
- v{version} -
- -