From 8a014804ba770c649aa79610fb316b5785d705d6 Mon Sep 17 00:00:00 2001 From: Alex Andres Date: Tue, 13 Oct 2026 14:21:12 +0200 Subject: [PATCH] ci: publish the docs of the latest release, and main's under /next/ The site was built from main, so it described features no release has yet. The root now shows the latest published release (or its release/X.Y.x branch, for doc fixes), and main goes to /next/ with a banner and noindex. Publishing a release redeploys the site. The release workflow wrote the docs' versions after the tag, so each tag named the previous release; they now go into the commit that gets tagged. --- .github/workflows/pages.yml | 100 ++++++++++++++++++++++++--- .github/workflows/release.yml | 29 ++++---- docs/.vitepress/channel.ts | 9 +++ docs/.vitepress/config.mts | 2 + docs/.vitepress/head.ts | 6 ++ docs/.vitepress/nav.ts | 7 +- docs/.vitepress/theme/NextBanner.vue | 35 ++++++++++ docs/.vitepress/theme/index.ts | 3 + 8 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 docs/.vitepress/channel.ts create mode 100644 docs/.vitepress/theme/NextBanner.vue diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 294b0f143..fcb1404ea 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,14 +1,28 @@ name: Deploy Documentation to Pages +# The site holds two builds of the docs: +# / the latest published GitHub release, so that the docs match what can +# be installed. A release/X.Y.x branch of that release, if there is one, +# is built instead of its tag, to fix those docs without a new release. +# /next/ main, which documents what has not been released yet. +# +# Publishing a release (the release workflow leaves it as a draft) moves the root +# to it. Pushes made by the release workflow itself trigger no workflows. +# +# The github-pages environment deploys from main only, so a release or a push to +# a release branch runs this workflow again on main, which builds both anyway. on: push: - branches: [main] + branches: [main, 'release/**'] paths: - 'docs/**' + release: + types: [published] + # PRs only build the site, so that a broken build shows up before the merge. pull_request: - branches: [main] + branches: [main, 'release/**'] paths: - 'docs/**' - '.github/workflows/pages.yml' @@ -25,11 +39,57 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: + redeploy: + if: github.event_name == 'release' || (github.event_name == 'push' && github.ref != 'refs/heads/main') + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Run on main + env: + GH_TOKEN: ${{ github.token }} + run: gh workflow run pages.yml --repo "$GITHUB_REPOSITORY" --ref main + build: + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: + # All branches and tags, to build the release's docs from another tree. - name: Checkout uses: actions/checkout@v7 + with: + fetch-depth: 0 + + # A PR stands in for the tree it targets, so that its build is the one checked. + - name: Determine what to build + id: refs + env: + GH_TOKEN: ${{ github.token }} + BASE_REF: ${{ github.base_ref }} + run: | + release_tag=$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name) + version="${release_tag#v}" + maintenance="release/${version%.*}.x" + + stable="$release_tag" + if git rev-parse -q --verify "refs/remotes/origin/$maintenance" >/dev/null; then + stable="origin/$maintenance" + fi + next="origin/main" + + if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then + if [ "$BASE_REF" == "main" ]; then next="HEAD"; else stable="HEAD"; fi + fi + + snapshot=$(git show "$next:pom.xml" | sed -n '0,//s|.*\([^<]*\).*|\1|p') + + echo "Latest release: $version, built from $stable; unreleased: $snapshot, built from $next" + { + echo "stable=$stable" + echo "next=$next" + echo "version=$version" + echo "snapshot=$snapshot" + } >> "$GITHUB_OUTPUT" - name: Setup Node uses: actions/setup-node@v7 @@ -42,21 +102,39 @@ jobs: if: github.event_name != 'pull_request' uses: actions/configure-pages@v6 - - name: Install dependencies - run: | - cd docs - npm ci - + # Both builds are told the same versions. The tree of a release tag still + # carries the versions of the release before it, which was how the release + # workflow used to write them. - name: Build with VitePress + env: + STABLE_REF: ${{ steps.refs.outputs.stable }} + NEXT_REF: ${{ steps.refs.outputs.next }} + VERSION: ${{ steps.refs.outputs.version }} + SNAPSHOT: ${{ steps.refs.outputs.snapshot }} run: | - cd docs - npm run build + build() { + local ref="$1" tree="$2" channel="$3" out="$4" + git worktree add --detach "$tree" "$ref" + ( + cd "$tree/docs" + sed -i -e "s/VERSION: '.*'/VERSION: '$VERSION'/" \ + -e "s/VERSION_SNAPSHOT: '.*'/VERSION_SNAPSHOT: '$SNAPSHOT'/" .vitepress/versions.ts + npm ci + DOCS_CHANNEL="$channel" npm run build + ) + mkdir -p "$out" + cp -r "$tree/docs/.vitepress/dist/." "$out" + } + + site="$RUNNER_TEMP/site" + build "$STABLE_REF" "$RUNNER_TEMP/stable" stable "$site" + build "$NEXT_REF" "$RUNNER_TEMP/next" next "$site/next" - name: Upload artifact if: github.event_name != 'pull_request' uses: actions/upload-pages-artifact@v5 with: - path: docs/.vitepress/dist + path: ${{ runner.temp }}/site deploy: if: github.event_name != 'pull_request' @@ -69,4 +147,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5 \ No newline at end of file + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8c06e18f..880e6135c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,11 +94,18 @@ jobs: env: OUTPUT: CHANGELOG.md - - name: Commit CHANGELOG.md + # The docs' versions go into this commit, so that the tagged tree already + # names the version it releases. + - name: Commit CHANGELOG.md and the docs' versions env: RELEASE_TAG: ${{ steps.release-version.outputs.tag }} + RELEASE_VERSION: ${{ steps.release-version.outputs.version }} + DEVELOPMENT_VERSION: ${{ inputs.developmentVersion }} run: | - git add CHANGELOG.md + sed -i -e "s/VERSION: '.*'/VERSION: '$RELEASE_VERSION'/" \ + -e "s/VERSION_SNAPSHOT: '.*'/VERSION_SNAPSHOT: '$DEVELOPMENT_VERSION'/" docs/.vitepress/versions.ts + + git add CHANGELOG.md docs/.vitepress/versions.ts git commit -m "chore(release): Update CHANGELOG.md for $RELEASE_TAG" - name: Set up Java @@ -112,7 +119,7 @@ jobs: # caches, only for the platform jobs below to build the tagged tree again. # # A dry run transforms the poms and writes release.properties, but creates no - # commit and no tag, so there is nothing to amend or push below. + # commit and no tag, so there is nothing to push below. - name: Prepare release env: DEVELOPMENT_VERSION: ${{ inputs.developmentVersion }} @@ -131,25 +138,13 @@ jobs: echo "::error::release:prepare chose version '$RELEASE_VERSION', expected '$EXPECTED_VERSION'" exit 1 fi - echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV" + # The branch and the tag land together or not at all. - name: Push release commits and tag if: ${{ !inputs.dryRun }} env: - DEVELOPMENT_VERSION: ${{ inputs.developmentVersion }} RELEASE_TAG: ${{ steps.release-version.outputs.tag }} - run: | - # Update versions.ts file - echo "Updating versions.ts with release version: $RELEASE_VERSION and development version: $DEVELOPMENT_VERSION" - sed -i "s/VERSION: '.*'/VERSION: '$RELEASE_VERSION'/g" docs/.vitepress/versions.ts - sed -i "s/VERSION_SNAPSHOT: '.*'/VERSION_SNAPSHOT: '$DEVELOPMENT_VERSION'/g" docs/.vitepress/versions.ts - - # Add the updated file to the existing commit - git add docs/.vitepress/versions.ts - git commit --amend --no-edit - - # The branch and the tag land together or not at all. - git push --atomic origin HEAD:main "refs/tags/$RELEASE_TAG" + run: git push --atomic origin HEAD:main "refs/tags/$RELEASE_TAG" # The tag prepare-release pushed. A dry run pushes none and builds the # dispatched commit instead. diff --git a/docs/.vitepress/channel.ts b/docs/.vitepress/channel.ts new file mode 100644 index 000000000..123d65cd0 --- /dev/null +++ b/docs/.vitepress/channel.ts @@ -0,0 +1,9 @@ +// The site is published twice: the docs of the latest release at the root, and +// the docs of main, which describe what has not been released yet, under /next/. +// The Pages workflow builds the latter with DOCS_CHANNEL=next. +export const SITE_URL = 'https://jrtc.dev/'; +export const NEXT_BASE = '/next/'; + +// The theme imports this file too, and runs in the browser, where there is no +// process; it goes by the site's base instead. +export const IS_NEXT = typeof process !== 'undefined' && process.env.DOCS_CHANNEL === 'next'; diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 0c626a51f..05ea48025 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -3,12 +3,14 @@ import { footer } from "./footer"; import { head } from "./head"; import { navbar } from "./nav"; import { sidebar } from "./sidebar"; +import { IS_NEXT, NEXT_BASE } from './channel' import { PROJECT_VARS } from './versions' export default defineConfig({ title: "webrtc-java", description: "Java native interface for WebRTC", cleanUrls: true, + base: IS_NEXT ? NEXT_BASE : '/', head: head, diff --git a/docs/.vitepress/head.ts b/docs/.vitepress/head.ts index f00097905..bb7bbefe9 100644 --- a/docs/.vitepress/head.ts +++ b/docs/.vitepress/head.ts @@ -1,4 +1,5 @@ import { HeadConfig } from "vitepress/types/shared"; +import { IS_NEXT } from "./channel"; const isProd = process.env.NODE_ENV === 'production'; @@ -11,6 +12,11 @@ const head: HeadConfig[] = [ ['meta', { name: "apple-mobile-web-app-title", content: "JRTC" }], ] +// Searches should lead to the docs of what can be installed. +if (IS_NEXT) { + head.push(['meta', { name: "robots", content: "noindex" }]) +} + if (isProd) { head.push([ 'script', diff --git a/docs/.vitepress/nav.ts b/docs/.vitepress/nav.ts index a98838507..b96449dda 100644 --- a/docs/.vitepress/nav.ts +++ b/docs/.vitepress/nav.ts @@ -1,4 +1,5 @@ import { DefaultTheme } from "vitepress"; +import { IS_NEXT, NEXT_BASE, SITE_URL } from './channel' import { PROJECT_VARS } from './versions' export const navbar: DefaultTheme.NavItem[] = [ @@ -15,8 +16,12 @@ export const navbar: DefaultTheme.NavItem[] = [ link: '/tools', }, { - text: PROJECT_VARS.VERSION, + text: IS_NEXT ? PROJECT_VARS.VERSION_SNAPSHOT : PROJECT_VARS.VERSION, items: [ + // Absolute, since each channel is a site of its own, with its own base. + IS_NEXT + ? { text: `Latest release (${PROJECT_VARS.VERSION})`, link: SITE_URL } + : { text: 'Unreleased (main)', link: new URL(NEXT_BASE, SITE_URL).href }, { text: 'Changelog', link: 'https://github.com/devopvoid/webrtc-java/blob/main/CHANGELOG.md' diff --git a/docs/.vitepress/theme/NextBanner.vue b/docs/.vitepress/theme/NextBanner.vue new file mode 100644 index 000000000..d1fcee9dd --- /dev/null +++ b/docs/.vitepress/theme/NextBanner.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index def4cfc87..cd4f91d14 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -2,6 +2,7 @@ import { h } from 'vue' import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' +import NextBanner from './NextBanner.vue' import './style.css' export default { @@ -9,6 +10,8 @@ export default { Layout: () => { return h(DefaultTheme.Layout, null, { // https://vitepress.dev/guide/extending-default-theme#layout-slots + 'doc-before': () => h(NextBanner), + 'home-hero-before': () => h(NextBanner), }) }, enhanceApp({ app, router, siteData }) {