fix: label latest dist-tag release as latest and anything greater current (#264)
Luke Karrys committed
Oct 19, 2022 at 13:26 UTC
40929f2f1ef94090b581db9012bdec0ec8407729
5 files changed
+126
-45
cli/lib/build.js
+43
-10
@@ -1,6 +1,9 @@
1
const { posix, join, sep } = require('path')
2
const fs = require('fs').promises
3
const yaml = require('yaml')
4
+const semver = require('semver')
5
+const pacote = require('pacote')
6
+const gh = require('./gh')
7
const extractRelease = require('./extract')
8
const log = require('./log')
9
@@ -64,18 +67,48 @@ const main = async ({
67
// can be used to write files later
68
const defaultBuiltDir = join('docs', 'content')
69
67
- const releases = require(releasesPath).map((release) => ({
68
- ...release,
69
- // dir of the built docs that should be copied
70
- built: defaultBuiltDir,
71
- // dir of the source for the docs that should
72
- // be linked to for editing on github
73
- src: release.src?.split(posix.sep).join(sep) || defaultBuiltDir,
74
- url: `/${DOCS_PATH}/${release.id}`,
75
- urlPrefix: DOCS_PATH,
76
- urlPrefixes: [DOCS_PATH, `${DOCS_PATH}-documentation`],
70
+ const rawReleases = require(releasesPath)
71
+
72
+ const releaseManifests = await Promise.all(rawReleases.map(async release => {
73
+ const manifest = await pacote.manifest(`${gh.owner}@${release.spec}`, {
74
+ preferOnline: true,
75
+ })
76
+ // the default release is always controlled by the latest dist-tag
77
+ release.default = release.spec === 'latest'
78
+ release.manifest = manifest
79
+ release.version = manifest.version
80
+ const sVersion = semver.parse(release.version)
81
+ release.semver = sVersion
82
+ release.prerelease = sVersion.prerelease.length > 0
83
+ return release
84
}))
85
86
+ const latestRelease = releaseManifests.find(r => r.spec === 'latest')
87
+
88
+ if (!latestRelease) {
89
+ throw new Error(`One of the CLI releases must have \`spec: 'latest'\``)
90
+ }
91
+
92
+ const releases = releaseManifests.map((release) => {
93
+ const type = release.default ? 'Latest Release'
94
+ : release.prerelease ? 'Prerelease'
95
+ : semver.gt(release.version, latestRelease.version) ? 'Current Release'
96
+ : 'Legacy Release'
97
+
98
+ return {
99
+ ...release,
100
+ title: `Version ${release.version} (${type})`,
101
+ // dir of the built docs that should be copied
102
+ built: defaultBuiltDir,
103
+ // dir of the source for the docs that should
104
+ // be linked to for editing on github
105
+ src: release.src?.split(posix.sep).join(sep) || defaultBuiltDir,
106
+ url: `/${DOCS_PATH}/${release.id}`,
107
+ urlPrefix: DOCS_PATH,
108
+ urlPrefixes: [DOCS_PATH, `${DOCS_PATH}-documentation`],
109
+ }
110
+ })
111
+
112
const baseNav = await fs.readFile(navPath, 'utf-8')
113
114
const updates = await Promise.all(
cli/lib/extract.js
+3
-18
@@ -2,7 +2,6 @@ const pacote = require('pacote')
2
const tar = require('tar')
3
const { join, sep, dirname, posix } = require('path')
4
const fs = require('fs/promises')
5
-const semver = require('semver')
5
const yaml = require('yaml')
6
const Transform = require('./transform')
7
const gh = require('./gh')
@@ -99,25 +98,11 @@ const resolveRelease = async (
98
{ resolved: current, ...release },
99
{ force, prerelease }
100
) => {
102
- const manifest = await pacote.manifest(`${gh.owner}@${release.spec}`, {
103
- preferOnline: true,
104
- })
105
-
106
- release.version = manifest.version
107
- const isPre = semver.parse(release.version).prerelease.length > 0
108
-
109
- if (isPre && !prerelease) {
101
+ if (release.prerelease && !prerelease) {
102
log.info(`Skipping ${release.id} due to prerelease ${release.version}`)
103
return null
104
}
105
114
- const versionType = release.default
115
- ? 'Current Release'
116
- : isPre
117
- ? 'Prerelease'
118
- : 'Legacy Release'
119
- release.title = `Version ${release.version} (${versionType})`
120
-
106
// The legacy v6 release has updated docs in GitHub that were never
107
// published. So in this case we skip cloning the repo with pacote
108
// and get the latest commit on the branch. Later we will use the
@@ -126,8 +111,8 @@ const resolveRelease = async (
111
if (release.useBranch) {
112
release.resolved = await gh.getLatestSha(release.branch)
113
} else {
129
- release.resolved = manifest._resolved
130
- release.spec = manifest._from
114
+ release.resolved = release.manifest._resolved
115
+ release.spec = release.manifest._from
116
}
117
118
log.info(release.id, release.version, release.resolved)
cli/releases.json
+1
-2
@@ -16,7 +16,6 @@
16
"id": "v8",
17
"branch": "v8",
18
"spec": "latest",
19
- "default": true,
19
"resolved": "https://registry.npmjs.org/npm/-/npm-8.19.2.tgz"
20
},
21
{
@@ -24,6 +23,6 @@
23
"branch": "latest",
24
"spec": "next-9",
25
"src": "docs/lib/content",
27
- "resolved": "https://registry.npmjs.org/npm/-/npm-9.0.0-pre.4.tgz"
26
+ "resolved": "https://registry.npmjs.org/npm/-/npm-9.0.0-pre.5.tgz"
27
}
28
]
cli/test/index.js
+76
-14
@@ -1,9 +1,8 @@
1
const t = require('tap')
2
const { resolve, join } = require('path')
3
const fs = require('fs/promises')
4
+const pacote = require('pacote')
5
5
-const build = require('../lib/build')
6
-const releases = require('../releases.json')
6
const navPath = resolve(
7
__dirname,
8
'..',
@@ -13,32 +12,95 @@ const navPath = resolve(
12
'nav.yml'
13
)
14
16
-t.test('builds successfully', async (t) => {
15
+const getReleases = () => {
16
+ return JSON.parse(JSON.stringify(require('../releases.json')))
17
+}
18
+
19
+const mockBuild = async ({ releases, testdir: testdirOpts }) => {
20
const testdir = t.testdir({
21
'releases.json': JSON.stringify(releases),
22
'nav.yml': await fs.readFile(navPath, 'utf-8'),
23
content: {},
24
+ ...testdirOpts,
25
})
26
23
- await build({
24
- force: true,
25
- prerelease: true,
27
+ const build = t.mock('../lib/build', {
28
+ pacote: {
29
+ ...pacote,
30
+ manifest: async (spec) => {
31
+ let version = spec.split('@')[1]
32
+ const release = releases.find(r => r.spec === version)
33
+
34
+ if (version.match(/^\^\d+$/)) {
35
+ version = version.slice(1) + '.0.0'
36
+ } else if (version === 'latest') {
37
+ version = release.id.slice(1) + '.0.0'
38
+ } else if (version.startsWith('next-')) {
39
+ version = version.replace('next-', '') + '.0.0-pre.4'
40
+ }
41
+
42
+ return {
43
+ version,
44
+ _resolved: release.resolved,
45
+ _from: spec,
46
+ }
47
+ },
48
+ },
49
+ })
50
+
51
+ return (opts) => build({
52
contentPath: join(testdir, 'content'),
53
releasesPath: join(testdir, 'releases.json'),
54
navPath: join(testdir, 'nav.yml'),
55
+ ...opts,
56
+ })
57
+}
58
+
59
+t.test('builds successfully', async (t) => {
60
+ const releases = getReleases()
61
+ const build = await mockBuild({ releases })
62
+
63
+ await build({
64
+ force: true,
65
+ prerelease: true,
66
})
67
})
68
69
t.test('no force', async (t) => {
33
- const testdir = t.testdir({
34
- 'releases.json': JSON.stringify(releases),
35
- 'nav.yml': '- title: cli\n url: /cli',
36
- content: {},
70
+ const releases = getReleases()
71
+ const build = await mockBuild({
72
+ releases,
73
+ testdir: {
74
+ 'nav.yml': '- title: cli\n url: /cli',
75
+ },
76
})
77
39
- await build({
40
- contentPath: join(testdir, 'content'),
41
- releasesPath: join(testdir, 'releases.json'),
42
- navPath: join(testdir, 'nav.yml'),
78
+ await build()
79
+})
80
+
81
+t.test('no default release', async (t) => {
82
+ const releases = getReleases().filter(r => r.spec !== 'latest')
83
+ const build = await mockBuild({
84
+ releases,
85
+ testdir: {
86
+ 'nav.yml': '- title: cli\n url: /cli',
87
+ },
88
})
89
+
90
+ await t.rejects(() => build())
91
+})
92
+
93
+t.test('earlier release is latest', async (t) => {
94
+ const releases = getReleases()
95
+ releases[1].spec = 'latest'
96
+ releases[2].spec = '^8'
97
+
98
+ const build = await mockBuild({
99
+ releases,
100
+ testdir: {
101
+ 'nav.yml': '- title: cli\n url: /cli',
102
+ },
103
+ })
104
+
105
+ await build()
106
})
src/theme/nav.yml
+3
-1
@@ -288,6 +288,7 @@
288
- title: Version 6.14.17 (Legacy Release)
289
shortName: v6
290
url: /cli/v6
291
+ default: false
292
children:
293
- title: CLI Commands
294
shortName: Commands
@@ -526,6 +527,7 @@
527
- title: Version 7.24.2 (Legacy Release)
528
shortName: v7
529
url: /cli/v7
530
+ default: false
531
children:
532
- title: CLI Commands
533
shortName: Commands
@@ -776,7 +778,7 @@
778
- title: Removal
779
url: /cli/v7/using-npm/removal
780
description: Cleaning the slate
779
- - title: Version 8.19.2 (Current Release)
781
+ - title: Version 8.19.2 (Latest Release)
782
shortName: v8
783
url: /cli/v8
784
default: true