@npmcli/template-oss 4.17.0 → 4.18.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.
package/lib/config.js CHANGED
@@ -2,7 +2,8 @@ const { relative, dirname, join, extname, posix, win32 } = require('path')
2
2
  const { defaults, pick, omit, uniq } = require('lodash')
3
3
  const semver = require('semver')
4
4
  const parseCIVersions = require('./util/parse-ci-versions.js')
5
- const getGitUrl = require('./util/get-git-url.js')
5
+ const parseDependabot = require('./util/dependabot.js')
6
+ const git = require('./util/git.js')
6
7
  const gitignore = require('./util/gitignore.js')
7
8
  const { mergeWithArrays } = require('./util/merge.js')
8
9
  const { FILE_KEYS, parseConfig: parseFiles, getAddedFiles, mergeFiles } = require('./util/files.js')
@@ -11,6 +12,7 @@ const CONFIG_KEY = 'templateOSS'
11
12
  const getPkgConfig = (pkg) => pkg[CONFIG_KEY] || {}
12
13
 
13
14
  const { name: NAME, version: LATEST_VERSION } = require('../package.json')
15
+ const { minimatch } = require('minimatch')
14
16
  const MERGE_KEYS = [...FILE_KEYS, 'defaultContent', 'content']
15
17
  const DEFAULT_CONTENT = require.resolve(NAME)
16
18
 
@@ -153,6 +155,12 @@ const getFullConfig = async ({
153
155
  const publicPkgs = pkgs.filter(p => !p.pkgJson.private)
154
156
  const allPrivate = pkgs.every(p => p.pkgJson.private)
155
157
 
158
+ const branches = uniq([...pkgConfig.branches ?? [], pkgConfig.releaseBranch]).filter(Boolean)
159
+ const gitBranches = await git.getBranches(rootPkg.path, branches)
160
+ const currentBranch = await git.currentBranch(rootPkg.path)
161
+ const isReleaseBranch = currentBranch ? minimatch(currentBranch, pkgConfig.releaseBranch) : false
162
+ const defaultBranch = await git.defaultBranch(rootPkg.path) ?? 'main'
163
+
156
164
  // all derived keys
157
165
  const derived = {
158
166
  isRoot,
@@ -170,6 +178,14 @@ const getFullConfig = async ({
170
178
  allPrivate,
171
179
  // controls whether we are in a monorepo with any public workspaces
172
180
  isMonoPublic: isMono && !!publicPkgs.filter(p => p.path !== rootPkg.path).length,
181
+ // git
182
+ defaultBranch,
183
+ baseBranch: isReleaseBranch ? currentBranch : defaultBranch,
184
+ branches: gitBranches.branches,
185
+ branchPatterns: gitBranches.patterns,
186
+ isReleaseBranch,
187
+ // dependabot
188
+ dependabot: parseDependabot(pkgConfig, defaultConfig, gitBranches.branches),
173
189
  // repo
174
190
  repoDir: rootPkg.path,
175
191
  repoFiles,
@@ -261,7 +277,7 @@ const getFullConfig = async ({
261
277
  }
262
278
  }
263
279
 
264
- const gitUrl = await getGitUrl(rootPkg.path)
280
+ const gitUrl = await git.getUrl(rootPkg.path)
265
281
  if (gitUrl) {
266
282
  derived.repository = {
267
283
  type: 'git',
@@ -12,7 +12,7 @@ pull_request:
12
12
  {{/if}}
13
13
  push:
14
14
  branches:
15
- {{#each branches}}
15
+ {{#each branchPatterns}}
16
16
  - {{ . }}
17
17
  {{/each}}
18
18
  {{#if isWorkspace}}
@@ -1,15 +1,20 @@
1
1
  - name: Setup Node
2
2
  uses: actions/setup-node@v3
3
+ id: node
3
4
  with:
4
5
  node-version: {{#if jobIsMatrix}}$\{{ matrix.node-version }}{{else}}{{ last ciVersions }}{{/if}}
6
+ check-latest: contains({{#if jobIsMatrix}}matrix.node-version{{else}}'{{ last ciVersions }}'{{/if}}, '.x')
5
7
  {{#if lockfile}}
6
8
  cache: npm
7
9
  {{/if}}
10
+
8
11
  {{#if updateNpm}}
9
- {{#if jobIsMatrix}}
12
+ # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows
10
13
  - name: Update Windows npm
11
- # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows
12
- if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.'))
14
+ if: |
15
+ matrix.platform.os == 'windows-latest' && (
16
+ startsWith(steps.node.outputs.node-version, 'v10.') || startsWith(steps.node.outputs.node-version, 'v12.') || startsWith(steps.node.outputs.node-version, 'v14.')
17
+ )
13
18
  run: |
14
19
  curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz
15
20
  tar xf npm-7.5.4.tgz
@@ -17,15 +22,36 @@
17
22
  node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz
18
23
  cd ..
19
24
  rmdir /s /q package
20
- - name: Install npm@7
21
- if: startsWith(matrix.node-version, '10.')
22
- run: npm i --prefer-online --no-fund --no-audit -g npm@7
23
- - name: Install npm@{{ npmSpec }}
24
- if: $\{{ !startsWith(matrix.node-version, '10.') }}
25
- {{else}}
26
- - name: Install npm@{{ npmSpec }}
27
- {{/if}}
28
- run: npm i --prefer-online --no-fund --no-audit -g npm@{{ npmSpec }}
25
+
26
+ # Start on Node 10 because we dont test on anything lower
27
+ - name: Install npm@7 on Node 10
28
+ shell: bash
29
+ if: startsWith(steps.node.outputs.node-version, 'v10.')
30
+ id: npm-7
31
+ run: |
32
+ npm i --prefer-online --no-fund --no-audit -g npm@7
33
+ echo "updated=true" >> "$GITHUB_OUTPUT"
34
+
35
+ - name: Install npm@8 on Node 12
36
+ shell: bash
37
+ if: startsWith(steps.node.outputs.node-version, 'v12.')
38
+ id: npm-8
39
+ run: |
40
+ npm i --prefer-online --no-fund --no-audit -g npm@8
41
+ echo "updated=true" >> "$GITHUB_OUTPUT"
42
+
43
+ - name: Install npm@9 on Node 14/16/18.0
44
+ shell: bash
45
+ if: startsWith(steps.node.outputs.node-version, 'v14.') || startsWith(steps.node.outputs.node-version, 'v16.') || startsWith(steps.node.outputs.node-version, 'v18.0.')
46
+ id: npm-9
47
+ run: |
48
+ npm i --prefer-online --no-fund --no-audit -g npm@9
49
+ echo "updated=true" >> "$GITHUB_OUTPUT"
50
+
51
+ - name: Install npm@latest on Node
52
+ if: $\{{ !(steps.npm-7.outputs.updated || steps.npm-8.outputs.updated || steps.npm-9.outputs.updated) }}
53
+ run: npm i --prefer-online --no-fund --no-audit -g npm@latest
54
+
29
55
  - name: npm Version
30
56
  run: npm -v
31
57
  {{/if}}
@@ -7,7 +7,7 @@ on:
7
7
  ref:
8
8
  required: true
9
9
  type: string
10
- default: {{ defaultBranch }}
10
+ default: {{ baseBranch }}
11
11
  workflow_call:
12
12
  inputs:
13
13
  ref:
@@ -3,12 +3,12 @@ name: CodeQL
3
3
  on:
4
4
  push:
5
5
  branches:
6
- {{#each branches}}
6
+ {{#each branchPatterns}}
7
7
  - {{ . }}
8
8
  {{/each}}
9
9
  pull_request:
10
10
  branches:
11
- {{#each branches}}
11
+ {{#each branchPatterns}}
12
12
  - {{ . }}
13
13
  {{/each}}
14
14
  schedule:
@@ -1,15 +1,24 @@
1
1
  version: 2
2
2
 
3
3
  updates:
4
+ {{#each dependabot}}
4
5
  - package-ecosystem: npm
5
- directory: {{ pkgDir }}
6
+ directory: /
6
7
  schedule:
7
8
  interval: daily
9
+ target-branch: "{{ branch }}"
8
10
  allow:
9
11
  - dependency-type: direct
10
- versioning-strategy: {{ dependabot }}
12
+ {{#each allowNames }}
13
+ dependency-name: "{{ . }}"
14
+ {{/each}}
15
+ versioning-strategy: {{ strategy }}
11
16
  commit-message:
12
17
  prefix: deps
13
18
  prefix-development: chore
14
19
  labels:
15
20
  - "Dependencies"
21
+ {{#each labels }}
22
+ - "{{ . }}"
23
+ {{/each}}
24
+ {{/each}}
@@ -38,14 +38,6 @@ const sharedRootAdd = (name) => ({
38
38
  '.github/dependabot.yml': {
39
39
  file: 'dependabot.yml',
40
40
  filter: (p) => p.config.dependabot,
41
- clean: (p) => p.config.isRoot,
42
- // dependabot takes a single top level config file. this parser
43
- // will run for all configured packages and each one will have
44
- // its item replaced in the updates array based on the directory
45
- parser: (p) => class extends p.YmlMerge {
46
- key = 'updates'
47
- id = 'directory'
48
- },
49
41
  },
50
42
  '.github/workflows/post-dependabot.yml': {
51
43
  file: 'post-dependabot.yml',
@@ -53,6 +45,7 @@ const sharedRootAdd = (name) => ({
53
45
  },
54
46
  '.github/settings.yml': {
55
47
  file: 'settings.yml',
48
+ filter: (p) => !p.config.isReleaseBranch,
56
49
  },
57
50
  })
58
51
 
@@ -60,6 +53,9 @@ const sharedRootRm = () => ({
60
53
  '.github/workflows/pull-request.yml': {
61
54
  filter: (p) => p.config.allPrivate,
62
55
  },
56
+ '.github/settings.yml': {
57
+ filter: (p) => p.config.isReleaseBranch,
58
+ },
63
59
  })
64
60
 
65
61
  // Changes applied to the root of the repo
@@ -139,8 +135,8 @@ module.exports = {
139
135
  workspaceModule,
140
136
  windowsCI: true,
141
137
  macCI: true,
142
- branches: ['main', 'latest', 'release/v*'],
143
- defaultBranch: 'main',
138
+ branches: ['main', 'latest'],
139
+ releaseBranch: 'release/v*',
144
140
  distPaths: [
145
141
  'bin/',
146
142
  'lib/',
@@ -167,7 +163,6 @@ module.exports = {
167
163
  publish: false,
168
164
  npm: 'npm',
169
165
  npx: 'npx',
170
- npmSpec: 'latest',
171
166
  updateNpm: true,
172
167
  dependabot: 'increase-if-necessary',
173
168
  unwantedPackages: [
@@ -21,4 +21,4 @@ jobs:
21
21
  env:
22
22
  PR_TITLE: $\{{ github.event.pull_request.title }}
23
23
  run: |
24
- echo '$PR_TITLE' | {{ rootNpxPath }} --offline commitlint -V
24
+ echo "$PR_TITLE" | {{ rootNpxPath }} --offline commitlint -V
@@ -8,7 +8,7 @@ on:
8
8
  type: string
9
9
  push:
10
10
  branches:
11
- {{#each branches}}
11
+ {{#each branchPatterns}}
12
12
  - {{ . }}
13
13
  {{/each}}
14
14
 
@@ -14,6 +14,7 @@ branches:
14
14
  protection:
15
15
  required_status_checks: null
16
16
  enforce_admins: true
17
+ block_creations: true
17
18
  required_pull_request_reviews:
18
19
  required_approving_review_count: 1
19
20
  require_code_owner_reviews: true
@@ -0,0 +1,27 @@
1
+ const { name: NAME } = require('../../package.json')
2
+ const { minimatch } = require('minimatch')
3
+
4
+ const parseDependabotConfig = (v) => typeof v === 'string' ? { strategy: v } : (v ?? {})
5
+
6
+ module.exports = (config, defaultConfig, branches) => {
7
+ const { dependabot } = config
8
+ const { dependabot: defaultDependabot } = defaultConfig
9
+
10
+ if (!dependabot) {
11
+ return false
12
+ }
13
+
14
+ return branches
15
+ .filter((b) => dependabot[b] !== false)
16
+ .map(branch => {
17
+ const isReleaseBranch = minimatch(branch, config.releaseBranch)
18
+ return {
19
+ branch,
20
+ allowNames: isReleaseBranch ? [NAME] : [],
21
+ labels: isReleaseBranch ? ['Backport', branch] : [],
22
+ ...parseDependabotConfig(defaultDependabot),
23
+ ...parseDependabotConfig(dependabot),
24
+ ...parseDependabotConfig(dependabot[branch]),
25
+ }
26
+ })
27
+ }
@@ -0,0 +1,82 @@
1
+ const hgi = require('hosted-git-info')
2
+ const git = require('@npmcli/git')
3
+ const { minimatch } = require('minimatch')
4
+
5
+ const cache = new Map()
6
+
7
+ const tryGit = async (path, ...args) => {
8
+ if (!await git.is({ cwd: path })) {
9
+ throw new Error('no git')
10
+ }
11
+ const key = [path, ...args].join(',')
12
+ if (cache.has(key)) {
13
+ return cache.get(key)
14
+ }
15
+ const res = git.spawn(args, { cwd: path }).then(r => r.stdout.trim())
16
+ cache.set(key, res)
17
+ return res
18
+ }
19
+
20
+ // parse a repo from a git origin into a format
21
+ // for a package.json#repository object
22
+ const getUrl = async (path) => {
23
+ try {
24
+ const urlStr = await tryGit(path, 'remote', 'get-url', 'origin')
25
+ const { domain, user, project } = hgi.fromUrl(urlStr)
26
+ const url = new URL(`https://${domain}`)
27
+ url.pathname = `/${user}/${project}.git`
28
+ return url.toString()
29
+ } catch {
30
+ // errors are ignored
31
+ }
32
+ }
33
+
34
+ const getBranches = async (path, branchPatterns) => {
35
+ let matchingBranches = new Set()
36
+ let matchingPatterns = new Set()
37
+
38
+ try {
39
+ const res = await tryGit(path, 'ls-remote', '--heads', 'origin').then(r => r.split('\n'))
40
+ const remotes = res.map((h) => h.match(/refs\/heads\/(.*)$/)).filter(Boolean).map(h => h[1])
41
+ for (const branch of remotes) {
42
+ for (const pattern of branchPatterns) {
43
+ if (minimatch(branch, pattern)) {
44
+ matchingBranches.add(branch)
45
+ matchingPatterns.add(pattern)
46
+ }
47
+ }
48
+ }
49
+ } catch {
50
+ matchingBranches = new Set(branchPatterns.filter(b => !b.includes('*')))
51
+ matchingPatterns = new Set(branchPatterns)
52
+ }
53
+
54
+ return {
55
+ branches: [...matchingBranches],
56
+ patterns: [...matchingPatterns],
57
+ }
58
+ }
59
+
60
+ const defaultBranch = async (path) => {
61
+ try {
62
+ const remotes = await tryGit(path, 'remote', 'show', 'origin')
63
+ return remotes.match(/HEAD branch: (.*)$/m)?.[1]
64
+ } catch {
65
+ // ignore errors
66
+ }
67
+ }
68
+
69
+ const currentBranch = async (path) => {
70
+ try {
71
+ return await tryGit(path, 'rev-parse', '--abbrev-ref', 'HEAD')
72
+ } catch {
73
+ // ignore errors
74
+ }
75
+ }
76
+
77
+ module.exports = {
78
+ getUrl,
79
+ getBranches,
80
+ defaultBranch,
81
+ currentBranch,
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npmcli/template-oss",
3
- "version": "4.17.0",
3
+ "version": "4.18.1",
4
4
  "description": "templated files used in npm CLI team oss projects",
5
5
  "main": "lib/content/index.js",
6
6
  "bin": {
@@ -1,26 +0,0 @@
1
- const hgi = require('hosted-git-info')
2
- const git = require('@npmcli/git')
3
-
4
- // parse a repo from a git origin into a format
5
- // for a package.json#repository object
6
- const getRepo = async (path) => {
7
- if (!await git.is({ cwd: path })) {
8
- return
9
- }
10
-
11
- try {
12
- const res = await git.spawn([
13
- 'remote',
14
- 'get-url',
15
- 'origin',
16
- ], { cwd: path })
17
- const { domain, user, project } = hgi.fromUrl(res.stdout.trim())
18
- const url = new URL(`https://${domain}`)
19
- url.pathname = `/${user}/${project}.git`
20
- return url.toString()
21
- } catch {
22
- // errors are ignored
23
- }
24
- }
25
-
26
- module.exports = getRepo