-
Notifications
You must be signed in to change notification settings - Fork 0
266 lines (241 loc) · 13 KB
/
Copy pathdocs.yml
File metadata and controls
266 lines (241 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
name: Docs
# Publishes the whole documentation site for this repository to GitHub Pages, as
# a SINGLE combined site made of two halves that live at non-overlapping paths:
#
# /ProcessKit-fSharp/ -> the mdBook GUIDES book (docs/**, theme/**, book.toml)
# /ProcessKit-fSharp/api/ -> the fsdocs API REFERENCE (generated from XML docs)
#
# WHY ONE WORKFLOW (not two): GitHub Pages serves one site per repository, and
# `actions/deploy-pages` publishes a single artifact that REPLACES the entire
# site. Two independent workflows each deploying only their own half would clobber
# the other half on every run. So every run of this one workflow builds BOTH
# halves and deploys their union — neither trigger can wipe the other's output.
#
# TRIGGERS:
# * release: published -> a new release ships; refresh the API reference (and
# rebuild the book from the tag).
# * push to docs/**, theme/**, book.toml -> a guides edit; rebuild the book.
# * workflow_dispatch -> manual rebuild (e.g. after editing this workflow).
#
# API-REFERENCE SOURCE = LATEST PUBLISHED RELEASE (not the pushed commit). The
# reference is generated from the XML doc comments of the two SHIPPING packages
# (ProcessKit, ProcessKit.Extensions.DependencyInjection) and is meant to track
# what a consumer actually installed from NuGet. Building it from the latest
# release tag — even when this run was triggered by an ordinary docs push to main
# — keeps that invariant: the published reference never shows an API surface that
# has not shipped yet. The book, by contrast, is built from the triggering ref so
# guide edits go live immediately on push. (Rebuilding the reference on a docs
# push is deliberately un-optimised: it is cheap insurance against clobbering, and
# docs pushes are infrequent.)
#
# GENERATOR CHOICE: fsdocs (FSharp.Formatting), not docfx. This repository's public
# surface is F#-authored (curried module functions, discriminated unions, `'T`
# generics) and fsdocs renders those natively and readably (e.g.
# `Command.arg value command`, `Result<int, ProcessError>`); docfx's metadata-based
# renderer is built around C# conventions and reads F#-specific shapes awkwardly. A
# C# reader still gets a correct, idiomatic call from the guides in `docs/`
# (linked from `apidocs/index.md`) for the handful of members (the `CommandVerbs`
# extension methods) where the F#-native rendering looks like a static call instead
# of C#'s dot-syntax — documented on the generated front page.
#
# This workflow does not modify ci.yml/release.yml — it is the single, independent
# publishing pipeline scoped to the doc site.
on:
release:
types: [published]
push:
branches: [main]
paths:
- 'docs/**'
- 'theme/**'
- 'book.toml'
- '.github/workflows/docs.yml'
- 'scripts/verify-doc-snippets.ps1'
workflow_dispatch:
# Only one Pages deployment may be in flight at a time; never cancel one that is
# already uploading/activating (unlike CI, a half-applied Pages deploy is not a
# safe thing to abandon mid-flight).
concurrency:
group: pages
cancel-in-progress: false
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
# Actions are pinned to a full commit SHA (supply-chain hardening); the
# trailing comment records the human-readable version. Dependabot bumps the
# SHA and updates the comment on its weekly run.
#
# First checkout = the triggering ref (main HEAD on a push, the tag on a
# release). This is what the mdBook GUIDES are built from, so a docs edit
# goes live as soon as it lands.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# ── Guides book (mdBook) → site root ────────────────────────────────────
# Pinned to the same mdBook version used by the sibling ProcessKit-rs docs
# workflow so both repositories render their books identically.
- name: Install mdBook
run: |
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.40/mdbook-v0.4.40-x86_64-unknown-linux-gnu.tar.gz \
| tar -xz -C /usr/local/bin
- name: Build guides (mdBook)
run: mdbook build
# Regression check for theme/custom.css's pinned-title sidebar selector: it
# parses the just-built, CI-pinned-mdBook DOM and asserts the selector
# matches exactly the "Overview" entry (see the K-005 pitfall and
# scripts/check-sidebar-nav.py's own docstring for the underlying mdBook
# v0.4.40 DOM quirk). python3 is preinstalled on this runner image; no
# extra setup step needed.
- name: Check sidebar pinned-title selector
run: python3 scripts/check-sidebar-nav.py book/index.html
# docs/internals/** and docs/planning/** are internal, non-public project
# notes, and docs/snippets/** is the compile harness for the guides' code
# samples (see the `snippets` job below), not prose. They are absent from
# docs/SUMMARY.md so they never appear in the book's navigation, but mdBook
# copies any non-chapter source file to the output verbatim — so drop them
# from the built site to guarantee they are not reachable at a guessed URL
# either.
- name: Prune internal docs from the built book
run: rm -rf book/internals book/planning book/snippets
# ── API reference (fsdocs) → /api/ subpath, built from the latest release ─
# Resolve which release the reference should reflect: the just-published tag
# on a release event, otherwise the latest published release (so an ordinary
# docs push never surfaces an unreleased API surface).
- name: Resolve the API-reference source (latest published release)
id: apiref
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ github.event_name }}" = "release" ]; then
tag="${{ github.event.release.tag_name }}"
else
tag="$(gh release view --repo "${{ github.repository }}" --json tagName --jq .tagName)"
fi
echo "Building the API reference from release: $tag"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
# Second checkout, into ./_released, pinned to that release tag. The fsdocs
# build below runs entirely inside this tree so the reference reflects the
# released source, independent of what is on main.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ steps.apiref.outputs.tag }}
path: _released
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
# Only the SDK band matters here (pinned by global.json) — this workflow
# builds Release assemblies for fsdocs to read, it does not run tests, so
# the net8.0 runtime is not needed the way it is in ci.yml's test job.
dotnet-version: '10.0.x'
- name: Cache NuGet packages
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('_released/Directory.Packages.props', '_released/nuget.config', '_released/global.json') }}
restore-keys: ${{ runner.os }}-nuget-
# Restores fsdocs-tool alongside Fantomas (.config/dotnet-tools.json).
- name: Restore local tools
working-directory: _released
run: dotnet tool restore
- name: Restore
working-directory: _released
run: dotnet restore
# fsdocs reads each project's built DLL + XML doc file. ProcessKit.Extensions.DependencyInjection
# resolves ProcessKit via Reference + AssemblySearchPaths (not ProjectReference,
# per this repo's convention), so ProcessKit must already be built at the same
# Configuration for that path to exist — building the whole solution up front
# (build order comes from ProcessKit.slnx's BuildDependency entries) guarantees it.
- name: Build
working-directory: _released
run: dotnet build ProcessKit.slnx --no-restore --configuration Release
# Resolves the real Pages base URL (e.g. https://zelanton.github.io/ProcessKit-fSharp)
# so generated absolute links/assets are correct without hardcoding it here —
# also works unchanged if a custom domain is ever configured for Pages.
- name: Configure Pages
id: pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
# --projects scopes the reference to the two shipping packages named in the
# task (ProcessKit, ProcessKit.Extensions.DependencyInjection) — ProcessKit.Testing
# (test doubles, not part of the runtime surface a consumer builds against) and
# the benchmarks project are intentionally excluded.
#
# `root` is the API reference's own base URL — the /api/ SUBPATH of the Pages
# site, since the mdBook guides occupy the root. fsdocs uses it to emit correct
# absolute links/asset URLs for the reference.
#
# --sourcerepo / fsdocs-license-link / fsdocs-release-notes-link override
# fsdocs' own defaults, which point at a `master` branch and a RELEASE_NOTES.md
# file this repo does not have (it uses `main` and CHANGELOG.md instead).
- name: Build API reference (fsdocs)
working-directory: _released
run: >-
dotnet fsdocs build
--input apidocs
--output apidocs/output
--projects src/ProcessKit/ProcessKit.fsproj src/ProcessKit.Extensions.DependencyInjection/ProcessKit.Extensions.DependencyInjection.fsproj
--properties Configuration=Release
--sourcerepo https://github.com/${{ github.repository }}/tree/main
--parameters
root "${{ steps.pages.outputs.base_url }}/api/"
fsdocs-collection-name "ProcessKit API Reference"
fsdocs-license-link "https://github.com/${{ github.repository }}/blob/main/LICENSE"
fsdocs-release-notes-link "https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md"
--clean
# ── Assemble the combined site: book at the root, API reference under /api/ ─
- name: Assemble the combined Pages site
run: |
mkdir -p _site
cp -r book/. _site/
mkdir -p _site/api
cp -r _released/apidocs/output/. _site/api/
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: _site
# ── Do the guides' code samples still compile against the library? ──────────
# The API snapshot test and ApiCompat protect the binary surface; nothing
# protected the prose, so every ```fsharp / ```csharp block in docs/*.md used to
# be free to rot as the API moved. This job extracts them all and compiles them
# against the library built from THIS ref (docs/snippets, see its README for the
# wrapping rules and the `docsnippet:ignore` marker).
#
# It runs beside `build` rather than gating `deploy`: a stale sample is a red
# check to fix, not a reason to block publishing an otherwise-correct docs edit —
# the same way ci.yml's format/analyze/typos jobs are independent signals.
#
# Coverage note: this workflow triggers on docs/theme/book edits and on every
# release, not on a src/** push. A pure API change therefore surfaces here at the
# next docs edit or at the next release, whichever comes first; run
# `pwsh scripts/verify-doc-snippets.ps1` locally when changing public API.
snippets:
name: Verify documentation snippets compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
# The harness pins net10.0; the libraries it references build for the
# whole TargetFrameworks set, which this SDK band covers.
dotnet-version: '10.0.x'
- name: Cache NuGet packages
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('Directory.Packages.props', 'nuget.config', 'global.json') }}
restore-keys: ${{ runner.os }}-nuget-
# pwsh is preinstalled on this runner image. The script builds
# docs/snippets/DocSnippets.slnx (a mini-solution outside ProcessKit.slnx,
# whose BuildDependency entries build the referenced libraries first) and
# reports every compiler error at the markdown line it came from.
- name: Compile every F#/C# sample in docs/*.md
run: pwsh scripts/verify-doc-snippets.ps1
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0