diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 2555c287..36f007e8 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -27,8 +27,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 6bf6fb4f..6169a73c 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -82,8 +82,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -167,6 +167,7 @@ jobs: - 16.x - 18.0.0 - 18.x + - 20.x runs-on: ${{ matrix.platform.os }} defaults: run: diff --git a/.github/workflows/ci-test-workspace.yml b/.github/workflows/ci-test-workspace.yml index 81fc5fb9..2f7d6684 100644 --- a/.github/workflows/ci-test-workspace.yml +++ b/.github/workflows/ci-test-workspace.yml @@ -35,8 +35,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -113,6 +113,7 @@ jobs: - 16.x - 18.0.0 - 18.x + - 20.x runs-on: ${{ matrix.platform.os }} defaults: run: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9b98ea3..ec4a5c1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,8 +35,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -113,6 +113,7 @@ jobs: - 16.x - 18.0.0 - 18.x + - 20.x runs-on: ${{ matrix.platform.os }} defaults: run: diff --git a/.github/workflows/post-dependabot.yml b/.github/workflows/post-dependabot.yml index c8898838..c144660e 100644 --- a/.github/workflows/post-dependabot.yml +++ b/.github/workflows/post-dependabot.yml @@ -28,8 +28,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index aac7cb6b..63a6169f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -31,8 +31,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb16e1da..805cce60 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,8 +44,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -201,8 +201,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm diff --git a/lib/config.js b/lib/config.js index 3e461a5c..2ef1c11e 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,11 +1,10 @@ const { relative, dirname, join, extname, posix, win32 } = require('path') -const { defaults, pick, omit, uniq } = require('lodash') -const semver = require('semver') -const parseCIVersions = require('./util/parse-ci-versions.js') +const { defaults, pick, omit, uniq, isPlainObject } = require('lodash') +const ciVersions = require('./util/ci-versions.js') const parseDependabot = require('./util/dependabot.js') const git = require('./util/git.js') const gitignore = require('./util/gitignore.js') -const { mergeWithArrays } = require('./util/merge.js') +const { mergeWithCustomizers, customizers } = require('./util/merge.js') const { FILE_KEYS, parseConfig: parseFiles, getAddedFiles, mergeFiles } = require('./util/files.js') const CONFIG_KEY = 'templateOSS' @@ -16,7 +15,14 @@ const { minimatch } = require('minimatch') const MERGE_KEYS = [...FILE_KEYS, 'defaultContent', 'content'] const DEFAULT_CONTENT = require.resolve(NAME) -const merge = mergeWithArrays('branches', 'distPaths', 'allowPaths', 'ignorePaths') +const merge = mergeWithCustomizers( + customizers.mergeArrays('branches', 'distPaths', 'allowPaths', 'ignorePaths'), + (value, srcValue, key) => { + if (key === 'ciVersions' && (Array.isArray(srcValue) || isPlainObject(srcValue))) { + return { ...ciVersions.parse(value), ...ciVersions.parse(srcValue) } + } + } +) const makePosix = (v) => v.split(win32.sep).join(posix.sep) const deglob = (v) => makePosix(v).replace(/[/*]+$/, '') @@ -218,6 +224,8 @@ const getFullConfig = async ({ // lockfiles are only present at the root, so this only should be set for // all workspaces based on the root lockfile: rootPkgConfig.lockfile, + // ci versions / engines + ciVersions: ciVersions.get(pkg.pkgJson.engines?.node, pkgConfig), // gitignore ignorePaths: [ ...gitignore.sort([ @@ -242,33 +250,6 @@ const getFullConfig = async ({ __PARTIAL_DIRS__: fileDirs, } - if (pkgConfig.ciVersions) { - let versions = pkgConfig.ciVersions - if (versions === 'latest' || (Array.isArray(versions) && versions.includes('latest'))) { - const { ciVersions } = [isWorkspace ? rootPkgConfig : {}, defaultConfig] - .find(c => Array.isArray(c.ciVersions)) - const defaultLatest = ciVersions[ciVersions.length - 1] - versions = [].concat(versions).map(v => v === 'latest' ? defaultLatest : v) - } - - const { targets, engines } = parseCIVersions(versions) - - // get just a list of the target versions (not ranges) - // these are used for the node version when doing engines checks - // since we want to test in the lowest version of each major - let targetVersions = targets.filter(t => semver.valid(t)) - // if the versions are all ranges then convert them to the lower bound of each range - if (!targetVersions.length) { - targetVersions = targets.filter(t => semver.validRange(t)).map(t => { - return new semver.Range(t).set[0][0].semver.version - }) - } - - derived.ciVersions = targets - derived.baseCiVersions = targetVersions - derived.engines = pkgConfig.engines || engines - } - if (!pkgConfig.eslint) { derived.ignorePaths = derived.ignorePaths.filter(p => !p.includes('eslint')) if (Array.isArray(pkgConfig.requiredPackages?.devDependencies)) { diff --git a/lib/content/index.js b/lib/content/index.js index 37b0f98e..5cbb17b8 100644 --- a/lib/content/index.js +++ b/lib/content/index.js @@ -156,7 +156,8 @@ module.exports = { '/CHANGELOG*', ], ignorePaths: [], - ciVersions: ['14.17.0', '14.x', '16.13.0', '16.x', '18.0.0', '18.x'], + ciVersions: {}, + latestCiVersion: 20, lockfile: false, codeowner: '@npm/cli-team', eslint: true, diff --git a/lib/content/pkg.json b/lib/content/pkg.json index 631f5dbd..59d33551 100644 --- a/lib/content/pkg.json +++ b/lib/content/pkg.json @@ -21,11 +21,6 @@ "postpublish": {{{ del }}} }, "repository": {{#if repository}}{{{ json repository }}}{{else}}{{{ del }}}{{/if}}, - "engines": { - {{#if engines}} - "node": {{{ json engines }}} - {{/if}} - }, {{{ json __CONFIG_KEY__ }}}: { "version": {{#if isDogFood}}{{{ del }}}{{else}}{{{ json __VERSION__ }}}{{/if}} }, diff --git a/lib/util/ci-versions.js b/lib/util/ci-versions.js new file mode 100644 index 00000000..ac42e96f --- /dev/null +++ b/lib/util/ci-versions.js @@ -0,0 +1,80 @@ +const { uniq, range, isPlainObject } = require('lodash') +const semver = require('semver') + +const parseCiVersions = (ciVersions) => { + if (Array.isArray(ciVersions)) { + return Object.fromEntries(ciVersions.map((v) => [v, true])) + } + if (isPlainObject(ciVersions)) { + return ciVersions + } +} + +const getLowerBounds = (sRange) => { + return new semver.Range(sRange).set.map(c => c[0]) +} + +const getCiVersions = (nodeEngines, pkgConfig) => { + let allCiVersions = {} + + // get ci versions + const { latestCiVersion, ciVersions } = pkgConfig + + if (latestCiVersion) { + allCiVersions[`${latestCiVersion}.x`] = true + } + + // determine the ci versions from the node engines set + if (nodeEngines) { + const lowerBounds = getLowerBounds(nodeEngines) + .map(v => v.semver) + .filter(v => v.version) + + for (const version of lowerBounds) { + allCiVersions[version.version] = true + allCiVersions[`${version.major}.x`] = true + } + + const lowestCiVersion = semver.sort(lowerBounds)[0]?.major + if (lowestCiVersion && latestCiVersion) { + for (const major of range(lowestCiVersion, latestCiVersion, 2)) { + allCiVersions[`${major}.x`] = true + } + } + } + + if (ciVersions === 'latest' && latestCiVersion) { + // the plain string 'latest' means latest only and everything else is removed + allCiVersions = { [`${latestCiVersion}.x`]: true } + } else { + // this allows ciVersions to turn off default versions by setting them to a falsy value + Object.assign(allCiVersions, parseCiVersions(ciVersions)) + } + + if (allCiVersions.latest && latestCiVersion) { + delete allCiVersions.latest + allCiVersions[`${latestCiVersion}.x`] = true + } + + const filteredCiVersions = Object.entries(allCiVersions) + .filter(([, v]) => v) + .map(([k]) => k) + + return uniq(filteredCiVersions).sort((a, b) => { + const aComp = getLowerBounds(a)[0] + const bComp = getLowerBounds(b)[0] + + if (aComp.semver.major > bComp.semver.major) { + return 1 + } else if (aComp.semver.major < bComp.semver.major) { + return -1 + } + + return aComp.operator ? 1 : -1 + }) +} + +module.exports = { + parse: parseCiVersions, + get: getCiVersions, +} diff --git a/lib/util/merge.js b/lib/util/merge.js index 6395b925..05a24300 100644 --- a/lib/util/merge.js +++ b/lib/util/merge.js @@ -65,7 +65,6 @@ const customizers = { module.exports = { // default merge is to overwrite arrays merge: mergeWithCustomizers(customizers.overwriteArrays), - mergeWithArrays: (...keys) => mergeWithCustomizers(customizers.mergeArrays(...keys)), mergeWithCustomizers, mergeWith, customizers, diff --git a/lib/util/parse-ci-versions.js b/lib/util/parse-ci-versions.js deleted file mode 100644 index 7a1bf0e3..00000000 --- a/lib/util/parse-ci-versions.js +++ /dev/null @@ -1,78 +0,0 @@ -const semver = require('semver') -const { partition, uniq, groupBy } = require('lodash') - -// try to parse a version. if its invalid then -// try to parse it as a range instead -const versionOrRange = (v) => semver.parse(v) || new semver.Range(v) - -// get the version or the upper bound of the range -// used for sorting to give the latest ci target -const getMaxVersion = (v) => v.version || v.set[0][1].semver.version - -// given an array of versions, returns an object where -// each key is a major and each value is a sorted list of versions -const versionsByMajor = (versions) => { - const majors = groupBy(versions, (v) => semver.major(v)) - for (const [k, vs] of Object.entries(majors)) { - majors[k] = semver.sort(vs)[0] - } - return majors -} - -// given a list of semver ci targets like: -// ['12.13.0', '12.x', '14.15.0', '14.x', '16.0.0', '16.x'] -// this will parse into a uniq list of lowest "supported" -// versions. In our cases so that will return -// '^12.13.0 || ^14.15.0 || >=16'. This is not super generic but fits -// our use case for now where we want to test on a bunch of -// specific versions/ranges and map them to somewhat loose -// semver range for package.json#engines.node. This only supports -// returning ^ ranges and makes the last version >= currently. -// -// Assumptions: -// - ranges span a single major version -// - specific versions are lower then the upper bound of -// ranges within the same major version -const parseCITargets = (targets = []) => { - const [versions, ranges] = partition( - targets.map((t) => versionOrRange(t)), - (t) => t.version - ) - - const sorted = [...versions, ...ranges] - .sort((a, b) => semver.compareBuild(getMaxVersion(a), getMaxVersion(b))) - .map((v) => v.version || v.raw) - - // object of {major: lowestVersion } for all passed in versions - const minVersions = versionsByMajor(versions) - - // object of {major: lowestVersionInRange } for all passed in ranges - const minRanges = versionsByMajor(ranges.map((r) => semver.minVersion(r))) - - // Given all the uniq major versions in targets... - const parsedRanges = uniq([...Object.keys(minVersions), ...Object.keys(minRanges)]) - // first sort by major to make it display nicer - .sort((a, b) => Number(a) - Number(b)) - .map((major) => { - const minVersion = minVersions[major] - const minRange = minRanges[major] - // if we only have one then return that - if (!minVersion || !minRange) { - return minVersion || minRange - } - // otherwise return min version - // XXX: this assumes the versions are lower than the upper - // bound for any range for the same major. This is ok for - // now but will break with more complex/specific semver ranges - return minVersion - }) - // make the last version allow all greater than - .map((v, index, list) => (index === list.length - 1 ? '>=' : '^') + v) - - return { - targets: sorted, - engines: parsedRanges.join(' || '), - } -} - -module.exports = parseCITargets diff --git a/tap-snapshots/test/apply/source-snapshots.js.test.cjs b/tap-snapshots/test/apply/source-snapshots.js.test.cjs index 7b2e17b5..ec1bf3d9 100644 --- a/tap-snapshots/test/apply/source-snapshots.js.test.cjs +++ b/tap-snapshots/test/apply/source-snapshots.js.test.cjs @@ -252,8 +252,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -393,8 +393,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -472,12 +472,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -637,8 +632,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -709,12 +704,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -860,8 +850,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -1031,8 +1021,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -1143,8 +1133,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -1300,8 +1290,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -1510,8 +1500,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -1768,9 +1758,6 @@ package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -2108,8 +2095,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -2204,8 +2191,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -2276,12 +2263,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -2393,8 +2375,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -2465,12 +2447,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -2627,8 +2604,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -2706,12 +2683,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -2877,8 +2849,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -2949,12 +2921,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -3100,8 +3067,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -3271,8 +3238,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -3383,8 +3350,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -3540,8 +3507,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -3750,8 +3717,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -4020,9 +3987,6 @@ package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -4163,9 +4127,6 @@ workspaces/a/package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -4241,9 +4202,6 @@ workspaces/b/package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -4412,8 +4370,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -4484,12 +4442,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -4601,8 +4554,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -4673,12 +4626,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -4835,8 +4783,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -4914,12 +4862,7 @@ jobs: os: windows-latest shell: cmd node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x + - 20.x runs-on: \${{ matrix.platform.os }} defaults: run: @@ -5074,8 +5017,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -5245,8 +5188,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -5357,8 +5300,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -5514,8 +5457,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -5724,8 +5667,8 @@ jobs: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -5983,9 +5926,6 @@ workspaces/a/package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -6061,9 +6001,6 @@ workspaces/b/package.json "bin/", "lib/" ], - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" diff --git a/tap-snapshots/test/check/diff-snapshots.js.test.cjs b/tap-snapshots/test/check/diff-snapshots.js.test.cjs index 0961c73c..d6574da4 100644 --- a/tap-snapshots/test/check/diff-snapshots.js.test.cjs +++ b/tap-snapshots/test/check/diff-snapshots.js.test.cjs @@ -153,8 +153,8 @@ The repo file audit.yml needs to be updated: uses: actions/setup-node@v3 id: node with: - node-version: 18.x - check-latest: contains('18.x', '.x') + node-version: 20.x + check-latest: contains('20.x', '.x') # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows - name: Update Windows npm @@ -217,7 +217,7 @@ The repo file ci.yml needs to be updated: .github/workflows/ci.yml ======================================== - @@ -158,4 +158,25 @@ + @@ -159,4 +159,25 @@ id: npm-8 run: | npm i --prefer-online --no-fund --no-audit -g npm@8 diff --git a/tap-snapshots/test/check/snapshots.js.test.cjs b/tap-snapshots/test/check/snapshots.js.test.cjs index c7bdff3f..0c2c6c71 100644 --- a/tap-snapshots/test/check/snapshots.js.test.cjs +++ b/tap-snapshots/test/check/snapshots.js.test.cjs @@ -68,9 +68,6 @@ The module file package.json needs to be updated: package.json ======================================== "author" is missing, expected "GitHub Inc." - "engines" is missing, expected { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } "files" is missing, expected [ "bin/", "lib/" @@ -319,9 +316,6 @@ The module file package.json needs to be updated: package.json ======================================== "author" is missing, expected "GitHub Inc." - "engines" is missing, expected { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } "files" is missing, expected [ "bin/", "lib/" @@ -398,9 +392,6 @@ The module file package.json needs to be updated: workspaces/a/package.json ======================================== "author" is missing, expected "GitHub Inc." - "engines" is missing, expected { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } "files" is missing, expected [ "bin/", "lib/" @@ -470,9 +461,6 @@ The module file package.json needs to be updated: workspaces/b/package.json ======================================== "author" is missing, expected "GitHub Inc." - "engines" is missing, expected { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } "files" is missing, expected [ "bin/", "lib/" diff --git a/test/apply/engines.js b/test/apply/engines.js index d34246ea..bd363a10 100644 --- a/test/apply/engines.js +++ b/test/apply/engines.js @@ -1,90 +1,118 @@ const t = require('tap') const { join } = require('path') +const yaml = require('yaml') const setup = require('../setup.js') -t.test('can set engines and ci separately', async (t) => { +const getCiJobs = async (s) => { + const file = await s.readFile(join('.github', 'workflows', 'ci.yml')) + const { jobs } = yaml.parse(file) + return { + lint: jobs.lint.steps[2].with['node-version'], + test: jobs.test.strategy.matrix['node-version'], + } +} + +t.test('sets ci versions from engines', async (t) => { const s = await setup(t, { package: { + engines: { node: '>=10' }, templateOSS: { - engines: '>=10', + ciVersions: { + '12.x': false, + }, }, }, }) await s.apply() const pkg = await s.readJson('package.json') - const ci = await s.readFile(join('.github', 'workflows', 'ci.yml')) - t.equal(pkg.engines.node, '>=10') - t.notOk(ci.includes('- 10')) - t.notOk(ci.includes('- 12')) + + const versions = await getCiJobs(s) + t.equal(versions.lint, '20.x') + t.strictSame(versions.test, [ + '10.0.0', + '10.x', + '14.x', + '16.x', + '18.x', + '20.x', + ]) }) t.test('can set ci to latest plus other versions', async (t) => { const s = await setup(t, { package: { + engines: { node: '*' }, templateOSS: { - ciVersions: ['6', '8', 'latest'], - engines: '*', + ciVersions: ['6.x', '8.x', 'latest'], }, }, }) await s.apply() const pkg = await s.readJson('package.json') - const ci = await s.readFile(join('.github', 'workflows', 'ci.yml')) - t.equal(pkg.engines.node, '*') - t.ok(ci.includes('- 6')) - t.ok(ci.includes('- 8')) - t.ok(ci.includes('- 18.x')) + + const versions = await getCiJobs(s) + t.equal(versions.lint, '20.x') + t.strictSame(versions.test, [ + '6.x', + '8.x', + '20.x', + ]) }) -t.test('latest ci versions', async (t) => { +t.test('sort by major', async (t) => { const s = await setup(t, { package: { + engines: { node: '*' }, templateOSS: { - ciVersions: 'latest', + latestCiVersion: null, + ciVersions: [ + '7.x', + '6.0.0', + '6.x', + '7.0.0', + '8.x', + '8.0.0', + ], }, }, }) await s.apply() const pkg = await s.readJson('package.json') + t.equal(pkg.engines.node, '*') - t.equal(pkg.engines.node, '>=18.0.0') + const versions = await getCiJobs(s) + t.equal(versions.lint, '8.x') + t.strictSame(versions.test, [ + '6.0.0', + '6.x', + '7.0.0', + '7.x', + '8.0.0', + '8.x', + ]) }) -t.test('latest ci versions in workspace', async (t) => { +t.test('latest ci versions', async (t) => { const s = await setup(t, { package: { templateOSS: { - content: 'content', - ciVersions: ['12.x', '14.x', '16.x'], - }, - }, - workspaces: { - a: { - templateOSS: { - ciVersions: 'latest', - }, - }, - }, - testdir: { - content: { - 'source.json': '{ "node": {{{ json engines }}} }', - 'index.js': `module.exports={ - rootRepo:{add:{'target.json':'source.json'}}, - workspaceRepo:{add:{'target-{{ pkgNameFs }}.json':'source.json'}} - }`, + ciVersions: 'latest', }, }, }) await s.apply() - const root = await s.readJson('target.json') - const workspace = await s.readJson('target-a.json') + const pkg = await s.readJson('package.json') + t.equal(pkg.engines, undefined) - t.equal(root.node, '^12.0.0 || ^14.0.0 || >=16.0.0') - t.equal(workspace.node, '>=16.0.0') + const versions = await getCiJobs(s) + t.equal(versions.lint, '20.x') + t.strictSame(versions.test, [ + '20.x', + ]) }) diff --git a/test/bin/apply.js b/test/bin/apply.js index ab4d2981..9c80e2f9 100644 --- a/test/bin/apply.js +++ b/test/bin/apply.js @@ -22,7 +22,9 @@ t.afterEach(() => { process.env.npm_config_global = _global process.env.npm_config_local_prefix = _prefix global.console = _console - delete process.exitCode + if (process.exitCode) { + process.exitCode = 0 + } }) t.test('when npm_config_local_prefix is unset, does nothing', async (t) => { diff --git a/test/bin/check.js b/test/bin/check.js index 12aba611..40b3704b 100644 --- a/test/bin/check.js +++ b/test/bin/check.js @@ -20,7 +20,9 @@ t.afterEach(() => { process.env.npm_config_local_prefix = _prefix errors.length = 0 global.console = _console - delete process.exitCode + if (process.exitCode) { + process.exitCode = 0 + } }) t.test('no local prefix', async (t) => { diff --git a/test/check/engines.js b/test/check/engines.js index 722ea153..7e5741c1 100644 --- a/test/check/engines.js +++ b/test/check/engines.js @@ -4,8 +4,8 @@ const setup = require('../setup.js') const setupEngines = ({ engines, omitEngines }) => setup(t, { ok: true, package: { + engines: { node: engines }, templateOSS: { - engines, omitEngines, }, dependencies: { diff --git a/test/setup.js b/test/setup.js index 92b52bb5..32c817de 100644 --- a/test/setup.js +++ b/test/setup.js @@ -33,6 +33,9 @@ const okPackage = () => Object.entries(CONTENT.requiredPackages) })) return acc }, { + engines: { + node: '^14.17.0 || ^16.13.0 || >=18.0.0', + }, tap: { 'nyc-arg': [ '--exclude', @@ -107,8 +110,8 @@ const setup = async (t, { } = {}) => { const wsLookup = {} const pkg = merge( - pkgWithName(package, 'testpkg'), - ok ? okPackage() : {} + ok ? okPackage() : {}, + pkgWithName(package, 'testpkg') ) // convenience for passing in workspaces as an object diff --git a/test/util/parse-ci-versions.js b/test/util/parse-ci-versions.js deleted file mode 100644 index 85226e76..00000000 --- a/test/util/parse-ci-versions.js +++ /dev/null @@ -1,30 +0,0 @@ -const t = require('tap') -const parse = require('../../lib/util/parse-ci-versions.js') - -const targets = [ - [[], ''], - [undefined, ''], - [['12.1.0'], '>=12.1.0'], - [['12.1.0', '12.2.0', '12'], '>=12.1.0'], - [['12'], '>=12.0.0'], - [['~12.5'], '>=12.5.0'], - [['12.99.0', '12.1.1'], '>=12.1.1'], - [['12.1.0', '12.x', '18.5.0'], '^12.1.0 || >=18.5.0'], - [['12.1.0', '12.x', '^18'], '^12.1.0 || >=18.0.0', '^18'], - [['12.1.0', '12.2.0', '~12.5'], '>=12.1.0', '~12.5'], - [['12.13.0', '12.x', '14.15.0', '14.x', '16.0.0', '16.x'], - '^12.13.0 || ^14.15.0 || >=16.0.0', '16.x'], - // this is to test current behavior but these go against - // the assumption that provided versions will always be lower - // then the upper bound of the range - [['12.99.0', '12.1.x'], '>=12.99.0'], - [['12.99.0', '12.100.0', '12.50.0', '12.2.x', '12.5.x'], '>=12.50.0'], -] - -targets.forEach(([target, engine, max]) => { - const found = parse(target) - t.equal(found.engines, engine, `${target} => ${engine}`) - if (max) { - t.equal(found.targets[found.targets.length - 1], max, 'sorted max version') - } -})