Skip to content

Commit 936b90c

Browse files
authored
feat(website): serve the released docs from a versioned copy (#2979)
1 parent b7836eb commit 936b90c

87 files changed

Lines changed: 12318 additions & 247 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,45 @@ jobs:
9595
run: python -m pip install 'check-jsonschema==0.27.3'
9696

9797
- name: 📋 Validate JSON Schema
98-
run: check-jsonschema --check-metaschema website/src/public/schema.json
98+
run: check-jsonschema --check-metaschema website/src/public/next-schema.json website/src/public/schema.json
99+
100+
check-latest-content:
101+
name: 📚 Check latest content
102+
# Pull requests only: the release commit is pushed straight to main and is
103+
# the one thing allowed to rewrite these files.
104+
if: github.event_name == 'pull_request'
105+
runs-on: ubuntu-latest
106+
permissions:
107+
pull-requests: read
108+
steps:
109+
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
110+
with:
111+
script: |
112+
// Everything cmd/release overwrites. Adding a file is fine -- that
113+
// is how a blog post gets published early -- editing one is not.
114+
const generated = (name) =>
115+
name.startsWith('website/src/latest/') ||
116+
name === 'website/src/public/schema.json' ||
117+
name === 'website/src/public/schema-taskrc.json' ||
118+
name === 'website/.vitepress/sidebar/latest.ts'
119+
120+
const files = await github.paginate(
121+
github.rest.pulls.listFiles, {
122+
pull_number: context.issue.number,
123+
owner: context.repo.owner,
124+
repo: context.repo.repo,
125+
per_page: 100,
126+
}
127+
)
128+
const edited = files.filter(
129+
(f) => generated(f.filename) && f.status !== 'added'
130+
)
131+
if (edited.length > 0) {
132+
core.setFailed(
133+
'These files are generated by cmd/release and would be overwritten at the next release. Update their website/src counterpart instead:\n' +
134+
edited.map((f) => f.filename).join('\n')
135+
)
136+
}
99137
100138
govulncheck:
101139
name: 🛡️ Vulnerabilities

Taskfile.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,7 @@ tasks:
197197
This task will do the following:
198198
199199
- Update the version and date in the CHANGELOG.md file
200-
- Update the version in the package.json and package-lock.json files
201-
- Copy the latest docs to the "current" version on the website
200+
- Promote the docs, sidebar and JSON schemas to the released version
202201
- Commit the changes
203202
- Create a new tag
204203
- Push the commit/tag to the repository

cmd/release/main.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,29 @@ import (
1515

1616
const (
1717
changelogSource = "CHANGELOG.md"
18-
changelogTarget = "website/src/docs/changelog.md"
18+
changelogTarget = "website/src/next/docs/changelog.md"
1919
versionFile = "internal/version/version.txt"
2020
)
2121

22+
type promotion struct{ source, target string }
23+
24+
// Promoted at release time: the website builds `next` from the sources on the
25+
// left and `latest` from the targets on the right, so that taskfile.dev only
26+
// ever documents the version being released. The other half of the mechanism
27+
// lives in website/.vitepress/config.ts, which picks a side at build time.
28+
var (
29+
promotedDirs = []promotion{
30+
{"website/src/next/docs", "website/src/latest/docs"},
31+
{"website/src/next/blog", "website/src/latest/blog"},
32+
}
33+
34+
promotedFiles = []promotion{
35+
{"website/.vitepress/sidebar/next.ts", "website/.vitepress/sidebar/latest.ts"},
36+
{"website/src/public/next-schema.json", "website/src/public/schema.json"},
37+
{"website/src/public/next-schema-taskrc.json", "website/src/public/schema-taskrc.json"},
38+
}
39+
)
40+
2241
var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
2342

2443
// Flags
@@ -61,13 +80,42 @@ func release() error {
6180
return err
6281
}
6382

83+
// After the changelog so that the promoted docs carry it.
84+
if err := promote(); err != nil {
85+
return err
86+
}
87+
6488
if err := setVersionFile(versionFile, version); err != nil {
6589
return err
6690
}
6791

6892
return nil
6993
}
7094

95+
func promote() error {
96+
for _, p := range promotedDirs {
97+
// CopyFS refuses to overwrite, so the previous release has to go first.
98+
if err := os.RemoveAll(p.target); err != nil {
99+
return err
100+
}
101+
if err := os.CopyFS(p.target, os.DirFS(p.source)); err != nil {
102+
return err
103+
}
104+
}
105+
106+
for _, p := range promotedFiles {
107+
b, err := os.ReadFile(p.source)
108+
if err != nil {
109+
return err
110+
}
111+
if err := os.WriteFile(p.target, b, 0o644); err != nil { //nolint:gosec
112+
return err
113+
}
114+
}
115+
116+
return nil
117+
}
118+
71119
func getVersion(filename string) (*semver.Version, error) {
72120
b, err := os.ReadFile(filename)
73121
if err != nil {

website/.vitepress/blog.data.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,20 @@ function extractExcerpt(html: string): string | undefined {
2929
.trim();
3030
}
3131

32-
export default createContentLoader('blog/*.md', {
32+
// Same channel as .vitepress/config.ts: the posts of the other one are not part
33+
// of this build.
34+
const channel = process.env.DOCS_CHANNEL === 'latest' ? 'latest' : 'next';
35+
36+
export default createContentLoader(`${channel}/blog/*.md`, {
3337
render: true,
3438
transform(raw) {
3539
return raw
40+
.map((page) => ({
41+
...page,
42+
// Content loaders resolve URLs against `srcDir` and know nothing about
43+
// `rewrites`, so the channel has to be stripped by hand.
44+
url: page.url.replace(`/${channel}/`, '/')
45+
}))
3646
.filter(({ url }) => url !== '/blog/')
3747
.map(({ frontmatter, html, url }) => {
3848
const date = new Date(frontmatter.date);

0 commit comments

Comments
 (0)