diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 79689b41..a514b609 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,10 +14,10 @@ permissions: jobs: release-please: - runs-on: ubuntu-latest outputs: pr: ${{ steps.release.outputs.pr }} release: ${{ steps.release.outputs.release }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup git user @@ -73,7 +73,7 @@ jobs: release-test: needs: post-pr if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release-test.yml + uses: ./.github/workflows/release.yml with: ref: ${{ needs.post-pr.outputs.ref }} diff --git a/.github/workflows/release-test.yml b/.github/workflows/release.yml similarity index 94% rename from .github/workflows/release-test.yml rename to .github/workflows/release.yml index db19c2b4..a608cd96 100644 --- a/.github/workflows/release-test.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint --if-present --workspaces --include-workspace-root + - run: npm run lint -ws -iwr --if-present test-all: strategy: @@ -83,4 +83,4 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test --if-present --workspaces --include-workspace-root + - run: npm run test -ws -iwr --if-present diff --git a/.gitignore b/.gitignore index 7ea0cfe4..0ec3c847 100644 --- a/.gitignore +++ b/.gitignore @@ -4,25 +4,25 @@ /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.commitlintrc.js !/.eslintrc.js +!/.eslintrc.local.* !/.github/ !/.gitignore !/.npmrc !/.release-please-manifest.json -!/CODE_OF_CONDUCT.md -!/SECURITY.md !/bin/ +!/CHANGELOG* +!/CODE_OF_CONDUCT.md +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* !/release-please-config.json +!/scripts/ +!/SECURITY.md +!/tap-snapshots/ +!/test/ diff --git a/lib/apply/apply-files.js b/lib/apply/apply-files.js index 920e24a7..c8925b9e 100644 --- a/lib/apply/apply-files.js +++ b/lib/apply/apply-files.js @@ -3,7 +3,7 @@ const log = require('proc-log') const { rmEach, parseEach } = require('../util/files.js') const run = async (dir, files, options) => { - const { rm = [], add = {} } = files + const { rm, add } = files log.verbose('apply-files', 'rm', rm) await rmEach(dir, rm, options, (f) => fs.rm(f)) diff --git a/lib/apply/index.js b/lib/apply/index.js index 80905d93..75e66a43 100644 --- a/lib/apply/index.js +++ b/lib/apply/index.js @@ -1,6 +1,6 @@ const run = require('../index.js') -module.exports = (root, content) => run(root, content, [ +module.exports = (root) => run(root, [ require('./apply-files.js'), require('./apply-version.js'), ]) diff --git a/lib/check/check-apply.js b/lib/check/check-apply.js index aeae7b54..c76399bb 100644 --- a/lib/check/check-apply.js +++ b/lib/check/check-apply.js @@ -2,13 +2,14 @@ const log = require('proc-log') const { relative, basename } = require('path') const { rmEach, parseEach } = require('../util/files.js') const { partition } = require('lodash') +const localeCompare = require('@isaacs/string-locale-compare')('en') const solution = 'npx template-oss-apply --force' const run = async (type, dir, files, options) => { const res = [] const rel = (f) => relative(options.root, f) - const { add: addFiles = {}, rm: rmFiles = [] } = files + const { add: addFiles, rm: rmFiles } = files const rm = await rmEach(dir, rmFiles, options, (f) => rel(f)) const [add, update] = partition(await parseEach(dir, addFiles, options, async (p) => { @@ -28,7 +29,7 @@ const run = async (type, dir, files, options) => { if (rm.length) { res.push({ title: `The following ${type} files need to be deleted:`, - body: rm, + body: rm.sort(localeCompare), solution, }) } @@ -37,13 +38,13 @@ const run = async (type, dir, files, options) => { if (add.length) { res.push({ title: `The following ${type} files need to be added:`, - body: add, + body: add.sort(localeCompare), solution, }) } log.verbose('check-apply', 'update', update) - res.push(...update.map(({ file, diff }) => ({ + res.push(...update.sort((a, b) => localeCompare(a.file, b.file)).map(({ file, diff }) => ({ title: `The ${type} file ${basename(file)} needs to be updated:`, body: [`${file}\n${'='.repeat(40)}\n${diff}`], solution, diff --git a/lib/check/index.js b/lib/check/index.js index b3a24345..0a3083cc 100644 --- a/lib/check/index.js +++ b/lib/check/index.js @@ -1,6 +1,6 @@ const run = require('../index.js') -module.exports = (root, content) => run(root, content, [ +module.exports = (root) => run(root, [ require('./check-apply.js'), require('./check-required.js'), require('./check-unwanted.js'), diff --git a/lib/config.js b/lib/config.js index 0ee78f2f..d0a34079 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,101 +1,142 @@ -const { relative, dirname, join, posix, win32 } = require('path') -const log = require('proc-log') -const { uniq, defaults } = require('lodash') +const { relative, dirname, join, extname, posix, win32 } = require('path') +const { defaults, pick, omit, uniq } = require('lodash') const parseCIVersions = require('./util/parse-ci-versions.js') const getGitUrl = require('./util/get-git-url.js') -const { name: NAME, version: LATEST_VERSION } = require('../package.json') +const gitignore = require('./util/gitignore.js') +const { withArrays } = require('./util/merge.js') +const { FILE_KEYS, parseConfig: parseFiles, getAddedFiles } = require('./util/files.js') const CONFIG_KEY = 'templateOSS' const getPkgConfig = (pkg) => pkg[CONFIG_KEY] || {} -const getContent = (contentPath) => { - if (typeof contentPath === 'string') { - return defaults(require(contentPath), { - sourceDir: dirname(require.resolve(contentPath)), - }) - } else { - // allow passing in content directly for tests - return contentPath +const { name: NAME, version: LATEST_VERSION } = require('../package.json') +const MERGE_KEYS = [...FILE_KEYS, 'defaultContent', 'content'] +const DEFAULT_CONTENT = require.resolve(NAME) + +const merge = withArrays('branches', 'distPaths', 'allowPaths', 'ignorePaths') + +const makePosix = (str) => str.split(win32.sep).join(posix.sep) + +const getCmdPath = (key, { rootConfig, defaultConfig, isRoot, path, root }) => { + // Make a path relative from a workspace to the root if we are in a workspace + const wsToRoot = (p) => isRoot ? p : makePosix(join(relative(path, root), p)) + + const rootPath = rootConfig[key] + const defaultPath = defaultConfig[key] + const isLocal = rootPath && rootPath !== defaultPath + + return { + isLocal, + root: !isLocal ? defaultPath : `node ${rootPath}`, + local: !isLocal ? defaultPath : `node ${wsToRoot(rootPath)}`, } } -// falsy means no content of this type -const getFiles = (config, content) => config ? content : null -const getFileKeys = (files) => files ? Object.keys(files.add || {}) : [] -const negatePath = (p) => { - // XXX: this negates the first part of each path for the gitignore - // files. it might make sense to negate more specific portions of the - // path for some paths like workspaces. so instead of ignoring !/workspaces - // it would only ignore !/workspaces/a, !/workspaces/b, etc - const [first, ...parts] = p.split(posix.sep) - const isDir = parts.length > 0 - return `!${posix.sep}${first}${isDir ? posix.sep : ''}` +const mergeConfigs = (...configs) => { + const mergedConfig = merge(...configs.map(c => pick(c, MERGE_KEYS))) + return defaults(mergedConfig, { + defaultContent: DEFAULT_CONTENT, + // allow all file types by default + ...FILE_KEYS.reduce((acc, key) => { + acc[key] = true + return acc + }, {}), + }) } -const makePosix = (str) => str.split(win32.sep).join(posix.sep) +const readContentPath = (path) => { + if (!path) { + return {} + } -const getConfig = async ({ - pkgs, - workspaces, + let content = {} + const index = extname(path) === '.js' ? path : join(path, 'index.js') + const dir = dirname(index) + + try { + content = require(index) + } catch { + // its ok if this fails since the content dir + // might only be to provide other files. the + // index.js is optional + } + + return { content, dir } +} + +const getConfig = (path, rawConfig) => { + const config = omit(readContentPath(path).content, FILE_KEYS) + return merge(config, rawConfig ? omit(rawConfig, FILE_KEYS) : {}) +} + +const getFiles = (path, rawConfig) => { + const { content, dir } = readContentPath(path) + if (!dir) { + return [] + } + return [parseFiles(pick(content, FILE_KEYS), dir, pick(rawConfig, FILE_KEYS)), dir] +} + +const getFullConfig = async ({ root, path, pkg, - // default content path is looked up via require.resolve - // so use the name of this module since package.json#main - // points to the content dir - content: contentPath = NAME, - config: { - rootRepo, - rootModule, - workspaceRepo, - workspaceModule, - version, - ...pkgConfig // this includes config merged in from root - }, + pkgs, + workspaces, + rootConfig: _rootConfig, + pkgConfig: _pkgConfig, }) => { const isRoot = root === path - const isLatest = version === LATEST_VERSION + const isRootMono = isRoot && workspaces.length > 0 + const isLatest = _pkgConfig.version === LATEST_VERSION const isDogFood = pkg.name === NAME const isForce = process.argv.includes('--force') - const rawPkgConfig = getPkgConfig(pkg) // this is written to ci yml files so it needs to always use posix const pkgRelPath = makePosix(relative(root, path)) - const gitUrl = await getGitUrl(root) - const { - rootRepo: rootRepoContent, - rootModule: rootModuleContent, - workspaceRepo: workspaceRepoContent, - workspaceModule: workspaceModuleContent, - ...baseContent - } = getContent(contentPath) - - let repoFiles, moduleFiles - const ignorePaths = [] - - if (isRoot) { - repoFiles = getFiles(rootRepo, rootRepoContent) - moduleFiles = getFiles(rootModule, rootModuleContent) - ignorePaths.push( - // allow workspace paths if they are set, this is directly from - // map-workspaces so normalize to posix paths for gitignore - ...workspaces.map((p) => makePosix(relative(root, p))), - // allow both the repo and module files since this is the root - ...getFileKeys(repoFiles), - ...getFileKeys(moduleFiles), - // allow all workspace repo level files - ...pkgs.filter((p) => p.path !== path).flatMap((p) => - getFileKeys(getFiles(p.config.workspaceRepo, workspaceRepoContent)) - ) - ) - } else { - repoFiles = getFiles(workspaceRepo, workspaceRepoContent) - moduleFiles = getFiles(workspaceModule, workspaceModuleContent) - // In a workspace gitignores are relative to the workspace dir - // so we should only allow added module files - ignorePaths.push(...getFileKeys(moduleFiles)) - } + const workspacePkgs = pkgs.filter((p) => p.path !== path) + const workspaceDirs = isRootMono && workspaces.map((p) => makePosix(relative(root, p))) + const workspaceGlobs = isRootMono && pkg.workspaces.map(p => p.replace(/[/*]+$/, '')) + + // These config items are merged betweent the root and child workspaces and only come from + // the package.json because they can be used to read configs from other the content directories + const mergedConfig = mergeConfigs(_rootConfig, _pkgConfig) + + const defaultConfig = getConfig(DEFAULT_CONTENT) + const [defaultFiles, defaultDir] = getFiles(DEFAULT_CONTENT, mergedConfig) + const useDefault = mergedConfig.defaultContent && defaultConfig + + const rootConfig = getConfig(_rootConfig.content, _rootConfig) + const [rootFiles, rootDir] = getFiles(_rootConfig.content, mergedConfig) + + // The content config only gets set from the package we are in, it doesn't inherit + // anything from the root + const pkgConfig = merge(useDefault, getConfig(_pkgConfig.content, _pkgConfig)) + const [pkgFiles, pkgDir] = getFiles(mergedConfig.content, mergedConfig) + + // Files get merged in from the default content (that template-oss provides) as well + // as any content paths provided from the root or the workspace + const fileDirs = uniq([useDefault && defaultDir, rootDir, pkgDir].filter(Boolean)) + const files = merge(useDefault && defaultFiles, rootFiles, pkgFiles) + const repoFiles = isRoot ? files.rootRepo : files.workspaceRepo + const moduleFiles = isRoot ? files.rootModule : files.workspaceModule + + const allowRootDirs = [ + // Allways allow module files in root or workspaces + ...getAddedFiles(moduleFiles), + ...isRoot ? [ + // in the root allow all repo files + ...getAddedFiles(repoFiles), + // and allow all workspace repo level files + ...workspacePkgs.filter(p => p.config.workspaceRepo !== false).flatMap((p) => + getAddedFiles(files.workspaceRepo) + ), + ] : [], + ] + + const npmPath = getCmdPath('npm', { rootConfig, defaultConfig, isRoot, path, root }) + const npxPath = getCmdPath('npx', { rootConfig, defaultConfig, isRoot, path, root }) // all derived keys const derived = { @@ -106,7 +147,8 @@ const getConfig = async ({ // For these cases it is helpful to know if we are in a // monorepo since template-oss might be used only for // workspaces and not the root or vice versa. - isMono: (isRoot && workspaces.length > 0) || !isRoot, + isRootMono, + isMono: isRootMono || !isRoot, // repo repoDir: root, repoFiles, @@ -120,10 +162,31 @@ const getConfig = async ({ pkgNameFs: pkg.name.replace(/\//g, '-').replace(/@/g, ''), pkgRelPath: pkgRelPath, pkgPrivate: !!pkg.private, + pkgPublic: !pkg.private, + workspaces: workspaceDirs, + workspaceGlobs, // booleans to control application of updates isForce, isDogFood, isLatest, + // whether to install and update npm in ci + // only do this if we aren't using a custom path to bin + updateNpm: !npmPath.isLocal, + rootNpmPath: npmPath.root, + localNpmPath: npmPath.local, + rootNpxPath: npxPath.root, + // gitignore + ignorePaths: [ + ...gitignore.sort([ + ...gitignore.allowRootDir(allowRootDirs), + ...isRoot && pkgConfig.lockfile ? ['!/package-lock.json'] : [], + ...(pkgConfig.allowPaths || []).map((p) => `!${p}`), + ...(pkgConfig.ignorePaths || []), + ]), + // these cant be sorted since they rely on order + // to allow a previously ignored directoy + ...gitignore.allowDir(workspaceDirs || []), + ], // needs update if we are dogfooding this repo, with force argv, or its // behind the current version needsUpdate: isForce || isDogFood || !isLatest, @@ -131,55 +194,31 @@ const getConfig = async ({ __NAME__: NAME, __CONFIG_KEY__: CONFIG_KEY, __VERSION__: LATEST_VERSION, + __PARTIAL_DIRS__: fileDirs, } - // merge the rest of base and pkg content to make the - // full content object - const content = { ...baseContent, ...pkgConfig } - - // set some defaults on content that can be overwritten unlike - // derived values which are calculated from other config - const contentDefaults = {} - - if (content.npmBin && content.npmBin !== baseContent.npmBin) { - // make it relative to each workspace if they did not set the config themselves - if (!rawPkgConfig.npmBin) { - content.npmBin = makePosix(join(relative(path, root), content.npmBin)) - } - // a bit of a hack but allow custom node paths or no node path at all - // checks if the first thing has node somewhere in it and if it doesnt - // puts a system node in front of the script - const execPaths = content.npmBin.split(' ')[0].split(posix.sep) - if (execPaths.every(p => p !== 'node')) { - content.npmBin = `node ${content.npmBin}` - } - } - if (Array.isArray(content.ciVersions)) { - const parsed = parseCIVersions(content.ciVersions) - contentDefaults.engines = parsed.engines - content.ciVersions = parsed.targets - log.verbose('config ci', parsed) + if (pkgConfig.ciVersions) { + const versions = pkgConfig.ciVersions + const defaultVersions = defaultConfig.ciVersions + const parsed = parseCIVersions(versions === 'latest' ? defaultVersions.slice(-1) : versions) + derived.ciVersions = parsed.targets + derived.engines = pkgConfig.engines || parsed.engines } + const gitUrl = await getGitUrl(root) if (gitUrl) { - contentDefaults.repository = { + derived.repository = { type: 'git', url: gitUrl, ...(pkgRelPath ? { directory: pkgRelPath } : {}), } } - contentDefaults.ignorePaths = uniq( - [...ignorePaths, ...(content.distPaths || [])].map(negatePath) - ).sort() - - log.verbose('config', 'defaults', contentDefaults) - return { - ...defaults(content, contentDefaults), + ...pkgConfig, ...derived, } } -module.exports = getConfig +module.exports = getFullConfig module.exports.getPkgConfig = getPkgConfig diff --git a/lib/content/_setup-ci-on.yml b/lib/content/_setup-ci-on.yml new file mode 100644 index 00000000..4ba9bd59 --- /dev/null +++ b/lib/content/_setup-ci-on.yml @@ -0,0 +1,32 @@ +workflow_dispatch: +pull_request: + branches: + - '*' + {{#if pkgRelPath}} + paths: + - {{pkgRelPath}}/** + {{/if}} + {{#if workspaceGlobs}} + paths-ignore: + {{#each workspaceGlobs}} + - {{.}}/** + {{/each}} + {{/if}} +push: + branches: + {{#each branches}} + - {{.}} + {{/each}} + {{#if pkgRelPath}} + paths: + - {{pkgRelPath}}/** + {{/if}} + {{#if workspaceGlobs}} + paths-ignore: + {{#each workspaceGlobs}} + - {{.}}/** + {{/each}} + {{/if}} +schedule: + # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 + - cron: "0 9 * * 1" diff --git a/lib/content/_setup-deps.yml b/lib/content/_setup-deps.yml new file mode 100644 index 00000000..4e521a13 --- /dev/null +++ b/lib/content/_setup-deps.yml @@ -0,0 +1 @@ +- run: {{rootNpmPath}} i --ignore-scripts --no-audit --no-fund {{~#if flags}} {{flags}}{{/if}} diff --git a/lib/content/setup-git.yml b/lib/content/_setup-git.yml similarity index 85% rename from lib/content/setup-git.yml rename to lib/content/_setup-git.yml index 738ecbb6..3a874352 100644 --- a/lib/content/setup-git.yml +++ b/lib/content/_setup-git.yml @@ -1,7 +1,7 @@ - uses: actions/checkout@v3 -{{#if with}} +{{#if checkout}} with: - {{#each with}} + {{#each checkout}} {{@key}}: {{this}} {{/each}} {{/if}} diff --git a/lib/content/_setup-job-matrix.yml b/lib/content/_setup-job-matrix.yml new file mode 100644 index 00000000..d626f1f5 --- /dev/null +++ b/lib/content/_setup-job-matrix.yml @@ -0,0 +1,26 @@ +strategy: + fail-fast: false + matrix: + node-version: + {{#each ciVersions}} + - {{.}} + {{/each}} + platform: + - os: ubuntu-latest + shell: bash + {{#if macCI}} + - os: macos-latest + shell: bash + {{/if}} + {{#if windowsCI}} + - os: windows-latest + shell: cmd + {{/if}} +runs-on: $\{{ matrix.platform.os }} +defaults: + run: + shell: $\{{ matrix.platform.shell }} +steps: + {{> setupGit}} + {{> setupNode useMatrix=true}} + {{> setupDeps}} diff --git a/lib/content/_setup-job.yml b/lib/content/_setup-job.yml new file mode 100644 index 00000000..ad64a92d --- /dev/null +++ b/lib/content/_setup-job.yml @@ -0,0 +1,5 @@ +runs-on: ubuntu-latest +steps: + {{> setupGit}} + {{> setupNode}} + {{> setupDeps}} diff --git a/lib/content/setup-node.yml b/lib/content/_setup-node.yml similarity index 97% rename from lib/content/setup-node.yml rename to lib/content/_setup-node.yml index 7516e63b..e1fc29db 100644 --- a/lib/content/setup-node.yml +++ b/lib/content/_setup-node.yml @@ -4,6 +4,7 @@ {{#if lockfile}} cache: npm {{/if}} +{{#if updateNpm}} {{#if useMatrix}} - name: Update to workable npm (windows) # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows @@ -26,3 +27,4 @@ {{/if}} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v +{{/if}} diff --git a/lib/content/audit.yml b/lib/content/audit.yml index c5bb7068..73d4ca82 100644 --- a/lib/content/audit.yml +++ b/lib/content/audit.yml @@ -13,4 +13,4 @@ jobs: {{> setupGit}} {{> setupNode}} {{> setupDeps flags="--package-lock"}} - - run: npm audit + - run: {{rootNpmPath}} audit diff --git a/lib/content/ci.yml b/lib/content/ci.yml index 6d0c1a98..225bd5b4 100644 --- a/lib/content/ci.yml +++ b/lib/content/ci.yml @@ -1,61 +1,15 @@ name: CI {{~#if isWorkspace}} - {{pkgName}}{{/if}} on: - workflow_dispatch: - pull_request: - branches: - - '*' - {{#if pkgRelPath}} - paths: - - {{pkgRelPath}}/** - {{/if}} - push: - branches: - {{#each branches}} - - {{.}} - {{/each}} - {{#if pkgRelPath}} - paths: - - {{pkgRelPath}}/** - {{/if}} - schedule: - # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - - cron: "0 9 * * 1" + {{> setupCiOn}} jobs: lint: - runs-on: ubuntu-latest - steps: - {{> setupGit}} - {{> setupNode}} - {{> setupDeps}} - - run: npm run lint {{~#if isWorkspace}} -w {{pkgName}}{{/if}} + {{> setupJob }} + - run: {{rootNpmPath}} run lint {{~#if isWorkspace}} -w {{pkgName}}{{/if}} test: - strategy: - fail-fast: false - matrix: - node-version: - {{#each ciVersions}} - - {{.}} - {{/each}} - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - {{#if windowsCI}} - - os: windows-latest - shell: cmd - {{/if}} - runs-on: $\{{ matrix.platform.os }} - defaults: - run: - shell: $\{{ matrix.platform.shell }} - steps: - {{> setupGit}} - {{> setupNode useMatrix=true}} - {{> setupDeps}} + {{> setupJobMatrix }} - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts {{~#if isWorkspace}} -w {{pkgName}}{{/if}} + - run: {{rootNpmPath}} test --ignore-scripts {{~#if isWorkspace}} -w {{pkgName}}{{/if}} diff --git a/lib/content/dependabot.yml b/lib/content/dependabot.yml index e05db348..ca82a4a1 100644 --- a/lib/content/dependabot.yml +++ b/lib/content/dependabot.yml @@ -13,3 +13,21 @@ updates: prefix-development: chore labels: - "Dependencies" + + {{#if workspaces}} + {{#each workspaces}} + - package-ecosystem: npm + directory: "{{.}}/" + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + + {{/each}} + {{/if}} diff --git a/lib/content/eslintrc.js b/lib/content/eslintrc.js index ae9b6ef7..5bb7b067 100644 --- a/lib/content/eslintrc.js +++ b/lib/content/eslintrc.js @@ -8,6 +8,13 @@ const localConfigs = readdir(__dirname) module.exports = { root: true, + {{#if isRootMono}} + ignorePatterns: [ + {{#each workspaces}} + '{{.}}', + {{/each}} + ], + {{/if}} extends: [ '@npmcli', ...localConfigs, diff --git a/lib/content/gitignore b/lib/content/gitignore index 01684a6e..d85a1774 100644 --- a/lib/content/gitignore +++ b/lib/content/gitignore @@ -2,19 +2,6 @@ /* # keep these -!/.eslintrc.local.* -!**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* {{#each ignorePaths}} {{.}} {{/each}} -{{#if lockfile}} -!/package-lock.json -{{/if}} diff --git a/lib/content/index.js b/lib/content/index.js index ac5a4874..0df1d8b7 100644 --- a/lib/content/index.js +++ b/lib/content/index.js @@ -5,9 +5,9 @@ const releasePlease = () => ({ file: 'release-please.yml', filter: (o) => !o.pkg.private, }, - '.github/workflows/release-test.yml': { - file: 'release-test.yml', - filter: (o) => !o.pkg.private && o.config.releaseTest === 'release-test.yml', + '.github/workflows/release.yml': { + file: 'release.yml', + filter: (o) => !o.pkg.private, }, '.release-please-manifest.json': { file: 'release-please-manifest.json', @@ -25,22 +25,29 @@ const releasePlease = () => ({ }, }) +const tap = (name) => ({ + '.github/matchers/tap.json': 'tap.json', + [`.github/workflows/ci${name ? `-${name}` : ''}.yml`]: 'ci.yml', +}) + // Changes applied to the root of the repo const rootRepo = { add: { '.commitlintrc.js': 'commitlintrc.js', - '.github/workflows/ci.yml': 'ci.yml', '.github/ISSUE_TEMPLATE/bug.yml': 'bug.yml', '.github/ISSUE_TEMPLATE/config.yml': 'config.yml', '.github/CODEOWNERS': 'CODEOWNERS', '.github/dependabot.yml': 'dependabot.yml', - '.github/matchers/tap.json': 'tap.json', '.github/workflows/audit.yml': 'audit.yml', '.github/workflows/codeql-analysis.yml': 'codeql-analysis.yml', '.github/workflows/post-dependabot.yml': 'post-dependabot.yml', '.github/workflows/pull-request.yml': 'pull-request.yml', ...releasePlease(), + ...tap(), }, + rm: [ + '.github/workflows/release-test.yml', + ], } // These are also applied to the root of the repo @@ -65,9 +72,8 @@ const rootModule = { // Changes for each workspace but applied to the root of the repo const workspaceRepo = { add: { - ...releasePlease(true), - '.github/matchers/tap.json': 'tap.json', - '.github/workflows/ci-{{pkgNameFs}}.yml': 'ci.yml', + ...releasePlease(), + ...tap('{{pkgNameFs}}'), }, rm: [ // These are the old release please files that should be removed now @@ -95,17 +101,33 @@ module.exports = { workspaceRepo, workspaceModule, windowsCI: true, + macCI: true, branches: ['main', 'latest'], releaseBranches: [], defaultBranch: 'main', - // Escape hatch since we write a release test file but the - // CLI has a very custom one we dont want to overwrite. This - // setting allows us to call a workflow by any name during release - releaseTest: 'release-test.yml', - distPaths: ['bin/', 'lib/'], + distPaths: [ + 'bin/', + 'lib/', + ], + allowPaths: [ + '/bin/', + '/lib/', + '/.eslintrc.local.*', + '**/.gitignore', + '/docs/', + '/tap-snapshots/', + '/test/', + '/map.js', + '/scripts/', + '/README*', + '/LICENSE*', + '/CHANGELOG*', + ], + ignorePaths: [], ciVersions: ['14.17.0', '14.x', '16.13.0', '16.x', '18.0.0', '18.x'], lockfile: false, - npmBin: 'npm', + npm: 'npm', + npx: 'npx', unwantedPackages: [ 'eslint', 'eslint-plugin-node', diff --git a/lib/content/pkg.json b/lib/content/pkg.json index 43505d48..4335fdbd 100644 --- a/lib/content/pkg.json +++ b/lib/content/pkg.json @@ -5,24 +5,41 @@ "lint": "eslint \"**/*.js\"", "postlint": "template-oss-check", "template-oss-apply": "template-oss-apply --force", - "lintfix": "{{npmBin}} run lint -- --fix", + "lintfix": "{{localNpmPath}} run lint -- --fix", "preversion": {{{del}}}, "postversion": {{{del}}}, "prepublishOnly": {{{del}}}, "postpublish": {{{del}}}, "snap": "tap", "test": "tap", - "posttest": "{{npmBin}} run lint", + "posttest": "{{localNpmPath}} run lint", + {{#if isRootMono}} + "test-all": "{{localNpmPath}} run test -ws -iwr --if-present", + "lint-all": "{{localNpmPath}} run lint -ws -iwr --if-present", + {{/if}} "template-copy": {{{del}}}, "lint:fix": {{{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}} }, "templateVersion": {{{del}}}, - "standard": {{{del}}} + "standard": {{{del}}}, + "tap": { + {{#if isRootMono}} + "test-ignore": "^({{#each workspaceGlobs}}{{this}}{{#unless @last}}|{{/unless}}{{/each}})/", + {{/if}} + "nyc-arg": [ + {{#if isRootMono}} + {{#each workspaceGlobs}}"--exclude", "{{this}}/**",{{/each}} + {{/if}} + "--exclude", "tap-snapshots/**" + ] + } } diff --git a/lib/content/post-dependabot.yml b/lib/content/post-dependabot.yml index 10f34a52..ba5218f0 100644 --- a/lib/content/post-dependabot.yml +++ b/lib/content/post-dependabot.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest if: github.actor == 'dependabot[bot]' steps: - {{> setupGit with=(obj ref="${{ github.event.pull_request.head_ref }}")}} + {{> setupGit checkout=(obj ref="${{ github.event.pull_request.head_ref }}")}} {{> setupNode}} {{> setupDeps}} - name: Dependabot metadata @@ -25,7 +25,7 @@ jobs: env: GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} run: | - npm run template-oss-apply + {{rootNpmPath}} run template-oss-apply git commit -am "chore: postinstall for dependabot template-oss PR" git push - npm run lint + {{rootNpmPath}} run lint diff --git a/lib/content/pull-request.yml b/lib/content/pull-request.yml index aa4f1528..c29b37b7 100644 --- a/lib/content/pull-request.yml +++ b/lib/content/pull-request.yml @@ -13,12 +13,12 @@ jobs: name: Check PR Title or Commits runs-on: ubuntu-latest steps: - {{> setupGit with=(obj fetch-depth=0)}} + {{> setupGit checkout=(obj fetch-depth=0)}} {{> setupNode}} {{> setupDeps}} - name: Check commits or PR title env: PR_TITLE: $\{{ github.event.pull_request.title }} run: | - npx --offline commitlint -V --from origin/{{defaultBranch}} --to $\{{ github.event.pull_request.head.sha }} \ + {{rootNpxPath}} --offline commitlint -V --from origin/{{defaultBranch}} --to $\{{ github.event.pull_request.head.sha }} \ || echo $PR_TITLE | npx --offline commitlint -V diff --git a/lib/content/release-please.yml b/lib/content/release-please.yml index da3834fb..5218b30b 100644 --- a/lib/content/release-please.yml +++ b/lib/content/release-please.yml @@ -16,14 +16,10 @@ permissions: jobs: release-please: - runs-on: ubuntu-latest outputs: pr: $\{{ steps.release.outputs.pr }} release: $\{{ steps.release.outputs.release }} - steps: - {{> setupGit}} - {{> setupNode}} - {{> setupDeps}} + {{> setupJob }} - name: Release Please id: release run: npx --offline template-oss-release-please $\{{ github.ref_name }} @@ -40,34 +36,30 @@ jobs: - name: Output ref id: ref run: echo "::set-output name=branch::$\{{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - {{> setupGit with=(obj ref="${{ steps.ref.outputs.branch }}" fetch-depth=0)}} + {{> setupGit checkout=(obj ref="${{ steps.ref.outputs.branch }}" fetch-depth=0)}} {{> setupNode}} {{> setupDeps}} - name: Post pull request actions env: GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} run: | - npm run rp-pull-request --ignore-scripts --if-present -ws -iwr + {{rootNpmPath}} run rp-pull-request --ignore-scripts --if-present -ws -iwr git commit -am "chore: post pull request" || true git push release-test: needs: post-pr if: needs.post-pr.outputs.ref - uses: ./.github/workflows/{{releaseTest}} + uses: ./.github/workflows/release.yml with: ref: $\{{ needs.post-pr.outputs.ref }} post-release: needs: release-please if: needs.release-please.outputs.release - runs-on: ubuntu-latest - steps: - {{> setupGit}} - {{> setupNode}} - {{> setupDeps}} + {{> setupJob }} - name: Post release actions env: GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} run: | - npm run rp-release --ignore-scripts --if-present -ws -iwr + {{rootNpmPath}} run rp-release --ignore-scripts --if-present -ws -iwr diff --git a/lib/content/release-test.yml b/lib/content/release-test.yml deleted file mode 100644 index b18b4174..00000000 --- a/lib/content/release-test.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Release - -on: - workflow_call: - inputs: - ref: - required: true - type: string - -jobs: - lint-all: - runs-on: ubuntu-latest - steps: - {{> setupGit with=(obj ref="${{ inputs.ref }}")}} - {{> setupNode}} - {{> setupDeps}} - - run: npm run lint --if-present --workspaces --include-workspace-root - - test-all: - strategy: - fail-fast: false - matrix: - node-version: - {{#each ciVersions}} - - {{.}} - {{/each}} - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - {{#if windowsCI}} - - os: windows-latest - shell: cmd - {{/if}} - runs-on: $\{{ matrix.platform.os }} - defaults: - run: - shell: $\{{ matrix.platform.shell }} - steps: - {{> setupGit with=(obj ref="${{ inputs.ref }}")}} - {{> setupNode useMatrix=true}} - {{> setupDeps}} - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test --if-present --workspaces --include-workspace-root diff --git a/lib/content/release.yml b/lib/content/release.yml new file mode 100644 index 00000000..f6a67398 --- /dev/null +++ b/lib/content/release.yml @@ -0,0 +1,24 @@ + +name: Release + +on: + workflow_call: + inputs: + ref: + required: true + type: string + +jobs: + lint-all: + runs-on: ubuntu-latest + steps: + {{> setupGit checkout=(obj ref="${{ inputs.ref }}")}} + {{> setupNode}} + {{> setupDeps}} + - run: {{rootNpmPath}} run lint -ws -iwr --if-present + + test-all: + {{> setupJobMatrix checkout=(obj ref="${{ inputs.ref }}")}} + - name: add tap problem matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - run: {{rootNpmPath}} run test -ws -iwr --if-present diff --git a/lib/content/setup-deps.yml b/lib/content/setup-deps.yml deleted file mode 100644 index ff19ad3a..00000000 --- a/lib/content/setup-deps.yml +++ /dev/null @@ -1 +0,0 @@ -- run: npm i --ignore-scripts --no-audit --no-fund {{~#if flags}} {{flags}}{{/if}} diff --git a/lib/index.js b/lib/index.js index a055823d..0a6f055d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,17 +1,25 @@ const log = require('proc-log') -const { defaults } = require('lodash') +const { resolve } = require('path') const getConfig = require('./config.js') const PackageJson = require('@npmcli/package-json') const mapWorkspaces = require('@npmcli/map-workspaces') -const getPkg = async (path, baseConfig) => { +const getPkg = async (path) => { log.verbose('get-pkg', path) const pkg = (await PackageJson.load(path)).content const pkgConfig = getConfig.getPkgConfig(pkg) log.verbose('get-pkg', pkgConfig) - return { pkg, path, config: { ...baseConfig, ...pkgConfig } } + if (pkgConfig.content) { + pkgConfig.content = resolve(path, pkgConfig.content) + } + + return { + pkg, + path, + config: pkgConfig, + } } const getWsPkgs = async (root, rootPkg) => { @@ -45,38 +53,30 @@ const getPkgs = async (root) => { log.verbose('get-pkgs', 'root', root) const rootPkg = await getPkg(root) - const pkgs = [rootPkg] - - defaults(rootPkg.config, { - rootRepo: true, - rootModule: true, - workspaceRepo: true, - workspaceModule: true, - workspaces: null, - }) const ws = await getWsPkgs(root, rootPkg) return { - pkgs: pkgs.concat(ws.pkgs), + rootPkg, + pkgs: [rootPkg].concat(ws.pkgs), workspaces: ws.paths, } } -const runAll = async (root, content, checks) => { +const runAll = async (root, checks) => { const results = [] - const { pkgs, workspaces } = await getPkgs(root) + const { pkgs, workspaces, rootPkg: { config: rootConfig } } = await getPkgs(root) - for (const { pkg, path, config } of pkgs) { + for (const { pkg, path, config: pkgConfig } of pkgs) { // full config includes original config values const fullConfig = await getConfig({ - pkgs, - workspaces, root, - pkg, path, - config, - content, + pkg, + pkgs, + workspaces, + rootConfig, + pkgConfig, }) const options = { root, pkg, path, config: fullConfig } diff --git a/lib/util/files.js b/lib/util/files.js index cd708179..4e38290d 100644 --- a/lib/util/files.js +++ b/lib/util/files.js @@ -1,43 +1,55 @@ const { join } = require('path') +const { defaultsDeep } = require('lodash') const { promisify } = require('util') +const merge = require('./merge.js') +const deepMapValues = require('just-deep-map-values') const glob = promisify(require('glob')) const Parser = require('./parser.js') const template = require('./template.js') +const FILE_KEYS = ['rootRepo', 'rootModule', 'workspaceRepo', 'workspaceModule'] + const globify = pattern => pattern.split('\\').join('/') -// target paths need to be joinsed with dir and templated -const fullTarget = (dir, file, options) => join(dir, template(file, options)) +const fileEntries = (dir, files, options) => Object.entries(files) + // remove any false values + .filter(([_, v]) => v !== false) + // target paths need to be joinsed with dir and templated + .map(([k, source]) => { + const target = join(dir, template(k, options)) + return [target, source] + }) // given an obj of files, return the full target/source paths and associated parser -const getParsers = (dir, files, options) => Object.entries(files).map(([t, s]) => { - let { - file, - parser: fileParser, - filter, - } = typeof s === 'string' ? { file: s } : s - - file = join(options.config.sourceDir, file) - const target = fullTarget(dir, t, options) - - if (typeof filter === 'function' && !filter(options)) { - return null - } +const getParsers = (dir, files, options) => { + const parsers = fileEntries(dir, files, options).map(([target, source]) => { + const { file, parser, filter } = source + + if (typeof filter === 'function' && !filter(options)) { + return null + } - if (fileParser) { + if (parser) { // allow files to extend base parsers or create new ones - return new (fileParser(Parser.Parsers))(target, file, options) - } + return new (parser(Parser.Parsers))(target, file, options) + } + + return new (Parser(file))(target, file, options) + }) - return new (Parser(file))(target, file, options) -}) + return parsers.filter(Boolean) +} + +const getRemovals = async (dir, files, options) => { + const targets = fileEntries(dir, files, options).map(([t]) => globify(t)) + const globs = await Promise.all(targets.map(t => glob(t, { cwd: dir }))) + return globs.flat() +} const rmEach = async (dir, files, options, fn) => { const res = [] - for (const target of files.map((t) => fullTarget(dir, t, options))) { - for (const file of await glob(globify(target), { cwd: dir })) { - res.push(await fn(file)) - } + for (const file of await getRemovals(dir, files, options)) { + res.push(await fn(file)) } return res.filter(Boolean) } @@ -45,14 +57,44 @@ const rmEach = async (dir, files, options, fn) => { const parseEach = async (dir, files, options, fn) => { const res = [] for (const parser of getParsers(dir, files, options)) { - if (parser) { - res.push(await fn(parser)) - } + res.push(await fn(parser)) } return res.filter(Boolean) } +const parseConfig = (files, dir, overrides) => { + const normalizeFiles = (v) => deepMapValues(v, (value, key) => { + if (key === 'rm' && Array.isArray(value)) { + return value.reduce((acc, k) => { + acc[k] = true + return acc + }, {}) + } + if (typeof value === 'string') { + const file = join(dir, value) + return key === 'file' ? file : { file } + } + if (value === true && FILE_KEYS.includes(key)) { + return {} + } + return value + }) + + const merged = merge(normalizeFiles(files), normalizeFiles(overrides)) + const withDefaults = defaultsDeep(merged, FILE_KEYS.reduce((acc, k) => { + acc[k] = { add: {}, rm: {} } + return acc + }, {})) + + return withDefaults +} + +const getAddedFiles = (files) => files ? Object.keys(files.add || {}) : [] + module.exports = { rmEach, parseEach, + FILE_KEYS, + parseConfig, + getAddedFiles, } diff --git a/lib/util/gitignore.js b/lib/util/gitignore.js new file mode 100644 index 00000000..665e1009 --- /dev/null +++ b/lib/util/gitignore.js @@ -0,0 +1,34 @@ +const { posix } = require('path') +const { uniq } = require('lodash') +const localeCompare = require('@isaacs/string-locale-compare')('en') + +const sortGitPaths = (a, b) => localeCompare(a.replace(/^!/g, ''), b.replace(/^!/g, '')) + +const allowDir = (p) => { + const parts = p.split(posix.sep) + return parts.flatMap((part, index, list) => { + const prev = list.slice(0, index) + const isLast = index === list.length - 1 + const ignorePart = ['', ...prev, part, ''].join(posix.sep) + return [`!${ignorePart}`, !isLast && `${ignorePart}*`] + }).filter(Boolean) +} + +const allowRootDir = (p) => { + // This negates the first part of each path for the gitignore + // files. It should be used to allow directories where everything + // should be allowed inside such as .github/. It shouldn't be used on + // directories like `workspaces/` since we want to be explicit and + // only allow each workspace directory individually. For those use + // the allowDir method above. + const [first, hasChildren] = p.split(posix.sep) + return `${first}${hasChildren ? posix.sep : ''}` +} + +const gitignore = { + allowDir: (dirs) => uniq(dirs.map(allowDir).flat()), + allowRootDir: (dirs) => dirs.map(allowRootDir).map((p) => `!${posix.sep}${p}`), + sort: (arr) => uniq(arr.sort(sortGitPaths)), +} + +module.exports = gitignore diff --git a/lib/util/merge.js b/lib/util/merge.js new file mode 100644 index 00000000..90646b82 --- /dev/null +++ b/lib/util/merge.js @@ -0,0 +1,21 @@ +const { mergeWith } = require('lodash') + +const merge = (...objects) => mergeWith({}, ...objects, (value, srcValue, key) => { + if (Array.isArray(srcValue)) { + // Dont merge arrays, last array wins + return srcValue + } +}) + +const mergeWithArrays = (...keys) => + (...objects) => mergeWith({}, ...objects, (value, srcValue, key) => { + if (Array.isArray(srcValue)) { + if (keys.includes(key)) { + return (Array.isArray(value) ? value : []).concat(srcValue) + } + return srcValue + } + }) + +module.exports = merge +module.exports.withArrays = mergeWithArrays diff --git a/lib/util/parser.js b/lib/util/parser.js index 84123d4e..59f650cc 100644 --- a/lib/util/parser.js +++ b/lib/util/parser.js @@ -4,9 +4,10 @@ const yaml = require('yaml') const NpmPackageJson = require('@npmcli/package-json') const jsonParse = require('json-parse-even-better-errors') const Diff = require('diff') -const { unset, merge } = require('lodash') +const { unset } = require('lodash') const template = require('./template.js') const jsonDiff = require('./json-diff') +const merge = require('./merge.js') const setFirst = (first, rest) => ({ ...first, ...rest }) const traverse = (value, visit, keys = []) => { if (keys.length) { @@ -220,7 +221,7 @@ class Json extends Base { class JsonMerge extends Json { static header = 'This file is partially managed by {{__NAME__}}. Edits may be overwritten.' - merge = (t, s) => merge({}, t, s) + merge = (t, s) => merge(t, s) } class PackageJson extends JsonMerge { diff --git a/lib/util/template.js b/lib/util/template.js index 4c89cdd3..d6deab9c 100644 --- a/lib/util/template.js +++ b/lib/util/template.js @@ -3,35 +3,37 @@ const { basename, extname, join } = require('path') const fs = require('fs') const DELETE = '__DELETE__' -const partialName = (s) => - basename(s, extname(s)).replace(/-([a-z])/g, (_, g) => g.toUpperCase()) +const partialName = (s) => basename(s, extname(s)) // remove extension + .replace(/^_/, '') // remove leading underscore + .replace(/-([a-z])/g, (_, g) => g.toUpperCase()) // camelcase -const setupHandlebars = (partialsDir) => { +const setupHandlebars = (...partialDirs) => { Handlebars.registerHelper('obj', ({ hash }) => hash) Handlebars.registerHelper('json', (c) => JSON.stringify(c)) Handlebars.registerHelper('del', () => JSON.stringify(DELETE)) - // Load all content files as camelcase partial names - for (const f of fs.readdirSync(join(partialsDir))) { - Handlebars.registerPartial( - partialName(f), - fs.readFileSync(join(partialsDir, f)).toString() - ) + // Load all files as camelcase partial names. + // all other content dirs only get special underscore leading + // files as partials. this prevents recursion loops when overwriting + // a filename to use as a enw file + let isBase = true + for (const dir of partialDirs) { + for (const f of fs.readdirSync(dir)) { + if (f.startsWith('_') || isBase) { + Handlebars.registerPartial( + partialName(f), + fs.readFileSync(join(dir, f)).toString() + ) + } + } + isBase = false } } -const cache = new Map() - const template = (str, { config, ...options }) => { - if (cache.size === 0) { - setupHandlebars(config.sourceDir) - } + setupHandlebars(...config.__PARTIAL_DIRS__) - let t = cache.get(str) - if (t == null) { - t = Handlebars.compile(str, { strict: true }) - cache.set(str, t) - } + const t = Handlebars.compile(str, { strict: true }) // merge in config as top level data in templates return t({ ...options, ...config }) diff --git a/package.json b/package.json index 884d9ac4..4a6d8da8 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "handlebars": "^4.7.7", "hosted-git-info": "^5.0.0", "json-parse-even-better-errors": "^2.3.1", + "just-deep-map-values": "^1.1.1", "just-diff": "^5.0.1", "lodash": "^4.17.21", "npm-package-arg": "^9.0.1", @@ -60,7 +61,11 @@ "tap": "^16.0.0" }, "tap": { - "timeout": 600 + "timeout": 600, + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten." diff --git a/tap-snapshots/test/apply/files-snapshots.js.test.cjs b/tap-snapshots/test/apply/files-snapshots.js.test.cjs new file mode 100644 index 00000000..b6aa6493 --- /dev/null +++ b/tap-snapshots/test/apply/files-snapshots.js.test.cjs @@ -0,0 +1,157 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/apply/files-snapshots.js TAP private workspace > expect resolving Promise 1`] = ` +.commitlintrc.js +.eslintrc.js +.github/CODEOWNERS +.github/dependabot.yml +.github/ISSUE_TEMPLATE/bug.yml +.github/ISSUE_TEMPLATE/config.yml +.github/matchers/tap.json +.github/workflows/audit.yml +.github/workflows/ci-a.yml +.github/workflows/ci-b.yml +.github/workflows/ci.yml +.github/workflows/codeql-analysis.yml +.github/workflows/post-dependabot.yml +.github/workflows/pull-request.yml +.github/workflows/release-please.yml +.github/workflows/release.yml +.gitignore +.npmrc +.release-please-manifest.json +CODE_OF_CONDUCT.md +package.json +release-please-config.json +SECURITY.md +workspaces/a/.eslintrc.js +workspaces/a/.gitignore +workspaces/a/package.json +workspaces/b/.eslintrc.js +workspaces/b/.gitignore +workspaces/b/package.json +` + +exports[`test/apply/files-snapshots.js TAP turn off add/rm types > expect resolving Promise 1`] = ` +.commitlintrc.js +.github/CODEOWNERS +.github/dependabot.yml +.github/ISSUE_TEMPLATE/bug.yml +.github/ISSUE_TEMPLATE/config.yml +.github/matchers/tap.json +.github/workflows/audit.yml +.github/workflows/ci.yml +.github/workflows/codeql-analysis.yml +.github/workflows/post-dependabot.yml +.github/workflows/pull-request.yml +.github/workflows/release-please.yml +.github/workflows/release.yml +.release-please-manifest.json +package.json +release-please-config.json +` + +exports[`test/apply/files-snapshots.js TAP turn off module > expect resolving Promise 1`] = ` +.commitlintrc.js +.github/CODEOWNERS +.github/dependabot.yml +.github/ISSUE_TEMPLATE/bug.yml +.github/ISSUE_TEMPLATE/config.yml +.github/matchers/tap.json +.github/workflows/audit.yml +.github/workflows/ci.yml +.github/workflows/codeql-analysis.yml +.github/workflows/post-dependabot.yml +.github/workflows/pull-request.yml +.github/workflows/release-please.yml +.github/workflows/release.yml +.release-please-manifest.json +package.json +release-please-config.json +` + +exports[`test/apply/files-snapshots.js TAP turn off repo > expect resolving Promise 1`] = ` +.eslintrc.js +.gitignore +.npmrc +CODE_OF_CONDUCT.md +package.json +SECURITY.md +` + +exports[`test/apply/files-snapshots.js TAP turn off root > expect resolving Promise 1`] = ` +package.json +` + +exports[`test/apply/files-snapshots.js TAP turn off specific files > expect resolving Promise 1`] = ` +.eslintrc.yml +.github/CODEOWNERS +.github/dependabot.yml +.github/ISSUE_TEMPLATE/bug.yml +.github/ISSUE_TEMPLATE/config.yml +.github/matchers/tap.json +.github/workflows/audit.yml +.github/workflows/ci.yml +.github/workflows/codeql-analysis.yml +.github/workflows/post-dependabot.yml +.github/workflows/pull-request.yml +.github/workflows/release-please.yml +.github/workflows/release-test.yml +.github/workflows/release.yml +.gitignore +.npmrc +.release-please-manifest.json +CODE_OF_CONDUCT.md +package.json +release-please-config.json +SECURITY.md +` + +exports[`test/apply/files-snapshots.js TAP workspaces > expect resolving Promise 1`] = ` +.github/matchers/tap.json +.github/workflows/ci-d.yml +.github/workflows/release-please.yml +.github/workflows/release.yml +.release-please-manifest.json +package.json +release-please-config.json +workspaces/a/.eslintrc.js +workspaces/a/.gitignore +workspaces/a/package.json +workspaces/b/.eslintrc.js +workspaces/b/.gitignore +workspaces/b/package.json +workspaces/c/package.json +workspaces/d/.eslintrc.js +workspaces/d/.gitignore +workspaces/d/package.json +` + +exports[`test/apply/files-snapshots.js TAP workspaces only (like npm/cli) > expect resolving Promise 1`] = ` +.github/matchers/tap.json +.github/workflows/ci-a.yml +.github/workflows/ci-b.yml +.github/workflows/release-please.yml +.github/workflows/release.yml +.release-please-manifest.json +package.json +release-please-config.json +workspaces/a/.eslintrc.js +workspaces/a/.gitignore +workspaces/a/package.json +workspaces/b/.eslintrc.js +workspaces/b/.gitignore +workspaces/b/package.json +` + +exports[`test/apply/files-snapshots.js TAP workspaces with relative content path > expect resolving Promise 1`] = ` +content_dir/index.js +content_dir2/index.js +package.json +workspaces/a/package.json +` diff --git a/tap-snapshots/test/apply/index.js.test.cjs b/tap-snapshots/test/apply/index.js.test.cjs deleted file mode 100644 index f2aa20f1..00000000 --- a/tap-snapshots/test/apply/index.js.test.cjs +++ /dev/null @@ -1,75 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/apply/index.js TAP turn off all > expect resolving Promise 1`] = ` -package.json -` - -exports[`test/apply/index.js TAP turn off module > expect resolving Promise 1`] = ` -.commitlintrc.js -.github/CODEOWNERS -.github/ISSUE_TEMPLATE/bug.yml -.github/ISSUE_TEMPLATE/config.yml -.github/dependabot.yml -.github/matchers/tap.json -.github/workflows/audit.yml -.github/workflows/ci.yml -.github/workflows/codeql-analysis.yml -.github/workflows/post-dependabot.yml -.github/workflows/pull-request.yml -.github/workflows/release-please.yml -.github/workflows/release-test.yml -.release-please-manifest.json -package.json -release-please-config.json -` - -exports[`test/apply/index.js TAP turn off repo > expect resolving Promise 1`] = ` -.eslintrc.js -.gitignore -.npmrc -CODE_OF_CONDUCT.md -SECURITY.md -package.json -` - -exports[`test/apply/index.js TAP workspaces > expect resolving Promise 1`] = ` -.github/matchers/tap.json -.github/workflows/ci-d.yml -.github/workflows/release-please.yml -.github/workflows/release-test.yml -.release-please-manifest.json -package.json -release-please-config.json -workspaces/a/.eslintrc.js -workspaces/a/.gitignore -workspaces/a/package.json -workspaces/b/.eslintrc.js -workspaces/b/.gitignore -workspaces/b/package.json -workspaces/c/package.json -workspaces/d/.eslintrc.js -workspaces/d/.gitignore -workspaces/d/package.json -` - -exports[`test/apply/index.js TAP workspaces only (like npm/cli) > expect resolving Promise 1`] = ` -.github/matchers/tap.json -.github/workflows/ci-a.yml -.github/workflows/ci-b.yml -.github/workflows/release-please.yml -.github/workflows/release-test.yml -.release-please-manifest.json -package.json -release-please-config.json -workspaces/a/.eslintrc.js -workspaces/a/.gitignore -workspaces/a/package.json -workspaces/b/.eslintrc.js -workspaces/b/.gitignore -workspaces/b/package.json -` diff --git a/tap-snapshots/test/apply/lockfile.js.test.cjs b/tap-snapshots/test/apply/lockfile.js.test.cjs deleted file mode 100644 index e7573319..00000000 --- a/tap-snapshots/test/apply/lockfile.js.test.cjs +++ /dev/null @@ -1,85 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/apply/lockfile.js TAP lockfile > expect resolving Promise 1`] = ` -.gitignore -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -# ignore everything in the root -/* - -# keep these -!/.eslintrc.local.* -!**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* -!/.commitlintrc.js -!/.eslintrc.js -!/.github/ -!/.gitignore -!/.npmrc -!/.release-please-manifest.json -!/CODE_OF_CONDUCT.md -!/SECURITY.md -!/bin/ -!/lib/ -!/package.json -!/release-please-config.json -!/package-lock.json - -.npmrc -======================================== -; This file is automatically added by @npmcli/template-oss. Do not edit. - -package-lock=true -` - -exports[`test/apply/lockfile.js TAP no lockfile by default > expect resolving Promise 1`] = ` -.gitignore -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -# ignore everything in the root -/* - -# keep these -!/.eslintrc.local.* -!**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* -!/.commitlintrc.js -!/.eslintrc.js -!/.github/ -!/.gitignore -!/.npmrc -!/.release-please-manifest.json -!/CODE_OF_CONDUCT.md -!/SECURITY.md -!/bin/ -!/lib/ -!/package.json -!/release-please-config.json - -.npmrc -======================================== -; This file is automatically added by @npmcli/template-oss. Do not edit. - -package-lock=false -` diff --git a/tap-snapshots/test/apply/full-content.js.test.cjs b/tap-snapshots/test/apply/source-snapshots.js.test.cjs similarity index 94% rename from tap-snapshots/test/apply/full-content.js.test.cjs rename to tap-snapshots/test/apply/source-snapshots.js.test.cjs index 6e63143f..e104b173 100644 --- a/tap-snapshots/test/apply/full-content.js.test.cjs +++ b/tap-snapshots/test/apply/source-snapshots.js.test.cjs @@ -5,7 +5,7 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/apply/full-content.js TAP default > expect resolving Promise 1`] = ` +exports[`test/apply/source-snapshots.js TAP root only > expect resolving Promise 1`] = ` .commitlintrc.js ======================================== /* This file is automatically added by @npmcli/template-oss. Do not edit. */ @@ -45,6 +45,26 @@ module.exports = { * @npm/cli-team +.github/dependabot.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. + +version: 2 + +updates: + - package-ecosystem: npm + directory: "/" + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + .github/ISSUE_TEMPLATE/bug.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -108,26 +128,6 @@ body: blank_issues_enabled: true -.github/dependabot.yml -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -version: 2 - -updates: - - package-ecosystem: npm - directory: "/" - schedule: - interval: daily - allow: - - dependency-type: direct - versioning-strategy: increase-if-necessary - commit-message: - prefix: deps - prefix-development: chore - labels: - - "Dependencies" - .github/matchers/tap.json ======================================== { @@ -435,10 +435,10 @@ permissions: jobs: release-please: - runs-on: ubuntu-latest outputs: pr: \${{ steps.release.outputs.pr }} release: \${{ steps.release.outputs.release }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup git user @@ -494,7 +494,7 @@ jobs: release-test: needs: post-pr if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release-test.yml + uses: ./.github/workflows/release.yml with: ref: \${{ needs.post-pr.outputs.ref }} @@ -521,7 +521,7 @@ jobs: run: | npm run rp-release --ignore-scripts --if-present -ws -iwr -.github/workflows/release-test.yml +.github/workflows/release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -552,7 +552,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint --if-present --workspaces --include-workspace-root + - run: npm run lint -ws -iwr --if-present test-all: strategy: @@ -608,7 +608,7 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test --if-present --workspaces --include-workspace-root + - run: npm run test -ws -iwr --if-present .gitignore ======================================== @@ -618,28 +618,28 @@ jobs: /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.commitlintrc.js !/.eslintrc.js +!/.eslintrc.local.* !/.github/ !/.gitignore !/.npmrc !/.release-please-manifest.json -!/CODE_OF_CONDUCT.md -!/SECURITY.md !/bin/ +!/CHANGELOG* +!/CODE_OF_CONDUCT.md +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* !/release-please-config.json +!/scripts/ +!/SECURITY.md +!/tap-snapshots/ +!/test/ .npmrc ======================================== @@ -663,12 +663,6 @@ Conduct](https://docs.npmjs.com/policies/conduct) The npm cli team may, at its own discretion, moderate, remove, or edit any interactions such as pull requests, issues, and comments. -SECURITY.md -======================================== - - -Please send vulnerability reports through [hackerone](https://hackerone.com/github). - package.json ======================================== { @@ -694,6 +688,12 @@ package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] } } @@ -735,9 +735,33 @@ release-please-config.json } } } + +SECURITY.md +======================================== + + +Please send vulnerability reports through [hackerone](https://hackerone.com/github). +` + +exports[`test/apply/source-snapshots.js TAP with content path > expect resolving Promise 1`] = ` +content_dir/index.js +======================================== +module.exports={} + +package.json +======================================== +{ + "templateOSS": { + "content": "content_dir", + "defaultContent": false, + "version": "{{VERSION}}" + }, + "name": "testpkg", + "version": "1.0.0" +} ` -exports[`test/apply/full-content.js TAP workspaces + everything > expect resolving Promise 1`] = ` +exports[`test/apply/source-snapshots.js TAP with workspaces > expect resolving Promise 1`] = ` .commitlintrc.js ======================================== /* This file is automatically added by @npmcli/template-oss. Do not edit. */ @@ -765,22 +789,68 @@ const localConfigs = readdir(__dirname) module.exports = { root: true, + ignorePatterns: [ + 'workspaces/a', + 'workspaces/b', + ], extends: [ '@npmcli', ...localConfigs, ], } -.eslintrc.local.yml -======================================== -KEEP - .github/CODEOWNERS ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. * @npm/cli-team +.github/dependabot.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. + +version: 2 + +updates: + - package-ecosystem: npm + directory: "/" + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + + - package-ecosystem: npm + directory: "workspaces/a/" + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + + - package-ecosystem: npm + directory: "workspaces/b/" + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + .github/ISSUE_TEMPLATE/bug.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -844,26 +914,6 @@ body: blank_issues_enabled: true -.github/dependabot.yml -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -version: 2 - -updates: - - package-ecosystem: npm - directory: "/" - schedule: - interval: daily - allow: - - dependency-type: direct - versioning-strategy: increase-if-necessary - commit-message: - prefix: deps - prefix-development: chore - labels: - - "Dependencies" - .github/matchers/tap.json ======================================== { @@ -929,11 +979,11 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - run: npm audit -.github/workflows/ci-bbb.yml +.github/workflows/ci-a.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: CI - bbb +name: CI - a on: workflow_dispatch: @@ -941,13 +991,13 @@ on: branches: - '*' paths: - - workspaces/b/** + - workspaces/a/** push: branches: - main - latest paths: - - workspaces/b/** + - workspaces/a/** schedule: # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - cron: "0 9 * * 1" @@ -968,7 +1018,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w bbb + - run: npm run lint -w a test: strategy: @@ -1022,13 +1072,13 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w bbb + - run: npm test --ignore-scripts -w a -.github/workflows/ci-name-aaaa.yml +.github/workflows/ci-b.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: CI - @name/aaaa +name: CI - b on: workflow_dispatch: @@ -1036,13 +1086,13 @@ on: branches: - '*' paths: - - workspaces/a/** + - workspaces/b/** push: branches: - main - latest paths: - - workspaces/a/** + - workspaces/b/** schedule: # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - cron: "0 9 * * 1" @@ -1063,7 +1113,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w @name/aaaa + - run: npm run lint -w b test: strategy: @@ -1117,7 +1167,7 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w @name/aaaa + - run: npm test --ignore-scripts -w b .github/workflows/ci.yml ======================================== @@ -1130,10 +1180,16 @@ on: pull_request: branches: - '*' + paths-ignore: + - workspaces/a/** + - workspaces/b/** push: branches: - main - latest + paths-ignore: + - workspaces/a/** + - workspaces/b/** schedule: # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - cron: "0 9 * * 1" @@ -1361,10 +1417,10 @@ permissions: jobs: release-please: - runs-on: ubuntu-latest outputs: pr: \${{ steps.release.outputs.pr }} release: \${{ steps.release.outputs.release }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup git user @@ -1420,7 +1476,7 @@ jobs: release-test: needs: post-pr if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release-test.yml + uses: ./.github/workflows/release.yml with: ref: \${{ needs.post-pr.outputs.ref }} @@ -1447,7 +1503,7 @@ jobs: run: | npm run rp-release --ignore-scripts --if-present -ws -iwr -.github/workflows/release-test.yml +.github/workflows/release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -1478,7 +1534,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint --if-present --workspaces --include-workspace-root + - run: npm run lint -ws -iwr --if-present test-all: strategy: @@ -1534,7 +1590,7 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test --if-present --workspaces --include-workspace-root + - run: npm run test -ws -iwr --if-present .gitignore ======================================== @@ -1544,29 +1600,32 @@ jobs: /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.commitlintrc.js !/.eslintrc.js +!/.eslintrc.local.* !/.github/ !/.gitignore !/.npmrc !/.release-please-manifest.json -!/CODE_OF_CONDUCT.md -!/SECURITY.md !/bin/ +!/CHANGELOG* +!/CODE_OF_CONDUCT.md +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* !/release-please-config.json +!/scripts/ +!/SECURITY.md +!/tap-snapshots/ +!/test/ !/workspaces/ +/workspaces/* +!/workspaces/a/ +!/workspaces/b/ .npmrc ======================================== @@ -1592,12 +1651,6 @@ Conduct](https://docs.npmjs.com/policies/conduct) The npm cli team may, at its own discretion, moderate, remove, or edit any interactions such as pull requests, issues, and comments. -SECURITY.md -======================================== - - -Please send vulnerability reports through [hackerone](https://hackerone.com/github). - package.json ======================================== { @@ -1614,7 +1667,9 @@ package.json "lintfix": "npm run lint -- --fix", "snap": "tap", "test": "tap", - "posttest": "npm run lint" + "posttest": "npm run lint", + "test-all": "npm run test -ws -iwr --if-present", + "lint-all": "npm run lint -ws -iwr --if-present" }, "author": "GitHub Inc.", "files": [ @@ -1627,6 +1682,17 @@ package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "test-ignore": "^(workspaces/a|workspaces/b)/", + "nyc-arg": [ + "--exclude", + "workspaces/a/**", + "--exclude", + "workspaces/b/**", + "--exclude", + "tap-snapshots/**" + ] } } @@ -1674,6 +1740,12 @@ release-please-config.json } } +SECURITY.md +======================================== + + +Please send vulnerability reports through [hackerone](https://hackerone.com/github). + workspaces/a/.eslintrc.js ======================================== /* This file is automatically added by @npmcli/template-oss. Do not edit. */ @@ -1694,10 +1766,6 @@ module.exports = { ], } -workspaces/a/.eslintrc.local.yml -======================================== -KEEP - workspaces/a/.gitignore ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -1706,26 +1774,26 @@ workspaces/a/.gitignore /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.eslintrc.js +!/.eslintrc.local.* !/.gitignore !/bin/ +!/CHANGELOG* +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* +!/scripts/ +!/tap-snapshots/ +!/test/ workspaces/a/package.json ======================================== { - "name": "@name/aaaa", + "name": "a", "version": "1.0.0", "scripts": { "lint": "eslint /"**/*.js/"", @@ -1747,6 +1815,12 @@ workspaces/a/package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] } } @@ -1770,10 +1844,6 @@ module.exports = { ], } -workspaces/b/.eslintrc.local.yml -======================================== -KEEP - workspaces/b/.gitignore ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -1782,26 +1852,26 @@ workspaces/b/.gitignore /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.eslintrc.js +!/.eslintrc.local.* !/.gitignore !/bin/ +!/CHANGELOG* +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* +!/scripts/ +!/tap-snapshots/ +!/test/ workspaces/b/package.json ======================================== { - "name": "bbb", + "name": "b", "version": "1.0.0", "scripts": { "lint": "eslint /"**/*.js/"", @@ -1823,11 +1893,17 @@ workspaces/b/package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] } } ` -exports[`test/apply/full-content.js TAP workspaces only > expect resolving Promise 1`] = ` +exports[`test/apply/source-snapshots.js TAP workspaces only > expect resolving Promise 1`] = ` .github/matchers/tap.json ======================================== { @@ -2071,10 +2147,10 @@ permissions: jobs: release-please: - runs-on: ubuntu-latest outputs: pr: \${{ steps.release.outputs.pr }} release: \${{ steps.release.outputs.release }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup git user @@ -2130,7 +2206,7 @@ jobs: release-test: needs: post-pr if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release-test.yml + uses: ./.github/workflows/release.yml with: ref: \${{ needs.post-pr.outputs.ref }} @@ -2157,7 +2233,7 @@ jobs: run: | npm run rp-release --ignore-scripts --if-present -ws -iwr -.github/workflows/release-test.yml +.github/workflows/release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -2188,7 +2264,7 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint --if-present --workspaces --include-workspace-root + - run: npm run lint -ws -iwr --if-present test-all: strategy: @@ -2244,7 +2320,7 @@ jobs: - run: npm i --ignore-scripts --no-audit --no-fund - name: add tap problem matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test --if-present --workspaces --include-workspace-root + - run: npm run test -ws -iwr --if-present .release-please-manifest.json ======================================== @@ -2338,21 +2414,21 @@ workspaces/a/.gitignore /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.eslintrc.js +!/.eslintrc.local.* !/.gitignore !/bin/ +!/CHANGELOG* +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* +!/scripts/ +!/tap-snapshots/ +!/test/ workspaces/a/package.json ======================================== @@ -2379,6 +2455,12 @@ workspaces/a/package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] } } @@ -2410,21 +2492,21 @@ workspaces/b/.gitignore /* # keep these -!/.eslintrc.local.* !**/.gitignore -!/docs/ -!/tap-snapshots/ -!/test/ -!/map.js -!/scripts/ -!/README* -!/LICENSE* -!/CHANGELOG* !/.eslintrc.js +!/.eslintrc.local.* !/.gitignore !/bin/ +!/CHANGELOG* +!/docs/ !/lib/ +!/LICENSE* +!/map.js !/package.json +!/README* +!/scripts/ +!/tap-snapshots/ +!/test/ workspaces/b/package.json ======================================== @@ -2451,6 +2533,48 @@ workspaces/b/package.json "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] } } ` + +exports[`test/apply/source-snapshots.js TAP workspaces with nested content path > expect resolving Promise 1`] = ` +content_dir/index.js +======================================== +module.exports={} + +content_dir2/index.js +======================================== +module.exports={} + +package.json +======================================== +{ + "templateOSS": { + "content": "content_dir", + "defaultContent": false, + "version": "{{VERSION}}" + }, + "name": "testpkg", + "version": "1.0.0", + "workspaces": [ + "workspaces/a" + ] +} + +workspaces/a/package.json +======================================== +{ + "templateOSS": { + "content": "../../content_dir2", + "version": "{{VERSION}}" + }, + "name": "a", + "version": "1.0.0" +} +` diff --git a/tap-snapshots/test/check/changelog.js.test.cjs b/tap-snapshots/test/check/changelog.js.test.cjs deleted file mode 100644 index a76ff451..00000000 --- a/tap-snapshots/test/check/changelog.js.test.cjs +++ /dev/null @@ -1,23 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/check/changelog.js TAP will report incorrect changelog > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The CHANGELOG.md is incorrect: - - The changelog should start with - "# Changelog - - #" - -To correct it: reformat the changelog to have the correct heading - -------------------------------------------------------------------- -` diff --git a/tap-snapshots/test/check/diffs.js.test.cjs b/tap-snapshots/test/check/diff-snapshots.js.test.cjs similarity index 50% rename from tap-snapshots/test/check/diffs.js.test.cjs rename to tap-snapshots/test/check/diff-snapshots.js.test.cjs index 87b168ec..a56d374d 100644 --- a/tap-snapshots/test/check/diffs.js.test.cjs +++ b/tap-snapshots/test/check/diff-snapshots.js.test.cjs @@ -5,7 +5,7 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/check/diffs.js TAP different headers > initial check 1`] = ` +exports[`test/check/diff-snapshots.js TAP different headers > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -13,72 +13,15 @@ Some problems were detected: The following repo files need to be added: header.txt - noheader.txt nocomment.txt + noheader.txt To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP different headers > source after apply 1`] = ` -content/index.js -======================================== -module.exports = { - rootRepo: { - add: { - 'header.txt': { - file: 'source.txt', - parser: (p) => class extends p.Base { - static header = 'Different header' - }, - }, - 'noheader.txt': { - file: 'source.txt', - parser: (p) => class extends p.Base { - static header = null - }, - }, - 'nocomment.txt': { - file: 'source.txt', - parser: (p) => class extends p.Base { - comment = null - }, - }, - }, - }, -} - -content/source.txt -======================================== -source - -header.txt -======================================== -Different header - -source - -nocomment.txt -======================================== -source - -noheader.txt -======================================== -source - -package.json -======================================== -{ - "name": "testpkg", - "version": "1.0.0", - "templateOSS": { - "version": "{{VERSION}}" - } -} -` - -exports[`test/check/diffs.js TAP json delete > initial check 1`] = ` +exports[`test/check/diff-snapshots.js TAP json delete > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -96,43 +39,7 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP json delete > source after apply 1`] = ` -content/index.js -======================================== -module.exports = { - rootRepo: { - add: { - 'target.json': { - file: 'source.json', - parser: (p) => p.Json, - }, - }, - }, -} - -content/source.json -======================================== -{"a":"__DELETE__","b":2} - -package.json -======================================== -{ - "name": "testpkg", - "version": "1.0.0", - "templateOSS": { - "version": "{{VERSION}}" - } -} - -target.json -======================================== -{ - "//@npmcli/template-oss": "This file is automatically added by @npmcli/template-oss. Do not edit.", - "b": 2 -} -` - -exports[`test/check/diffs.js TAP json merge > initial check 1`] = ` +exports[`test/check/diff-snapshots.js TAP json merge > initial check 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -149,44 +56,7 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP json merge > source after apply 1`] = ` -content/index.js -======================================== -module.exports = { - rootRepo: { - add: { - 'target.json': { - file: 'source.json', - parser: (p) => p.JsonMerge, - }, - }, - }, -} - -content/source.json -======================================== -{"b":1} - -package.json -======================================== -{ - "name": "testpkg", - "version": "1.0.0", - "templateOSS": { - "version": "{{VERSION}}" - } -} - -target.json -======================================== -{ - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "a": 1, - "b": 1 -} -` - -exports[`test/check/diffs.js TAP json overwrite > initial check 1`] = ` +exports[`test/check/diff-snapshots.js TAP json overwrite > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -203,88 +73,7 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP json overwrite > source after apply 1`] = ` -content/index.js -======================================== -module.exports={rootRepo:{add:{'target.json':'source.json'}}} - -content/source.json -======================================== -{"b":1} - -package.json -======================================== -{ - "name": "testpkg", - "version": "1.0.0", - "templateOSS": { - "version": "{{VERSION}}" - } -} - -target.json -======================================== -{ - "//@npmcli/template-oss": "This file is automatically added by @npmcli/template-oss. Do not edit.", - "b": 1 -} -` - -exports[`test/check/diffs.js TAP node 10 > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The repo file ci.yml needs to be updated: - - .github/workflows/ci.yml - ======================================== - @@ -37,8 +37,9 @@ - strategy: - fail-fast: false - matrix: - node-version: - + - 10 - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- - -The repo file release-test.yml needs to be updated: - - .github/workflows/release-test.yml - ======================================== - @@ -33,8 +33,9 @@ - strategy: - fail-fast: false - matrix: - node-version: - + - 10 - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- - -The module file package.json needs to be updated: - - package.json - ======================================== - "engines.node" is "^14.17.0 || ^16.13.0 || >=18.0.0", expected "^10.0.0 || ^14.17.0 || ^16.13.0 || >=18.0.0" - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- -` - -exports[`test/check/diffs.js TAP unknown file type > initial check 1`] = ` +exports[`test/check/diff-snapshots.js TAP unknown file type > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -298,71 +87,11 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP unknown file type > source after apply 1`] = ` -content/index.js -======================================== -module.exports={rootRepo:{add:{'target.txt':'source.txt'}}} - -content/source.txt -======================================== -source - -package.json -======================================== -{ - "name": "testpkg", - "version": "1.0.0", - "templateOSS": { - "version": "{{VERSION}}" - } -} - -target.txt -======================================== -This file is automatically added by @npmcli/template-oss. Do not edit. - -source -` - -exports[`test/check/diffs.js TAP update, remove, errors > expect resolving Promise 1`] = ` +exports[`test/check/diff-snapshots.js TAP update and remove errors > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- -The repo file ci.yml needs to be updated: - - .github/workflows/ci.yml - ======================================== - @@ -65,4 +65,24 @@ - with: - node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) - # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) - + run: | - + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz - + tar xf npm-7.5.4.tgz - + cd package - + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz - + cd .. - + rmdir /s /q package - + - name: Update npm to 7 - + # If we do test on npm 10 it needs npm7 - + if: startsWith(matrix.node-version, '10.') - + run: npm i --prefer-online --no-fund --no-audit -g npm@7 - + - name: Update npm to latest - + if: \${{ !startsWith(matrix.node-version, '10.') }} - + run: npm i --prefer-online --no-fund --no-audit -g npm@latest - + - run: npm -v - + - run: npm i --ignore-scripts --no-audit --no-fund - + - name: add tap problem matcher - + run: echo "::add-matcher::.github/matchers/tap.json" - + - run: npm test --ignore-scripts - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- - The repo file audit.yml needs to be updated: .github/workflows/audit.yml @@ -404,6 +133,40 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- +The repo file ci.yml needs to be updated: + + .github/workflows/ci.yml + ======================================== + @@ -65,4 +65,24 @@ + with: + node-version: \${{ matrix.node-version }} + - name: Update to workable npm (windows) + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + + run: | + + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + + tar xf npm-7.5.4.tgz + + cd package + + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz + + cd .. + + rmdir /s /q package + + - name: Update npm to 7 + + # If we do test on npm 10 it needs npm7 + + if: startsWith(matrix.node-version, '10.') + + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + + - name: Update npm to latest + + if: \${{ !startsWith(matrix.node-version, '10.') }} + + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + + - run: npm -v + + - run: npm i --ignore-scripts --no-audit --no-fund + + - name: add tap problem matcher + + run: echo "::add-matcher::.github/matchers/tap.json" + + - run: npm test --ignore-scripts + +To correct it: npx template-oss-apply --force + +------------------------------------------------------------------- + The following module files need to be deleted: .eslintrc.json @@ -421,7 +184,7 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` -exports[`test/check/diffs.js TAP will diff json > expect resolving Promise 1`] = ` +exports[`test/check/diff-snapshots.js TAP will diff json > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -443,25 +206,3 @@ To correct it: npx template-oss-apply --force ------------------------------------------------------------------- ` - -exports[`test/check/diffs.js TAP workspaces > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following module files need to be deleted: - - workspaces/a/.npmrc - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- - -The following module files need to be deleted: - - workspaces/b/.npmrc - -To correct it: npx template-oss-apply --force - -------------------------------------------------------------------- -` diff --git a/tap-snapshots/test/check/gitignore.js.test.cjs b/tap-snapshots/test/check/gitignore.js.test.cjs deleted file mode 100644 index 794a5c8e..00000000 --- a/tap-snapshots/test/check/gitignore.js.test.cjs +++ /dev/null @@ -1,224 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/check/gitignore.js TAP will report tracked files in gitignore > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in .gitignore: - - ignorethis - package-lock.json - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.commitlintrc.js - !/.eslintrc.js - !/.github/ - !/.gitignore - !/.npmrc - !/.release-please-manifest.json - !/CODE_OF_CONDUCT.md - !/SECURITY.md - !/bin/ - !/lib/ - !/package.json - !/release-please-config.json - -------------------------------------------------------------------- -` - -exports[`test/check/gitignore.js TAP will report tracked files in gitignore workspace > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in .gitignore: - - ignorethis - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.commitlintrc.js - !/.eslintrc.js - !/.github/ - !/.gitignore - !/.npmrc - !/.release-please-manifest.json - !/CODE_OF_CONDUCT.md - !/SECURITY.md - !/bin/ - !/lib/ - !/package.json - !/release-please-config.json - !/workspaces/ - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in workspaces/a/.gitignore: - - workspaces/a/wsafile - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.eslintrc.js - !/.gitignore - !/bin/ - !/lib/ - !/package.json - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in workspaces/b/.gitignore: - - workspaces/b/wsbfile - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.eslintrc.js - !/.gitignore - !/bin/ - !/lib/ - !/package.json - -------------------------------------------------------------------- -` - -exports[`test/check/gitignore.js TAP works with workspaces in separate dirs > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in .gitignore: - - ignorethis - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.commitlintrc.js - !/.eslintrc.js - !/.github/ - !/.gitignore - !/.npmrc - !/.release-please-manifest.json - !/CODE_OF_CONDUCT.md - !/SECURITY.md - !/bin/ - !/lib/ - !/package.json - !/release-please-config.json - !/workspace-a - !/workspace-b - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in workspace-a/.gitignore: - - workspace-a/wsafile - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.eslintrc.js - !/.gitignore - !/bin/ - !/lib/ - !/package.json - -------------------------------------------------------------------- - -The following files are tracked by git but matching a pattern in workspace-b/.gitignore: - - workspace-b/wsbfile - -To correct it: move files to not match one of the following patterns: - - /* - !/.eslintrc.local.* - !**/.gitignore - !/docs/ - !/tap-snapshots/ - !/test/ - !/map.js - !/scripts/ - !/README* - !/LICENSE* - !/CHANGELOG* - !/.eslintrc.js - !/.gitignore - !/bin/ - !/lib/ - !/package.json - -------------------------------------------------------------------- -` diff --git a/tap-snapshots/test/check/required.js.test.cjs b/tap-snapshots/test/check/required.js.test.cjs deleted file mode 100644 index 1d76457b..00000000 --- a/tap-snapshots/test/check/required.js.test.cjs +++ /dev/null @@ -1,22 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/check/required.js TAP not ok without required > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following required devDependencies were not found: - - @npmcli/template-oss@{{VERSION}} - @npmcli/eslint-config - tap - -To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @npmcli/eslint-config@latest tap@latest --save-dev && npm i @npmcli/template-oss@{{VERSION}} --save-dev --save-exact - -------------------------------------------------------------------- -` diff --git a/tap-snapshots/test/check/index.js.test.cjs b/tap-snapshots/test/check/snapshots.js.test.cjs similarity index 62% rename from tap-snapshots/test/check/index.js.test.cjs rename to tap-snapshots/test/check/snapshots.js.test.cjs index a641d719..415ef453 100644 --- a/tap-snapshots/test/check/index.js.test.cjs +++ b/tap-snapshots/test/check/snapshots.js.test.cjs @@ -5,7 +5,24 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/check/index.js TAP check empty dir > expect resolving Promise 1`] = ` +exports[`test/check/snapshots.js TAP changelog > expect resolving Promise 1`] = ` +Some problems were detected: + +------------------------------------------------------------------- + +The CHANGELOG.md is incorrect: + + The changelog should start with + "# Changelog + + #" + +To correct it: reformat the changelog to have the correct heading + +------------------------------------------------------------------- +` + +exports[`test/check/snapshots.js TAP check empty dir > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -13,18 +30,18 @@ Some problems were detected: The following repo files need to be added: .commitlintrc.js - .github/workflows/ci.yml - .github/ISSUE_TEMPLATE/bug.yml - .github/ISSUE_TEMPLATE/config.yml .github/CODEOWNERS .github/dependabot.yml + .github/ISSUE_TEMPLATE/bug.yml + .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml + .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml .github/workflows/release-please.yml - .github/workflows/release-test.yml + .github/workflows/release.yml .release-please-manifest.json release-please-config.json @@ -37,8 +54,8 @@ The following module files need to be added: .eslintrc.js .gitignore .npmrc - SECURITY.md CODE_OF_CONDUCT.md + SECURITY.md To correct it: npx template-oss-apply --force @@ -65,6 +82,12 @@ The module file package.json needs to be updated: "test": "tap", "posttest": "npm run lint" } + "tap" is missing, expected { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + } "templateOSS" is missing, expected { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -85,7 +108,168 @@ To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @n ------------------------------------------------------------------- ` -exports[`test/check/index.js TAP workspaces with empty dir > expect resolving Promise 1`] = ` +exports[`test/check/snapshots.js TAP gitignore > expect resolving Promise 1`] = ` +Some problems were detected: + +------------------------------------------------------------------- + +The following files are tracked by git but matching a pattern in .gitignore: + + ignorethis + package-lock.json + +To correct it: move files to not match one of the following patterns: + + /* + !**/.gitignore + !/.commitlintrc.js + !/.eslintrc.js + !/.eslintrc.local.* + !/.github/ + !/.gitignore + !/.npmrc + !/.release-please-manifest.json + !/bin/ + !/CHANGELOG* + !/CODE_OF_CONDUCT.md + !/docs/ + !/lib/ + !/LICENSE* + !/map.js + !/package.json + !/README* + !/release-please-config.json + !/scripts/ + !/SECURITY.md + !/tap-snapshots/ + !/test/ + +------------------------------------------------------------------- +` + +exports[`test/check/snapshots.js TAP gitignore with workspaces workspace > expect resolving Promise 1`] = ` +Some problems were detected: + +------------------------------------------------------------------- + +The following files are tracked by git but matching a pattern in .gitignore: + + ignorethis + +To correct it: move files to not match one of the following patterns: + + /* + !**/.gitignore + !/.commitlintrc.js + !/.eslintrc.js + !/.eslintrc.local.* + !/.github/ + !/.gitignore + !/.npmrc + !/.release-please-manifest.json + !/bin/ + !/CHANGELOG* + !/CODE_OF_CONDUCT.md + !/docs/ + !/lib/ + !/LICENSE* + !/map.js + !/package.json + !/README* + !/release-please-config.json + !/scripts/ + !/SECURITY.md + !/tap-snapshots/ + !/test/ + !/workspaces/ + /workspaces/* + !/workspaces/a/ + !/workspaces/b/ + +------------------------------------------------------------------- + +The following files are tracked by git but matching a pattern in workspaces/a/.gitignore: + + workspaces/a/wsafile + +To correct it: move files to not match one of the following patterns: + + /* + !**/.gitignore + !/.eslintrc.js + !/.eslintrc.local.* + !/.gitignore + !/bin/ + !/CHANGELOG* + !/docs/ + !/lib/ + !/LICENSE* + !/map.js + !/package.json + !/README* + !/scripts/ + !/tap-snapshots/ + !/test/ + +------------------------------------------------------------------- + +The following files are tracked by git but matching a pattern in workspaces/b/.gitignore: + + workspaces/b/wsbfile + +To correct it: move files to not match one of the following patterns: + + /* + !**/.gitignore + !/.eslintrc.js + !/.eslintrc.local.* + !/.gitignore + !/bin/ + !/CHANGELOG* + !/docs/ + !/lib/ + !/LICENSE* + !/map.js + !/package.json + !/README* + !/scripts/ + !/tap-snapshots/ + !/test/ + +------------------------------------------------------------------- +` + +exports[`test/check/snapshots.js TAP not ok without required > expect resolving Promise 1`] = ` +Some problems were detected: + +------------------------------------------------------------------- + +The following required devDependencies were not found: + + @npmcli/template-oss@{{VERSION}} + @npmcli/eslint-config + tap + +To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @npmcli/eslint-config@latest tap@latest --save-dev && npm i @npmcli/template-oss@{{VERSION}} --save-dev --save-exact + +------------------------------------------------------------------- +` + +exports[`test/check/snapshots.js TAP unwanted > expect resolving Promise 1`] = ` +Some problems were detected: + +------------------------------------------------------------------- + +The following unwanted packages were found: + + eslint + +To correct it: npm rm eslint + +------------------------------------------------------------------- +` + +exports[`test/check/snapshots.js TAP workspaces with empty dir > expect resolving Promise 1`] = ` Some problems were detected: ------------------------------------------------------------------- @@ -93,18 +277,18 @@ Some problems were detected: The following repo files need to be added: .commitlintrc.js - .github/workflows/ci.yml - .github/ISSUE_TEMPLATE/bug.yml - .github/ISSUE_TEMPLATE/config.yml .github/CODEOWNERS .github/dependabot.yml + .github/ISSUE_TEMPLATE/bug.yml + .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml + .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml .github/workflows/release-please.yml - .github/workflows/release-test.yml + .github/workflows/release.yml .release-please-manifest.json release-please-config.json @@ -117,8 +301,8 @@ The following module files need to be added: .eslintrc.js .gitignore .npmrc - SECURITY.md CODE_OF_CONDUCT.md + SECURITY.md To correct it: npx template-oss-apply --force @@ -143,7 +327,20 @@ The module file package.json needs to be updated: "lintfix": "npm run lint -- --fix", "snap": "tap", "test": "tap", - "posttest": "npm run lint" + "posttest": "npm run lint", + "test-all": "npm run test -ws -iwr --if-present", + "lint-all": "npm run lint -ws -iwr --if-present" + } + "tap" is missing, expected { + "test-ignore": "^(workspaces/a|workspaces/b)/", + "nyc-arg": [ + "--exclude", + "workspaces/a/**", + "--exclude", + "workspaces/b/**", + "--exclude", + "tap-snapshots/**" + ] } "templateOSS" is missing, expected { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", @@ -166,12 +363,12 @@ To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @n The following repo files need to be added: + .github/matchers/tap.json + .github/workflows/ci-name-aaaa.yml .github/workflows/release-please.yml - .github/workflows/release-test.yml + .github/workflows/release.yml .release-please-manifest.json release-please-config.json - .github/matchers/tap.json - .github/workflows/ci-name-aaaa.yml To correct it: npx template-oss-apply --force @@ -207,6 +404,12 @@ The module file package.json needs to be updated: "test": "tap", "posttest": "npm run lint" } + "tap" is missing, expected { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + } "templateOSS" is missing, expected { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "{{VERSION}}" @@ -228,12 +431,12 @@ To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @n The following repo files need to be added: + .github/matchers/tap.json + .github/workflows/ci-bbb.yml .github/workflows/release-please.yml - .github/workflows/release-test.yml + .github/workflows/release.yml .release-please-manifest.json release-please-config.json - .github/matchers/tap.json - .github/workflows/ci-bbb.yml To correct it: npx template-oss-apply --force @@ -269,6 +472,12 @@ The module file package.json needs to be updated: "test": "tap", "posttest": "npm run lint" } + "tap" is missing, expected { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + } "templateOSS" is missing, expected { "//@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/unwanted.js.test.cjs b/tap-snapshots/test/check/unwanted.js.test.cjs deleted file mode 100644 index b4fb97e7..00000000 --- a/tap-snapshots/test/check/unwanted.js.test.cjs +++ /dev/null @@ -1,20 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/check/unwanted.js TAP unwanted > expect resolving Promise 1`] = ` -Some problems were detected: - -------------------------------------------------------------------- - -The following unwanted packages were found: - - eslint - -To correct it: npm rm eslint - -------------------------------------------------------------------- -` diff --git a/test/apply/allow-paths.js b/test/apply/allow-paths.js new file mode 100644 index 00000000..2cf93fd1 --- /dev/null +++ b/test/apply/allow-paths.js @@ -0,0 +1,47 @@ +const t = require('tap') +const setup = require('../setup.js') + +t.test('allow paths are merged', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + allowPaths: [ + '/a', + '/b', + ], + }, + }, + }) + await s.apply() + + const ignore = await s.readFile('.gitignore') + t.ok(ignore.includes('!/a')) + t.ok(ignore.includes('!/b')) + t.ok(ignore.includes('!/lib/')) +}) + +t.test('works with custom content', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + allowPaths: [ + '/a', + '/b', + ], + }, + }, + testdir: { + content_dir: { + 'paths.json': '{{{json allowPaths}}}', + 'index.js': 'module.exports={rootRepo:{add:{"paths.json":"paths.json"}}}', + }, + }, + }) + await s.apply() + + const paths = await s.readJson('paths.json') + t.equal(paths[0], '/a') + t.equal(paths[1], '/b') +}) diff --git a/test/apply/engines.js b/test/apply/engines.js new file mode 100644 index 00000000..2dab6b72 --- /dev/null +++ b/test/apply/engines.js @@ -0,0 +1,35 @@ +const t = require('tap') +const { join } = require('path') +const setup = require('../setup.js') + +t.test('can set engines and ci separately', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + engines: '>=10', + }, + }, + }) + 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')) +}) + +t.test('latest ci versions', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + ciVersions: 'latest', + }, + }, + }) + await s.apply() + + const pkg = await s.readJson('package.json') + t.equal(pkg.engines.node, '>=18.0.0') +}) diff --git a/test/apply/files-snapshots.js b/test/apply/files-snapshots.js new file mode 100644 index 00000000..79d27bf7 --- /dev/null +++ b/test/apply/files-snapshots.js @@ -0,0 +1,178 @@ +const t = require('tap') +const setup = require('../setup.js') + +t.cleanSnapshot = setup.clean +t.formatSnapshot = setup.format.readdir + +t.test('turn off repo', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: false, + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('turn off module', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootModule: false, + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('turn off root', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: false, + rootModule: false, + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('turn off add/rm types', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: { + rm: false, + }, + rootModule: { + add: false, + }, + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('turn off specific files', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: { + add: { + '.commitlintrc.js': false, + }, + rm: { + '.github/workflows/release-test.yml': false, + }, + }, + rootModule: { + add: { + '.eslintrc.js': false, + }, + rm: { + '.eslintrc.!(js|local.*)': false, + }, + }, + }, + }, + testdir: { + '.github': { + workflows: { + 'release-test.yml': 'exists', + }, + }, + '.eslintrc.yml': 'exists', + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('workspaces with relative content path', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + workspaces: { + a: { + templateOSS: { + content: '../../content_dir2', + defaultContent: false, + }, + }, + }, + testdir: { + content_dir: { 'index.js': 'module.exports={}' }, + content_dir2: { 'index.js': 'module.exports={}' }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('workspaces', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: false, + rootModule: false, + workspaceRepo: false, + workspaces: ['@aaa/aaa', '@bbb/bbb', 'd'], + }, + }, + workspaces: { + a: '@aaa/aaa', + b: '@bbb/bbb', + c: { + templateOSS: { + // this has no effect since its filtered out at root + workspaceRepo: true, + }, + }, + d: { + templateOSS: { + // turn on repo to override root config + workspaceRepo: true, + }, + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('workspaces only (like npm/cli)', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: false, + rootModule: false, + }, + }, + workspaces: { a: 'a', b: 'b' }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) + +t.test('private workspace', async (t) => { + const s = await setup(t, { + package: { + name: 'root-pkg', + }, + workspaces: { + a: { private: true }, + b: 'b', + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdir()) +}) diff --git a/test/apply/full-content.js b/test/apply/full-content.js deleted file mode 100644 index 5308d7b7..00000000 --- a/test/apply/full-content.js +++ /dev/null @@ -1,63 +0,0 @@ -const t = require('tap') -const setup = require('../setup.js') - -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.readdirSource - -t.test('default', async (t) => { - const s = await setup(t) - await s.apply() - await t.resolveMatchSnapshot(s.readdirSource()) -}) - -t.test('workspaces + everything', async (t) => { - const s = await setup(t, { - workspaces: { a: '@name/aaaa', b: 'bbb' }, - testdir: { - '.eslintrc.json': 'DELETE', - '.eslintrc.local.yml': 'KEEP', - workspaces: { - a: { - '.npmrc': 'DELETE', - '.eslintrc.json': 'DELETE', - '.eslintrc.local.yml': 'KEEP', - }, - b: { - '.npmrc': 'DELETE', - '.eslintrc.json': 'DELETE', - '.eslintrc.local.yml': 'KEEP', - }, - }, - }, - }) - await s.apply() - await t.resolveMatchSnapshot(s.readdirSource()) -}) - -t.test('workspaces only', async (t) => { - const s = await setup(t, { - package: { - templateOSS: { - rootRepo: false, - rootModule: false, - }, - }, - workspaces: { a: 'a', b: 'b' }, - }) - await s.apply() - await t.resolveMatchSnapshot(s.readdirSource()) -}) - -t.test('with empty content', async (t) => { - const s = await setup(t, { content: {} }) - await s.apply() - const source = await s.readdirSource() - t.strictSame(Object.keys(source), ['package.json']) - t.strictSame(JSON.parse(source['package.json']), { - name: 'testpkg', - version: '1.0.0', - templateOSS: { - version: setup.pkgVersion, - }, - }) -}) diff --git a/test/apply/index.js b/test/apply/index.js index 442a1a7a..77cbf2f1 100644 --- a/test/apply/index.js +++ b/test/apply/index.js @@ -3,89 +3,292 @@ const fs = require('fs') const { join } = require('path') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.readdir - -t.test('turn off repo', async (t) => { +t.test('turn off root files', async (t) => { const s = await setup(t, { package: { templateOSS: { rootRepo: false, + rootModule: false, + }, + }, + testdir: { + '.github': { + workflows: { + 'release-test.yml': 'exists', + }, }, + '.eslintrc.yml': 'exists', }, }) await s.apply() - await t.resolveMatchSnapshot(s.readdir()) + t.notOk(fs.existsSync(s.join('.commitlintrc.js'))) + t.notOk(fs.existsSync(s.join('.eslintrc.js'))) + t.ok(fs.existsSync(s.join('.github', 'workflows', 'release-test.yml'))) + t.ok(fs.existsSync(s.join('.eslintrc.yml'))) }) -t.test('turn off module', async (t) => { +t.test('turn off root rm only', async (t) => { const s = await setup(t, { package: { templateOSS: { - rootModule: false, + rootRepo: { + rm: false, + }, + rootModule: { + rm: false, + }, + }, + }, + testdir: { + '.github': { + workflows: { + 'release-test.yml': 'exists', + }, }, + '.eslintrc.yml': 'exists', }, }) await s.apply() - await t.resolveMatchSnapshot(s.readdir()) + t.ok(fs.existsSync(s.join('.commitlintrc.js'))) + t.ok(fs.existsSync(s.join('.eslintrc.js'))) + t.ok(fs.existsSync(s.join('.github', 'workflows', 'release-test.yml'))) + t.ok(fs.existsSync(s.join('.eslintrc.yml'))) }) -t.test('turn off all', async (t) => { +t.test('turn off root add only', async (t) => { const s = await setup(t, { package: { templateOSS: { - rootRepo: false, - rootModule: false, + rootRepo: { + add: false, + }, + rootModule: { + add: false, + }, }, }, + testdir: { + '.github': { + workflows: { + 'release-test.yml': 'exists', + }, + }, + '.eslintrc.yml': 'exists', + }, }) await s.apply() - await t.resolveMatchSnapshot(s.readdir()) + t.notOk(fs.existsSync(s.join('.commitlintrc.js'))) + t.notOk(fs.existsSync(s.join('.eslintrc.js'))) + t.notOk(fs.existsSync(s.join('.github', 'workflows', 'release-test.yml'))) + t.notOk(fs.existsSync(s.join('.eslintrc.yml'))) }) -t.test('workspaces', async (t) => { +t.test('turn off specific files', async (t) => { const s = await setup(t, { package: { templateOSS: { - rootRepo: false, - rootModule: false, - workspaceRepo: false, - workspaces: ['@aaa/aaa', '@bbb/bbb', 'd'], + rootRepo: { + add: { + '.commitlintrc.js': false, + }, + rm: { + '.github/workflows/release-test.yml': false, + }, + }, + rootModule: { + add: { + '.eslintrc.js': false, + }, + rm: { + '.eslintrc.!(js|local.*)': false, + }, + }, + }, + }, + testdir: { + '.github': { + workflows: { + 'release-test.yml': 'exists', + }, + }, + '.eslintrc.yml': 'exists', + }, + }) + await s.apply() + t.notOk(fs.existsSync(s.join('.commitlintrc.js'))) + t.notOk(fs.existsSync(s.join('.eslintrc.js'))) + t.ok(fs.existsSync(s.join('.github', 'workflows', 'release-test.yml'))) + t.ok(fs.existsSync(s.join('.eslintrc.yml'))) +}) + +t.test('root can set workspace files', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + workspaceModule: { + add: { '.eslintrc.js': false }, + rm: { '.npmrc': false }, + }, }, }, workspaces: { - a: '@aaa/aaa', - b: '@bbb/bbb', - c: { + a: 'a', + }, + testdir: { + workspaces: { + a: { + '.npmrc': 'exists', + }, + }, + }, + }) + await s.apply() + t.notOk(fs.existsSync(s.join(s.workspaces.a, '.eslintrc.js'))) + t.ok(fs.existsSync(s.join(s.workspaces.a, '.npmrc'))) +}) + +t.test('workspace config can override root', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + workspaceModule: { + add: { '.eslintrc.js': false }, + rm: { '.npmrc': false }, + }, + }, + }, + workspaces: { + a: { templateOSS: { - // this has no effect since its filtered out at root - workspaceRepo: true, + workspaceModule: { + add: { '.eslintrc.js': 'eslintrc.js' }, + rm: { '.npmrc': true }, + }, + }, + }, + }, + testdir: { + workspaces: { + a: { + '.npmrc': 'exists', }, }, - d: { + }, + }) + await s.apply() + t.ok(fs.existsSync(s.join(s.workspaces.a, '.eslintrc.js'))) + t.notOk(fs.existsSync(s.join(s.workspaces.a, '.npmrc'))) +}) + +t.test('workspaces can override content', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + workspaces: { + a: { templateOSS: { - // turn on repo to override root config - workspaceRepo: true, + content: '../../content_dir2', + defaultContent: true, }, }, }, + testdir: { + content_dir: { 'index.js': 'module.exports={}' }, + content_dir2: { + 'x.js': 'exists', + 'index.js': 'module.exports={workspaceRepo:{add:{"x.js":"x.js"}}}', + }, + }, }) await s.apply() - await t.resolveMatchSnapshot(s.readdir()) + t.notOk(fs.existsSync(s.join('.eslintrc.js'))) + t.ok(fs.existsSync(s.join(s.workspaces.a, '.eslintrc.js'))) + t.ok(fs.existsSync(s.join('x.js'))) }) -t.test('workspaces only (like npm/cli)', async (t) => { +t.test('content can override partials', async (t) => { const s = await setup(t, { package: { templateOSS: { - rootRepo: false, - rootModule: false, + content: 'content_dir', + }, + }, + testdir: { + content_dir: { + '_setup-deps.yml': '- run: INSTALL\n', }, }, - workspaces: { a: 'a', b: 'b' }, }) await s.apply() - await t.resolveMatchSnapshot(s.readdir()) + const ci = await s.readFile(join('.github', 'workflows', 'ci.yml')) + t.ok(ci.includes('- run: INSTALL')) + t.notOk(ci.includes('npm i --ignore-scripts --no-audit --no-fund')) +}) + +t.test('content can extend files', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + }, + }, + testdir: { + content_dir: { + // eslint-disable-next-line max-len + 'index.js': 'module.exports={rootRepo:{add:{".github/workflows/release.yml": "release.yml"}}}', + 'release.yml': '{{> release}}\n smoke-publish:\n runs-on: ubuntu-latest', + }, + }, + }) + await s.apply() + const release = await s.readFile(join('.github', 'workflows', 'release.yml')) + t.ok(release.includes('smoke-publish')) +}) + +t.test('config via multiple locations', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + a: 'root-a', + b: 'root-b', + content: 'root-content', + }, + }, + workspaces: { + a: { + templateOSS: { + a: 'ws-a', + b: 'ws-b', + content: 'ws-content', + }, + }, + }, + testdir: { + 'root-content': { + root: '{{defaultBranch}}-{{a}}-{{b}}-{{c}}', + 'index.js': 'module.exports={rootRepo:{add:{"root.txt":"root"}},c:"root-c"}', + }, + workspaces: { + a: { + 'ws-content': { + ws: '{{defaultBranch}}-{{a}}-{{b}}-{{c}}', + 'index.js': 'module.exports={workspaceRepo:{add:{"ws.txt":"ws"}},c:"ws-c"}', + }, + }, + }, + }, + }) + await s.apply() + + const root = await s.readFile('root.txt') + const ws = await s.readFile(join('ws.txt')) + + t.equal(root.split('\n').slice(-1)[0], 'main-root-a-root-b-root-c') + t.equal(ws.split('\n').slice(-1)[0], 'main-ws-a-ws-b-ws-c') }) t.test('private workspace', async (t) => { diff --git a/test/apply/lockfile.js b/test/apply/lockfile.js index 53cb800c..3ffeba99 100644 --- a/test/apply/lockfile.js +++ b/test/apply/lockfile.js @@ -1,12 +1,6 @@ const t = require('tap') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = (obj) => setup.format.readdirSource({ - '.gitignore': obj['.gitignore'], - '.npmrc': obj['.npmrc'], -}) - t.test('lockfile', async (t) => { const s = await setup(t, { package: { @@ -21,8 +15,6 @@ t.test('lockfile', async (t) => { const npmrc = await s.readFile('.npmrc') t.ok(npmrc.includes('package-lock=true')) - - await t.resolveMatchSnapshot(s.readdirSource()) }) t.test('no lockfile by default', async (t) => { @@ -33,6 +25,4 @@ t.test('no lockfile by default', async (t) => { const npmrc = await s.readFile('.npmrc') t.ok(npmrc.includes('package-lock=false')) - - await t.resolveMatchSnapshot(s.readdirSource()) }) diff --git a/test/apply/npm-bin.js b/test/apply/npm-bin.js index 33854cb3..c9e67f39 100644 --- a/test/apply/npm-bin.js +++ b/test/apply/npm-bin.js @@ -2,26 +2,12 @@ const t = require('tap') const { join } = require('path') const setup = require('../setup.js') -t.test('custom node', async (t) => { +t.test('custom npm path', async (t) => { const s = await setup(t, { ok: true, package: { templateOSS: { - npmBin: 'node /path/to/npm', - }, - }, - }) - await s.apply() - const { scripts } = await s.readJson('package.json') - t.equal(scripts.posttest, 'node /path/to/npm run lint') -}) - -t.test('custom node', async (t) => { - const s = await setup(t, { - ok: true, - package: { - templateOSS: { - npmBin: 'node /path/to/npm', + npm: '/path/to/npm', }, }, }) @@ -35,7 +21,7 @@ t.test('relative npm bin with workspaces', async (t) => { ok: true, package: { templateOSS: { - npmBin: 'cli.js', + npm: 'cli.js', }, }, workspaces: { a: '@name/aaaa', b: 'bbb' }, @@ -48,57 +34,3 @@ t.test('relative npm bin with workspaces', async (t) => { t.equal(scriptsA.posttest, 'node ../../cli.js run lint') t.equal(scriptsB.posttest, 'node ../../cli.js run lint') }) - -t.test('npm bin workspaces only with root config', async (t) => { - const s = await setup(t, { - ok: true, - package: { - templateOSS: { - rootRepo: false, - rootModule: false, - npmBin: './cli.js', - }, - }, - workspaces: { a: '@name/aaaa', b: 'bbb' }, - }) - await s.apply() - const { scripts } = await s.readJson('package.json') - const { scripts: scriptsA } = await s.readJson(join(s.workspaces.a, 'package.json')) - const { scripts: scriptsB } = await s.readJson(join(s.workspaces.b, 'package.json')) - t.equal(scripts, undefined) - t.equal(scriptsA.posttest, 'node ../../cli.js run lint') - t.equal(scriptsB.posttest, 'node ../../cli.js run lint') -}) - -t.test('separate workspace configs', async (t) => { - const s = await setup(t, { - ok: true, - package: { - templateOSS: { - rootRepo: false, - rootModule: false, - }, - }, - workspaces: { - a: { - name: 'a', - templateOSS: { - npmBin: 'bin_a.js', - }, - }, - b: { - name: 'b', - templateOSS: { - npmBin: 'bin_b.js', - }, - }, - }, - }) - await s.apply() - const { scripts } = await s.readJson('package.json') - const { scripts: scriptsA } = await s.readJson(join(s.workspaces.a, 'package.json')) - const { scripts: scriptsB } = await s.readJson(join(s.workspaces.b, 'package.json')) - t.equal(scripts, undefined) - t.equal(scriptsA.posttest, 'node bin_a.js run lint') - t.equal(scriptsB.posttest, 'node bin_b.js run lint') -}) diff --git a/test/apply/source-snapshots.js b/test/apply/source-snapshots.js new file mode 100644 index 00000000..f1ec9901 --- /dev/null +++ b/test/apply/source-snapshots.js @@ -0,0 +1,75 @@ +const t = require('tap') +const setup = require('../setup.js') + +t.cleanSnapshot = setup.clean +t.formatSnapshot = setup.format.readdirSource + +t.test('root only', async (t) => { + const s = await setup(t) + await s.apply() + await t.resolveMatchSnapshot(s.readdirSource()) +}) + +t.test('with workspaces', async (t) => { + const s = await setup(t, { + workspaces: { a: 'a', b: 'b' }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdirSource()) +}) + +t.test('workspaces only', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + rootRepo: false, + rootModule: false, + }, + }, + workspaces: { a: 'a', b: 'b' }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdirSource()) +}) + +t.test('with content path', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + testdir: { + content_dir: { + 'index.js': 'module.exports={}', + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdirSource()) +}) + +t.test('workspaces with nested content path', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + workspaces: { + a: { + templateOSS: { + content: '../../content_dir2', + }, + }, + }, + testdir: { + content_dir: { 'index.js': 'module.exports={}' }, + content_dir2: { 'index.js': 'module.exports={}' }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.readdirSource()) +}) diff --git a/test/apply/version.js b/test/apply/version.js new file mode 100644 index 00000000..83e9607d --- /dev/null +++ b/test/apply/version.js @@ -0,0 +1,27 @@ +const t = require('tap') +const { join } = require('path') +const setup = require('../setup.js') + +t.test('applies version', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'empty', + defaultContent: false, + }, + }, + workspaces: { + a: 'a', + }, + testdir: { + empty: {}, + }, + }) + await s.apply() + + const pkg = await s.readJson('package.json') + const pkgA = await s.readJson(join(s.workspaces.a, 'package.json')) + + t.equal(pkg.templateOSS.version, setup.pkgVersion) + t.equal(pkgA.templateOSS.version, setup.pkgVersion) +}) diff --git a/test/check/changelog.js b/test/check/changelog.js deleted file mode 100644 index 317c11c0..00000000 --- a/test/check/changelog.js +++ /dev/null @@ -1,17 +0,0 @@ -const t = require('tap') -const setup = require('../setup.js') - -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.checks - -t.test('will report incorrect changelog', async (t) => { - const s = await setup(t, { - ok: true, - testdir: { - 'CHANGELOG.md': '# changelorg\n\n#', - }, - }) - - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) diff --git a/test/check/diffs.js b/test/check/diff-snapshots.js similarity index 65% rename from test/check/diffs.js rename to test/check/diff-snapshots.js index 478b6dd6..569b362d 100644 --- a/test/check/diffs.js +++ b/test/check/diff-snapshots.js @@ -3,11 +3,9 @@ const { join } = require('path') const setup = require('../setup.js') t.cleanSnapshot = setup.clean -t.formatSnapshot = (a) => Array.isArray(a) - ? setup.format.checks(a) - : setup.format.readdirSource(a) +t.formatSnapshot = setup.format.checks -t.test('update, remove, errors', async (t) => { +t.test('update and remove errors', async (t) => { const s = await setup(t, { ok: true }) await s.apply() @@ -28,20 +26,6 @@ t.test('update, remove, errors', async (t) => { await t.resolveMatchSnapshot(s.check()) }) -t.test('workspaces', async (t) => { - const s = await setup(t, { - ok: true, - workspaces: { a: 'a', b: 'b' }, - }) - - await s.apply() - - await s.writeFile(join(s.workspaces.a, '.npmrc'), 'no workspace npmrc') - await s.writeFile(join(s.workspaces.b, '.npmrc'), 'no workspace npmrc') - - await t.resolveMatchSnapshot(s.check()) -}) - t.test('will diff json', async (t) => { const s = await setup(t, { ok: true }) await s.apply() @@ -62,6 +46,12 @@ t.test('will diff json', async (t) => { t.test('json overwrite', async (t) => { const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, testdir: { 'target.json': JSON.stringify({ a: 1 }), content: { @@ -69,17 +59,21 @@ t.test('json overwrite', async (t) => { 'index.js': `module.exports={rootRepo:{add:{'target.json':'source.json'}}}`, }, }, - content: 'content', }) - await t.resolveMatchSnapshot(s.check(), 'initial check') + await t.resolveMatchSnapshot(s.check()) await s.apply() t.strictSame(await s.check(), []) - await t.resolveMatchSnapshot(s.readdirSource(), 'source after apply') }) t.test('json merge', async (t) => { const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, testdir: { 'target.json': JSON.stringify({ a: 1 }), content: { @@ -87,17 +81,21 @@ t.test('json merge', async (t) => { 'index.js': await setup.fixture('json-merge.js'), }, }, - content: 'content', }) await t.resolveMatchSnapshot(s.check(), 'initial check') await s.apply() t.strictSame(await s.check(), []) - await t.resolveMatchSnapshot(s.readdirSource(), 'source after apply') }) t.test('json delete', async (t) => { const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, testdir: { 'target.json': JSON.stringify({ a: 1 }), content: { @@ -105,59 +103,51 @@ t.test('json delete', async (t) => { 'index.js': await setup.fixture('json-delete.js'), }, }, - content: 'content', }) - await t.resolveMatchSnapshot(s.check(), 'initial check') - await s.apply() - t.strictSame(await s.check(), []) - await t.resolveMatchSnapshot(s.readdirSource(), 'source after apply') -}) - -t.test('node 10', async (t) => { - const s = await setup(t, { ok: true }) + await t.resolveMatchSnapshot(s.check()) await s.apply() - t.strictSame(await s.check(), []) - - const pkg = await s.readJson('package.json') - pkg.templateOSS.ciVersions = [...setup.content.ciVersions, '10'] - await s.writeJson('package.json', pkg) - - await s.apply() - await t.resolveMatchSnapshot(s.check()) }) t.test('different headers', async (t) => { const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, testdir: { content: { 'source.txt': 'source', 'index.js': await setup.fixture('header.js'), }, }, - content: 'content', }) - await t.resolveMatchSnapshot(s.check(), 'initial check') + await t.resolveMatchSnapshot(s.check()) await s.apply() t.strictSame(await s.check(), []) - await t.resolveMatchSnapshot(s.readdirSource(), 'source after apply') }) t.test('unknown file type', async (t) => { const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, testdir: { content: { 'source.txt': 'source', 'index.js': `module.exports={rootRepo:{add:{'target.txt':'source.txt'}}}`, }, }, - content: 'content', }) - await t.resolveMatchSnapshot(s.check(), 'initial check') + await t.resolveMatchSnapshot(s.check()) await s.apply() t.strictSame(await s.check(), []) - await t.resolveMatchSnapshot(s.readdirSource(), 'source after apply') }) diff --git a/test/check/dogfood.js b/test/check/dogfood.js index 51ff024b..a9a323cc 100644 --- a/test/check/dogfood.js +++ b/test/check/dogfood.js @@ -4,6 +4,6 @@ const check = require('../../lib/check/index.js') t.test('this repo passes all checks', async (t) => { const root = resolve(__dirname, '..', '..') - const res = await check(root, resolve(root, 'lib', 'content')) + const res = await check(root) t.equal(res.length, 0) }) diff --git a/test/check/gitignore.js b/test/check/gitignore.js index f12b7f4b..614de7dc 100644 --- a/test/check/gitignore.js +++ b/test/check/gitignore.js @@ -1,72 +1,6 @@ -const { join } = require('path') const t = require('tap') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.checks - -t.test('will report tracked files in gitignore', async (t) => { - const s = await setup.git(t, { ok: true }) - - await s.writeFile('ignorethis', 'empty') - await s.writeFile('package-lock.json', '{}') - - await s.gca() - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) - -t.test('will report tracked files in gitignore workspace', async (t) => { - const s = await setup.git(t, { - ok: true, - workspaces: { - a: '@aaa/a', - b: 'b', - }, - }) - - await s.writeFile('ignorethis', 'empty') - await s.writeFile(join(s.workspaces.a, 'wsafile'), 'empty') - await s.writeFile(join(s.workspaces.b, 'wsbfile'), 'empty') - - await s.gca() - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) - -t.test('works with workspaces in separate dirs', async (t) => { - const s = await setup.git(t, { - ok: true, - package: { - workspaces: ['workspace-a', 'workspace-b'], - }, - testdir: { - 'workspace-a': { - 'package.json': JSON.stringify({ - name: 'a', - version: '1.0.0', - ...setup.okPackage(), - }), - }, - 'workspace-b': { - 'package.json': JSON.stringify({ - name: 'b', - version: '1.0.0', - ...setup.okPackage(), - }), - }, - }, - }) - - await s.writeFile('ignorethis', 'empty') - await s.writeFile(join('workspace-a', 'wsafile'), 'empty') - await s.writeFile(join('workspace-b', 'wsbfile'), 'empty') - - await s.gca() - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) - t.test('allow package-lock', async (t) => { const s = await setup.git(t, { ok: true, diff --git a/test/check/index.js b/test/check/index.js index 369b5511..d0205c9a 100644 --- a/test/check/index.js +++ b/test/check/index.js @@ -1,22 +1,17 @@ const t = require('tap') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.checks - -t.test('check empty dir', async (t) => { - const s = await setup(t) - await t.resolveMatchSnapshot(s.check()) -}) - -t.test('workspaces with empty dir', async (t) => { +t.test('empty content is ok', async (t) => { const s = await setup(t, { - workspaces: { a: '@name/aaaa', b: 'bbb' }, + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + testdir: { + content_dir: { 'index.js': 'module.exports={}' }, + }, }) - await t.resolveMatchSnapshot(s.check()) -}) - -t.test('with empty content', async (t) => { - const s = await setup(t, { content: {} }) t.same(await s.check(), []) }) diff --git a/test/check/required.js b/test/check/required.js index 66e68ec6..b94cfd3d 100644 --- a/test/check/required.js +++ b/test/check/required.js @@ -1,9 +1,6 @@ const t = require('tap') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.checks - t.test('ok with required', async (t) => { const s = await setup(t, { ok: true, @@ -12,12 +9,6 @@ t.test('ok with required', async (t) => { t.strictSame(await s.check(), []) }) -t.test('not ok without required', async (t) => { - const s = await setup(t) - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) - t.test('required in each location', async (t) => { const s = await setup(t, { package: { diff --git a/test/check/snapshots.js b/test/check/snapshots.js new file mode 100644 index 00000000..07fd3c38 --- /dev/null +++ b/test/check/snapshots.js @@ -0,0 +1,77 @@ +const t = require('tap') +const { join } = require('path') +const setup = require('../setup.js') + +t.cleanSnapshot = setup.clean +t.formatSnapshot = setup.format.checks + +t.test('check empty dir', async (t) => { + const s = await setup(t) + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('workspaces with empty dir', async (t) => { + const s = await setup(t, { + workspaces: { a: '@name/aaaa', b: 'bbb' }, + }) + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('not ok without required', async (t) => { + const s = await setup(t) + await s.apply() + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('changelog', async (t) => { + const s = await setup(t, { + ok: true, + testdir: { + 'CHANGELOG.md': '# changelorg\n\n#', + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('gitignore', async (t) => { + const s = await setup.git(t, { ok: true }) + + await s.writeFile('ignorethis', 'empty') + await s.writeFile('package-lock.json', '{}') + + await s.gca() + await s.apply() + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('gitignore with workspaces workspace', async (t) => { + const s = await setup.git(t, { + ok: true, + workspaces: { + a: '@aaa/a', + b: 'b', + }, + }) + + await s.writeFile('ignorethis', 'empty') + await s.writeFile(join(s.workspaces.a, 'wsafile'), 'empty') + await s.writeFile(join(s.workspaces.b, 'wsbfile'), 'empty') + + await s.gca() + await s.apply() + await t.resolveMatchSnapshot(s.check()) +}) + +t.test('unwanted', async (t) => { + const s = await setup(t, { + ok: true, + package: { + dependencies: { + eslint: '^8.0.0', + }, + }, + }) + await s.apply() + await t.resolveMatchSnapshot(s.check()) +}) diff --git a/test/check/unwanted.js b/test/check/unwanted.js index c145f550..1d49912c 100644 --- a/test/check/unwanted.js +++ b/test/check/unwanted.js @@ -1,23 +1,6 @@ const t = require('tap') const setup = require('../setup.js') -t.cleanSnapshot = setup.clean -t.formatSnapshot = setup.format.checks - -t.test('unwanted', async (t) => { - const s = await setup(t, { - ok: true, - package: { - dependencies: { - eslint: '^8.0.0', - }, - }, - }) - - await s.apply() - await t.resolveMatchSnapshot(s.check()) -}) - t.test('unwanted can be overriden with allow', async (t) => { const s = await setup(t, { ok: true, diff --git a/test/index.js b/test/index.js index 0783d3f1..f7cec74a 100644 --- a/test/index.js +++ b/test/index.js @@ -13,19 +13,15 @@ t.test('apply and check multiple is ok', async (t) => { }) t.test('empty content is ok', async (t) => { - const s = await setup(t, { content: {} }) - t.same(await s.runAll(), []) -}) - -t.test('empty file objects', async (t) => { - const s = await setup.git(t, { - ok: true, - workspaces: { a: 'a', b: 'b' }, - content: { - rootRepo: {}, - rootModule: {}, - workspaceRepo: {}, - workspaceModule: {}, + const s = await setup(t, { + package: { + templateOSS: { + content: 'content_dir', + defaultContent: false, + }, + }, + testdir: { + content_dir: { 'index.js': 'module.exports={}' }, }, }) t.same(await s.runAll(), []) diff --git a/test/setup.js b/test/setup.js index 794110ac..c36ebb5b 100644 --- a/test/setup.js +++ b/test/setup.js @@ -1,8 +1,9 @@ const t = require('tap') -const { join, isAbsolute, resolve } = require('path') +const { join, resolve, posix } = require('path') const { merge, defaults, escapeRegExp: esc } = require('lodash') const fs = require('@npmcli/fs') const Git = require('@npmcli/git') +const localeCompare = require('@isaacs/string-locale-compare')('en') const npa = require('npm-package-arg') const output = require('../lib/util/output.js') const apply = require('../lib/apply/index.js') @@ -33,9 +34,16 @@ const okPackage = () => Object.entries(CONTENT.requiredPackages) return [arg.name, arg.fetchSpec === 'latest' ? '*' : arg.fetchSpec] })) return acc - }, {}) - -const setupRoot = async (root, content) => { + }, { + tap: { + 'nyc-arg': [ + '--exclude', + 'tap-snapshots/**', + ], + }, + }) + +const setupRoot = async (root) => { const rootPath = (...p) => join(root, ...p) // fs methods for reading from the root @@ -83,10 +91,9 @@ const setupRoot = async (root, content) => { readJson: async (f) => JSON.parse(await rootFs.readFile(f)), writeJson: (p, d) => rootFs.writeFile(p, JSON.stringify(d, null, 2)), join: rootPath, - // use passed in content path or allow overriding per method call for tests - apply: () => apply(root, content), - check: () => check(root, content), - runAll: () => apply(root, content).then(() => check(root, content)), + apply: () => apply(root), + check: () => check(root), + runAll: () => apply(root).then(() => check(root)), } } @@ -94,7 +101,6 @@ const setup = async (t, { package = {}, workspaces = {}, testdir = {}, - content, ok, } = {}) => { const wsLookup = {} @@ -117,7 +123,7 @@ const setup = async (t, { pkgWithName(wsPkgName, wsBase), ok ? okPackage() : {} ) - const wsPath = join(wsDir, wsBase) + const wsPath = posix.join(wsDir, wsBase) // obj to lookup workspaces by path in tests wsLookup[wsBase] = wsPath merge(testdir[wsDir], { [wsBase]: createPackageJson(wsPkg) }) @@ -132,15 +138,8 @@ const setup = async (t, { testdir )) - if (typeof content === 'string') { - // default content path is absolute but tests can either setup their - // own relative path or pass in objects. But if it is a path it has - // to be absolute to be passed in - content = isAbsolute(content) ? content : join(root, content) - } - return { - ...(await setupRoot(root, content)), + ...(await setupRoot(root)), workspaces: wsLookup, } } @@ -170,10 +169,13 @@ const cleanSnapshot = (str) => str .replace(/\r\n/g, '\n') .replace(new RegExp(`("version": "|${esc(NAME)}@)${esc(VERSION)}`, 'g'), '$1{{VERSION}}') const formatSnapshots = { - readdir: (arr) => arr.join('\n').trim(), - readdirSource: (obj) => Object.entries(obj).map(([file, content]) => - [file, '='.repeat(40), content].join('\n').trim()).join('\n\n').trim(), checks: (arr) => output(arr).trim(), + readdir: (arr) => arr.sort(localeCompare).join('\n').trim(), + readdirSource: (obj) => Object.entries(obj) + .sort((a, b) => localeCompare(a[0], b[0])) + .map(([file, content]) => [file, '='.repeat(40), content].join('\n').trim()) + .join('\n\n') + .trim(), } module.exports = setup