Skip to content

Commit 89021ee

Browse files
Copilotpetesramek
andauthored
Add URL-aware version switcher to API docs via docs-versioning template (#157)
- [x] Add docs-versioning template (layout/_master.tmpl + version-switcher.js) - [x] Add versions.json manifest (latest: null, versions: []) - [x] Add `api-reference/_docs/` to `.gitignore` and untrack it from git - [x] `api-reference.json` — version content sections removed from repo (populated dynamically by CI) - [x] `toc.yml` — Reference dropdown removed from repo (populated dynamically by CI) - [x] Fix versioning job output name bug (`friendly-version` → `version`) - [x] Add `dotnet-sdk-version: '10.x'` env to `publish-documentation.yml` - [x] Rewrite `generate-docs` job: regenerate metadata, patch api-reference.json/versions.json/toc.yml, inline docfx build - [x] Fix `docfx-build` action: remove internal checkout, fix dotnet-version input reference --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
1 parent 4fa30ca commit 89021ee

8 files changed

Lines changed: 339 additions & 35 deletions

File tree

.github/actions/documentation/docfx-build/action.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ inputs:
2121
runs:
2222
using: composite
2323
steps:
24-
- name: 'Checkout ${{ github.head_ref || github.ref }}'
25-
uses: actions/checkout@v6
2624
- name: Dotnet Setup
2725
uses: actions/setup-dotnet@v4
2826
with:
29-
dotnet-version: ${{ env.dotnet-sdk-version }}
27+
dotnet-version: ${{ inputs.dotnet_sdk_version }}
3028
- name: 'testing variables'
3129
shell: bash
3230
run: |

.github/workflows/publish-documentation.yml

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ concurrency:
1818
group: publish-docs-${{ github.head_ref || github.ref }}
1919
cancel-in-progress: true
2020

21+
env:
22+
dotnet-sdk-version: '10.x'
23+
2124
jobs:
2225
validate-branch:
2326
name: 'Validate branch'
@@ -49,7 +52,7 @@ jobs:
4952
needs: [workflow-variables]
5053
runs-on: ubuntu-latest
5154
outputs:
52-
friendly-version: ${{ steps.versioning.outputs.friendly-version }}
55+
friendly-version: ${{ steps.versioning.outputs.version }}
5356

5457
steps:
5558
- name: 'Checkout ${{ github.head_ref || github.ref }}'
@@ -76,12 +79,80 @@ jobs:
7679
- name: 'Checkout ${{ github.head_ref || github.ref }}'
7780
uses: actions/checkout@v6
7881

79-
- name: 'Generate documentation'
80-
uses: ./.github/actions/documentation/docfx-build
82+
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
83+
uses: actions/setup-dotnet@v5
8184
with:
82-
artifact-name: 'documentation'
83-
docfx-json-manifest: './api-reference/api-reference.json'
84-
output-directory: './api-reference/_docs'
85+
dotnet-version: ${{ env.dotnet-sdk-version }}
86+
87+
- name: 'Install docfx'
88+
shell: bash
89+
run: dotnet tool update -g docfx
90+
91+
- name: 'Regenerate API metadata for v${{ env.friendly-version }}'
92+
shell: bash
93+
run: |
94+
rm -rf api-reference/${{ env.friendly-version }}
95+
cd api-reference
96+
docfx metadata assembly-metadata.json
97+
mv temp ${{ env.friendly-version }}
98+
99+
- name: 'Discover all versions'
100+
id: discover-versions
101+
shell: bash
102+
run: |
103+
versions=$(ls api-reference/ | grep -E '^\d+\.\d+$' | sort -V | paste -sd ',' -)
104+
latest=$(ls api-reference/ | grep -E '^\d+\.\d+$' | sort -V | tail -1)
105+
echo "versions=$versions" >> $GITHUB_OUTPUT
106+
echo "latest=$latest" >> $GITHUB_OUTPUT
107+
108+
- name: 'Update versions.json'
109+
shell: bash
110+
run: |
111+
versions_array=$(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' '\n' | jq -R . | jq -s .)
112+
jq --arg latest "${{ steps.discover-versions.outputs.latest }}" \
113+
--argjson versions "$versions_array" \
114+
'.latest = $latest | .versions = $versions' \
115+
api-reference/versions.json > /tmp/versions.json
116+
mv /tmp/versions.json api-reference/versions.json
117+
118+
- name: 'Update api-reference.json with version groups'
119+
shell: bash
120+
run: |
121+
cp api-reference/api-reference.json /tmp/api-reference.json
122+
for ver in $(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' ' '); do
123+
jq --arg ver "$ver" --arg group "v${ver}" \
124+
'.build.content += [{"dest": "", "files": ["*.yml"], "group": $group, "src": $ver, "rootTocPath": "~/toc.html"}] |
125+
.build.groups = (.build.groups // {}) |
126+
.build.groups[$group] = {"dest": $ver}' \
127+
/tmp/api-reference.json > /tmp/api-reference-new.json
128+
mv /tmp/api-reference-new.json /tmp/api-reference.json
129+
done
130+
mv /tmp/api-reference.json api-reference/api-reference.json
131+
132+
- name: 'Update toc.yml with Reference dropdown'
133+
shell: bash
134+
run: |
135+
latest="${{ steps.discover-versions.outputs.latest }}"
136+
{
137+
echo "### YamlMime:TableOfContent"
138+
echo "- name: Guide"
139+
echo " href: guide/"
140+
echo "- name: Reference"
141+
echo " dropdown: true"
142+
echo " items:"
143+
for ver in $(echo "${{ steps.discover-versions.outputs.versions }}" | tr ',' '\n' | sort -Vr); do
144+
if [ "$ver" = "$latest" ]; then
145+
echo " - name: v${ver} (latest)"
146+
else
147+
echo " - name: v${ver}"
148+
fi
149+
echo " href: ${ver}/PolylineAlgorithm.html"
150+
done
151+
} > api-reference/toc.yml
152+
153+
- name: 'Build documentation'
154+
shell: bash
155+
run: docfx build api-reference/api-reference.json
85156

86157
- name: 'Upload artifact'
87158
uses: actions/upload-pages-artifact@v4

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,6 @@ __pycache__/
265265

266266
# GitHub Copilot Testing folder
267267
/.codetesting
268+
269+
# DocFX build output
270+
api-reference/_docs/

api-reference/api-reference.json

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,21 @@
1414
"exclude": [
1515
"_docs/**"
1616
]
17-
},
18-
{
19-
"dest": "",
20-
"files": [ "*.yml" ],
21-
"group": "v1.0",
22-
"src": "1.0",
23-
"rootTocPath": "~/toc.html"
24-
},
25-
{
26-
"dest": "",
27-
"files": [ "*.yml" ],
28-
"group": "v1.1",
29-
"src": "1.1",
30-
"rootTocPath": "~/toc.html"
3117
}
3218
],
3319
"resource": [
3420
{
3521
"files": [
36-
"media/**"
22+
"media/**",
23+
"versions.json"
3724
]
3825
}
3926
],
40-
"groups": {
41-
"v1.0": { "dest": "1.0" },
42-
"v1.1": { "dest": "1.1" }
43-
},
4427
"output": "_docs",
4528
"template": [
4629
"default",
47-
"modern"
30+
"modern",
31+
"docs-versioning"
4832
],
4933
"maxParallelism": 1,
5034
"globalMetadata": {
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
2+
{{!include(/^public/.*/)}}
3+
{{!include(favicon.ico)}}
4+
{{!include(logo.svg)}}
5+
<!DOCTYPE html>
6+
<html {{#_lang}}lang="{{_lang}}"{{/_lang}}>
7+
<head>
8+
<meta charset="utf-8">
9+
{{#redirect_url}}
10+
<meta http-equiv="refresh" content="0;URL='{{redirect_url}}'">
11+
{{/redirect_url}}
12+
{{^redirect_url}}
13+
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
14+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
15+
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
16+
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
17+
{{#description}}<meta name="description" content="{{description}}">{{/description}}
18+
<link rel="icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
19+
<link rel="stylesheet" href="{{_rel}}public/docfx.min.css">
20+
<link rel="stylesheet" href="{{_rel}}public/main.css">
21+
<meta name="docfx:navrel" content="{{_navRel}}">
22+
<meta name="docfx:tocrel" content="{{_tocRel}}">
23+
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
24+
<meta name="docfx:rel" content="{{_rel}}">
25+
{{#_disableNewTab}}<meta name="docfx:disablenewtab" content="true">{{/_disableNewTab}}
26+
{{#_disableTocFilter}}<meta name="docfx:disabletocfilter" content="true">{{/_disableTocFilter}}
27+
{{#docurl}}<meta name="docfx:docurl" content="{{docurl}}">{{/docurl}}
28+
<meta name="loc:inThisArticle" content="{{__global.inThisArticle}}">
29+
<meta name="loc:searchResultsCount" content="{{__global.searchResultsCount}}">
30+
<meta name="loc:searchNoResults" content="{{__global.searchNoResults}}">
31+
<meta name="loc:tocFilter" content="{{__global.tocFilter}}">
32+
<meta name="loc:nextArticle" content="{{__global.nextArticle}}">
33+
<meta name="loc:prevArticle" content="{{__global.prevArticle}}">
34+
<meta name="loc:themeLight" content="{{__global.themeLight}}">
35+
<meta name="loc:themeDark" content="{{__global.themeDark}}">
36+
<meta name="loc:themeAuto" content="{{__global.themeAuto}}">
37+
<meta name="loc:changeTheme" content="{{__global.changeTheme}}">
38+
<meta name="loc:copy" content="{{__global.copy}}">
39+
<meta name="loc:downloadPdf" content="{{__global.downloadPdf}}">
40+
41+
<script type="module" src="./{{_rel}}public/docfx.min.js"></script>
42+
43+
<script>
44+
const theme = localStorage.getItem('theme') || 'auto'
45+
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
46+
</script>
47+
48+
{{#_googleAnalyticsTagId}}
49+
<script async src="https://www.googletagmanager.com/gtag/js?id={{_googleAnalyticsTagId}}"></script>
50+
<script>
51+
window.dataLayer = window.dataLayer || [];
52+
function gtag() { dataLayer.push(arguments); }
53+
gtag('js', new Date());
54+
gtag('config', '{{_googleAnalyticsTagId}}');
55+
</script>
56+
{{/_googleAnalyticsTagId}}
57+
{{/redirect_url}}
58+
</head>
59+
60+
{{^redirect_url}}
61+
<body class="tex2jax_ignore" data-layout="{{_layout}}{{layout}}" data-yaml-mime="{{yamlmime}}">
62+
<header class="bg-body border-bottom">
63+
{{^_disableNavbar}}
64+
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
65+
<div class="container-xxl flex-nowrap">
66+
<a class="navbar-brand" href="{{_appLogoUrl}}{{^_appLogoUrl}}{{_rel}}index.html{{/_appLogoUrl}}">
67+
<img id="logo" class="svg" src="{{_rel}}{{{_appLogoPath}}}{{^_appLogoPath}}logo.svg{{/_appLogoPath}}" alt="{{_appName}}" >
68+
{{_appName}}
69+
</a>
70+
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
71+
<i class="bi bi-three-dots"></i>
72+
</button>
73+
<div class="collapse navbar-collapse" id="navpanel">
74+
<div id="navbar">
75+
{{#_enableSearch}}
76+
<form class="search" role="search" id="search">
77+
<i class="bi bi-search"></i>
78+
<input class="form-control" id="search-query" type="search" disabled placeholder="{{__global.search}}" autocomplete="off" aria-label="Search">
79+
</form>
80+
{{/_enableSearch}}
81+
</div>
82+
<div id="version-picker-container" class="ms-auto d-flex align-items-center px-2">
83+
<select id="version-picker" class="form-select form-select-sm" style="width:auto" aria-label="Select version"></select>
84+
</div>
85+
</div>
86+
</div>
87+
</nav>
88+
{{/_disableNavbar}}
89+
</header>
90+
91+
<main class="container-xxl">
92+
{{^_disableToc}}
93+
<div class="toc-offcanvas">
94+
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
95+
<div class="offcanvas-header">
96+
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
97+
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
98+
</div>
99+
<div class="offcanvas-body">
100+
<nav class="toc" id="toc"></nav>
101+
</div>
102+
</div>
103+
</div>
104+
{{/_disableToc}}
105+
106+
<div class="content">
107+
<div class="actionbar">
108+
{{^_disableToc}}
109+
<button class="btn btn-lg border-0 d-md-none"
110+
type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas"
111+
aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
112+
<i class="bi bi-list"></i>
113+
</button>
114+
{{/_disableToc}}
115+
116+
{{^_disableBreadcrumb}}
117+
<nav id="breadcrumb"></nav>
118+
{{/_disableBreadcrumb}}
119+
</div>
120+
121+
<article data-uid="{{uid}}">
122+
{{!body}}
123+
</article>
124+
125+
{{^_disableContribution}}
126+
<div class="contribution d-print-none">
127+
{{#sourceurl}}
128+
<a href="{{sourceurl}}" class="edit-link">{{__global.improveThisDoc}}</a>
129+
{{/sourceurl}}
130+
{{^sourceurl}}{{#docurl}}
131+
<a href="{{docurl}}" class="edit-link">{{__global.improveThisDoc}}</a>
132+
{{/docurl}}{{/sourceurl}}
133+
</div>
134+
{{/_disableContribution}}
135+
136+
{{^_disableNextArticle}}
137+
<div class="next-article d-print-none border-top" id="nextArticle"></div>
138+
{{/_disableNextArticle}}
139+
140+
</div>
141+
142+
{{^_disableAffix}}
143+
<div class="affix">
144+
<nav id="affix"></nav>
145+
</div>
146+
{{/_disableAffix}}
147+
</main>
148+
149+
{{#_enableSearch}}
150+
<div class="container-xxl search-results" id="search-results"></div>
151+
{{/_enableSearch}}
152+
153+
<footer class="border-top text-secondary">
154+
<div class="container-xxl">
155+
<div class="flex-fill">
156+
{{{_appFooter}}}{{^_appFooter}}<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>{{/_appFooter}}
157+
</div>
158+
</div>
159+
</footer>
160+
<script src="./{{_rel}}public/version-switcher.js"></script>
161+
</body>
162+
{{/redirect_url}}
163+
</html>

0 commit comments

Comments
 (0)