@npmcli/template-oss 4.21.3 → 4.22.0

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.
@@ -1,5 +1,5 @@
1
1
  const fs = require('fs/promises')
2
- const log = require('proc-log')
2
+ const { log } = require('proc-log')
3
3
  const { rmEach, parseEach } = require('../util/files.js')
4
4
 
5
5
  const run = async (dir, files, options) => {
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const PackageJson = require('@npmcli/package-json')
3
3
 
4
4
  const run = async ({ config: c }) => {
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const { relative, basename } = require('path')
3
3
  const { rmEach, parseEach } = require('../util/files.js')
4
4
  const { partition } = require('lodash')
@@ -12,8 +12,7 @@ const run = async (type, dir, files, options) => {
12
12
  const { add: addFiles, rm: rmFiles } = files
13
13
 
14
14
  const rm = await rmEach(dir, rmFiles, options, (f) => rel(f))
15
- const parseOpts = { allowMultipleSources: false }
16
- const [add, update] = partition(await parseEach(dir, addFiles, options, parseOpts, async (p) => {
15
+ const [add, update] = partition(await parseEach(dir, addFiles, options, {}, async (p) => {
17
16
  const diff = await p.applyDiff()
18
17
  const target = rel(p.target)
19
18
  if (diff === null) {
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const { resolve, relative, basename } = require('path')
3
3
  const fs = require('fs/promises')
4
4
  const { existsSync } = require('fs')
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const npa = require('npm-package-arg')
3
3
  const { partition } = require('lodash')
4
4
  const hasPackage = require('../util/has-package.js')
@@ -7,10 +7,10 @@ software for any purpose with or without fee is hereby
7
7
  granted, provided that the above copyright notice and this
8
8
  permission notice appear in all copies.
9
9
 
10
- THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11
11
  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
12
12
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
13
- EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT,
13
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
14
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
15
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16
16
  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
@@ -11,6 +11,9 @@ strategy:
11
11
  - name: macOS
12
12
  os: macos-latest
13
13
  shell: bash
14
+ - name: macOS
15
+ os: macos-13
16
+ shell: bash
14
17
  {{/if}}
15
18
  {{#if windowsCI}}
16
19
  - name: Windows
@@ -21,6 +24,13 @@ strategy:
21
24
  {{#each ciVersions}}
22
25
  - {{ . }}
23
26
  {{/each}}
27
+ {{#if macCI}}
28
+ exclude:
29
+ {{#each ciVersions}}
30
+ - platform: {name: macOS, os: macos-{{#if (lte (semverRangeMajor .) 14)}}latest{{else}}13{{/if}}, shell: bash}
31
+ node-version: {{ . }}
32
+ {{/each}}
33
+ {{/if}}
24
34
  runs-on: $\{{ matrix.platform.os }}
25
35
  defaults:
26
36
  run:
@@ -15,6 +15,7 @@ steps:
15
15
  - name: Publish
16
16
  env:
17
17
  PUBLISH_TOKEN: $\{{ secrets.PUBLISH_TOKEN }}
18
+ RELEASES: $\{{ inputs.releases }}
18
19
  {{else}}
19
20
  {{> stepsSetupYml }}
20
21
  - name: Check If Published
@@ -22,22 +23,15 @@ steps:
22
23
  run: |
23
24
  EXIT_CODE=0
24
25
 
25
- function each_release {
26
- if {{#if publish}}npm publish --provenance --tag="$1"{{else}}npm view "$@" --loglevel=error > /dev/null{{/if}}; then
27
- echo 0
28
- else
29
- echo 1
30
- fi
31
- }
32
-
33
- for release in $(echo '$\{{ inputs.releases }}' | jq -r '.[] | @base64'); do
26
+ for release in $(echo $RELEASES | jq -r '.[] | @base64'); do
34
27
  {{#if publish}}
35
28
  PUBLISH_TAG=$(echo "$release" | base64 --decode | jq -r .publishTag)
36
- STATUS=$(each_release "$PUBLISH_TAG")
29
+ npm publish --provenance --tag="$PUBLISH_TAG"
37
30
  {{else}}
38
31
  SPEC="$(echo "$release" | base64 --decode | jq -r .pkgName)@$(echo "$release" | base64 --decode | jq -r .version)"
39
- STATUS=$(each_release "$SPEC")
32
+ npm view "$SPEC" --json
40
33
  {{/if}}
34
+ STATUS=$?
41
35
  if [[ "$STATUS" -eq 1 ]]; then
42
36
  EXIT_CODE=$STATUS
43
37
  {{#unless publish}}
@@ -1,5 +1,5 @@
1
1
  - name: Checkout
2
- uses: actions/checkout@v3
2
+ uses: actions/checkout@v4
3
3
  {{#if jobCheckout}}
4
4
  with:
5
5
  {{#each jobCheckout}}
@@ -1,5 +1,5 @@
1
1
  - name: Setup Node
2
- uses: actions/setup-node@v3
2
+ uses: actions/setup-node@v4
3
3
  id: node
4
4
  with:
5
5
  node-version: {{#if jobIsMatrix}}$\{{ matrix.node-version }}{{else}}{{ last ciVersions }}{{/if}}
@@ -15,7 +15,7 @@ runs:
15
15
  using: "composite"
16
16
  steps:
17
17
  - name: Get Workflow Job
18
- uses: actions/github-script@v6
18
+ uses: actions/github-script@v7
19
19
  id: workflow
20
20
  env:
21
21
  JOB_NAME: "$\{{ inputs.name }}"
@@ -3,6 +3,7 @@ module.exports = {
3
3
  rules: {
4
4
  'type-enum': [2, 'always', [{{{ join (quote (pluck changelogTypes "type")) }}}]],
5
5
  'header-max-length': [2, 'always', 80],
6
- 'subject-case': [0, 'always', ['lower-case', 'sentence-case', 'start-case']],
6
+ 'subject-case': [0],
7
+ 'body-max-line-length': [0],
7
8
  },
8
9
  }
@@ -172,7 +172,7 @@ module.exports = {
172
172
  /* to be provided by consuming package */
173
173
  ],
174
174
  ciVersions: {},
175
- latestCiVersion: 20,
175
+ latestCiVersion: 22,
176
176
  lockfile: false,
177
177
  codeowner: '@npm/cli-team',
178
178
  eslint: true,
@@ -199,10 +199,10 @@ module.exports = {
199
199
  },
200
200
  allowedPackages: [],
201
201
  changelogTypes: [
202
- { type: 'feat', section: 'Features', hidden: false, collapse: false },
203
- { type: 'fix', section: 'Bug Fixes', hidden: false, collapse: false },
204
- { type: 'docs', section: 'Documentation', hidden: false, collapse: false },
205
- { type: 'deps', section: 'Dependencies', hidden: false, collapse: false },
206
- { type: 'chore', section: 'Chores', hidden: false, collapse: false },
202
+ { type: 'feat', section: 'Features', hidden: false },
203
+ { type: 'fix', section: 'Bug Fixes', hidden: false },
204
+ { type: 'docs', section: 'Documentation', hidden: false },
205
+ { type: 'deps', section: 'Dependencies', hidden: false },
206
+ { type: 'chore', section: 'Chores', hidden: true },
207
207
  ],
208
208
  }
@@ -30,7 +30,7 @@ jobs:
30
30
  run: {{ rootNpxPath }} --offline template-oss-release-please --branch="$\{{ github.ref_name }}" --backport="{{ backport }}" --defaultTag="{{ defaultPublishTag }}"
31
31
  - name: Create Release Manager Comment Text
32
32
  if: steps.release.outputs.pr-number
33
- uses: actions/github-script@v6
33
+ uses: actions/github-script@v7
34
34
  id: comment-text
35
35
  with:
36
36
  result-encoding: string
@@ -156,7 +156,7 @@ jobs:
156
156
  {{> jobYml jobName="Post Release - Release" jobIf="needs.release.outputs.releases" jobSkipSetup=true }}
157
157
  - name: Create Release PR Comment Text
158
158
  id: comment-text
159
- uses: actions/github-script@v6
159
+ uses: actions/github-script@v7
160
160
  env:
161
161
  RELEASES: $\{{ needs.release.outputs.releases }}
162
162
  with:
@@ -217,7 +217,7 @@ jobs:
217
217
  - name: Create Release PR Comment Text
218
218
  id: comment-text
219
219
  if: steps.found-comment.outputs.comment-id
220
- uses: actions/github-script@v6
220
+ uses: actions/github-script@v7
221
221
  env:
222
222
  RESULT: $\{{ steps.conclusion.outputs.result }}
223
223
  BODY: $\{{ steps.found-comment.outputs.comment-body }}
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const { resolve } = require('path')
3
3
  const getConfig = require('./config.js')
4
4
  const PackageJson = require('@npmcli/package-json')
@@ -29,28 +29,28 @@ class Changelog {
29
29
  }
30
30
 
31
31
  #getEntries (type) {
32
- const section = this.#sections[type]
33
- const entries = this.#entries[type].map(list)
34
- // Ignoring coverage until we use this again
35
- /* istanbul ignore next */
36
- if (section?.collapse) {
37
- entries.unshift('<details><summary>Commits</summary>\n')
38
- entries.push('\n</details>')
39
- }
40
- return entries.join('\n')
32
+ return this.#entries[type].map(list).join('\n')
41
33
  }
42
34
 
43
35
  toString () {
44
36
  const body = [this.#title]
37
+ const includedTypes = []
38
+
45
39
  for (const type of this.#types) {
46
- const title = this.#titles[type]
47
40
  if (this.#entries[type]?.length) {
48
- body.push(
49
- `### ${title}`,
50
- this.#getEntries(type)
51
- )
41
+ includedTypes.push(type)
42
+ body.push(`### ${this.#titles[type]}`, this.#getEntries(type))
52
43
  }
53
44
  }
45
+
46
+ // If every commit is from a hidden section then we return an
47
+ // empty string which will skip the release PR being created.
48
+ // We do this because we don't want PRs opened if they only contain
49
+ // chores but we do want to rebuild existing PRs if chores are added.
50
+ if (includedTypes.every((type) => this.#sections[type]?.hidden)) {
51
+ return ''
52
+ }
53
+
54
54
  return body.join('\n\n').trim()
55
55
  }
56
56
  }
@@ -110,7 +110,7 @@ class ChangelogNotes {
110
110
  return authorsByCommit
111
111
  }
112
112
 
113
- async #getPullRequestForCommits (commits) {
113
+ async #getPullRequestNumbersForCommits (commits) {
114
114
  const shas = commits
115
115
  .filter(c => !c.pullRequest?.number)
116
116
  .map(c => c.sha)
@@ -134,7 +134,7 @@ class ChangelogNotes {
134
134
  return pullRequestsByCommit
135
135
  }
136
136
 
137
- #buildEntry (commit, { authors = [], pullRequest }) {
137
+ #buildEntry (commit) {
138
138
  const entry = []
139
139
 
140
140
  if (commit.sha) {
@@ -143,7 +143,7 @@ class ChangelogNotes {
143
143
  }
144
144
 
145
145
  // A link to the pull request if the commit has one
146
- const commitPullRequest = commit.pullRequest?.number ?? pullRequest
146
+ const commitPullRequest = commit.pullRequestNumber
147
147
  if (commitPullRequest) {
148
148
  entry.push(link(`#${commitPullRequest}`, this.#ghUrl('pull', commitPullRequest)))
149
149
  }
@@ -154,21 +154,65 @@ class ChangelogNotes {
154
154
  entry.push([scope, subject].filter(Boolean).join(' '))
155
155
 
156
156
  // A list og the authors github handles or names
157
- if (authors.length) {
158
- entry.push(`(${authors.join(', ')})`)
157
+ if (commit.authors.length) {
158
+ entry.push(`(${commit.authors.join(', ')})`)
159
159
  }
160
160
 
161
161
  return entry.join(' ')
162
162
  }
163
163
 
164
- async buildNotes (commits, { version, previousTag, currentTag, changelogSections }) {
164
+ #filterCommits (commits) {
165
+ const filteredCommits = []
166
+ const keyedDuplicates = {}
167
+
168
+ // Filter certain commits so we can make sure only the latest version of
169
+ // each one gets into the changelog
170
+ for (const commit of commits) {
171
+ if (commit.bareMessage.startsWith('postinstall for dependabot template-oss PR')) {
172
+ keyedDuplicates.templateOssPostInstall ??= []
173
+ keyedDuplicates.templateOssPostInstall.push(commit)
174
+ continue
175
+ }
176
+
177
+ if (commit.bareMessage.startsWith('bump @npmcli/template-oss from')) {
178
+ keyedDuplicates.templateOssBump ??= []
179
+ keyedDuplicates.templateOssBump.push(commit)
180
+ continue
181
+ }
182
+
183
+ filteredCommits.push(commit)
184
+ }
185
+
186
+ // Sort all our duplicates so we get the latest verion (by PR number) of each type.
187
+ // Then flatten so we can put them all back into the changelog
188
+ const sortedDupes = Object.values(keyedDuplicates)
189
+ .filter((items) => Boolean(items.length))
190
+ .map((items) => items.sort((a, b) => b.pullRequestNumber - a.pullRequestNumber))
191
+ .flatMap(items => items[0])
192
+
193
+ // This moves them to the bottom of their changelog section which is not
194
+ // strictly necessary but it's easier to do this way.
195
+ for (const duplicate of sortedDupes) {
196
+ filteredCommits.push(duplicate)
197
+ }
198
+
199
+ return filteredCommits
200
+ }
201
+
202
+ async buildNotes (rawCommits, { version, previousTag, currentTag, changelogSections }) {
165
203
  // get authors for commits for each sha
166
- const authorsByCommit = await this.#getAuthorsForCommits(commits)
204
+ const authors = await this.#getAuthorsForCommits(rawCommits)
167
205
 
168
206
  // when rebase merging multiple commits with a single PR, only the first commit
169
207
  // will have a pr number when coming from release-please. this check will manually
170
208
  // lookup commits without a pr number and find one if it exists
171
- const pullRequestByCommit = await this.#getPullRequestForCommits(commits)
209
+ const prNumbers = await this.#getPullRequestNumbersForCommits(rawCommits)
210
+
211
+ const fullCommits = rawCommits.map((commit) => {
212
+ commit.authors = authors[commit.sha] ?? []
213
+ commit.pullRequestNumber = Number(commit.pullRequest?.number ?? prNumbers[commit.sha])
214
+ return commit
215
+ })
172
216
 
173
217
  const changelog = new Changelog({
174
218
  version,
@@ -178,12 +222,9 @@ class ChangelogNotes {
178
222
  sections: changelogSections,
179
223
  })
180
224
 
181
- for (const commit of commits) {
225
+ for (const commit of this.#filterCommits(fullCommits)) {
182
226
  // Collect commits by type
183
- changelog.add(commit.type, this.#buildEntry(commit, {
184
- authors: authorsByCommit[commit.sha],
185
- pullRequest: pullRequestByCommit[commit.sha],
186
- }))
227
+ changelog.add(commit.type, this.#buildEntry(commit))
187
228
 
188
229
  // And breaking changes to its own section
189
230
  changelog.add(Changelog.BREAKING, ...commit.notes
@@ -3,7 +3,7 @@ const { ManifestPlugin } = require('release-please/build/src/plugin.js')
3
3
  const { addPath } = require('release-please/build/src/plugins/workspace.js')
4
4
  const { TagName } = require('release-please/build/src/util/tag-name.js')
5
5
  const { ROOT_PROJECT_PATH } = require('release-please/build/src/manifest.js')
6
- const { DEPS, link, wrapSpecs } = require('./util.js')
6
+ const { DEPS, link, wrapSpecs, list } = require('./util.js')
7
7
 
8
8
  /* istanbul ignore next: TODO fix flaky tests and enable coverage */
9
9
  module.exports = class extends ManifestPlugin {
@@ -11,7 +11,6 @@ module.exports = class extends ManifestPlugin {
11
11
 
12
12
  #releasesByPackage = new Map()
13
13
  #pathsByComponent = new Map()
14
- #WORKSPACE_SCOPE = /(?<scope>workspace): `?(?<name>\S+?)[@\s](?<version>\S+?)`?$/gm
15
14
 
16
15
  async preconfigure (strategiesByPath) {
17
16
  // First build a list of all releases that will happen based on
@@ -32,6 +31,16 @@ module.exports = class extends ManifestPlugin {
32
31
  return candidates
33
32
  }
34
33
 
34
+ #replaceWorkspace ({ name, versionRange }) {
35
+ const version = versionRange.replace(/^[\^~]/, '')
36
+ const { path, component } = this.#releasesByPackage.get(name)
37
+ const { tagSeparator, includeVInTag } = this.repositoryConfig[path]
38
+ const { repository: { owner, repo } } = this.github
39
+ const tag = new TagName(version, component, tagSeparator, includeVInTag).toString()
40
+ const url = `https://github.com/${owner}/${repo}/releases/tag/${tag}`
41
+ return list(`${link('workspace', url)}: ${wrapSpecs(`${name}@${version}`)}`)
42
+ }
43
+
35
44
  // I don't like how release-please formats workspace changelog entries
36
45
  // so this rewrites them to look like the rest of our changelog. This can't
37
46
  // be part of the changelog plugin since they are written after that by the
@@ -42,16 +51,20 @@ module.exports = class extends ManifestPlugin {
42
51
  for (const release of candidate.pullRequest.body.releaseData) {
43
52
  // Update notes with a link to each workspaces release notes
44
53
  // now that we have all of the releases in a single pull request
45
- release.notes =
46
- release.notes.replace(this.#WORKSPACE_SCOPE, (...args) => {
47
- const { scope, name, version } = args.pop()
48
- const { path, component } = this.#releasesByPackage.get(name)
49
- const { tagSeparator, includeVInTag } = this.repositoryConfig[path]
50
- const { repository: { owner, repo } } = this.github
51
- const tag = new TagName(version, component, tagSeparator, includeVInTag).toString()
52
- const url = `https://github.com/${owner}/${repo}/releases/tag/${tag}`
53
- return `${link(scope, url)}: ${wrapSpecs(`${name}@${version}`)}`
54
- })
54
+ release.notes = release.notes
55
+ .replace(/^\* The following workspace dependencies were updated\n/gm, '')
56
+ .replace(/^\s{2}\* dependencies\n/gm, '')
57
+ .replace(/^\s{2}\* devDependencies\n/gm, '')
58
+ .replace(/^\s{2}\* peerDependencies\n/gm, '')
59
+ .replace(/^\s{2}\* optionalDependencies\n/gm, '')
60
+ .replace(
61
+ /^\s{4}\* (?<name>[^\s]+) bumped to (?<versionRange>[^\s]+)/gm,
62
+ (...args) => this.#replaceWorkspace(args.at(-1))
63
+ )
64
+ .replace(
65
+ /^\s{4}\* (?<name>[^\s]+) bumped from (?:[^\s]+) to (?<versionRange>[^\s]+)/gm,
66
+ (...args) => this.#replaceWorkspace(args.at(-1))
67
+ )
55
68
 
56
69
  // Find the associated changelog and update that too
57
70
  const path = this.#pathsByComponent.get(release.component)
@@ -11,7 +11,6 @@ const assert = require('assert')
11
11
  const core = require('@actions/core')
12
12
  const omit = require('just-omit')
13
13
  const ChangelogNotes = require('./changelog.js')
14
- const NodeWorkspace = require('./node-workspace.js')
15
14
  const NodeWorkspaceFormat = require('./node-workspace-format.js')
16
15
  const { getPublishTag, noop } = require('./util.js')
17
16
 
@@ -69,8 +68,6 @@ class ReleasePlease {
69
68
  new ChangelogNotes(github, o))
70
69
  RP.registerVersioningStrategy('default', (o) =>
71
70
  o.prerelease ? new PrereleaseVersioningStrategy(o) : new DefaultVersioningStrategy(o))
72
- RP.registerPlugin('node-workspace', ({ github, targetBranch, repositoryConfig, ...o }) =>
73
- new NodeWorkspace(github, targetBranch, repositoryConfig, o))
74
71
  RP.registerPlugin('node-workspace-format', ({ github, targetBranch, repositoryConfig, ...o }) =>
75
72
  new NodeWorkspaceFormat(github, targetBranch, repositoryConfig, o))
76
73
 
package/lib/util/files.js CHANGED
@@ -31,29 +31,28 @@ const mergeFiles = mergeWithCustomizers((value, srcValue, key, target, source, s
31
31
  }
32
32
  }, customizers.overwriteArrays)
33
33
 
34
- const fileEntries = (dir, files, options, { allowMultipleSources = true } = {}) => {
34
+ const fileEntries = (dir, files, options) => {
35
35
  const results = []
36
36
 
37
- for (const [key, source] of Object.entries(files)) {
37
+ for (const [key, value] of Object.entries(files)) {
38
38
  // remove any false values first since that means those targets are skipped
39
- if (source === false) {
39
+ if (value === false) {
40
40
  continue
41
41
  }
42
42
 
43
43
  // target paths need to be joined with dir and templated
44
44
  const target = join(dir, template(key, options))
45
45
 
46
- if (Array.isArray(source)) {
47
- // When turning an object of files into all its entries, we allow
48
- // multiples when applying changes, but not when checking for changes
49
- // since earlier files would always return as needing an update. So we
50
- // either allow multiples and return the array or only return the last
51
- // source file in the array.
52
- const sources = allowMultipleSources ? source : source.slice(-1)
53
- results.push(...sources.map(s => [target, s]))
54
- } else {
55
- results.push([target, source])
56
- }
46
+ // Allow an array of values to merge into a single source to be
47
+ // applied or diffed against the target. This is how overwrite:false
48
+ // works and they are merged.
49
+ const source = Array.isArray(value)
50
+ ? value.reduce((acc, { file, ...rest }) => {
51
+ acc.file.push(file)
52
+ return Object.assign(acc, rest)
53
+ }, { file: [] }) : value
54
+
55
+ results.push([target, source])
57
56
  }
58
57
 
59
58
  return results
package/lib/util/git.js CHANGED
@@ -23,7 +23,7 @@ const getRemoteUrl = async (path, remote) => {
23
23
  try {
24
24
  const urlStr = await tryGit(path, 'remote', 'get-url', remote)
25
25
  const { domain, user, project } = hgi.fromUrl(urlStr)
26
- const url = new URL(`https://${domain}`)
26
+ const url = new URL(`git+https://${domain}`)
27
27
  url.pathname = `/${user}/${project}.git`
28
28
  return url.toString()
29
29
  } catch {
@@ -62,10 +62,16 @@ class Base {
62
62
  }
63
63
 
64
64
  read (s) {
65
+ if (Array.isArray(s)) {
66
+ return Promise.all(s.map(f => this.read(f)))
67
+ }
65
68
  return fs.readFile(s, { encoding: 'utf-8' })
66
69
  }
67
70
 
68
71
  template (s) {
72
+ if (Array.isArray(s)) {
73
+ return Promise.all(s.map(f => this.template(f)))
74
+ }
69
75
  return template(s, this.options)
70
76
  }
71
77
 
@@ -285,6 +291,9 @@ class Json extends Base {
285
291
  }
286
292
 
287
293
  parse (s) {
294
+ if (Array.isArray(s)) {
295
+ return s.map(f => this.parse(f)).reduce((a, f) => this.merge(a, f), {})
296
+ }
288
297
  return jsonParse(s)
289
298
  }
290
299
 
@@ -1,5 +1,6 @@
1
1
  const Handlebars = require('handlebars')
2
2
  const { basename, extname, join } = require('path')
3
+ const { Range } = require('semver')
3
4
  const fs = require('fs')
4
5
  const DELETE = '__DELETE__'
5
6
 
@@ -39,6 +40,8 @@ const setupHandlebars = (dirs) => {
39
40
  Handlebars.registerHelper('last', (arr) => arr[arr.length - 1])
40
41
  Handlebars.registerHelper('json', (c) => JSON.stringify(c))
41
42
  Handlebars.registerHelper('del', () => JSON.stringify(DELETE))
43
+ Handlebars.registerHelper('semverRangeMajor', (v) => new Range(v).set[0][0].semver.major)
44
+ Handlebars.registerHelper('lte', (a, b) => a <= b)
42
45
 
43
46
  if (Array.isArray(dirs)) {
44
47
  const [baseDir, ...otherDirs] = dirs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npmcli/template-oss",
3
- "version": "4.21.3",
3
+ "version": "4.22.0",
4
4
  "description": "templated files used in npm CLI team oss projects",
5
5
  "main": "lib/content/index.js",
6
6
  "bin": {
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/npm/template-oss.git"
27
+ "url": "git+https://github.com/npm/template-oss.git"
28
28
  },
29
29
  "keywords": [
30
30
  "npm",
@@ -34,8 +34,8 @@
34
34
  "license": "ISC",
35
35
  "dependencies": {
36
36
  "@actions/core": "^1.9.1",
37
- "@commitlint/cli": "^18.2.0",
38
- "@commitlint/config-conventional": "^18.1.0",
37
+ "@commitlint/cli": "^19.0.3",
38
+ "@commitlint/config-conventional": "^19.2.2",
39
39
  "@isaacs/string-locale-compare": "^1.1.0",
40
40
  "@npmcli/arborist": "^7.2.1",
41
41
  "@npmcli/git": "^5.0.3",
@@ -55,10 +55,10 @@
55
55
  "lodash": "^4.17.21",
56
56
  "minimatch": "^9.0.2",
57
57
  "npm-package-arg": "^11.0.1",
58
- "proc-log": "^3.0.0",
59
- "release-please": "16.3.1",
58
+ "proc-log": "^4.0.0",
59
+ "release-please": "16.10.2",
60
60
  "semver": "^7.3.5",
61
- "undici": "^5.27.2",
61
+ "undici": "^6.7.0",
62
62
  "yaml": "^2.1.1"
63
63
  },
64
64
  "files": [
@@ -1,103 +0,0 @@
1
- const { NodeWorkspace } = require('release-please/build/src/plugins/node-workspace')
2
- const { parseConventionalCommits } = require('release-please/build/src/commit')
3
- const { DEPS } = require('./util')
4
- const { WORKSPACE_MESSAGE } = require('./node-workspace-format')
5
-
6
- // This adds a preconfigure method to the release-please node-workspace plugin
7
- // which fixes https://github.com/googleapis/release-please/issues/2089 for our
8
- // use case. We should attempt to upstream this to release-please but it
9
- // fundamentally changes the way the node-workspace plugin behaves so it might
10
- // not be easy to land. For now we extend the base plugin and add one method
11
- // which is much better than previously when we needed to fork and maintain
12
- // release-please ourselves.
13
- /* istanbul ignore next: TODO fix flaky tests and enable coverage */
14
- class NpmNodeWorkspace extends NodeWorkspace {
15
- updateCandidate (pr) {
16
- // no-op so release-please node-workspace plugin does not add
17
- // its broken changelog to the workspace releases. our changelog
18
- // formatting and the preconfigure method below fix this.
19
- return pr
20
- }
21
-
22
- async preconfigure (strategiesByPath, commitsByPath, releasesByPath) {
23
- // First build a list of all releases that will happen based on the
24
- // conventional commits
25
- const candidates = []
26
- for (const path in strategiesByPath) {
27
- const pullRequest = await strategiesByPath[path].buildReleasePullRequest(
28
- parseConventionalCommits(commitsByPath[path]),
29
- releasesByPath[path]
30
- )
31
- // Release please types say this sometimes will return undefined but I could not
32
- // get any scenario where that was the case. If it was undefined we would want to
33
- // just ignore it anyway.
34
- /* istanbul ignore else */
35
- if (pullRequest) {
36
- candidates.push({
37
- path,
38
- pullRequest,
39
- config: this.repositoryConfig[path],
40
- })
41
- }
42
- }
43
-
44
- // Then build the graph of all those releases + any other connected workspaces
45
- const { allPackages, candidatesByPackage } = await this.buildAllPackages(candidates)
46
- const graph = await this.buildGraph(allPackages)
47
- const packageNamesToUpdate = this.packageNamesToUpdate(graph, candidatesByPackage)
48
- const graphPackages = this.buildGraphOrder(graph, packageNamesToUpdate)
49
-
50
- // Then build a list of all the updated versions
51
- const updatedVersions = {}
52
- for (const pkg of graphPackages) {
53
- const path = this.pathFromPackage(pkg)
54
- const packageName = this.packageNameFromPackage(pkg)
55
- const existingCandidate = candidatesByPackage[packageName]
56
-
57
- if (existingCandidate) {
58
- // If there is an existing pull request use that version
59
- updatedVersions[packageName] = existingCandidate.pullRequest?.version
60
- } else {
61
- // Otherwise build another pull request (that will be discarded) just
62
- // to see what the version would be if it only contained a deps commit.
63
- // This is to make sure we use any custom versioning or release strategy.
64
- const releasePullRequest = await strategiesByPath[path].buildReleasePullRequest(
65
- parseConventionalCommits([{ message: `${DEPS}: ${Math.random()}` }]),
66
- releasesByPath[path]
67
- )
68
- updatedVersions[packageName] = releasePullRequest?.version
69
- }
70
- }
71
-
72
- // Then go through all the packages again and add deps commits for each
73
- // updated workspace
74
- for (const pkg of graphPackages) {
75
- const path = this.pathFromPackage(pkg)
76
- const packageName = this.packageNameFromPackage(pkg)
77
- const graphPackage = graph.get(packageName)
78
-
79
- // Update dependency versions add a deps commit to each path so it gets
80
- // processed later. This else never happens in our cases because our
81
- // packages always have deps, but keeping this around to make it easier to
82
- // upstream in the future.
83
- /* istanbul ignore else */
84
- if (graphPackage.deps) {
85
- for (const depName of graphPackage.deps) {
86
- const depVersion = updatedVersions[depName]
87
- // Same as the above check, we always have a version here but technically
88
- // we could not in which it would be safe to ignore it.
89
- /* istanbul ignore else */
90
- if (depVersion) {
91
- commitsByPath[path].push({
92
- message: WORKSPACE_MESSAGE(depName, depVersion.toString()),
93
- })
94
- }
95
- }
96
- }
97
- }
98
-
99
- return strategiesByPath
100
- }
101
- }
102
-
103
- module.exports = NpmNodeWorkspace