fix: change publish workflow to pull_request
This also adds a check to fail if CLI content is updated in this repo instead of `npm/cli`
Luke Karrys committed
Jan 1, 2023 at 21:20 UTC
2c54fe219fba05371b4889464813fb066510b032
11 files changed
+157
-60
.github/workflows/ci.yml
+53
@@ -103,6 +103,59 @@ jobs:
103
- name: Test
104
run: npm test --ignore-scripts
105
106
+ test-cli-content:
107
+ name: Test CLI Content - ${{ matrix.platform.name }} - ${{ matrix.node-version }}
108
+ if: github.repository_owner == 'npm'
109
+ strategy:
110
+ fail-fast: false
111
+ matrix:
112
+ platform:
113
+ - name: Linux
114
+ os: ubuntu-latest
115
+ shell: bash
116
+ node-version:
117
+ - 18.x
118
+ runs-on: ${{ matrix.platform.os }}
119
+ defaults:
120
+ run:
121
+ shell: ${{ matrix.platform.shell }}
122
+ steps:
123
+ - name: Checkout
124
+ uses: actions/checkout@v3
125
+ - name: Setup Git User
126
+ run: |
127
+ git config --global user.email "npm-cli+bot@github.com"
128
+ git config --global user.name "npm CLI robot"
129
+ - name: Setup Node
130
+ uses: actions/setup-node@v3
131
+ with:
132
+ node-version: ${{ matrix.node-version }}
133
+ cache: npm
134
+ - name: Update Windows npm
135
+ # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows
136
+ if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.'))
137
+ run: |
138
+ curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz
139
+ tar xf npm-7.5.4.tgz
140
+ cd package
141
+ node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz
142
+ cd ..
143
+ rmdir /s /q package
144
+ - name: Install npm@7
145
+ if: startsWith(matrix.node-version, '10.')
146
+ run: npm i --prefer-online --no-fund --no-audit -g npm@7
147
+ - name: Install npm@latest
148
+ if: ${{ !startsWith(matrix.node-version, '10.') }}
149
+ run: npm i --prefer-online --no-fund --no-audit -g npm@latest
150
+ - name: npm Version
151
+ run: npm -v
152
+ - name: Install Dependencies
153
+ run: npm i --no-audit --no-fund
154
+ - name: Check CLI Documentation
155
+ run: npm run build -w cli -- --check-only
156
+ env:
157
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
158
+
159
licenses:
160
name: REUSE Compliance Check
161
runs-on: ubuntu-latest
.github/workflows/publish.yml
+8
-13
@@ -6,7 +6,7 @@ on:
6
push:
7
branches:
8
- main
9
- pull_request_target:
9
+ pull_request:
10
workflow_dispatch:
11
workflow_call:
12
@@ -23,19 +23,12 @@ jobs:
23
run:
24
shell: bash
25
steps:
26
- - name: Checkout PR
27
- if: ${{ github.event_name == 'pull_request_target' }}
28
- uses: actions/checkout@v3
29
- with:
30
- ref: ${{ github.event.pull_request.head.ref }}
31
- repository: ${{ github.event.pull_request.head.repo.full_name }}
26
- name: Checkout
33
- if: ${{ github.event_name != 'pull_request_target' }}
27
uses: actions/checkout@v3
35
- with:
36
- ref: main
37
- - name: Setup Pages
38
- uses: actions/configure-pages@v1
28
+ - name: Setup Git User
29
+ run: |
30
+ git config --global user.email "npm-cli+bot@github.com"
31
+ git config --global user.name "npm CLI robot"
32
- name: Setup Node
33
uses: actions/setup-node@v3
34
with:
@@ -47,6 +40,8 @@ jobs:
40
run: npm -v
41
- name: Install Dependencies
42
run: npm i --no-audit --no-fund
43
+ - name: Setup Pages
44
+ uses: actions/configure-pages@v1
45
- name: Build documentation
46
run: npm run build
47
env:
@@ -70,4 +65,4 @@ jobs:
65
id: deployment
66
uses: actions/deploy-pages@v1
67
with:
73
- preview: ${{ github.event_name == 'pull_request_target' }}
68
+ preview: ${{ github.event_name == 'pull_request' }}
.github/workflows/update-cli.yml
+2
@@ -38,6 +38,8 @@ jobs:
38
- name: Build documentation
39
run: npm run build -w cli
40
env:
41
+ # token is used to get files from `npm/cli` that
42
+ # are not present in the published tarball
43
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44
- name: Check for changes
45
id: status
cli/bin/build.js
+34
-12
@@ -1,21 +1,43 @@
1
-const { resolve } = require('path')
1
+const { resolve, relative, join } = require('path')
2
+const { spawnSync } = require('child_process')
3
const build = require('../lib/build.js')
4
+const { nwo } = require('../lib/gh')
5
+
6
+// check only build with the current versions instead of checking the registry
7
+// and also fails if any changes are detected. this is used in CI to make sure
8
+// edits to the CLI content are made in the CLI repo
9
+const checkOnly = process.argv.includes('--check-only')
10
+
11
+const ROOT = resolve(__dirname, '../..')
12
+const contentPath = join(ROOT, 'content/cli')
13
+const navPath = join(ROOT, 'src/theme/nav.yml')
14
+
15
+const checkContent = () => {
16
+ const status = spawnSync('git', ['status', '--porcelain', contentPath], { encoding: 'utf-8' })
17
+ if (status.stdout) {
18
+ const msg = [
19
+ `The following untracked changes to ${relative(process.cwd(), contentPath)} were found:`,
20
+ status.stdout,
21
+ `These files are generated and changes might need to be made in the ${nwo} repository.`,
22
+ ]
23
+ throw new Error(msg.join('\n'))
24
+ }
25
+}
26
27
build({
28
+ releases: require('../releases.json'),
29
loglevel: process.argv.includes('--debug') || process.env.CI ? 'verbose' : 'info',
30
prerelease: false,
7
- contentPath: resolve(__dirname, '..', '..', 'content', 'cli'),
8
- releasesPath: resolve(__dirname, '..', 'releases.json'),
9
- navPath: resolve(
10
- __dirname,
11
- '..',
12
- '..',
13
- 'src',
14
- 'theme',
15
- 'nav.yml'
16
- ),
31
+ useCurrent: checkOnly,
32
+ contentPath,
33
+ navPath,
34
})
18
- .then(() => console.log('DONE'))
35
+ .then(() => {
36
+ if (checkOnly) {
37
+ checkContent()
38
+ }
39
+ return console.log('DONE')
40
+ })
41
.catch((e) => {
42
console.error(e)
43
process.exit(1)
cli/lib/build.js
+30
-10
@@ -38,9 +38,25 @@ const updateNav = async (updates, { nav, path }) => {
38
return fs.writeFile(path, nav.toString(), 'utf-8')
39
}
40
41
+const getCurrentVersions = (nav) => {
42
+ // the only place the current versions are stored is in the nav
43
+ const currentSections = nav.find(s => s.url === `/${DOCS_PATH}`).variants
44
+
45
+ const currentVersions = currentSections.map((v) => {
46
+ const version = v.title?.match(/^Version\s(.*?)\s/)[1]
47
+ return version
48
+ }).sort(semver.compare)
49
+
50
+ return {
51
+ versions: currentVersions,
52
+ latest: currentVersions[currentVersions.length - 1],
53
+ }
54
+}
55
+
56
const main = async ({
57
loglevel,
43
- releasesPath,
58
+ releases: rawReleases,
59
+ useCurrent,
60
navPath,
61
contentPath,
62
prerelease,
@@ -50,12 +66,18 @@ const main = async ({
66
log.on(loglevel)
67
}
68
53
- const pack = await pacote.packument('npm', { preferOnline: true }).then(p => ({
54
- versions: Object.keys(p.versions),
55
- latest: p['dist-tags'].latest,
56
- }))
69
+ const baseNav = await fs.readFile(navPath, 'utf-8')
70
+ const navData = yaml.parse(baseNav)
71
+ const navDoc = yaml.parseDocument(baseNav)
72
+
73
+ const pack = useCurrent
74
+ ? getCurrentVersions(navData)
75
+ : await pacote.packument('npm', { preferOnline: true }).then(p => ({
76
+ versions: Object.keys(p.versions),
77
+ latest: p['dist-tags'].latest,
78
+ }))
79
58
- const releaseVersions = require(releasesPath).map(release => {
80
+ const releaseVersions = rawReleases.map(release => {
81
const major = Number(release.id.replace(/^v/, ''))
82
const range = `>=${major}.0.0-a <${major + 1}.0.0` // include all prereleases
83
const version = semver.parse(semver.maxSatisfying(pack.versions, range))
@@ -86,15 +108,13 @@ const main = async ({
108
}
109
})
110
89
- const baseNav = await fs.readFile(navPath, 'utf-8')
90
-
111
const updates = await Promise.all(
112
releases.map((r) =>
93
- extractRelease(r, { contentPath, baseNav: yaml.parse(baseNav), prerelease })
113
+ extractRelease(r, { contentPath, baseNav: navData, prerelease })
114
)
115
).then((r) => r.filter(Boolean))
116
97
- await updateNav(updates, { nav: yaml.parseDocument(baseNav), path: navPath })
117
+ await updateNav(updates, { nav: navDoc, path: navPath })
118
}
119
120
module.exports = main
cli/lib/extract.js
+4
-7
@@ -96,9 +96,6 @@ const unpackRelease = async (
96
log.info(release.id, release)
97
98
const cwd = join(contentPath, release.id)
99
- await fs
100
- .rm(cwd, { force: true, recursive: true })
101
- .then(() => fs.mkdir(cwd, { recursive: true }))
99
100
const builtPath = join('docs', 'content')
101
const srcPath = join('docs', 'lib', 'content')
@@ -119,10 +116,10 @@ const unpackRelease = async (
116
?? await gh.pathExists(release.branch, join('docs', 'nav.yml')),
117
})
118
122
- // If we are using the release's GitHub ref, then we fetch
123
- // the tree of the doc directory's sha which has all the docs
124
- // we need in it. Note that this requires the docs to all be
125
- // built in source, which is true for v6 but not for v9 and later.
119
+ await fs
120
+ .rm(cwd, { force: true, recursive: true })
121
+ .then(() => fs.mkdir(cwd, { recursive: true }))
122
+
123
const files = await unpackTarball({
124
release,
125
cwd,
cli/lib/gh.js
+4
@@ -1,6 +1,10 @@
1
const { Octokit } = require('@octokit/rest')
2
const { posix, sep } = require('path')
3
4
+if (!process.env.GITHUB_TOKEN) {
5
+ throw new Error('GITHUB_TOKEN env var is required to build CLI docs')
6
+}
7
+
8
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
9
const owner = 'npm'
10
const repo = 'cli'
cli/scripts/template-oss/update-cli.yml
+2
@@ -13,6 +13,8 @@ jobs:
13
- name: Build documentation
14
run: npm run build -w cli
15
env:
16
+ # token is used to get files from `npm/cli` that
17
+ # are not present in the published tarball
18
GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
19
- name: Check for changes
20
id: status
cli/test/index.js
+10
-2
@@ -38,7 +38,6 @@ const mockBuild = async ({ releases, packument = {}, testdir: testdirOpts }) =>
38
const nav = yaml.parse(rawNav)
39
40
const testdir = t.testdir({
41
- 'releases.json': JSON.stringify(releases),
41
'nav.yml': rawNav,
42
content: {},
43
...testdirOpts,
@@ -102,8 +101,8 @@ const mockBuild = async ({ releases, packument = {}, testdir: testdirOpts }) =>
101
return {
102
testdir,
103
build: (opts) => build({
104
+ releases,
105
contentPath: join(testdir, 'content'),
106
- releasesPath: join(testdir, 'releases.json'),
106
navPath: join(testdir, 'nav.yml'),
107
...opts,
108
}),
@@ -158,6 +157,15 @@ t.test('earlier release is latest', async (t) => {
157
await build()
158
})
159
160
+t.test('can skip fetching latest', async (t) => {
161
+ const releases = getReleases()
162
+ const { build } = await mockBuild({
163
+ releases,
164
+ })
165
+
166
+ await build({ useCurrent: true })
167
+})
168
+
169
t.test('add variant to nav', async (t) => {
170
const releases = getReleases()
171
const { build } = await mockBuild({
scripts/template-oss/ci.yml
+7
@@ -1,5 +1,12 @@
1
{{> ci }}
2
3
+ test-cli-content:
4
+ {{> jobMatrix jobName="Test CLI Content" }}
5
+ - name: Check CLI Documentation
6
+ run: npm run build -w cli -- --check-only
7
+ env:
8
+ GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
9
+
10
licenses:
11
name: REUSE Compliance Check
12
runs-on: ubuntu-latest
scripts/template-oss/publish.yml
+3
-16
@@ -4,7 +4,7 @@ on:
4
push:
5
branches:
6
- {{ defaultBranch }}
7
- pull_request_target:
7
+ pull_request:
8
workflow_dispatch:
9
workflow_call:
10
@@ -13,22 +13,9 @@ jobs:
13
permissions:
14
contents: read
15
pages: read
16
- {{> job jobName="Build and Upload" jobSkipSetup=true }}
17
- - name: Checkout PR
18
- if: $\{{ github.event_name == 'pull_request_target' }}
19
- uses: actions/checkout@v3
20
- with:
21
- ref: $\{{ github.event.pull_request.head.ref }}
22
- repository: $\{{ github.event.pull_request.head.repo.full_name }}
23
- - name: Checkout
24
- if: $\{{ github.event_name != 'pull_request_target' }}
25
- uses: actions/checkout@v3
26
- with:
27
- ref: {{ defaultBranch }}
16
+ {{> job jobName="Build and Upload" }}
17
- name: Setup Pages
18
uses: actions/configure-pages@v1
30
- {{> stepNode }}
31
- {{> stepDeps }}
19
- name: Build documentation
20
run: npm run build
21
env:
@@ -52,4 +39,4 @@ jobs:
39
id: deployment
40
uses: actions/deploy-pages@v1
41
with:
55
- preview: $\{{ github.event_name == 'pull_request_target' }}
42
+ preview: $\{{ github.event_name == 'pull_request' }}