@npmcli/template-oss 4.11.3 → 4.11.4
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.
- package/bin/release-please.js +2 -2
- package/lib/content/release.yml +17 -6
- package/lib/release-please/index.js +201 -62
- package/package.json +1 -1
package/bin/release-please.js
CHANGED
|
@@ -4,7 +4,7 @@ const core = require('@actions/core')
|
|
|
4
4
|
const main = require('../lib/release-please/index.js')
|
|
5
5
|
|
|
6
6
|
const dryRun = !process.env.CI
|
|
7
|
-
const [branch,
|
|
7
|
+
const [branch, forcePullRequest] = process.argv.slice(2)
|
|
8
8
|
|
|
9
9
|
const debugPr = (val) => {
|
|
10
10
|
if (dryRun) {
|
|
@@ -45,7 +45,7 @@ main({
|
|
|
45
45
|
repo: process.env.GITHUB_REPOSITORY,
|
|
46
46
|
dryRun,
|
|
47
47
|
branch,
|
|
48
|
-
|
|
48
|
+
forcePullRequest: forcePullRequest ? +forcePullRequest : null,
|
|
49
49
|
}).then(({ pr, release, releases }) => {
|
|
50
50
|
if (pr) {
|
|
51
51
|
debugPr(pr)
|
package/lib/content/release.yml
CHANGED
|
@@ -2,6 +2,10 @@ name: Release
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
release-pr:
|
|
7
|
+
description: a release PR number to rerun release jobs on
|
|
8
|
+
type: string
|
|
5
9
|
push:
|
|
6
10
|
branches:
|
|
7
11
|
{{#each branches}}
|
|
@@ -30,7 +34,7 @@ jobs:
|
|
|
30
34
|
env:
|
|
31
35
|
GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
|
|
32
36
|
run: |
|
|
33
|
-
{{ rootNpxPath }} --offline template-oss-release-please $\{{ github.ref_name }} $\{{
|
|
37
|
+
{{ rootNpxPath }} --offline template-oss-release-please "$\{{ github.ref_name }}" "$\{{ inputs.release-pr }}"
|
|
34
38
|
- name: Post Pull Request Comment
|
|
35
39
|
if: steps.release.outputs.pr-number
|
|
36
40
|
uses: actions/github-script@v6
|
|
@@ -53,7 +57,7 @@ jobs:
|
|
|
53
57
|
body += `Release workflow run: ${workflow.html_url}\n\n#### Force CI to Update This Release\n\n`
|
|
54
58
|
body += `This PR will be updated and CI will run for every non-\`chore:\` commit that is pushed to \`{{ defaultBranch }}\`. `
|
|
55
59
|
body += `To force CI to update this PR, run this command:\n\n`
|
|
56
|
-
body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME} -R ${owner}/${repo}\n\`\`\``
|
|
60
|
+
body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME} -R ${owner}/${repo} -f release-pr=${issue_number}\n\`\`\``
|
|
57
61
|
|
|
58
62
|
if (commentId) {
|
|
59
63
|
await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body })
|
|
@@ -139,14 +143,17 @@ jobs:
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number })
|
|
142
|
-
|
|
146
|
+
.then(cs => cs.map(c => ({ id: c.id, login: c.user.login, body: c.body })))
|
|
147
|
+
console.log(`Found comments: ${JSON.stringify(comments, null, 2)}`)
|
|
148
|
+
const releaseComments = comments.filter(c => c.login === 'github-actions[bot]' && c.body.includes('Release is at'))
|
|
143
149
|
|
|
144
150
|
for (const comment of releaseComments) {
|
|
151
|
+
console.log(`Release comment: ${JSON.stringify(comment, null, 2)}`)
|
|
145
152
|
await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id })
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`
|
|
149
|
-
await github.rest.issues.createComment({
|
|
156
|
+
await github.rest.issues.createComment({
|
|
150
157
|
owner,
|
|
151
158
|
repo,
|
|
152
159
|
issue_number,
|
|
@@ -182,10 +189,14 @@ jobs:
|
|
|
182
189
|
with:
|
|
183
190
|
script: |
|
|
184
191
|
const { PR_NUMBER: issue_number, RESULT } = process.env
|
|
185
|
-
const { repo: { owner, repo } } = context
|
|
192
|
+
const { runId, repo: { owner, repo } } = context
|
|
186
193
|
|
|
187
194
|
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number })
|
|
188
|
-
const updateComment = comments.find(c =>
|
|
195
|
+
const updateComment = comments.find(c =>
|
|
196
|
+
c.user.login === 'github-actions[bot]' &&
|
|
197
|
+
c.body.startsWith('## Release Workflow\n\n') &&
|
|
198
|
+
c.body.includes(runId)
|
|
199
|
+
)
|
|
189
200
|
|
|
190
201
|
if (updateComment) {
|
|
191
202
|
console.log('Found comment to update:', JSON.stringify(updateComment, null, 2))
|
|
@@ -4,26 +4,33 @@ const ChangelogNotes = require('./changelog.js')
|
|
|
4
4
|
const Version = require('./version.js')
|
|
5
5
|
const NodeWs = require('./node-workspace.js')
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const logger = new CheckpointLogger(true, true)
|
|
8
|
+
RP.setLogger(logger)
|
|
8
9
|
RP.registerChangelogNotes('default', (o) => new ChangelogNotes(o))
|
|
9
10
|
RP.registerVersioningStrategy('default', (o) => new Version(o))
|
|
10
11
|
RP.registerPlugin('node-workspace', (o) => new NodeWs(o.github, o.targetBranch, o.repositoryConfig))
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const omit = (obj, ...keys) => {
|
|
14
|
+
const res = {}
|
|
15
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
16
|
+
if (!keys.includes(key)) {
|
|
17
|
+
res[key] = value
|
|
18
|
+
}
|
|
15
19
|
}
|
|
20
|
+
return res
|
|
21
|
+
}
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
const getManifest = async ({ repo: fullRepo, token, branch }) => {
|
|
24
|
+
const fullRepoParts = fullRepo.split('/')
|
|
25
|
+
const github = await RP.GitHub.create({
|
|
26
|
+
owner: fullRepoParts[0],
|
|
27
|
+
repo: fullRepoParts[1],
|
|
28
|
+
token,
|
|
29
|
+
})
|
|
20
30
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
octokit,
|
|
25
|
-
repository: { owner, repo, defaultBranch },
|
|
26
|
-
} = github
|
|
31
|
+
const { octokit, repository: { owner, repo, defaultBranch } } = github
|
|
32
|
+
|
|
33
|
+
const baseBranch = branch ?? defaultBranch
|
|
27
34
|
|
|
28
35
|
// This is mostly for testing and debugging. Use environs with the
|
|
29
36
|
// format `RELEASE_PLEASE_<manfiestOverrideConfigName>` (eg
|
|
@@ -33,8 +40,6 @@ const main = async ({ repo: _fullRepo, token, dryRun, branch, force }) => {
|
|
|
33
40
|
.filter(([k, v]) => k.startsWith('RELEASE_PLEASE_') && v != null)
|
|
34
41
|
.map(([k, v]) => [k.replace('RELEASE_PLEASE_', ''), v])
|
|
35
42
|
|
|
36
|
-
const baseBranch = branch ?? defaultBranch
|
|
37
|
-
|
|
38
43
|
const manifest = await RP.Manifest.fromManifest(
|
|
39
44
|
github,
|
|
40
45
|
baseBranch,
|
|
@@ -43,79 +48,213 @@ const main = async ({ repo: _fullRepo, token, dryRun, branch, force }) => {
|
|
|
43
48
|
Object.fromEntries(manifestOverrides)
|
|
44
49
|
)
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
return {
|
|
52
|
+
github,
|
|
53
|
+
manifest,
|
|
54
|
+
octokit,
|
|
55
|
+
owner,
|
|
56
|
+
repo,
|
|
57
|
+
baseBranch,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
const getReleasesFromPr = async ({ manifest, github, number }) => {
|
|
62
|
+
const baseUrl = `https://github.com/${github.repository.owner}/${github.repository.repo}`
|
|
63
|
+
// get the release please formatted pull request
|
|
64
|
+
let pullRequest
|
|
65
|
+
const prGenerator = github.pullRequestIterator(this.targetBranch, 'MERGED', 200, false)
|
|
66
|
+
for await (const pr of prGenerator) {
|
|
67
|
+
if (pr.number === number) {
|
|
68
|
+
pullRequest = pr
|
|
69
|
+
break
|
|
55
70
|
}
|
|
71
|
+
}
|
|
72
|
+
const strategiesByPath = await manifest.getStrategiesByPath()
|
|
73
|
+
const releases = []
|
|
74
|
+
for (const path in manifest.repositoryConfig) {
|
|
75
|
+
const config = manifest.repositoryConfig[path]
|
|
76
|
+
const release = await strategiesByPath[path].buildRelease(pullRequest)
|
|
77
|
+
if (release) {
|
|
78
|
+
const { tag, ...rest } = release
|
|
79
|
+
releases.push({
|
|
80
|
+
...rest,
|
|
81
|
+
...tag.version,
|
|
82
|
+
tagName: tag.toString(),
|
|
83
|
+
version: tag.version.toString(),
|
|
84
|
+
path,
|
|
85
|
+
draft: false,
|
|
86
|
+
url: `${baseUrl}/releases/tag/${tag.toString()}`,
|
|
87
|
+
prerelease: config.prerelease && !!tag.version.preRelease,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return releases
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const getReleaseArtifacts = async ({ dryRun, manifest, forceReleases }) => {
|
|
95
|
+
let pullRequests = []
|
|
96
|
+
let releases = []
|
|
97
|
+
|
|
98
|
+
if (forceReleases) {
|
|
99
|
+
releases = forceReleases
|
|
100
|
+
} else if (dryRun) {
|
|
101
|
+
pullRequests = await manifest.buildPullRequests()
|
|
102
|
+
releases = await manifest.buildReleases()
|
|
103
|
+
} else {
|
|
104
|
+
pullRequests = await manifest.createPullRequests()
|
|
105
|
+
releases = await manifest.createReleases()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
pullRequests: pullRequests.filter(Boolean),
|
|
110
|
+
releases: releases.filter(Boolean),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// XXX(hack): to get release please to recreate a pull request it needs
|
|
115
|
+
// to have a different body string so we append a message a message that CI
|
|
116
|
+
// is running. This will force release-please to rebase the PR but it
|
|
117
|
+
// wont update the body again, so we only append to it.
|
|
118
|
+
const touchPullRequest = async ({ octokit, owner, repo, releasePr }) => {
|
|
119
|
+
const id = process.env.GITHUB_RUN_ID
|
|
120
|
+
? `by https://github.com/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
|
121
|
+
: `manually starting at ${new Date().toJSON()}`
|
|
122
|
+
|
|
123
|
+
await octokit.pulls.update({
|
|
124
|
+
owner,
|
|
125
|
+
repo,
|
|
126
|
+
pull_number: releasePr.number,
|
|
127
|
+
body: `${releasePr.body.trim()}\n- This PR is being recreated ${id}`,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const main = async ({ repo: fullRepo, token, dryRun, branch, forcePullRequest }) => {
|
|
132
|
+
if (!token) {
|
|
133
|
+
throw new Error('Token is required')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!fullRepo) {
|
|
137
|
+
throw new Error('Repo is required')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const {
|
|
141
|
+
github,
|
|
142
|
+
octokit,
|
|
143
|
+
manifest,
|
|
144
|
+
owner,
|
|
145
|
+
repo,
|
|
146
|
+
baseBranch,
|
|
147
|
+
} = await getManifest({ repo: fullRepo, token, branch })
|
|
56
148
|
|
|
57
|
-
|
|
58
|
-
const id = process.env.GITHUB_RUN_ID
|
|
59
|
-
? `by https://github.com/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
|
60
|
-
: `manually starting at ${new Date().toJSON()}`
|
|
149
|
+
let forceReleases = null
|
|
61
150
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// is running. This will force release-please to rebase the PR but it
|
|
65
|
-
// wont update the body again, so we only append to it.
|
|
66
|
-
await octokit.pulls.update({
|
|
151
|
+
if (forcePullRequest) {
|
|
152
|
+
const { data: releasePr } = await octokit.rest.pulls.get({
|
|
67
153
|
owner,
|
|
68
154
|
repo,
|
|
69
|
-
pull_number:
|
|
70
|
-
body: `${releasePr.body.trim()}\n- This PR is being recreated ${id}`,
|
|
155
|
+
pull_number: forcePullRequest,
|
|
71
156
|
})
|
|
157
|
+
|
|
158
|
+
if (!releasePr) {
|
|
159
|
+
throw new Error(`Could not find PR from number: ${forcePullRequest}`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (releasePr.state === 'open') {
|
|
163
|
+
await touchPullRequest({ octokit, owner, repo, releasePr })
|
|
164
|
+
} else if (releasePr.state === 'closed' && releasePr.merged) {
|
|
165
|
+
forceReleases = await getReleasesFromPr({ manifest, github, number: releasePr.number })
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error(`Could not run workflow on PR with wrong state: ${JSON.stringify(
|
|
168
|
+
releasePr,
|
|
169
|
+
null,
|
|
170
|
+
2
|
|
171
|
+
)}`)
|
|
172
|
+
}
|
|
72
173
|
}
|
|
73
174
|
|
|
74
|
-
const pullRequests = await (dryRun
|
|
75
|
-
const allReleases = await (dryRun ? manifest.buildReleases() : manifest.createReleases())
|
|
175
|
+
const { pullRequests, releases } = await getReleaseArtifacts({ dryRun, manifest, forceReleases })
|
|
76
176
|
|
|
77
177
|
// We only ever get a single pull request with our current release-please settings
|
|
78
|
-
|
|
178
|
+
// Update this if we start creating individual PRs per workspace release
|
|
179
|
+
const rootPr = pullRequests[0]
|
|
180
|
+
let rootRelease = releases[0]
|
|
181
|
+
|
|
182
|
+
logger.debug(`pull requests: ${pullRequests.length}`)
|
|
183
|
+
logger.debug(`releases: ${releases.length}`)
|
|
184
|
+
|
|
185
|
+
if (rootPr) {
|
|
186
|
+
logger.debug(`root pr: ${JSON.stringify(omit(rootPr, 'body'), null, 2)}`)
|
|
187
|
+
}
|
|
188
|
+
|
|
79
189
|
if (rootPr?.number) {
|
|
80
190
|
const commits = await octokit.paginate(octokit.rest.pulls.listCommits, {
|
|
81
191
|
owner,
|
|
82
192
|
repo,
|
|
83
193
|
pull_number: rootPr.number,
|
|
84
194
|
})
|
|
85
|
-
rootPr.sha = commits?.[commits.length - 1]?.sha
|
|
86
|
-
}
|
|
87
195
|
|
|
88
|
-
|
|
89
|
-
|
|
196
|
+
const prSha = commits?.[commits.length - 1]?.sha
|
|
197
|
+
if (!prSha) {
|
|
198
|
+
throw new Error(`Could not find a latest sha for pull request: ${rootPr.number}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
rootPr.sha = prSha
|
|
202
|
+
}
|
|
90
203
|
|
|
91
204
|
for (const release of releases) {
|
|
92
|
-
const
|
|
205
|
+
const { path, sha } = release
|
|
206
|
+
const prefix = path === '.' ? '' : path
|
|
207
|
+
const isRoot = !prefix
|
|
208
|
+
const packagePath = `${prefix}/package.json`
|
|
93
209
|
|
|
94
|
-
|
|
95
|
-
|
|
210
|
+
logger.debug(`release: ${JSON.stringify({
|
|
211
|
+
...omit(release, 'notes'),
|
|
212
|
+
isRoot,
|
|
213
|
+
prefix,
|
|
214
|
+
}, null, 2)}`)
|
|
215
|
+
|
|
216
|
+
const releasePrNumber = await octokit.rest.repos.listPullRequestsAssociatedWithCommit({
|
|
217
|
+
owner,
|
|
218
|
+
repo,
|
|
219
|
+
commit_sha: sha,
|
|
220
|
+
per_page: 1,
|
|
221
|
+
}).then(r => r.data[0]?.number)
|
|
222
|
+
|
|
223
|
+
if (!releasePrNumber) {
|
|
224
|
+
throw new Error(`Could not find release PR number from commit: "${sha}"`)
|
|
96
225
|
}
|
|
97
226
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
227
|
+
logger.debug(`pr from ${sha}: ${releasePrNumber}`)
|
|
228
|
+
|
|
229
|
+
const releasePkgName = await octokit.rest.repos.getContent({
|
|
230
|
+
owner,
|
|
231
|
+
repo,
|
|
232
|
+
ref: baseBranch,
|
|
233
|
+
path: packagePath,
|
|
234
|
+
}).then(r => {
|
|
235
|
+
try {
|
|
236
|
+
return JSON.parse(Buffer.from(r.data.content, r.data.encoding)).name
|
|
237
|
+
} catch {
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
if (!releasePkgName) {
|
|
243
|
+
throw new Error(`Could not find package name for release at: "${packagePath}#${baseBranch}"`)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
logger.debug(`pkg name from ${packagePath}#${baseBranch}: "${releasePkgName}"`)
|
|
247
|
+
|
|
248
|
+
release.prNumber = releasePrNumber
|
|
249
|
+
release.pkgName = releasePkgName
|
|
250
|
+
if (isRoot) {
|
|
251
|
+
rootRelease = release
|
|
252
|
+
}
|
|
114
253
|
}
|
|
115
254
|
|
|
116
255
|
return {
|
|
117
|
-
pr: rootPr,
|
|
118
|
-
release: rootRelease,
|
|
256
|
+
pr: rootPr ?? null,
|
|
257
|
+
release: rootRelease ?? null,
|
|
119
258
|
releases: releases.length ? releases : null,
|
|
120
259
|
}
|
|
121
260
|
}
|