@npmcli/template-oss 4.4.5 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Octokit } = require('@octokit/rest')
|
|
4
|
+
const semver = require('semver')
|
|
5
|
+
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
6
|
+
const { join } = require('path')
|
|
7
|
+
|
|
8
|
+
const log = (...logs) => console.error('LOG', ...logs)
|
|
9
|
+
|
|
10
|
+
const ROOT = process.cwd()
|
|
11
|
+
const pkg = require(join(ROOT, 'package.json'))
|
|
12
|
+
|
|
13
|
+
/* eslint-disable max-len */
|
|
14
|
+
const DEFAULT_RELEASE_PROCESS = `
|
|
15
|
+
1. Checkout the release branch and test
|
|
16
|
+
|
|
17
|
+
\`\`\`sh
|
|
18
|
+
gh pr checkout <PR-NUMBER> --force
|
|
19
|
+
npm i
|
|
20
|
+
npm test
|
|
21
|
+
gh pr checks --watch
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
1. Publish workspaces
|
|
25
|
+
|
|
26
|
+
\`\`\`sh
|
|
27
|
+
npm publish -w <WS-PKG-N>
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
1. Publish
|
|
31
|
+
|
|
32
|
+
\`\`\`sh
|
|
33
|
+
npm publish <PUBLISH-FLAGS>
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
1. Merge release PR
|
|
37
|
+
|
|
38
|
+
\`\`\`sh
|
|
39
|
+
gh pr merge --rebase
|
|
40
|
+
git checkout <BASE-BRANCH>
|
|
41
|
+
git fetch
|
|
42
|
+
git reset --hard origin/<BASE-BRANCH>
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
1. Check For Release Tags
|
|
46
|
+
|
|
47
|
+
Release Please will run on the just pushed release commit and create GitHub releases and tags for each package.
|
|
48
|
+
|
|
49
|
+
\`\`\`
|
|
50
|
+
gh run watch \`gh run list -w release -b <BASE-BRANCH> -L 1 --json databaseId -q ".[0].databaseId"\`
|
|
51
|
+
\`\`\`
|
|
52
|
+
` /* eslint-enable max-len */
|
|
53
|
+
|
|
54
|
+
const getReleaseProcess = async ({ owner, repo }) => {
|
|
55
|
+
const RELEASE_LIST_ITEM = /^\d+\.\s/gm
|
|
56
|
+
|
|
57
|
+
log(`Fetching release process from:`, owner, repo, 'wiki')
|
|
58
|
+
|
|
59
|
+
let releaseProcess = ''
|
|
60
|
+
try {
|
|
61
|
+
releaseProcess = await new Promise((resolve, reject) => {
|
|
62
|
+
require('https')
|
|
63
|
+
.get(`https://raw.githubusercontent.com/wiki/${owner}/${repo}/Release-Process.md`, resp => {
|
|
64
|
+
let d = ''
|
|
65
|
+
resp.on('data', c => (d += c))
|
|
66
|
+
resp.on('end', () => {
|
|
67
|
+
if (resp.statusCode !== 200) {
|
|
68
|
+
reject(new Error(`${resp.req.protocol + resp.req.host + resp.req.path}: ${d}`))
|
|
69
|
+
} else {
|
|
70
|
+
resolve(d)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
.on('error', reject)
|
|
75
|
+
})
|
|
76
|
+
} catch (e) {
|
|
77
|
+
log('Release wiki not found', e.message)
|
|
78
|
+
log('Using default release process')
|
|
79
|
+
releaseProcess = DEFAULT_RELEASE_PROCESS.trim() + '\n'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// XXX: the release steps need to always be the last thing in the doc for this to work
|
|
83
|
+
const releaseLines = releaseProcess.split('\n')
|
|
84
|
+
const releaseStartLine = releaseLines.reduce((acc, line, index) =>
|
|
85
|
+
line.match(/^#+\s/) ? index : acc, 0)
|
|
86
|
+
const section = releaseLines.slice(releaseStartLine).join('\n')
|
|
87
|
+
|
|
88
|
+
return section.split({
|
|
89
|
+
[Symbol.split] (str) {
|
|
90
|
+
const [, ...matches] = str.split(RELEASE_LIST_ITEM)
|
|
91
|
+
log(`Found ${matches.length} release items`)
|
|
92
|
+
return matches.map((m) => `- [ ] <STEP_INDEX>. ${m}`.trim())
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const getPrReleases = async (pr) => {
|
|
98
|
+
const RELEASE_SEPARATOR = /<details><summary>.*<\/summary>/g
|
|
99
|
+
const MONO_VERSIONS = /<details><summary>(?:(.*?):\s)?(.*?)<\/summary>/
|
|
100
|
+
const ROOT_VERSION = /\n##\s\[(.*?)\]/
|
|
101
|
+
|
|
102
|
+
const workspaces = [...await mapWorkspaces({ pkg: pkg, cwd: ROOT })].reduce((acc, [k]) => {
|
|
103
|
+
const wsComponentName = k.startsWith('@') ? k.split('/')[1] : k
|
|
104
|
+
acc[wsComponentName] = k
|
|
105
|
+
return acc
|
|
106
|
+
}, {})
|
|
107
|
+
|
|
108
|
+
const getReleaseInfo = ({ name, version: rawVersion }) => {
|
|
109
|
+
const version = semver.parse(rawVersion)
|
|
110
|
+
const prerelease = !!version.prerelease.length
|
|
111
|
+
const tag = `${name ? `${name}-` : ''}v${rawVersion}`
|
|
112
|
+
const workspace = workspaces[name]
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
tag,
|
|
117
|
+
prerelease,
|
|
118
|
+
version: rawVersion,
|
|
119
|
+
major: version.major,
|
|
120
|
+
url: `https://github.com/${pr.base.repo.full_name}/releases/tag/${tag}`,
|
|
121
|
+
flags: `${name ? `-w ${workspace}` : ''} ${prerelease ? `--tag prerelease` : ''}`.trim(),
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const releases = pr.body.match(RELEASE_SEPARATOR)
|
|
126
|
+
|
|
127
|
+
if (!releases) {
|
|
128
|
+
log('Found no monorepo, checking for single root version')
|
|
129
|
+
const [, version] = pr.body.match(ROOT_VERSION) || []
|
|
130
|
+
|
|
131
|
+
if (!version) {
|
|
132
|
+
throw new Error('Could not find version with:', ROOT_VERSION)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
log('Found version', version)
|
|
136
|
+
return [getReleaseInfo({ version })]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
log(`Found ${releases.length} releases`)
|
|
140
|
+
|
|
141
|
+
return releases.reduce((acc, r) => {
|
|
142
|
+
const [, name, version] = r.match(MONO_VERSIONS)
|
|
143
|
+
const release = getReleaseInfo({ name, version })
|
|
144
|
+
|
|
145
|
+
if (!name) {
|
|
146
|
+
log('Found root', release)
|
|
147
|
+
acc[0] = release
|
|
148
|
+
} else {
|
|
149
|
+
log('Found workspace', release)
|
|
150
|
+
acc[1].push(release)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return acc
|
|
154
|
+
}, [null, []])
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const appendToComment = async ({ github, commentId, title, body }) => {
|
|
158
|
+
if (!commentId) {
|
|
159
|
+
log(`No comment id, skipping append to comment`)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { data: comment } = await github.rest.issues.getComment({
|
|
164
|
+
...github.repo,
|
|
165
|
+
comment_id: commentId,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const hasAppended = comment.body.includes(title)
|
|
169
|
+
|
|
170
|
+
log('Found comment with id:', commentId)
|
|
171
|
+
log(hasAppended ? 'Comment has aready been appended, replacing' : 'Appending to comment')
|
|
172
|
+
|
|
173
|
+
const prefix = hasAppended
|
|
174
|
+
? comment.body.split(title)[0]
|
|
175
|
+
: comment.body
|
|
176
|
+
|
|
177
|
+
return github.rest.issues.updateComment({
|
|
178
|
+
...github.repo,
|
|
179
|
+
comment_id: commentId,
|
|
180
|
+
body: [prefix, title, body].join('\n\n'),
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const main = async (env) => {
|
|
185
|
+
// These env vars are set by the release.yml workflow from template-oss
|
|
186
|
+
const {
|
|
187
|
+
CI,
|
|
188
|
+
GITHUB_TOKEN,
|
|
189
|
+
GITHUB_REPOSITORY,
|
|
190
|
+
RELEASE_PR_NUMBER,
|
|
191
|
+
RELEASE_COMMENT_ID, // comment is optional for testing
|
|
192
|
+
} = env
|
|
193
|
+
|
|
194
|
+
if (!CI || !GITHUB_TOKEN || !GITHUB_REPOSITORY || !RELEASE_PR_NUMBER) {
|
|
195
|
+
throw new Error('This script is designed to run in CI. If you want to test it, set the ' +
|
|
196
|
+
`following env vars: \`CI, GITHUB_TOKEN, GITHUB_REPOSITORY, RELEASE_PR_NUMBER\``)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const [owner, repo] = GITHUB_REPOSITORY.split('/')
|
|
200
|
+
const github = new Octokit({ auth: GITHUB_TOKEN })
|
|
201
|
+
github.repo = { owner, repo }
|
|
202
|
+
|
|
203
|
+
const { data: pr } = await github.rest.pulls.get({
|
|
204
|
+
...github.repo,
|
|
205
|
+
pull_number: RELEASE_PR_NUMBER,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
const [release, workspaces = []] = await getPrReleases(pr)
|
|
209
|
+
|
|
210
|
+
const RELEASE_OMIT_PRERELEASE = '> NOT FOR PRERELEASE'
|
|
211
|
+
const RELEASE_OMIT_WORKSPACES = 'Publish workspaces'
|
|
212
|
+
const releaseItems = (await getReleaseProcess({ owner, repo }))
|
|
213
|
+
.filter((item) => {
|
|
214
|
+
if (release.prerelease && item.includes(RELEASE_OMIT_PRERELEASE)) {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!workspaces.length && item.includes(RELEASE_OMIT_WORKSPACES)) {
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return true
|
|
223
|
+
})
|
|
224
|
+
.map((item, index) => item.replace('<STEP_INDEX>', index + 1))
|
|
225
|
+
|
|
226
|
+
log(
|
|
227
|
+
`Filtered ${releaseItems.length} release process items:\n`,
|
|
228
|
+
releaseItems.map(r => r.split('\n')[0].replace('- [ ] ', '')).join(', ')
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
const releaseTitle = `### Release Checklist for ${release.tag}`
|
|
232
|
+
const releaseChecklist = releaseItems
|
|
233
|
+
.join('\n\n')
|
|
234
|
+
.replace(/<PR-NUMBER>/g, RELEASE_PR_NUMBER)
|
|
235
|
+
.replace(/<RELEASE-BRANCH>/g, pr.head.ref)
|
|
236
|
+
.replace(/<BASE-BRANCH>/g, pr.base.ref)
|
|
237
|
+
.replace(/<MAJOR>/g, release.major)
|
|
238
|
+
.replace(/<X\.Y\.Z>/g, release.version)
|
|
239
|
+
.replace(/<GITHUB-RELEASE-LINK>/g, release.url)
|
|
240
|
+
.replace(/<PUBLISH-FLAGS>/g, release.flags)
|
|
241
|
+
.replace(/^(\s*\S.*)(-w <WS-PKG-N>)$/gm, workspaces.map(w => `$1${w.flags}`).join('\n'))
|
|
242
|
+
.trim()
|
|
243
|
+
|
|
244
|
+
await appendToComment({
|
|
245
|
+
github,
|
|
246
|
+
commentId: RELEASE_COMMENT_ID,
|
|
247
|
+
title: releaseTitle,
|
|
248
|
+
body: releaseChecklist,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
if (!RELEASE_COMMENT_ID) {
|
|
252
|
+
console.log(releaseChecklist)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
main(process.env)
|
|
257
|
+
// This is part of the release CI and is for posting a release manager
|
|
258
|
+
// comment to the issue but we dont want it to ever fail the workflow so
|
|
259
|
+
// just log but dont set the error code
|
|
260
|
+
.catch(err => console.error(err))
|
package/lib/config.js
CHANGED
|
@@ -99,6 +99,10 @@ const getFullConfig = async ({
|
|
|
99
99
|
pkgConfig: _pkgConfig,
|
|
100
100
|
}) => {
|
|
101
101
|
const isRoot = root === path
|
|
102
|
+
const isWorkspace = !isRoot
|
|
103
|
+
const isMono = !!workspaces.length
|
|
104
|
+
const isRootMono = isRoot && isMono
|
|
105
|
+
|
|
102
106
|
const isLatest = _pkgConfig.version === LATEST_VERSION
|
|
103
107
|
const isDogFood = pkgJson.name === NAME
|
|
104
108
|
const isForce = process.argv.includes('--force')
|
|
@@ -154,14 +158,14 @@ const getFullConfig = async ({
|
|
|
154
158
|
// all derived keys
|
|
155
159
|
const derived = {
|
|
156
160
|
isRoot,
|
|
157
|
-
isWorkspace
|
|
161
|
+
isWorkspace,
|
|
158
162
|
// Some files are written to the base of a repo but will
|
|
159
163
|
// include configuration for all packages within a monorepo
|
|
160
164
|
// For these cases it is helpful to know if we are in a
|
|
161
165
|
// monorepo since template-oss might be used only for
|
|
162
166
|
// workspaces and not the root or vice versa.
|
|
163
|
-
isRootMono
|
|
164
|
-
isMono
|
|
167
|
+
isRootMono,
|
|
168
|
+
isMono,
|
|
165
169
|
// repo
|
|
166
170
|
repoDir: root,
|
|
167
171
|
repoFiles,
|
|
@@ -177,8 +181,8 @@ const getFullConfig = async ({
|
|
|
177
181
|
pkgPath,
|
|
178
182
|
pkgDir: posixDir(pkgPath),
|
|
179
183
|
pkgGlob: posixGlob(pkgPath),
|
|
180
|
-
pkgFlags:
|
|
181
|
-
allFlags: '-ws -iwr --if-present',
|
|
184
|
+
pkgFlags: isWorkspace ? `-w ${pkgJson.name}` : '',
|
|
185
|
+
allFlags: isMono ? '-ws -iwr --if-present' : '',
|
|
182
186
|
workspacePaths,
|
|
183
187
|
workspaceGlobs: workspacePaths.map(posixGlob),
|
|
184
188
|
// booleans to control application of updates
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
- name: Lint
|
|
2
|
-
run: {{ rootNpmPath }} run lint --ignore-scripts
|
|
2
|
+
run: {{ rootNpmPath }} run lint --ignore-scripts {{~#if jobRunFlags}} {{ jobRunFlags }}{{/if}}
|
|
3
3
|
- name: Post Lint
|
|
4
|
-
run: {{ rootNpmPath }} run postlint --ignore-scripts
|
|
4
|
+
run: {{ rootNpmPath }} run postlint --ignore-scripts {{~#if jobRunFlags}} {{ jobRunFlags }}{{/if}}
|
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
{{> job
|
|
12
12
|
jobName="template-oss"
|
|
13
13
|
jobIf="github.actor == 'dependabot[bot]'"
|
|
14
|
-
jobCheckout=(obj ref="${{ github.
|
|
14
|
+
jobCheckout=(obj ref="${{ github.event.pull_request.head.ref }}")
|
|
15
15
|
}}
|
|
16
16
|
- name: Fetch Dependabot Metadata
|
|
17
17
|
id: metadata
|
package/lib/content/release.yml
CHANGED
|
@@ -77,7 +77,8 @@ jobs:
|
|
|
77
77
|
RELEASE_COMMENT_ID: $\{{ needs.release.outputs.comment-id }}
|
|
78
78
|
GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
|
|
79
79
|
run: |
|
|
80
|
-
{{ rootNpmPath }}
|
|
80
|
+
{{ rootNpmPath }} exec --offline -- template-oss-release-manager
|
|
81
|
+
{{ rootNpmPath }} run rp-pull-request --ignore-scripts {{~#if allFlags}} {{ allFlags }}{{else}} --if-present{{/if}}
|
|
81
82
|
- name: Commit
|
|
82
83
|
id: commit
|
|
83
84
|
env:
|
|
@@ -81,9 +81,13 @@ module.exports = class extends NodeWorkspace {
|
|
|
81
81
|
for (const [depName, resolved] of graphPackage.localDependencies) {
|
|
82
82
|
const depVersion = updatedVersions.get(depName)
|
|
83
83
|
const isNotDir = resolved.type !== 'directory'
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
|
|
85
|
+
// Due to some bugs in release-please we need to create `deps:` commits
|
|
86
|
+
// for all dev and prod dependencies even though we normally would only
|
|
87
|
+
// do a `chore:` commit to bump a dev dep. The tradeoff is an extra
|
|
88
|
+
// patch release to workspaces that depend on other workspaces as dev
|
|
89
|
+
// dependencies.
|
|
90
|
+
if (depVersion && isNotDir) {
|
|
87
91
|
commitsByPath[path].push({
|
|
88
92
|
message: `deps(${SCOPE}): ${depName} ${depVersion.toString()}`,
|
|
89
93
|
})
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/template-oss",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "templated files used in npm CLI team oss projects",
|
|
5
5
|
"main": "lib/content/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"template-oss-apply": "bin/apply.js",
|
|
8
8
|
"template-oss-check": "bin/check.js",
|
|
9
|
-
"template-oss-release-please": "bin/release-please.js"
|
|
9
|
+
"template-oss-release-please": "bin/release-please.js",
|
|
10
|
+
"template-oss-release-manager": "bin/release-manager.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"lint": "eslint \"**/*.js\"",
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"@npmcli/git": "^3.0.0",
|
|
38
39
|
"@npmcli/map-workspaces": "^2.0.2",
|
|
39
40
|
"@npmcli/package-json": "^2.0.0",
|
|
41
|
+
"@octokit/rest": "^19.0.4",
|
|
40
42
|
"diff": "^5.0.0",
|
|
41
43
|
"glob": "^8.0.1",
|
|
42
44
|
"handlebars": "^4.7.7",
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
"lodash": "^4.17.21",
|
|
48
50
|
"npm-package-arg": "^9.0.1",
|
|
49
51
|
"proc-log": "^2.0.0",
|
|
50
|
-
"release-please": "npm:@npmcli/release-please@^14.2.
|
|
52
|
+
"release-please": "npm:@npmcli/release-please@^14.2.6",
|
|
51
53
|
"semver": "^7.3.5",
|
|
52
54
|
"yaml": "^2.1.1"
|
|
53
55
|
},
|