Repository navigation
Publish Docs #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish Docs | |
| # Publishes the versioned docs site to GitHub Pages. | |
| # | |
| # Discovers the versions to publish from the repository's GitHub releases (the | |
| # latest release, by date, for each major >= 1), builds each release tag into | |
| # its own sub-path (e.g. /v1/ and /v2/) with make generate-docs, injects a | |
| # version-picker widget into each page, adds a root redirect to the default | |
| # version, and deploys the combined site. A manual dispatch can build every | |
| # major-version path from release tags or current source branches. | |
| # Triggers on release publish and manual dispatch. | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| docs_source: | |
| description: Source for every published major-version path | |
| required: true | |
| default: release-tags | |
| type: choice | |
| options: | |
| - release-tags | |
| - latest-branches | |
| # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write # Required for actions/deploy-pages | |
| # Allow only one concurrent deployment. Cancel any in-progress run when a newer one | |
| # starts: every run rediscovers the current releases and rebuilds the whole site from | |
| # scratch, so a newer run fully supersedes the work of the one it cancels. | |
| concurrency: | |
| group: "pages" | |
| cancel-in-progress: true | |
| jobs: | |
| publish-docs: | |
| # Only publish from the modelcontextprotocol/csharp-sdk repository | |
| if: ${{ github.repository == 'modelcontextprotocol/csharp-sdk' }} | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout docs orchestration | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| # A release event otherwise checks out the released tag. Keep the | |
| # orchestration scripts and picker assets current with main. | |
| ref: main | |
| # Full history + tags so we can add a worktree for each version's release tag. | |
| fetch-depth: 0 | |
| - name: .NET Setup | |
| uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| dotnet-version: | | |
| 10.0.x | |
| 9.0.x | |
| - name: Discover versions from GitHub releases | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # For every major version >= 1, take the most recently published (by date) | |
| # non-draft release tagged v{major}.*. The vN prefix becomes the URL slug; | |
| # the site root redirects to the newest release, including prereleases. | |
| run: | | |
| set -euo pipefail | |
| gh release list --repo "${{ github.repository }}" --limit 200 \ | |
| --json tagName,isPrerelease,isDraft,publishedAt \ | |
| --jq ' | |
| [ .[] | |
| | select((.isDraft | not) and (.tagName | test("^v[1-9][0-9]*\\."))) | |
| | { slug: (.tagName | match("^v[0-9]+").string), | |
| ref: .tagName, label: .tagName, | |
| prerelease: .isPrerelease, published: .publishedAt } | |
| ] | |
| | group_by(.slug) | |
| | map(max_by(.published)) | |
| | sort_by(.published) | reverse | |
| | { default: .[0].slug, | |
| versions: map({ slug, ref, label, prerelease }) } | |
| ' | tee "$RUNNER_TEMP/docs-versions.json" | |
| - name: Build all versions | |
| shell: bash | |
| env: | |
| USE_SOURCE_BRANCHES: ${{ inputs.docs_source == 'latest-branches' }} | |
| run: | | |
| set -euo pipefail | |
| ROOT="$PWD" | |
| COMBINED="$ROOT/combined" | |
| rm -rf "$COMBINED" | |
| mkdir -p "$COMBINED" | |
| # Orchestration (discovered docs-versions.json, scripts, picker assets) | |
| # always comes from THIS branch. Normally each version's HTML is produced | |
| # by its release tag. A manual dispatch can instead build every major from | |
| # its current source branch. | |
| git fetch --tags --force --prune origin "+refs/heads/*:refs/remotes/origin/*" | |
| USE_SOURCE_BRANCHES="${USE_SOURCE_BRANCHES:-false}" | |
| MAIN_SLUG="" | |
| if [[ "$USE_SOURCE_BRANCHES" == "true" ]]; then | |
| MAIN_PROPS="$(mktemp)" | |
| git show refs/remotes/origin/main:src/Directory.Build.props > "$MAIN_PROPS" | |
| MAIN_SLUG="$(node "$ROOT/scripts/get-docs-version-slug.mjs" "$MAIN_PROPS")" | |
| rm -f "$MAIN_PROPS" | |
| echo "Refreshing all published docs from source branches (main serves $MAIN_SLUG)" | |
| else | |
| echo "Refreshing all published docs from release tags" | |
| fi | |
| while IFS=$'\t' read -r slug ref; do | |
| if [[ "$USE_SOURCE_BRANCHES" == "true" ]]; then | |
| source_branch="release/${slug#v}.x" | |
| if ! git show-ref --verify --quiet "refs/remotes/origin/$source_branch"; then | |
| if [[ "$slug" == "$MAIN_SLUG" ]]; then | |
| source_branch="main" | |
| else | |
| echo "::error::No source branch found for $slug. Expected release/${slug#v}.x, or main with VersionPrefix for $slug." | |
| exit 1 | |
| fi | |
| fi | |
| echo "::group::Build $slug (from branch $source_branch)" | |
| wt="$ROOT/../work-$slug" | |
| git worktree add --force --detach "$wt" "refs/remotes/origin/$source_branch" | |
| source_slug="$(node "$ROOT/scripts/get-docs-version-slug.mjs" "$wt/src/Directory.Build.props")" | |
| if [[ "$source_slug" != "$slug" ]]; then | |
| git worktree remove --force "$wt" | |
| echo "::error::Source branch '$source_branch' has major '$source_slug', not '$slug'." | |
| exit 1 | |
| fi | |
| else | |
| echo "::group::Build $slug (from tag $ref)" | |
| wt="$ROOT/../work-$slug" | |
| git worktree add --force --detach "$wt" "refs/tags/$ref" | |
| fi | |
| make -C "$wt" generate-docs | |
| mkdir -p "$COMBINED/$slug" | |
| cp -a "$wt/artifacts/_site/." "$COMBINED/$slug/" | |
| node "$ROOT/scripts/inject-version-picker.mjs" "$COMBINED/$slug" "$slug" --base / | |
| git worktree remove --force "$wt" | |
| echo "::endgroup::" | |
| done < <(node "$ROOT/scripts/list-versions.mjs") | |
| node "$ROOT/scripts/finalize-docs-site.mjs" "$COMBINED" | |
| echo "Combined site contents:" | |
| ls -la "$COMBINED" | |
| - name: Upload Pages artifact | |
| uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 | |
| with: | |
| path: 'combined' | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 |