diff --git a/.github/actions/documentation/docfx-build/action.yml b/.github/actions/documentation/docfx-build/action.yml index e21ced83..a24e6e75 100644 --- a/.github/actions/documentation/docfx-build/action.yml +++ b/.github/actions/documentation/docfx-build/action.yml @@ -21,12 +21,10 @@ inputs: runs: using: composite steps: - - name: 'Checkout ${{ github.head_ref || github.ref }}' - uses: actions/checkout@v6 - name: Dotnet Setup uses: actions/setup-dotnet@v4 with: - dotnet-version: ${{ env.dotnet-sdk-version }} + dotnet-version: ${{ inputs.dotnet_sdk_version }} - name: 'testing variables' shell: bash run: | diff --git a/.github/workflows/publish-documentation.yml b/.github/workflows/publish-documentation.yml index ddaa2f04..2a579943 100644 --- a/.github/workflows/publish-documentation.yml +++ b/.github/workflows/publish-documentation.yml @@ -18,6 +18,9 @@ concurrency: group: publish-docs-${{ github.head_ref || github.ref }} cancel-in-progress: true +env: + dotnet-sdk-version: '10.x' + jobs: validate-branch: name: 'Validate branch' @@ -49,7 +52,7 @@ jobs: needs: [workflow-variables] runs-on: ubuntu-latest outputs: - friendly-version: ${{ steps.versioning.outputs.friendly-version }} + friendly-version: ${{ steps.versioning.outputs.version }} steps: - name: 'Checkout ${{ github.head_ref || github.ref }}' @@ -76,12 +79,80 @@ jobs: - name: 'Checkout ${{ github.head_ref || github.ref }}' uses: actions/checkout@v6 - - name: 'Generate documentation' - uses: ./.github/actions/documentation/docfx-build + - name: 'Setup .NET ${{ env.dotnet-sdk-version }}' + uses: actions/setup-dotnet@v5 with: - artifact-name: 'documentation' - docfx-json-manifest: './api-reference/api-reference.json' - output-directory: './api-reference/_docs' + dotnet-version: ${{ env.dotnet-sdk-version }} + + - name: 'Install docfx' + shell: bash + run: dotnet tool update -g docfx + + - name: 'Regenerate API metadata for v${{ env.friendly-version }}' + shell: bash + run: | + rm -rf api-reference/${{ env.friendly-version }} + cd api-reference + docfx metadata assembly-metadata.json + mv temp ${{ env.friendly-version }} + + - name: 'Discover all versions' + id: discover-versions + shell: bash + run: | + versions=$(ls api-reference/ | grep -E '^\d+\.\d+$' | sort -V | paste -sd ',' -) + latest=$(ls api-reference/ | grep -E '^\d+\.\d+$' | sort -V | tail -1) + echo "versions=$versions" >> $GITHUB_OUTPUT + echo "latest=$latest" >> $GITHUB_OUTPUT + + - name: 'Update versions.json' + shell: bash + run: | + versions_array=$(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' '\n' | jq -R . | jq -s .) + jq --arg latest "${{ steps.discover-versions.outputs.latest }}" \ + --argjson versions "$versions_array" \ + '.latest = $latest | .versions = $versions' \ + api-reference/versions.json > /tmp/versions.json + mv /tmp/versions.json api-reference/versions.json + + - name: 'Update api-reference.json with version groups' + shell: bash + run: | + cp api-reference/api-reference.json /tmp/api-reference.json + for ver in $(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' ' '); do + jq --arg ver "$ver" --arg group "v${ver}" \ + '.build.content += [{"dest": "", "files": ["*.yml"], "group": $group, "src": $ver, "rootTocPath": "~/toc.html"}] | + .build.groups = (.build.groups // {}) | + .build.groups[$group] = {"dest": $ver}' \ + /tmp/api-reference.json > /tmp/api-reference-new.json + mv /tmp/api-reference-new.json /tmp/api-reference.json + done + mv /tmp/api-reference.json api-reference/api-reference.json + + - name: 'Update toc.yml with Reference dropdown' + shell: bash + run: | + latest="${{ steps.discover-versions.outputs.latest }}" + { + echo "### YamlMime:TableOfContent" + echo "- name: Guide" + echo " href: guide/" + echo "- name: Reference" + echo " dropdown: true" + echo " items:" + for ver in $(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' '\n' | sort -Vr); do + if [ "$ver" = "$latest" ]; then + echo " - name: v${ver} (latest)" + else + echo " - name: v${ver}" + fi + echo " href: ${ver}/PolylineAlgorithm.html" + done + } > api-reference/toc.yml + + - name: 'Build documentation' + shell: bash + run: docfx build api-reference/api-reference.json - name: 'Upload artifact' uses: actions/upload-pages-artifact@v4 diff --git a/.gitignore b/.gitignore index 6105dc57..2118d126 100644 --- a/.gitignore +++ b/.gitignore @@ -265,3 +265,6 @@ __pycache__/ # GitHub Copilot Testing folder /.codetesting + +# DocFX build output +api-reference/_docs/ diff --git a/api-reference/api-reference.json b/api-reference/api-reference.json index 78956c32..c5797921 100644 --- a/api-reference/api-reference.json +++ b/api-reference/api-reference.json @@ -14,37 +14,21 @@ "exclude": [ "_docs/**" ] - }, - { - "dest": "", - "files": [ "*.yml" ], - "group": "v1.0", - "src": "1.0", - "rootTocPath": "~/toc.html" - }, - { - "dest": "", - "files": [ "*.yml" ], - "group": "v1.1", - "src": "1.1", - "rootTocPath": "~/toc.html" } ], "resource": [ { "files": [ - "media/**" + "media/**", + "versions.json" ] } ], - "groups": { - "v1.0": { "dest": "1.0" }, - "v1.1": { "dest": "1.1" } - }, "output": "_docs", "template": [ "default", - "modern" + "modern", + "docs-versioning" ], "maxParallelism": 1, "globalMetadata": { diff --git a/api-reference/docs-versioning/layout/_master.tmpl b/api-reference/docs-versioning/layout/_master.tmpl new file mode 100644 index 00000000..68043875 --- /dev/null +++ b/api-reference/docs-versioning/layout/_master.tmpl @@ -0,0 +1,163 @@ +{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}} +{{!include(/^public/.*/)}} +{{!include(favicon.ico)}} +{{!include(logo.svg)}} + + + + + {{#redirect_url}} + + {{/redirect_url}} + {{^redirect_url}} + {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} + + + {{#_description}}{{/_description}} + {{#description}}{{/description}} + + + + + + {{#_noindex}}{{/_noindex}} + + {{#_disableNewTab}}{{/_disableNewTab}} + {{#_disableTocFilter}}{{/_disableTocFilter}} + {{#docurl}}{{/docurl}} + + + + + + + + + + + + + + + + + + {{#_googleAnalyticsTagId}} + + + {{/_googleAnalyticsTagId}} + {{/redirect_url}} + + + {{^redirect_url}} + +
+ {{^_disableNavbar}} + + {{/_disableNavbar}} +
+ +
+ {{^_disableToc}} +
+
+
+
Table of Contents
+ +
+
+ +
+
+
+ {{/_disableToc}} + +
+
+ {{^_disableToc}} + + {{/_disableToc}} + + {{^_disableBreadcrumb}} + + {{/_disableBreadcrumb}} +
+ +
+ {{!body}} +
+ + {{^_disableContribution}} +
+ {{#sourceurl}} + {{__global.improveThisDoc}} + {{/sourceurl}} + {{^sourceurl}}{{#docurl}} + {{__global.improveThisDoc}} + {{/docurl}}{{/sourceurl}} +
+ {{/_disableContribution}} + + {{^_disableNextArticle}} + + {{/_disableNextArticle}} + +
+ + {{^_disableAffix}} +
+ +
+ {{/_disableAffix}} +
+ + {{#_enableSearch}} +
+ {{/_enableSearch}} + + + + + {{/redirect_url}} + diff --git a/api-reference/docs-versioning/public/version-switcher.js b/api-reference/docs-versioning/public/version-switcher.js new file mode 100644 index 00000000..325a0531 --- /dev/null +++ b/api-reference/docs-versioning/public/version-switcher.js @@ -0,0 +1,86 @@ +(function () { + 'use strict'; + + function getVersionFromPath(pathname) { + var match = pathname.match(/\/(\d+\.\d+)\//); + return match ? match[1] : null; + } + + function getPathAfterVersion(pathname) { + var match = pathname.match(/\/\d+\.\d+\/(.*)/); + return match ? match[1] : ''; + } + + function getSiteRoot() { + var meta = document.querySelector('meta[name="docfx:rel"]'); + return meta ? meta.getAttribute('content') : './'; + } + + function initVersionPicker(versions, latest) { + var select = document.getElementById('version-picker'); + if (!select) return; + + var currentVersion = getVersionFromPath(window.location.pathname); + var relativePath = getPathAfterVersion(window.location.pathname); + + versions.forEach(function (v) { + var option = document.createElement('option'); + option.value = v; + option.textContent = v === latest ? 'v' + v + ' (latest)' : 'v' + v; + if (v === currentVersion) { + option.selected = true; + } + select.appendChild(option); + }); + + if (!currentVersion) { + var placeholder = document.createElement('option'); + placeholder.value = ''; + placeholder.textContent = 'Select version'; + placeholder.selected = true; + placeholder.disabled = true; + select.insertBefore(placeholder, select.firstChild); + } + + select.addEventListener('change', function () { + var targetVersion = select.value; + if (!targetVersion) return; + + var newPathname; + if (currentVersion && relativePath) { + newPathname = window.location.pathname.replace( + '/' + currentVersion + '/', + '/' + targetVersion + '/' + ); + } else { + newPathname = window.location.pathname.replace(/\/$/, '') + '/' + targetVersion + '/'; + } + + window.location.pathname = newPathname; + }); + } + + function loadVersions() { + var root = getSiteRoot(); + var url = root + 'versions.json'; + + fetch(url) + .then(function (r) { + if (!r.ok) throw new Error('versions.json not found'); + return r.json(); + }) + .then(function (data) { + initVersionPicker(data.versions, data.latest); + }) + .catch(function () { + var container = document.getElementById('version-picker-container'); + if (container) container.style.display = 'none'; + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', loadVersions); + } else { + loadVersions(); + } +}()); diff --git a/api-reference/toc.yml b/api-reference/toc.yml index 114c10f6..df40958f 100644 --- a/api-reference/toc.yml +++ b/api-reference/toc.yml @@ -1,8 +1,3 @@ ### YamlMime:TableOfContent - name: Guide - href: guide/ -- name: Reference - dropdown: true - items: - - name: v1.0 - href: 1.0/PolylineAlgorithm.html \ No newline at end of file + href: guide/ \ No newline at end of file diff --git a/api-reference/versions.json b/api-reference/versions.json new file mode 100644 index 00000000..6b9556d1 --- /dev/null +++ b/api-reference/versions.json @@ -0,0 +1,4 @@ +{ + "latest": null, + "versions": [] +}