Merge pull request #2741 from semantic-release/beta
upgraded to next major version of the npm plugin
This commit is contained in:
		
						commit
						deb1b7f9cb
					
				
							
								
								
									
										2
									
								
								.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| # style: prettier (#2670) | ||||
| b06c9bbe4c6be121c5561b356d8c465c1cadffba | ||||
							
								
								
									
										1
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -14,6 +14,7 @@ jobs: | ||||
|       contents: write # to be able to publish a GitHub release | ||||
|       issues: write # to be able to comment on released issues | ||||
|       pull-requests: write # to be able to comment on released pull requests | ||||
|       id-token: write # to enable use of OIDC for npm provenance | ||||
|     name: release | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|  | ||||
| @ -1,18 +1,18 @@ | ||||
| import {isString, mapValues, omit, remove, template} from 'lodash-es'; | ||||
| import micromatch from 'micromatch'; | ||||
| import {getBranches} from '../git.js'; | ||||
| import { isString, mapValues, omit, remove, template } from "lodash-es"; | ||||
| import micromatch from "micromatch"; | ||||
| import { getBranches } from "../git.js"; | ||||
| 
 | ||||
| export default async (repositoryUrl, {cwd}, branches) => { | ||||
|   const gitBranches = await getBranches(repositoryUrl, {cwd}); | ||||
| export default async (repositoryUrl, { cwd }, branches) => { | ||||
|   const gitBranches = await getBranches(repositoryUrl, { cwd }); | ||||
| 
 | ||||
|   return branches.reduce( | ||||
|     (branches, branch) => [ | ||||
|       ...branches, | ||||
|       ...remove(gitBranches, (name) => micromatch(gitBranches, branch.name).includes(name)).map((name) => ({ | ||||
|         name, | ||||
|         ...mapValues(omit(branch, 'name'), (value) => (isString(value) ? template(value)({name}) : value)), | ||||
|         ...mapValues(omit(branch, "name"), (value) => (isString(value) ? template(value)({ name }) : value)), | ||||
|       })), | ||||
|     ], | ||||
|     [] | ||||
|   ); | ||||
| } | ||||
| }; | ||||
|  | ||||
| @ -1,36 +1,35 @@ | ||||
| import {escapeRegExp, template} from 'lodash-es'; | ||||
| import semver from 'semver'; | ||||
| import pReduce from 'p-reduce'; | ||||
| import debugTags from 'debug'; | ||||
| import {getNote, getTags} from '../../lib/git.js'; | ||||
| import { escapeRegExp, template } from "lodash-es"; | ||||
| import semver from "semver"; | ||||
| import pReduce from "p-reduce"; | ||||
| import debugTags from "debug"; | ||||
| import { getNote, getTags } from "../../lib/git.js"; | ||||
| 
 | ||||
| const debug = debugTags('semantic-release:get-tags'); | ||||
| const debug = debugTags("semantic-release:get-tags"); | ||||
| 
 | ||||
| 
 | ||||
| export default async ({cwd, env, options: {tagFormat}}, branches) => { | ||||
| export default async ({ cwd, env, options: { tagFormat } }, branches) => { | ||||
|   // Generate a regex to parse tags formatted with `tagFormat`
 | ||||
|   // by replacing the `version` variable in the template by `(.+)`.
 | ||||
|   // The `tagFormat` is compiled with space as the `version` as it's an invalid tag character,
 | ||||
|   // so it's guaranteed to no be present in the `tagFormat`.
 | ||||
|   const tagRegexp = `^${escapeRegExp(template(tagFormat)({version: ' '})).replace(' ', '(.+)')}`; | ||||
|   const tagRegexp = `^${escapeRegExp(template(tagFormat)({ version: " " })).replace(" ", "(.+)")}`; | ||||
| 
 | ||||
|   return pReduce( | ||||
|     branches, | ||||
|     async (branches, branch) => { | ||||
|       const branchTags = await pReduce( | ||||
|         await getTags(branch.name, {cwd, env}), | ||||
|         await getTags(branch.name, { cwd, env }), | ||||
|         async (branchTags, tag) => { | ||||
|           const [, version] = tag.match(tagRegexp) || []; | ||||
|           return version && semver.valid(semver.clean(version)) | ||||
|             ? [...branchTags, {gitTag: tag, version, channels: (await getNote(tag, {cwd, env})).channels || [null]}] | ||||
|             ? [...branchTags, { gitTag: tag, version, channels: (await getNote(tag, { cwd, env })).channels || [null] }] | ||||
|             : branchTags; | ||||
|         }, | ||||
|         [] | ||||
|       ); | ||||
| 
 | ||||
|       debug('found tags for branch %s: %o', branch.name, branchTags); | ||||
|       return [...branches, {...branch, tags: branchTags}]; | ||||
|       debug("found tags for branch %s: %o", branch.name, branchTags); | ||||
|       return [...branches, { ...branch, tags: branchTags }]; | ||||
|     }, | ||||
|     [] | ||||
|   ); | ||||
| } | ||||
| }; | ||||
|  | ||||
| @ -1,51 +1,51 @@ | ||||
| import {isRegExp, isString} from 'lodash-es'; | ||||
| import AggregateError from 'aggregate-error'; | ||||
| import pEachSeries from 'p-each-series'; | ||||
| import * as DEFINITIONS from '../definitions/branches.js'; | ||||
| import getError from '../get-error.js'; | ||||
| import {fetch, fetchNotes, verifyBranchName} from '../git.js'; | ||||
| import expand from './expand.js'; | ||||
| import getTags from './get-tags.js'; | ||||
| import * as normalize from './normalize.js'; | ||||
| import { isRegExp, isString } from "lodash-es"; | ||||
| import AggregateError from "aggregate-error"; | ||||
| import pEachSeries from "p-each-series"; | ||||
| import * as DEFINITIONS from "../definitions/branches.js"; | ||||
| import getError from "../get-error.js"; | ||||
| import { fetch, fetchNotes, verifyBranchName } from "../git.js"; | ||||
| import expand from "./expand.js"; | ||||
| import getTags from "./get-tags.js"; | ||||
| import * as normalize from "./normalize.js"; | ||||
| 
 | ||||
| export default async (repositoryUrl, ciBranch, context) => { | ||||
|   const {cwd, env} = context; | ||||
|   const { cwd, env } = context; | ||||
| 
 | ||||
|   const remoteBranches = await expand( | ||||
|     repositoryUrl, | ||||
|     context, | ||||
|     context.options.branches.map((branch) => (isString(branch) || isRegExp(branch) ? {name: branch} : branch)) | ||||
|     context.options.branches.map((branch) => (isString(branch) || isRegExp(branch) ? { name: branch } : branch)) | ||||
|   ); | ||||
| 
 | ||||
|   await pEachSeries(remoteBranches, async ({name}) => { | ||||
|     await fetch(repositoryUrl, name, ciBranch, {cwd, env}); | ||||
|   await pEachSeries(remoteBranches, async ({ name }) => { | ||||
|     await fetch(repositoryUrl, name, ciBranch, { cwd, env }); | ||||
|   }); | ||||
| 
 | ||||
|   await fetchNotes(repositoryUrl, {cwd, env}); | ||||
|   await fetchNotes(repositoryUrl, { cwd, env }); | ||||
| 
 | ||||
|   const branches = await getTags(context, remoteBranches); | ||||
| 
 | ||||
|   const errors = []; | ||||
|   const branchesByType = Object.entries(DEFINITIONS).reduce( | ||||
|     // eslint-disable-next-line unicorn/no-fn-reference-in-iterator
 | ||||
|     (branchesByType, [type, {filter}]) => ({[type]: branches.filter(filter), ...branchesByType}), | ||||
|     (branchesByType, [type, { filter }]) => ({ [type]: branches.filter(filter), ...branchesByType }), | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const result = Object.entries(DEFINITIONS).reduce((result, [type, {branchesValidator, branchValidator}]) => { | ||||
|   const result = Object.entries(DEFINITIONS).reduce((result, [type, { branchesValidator, branchValidator }]) => { | ||||
|     branchesByType[type].forEach((branch) => { | ||||
|       if (branchValidator && !branchValidator(branch)) { | ||||
|         errors.push(getError(`E${type.toUpperCase()}BRANCH`, {branch})); | ||||
|         errors.push(getError(`E${type.toUpperCase()}BRANCH`, { branch })); | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     const branchesOfType = normalize[type](branchesByType); | ||||
| 
 | ||||
|     if (!branchesValidator(branchesOfType)) { | ||||
|       errors.push(getError(`E${type.toUpperCase()}BRANCHES`, {branches: branchesOfType})); | ||||
|       errors.push(getError(`E${type.toUpperCase()}BRANCHES`, { branches: branchesOfType })); | ||||
|     } | ||||
| 
 | ||||
|     return {...result, [type]: branchesOfType}; | ||||
|     return { ...result, [type]: branchesOfType }; | ||||
|   }, {}); | ||||
| 
 | ||||
|   const duplicates = [...branches] | ||||
| @ -54,12 +54,12 @@ export default async (repositoryUrl, ciBranch, context) => { | ||||
|     .filter((_, idx, array) => array[idx] === array[idx + 1] && array[idx] !== array[idx - 1]); | ||||
| 
 | ||||
|   if (duplicates.length > 0) { | ||||
|     errors.push(getError('EDUPLICATEBRANCHES', {duplicates})); | ||||
|     errors.push(getError("EDUPLICATEBRANCHES", { duplicates })); | ||||
|   } | ||||
| 
 | ||||
|   await pEachSeries(branches, async (branch) => { | ||||
|     if (!(await verifyBranchName(branch.name))) { | ||||
|       errors.push(getError('EINVALIDBRANCHNAME', branch)); | ||||
|       errors.push(getError("EINVALIDBRANCHNAME", branch)); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
| @ -68,4 +68,4 @@ export default async (repositoryUrl, ciBranch, context) => { | ||||
|   } | ||||
| 
 | ||||
|   return [...result.maintenance, ...result.release, ...result.prerelease]; | ||||
| } | ||||
| }; | ||||
|  | ||||
| @ -1,27 +1,28 @@ | ||||
| import {isNil, sortBy} from 'lodash-es'; | ||||
| import semverDiff from 'semver-diff'; | ||||
| import {FIRST_RELEASE, RELEASE_TYPE} from '../definitions/constants.js'; | ||||
| import { isNil, sortBy } from "lodash-es"; | ||||
| import semverDiff from "semver-diff"; | ||||
| import { FIRST_RELEASE, RELEASE_TYPE } from "../definitions/constants.js"; | ||||
| import { | ||||
|   getFirstVersion, | ||||
|   getLatestVersion, | ||||
|   getLowerBound, getRange, | ||||
|   getLowerBound, | ||||
|   getRange, | ||||
|   getUpperBound, | ||||
|   highest, | ||||
|   isMajorRange, | ||||
|   lowest, | ||||
|   tagsToVersions | ||||
| } from '../utils.js'; | ||||
|   tagsToVersions, | ||||
| } from "../utils.js"; | ||||
| 
 | ||||
| export function maintenance({maintenance, release}) { | ||||
| export function maintenance({ maintenance, release }) { | ||||
|   return sortBy( | ||||
|     maintenance.map(({name, range, channel, ...rest}) => ({ | ||||
|     maintenance.map(({ name, range, channel, ...rest }) => ({ | ||||
|       ...rest, | ||||
|       name, | ||||
|       range: range || name, | ||||
|       channel: isNil(channel) ? name : channel, | ||||
|     })), | ||||
|     'range' | ||||
|   ).map(({name, range, tags, ...rest}, idx, branches) => { | ||||
|     "range" | ||||
|   ).map(({ name, range, tags, ...rest }, idx, branches) => { | ||||
|     const versions = tagsToVersions(tags); | ||||
|     // Find the lower bound based on Maintenance branches
 | ||||
|     const maintenanceMin = | ||||
| @ -44,7 +45,7 @@ export function maintenance({maintenance, release}) { | ||||
|     const diff = semverDiff(min, max); | ||||
|     return { | ||||
|       ...rest, | ||||
|       type: 'maintenance', | ||||
|       type: "maintenance", | ||||
|       name, | ||||
|       tags, | ||||
|       range: getRange(min, max), | ||||
| @ -54,7 +55,7 @@ export function maintenance({maintenance, release}) { | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export function release({release}) { | ||||
| export function release({ release }) { | ||||
|   if (release.length === 0) { | ||||
|     return release; | ||||
|   } | ||||
| @ -62,7 +63,7 @@ export function release({release}) { | ||||
|   // The intial lastVersion is the last release from the base branch of `FIRST_RELEASE` (1.0.0)
 | ||||
|   let lastVersion = getLatestVersion(tagsToVersions(release[0].tags)) || FIRST_RELEASE; | ||||
| 
 | ||||
|   return release.map(({name, tags, channel, ...rest}, idx) => { | ||||
|   return release.map(({ name, tags, channel, ...rest }, idx) => { | ||||
|     const versions = tagsToVersions(tags); | ||||
|     // The new lastVersion is the highest version between the current branch last release and the previous branch lastVersion
 | ||||
|     lastVersion = highest(getLatestVersion(versions), lastVersion); | ||||
| @ -79,7 +80,7 @@ export function release({release}) { | ||||
|       ...rest, | ||||
|       channel: idx === 0 ? channel : isNil(channel) ? name : channel, | ||||
|       tags, | ||||
|       type: 'release', | ||||
|       type: "release", | ||||
|       name, | ||||
|       range: getRange(lastVersion, bound), | ||||
|       accept: bound ? RELEASE_TYPE.slice(0, RELEASE_TYPE.indexOf(diff)) : RELEASE_TYPE, | ||||
| @ -88,13 +89,13 @@ export function release({release}) { | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export function prerelease({prerelease}) { | ||||
|   return prerelease.map(({name, prerelease, channel, tags, ...rest}) => { | ||||
| export function prerelease({ prerelease }) { | ||||
|   return prerelease.map(({ name, prerelease, channel, tags, ...rest }) => { | ||||
|     const preid = prerelease === true ? name : prerelease; | ||||
|     return { | ||||
|       ...rest, | ||||
|       channel: isNil(channel) ? name : channel, | ||||
|       type: 'prerelease', | ||||
|       type: "prerelease", | ||||
|       name, | ||||
|       prerelease: preid, | ||||
|       tags, | ||||
|  | ||||
| @ -1,18 +1,18 @@ | ||||
| import {isNil, uniqBy} from 'lodash-es'; | ||||
| import semver from 'semver'; | ||||
| import {isMaintenanceRange} from '../utils.js'; | ||||
| import { isNil, uniqBy } from "lodash-es"; | ||||
| import semver from "semver"; | ||||
| import { isMaintenanceRange } from "../utils.js"; | ||||
| 
 | ||||
| export const maintenance = { | ||||
|   filter: ({name, range}) => (!isNil(range) && range !== false) || isMaintenanceRange(name), | ||||
|   branchValidator: ({range}) => (isNil(range) ? true : isMaintenanceRange(range)), | ||||
|   branchesValidator: (branches) => uniqBy(branches, ({range}) => semver.validRange(range)).length === branches.length, | ||||
|   filter: ({ name, range }) => (!isNil(range) && range !== false) || isMaintenanceRange(name), | ||||
|   branchValidator: ({ range }) => (isNil(range) ? true : isMaintenanceRange(range)), | ||||
|   branchesValidator: (branches) => uniqBy(branches, ({ range }) => semver.validRange(range)).length === branches.length, | ||||
| }; | ||||
| 
 | ||||
| export const prerelease = { | ||||
|   filter: ({prerelease}) => !isNil(prerelease) && prerelease !== false, | ||||
|   branchValidator: ({name, prerelease}) => | ||||
|   filter: ({ prerelease }) => !isNil(prerelease) && prerelease !== false, | ||||
|   branchValidator: ({ name, prerelease }) => | ||||
|     Boolean(prerelease) && Boolean(semver.valid(`1.0.0-${prerelease === true ? name : prerelease}.1`)), | ||||
|   branchesValidator: (branches) => uniqBy(branches, 'prerelease').length === branches.length, | ||||
|   branchesValidator: (branches) => uniqBy(branches, "prerelease").length === branches.length, | ||||
| }; | ||||
| 
 | ||||
| export const release = { | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| export const RELEASE_TYPE = ['patch', 'minor', 'major']; | ||||
| export const RELEASE_TYPE = ["patch", "minor", "major"]; | ||||
| 
 | ||||
| export const FIRST_RELEASE = '1.0.0'; | ||||
| export const FIRST_RELEASE = "1.0.0"; | ||||
| 
 | ||||
| export const FIRSTPRERELEASE = '1'; | ||||
| export const FIRSTPRERELEASE = "1"; | ||||
| 
 | ||||
| export const COMMIT_NAME = 'semantic-release-bot'; | ||||
| export const COMMIT_NAME = "semantic-release-bot"; | ||||
| 
 | ||||
| export const COMMIT_EMAIL = 'semantic-release-bot@martynus.net'; | ||||
| export const COMMIT_EMAIL = "semantic-release-bot@martynus.net"; | ||||
| 
 | ||||
| export const RELEASE_NOTES_SEPARATOR = '\n\n'; | ||||
| export const RELEASE_NOTES_SEPARATOR = "\n\n"; | ||||
| 
 | ||||
| export const SECRET_REPLACEMENT = '[secure]'; | ||||
| export const SECRET_REPLACEMENT = "[secure]"; | ||||
| 
 | ||||
| export const SECRET_MIN_SIZE = 5; | ||||
| 
 | ||||
| export const GIT_NOTE_REF = 'semantic-release'; | ||||
| export const GIT_NOTE_REF = "semantic-release"; | ||||
|  | ||||
| @ -1,21 +1,21 @@ | ||||
| import {inspect} from 'node:util'; | ||||
| import {createRequire} from 'node:module'; | ||||
| import {isString, toLower, trim} from 'lodash-es'; | ||||
| import {RELEASE_TYPE} from './constants.js'; | ||||
| import { inspect } from "node:util"; | ||||
| import { createRequire } from "node:module"; | ||||
| import { isString, toLower, trim } from "lodash-es"; | ||||
| import { RELEASE_TYPE } from "./constants.js"; | ||||
| 
 | ||||
| const require = createRequire(import.meta.url); | ||||
| const pkg = require('../../package.json'); | ||||
| const pkg = require("../../package.json"); | ||||
| 
 | ||||
| const [homepage] = pkg.homepage.split('#'); | ||||
| const [homepage] = pkg.homepage.split("#"); | ||||
| const stringify = (object) => | ||||
|   isString(object) ? object : inspect(object, {breakLength: Infinity, depth: 2, maxArrayLength: 5}); | ||||
|   isString(object) ? object : inspect(object, { breakLength: Infinity, depth: 2, maxArrayLength: 5 }); | ||||
| const linkify = (file) => `${homepage}/blob/master/${file}`; | ||||
| const wordsList = (words) => | ||||
|   `${words.slice(0, -1).join(', ')}${words.length > 1 ? ` or ${words[words.length - 1]}` : trim(words[0])}`; | ||||
|   `${words.slice(0, -1).join(", ")}${words.length > 1 ? ` or ${words[words.length - 1]}` : trim(words[0])}`; | ||||
| 
 | ||||
| export function ENOGITREPO({cwd}) { | ||||
| export function ENOGITREPO({ cwd }) { | ||||
|   return { | ||||
|     message: 'Not running from a git repository.', | ||||
|     message: "Not running from a git repository.", | ||||
|     details: `The \`semantic-release\` command must be executed from a Git repository.
 | ||||
| 
 | ||||
| The current working directory is \`${cwd}\`.
 | ||||
| @ -26,76 +26,76 @@ Please verify your CI configuration to make sure the \`semantic-release\` comman | ||||
| 
 | ||||
| export function ENOREPOURL() { | ||||
|   return { | ||||
|     message: 'The `repositoryUrl` option is required.', | ||||
|     message: "The `repositoryUrl` option is required.", | ||||
|     details: `The [repositoryUrl option](${linkify( | ||||
|       'docs/usage/configuration.md#repositoryurl' | ||||
|       "docs/usage/configuration.md#repositoryurl" | ||||
|     )}) cannot be determined from the semantic-release configuration, the \`package.json\` nor the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes).
 | ||||
| 
 | ||||
| Please make sure to add the \`repositoryUrl\` to the [semantic-release configuration] (${linkify( | ||||
|       'docs/usage/configuration.md' | ||||
|       "docs/usage/configuration.md" | ||||
|     )}).`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EGITNOPERMISSION({options: {repositoryUrl}, branch: {name}}) { | ||||
| export function EGITNOPERMISSION({ options: { repositoryUrl }, branch: { name } }) { | ||||
|   return { | ||||
|     message: 'Cannot push to the Git repository.', | ||||
|     message: "Cannot push to the Git repository.", | ||||
|     details: `**semantic-release** cannot push the version tag to the branch \`${name}\` on the remote Git repository with URL \`${repositoryUrl}\`.
 | ||||
| 
 | ||||
| This can be caused by: | ||||
|  - a misconfiguration of the [repositoryUrl](${linkify('docs/usage/configuration.md#repositoryurl')}) option | ||||
|  - a misconfiguration of the [repositoryUrl](${linkify("docs/usage/configuration.md#repositoryurl")}) option | ||||
|  - the repository being unavailable | ||||
|  - or missing push permission for the user configured via the [Git credentials on your CI environment](${linkify( | ||||
|       'docs/usage/ci-configuration.md#authentication' | ||||
|    "docs/usage/ci-configuration.md#authentication" | ||||
|  )})`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EINVALIDTAGFORMAT({options: {tagFormat}}) { | ||||
| export function EINVALIDTAGFORMAT({ options: { tagFormat } }) { | ||||
|   return { | ||||
|     message: 'Invalid `tagFormat` option.', | ||||
|     message: "Invalid `tagFormat` option.", | ||||
|     details: `The [tagFormat](${linkify( | ||||
|       'docs/usage/configuration.md#tagformat' | ||||
|       "docs/usage/configuration.md#tagformat" | ||||
|     )}) must compile to a [valid Git reference](https://git-scm.com/docs/git-check-ref-format#_description).
 | ||||
| 
 | ||||
| Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function ETAGNOVERSION({options: {tagFormat}}) { | ||||
| export function ETAGNOVERSION({ options: { tagFormat } }) { | ||||
|   return { | ||||
|     message: 'Invalid `tagFormat` option.', | ||||
|     message: "Invalid `tagFormat` option.", | ||||
|     details: `The [tagFormat](${linkify( | ||||
|       'docs/usage/configuration.md#tagformat' | ||||
|       "docs/usage/configuration.md#tagformat" | ||||
|     )}) option must contain the variable \`version\` exactly once.
 | ||||
| 
 | ||||
| Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPLUGINCONF({type, required, pluginConf}) { | ||||
| export function EPLUGINCONF({ type, required, pluginConf }) { | ||||
|   return { | ||||
|     message: `The \`${type}\` plugin configuration is invalid.`, | ||||
|     details: `The [${type} plugin configuration](${linkify(`docs/usage/plugins.md#${toLower(type)}-plugin`)}) ${ | ||||
|       required ? 'is required and ' : '' | ||||
|       required ? "is required and " : "" | ||||
|     } must be a single or an array of plugins definition. A plugin definition is an npm module name, optionally wrapped in an array with an object. | ||||
| 
 | ||||
| Your configuration for the \`${type}\` plugin is \`${stringify(pluginConf)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPLUGINSCONF({plugin}) { | ||||
| export function EPLUGINSCONF({ plugin }) { | ||||
|   return { | ||||
|     message: 'The `plugins` configuration is invalid.', | ||||
|     message: "The `plugins` configuration is invalid.", | ||||
|     details: `The [plugins](${linkify( | ||||
|       'docs/usage/configuration.md#plugins' | ||||
|       "docs/usage/configuration.md#plugins" | ||||
|     )}) option must be an array of plugin definitions. A plugin definition is an npm module name, optionally wrapped in an array with an object. | ||||
| 
 | ||||
| The invalid configuration is \`${stringify(plugin)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPLUGIN({pluginName, type}) { | ||||
| export function EPLUGIN({ pluginName, type }) { | ||||
|   return { | ||||
|     message: `A plugin configured in the step ${type} is not a valid semantic-release plugin.`, | ||||
|     details: `A valid \`${type}\` **semantic-release** plugin must be a function or an object with a function in the property \`${type}\`.
 | ||||
| @ -103,17 +103,17 @@ export function EPLUGIN({pluginName, type}) { | ||||
| The plugin \`${pluginName}\` doesn't have the property \`${type}\` and cannot be used for the \`${type}\` step.
 | ||||
| 
 | ||||
| Please refer to the \`${pluginName}\` and [semantic-release plugins configuration](${linkify( | ||||
|       'docs/usage/plugins.md' | ||||
|       "docs/usage/plugins.md" | ||||
|     )}) documentation for more details.`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EANALYZECOMMITSOUTPUT({result, pluginName}) { | ||||
| export function EANALYZECOMMITSOUTPUT({ result, pluginName }) { | ||||
|   return { | ||||
|     message: 'The `analyzeCommits` plugin returned an invalid value. It must return a valid semver release type.', | ||||
|     message: "The `analyzeCommits` plugin returned an invalid value. It must return a valid semver release type.", | ||||
|     details: `The \`analyzeCommits\` plugin must return a valid [semver](https://semver.org) release type. The valid values are: ${RELEASE_TYPE.map( | ||||
|       (type) => `\`${type}\`` | ||||
|     ).join(', ')}. | ||||
|     ).join(", ")}. | ||||
| 
 | ||||
| The \`analyzeCommits\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
 | ||||
| 
 | ||||
| @ -121,15 +121,15 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the | ||||
| - The **semantic-release** version: \`${pkg.version}\` | ||||
| - The **semantic-release** logs from your CI job | ||||
| - The value returned by the plugin: \`${stringify(result)}\` | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify('docs/developer-guide/plugin.md')}](${linkify( | ||||
|       'docs/developer-guide/plugin.md' | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify("docs/developer-guide/plugin.md")}](${linkify( | ||||
|       "docs/developer-guide/plugin.md" | ||||
|     )})`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EGENERATENOTESOUTPUT({result, pluginName}) { | ||||
| export function EGENERATENOTESOUTPUT({ result, pluginName }) { | ||||
|   return { | ||||
|     message: 'The `generateNotes` plugin returned an invalid value. It must return a `String`.', | ||||
|     message: "The `generateNotes` plugin returned an invalid value. It must return a `String`.", | ||||
|     details: `The \`generateNotes\` plugin must return a \`String\`.
 | ||||
| 
 | ||||
| The \`generateNotes\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
 | ||||
| @ -138,15 +138,15 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the | ||||
| - The **semantic-release** version: \`${pkg.version}\` | ||||
| - The **semantic-release** logs from your CI job | ||||
| - The value returned by the plugin: \`${stringify(result)}\` | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify('docs/developer-guide/plugin.md')}](${linkify( | ||||
|       'docs/developer-guide/plugin.md' | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify("docs/developer-guide/plugin.md")}](${linkify( | ||||
|       "docs/developer-guide/plugin.md" | ||||
|     )})`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPUBLISHOUTPUT({result, pluginName}) { | ||||
| export function EPUBLISHOUTPUT({ result, pluginName }) { | ||||
|   return { | ||||
|     message: 'A `publish` plugin returned an invalid value. It must return an `Object`.', | ||||
|     message: "A `publish` plugin returned an invalid value. It must return an `Object`.", | ||||
|     details: `The \`publish\` plugins must return an \`Object\`.
 | ||||
| 
 | ||||
| The \`publish\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
 | ||||
| @ -155,15 +155,15 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the | ||||
| - The **semantic-release** version: \`${pkg.version}\` | ||||
| - The **semantic-release** logs from your CI job | ||||
| - The value returned by the plugin: \`${stringify(result)}\` | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify('docs/developer-guide/plugin.md')}](${linkify( | ||||
|       'docs/developer-guide/plugin.md' | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify("docs/developer-guide/plugin.md")}](${linkify( | ||||
|       "docs/developer-guide/plugin.md" | ||||
|     )})`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EADDCHANNELOUTPUT({result, pluginName}) { | ||||
| export function EADDCHANNELOUTPUT({ result, pluginName }) { | ||||
|   return { | ||||
|     message: 'A `addChannel` plugin returned an invalid value. It must return an `Object`.', | ||||
|     message: "A `addChannel` plugin returned an invalid value. It must return an `Object`.", | ||||
|     details: `The \`addChannel\` plugins must return an \`Object\`.
 | ||||
| 
 | ||||
| The \`addChannel\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
 | ||||
| @ -172,72 +172,72 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the | ||||
| - The **semantic-release** version: \`${pkg.version}\` | ||||
| - The **semantic-release** logs from your CI job | ||||
| - The value returned by the plugin: \`${stringify(result)}\` | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify('docs/developer-guide/plugin.md')}](${linkify( | ||||
|       'docs/developer-guide/plugin.md' | ||||
| - A link to the **semantic-release** plugin developer guide: [${linkify("docs/developer-guide/plugin.md")}](${linkify( | ||||
|       "docs/developer-guide/plugin.md" | ||||
|     )})`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EINVALIDBRANCH({branch}) { | ||||
| export function EINVALIDBRANCH({ branch }) { | ||||
|   return { | ||||
|     message: 'A branch is invalid in the `branches` configuration.', | ||||
|     message: "A branch is invalid in the `branches` configuration.", | ||||
|     details: `Each branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must be either a string, a regexp or an object with a \`name\` property.
 | ||||
| 
 | ||||
| Your configuration for the problematic branch is \`${stringify(branch)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EINVALIDBRANCHNAME({branch}) { | ||||
| export function EINVALIDBRANCHNAME({ branch }) { | ||||
|   return { | ||||
|     message: 'A branch name is invalid in the `branches` configuration.', | ||||
|     message: "A branch name is invalid in the `branches` configuration.", | ||||
|     details: `Each branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must be a [valid Git reference](https://git-scm.com/docs/git-check-ref-format#_description).
 | ||||
| 
 | ||||
| Your configuration for the problematic branch is \`${stringify(branch)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EDUPLICATEBRANCHES({duplicates}) { | ||||
| export function EDUPLICATEBRANCHES({ duplicates }) { | ||||
|   return { | ||||
|     message: 'The `branches` configuration has duplicate branches.', | ||||
|     message: "The `branches` configuration has duplicate branches.", | ||||
|     details: `Each branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must havea unique name. | ||||
| 
 | ||||
| Your configuration contains duplicates for the following branch names: \`${stringify(duplicates)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EMAINTENANCEBRANCH({branch}) { | ||||
| export function EMAINTENANCEBRANCH({ branch }) { | ||||
|   return { | ||||
|     message: 'A maintenance branch is invalid in the `branches` configuration.', | ||||
|     message: "A maintenance branch is invalid in the `branches` configuration.", | ||||
|     details: `Each maintenance branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must have a \`range\` property formatted like \`N.x\`, \`N.x.x\` or \`N.N.x\` (\`N\` is a number).
 | ||||
| 
 | ||||
| Your configuration for the problematic branch is \`${stringify(branch)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EMAINTENANCEBRANCHES({branches}) { | ||||
| export function EMAINTENANCEBRANCHES({ branches }) { | ||||
|   return { | ||||
|     message: 'The maintenance branches are invalid in the `branches` configuration.', | ||||
|     message: "The maintenance branches are invalid in the `branches` configuration.", | ||||
|     details: `Each maintenance branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must have a unique \`range\` property.
 | ||||
| 
 | ||||
| Your configuration for the problematic branches is \`${stringify(branches)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function ERELEASEBRANCHES({branches}) { | ||||
| export function ERELEASEBRANCHES({ branches }) { | ||||
|   return { | ||||
|     message: 'The release branches are invalid in the `branches` configuration.', | ||||
|     message: "The release branches are invalid in the `branches` configuration.", | ||||
|     details: `A minimum of 1 and a maximum of 3 release branches are required in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}). | ||||
| 
 | ||||
| This may occur if your repository does not have a release branch, such as \`master\`.
 | ||||
| @ -246,53 +246,53 @@ Your configuration for the problematic branches is \`${stringify(branches)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPRERELEASEBRANCH({branch}) { | ||||
| export function EPRERELEASEBRANCH({ branch }) { | ||||
|   return { | ||||
|     message: 'A pre-release branch configuration is invalid in the `branches` configuration.', | ||||
|     message: "A pre-release branch configuration is invalid in the `branches` configuration.", | ||||
|     details: `Each pre-release branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must have a \`prerelease\` property valid per the [Semantic Versioning Specification](https://semver.org/#spec-item-9). If the \`prerelease\` property is set to \`true\`, then the \`name\` property is used instead.
 | ||||
| 
 | ||||
| Your configuration for the problematic branch is \`${stringify(branch)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EPRERELEASEBRANCHES({branches}) { | ||||
| export function EPRERELEASEBRANCHES({ branches }) { | ||||
|   return { | ||||
|     message: 'The pre-release branches are invalid in the `branches` configuration.', | ||||
|     message: "The pre-release branches are invalid in the `branches` configuration.", | ||||
|     details: `Each pre-release branch in the [branches configuration](${linkify( | ||||
|       'docs/usage/configuration.md#branches' | ||||
|       "docs/usage/configuration.md#branches" | ||||
|     )}) must have a unique \`prerelease\` property. If the \`prerelease\` property is set to \`true\`, then the \`name\` property is used instead.
 | ||||
| 
 | ||||
| Your configuration for the problematic branches is \`${stringify(branches)}\`.`, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EINVALIDNEXTVERSION({nextRelease: {version}, branch: {name, range}, commits, validBranches}) { | ||||
| export function EINVALIDNEXTVERSION({ nextRelease: { version }, branch: { name, range }, commits, validBranches }) { | ||||
|   return { | ||||
|     message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`, | ||||
|     details: `Based on the releases published on other branches, only versions within the range \`${range}\` can be published from branch \`${name}\`.
 | ||||
| 
 | ||||
| The following commit${commits.length > 1 ? 's are' : ' is'} responsible for the invalid release: | ||||
| ${commits.map(({commit: {short}, subject}) => `- ${subject} (${short})`).join('\n')} | ||||
| The following commit${commits.length > 1 ? "s are" : " is"} responsible for the invalid release: | ||||
| ${commits.map(({ commit: { short }, subject }) => `- ${subject} (${short})`).join("\n")} | ||||
| 
 | ||||
| ${ | ||||
|       commits.length > 1 ? 'Those commits' : 'This commit' | ||||
|     } should be moved to a valid branch with [git merge](https://git-scm.com/docs/git-merge) or [git cherry-pick](https://git-scm.com/docs/git-cherry-pick) and removed from branch \`${name}\` with [git revert](https://git-scm.com/docs/git-revert) or [git reset](https://git-scm.com/docs/git-reset).
 | ||||
|   commits.length > 1 ? "Those commits" : "This commit" | ||||
| } should be moved to a valid branch with [git merge](https://git-scm.com/docs/git-merge) or [git cherry-pick](https://git-scm.com/docs/git-cherry-pick) and removed from branch \`${name}\` with [git revert](https://git-scm.com/docs/git-revert) or [git reset](https://git-scm.com/docs/git-reset).
 | ||||
| 
 | ||||
| A valid branch could be ${wordsList(validBranches.map(({name}) => `\`${name}\``))}. | ||||
| A valid branch could be ${wordsList(validBranches.map(({ name }) => `\`${name}\``))}. | ||||
| 
 | ||||
| See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
 | ||||
| See the [workflow configuration documentation](${linkify("docs/usage/workflow-configuration.md")}) for more details.`,
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function EINVALIDMAINTENANCEMERGE({nextRelease: {channel, gitTag, version}, branch: {mergeRange, name}}) { | ||||
| export function EINVALIDMAINTENANCEMERGE({ nextRelease: { channel, gitTag, version }, branch: { mergeRange, name } }) { | ||||
|   return { | ||||
|     message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`, | ||||
|     details: `Only releases within the range \`${mergeRange}\` can be merged into the maintenance branch \`${name}\` and published to the \`${channel}\` distribution channel.
 | ||||
| 
 | ||||
| The branch \`${name}\` head should be [reset](https://git-scm.com/docs/git-reset) to a previous commit so the commit with tag \`${gitTag}\` is removed from the branch history.
 | ||||
| 
 | ||||
| See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
 | ||||
| See the [workflow configuration documentation](${linkify("docs/usage/workflow-configuration.md")}) for more details.`,
 | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -1,23 +1,23 @@ | ||||
| /* eslint require-atomic-updates: off */ | ||||
| 
 | ||||
| import {isPlainObject, isString} from 'lodash-es'; | ||||
| import {getGitHead} from '../git.js'; | ||||
| import hideSensitive from '../hide-sensitive.js'; | ||||
| import {hideSensitiveValues} from '../utils.js'; | ||||
| import {RELEASE_NOTES_SEPARATOR, RELEASE_TYPE} from './constants.js'; | ||||
| import { isPlainObject, isString } from "lodash-es"; | ||||
| import { getGitHead } from "../git.js"; | ||||
| import hideSensitive from "../hide-sensitive.js"; | ||||
| import { hideSensitiveValues } from "../utils.js"; | ||||
| import { RELEASE_NOTES_SEPARATOR, RELEASE_TYPE } from "./constants.js"; | ||||
| 
 | ||||
| export default { | ||||
|   verifyConditions: { | ||||
|     required: false, | ||||
|     dryRun: true, | ||||
|     pipelineConfig: () => ({settleAll: true}), | ||||
|     pipelineConfig: () => ({ settleAll: true }), | ||||
|   }, | ||||
|   analyzeCommits: { | ||||
|     default: ['@semantic-release/commit-analyzer'], | ||||
|     default: ["@semantic-release/commit-analyzer"], | ||||
|     required: true, | ||||
|     dryRun: true, | ||||
|     outputValidator: (output) => !output || RELEASE_TYPE.includes(output), | ||||
|     preprocess: ({commits, ...inputs}) => ({ | ||||
|     preprocess: ({ commits, ...inputs }) => ({ | ||||
|       ...inputs, | ||||
|       commits: commits.filter((commit) => !/\[skip\s+release]|\[release\s+skip]/i.test(commit.message)), | ||||
|     }), | ||||
| @ -32,29 +32,29 @@ export default { | ||||
|   verifyRelease: { | ||||
|     required: false, | ||||
|     dryRun: true, | ||||
|     pipelineConfig: () => ({settleAll: true}), | ||||
|     pipelineConfig: () => ({ settleAll: true }), | ||||
|   }, | ||||
|   generateNotes: { | ||||
|     required: false, | ||||
|     dryRun: true, | ||||
|     outputValidator: (output) => !output || isString(output), | ||||
|     pipelineConfig: () => ({ | ||||
|       getNextInput: ({nextRelease, ...context}, notes) => ({ | ||||
|       getNextInput: ({ nextRelease, ...context }, notes) => ({ | ||||
|         ...context, | ||||
|         nextRelease: { | ||||
|           ...nextRelease, | ||||
|           notes: `${nextRelease.notes ? `${nextRelease.notes}${RELEASE_NOTES_SEPARATOR}` : ''}${notes}`, | ||||
|           notes: `${nextRelease.notes ? `${nextRelease.notes}${RELEASE_NOTES_SEPARATOR}` : ""}${notes}`, | ||||
|         }, | ||||
|       }), | ||||
|     }), | ||||
|     postprocess: (results, {env}) => hideSensitive(env)(results.filter(Boolean).join(RELEASE_NOTES_SEPARATOR)), | ||||
|     postprocess: (results, { env }) => hideSensitive(env)(results.filter(Boolean).join(RELEASE_NOTES_SEPARATOR)), | ||||
|   }, | ||||
|   prepare: { | ||||
|     required: false, | ||||
|     dryRun: false, | ||||
|     pipelineConfig: ({generateNotes}) => ({ | ||||
|     pipelineConfig: ({ generateNotes }) => ({ | ||||
|       getNextInput: async (context) => { | ||||
|         const newGitHead = await getGitHead({cwd: context.cwd}); | ||||
|         const newGitHead = await getGitHead({ cwd: context.cwd }); | ||||
|         // If previous prepare plugin has created a commit (gitHead changed)
 | ||||
|         if (context.nextRelease.gitHead !== newGitHead) { | ||||
|           context.nextRelease.gitHead = newGitHead; | ||||
| @ -73,7 +73,7 @@ export default { | ||||
|     outputValidator: (output) => !output || isPlainObject(output), | ||||
|     pipelineConfig: () => ({ | ||||
|       // Add `nextRelease` and plugin properties to published release
 | ||||
|       transform: (release, step, {nextRelease}) => ({ | ||||
|       transform: (release, step, { nextRelease }) => ({ | ||||
|         ...(release === false ? {} : nextRelease), | ||||
|         ...release, | ||||
|         ...step, | ||||
| @ -86,7 +86,7 @@ export default { | ||||
|     outputValidator: (output) => !output || isPlainObject(output), | ||||
|     pipelineConfig: () => ({ | ||||
|       // Add `nextRelease` and plugin properties to published release
 | ||||
|       transform: (release, step, {nextRelease}) => ({ | ||||
|       transform: (release, step, { nextRelease }) => ({ | ||||
|         ...(release === false ? {} : nextRelease), | ||||
|         ...release, | ||||
|         ...step, | ||||
| @ -96,13 +96,13 @@ export default { | ||||
|   success: { | ||||
|     required: false, | ||||
|     dryRun: false, | ||||
|     pipelineConfig: () => ({settleAll: true}), | ||||
|     preprocess: ({releases, env, ...inputs}) => ({...inputs, env, releases: hideSensitiveValues(env, releases)}), | ||||
|     pipelineConfig: () => ({ settleAll: true }), | ||||
|     preprocess: ({ releases, env, ...inputs }) => ({ ...inputs, env, releases: hideSensitiveValues(env, releases) }), | ||||
|   }, | ||||
|   fail: { | ||||
|     required: false, | ||||
|     dryRun: false, | ||||
|     pipelineConfig: () => ({settleAll: true}), | ||||
|     preprocess: ({errors, env, ...inputs}) => ({...inputs, env, errors: hideSensitiveValues(env, errors)}), | ||||
|     pipelineConfig: () => ({ settleAll: true }), | ||||
|     preprocess: ({ errors, env, ...inputs }) => ({ ...inputs, env, errors: hideSensitiveValues(env, errors) }), | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| import {castArray, identity, isNil, isPlainObject, isString, omit} from 'lodash-es'; | ||||
| import AggregateError from 'aggregate-error'; | ||||
| import getError from '../get-error.js'; | ||||
| import PLUGINS_DEFINITIONS from '../definitions/plugins.js'; | ||||
| import {loadPlugin, parseConfig, validatePlugin, validateStep} from './utils.js'; | ||||
| import pipeline from './pipeline.js'; | ||||
| import normalize from './normalize.js'; | ||||
| import { castArray, identity, isNil, isPlainObject, isString, omit } from "lodash-es"; | ||||
| import AggregateError from "aggregate-error"; | ||||
| import getError from "../get-error.js"; | ||||
| import PLUGINS_DEFINITIONS from "../definitions/plugins.js"; | ||||
| import { loadPlugin, parseConfig, validatePlugin, validateStep } from "./utils.js"; | ||||
| import pipeline from "./pipeline.js"; | ||||
| import normalize from "./normalize.js"; | ||||
| 
 | ||||
| export default async (context, pluginsPath) => { | ||||
|   let {options, logger} = context; | ||||
|   let { options, logger } = context; | ||||
|   const errors = []; | ||||
| 
 | ||||
|   const plugins = options.plugins | ||||
| @ -20,8 +20,8 @@ export default async (context, pluginsPath) => { | ||||
|           if (isPlainObject(plugin)) { | ||||
|             Object.entries(plugin).forEach(([type, func]) => { | ||||
|               if (PLUGINS_DEFINITIONS[type]) { | ||||
|                 Reflect.defineProperty(func, 'pluginName', { | ||||
|                   value: isPlainObject(name) ? 'Inline plugin' : name, | ||||
|                 Reflect.defineProperty(func, "pluginName", { | ||||
|                   value: isPlainObject(name) ? "Inline plugin" : name, | ||||
|                   writable: false, | ||||
|                   enumerable: true, | ||||
|                 }); | ||||
| @ -29,10 +29,10 @@ export default async (context, pluginsPath) => { | ||||
|               } | ||||
|             }); | ||||
|           } else { | ||||
|             errors.push(getError('EPLUGINSCONF', {plugin})); | ||||
|             errors.push(getError("EPLUGINSCONF", { plugin })); | ||||
|           } | ||||
|         } else { | ||||
|           errors.push(getError('EPLUGINSCONF', {plugin})); | ||||
|           errors.push(getError("EPLUGINSCONF", { plugin })); | ||||
|         } | ||||
| 
 | ||||
|         return pluginsList; | ||||
| @ -43,12 +43,12 @@ export default async (context, pluginsPath) => { | ||||
|     throw new AggregateError(errors); | ||||
|   } | ||||
| 
 | ||||
|   options = {...plugins, ...options}; | ||||
|   options = { ...plugins, ...options }; | ||||
| 
 | ||||
|   const pluginsConfig = await Object.entries(PLUGINS_DEFINITIONS).reduce( | ||||
|     async ( | ||||
|       eventualPluginsConfigAccumulator, | ||||
|       [type, {required, default: def, pipelineConfig, postprocess = identity, preprocess = identity}] | ||||
|       [type, { required, default: def, pipelineConfig, postprocess = identity, preprocess = identity }] | ||||
|     ) => { | ||||
|       let pluginOptions; | ||||
|       const pluginsConfigAccumulator = await eventualPluginsConfigAccumulator; | ||||
| @ -63,8 +63,8 @@ export default async (context, pluginsPath) => { | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         if (!validateStep({required}, options[type])) { | ||||
|           errors.push(getError('EPLUGINCONF', {type, required, pluginConf: options[type]})); | ||||
|         if (!validateStep({ required }, options[type])) { | ||||
|           errors.push(getError("EPLUGINCONF", { type, required, pluginConf: options[type] })); | ||||
|           return pluginsConfigAccumulator; | ||||
|         } | ||||
| 
 | ||||
| @ -74,7 +74,7 @@ export default async (context, pluginsPath) => { | ||||
|       const steps = await Promise.all( | ||||
|         castArray(pluginOptions).map(async (pluginOpt) => | ||||
|           normalize( | ||||
|             {...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS), 'plugins')}, | ||||
|             { ...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS), "plugins") }, | ||||
|             type, | ||||
|             pluginOpt, | ||||
|             pluginsPath | ||||
| @ -100,4 +100,4 @@ export default async (context, pluginsPath) => { | ||||
|   } | ||||
| 
 | ||||
|   return pluginsConfig; | ||||
| } | ||||
| }; | ||||
|  | ||||
| @ -1,14 +1,14 @@ | ||||
| import {cloneDeep, isFunction, isPlainObject, noop, omit} from 'lodash-es'; | ||||
| import debugPlugins from 'debug'; | ||||
| import getError from '../get-error.js'; | ||||
| import {extractErrors} from '../utils.js'; | ||||
| import PLUGINS_DEFINITIONS from '../definitions/plugins.js'; | ||||
| import {loadPlugin, parseConfig} from './utils.js'; | ||||
| import { cloneDeep, isFunction, isPlainObject, noop, omit } from "lodash-es"; | ||||
| import debugPlugins from "debug"; | ||||
| import getError from "../get-error.js"; | ||||
| import { extractErrors } from "../utils.js"; | ||||
| import PLUGINS_DEFINITIONS from "../definitions/plugins.js"; | ||||
| import { loadPlugin, parseConfig } from "./utils.js"; | ||||
| 
 | ||||
| const debug = debugPlugins('semantic-release:plugins'); | ||||
| const debug = debugPlugins("semantic-release:plugins"); | ||||
| 
 | ||||
| export default async (context, type, pluginOpt, pluginsPath) => { | ||||
|   const {stdout, stderr, options, logger} = context; | ||||
|   const { stdout, stderr, options, logger } = context; | ||||
|   if (!pluginOpt) { | ||||
|     return noop; | ||||
|   } | ||||
| @ -21,26 +21,26 @@ export default async (context, type, pluginOpt, pluginsPath) => { | ||||
| 
 | ||||
|   let func; | ||||
|   if (isFunction(plugin)) { | ||||
|     func = plugin.bind(null, cloneDeep({...options, ...config})); | ||||
|     func = plugin.bind(null, cloneDeep({ ...options, ...config })); | ||||
|   } else if (isPlainObject(plugin) && plugin[type] && isFunction(plugin[type])) { | ||||
|     func = plugin[type].bind(null, cloneDeep({...options, ...config})); | ||||
|     func = plugin[type].bind(null, cloneDeep({ ...options, ...config })); | ||||
|   } else { | ||||
|     throw getError('EPLUGIN', {type, pluginName}); | ||||
|     throw getError("EPLUGIN", { type, pluginName }); | ||||
|   } | ||||
| 
 | ||||
|   const validator = async (input) => { | ||||
|     const {dryRun, outputValidator} = PLUGINS_DEFINITIONS[type] || {}; | ||||
|     const { dryRun, outputValidator } = PLUGINS_DEFINITIONS[type] || {}; | ||||
|     try { | ||||
|       if (!input.options.dryRun || dryRun) { | ||||
|         logger.log(`Start step "${type}" of plugin "${pluginName}"`); | ||||
|         const result = await func({ | ||||
|           ...cloneDeep(omit(input, ['stdout', 'stderr', 'logger'])), | ||||
|           ...cloneDeep(omit(input, ["stdout", "stderr", "logger"])), | ||||
|           stdout, | ||||
|           stderr, | ||||
|           logger: logger.scope(logger.scopeName, pluginName), | ||||
|         }); | ||||
|         if (outputValidator && !outputValidator(result)) { | ||||
|           throw getError(`E${type.toUpperCase()}OUTPUT`, {result, pluginName}); | ||||
|           throw getError(`E${type.toUpperCase()}OUTPUT`, { result, pluginName }); | ||||
|         } | ||||
| 
 | ||||
|         logger.success(`Completed step "${type}" of plugin "${pluginName}"`); | ||||
| @ -50,12 +50,12 @@ export default async (context, type, pluginOpt, pluginsPath) => { | ||||
|       logger.warn(`Skip step "${type}" of plugin "${pluginName}" in dry-run mode`); | ||||
|     } catch (error) { | ||||
|       logger.error(`Failed step "${type}" of plugin "${pluginName}"`); | ||||
|       extractErrors(error).forEach((err) => Object.assign(err, {pluginName})); | ||||
|       extractErrors(error).forEach((err) => Object.assign(err, { pluginName })); | ||||
|       throw error; | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   Reflect.defineProperty(validator, 'pluginName', {value: pluginName, writable: false, enumerable: true}); | ||||
|   Reflect.defineProperty(validator, "pluginName", { value: pluginName, writable: false, enumerable: true }); | ||||
| 
 | ||||
|   if (!isFunction(pluginOpt)) { | ||||
|     if (pluginsPath[name]) { | ||||
| @ -66,4 +66,4 @@ export default async (context, type, pluginOpt, pluginsPath) => { | ||||
|   } | ||||
| 
 | ||||
|   return validator; | ||||
| } | ||||
| }; | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import {identity} from 'lodash-es'; | ||||
| import pReduce from 'p-reduce'; | ||||
| import AggregateError from 'aggregate-error'; | ||||
| import {extractErrors} from '../utils.js'; | ||||
| import { identity } from "lodash-es"; | ||||
| import pReduce from "p-reduce"; | ||||
| import AggregateError from "aggregate-error"; | ||||
| import { extractErrors } from "../utils.js"; | ||||
| 
 | ||||
| /** | ||||
|  * A Function that execute a list of function sequencially. If at least one Function ins the pipeline throws an Error or rejects, the pipeline function rejects as well. | ||||
| @ -25,7 +25,8 @@ import {extractErrors} from '../utils.js'; | ||||
|  * | ||||
|  * @return {Pipeline} A Function that execute the `steps` sequentially | ||||
|  */ | ||||
| export default (steps, {settleAll = false, getNextInput = identity, transform = identity} = {}) => async (input) => { | ||||
| export default (steps, { settleAll = false, getNextInput = identity, transform = identity } = {}) => | ||||
|   async (input) => { | ||||
|     const results = []; | ||||
|     const errors = []; | ||||
|     await pReduce( | ||||
| @ -55,4 +56,4 @@ export default (steps, {settleAll = false, getNextInput = identity, transform = | ||||
|     } | ||||
| 
 | ||||
|     return results; | ||||
| } | ||||
|   }; | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import {dirname} from 'node:path'; | ||||
| import {fileURLToPath} from 'node:url'; | ||||
| import {castArray, isArray, isFunction, isNil, isPlainObject, isString} from 'lodash-es'; | ||||
| import resolveFrom from 'resolve-from'; | ||||
| import { dirname } from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
| import { castArray, isArray, isFunction, isNil, isPlainObject, isString } from "lodash-es"; | ||||
| import resolveFrom from "resolve-from"; | ||||
| 
 | ||||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||||
| 
 | ||||
| @ -38,7 +38,7 @@ export function validatePlugin(conf) { | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export function validateStep({required}, conf) { | ||||
| export function validateStep({ required }, conf) { | ||||
|   conf = castArray(conf).filter(Boolean); | ||||
|   if (required) { | ||||
|     return conf.length >= 1 && validateSteps(conf); | ||||
| @ -47,7 +47,7 @@ export function validateStep({required}, conf) { | ||||
|   return conf.length === 0 || validateSteps(conf); | ||||
| } | ||||
| 
 | ||||
| export async function loadPlugin({cwd}, name, pluginsPath) { | ||||
| export async function loadPlugin({ cwd }, name, pluginsPath) { | ||||
|   const basePath = pluginsPath[name] | ||||
|     ? dirname(resolveFrom.silent(__dirname, pluginsPath[name]) || resolveFrom(cwd, pluginsPath[name])) | ||||
|     : __dirname; | ||||
| @ -72,7 +72,7 @@ export function parseConfig(plugin) { | ||||
|   if (isArray(plugin)) { | ||||
|     [path, config] = plugin; | ||||
|   } else if (isPlainObject(plugin) && !isNil(plugin.path)) { | ||||
|     ({path, ...config} = plugin); | ||||
|     ({ path, ...config } = plugin); | ||||
|   } else { | ||||
|     path = plugin; | ||||
|   } | ||||
|  | ||||
							
								
								
									
										8706
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8706
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -29,7 +29,7 @@ | ||||
|     "@semantic-release/commit-analyzer": "^9.0.2", | ||||
|     "@semantic-release/error": "^3.0.0", | ||||
|     "@semantic-release/github": "^8.0.0", | ||||
|     "@semantic-release/npm": "^9.0.0", | ||||
|     "@semantic-release/npm": "^10.0.2", | ||||
|     "@semantic-release/release-notes-generator": "^10.0.0", | ||||
|     "aggregate-error": "^4.0.1", | ||||
|     "cosmiconfig": "^8.0.0", | ||||
| @ -117,7 +117,8 @@ | ||||
|     "trailingComma": "es5" | ||||
|   }, | ||||
|   "publishConfig": { | ||||
|     "access": "public" | ||||
|     "access": "public", | ||||
|     "provenance": true | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
| @ -125,8 +126,8 @@ | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "codecov": "codecov -f coverage/coverage-final.json", | ||||
|     "lint": "prettier --check \"*.{js,json,md}\" \".github/**/*.{md,yml}\" \"docs/**/*.md\" \"{bin,lib,test}/*.js\"", | ||||
|     "lint:fix": "prettier --write \"*.{js,json,md}\" \".github/**/*.{md,yml}\" \"docs/**/*.md\" \"{bin,lib,test}/*.js\"", | ||||
|     "lint": "prettier --check \"*.{js,json,md}\" \".github/**/*.{md,yml}\" \"docs/**/*.md\" \"{bin,lib,test}/**/*.js\"", | ||||
|     "lint:fix": "prettier --write \"*.{js,json,md}\" \".github/**/*.{md,yml}\" \"docs/**/*.md\" \"{bin,lib,test}/**/*.js\"", | ||||
|     "pretest": "npm run lint", | ||||
|     "semantic-release": "./bin/semantic-release.js", | ||||
|     "test": "c8 ava --verbose", | ||||
|  | ||||
| @ -1,277 +1,277 @@ | ||||
| import test from 'ava'; | ||||
| import {union} from 'lodash-es'; | ||||
| import semver from 'semver'; | ||||
| import * as td from 'testdouble'; | ||||
| import test from "ava"; | ||||
| import { union } from "lodash-es"; | ||||
| import semver from "semver"; | ||||
| import * as td from "testdouble"; | ||||
| 
 | ||||
| const getBranch = (branches, branch) => branches.find(({name}) => name === branch); | ||||
| const release = (branches, name, version) => getBranch(branches, name).tags.push({version}); | ||||
| const getBranch = (branches, branch) => branches.find(({ name }) => name === branch); | ||||
| const release = (branches, name, version) => getBranch(branches, name).tags.push({ version }); | ||||
| const merge = (branches, source, target, tag) => { | ||||
|   getBranch(branches, target).tags = union( | ||||
|     getBranch(branches, source).tags.filter(({version}) => !tag || semver.cmp(version, '<=', tag)), | ||||
|     getBranch(branches, source).tags.filter(({ version }) => !tag || semver.cmp(version, "<=", tag)), | ||||
|     getBranch(branches, target).tags | ||||
|   ); | ||||
| }; | ||||
| const remoteBranches = []; | ||||
| const repositoryUrl = 'repositoryUrl'; | ||||
| const repositoryUrl = "repositoryUrl"; | ||||
| let expand, getTags, getBranches; | ||||
| 
 | ||||
| test.beforeEach(async (t) => { | ||||
|   getTags = (await td.replaceEsm('../../lib/branches/get-tags.js')).default; | ||||
|   expand = (await td.replaceEsm('../../lib/branches/expand.js')).default; | ||||
|   getBranches = (await import('../../lib/branches/index.js')).default; | ||||
| }) | ||||
|   getTags = (await td.replaceEsm("../../lib/branches/get-tags.js")).default; | ||||
|   expand = (await td.replaceEsm("../../lib/branches/expand.js")).default; | ||||
|   getBranches = (await import("../../lib/branches/index.js")).default; | ||||
| }); | ||||
| 
 | ||||
| test.afterEach.always((t) => { | ||||
|   td.reset(); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Enforce ranges with branching release workflow', async (t) => { | ||||
| test.serial("Enforce ranges with branching release workflow", async (t) => { | ||||
|   const branches = [ | ||||
|     {name: '1.x', tags: []}, | ||||
|     {name: '1.0.x', tags: []}, | ||||
|     {name: 'master', tags: []}, | ||||
|     {name: 'next', tags: []}, | ||||
|     {name: 'next-major', tags: []}, | ||||
|     {name: 'beta', prerelease: true, tags: []}, | ||||
|     {name: 'alpha', prerelease: true, tags: []}, | ||||
|     { name: "1.x", tags: [] }, | ||||
|     { name: "1.0.x", tags: [] }, | ||||
|     { name: "master", tags: [] }, | ||||
|     { name: "next", tags: [] }, | ||||
|     { name: "next-major", tags: [] }, | ||||
|     { name: "beta", prerelease: true, tags: [] }, | ||||
|     { name: "alpha", prerelease: true, tags: [] }, | ||||
|   ]; | ||||
|   const context = {options: {branches}}; | ||||
|   const context = { options: { branches } }; | ||||
|   td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches); | ||||
|   td.when(getTags(context, remoteBranches)).thenResolve(branches); | ||||
| 
 | ||||
|   let result = (await getBranches(repositoryUrl, 'master', context)).map(({name, range}) => ({name, range,})); | ||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master'); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.0'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.0.0'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=1.0.0'); | ||||
|   let result = (await getBranches(repositoryUrl, "master", context)).map(({ name, range }) => ({ name, range })); | ||||
|   t.is(getBranch(result, "1.0.x").range, ">=1.0.0 <1.0.0", "Cannot release on 1.0.x before a releasing on master"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.0", "Cannot release on 1.x before a releasing on master"); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.0"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.0.0"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=1.0.0"); | ||||
| 
 | ||||
|   release(branches, 'master', '1.0.0'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', context)).map(({name, range}) => ({name, range})); | ||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master'); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.0'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.0.0'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=1.0.0'); | ||||
|   release(branches, "master", "1.0.0"); | ||||
|   result = (await getBranches("repositoryUrl", "master", context)).map(({ name, range }) => ({ name, range })); | ||||
|   t.is(getBranch(result, "1.0.x").range, ">=1.0.0 <1.0.0", "Cannot release on 1.0.x before a releasing on master"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.0", "Cannot release on 1.x before a releasing on master"); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.0"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.0.0"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=1.0.0"); | ||||
| 
 | ||||
|   release(branches, 'master', '1.0.1'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   release(branches, "master", "1.0.1"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.1', 'Can release only > than 1.0.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.0.1', 'Can release only > than 1.0.1 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=1.0.1', 'Can release only > than 1.0.1 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.1", "Can release only > than 1.0.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.0.1", "Can release only > than 1.0.1 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=1.0.1", "Can release only > than 1.0.1 on next-major"); | ||||
| 
 | ||||
|   merge(branches, 'master', 'next'); | ||||
|   merge(branches, 'master', 'next-major'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "master", "next"); | ||||
|   merge(branches, "master", "next-major"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.1', 'Can release only > than 1.0.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.0.1', 'Can release only > than 1.0.1 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=1.0.1', 'Can release only > than 1.0.1 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.1", "Can release only > than 1.0.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.0.1", "Can release only > than 1.0.1 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=1.0.1", "Can release only > than 1.0.1 on next-major"); | ||||
| 
 | ||||
|   release(branches, 'next', '1.1.0'); | ||||
|   release(branches, 'next', '1.1.1'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   release(branches, "next", "1.1.0"); | ||||
|   release(branches, "next", "1.1.1"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.1.1', 'Can release only > than 1.1.1 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=1.1.1', 'Can release > than 1.1.1 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.1.1", "Can release only > than 1.1.1 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=1.1.1", "Can release > than 1.1.1 on next-major"); | ||||
| 
 | ||||
|   release(branches, 'next-major', '2.0.0'); | ||||
|   release(branches, 'next-major', '2.0.1'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   release(branches, "next-major", "2.0.0"); | ||||
|   release(branches, "next-major", "2.0.1"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.1.1 <2.0.0', 'Can release only patch or minor, > than 1.1.0 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.1.1 <2.0.0", "Can release only patch or minor, > than 1.1.0 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major"); | ||||
| 
 | ||||
|   merge(branches, 'next-major', 'beta'); | ||||
|   release(branches, 'beta', '3.0.0-beta.1'); | ||||
|   merge(branches, 'beta', 'alpha'); | ||||
|   release(branches, 'alpha', '4.0.0-alpha.1'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "next-major", "beta"); | ||||
|   release(branches, "beta", "3.0.0-beta.1"); | ||||
|   merge(branches, "beta", "alpha"); | ||||
|   release(branches, "alpha", "4.0.0-alpha.1"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major'); | ||||
|   t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major"); | ||||
| 
 | ||||
|   merge(branches, 'master', '1.0.x'); | ||||
|   merge(branches, 'master', '1.x'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "master", "1.0.x"); | ||||
|   merge(branches, "master", "1.x"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master"); | ||||
|   t.is( | ||||
|     getBranch(result, '1.0.x').range, | ||||
|     '>=1.0.1 <1.0.1', | ||||
|     'Cannot release on 1.0.x before >= 1.1.0 is released on master' | ||||
|     getBranch(result, "1.0.x").range, | ||||
|     ">=1.0.1 <1.0.1", | ||||
|     "Cannot release on 1.0.x before >= 1.1.0 is released on master" | ||||
|   ); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.1', 'Cannot release on 1.x before >= 1.2.0 is released on master'); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.1", "Cannot release on 1.x before >= 1.2.0 is released on master"); | ||||
| 
 | ||||
|   release(branches, 'master', '1.0.2'); | ||||
|   release(branches, 'master', '1.0.3'); | ||||
|   release(branches, 'master', '1.0.4'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   release(branches, "master", "1.0.2"); | ||||
|   release(branches, "master", "1.0.3"); | ||||
|   release(branches, "master", "1.0.4"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.0.4 <1.1.0', 'Can release only patch, > than 1.0.4 on master'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.0.4 <1.1.0", "Can release only patch, > than 1.0.4 on master"); | ||||
|   t.is( | ||||
|     getBranch(result, '1.0.x').range, | ||||
|     '>=1.0.1 <1.0.2', | ||||
|     'Cannot release on 1.0.x before >= 1.1.0 is released on master' | ||||
|     getBranch(result, "1.0.x").range, | ||||
|     ">=1.0.1 <1.0.2", | ||||
|     "Cannot release on 1.0.x before >= 1.1.0 is released on master" | ||||
|   ); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.2', 'Cannot release on 1.x before >= 1.2.0 is released on master'); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.2", "Cannot release on 1.x before >= 1.2.0 is released on master"); | ||||
| 
 | ||||
|   merge(branches, 'next', 'master'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "next", "master"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
| 
 | ||||
|   t.is(getBranch(result, 'master').range, '>=1.1.1', 'Can release only > than 1.1.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=1.1.1 <2.0.0', 'Can release only patch or minor, > than 1.1.1 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=1.1.1 <2.0.0", "Can release only patch or minor, > than 1.1.1 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major"); | ||||
|   t.is( | ||||
|     getBranch(result, '1.0.x').range, | ||||
|     '>=1.0.1 <1.0.2', | ||||
|     'Cannot release on 1.0.x before 1.0.x version from master are merged' | ||||
|     getBranch(result, "1.0.x").range, | ||||
|     ">=1.0.1 <1.0.2", | ||||
|     "Cannot release on 1.0.x before 1.0.x version from master are merged" | ||||
|   ); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.2', 'Cannot release on 1.x before >= 2.0.0 is released on master'); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.2", "Cannot release on 1.x before >= 2.0.0 is released on master"); | ||||
| 
 | ||||
|   merge(branches, 'master', '1.0.x', '1.0.4'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "master", "1.0.x", "1.0.4"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.1.1', 'Can release only > than 1.1.1 on master'); | ||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.1.0', 'Cannot release on 1.x before >= 2.0.0 is released on master'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master"); | ||||
|   t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.1.0", "Cannot release on 1.x before >= 2.0.0 is released on master"); | ||||
| 
 | ||||
|   merge(branches, 'master', '1.x'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "master", "1.x"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=1.1.1', 'Can release only > than 1.1.1 on master'); | ||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.1 <1.1.1', 'Cannot release on 1.x before >= 2.0.0 is released on master'); | ||||
|   t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master"); | ||||
|   t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.1 <1.1.1", "Cannot release on 1.x before >= 2.0.0 is released on master"); | ||||
| 
 | ||||
|   merge(branches, 'next-major', 'next'); | ||||
|   merge(branches, 'next', 'master'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "next-major", "next"); | ||||
|   merge(branches, "next", "master"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=2.0.1', 'Can release only > than 2.0.1 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=2.0.1', 'Can release only > than 2.0.1 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release only > than 2.0.1 on next-major'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.1.1 <2.0.0', 'Can release on 1.x only within range'); | ||||
|   t.is(getBranch(result, "master").range, ">=2.0.1", "Can release only > than 2.0.1 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=2.0.1", "Can release only > than 2.0.1 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release only > than 2.0.1 on next-major"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.1.1 <2.0.0", "Can release on 1.x only within range"); | ||||
| 
 | ||||
|   merge(branches, 'beta', 'master'); | ||||
|   release(branches, 'master', '3.0.0'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   merge(branches, "beta", "master"); | ||||
|   release(branches, "master", "3.0.0"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, 'master').range, '>=3.0.0', 'Can release only > than 3.0.0 on master'); | ||||
|   t.is(getBranch(result, 'next').range, '>=3.0.0', 'Can release only > than 3.0.0 on next'); | ||||
|   t.is(getBranch(result, 'next-major').range, '>=3.0.0', 'Can release only > than 3.0.0 on next-major'); | ||||
|   t.is(getBranch(result, "master").range, ">=3.0.0", "Can release only > than 3.0.0 on master"); | ||||
|   t.is(getBranch(result, "next").range, ">=3.0.0", "Can release only > than 3.0.0 on next"); | ||||
|   t.is(getBranch(result, "next-major").range, ">=3.0.0", "Can release only > than 3.0.0 on next-major"); | ||||
| 
 | ||||
|   branches.push({name: '1.1.x', tags: []}); | ||||
|   merge(branches, '1.x', '1.1.x'); | ||||
|   result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({ | ||||
|   branches.push({ name: "1.1.x", tags: [] }); | ||||
|   merge(branches, "1.x", "1.1.x"); | ||||
|   result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({ | ||||
|     name, | ||||
|     range, | ||||
|   })); | ||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range'); | ||||
|   t.is(getBranch(result, '1.1.x').range, '>=1.1.1 <1.2.0', 'Can release on 1.1.x only within range'); | ||||
|   t.is(getBranch(result, '1.x').range, '>=1.2.0 <2.0.0', 'Can release on 1.x only within range'); | ||||
|   t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range"); | ||||
|   t.is(getBranch(result, "1.1.x").range, ">=1.1.1 <1.2.0", "Can release on 1.1.x only within range"); | ||||
|   t.is(getBranch(result, "1.x").range, ">=1.2.0 <2.0.0", "Can release on 1.x only within range"); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Throw SemanticReleaseError for invalid configurations', async (t) => { | ||||
| test.serial("Throw SemanticReleaseError for invalid configurations", async (t) => { | ||||
|   const branches = [ | ||||
|     {name: '123', range: '123', tags: []}, | ||||
|     {name: '1.x', tags: []}, | ||||
|     {name: 'maintenance-1', range: '1.x', tags: []}, | ||||
|     {name: '1.x.x', tags: []}, | ||||
|     {name: 'beta', prerelease: '', tags: []}, | ||||
|     {name: 'alpha', prerelease: 'alpha', tags: []}, | ||||
|     {name: 'preview', prerelease: 'alpha', tags: []}, | ||||
|     { name: "123", range: "123", tags: [] }, | ||||
|     { name: "1.x", tags: [] }, | ||||
|     { name: "maintenance-1", range: "1.x", tags: [] }, | ||||
|     { name: "1.x.x", tags: [] }, | ||||
|     { name: "beta", prerelease: "", tags: [] }, | ||||
|     { name: "alpha", prerelease: "alpha", tags: [] }, | ||||
|     { name: "preview", prerelease: "alpha", tags: [] }, | ||||
|   ]; | ||||
|   const context = {options: {branches}}; | ||||
|   const context = { options: { branches } }; | ||||
|   td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches); | ||||
|   td.when(getTags(context, remoteBranches)).thenResolve(branches); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(getBranches(repositoryUrl, 'master', context)); | ||||
|   const error = await t.throwsAsync(getBranches(repositoryUrl, "master", context)); | ||||
|   const errors = [...error.errors]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EMAINTENANCEBRANCH'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EMAINTENANCEBRANCH"); | ||||
|   t.truthy(errors[0].message); | ||||
|   t.truthy(errors[0].details); | ||||
|   t.is(errors[1].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[1].code, 'EMAINTENANCEBRANCHES'); | ||||
|   t.is(errors[1].name, "SemanticReleaseError"); | ||||
|   t.is(errors[1].code, "EMAINTENANCEBRANCHES"); | ||||
|   t.truthy(errors[1].message); | ||||
|   t.truthy(errors[1].details); | ||||
|   t.is(errors[2].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[2].code, 'EPRERELEASEBRANCH'); | ||||
|   t.is(errors[2].name, "SemanticReleaseError"); | ||||
|   t.is(errors[2].code, "EPRERELEASEBRANCH"); | ||||
|   t.truthy(errors[2].message); | ||||
|   t.truthy(errors[2].details); | ||||
|   t.is(errors[3].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[3].code, 'EPRERELEASEBRANCHES'); | ||||
|   t.is(errors[3].name, "SemanticReleaseError"); | ||||
|   t.is(errors[3].code, "EPRERELEASEBRANCHES"); | ||||
|   t.truthy(errors[3].message); | ||||
|   t.truthy(errors[3].details); | ||||
|   t.is(errors[4].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[4].code, 'ERELEASEBRANCHES'); | ||||
|   t.is(errors[4].name, "SemanticReleaseError"); | ||||
|   t.is(errors[4].code, "ERELEASEBRANCHES"); | ||||
|   t.truthy(errors[4].message); | ||||
|   t.truthy(errors[4].details); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Throw a SemanticReleaseError if there is duplicate branches', async (t) => { | ||||
| test.serial("Throw a SemanticReleaseError if there is duplicate branches", async (t) => { | ||||
|   const branches = [ | ||||
|     {name: 'master', tags: []}, | ||||
|     {name: 'master', tags: []}, | ||||
|     { name: "master", tags: [] }, | ||||
|     { name: "master", tags: [] }, | ||||
|   ]; | ||||
|   const context = {options: {branches}}; | ||||
|   const context = { options: { branches } }; | ||||
|   td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches); | ||||
|   td.when(getTags(context, remoteBranches)).thenResolve(branches); | ||||
| 
 | ||||
|   const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, 'master', context))).errors]; | ||||
|   const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, "master", context))).errors]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EDUPLICATEBRANCHES'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EDUPLICATEBRANCHES"); | ||||
|   t.truthy(errors[0].message); | ||||
|   t.truthy(errors[0].details); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Throw a SemanticReleaseError for each invalid branch name', async (t) => { | ||||
| test.serial("Throw a SemanticReleaseError for each invalid branch name", async (t) => { | ||||
|   const branches = [ | ||||
|     {name: '~master', tags: []}, | ||||
|     {name: '^master', tags: []}, | ||||
|     { name: "~master", tags: [] }, | ||||
|     { name: "^master", tags: [] }, | ||||
|   ]; | ||||
|   const context = {options: {branches}}; | ||||
|   const context = { options: { branches } }; | ||||
|   const remoteBranches = []; | ||||
|   td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches); | ||||
|   td.when(getTags(context, remoteBranches)).thenResolve(branches); | ||||
| 
 | ||||
|   const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, 'master', context))).errors]; | ||||
|   const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, "master", context))).errors]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EINVALIDBRANCHNAME'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EINVALIDBRANCHNAME"); | ||||
|   t.truthy(errors[0].message); | ||||
|   t.truthy(errors[0].details); | ||||
|   t.is(errors[1].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[1].code, 'EINVALIDBRANCHNAME'); | ||||
|   t.is(errors[1].name, "SemanticReleaseError"); | ||||
|   t.is(errors[1].code, "EINVALIDBRANCHNAME"); | ||||
|   t.truthy(errors[1].message); | ||||
|   t.truthy(errors[1].details); | ||||
| }); | ||||
|  | ||||
| @ -1,54 +1,54 @@ | ||||
| import test from 'ava'; | ||||
| import expand from '../../lib/branches/expand.js'; | ||||
| import {gitCheckout, gitCommits, gitPush, gitRepo} from '../helpers/git-utils.js'; | ||||
| import test from "ava"; | ||||
| import expand from "../../lib/branches/expand.js"; | ||||
| import { gitCheckout, gitCommits, gitPush, gitRepo } from "../helpers/git-utils.js"; | ||||
| 
 | ||||
| test('Expand branches defined with globs', async (t) => { | ||||
|   const {cwd, repositoryUrl} = await gitRepo(true); | ||||
|   await gitCommits(['First'], {cwd}); | ||||
|   await gitPush(repositoryUrl, 'master', {cwd}); | ||||
|   await gitCheckout('1.0.x', true, {cwd}); | ||||
|   await gitCommits(['Second'], {cwd}); | ||||
|   await gitPush(repositoryUrl, '1.0.x', {cwd}); | ||||
|   await gitCheckout('1.x.x', true, {cwd}); | ||||
|   await gitCommits(['Third'], {cwd}); | ||||
|   await gitPush(repositoryUrl, '1.x.x', {cwd}); | ||||
|   await gitCheckout('2.x', true, {cwd}); | ||||
|   await gitCommits(['Fourth'], {cwd}); | ||||
|   await gitPush(repositoryUrl, '2.x', {cwd}); | ||||
|   await gitCheckout('next', true, {cwd}); | ||||
|   await gitCommits(['Fifth'], {cwd}); | ||||
|   await gitPush(repositoryUrl, 'next', {cwd}); | ||||
|   await gitCheckout('pre/foo', true, {cwd}); | ||||
|   await gitCommits(['Sixth'], {cwd}); | ||||
|   await gitPush(repositoryUrl, 'pre/foo', {cwd}); | ||||
|   await gitCheckout('pre/bar', true, {cwd}); | ||||
|   await gitCommits(['Seventh'], {cwd}); | ||||
|   await gitPush(repositoryUrl, 'pre/bar', {cwd}); | ||||
|   await gitCheckout('beta', true, {cwd}); | ||||
|   await gitCommits(['Eighth'], {cwd}); | ||||
|   await gitPush(repositoryUrl, 'beta', {cwd}); | ||||
| test("Expand branches defined with globs", async (t) => { | ||||
|   const { cwd, repositoryUrl } = await gitRepo(true); | ||||
|   await gitCommits(["First"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "master", { cwd }); | ||||
|   await gitCheckout("1.0.x", true, { cwd }); | ||||
|   await gitCommits(["Second"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "1.0.x", { cwd }); | ||||
|   await gitCheckout("1.x.x", true, { cwd }); | ||||
|   await gitCommits(["Third"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "1.x.x", { cwd }); | ||||
|   await gitCheckout("2.x", true, { cwd }); | ||||
|   await gitCommits(["Fourth"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "2.x", { cwd }); | ||||
|   await gitCheckout("next", true, { cwd }); | ||||
|   await gitCommits(["Fifth"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "next", { cwd }); | ||||
|   await gitCheckout("pre/foo", true, { cwd }); | ||||
|   await gitCommits(["Sixth"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "pre/foo", { cwd }); | ||||
|   await gitCheckout("pre/bar", true, { cwd }); | ||||
|   await gitCommits(["Seventh"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "pre/bar", { cwd }); | ||||
|   await gitCheckout("beta", true, { cwd }); | ||||
|   await gitCommits(["Eighth"], { cwd }); | ||||
|   await gitPush(repositoryUrl, "beta", { cwd }); | ||||
| 
 | ||||
|   const branches = [ | ||||
|     // Should match all maintenance type branches
 | ||||
|     {name: '+([0-9])?(.{+([0-9]),x}).x'}, | ||||
|     {name: 'master', channel: 'latest'}, | ||||
|     {name: 'next'}, | ||||
|     {name: 'pre/{foo,bar}', channel: `\${name.replace(/^pre\\//g, '')}`, prerelease: true}, | ||||
|     { name: "+([0-9])?(.{+([0-9]),x}).x" }, | ||||
|     { name: "master", channel: "latest" }, | ||||
|     { name: "next" }, | ||||
|     { name: "pre/{foo,bar}", channel: `\${name.replace(/^pre\\//g, '')}`, prerelease: true }, | ||||
|     // Should be ignored as there is no matching branches in the repo
 | ||||
|     {name: 'missing'}, | ||||
|     { name: "missing" }, | ||||
|     // Should be ignored as the matching branch in the repo is already matched by `/^pre\\/(\\w+)$/gi`
 | ||||
|     {name: '*/foo', channel: 'foo', prerelease: 'foo'}, | ||||
|     {name: 'beta', channel: `channel-\${name}`, prerelease: true}, | ||||
|     { name: "*/foo", channel: "foo", prerelease: "foo" }, | ||||
|     { name: "beta", channel: `channel-\${name}`, prerelease: true }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual(await expand(repositoryUrl, {cwd}, branches), [ | ||||
|     {name: '1.0.x'}, | ||||
|     {name: '1.x.x'}, | ||||
|     {name: '2.x'}, | ||||
|     {name: 'master', channel: 'latest'}, | ||||
|     {name: 'next'}, | ||||
|     {name: 'pre/bar', channel: 'bar', prerelease: true}, | ||||
|     {name: 'pre/foo', channel: 'foo', prerelease: true}, | ||||
|     {name: 'beta', channel: 'channel-beta', prerelease: true}, | ||||
|   t.deepEqual(await expand(repositoryUrl, { cwd }, branches), [ | ||||
|     { name: "1.0.x" }, | ||||
|     { name: "1.x.x" }, | ||||
|     { name: "2.x" }, | ||||
|     { name: "master", channel: "latest" }, | ||||
|     { name: "next" }, | ||||
|     { name: "pre/bar", channel: "bar", prerelease: true }, | ||||
|     { name: "pre/foo", channel: "foo", prerelease: true }, | ||||
|     { name: "beta", channel: "channel-beta", prerelease: true }, | ||||
|   ]); | ||||
| }); | ||||
|  | ||||
| @ -1,153 +1,156 @@ | ||||
| import test from 'ava'; | ||||
| import getTags from '../../lib/branches/get-tags.js'; | ||||
| import {gitAddNote, gitCheckout, gitCommits, gitRepo, gitTagVersion} from '../helpers/git-utils.js'; | ||||
| import test from "ava"; | ||||
| import getTags from "../../lib/branches/get-tags.js"; | ||||
| import { gitAddNote, gitCheckout, gitCommits, gitRepo, gitTagVersion } from "../helpers/git-utils.js"; | ||||
| 
 | ||||
| test('Get the valid tags', async (t) => { | ||||
|   const {cwd} = await gitRepo(); | ||||
|   const commits = await gitCommits(['First'], {cwd}); | ||||
|   await gitTagVersion('foo', undefined, {cwd}); | ||||
|   await gitTagVersion('v2.0.0', undefined, {cwd}); | ||||
|   commits.push(...(await gitCommits(['Second'], {cwd}))); | ||||
|   await gitTagVersion('v1.0.0', undefined, {cwd}); | ||||
|   commits.push(...(await gitCommits(['Third'], {cwd}))); | ||||
|   await gitTagVersion('v3.0', undefined, {cwd}); | ||||
|   commits.push(...(await gitCommits(['Fourth'], {cwd}))); | ||||
|   await gitTagVersion('v3.0.0-beta.1', undefined, {cwd}); | ||||
| test("Get the valid tags", async (t) => { | ||||
|   const { cwd } = await gitRepo(); | ||||
|   const commits = await gitCommits(["First"], { cwd }); | ||||
|   await gitTagVersion("foo", undefined, { cwd }); | ||||
|   await gitTagVersion("v2.0.0", undefined, { cwd }); | ||||
|   commits.push(...(await gitCommits(["Second"], { cwd }))); | ||||
|   await gitTagVersion("v1.0.0", undefined, { cwd }); | ||||
|   commits.push(...(await gitCommits(["Third"], { cwd }))); | ||||
|   await gitTagVersion("v3.0", undefined, { cwd }); | ||||
|   commits.push(...(await gitCommits(["Fourth"], { cwd }))); | ||||
|   await gitTagVersion("v3.0.0-beta.1", undefined, { cwd }); | ||||
| 
 | ||||
|   const result = await getTags({cwd, options: {tagFormat: `v\${version}`}}, [{name: 'master'}]); | ||||
|   const result = await getTags({ cwd, options: { tagFormat: `v\${version}` } }, [{ name: "master" }]); | ||||
| 
 | ||||
|   t.deepEqual(result, [ | ||||
|     { | ||||
|       name: 'master', | ||||
|       name: "master", | ||||
|       tags: [ | ||||
|         {gitTag: 'v1.0.0', version: '1.0.0', channels: [null]}, | ||||
|         {gitTag: 'v2.0.0', version: '2.0.0', channels: [null]}, | ||||
|         {gitTag: 'v3.0.0-beta.1', version: '3.0.0-beta.1', channels: [null]}, | ||||
|         { gitTag: "v1.0.0", version: "1.0.0", channels: [null] }, | ||||
|         { gitTag: "v2.0.0", version: "2.0.0", channels: [null] }, | ||||
|         { gitTag: "v3.0.0-beta.1", version: "3.0.0-beta.1", channels: [null] }, | ||||
|       ], | ||||
|     }, | ||||
|   ]); | ||||
| }); | ||||
| 
 | ||||
| test('Get the valid tags from multiple branches', async (t) => { | ||||
|   const {cwd} = await gitRepo(); | ||||
|   await gitCommits(['First'], {cwd}); | ||||
|   await gitTagVersion('v1.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, '1.x']}), 'v1.0.0', {cwd}); | ||||
|   await gitCommits(['Second'], {cwd}); | ||||
|   await gitTagVersion('v1.1.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, '1.x']}), 'v1.1.0', {cwd}); | ||||
|   await gitCheckout('1.x', true, {cwd}); | ||||
|   await gitCheckout('master', false, {cwd}); | ||||
|   await gitCommits(['Third'], {cwd}); | ||||
|   await gitTagVersion('v2.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v2.0.0', {cwd}); | ||||
|   await gitCheckout('next', true, {cwd}); | ||||
|   await gitCommits(['Fourth'], {cwd}); | ||||
|   await gitTagVersion('v3.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: ['next']}), 'v3.0.0', {cwd}); | ||||
| test("Get the valid tags from multiple branches", async (t) => { | ||||
|   const { cwd } = await gitRepo(); | ||||
|   await gitCommits(["First"], { cwd }); | ||||
|   await gitTagVersion("v1.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "1.x"] }), "v1.0.0", { cwd }); | ||||
|   await gitCommits(["Second"], { cwd }); | ||||
|   await gitTagVersion("v1.1.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "1.x"] }), "v1.1.0", { cwd }); | ||||
|   await gitCheckout("1.x", true, { cwd }); | ||||
|   await gitCheckout("master", false, { cwd }); | ||||
|   await gitCommits(["Third"], { cwd }); | ||||
|   await gitTagVersion("v2.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "next"] }), "v2.0.0", { cwd }); | ||||
|   await gitCheckout("next", true, { cwd }); | ||||
|   await gitCommits(["Fourth"], { cwd }); | ||||
|   await gitTagVersion("v3.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: ["next"] }), "v3.0.0", { cwd }); | ||||
| 
 | ||||
|   const result = await getTags({cwd, options: {tagFormat: `v\${version}`}}, [ | ||||
|     {name: '1.x'}, | ||||
|     {name: 'master'}, | ||||
|     {name: 'next'}, | ||||
|   const result = await getTags({ cwd, options: { tagFormat: `v\${version}` } }, [ | ||||
|     { name: "1.x" }, | ||||
|     { name: "master" }, | ||||
|     { name: "next" }, | ||||
|   ]); | ||||
| 
 | ||||
|   t.deepEqual(result, [ | ||||
|     { | ||||
|       name: '1.x', | ||||
|       name: "1.x", | ||||
|       tags: [ | ||||
|         {gitTag: 'v1.0.0', version: '1.0.0', channels: [null, '1.x']}, | ||||
|         {gitTag: 'v1.1.0', version: '1.1.0', channels: [null, '1.x']}, | ||||
|         { gitTag: "v1.0.0", version: "1.0.0", channels: [null, "1.x"] }, | ||||
|         { gitTag: "v1.1.0", version: "1.1.0", channels: [null, "1.x"] }, | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       name: 'master', | ||||
|       tags: [...result[0].tags, {gitTag: 'v2.0.0', version: '2.0.0', channels: [null, 'next']}], | ||||
|       name: "master", | ||||
|       tags: [...result[0].tags, { gitTag: "v2.0.0", version: "2.0.0", channels: [null, "next"] }], | ||||
|     }, | ||||
|     { | ||||
|       name: 'next', | ||||
|       tags: [...result[1].tags, {gitTag: 'v3.0.0', version: '3.0.0', channels: ['next']}], | ||||
|       name: "next", | ||||
|       tags: [...result[1].tags, { gitTag: "v3.0.0", version: "3.0.0", channels: ["next"] }], | ||||
|     }, | ||||
|   ]); | ||||
| }); | ||||
| 
 | ||||
| test('Return branches with and empty tags array if no valid tag is found', async (t) => { | ||||
|   const {cwd} = await gitRepo(); | ||||
|   await gitCommits(['First'], {cwd}); | ||||
|   await gitTagVersion('foo', undefined, {cwd}); | ||||
|   await gitCommits(['Second'], {cwd}); | ||||
|   await gitTagVersion('v2.0.x', undefined, {cwd}); | ||||
|   await gitCommits(['Third'], {cwd}); | ||||
|   await gitTagVersion('v3.0', undefined, {cwd}); | ||||
| test("Return branches with and empty tags array if no valid tag is found", async (t) => { | ||||
|   const { cwd } = await gitRepo(); | ||||
|   await gitCommits(["First"], { cwd }); | ||||
|   await gitTagVersion("foo", undefined, { cwd }); | ||||
|   await gitCommits(["Second"], { cwd }); | ||||
|   await gitTagVersion("v2.0.x", undefined, { cwd }); | ||||
|   await gitCommits(["Third"], { cwd }); | ||||
|   await gitTagVersion("v3.0", undefined, { cwd }); | ||||
| 
 | ||||
|   const result = await getTags({cwd, options: {tagFormat: `prefix@v\${version}`}}, [{name: 'master'}]); | ||||
|   const result = await getTags({ cwd, options: { tagFormat: `prefix@v\${version}` } }, [{ name: "master" }]); | ||||
| 
 | ||||
|   t.deepEqual(result, [{name: 'master', tags: []}]); | ||||
|   t.deepEqual(result, [{ name: "master", tags: [] }]); | ||||
| }); | ||||
| 
 | ||||
| test('Return branches with and empty tags array if no valid tag is found in history of configured branches', async (t) => { | ||||
|   const {cwd} = await gitRepo(); | ||||
|   await gitCommits(['First'], {cwd}); | ||||
|   await gitCheckout('next', true, {cwd}); | ||||
|   await gitCommits(['Second'], {cwd}); | ||||
|   await gitTagVersion('v1.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v1.0.0', {cwd}); | ||||
|   await gitCommits(['Third'], {cwd}); | ||||
|   await gitTagVersion('v2.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v2.0.0', {cwd}); | ||||
|   await gitCommits(['Fourth'], {cwd}); | ||||
|   await gitTagVersion('v3.0.0', undefined, {cwd}); | ||||
|   await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v3.0.0', {cwd}); | ||||
|   await gitCheckout('master', false, {cwd}); | ||||
| test("Return branches with and empty tags array if no valid tag is found in history of configured branches", async (t) => { | ||||
|   const { cwd } = await gitRepo(); | ||||
|   await gitCommits(["First"], { cwd }); | ||||
|   await gitCheckout("next", true, { cwd }); | ||||
|   await gitCommits(["Second"], { cwd }); | ||||
|   await gitTagVersion("v1.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "next"] }), "v1.0.0", { cwd }); | ||||
|   await gitCommits(["Third"], { cwd }); | ||||
|   await gitTagVersion("v2.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "next"] }), "v2.0.0", { cwd }); | ||||
|   await gitCommits(["Fourth"], { cwd }); | ||||
|   await gitTagVersion("v3.0.0", undefined, { cwd }); | ||||
|   await gitAddNote(JSON.stringify({ channels: [null, "next"] }), "v3.0.0", { cwd }); | ||||
|   await gitCheckout("master", false, { cwd }); | ||||
| 
 | ||||
|   const result = await getTags({cwd, options: {tagFormat: `prefix@v\${version}`}}, [{name: 'master'}, {name: 'next'}]); | ||||
|   const result = await getTags({ cwd, options: { tagFormat: `prefix@v\${version}` } }, [ | ||||
|     { name: "master" }, | ||||
|     { name: "next" }, | ||||
|   ]); | ||||
| 
 | ||||
|   t.deepEqual(result, [ | ||||
|     {name: 'master', tags: []}, | ||||
|     {name: 'next', tags: []}, | ||||
|     { name: "master", tags: [] }, | ||||
|     { name: "next", tags: [] }, | ||||
|   ]); | ||||
| }); | ||||
| 
 | ||||
| test('Get the highest valid tag corresponding to the "tagFormat"', async (t) => { | ||||
|   const {cwd} = await gitRepo(); | ||||
|   await gitCommits(['First'], {cwd}); | ||||
|   const { cwd } = await gitRepo(); | ||||
|   await gitCommits(["First"], { cwd }); | ||||
| 
 | ||||
|   await gitTagVersion('1.0.0', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `\${version}`}}, [{name: 'master'}]), [ | ||||
|     {name: 'master', tags: [{gitTag: '1.0.0', version: '1.0.0', channels: [null]}]}, | ||||
|   await gitTagVersion("1.0.0", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `\${version}` } }, [{ name: "master" }]), [ | ||||
|     { name: "master", tags: [{ gitTag: "1.0.0", version: "1.0.0", channels: [null] }] }, | ||||
|   ]); | ||||
| 
 | ||||
|   await gitTagVersion('foo-1.0.0-bar', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `foo-\${version}-bar`}}, [{name: 'master'}]), [ | ||||
|     {name: 'master', tags: [{gitTag: 'foo-1.0.0-bar', version: '1.0.0', channels: [null]}]}, | ||||
|   await gitTagVersion("foo-1.0.0-bar", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `foo-\${version}-bar` } }, [{ name: "master" }]), [ | ||||
|     { name: "master", tags: [{ gitTag: "foo-1.0.0-bar", version: "1.0.0", channels: [null] }] }, | ||||
|   ]); | ||||
| 
 | ||||
|   await gitTagVersion('foo-v1.0.0-bar', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `foo-v\${version}-bar`}}, [{name: 'master'}]), [ | ||||
|   await gitTagVersion("foo-v1.0.0-bar", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `foo-v\${version}-bar` } }, [{ name: "master" }]), [ | ||||
|     { | ||||
|       name: 'master', | ||||
|       tags: [{gitTag: 'foo-v1.0.0-bar', version: '1.0.0', channels: [null]}], | ||||
|       name: "master", | ||||
|       tags: [{ gitTag: "foo-v1.0.0-bar", version: "1.0.0", channels: [null] }], | ||||
|     }, | ||||
|   ]); | ||||
| 
 | ||||
|   await gitTagVersion('(.+)/1.0.0/(a-z)', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `(.+)/\${version}/(a-z)`}}, [{name: 'master'}]), [ | ||||
|   await gitTagVersion("(.+)/1.0.0/(a-z)", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `(.+)/\${version}/(a-z)` } }, [{ name: "master" }]), [ | ||||
|     { | ||||
|       name: 'master', | ||||
|       tags: [{gitTag: '(.+)/1.0.0/(a-z)', version: '1.0.0', channels: [null]}], | ||||
|       name: "master", | ||||
|       tags: [{ gitTag: "(.+)/1.0.0/(a-z)", version: "1.0.0", channels: [null] }], | ||||
|     }, | ||||
|   ]); | ||||
| 
 | ||||
|   await gitTagVersion('2.0.0-1.0.0-bar.1', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `2.0.0-\${version}-bar.1`}}, [{name: 'master'}]), [ | ||||
|   await gitTagVersion("2.0.0-1.0.0-bar.1", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `2.0.0-\${version}-bar.1` } }, [{ name: "master" }]), [ | ||||
|     { | ||||
|       name: 'master', | ||||
|       tags: [{gitTag: '2.0.0-1.0.0-bar.1', version: '1.0.0', channels: [null]}], | ||||
|       name: "master", | ||||
|       tags: [{ gitTag: "2.0.0-1.0.0-bar.1", version: "1.0.0", channels: [null] }], | ||||
|     }, | ||||
|   ]); | ||||
| 
 | ||||
|   await gitTagVersion('3.0.0-bar.2', undefined, {cwd}); | ||||
|   t.deepEqual(await getTags({cwd, options: {tagFormat: `\${version}-bar.2`}}, [{name: 'master'}]), [ | ||||
|     {name: 'master', tags: [{gitTag: '3.0.0-bar.2', version: '3.0.0', channels: [null]}]}, | ||||
|   await gitTagVersion("3.0.0-bar.2", undefined, { cwd }); | ||||
|   t.deepEqual(await getTags({ cwd, options: { tagFormat: `\${version}-bar.2` } }, [{ name: "master" }]), [ | ||||
|     { name: "master", tags: [{ gitTag: "3.0.0-bar.2", version: "3.0.0", channels: [null] }] }, | ||||
|   ]); | ||||
| }); | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| import test from 'ava'; | ||||
| import * as normalize from '../../lib/branches/normalize.js'; | ||||
| import test from "ava"; | ||||
| import * as normalize from "../../lib/branches/normalize.js"; | ||||
| 
 | ||||
| const toTags = (versions) => versions.map((version) => ({version})); | ||||
| const toTags = (versions) => versions.map((version) => ({ version })); | ||||
| 
 | ||||
| test('Maintenance branches - initial state', (t) => { | ||||
| test("Maintenance branches - initial state", (t) => { | ||||
|   const maintenance = [ | ||||
|     {name: '1.x', channel: '1.x', tags: []}, | ||||
|     {name: '1.1.x', tags: []}, | ||||
|     {name: '1.2.x', tags: []}, | ||||
|     { name: "1.x", channel: "1.x", tags: [] }, | ||||
|     { name: "1.1.x", tags: [] }, | ||||
|     { name: "1.2.x", tags: [] }, | ||||
|   ]; | ||||
|   const release = [{name: 'master', tags: []}]; | ||||
|   const release = [{ name: "master", tags: [] }]; | ||||
|   t.deepEqual( | ||||
|     normalize.maintenance({maintenance, release}).map(({type, name, range, accept, channel, mergeRange}) => ({ | ||||
|     normalize.maintenance({ maintenance, release }).map(({ type, name, range, accept, channel, mergeRange }) => ({ | ||||
|       type, | ||||
|       name, | ||||
|       range, | ||||
| @ -21,51 +21,51 @@ test('Maintenance branches - initial state', (t) => { | ||||
|     })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.1.x', | ||||
|         range: '>=1.1.0 <1.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.1.x", | ||||
|         range: ">=1.1.0 <1.0.0", | ||||
|         accept: [], | ||||
|         channel: '1.1.x', | ||||
|         mergeRange: '>=1.1.0 <1.2.0', | ||||
|         channel: "1.1.x", | ||||
|         mergeRange: ">=1.1.0 <1.2.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.2.x', | ||||
|         range: '>=1.2.0 <1.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.2.x", | ||||
|         range: ">=1.2.0 <1.0.0", | ||||
|         accept: [], | ||||
|         channel: '1.2.x', | ||||
|         mergeRange: '>=1.2.0 <1.3.0', | ||||
|         channel: "1.2.x", | ||||
|         mergeRange: ">=1.2.0 <1.3.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.x', | ||||
|         range: '>=1.3.0 <1.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.x", | ||||
|         range: ">=1.3.0 <1.0.0", | ||||
|         accept: [], | ||||
|         channel: '1.x', | ||||
|         mergeRange: '>=1.3.0 <2.0.0', | ||||
|         channel: "1.x", | ||||
|         mergeRange: ">=1.3.0 <2.0.0", | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Maintenance branches - cap range to first release present on default branch and not in any Maintenance one', (t) => { | ||||
| test("Maintenance branches - cap range to first release present on default branch and not in any Maintenance one", (t) => { | ||||
|   const maintenance = [ | ||||
|     {name: '1.x', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '1.2.1', '1.3.0', '1.4.0', '1.5.0'])}, | ||||
|     {name: 'name', range: '1.1.x', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1'])}, | ||||
|     {name: '1.2.x', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '1.2.1'])}, | ||||
|     {name: '2.x.x', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '1.2.1', '1.5.0'])}, | ||||
|     { name: "1.x", tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0", "1.2.1", "1.3.0", "1.4.0", "1.5.0"]) }, | ||||
|     { name: "name", range: "1.1.x", tags: toTags(["1.0.0", "1.0.1", "1.1.0", "1.1.1"]) }, | ||||
|     { name: "1.2.x", tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0", "1.2.1"]) }, | ||||
|     { name: "2.x.x", tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0", "1.2.1", "1.5.0"]) }, | ||||
|   ]; | ||||
|   const release = [ | ||||
|     { | ||||
|       name: 'master', | ||||
|       tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '1.2.1', '1.3.0', '1.4.0', '1.5.0', '1.6.0', '2.0.0']), | ||||
|       name: "master", | ||||
|       tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0", "1.2.1", "1.3.0", "1.4.0", "1.5.0", "1.6.0", "2.0.0"]), | ||||
|     }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .maintenance({maintenance, release}) | ||||
|       .map(({type, name, range, accept, channel, mergeRange: maintenanceRange}) => ({ | ||||
|       .maintenance({ maintenance, release }) | ||||
|       .map(({ type, name, range, accept, channel, mergeRange: maintenanceRange }) => ({ | ||||
|         type, | ||||
|         name, | ||||
|         range, | ||||
| @ -75,50 +75,50 @@ test('Maintenance branches - cap range to first release present on default branc | ||||
|       })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: 'name', | ||||
|         range: '>=1.1.1 <1.2.0', | ||||
|         accept: ['patch'], | ||||
|         channel: 'name', | ||||
|         mergeRange: '>=1.1.0 <1.2.0', | ||||
|         type: "maintenance", | ||||
|         name: "name", | ||||
|         range: ">=1.1.1 <1.2.0", | ||||
|         accept: ["patch"], | ||||
|         channel: "name", | ||||
|         mergeRange: ">=1.1.0 <1.2.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.2.x', | ||||
|         range: '>=1.2.1 <1.3.0', | ||||
|         accept: ['patch'], | ||||
|         channel: '1.2.x', | ||||
|         mergeRange: '>=1.2.0 <1.3.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.2.x", | ||||
|         range: ">=1.2.1 <1.3.0", | ||||
|         accept: ["patch"], | ||||
|         channel: "1.2.x", | ||||
|         mergeRange: ">=1.2.0 <1.3.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.x', | ||||
|         range: '>=1.5.0 <1.6.0', | ||||
|         accept: ['patch'], | ||||
|         channel: '1.x', | ||||
|         mergeRange: '>=1.3.0 <2.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.x", | ||||
|         range: ">=1.5.0 <1.6.0", | ||||
|         accept: ["patch"], | ||||
|         channel: "1.x", | ||||
|         mergeRange: ">=1.3.0 <2.0.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '2.x.x', | ||||
|         range: '>=2.0.0 <1.6.0', | ||||
|         type: "maintenance", | ||||
|         name: "2.x.x", | ||||
|         range: ">=2.0.0 <1.6.0", | ||||
|         accept: [], | ||||
|         channel: '2.x.x', | ||||
|         mergeRange: '>=2.0.0 <3.0.0', | ||||
|         channel: "2.x.x", | ||||
|         mergeRange: ">=2.0.0 <3.0.0", | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Maintenance branches - cap range to default branch last release if all release are also present on maintenance branch', (t) => { | ||||
| test("Maintenance branches - cap range to default branch last release if all release are also present on maintenance branch", (t) => { | ||||
|   const maintenance = [ | ||||
|     {name: '1.x', tags: toTags(['1.0.0', '1.2.0', '1.3.0'])}, | ||||
|     {name: '2.x.x', tags: toTags(['1.0.0', '1.2.0', '1.3.0', '2.0.0'])}, | ||||
|     { name: "1.x", tags: toTags(["1.0.0", "1.2.0", "1.3.0"]) }, | ||||
|     { name: "2.x.x", tags: toTags(["1.0.0", "1.2.0", "1.3.0", "2.0.0"]) }, | ||||
|   ]; | ||||
|   const release = [{name: 'master', tags: toTags(['1.0.0', '1.2.0', '1.3.0', '2.0.0'])}]; | ||||
|   const release = [{ name: "master", tags: toTags(["1.0.0", "1.2.0", "1.3.0", "2.0.0"]) }]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize.maintenance({maintenance, release}).map(({type, name, range, accept, channel, mergeRange}) => ({ | ||||
|     normalize.maintenance({ maintenance, release }).map(({ type, name, range, accept, channel, mergeRange }) => ({ | ||||
|       type, | ||||
|       name, | ||||
|       range, | ||||
| @ -128,270 +128,272 @@ test('Maintenance branches - cap range to default branch last release if all rel | ||||
|     })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '1.x', | ||||
|         range: '>=1.3.0 <2.0.0', | ||||
|         accept: ['patch', 'minor'], | ||||
|         channel: '1.x', | ||||
|         mergeRange: '>=1.0.0 <2.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "1.x", | ||||
|         range: ">=1.3.0 <2.0.0", | ||||
|         accept: ["patch", "minor"], | ||||
|         channel: "1.x", | ||||
|         mergeRange: ">=1.0.0 <2.0.0", | ||||
|       }, | ||||
|       { | ||||
|         type: 'maintenance', | ||||
|         name: '2.x.x', | ||||
|         range: '>=2.0.0 <2.0.0', | ||||
|         type: "maintenance", | ||||
|         name: "2.x.x", | ||||
|         range: ">=2.0.0 <2.0.0", | ||||
|         accept: [], | ||||
|         channel: '2.x.x', | ||||
|         mergeRange: '>=2.0.0 <3.0.0', | ||||
|         channel: "2.x.x", | ||||
|         mergeRange: ">=2.0.0 <3.0.0", | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - initial state', (t) => { | ||||
| test("Release branches - initial state", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: []}, | ||||
|     {name: 'next', channel: 'next', tags: []}, | ||||
|     {name: 'next-major', tags: []}, | ||||
|     { name: "master", tags: [] }, | ||||
|     { name: "next", channel: "next", tags: [] }, | ||||
|     { name: "next-major", tags: [] }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'master', | ||||
|         range: '>=1.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         type: "release", | ||||
|         name: "master", | ||||
|         range: ">=1.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: undefined, | ||||
|         main: true, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next', | ||||
|         range: '>=1.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next', | ||||
|         type: "release", | ||||
|         name: "next", | ||||
|         range: ">=1.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next", | ||||
|         main: false, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next-major', | ||||
|         range: '>=1.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next-major', | ||||
|         type: "release", | ||||
|         name: "next-major", | ||||
|         range: ">=1.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next-major", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - 3 release branches', (t) => { | ||||
| test("Release branches - 3 release branches", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: toTags(['1.0.0', '1.0.1', '1.0.2'])}, | ||||
|     {name: 'next', tags: toTags(['1.0.0', '1.0.1', '1.0.2', '1.1.0', '1.2.0'])}, | ||||
|     {name: 'next-major', tags: toTags(['1.0.0', '1.0.1', '1.0.2', '1.1.0', '1.2.0', '2.0.0', '2.0.1', '2.1.0'])}, | ||||
|     { name: "master", tags: toTags(["1.0.0", "1.0.1", "1.0.2"]) }, | ||||
|     { name: "next", tags: toTags(["1.0.0", "1.0.1", "1.0.2", "1.1.0", "1.2.0"]) }, | ||||
|     { name: "next-major", tags: toTags(["1.0.0", "1.0.1", "1.0.2", "1.1.0", "1.2.0", "2.0.0", "2.0.1", "2.1.0"]) }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       {type: 'release', name: 'master', range: '>=1.0.2 <1.1.0', accept: ['patch'], channel: undefined, main: true}, | ||||
|       { type: "release", name: "master", range: ">=1.0.2 <1.1.0", accept: ["patch"], channel: undefined, main: true }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next', | ||||
|         range: '>=1.2.0 <2.0.0', | ||||
|         accept: ['patch', 'minor'], | ||||
|         channel: 'next', | ||||
|         type: "release", | ||||
|         name: "next", | ||||
|         range: ">=1.2.0 <2.0.0", | ||||
|         accept: ["patch", "minor"], | ||||
|         channel: "next", | ||||
|         main: false, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next-major', | ||||
|         range: '>=2.1.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next-major', | ||||
|         type: "release", | ||||
|         name: "next-major", | ||||
|         range: ">=2.1.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next-major", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - 2 release branches', (t) => { | ||||
| test("Release branches - 2 release branches", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1', '1.2.0'])}, | ||||
|     {name: 'next', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1', '1.2.0', '2.0.0', '2.0.1', '2.1.0'])}, | ||||
|     { name: "master", tags: toTags(["1.0.0", "1.0.1", "1.1.0", "1.1.1", "1.2.0"]) }, | ||||
|     { name: "next", tags: toTags(["1.0.0", "1.0.1", "1.1.0", "1.1.1", "1.2.0", "2.0.0", "2.0.1", "2.1.0"]) }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'master', | ||||
|         range: '>=1.2.0 <2.0.0', | ||||
|         accept: ['patch', 'minor'], | ||||
|         type: "release", | ||||
|         name: "master", | ||||
|         range: ">=1.2.0 <2.0.0", | ||||
|         accept: ["patch", "minor"], | ||||
|         channel: undefined, | ||||
|         main: true, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next', | ||||
|         range: '>=2.1.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next', | ||||
|         type: "release", | ||||
|         name: "next", | ||||
|         range: ">=2.1.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - 1 release branches', (t) => { | ||||
|   const release = [{name: 'master', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0'])}]; | ||||
| test("Release branches - 1 release branches", (t) => { | ||||
|   const release = [{ name: "master", tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0"]) }]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})), | ||||
|     [{type: 'release', name: 'master', range: '>=1.2.0', accept: ['patch', 'minor', 'major'], channel: undefined}] | ||||
|     normalize | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel }) => ({ type, name, range, accept, channel })), | ||||
|     [{ type: "release", name: "master", range: ">=1.2.0", accept: ["patch", "minor", "major"], channel: undefined }] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - cap ranges to first release only present on following branch', (t) => { | ||||
| test("Release branches - cap ranges to first release only present on following branch", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: toTags(['1.0.0', '1.1.0', '1.2.0', '2.0.0'])}, | ||||
|     {name: 'next', tags: toTags(['1.0.0', '1.1.0', '1.2.0', '2.0.0', '2.1.0'])}, | ||||
|     {name: 'next-major', tags: toTags(['1.0.0', '1.1.0', '1.2.0', '2.0.0', '2.1.0', '2.2.0'])}, | ||||
|     { name: "master", tags: toTags(["1.0.0", "1.1.0", "1.2.0", "2.0.0"]) }, | ||||
|     { name: "next", tags: toTags(["1.0.0", "1.1.0", "1.2.0", "2.0.0", "2.1.0"]) }, | ||||
|     { name: "next-major", tags: toTags(["1.0.0", "1.1.0", "1.2.0", "2.0.0", "2.1.0", "2.2.0"]) }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       {type: 'release', name: 'master', range: '>=2.0.0 <2.1.0', accept: ['patch'], channel: undefined, main: true}, | ||||
|       {type: 'release', name: 'next', range: '>=2.1.0 <2.2.0', accept: ['patch'], channel: 'next', main: false}, | ||||
|       { type: "release", name: "master", range: ">=2.0.0 <2.1.0", accept: ["patch"], channel: undefined, main: true }, | ||||
|       { type: "release", name: "next", range: ">=2.1.0 <2.2.0", accept: ["patch"], channel: "next", main: false }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next-major', | ||||
|         range: '>=2.2.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next-major', | ||||
|         type: "release", | ||||
|         name: "next-major", | ||||
|         range: ">=2.2.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next-major", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - Handle missing previous tags in branch history', (t) => { | ||||
| test("Release branches - Handle missing previous tags in branch history", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: toTags(['1.0.0', '2.0.0'])}, | ||||
|     {name: 'next', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '2.0.0'])}, | ||||
|     { name: "master", tags: toTags(["1.0.0", "2.0.0"]) }, | ||||
|     { name: "next", tags: toTags(["1.0.0", "1.1.0", "1.1.1", "1.2.0", "2.0.0"]) }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'master', | ||||
|         range: '>=2.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         type: "release", | ||||
|         name: "master", | ||||
|         range: ">=2.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: undefined, | ||||
|         main: true, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next', | ||||
|         range: '>=2.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next', | ||||
|         type: "release", | ||||
|         name: "next", | ||||
|         range: ">=2.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Release branches - limit releases on 2nd and 3rd branch based on 1st branch last release', (t) => { | ||||
| test("Release branches - limit releases on 2nd and 3rd branch based on 1st branch last release", (t) => { | ||||
|   const release = [ | ||||
|     {name: 'master', tags: toTags(['1.0.0', '1.1.0', '2.0.0', '3.0.0'])}, | ||||
|     {name: 'next', tags: toTags(['1.0.0', '1.1.0'])}, | ||||
|     {name: 'next-major', tags: toTags(['1.0.0', '1.1.0', '2.0.0'])}, | ||||
|     { name: "master", tags: toTags(["1.0.0", "1.1.0", "2.0.0", "3.0.0"]) }, | ||||
|     { name: "next", tags: toTags(["1.0.0", "1.1.0"]) }, | ||||
|     { name: "next-major", tags: toTags(["1.0.0", "1.1.0", "2.0.0"]) }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize | ||||
|       .release({release}) | ||||
|       .map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})), | ||||
|       .release({ release }) | ||||
|       .map(({ type, name, range, accept, channel, main }) => ({ type, name, range, accept, channel, main })), | ||||
|     [ | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'master', | ||||
|         range: '>=3.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         type: "release", | ||||
|         name: "master", | ||||
|         range: ">=3.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: undefined, | ||||
|         main: true, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next', | ||||
|         range: '>=3.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next', | ||||
|         type: "release", | ||||
|         name: "next", | ||||
|         range: ">=3.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next", | ||||
|         main: false, | ||||
|       }, | ||||
|       { | ||||
|         type: 'release', | ||||
|         name: 'next-major', | ||||
|         range: '>=3.0.0', | ||||
|         accept: ['patch', 'minor', 'major'], | ||||
|         channel: 'next-major', | ||||
|         type: "release", | ||||
|         name: "next-major", | ||||
|         range: ">=3.0.0", | ||||
|         accept: ["patch", "minor", "major"], | ||||
|         channel: "next-major", | ||||
|         main: false, | ||||
|       }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Prerelease branches', (t) => { | ||||
| test("Prerelease branches", (t) => { | ||||
|   const prerelease = [ | ||||
|     {name: 'beta', channel: 'beta', prerelease: true, tags: []}, | ||||
|     {name: 'alpha', prerelease: 'preview', tags: []}, | ||||
|     { name: "beta", channel: "beta", prerelease: true, tags: [] }, | ||||
|     { name: "alpha", prerelease: "preview", tags: [] }, | ||||
|   ]; | ||||
| 
 | ||||
|   t.deepEqual( | ||||
|     normalize.prerelease({prerelease}).map(({type, name, channel}) => ({type, name, channel})), | ||||
|     normalize.prerelease({ prerelease }).map(({ type, name, channel }) => ({ type, name, channel })), | ||||
|     [ | ||||
|       {type: 'prerelease', name: 'beta', channel: 'beta'}, | ||||
|       {type: 'prerelease', name: 'alpha', channel: 'alpha'}, | ||||
|       { type: "prerelease", name: "beta", channel: "beta" }, | ||||
|       { type: "prerelease", name: "alpha", channel: "alpha" }, | ||||
|     ] | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Allow to set channel to "false" to prevent default', (t) => { | ||||
|   const maintenance = [{name: '1.x', channel: false, tags: []}]; | ||||
|   const maintenance = [{ name: "1.x", channel: false, tags: [] }]; | ||||
|   const release = [ | ||||
|     {name: 'master', channel: false, tags: []}, | ||||
|     {name: 'next', channel: false, tags: []}, | ||||
|     { name: "master", channel: false, tags: [] }, | ||||
|     { name: "next", channel: false, tags: [] }, | ||||
|   ]; | ||||
|   const prerelease = [{name: 'beta', channel: false, prerelease: true, tags: []}]; | ||||
|   const prerelease = [{ name: "beta", channel: false, prerelease: true, tags: [] }]; | ||||
|   t.deepEqual( | ||||
|     normalize.maintenance({maintenance, release}).map(({name, channel}) => ({name, channel})), | ||||
|     [{name: '1.x', channel: false}] | ||||
|     normalize.maintenance({ maintenance, release }).map(({ name, channel }) => ({ name, channel })), | ||||
|     [{ name: "1.x", channel: false }] | ||||
|   ); | ||||
|   t.deepEqual( | ||||
|     normalize.release({release}).map(({name, channel}) => ({name, channel})), | ||||
|     normalize.release({ release }).map(({ name, channel }) => ({ name, channel })), | ||||
|     [ | ||||
|       {name: 'master', channel: false}, | ||||
|       {name: 'next', channel: false}, | ||||
|       { name: "master", channel: false }, | ||||
|       { name: "next", channel: false }, | ||||
|     ] | ||||
|   ); | ||||
|   t.deepEqual( | ||||
|     normalize.prerelease({prerelease}).map(({name, channel}) => ({name, channel})), | ||||
|     [{name: 'beta', channel: false}] | ||||
|     normalize.prerelease({ prerelease }).map(({ name, channel }) => ({ name, channel })), | ||||
|     [{ name: "beta", channel: false }] | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| @ -1,92 +1,94 @@ | ||||
| import test from 'ava'; | ||||
| import {maintenance, prerelease, release} from '../../lib/definitions/branches.js'; | ||||
| import test from "ava"; | ||||
| import { maintenance, prerelease, release } from "../../lib/definitions/branches.js"; | ||||
| 
 | ||||
| test('A "maintenance" branch is identified by having a "range" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', (t) => { | ||||
|   /* eslint-disable unicorn/no-fn-reference-in-iterator */ | ||||
|   t.true(maintenance.filter({name: '1.x.x'})); | ||||
|   t.true(maintenance.filter({name: '1.0.x'})); | ||||
|   t.true(maintenance.filter({name: '1.x'})); | ||||
|   t.true(maintenance.filter({name: 'some-name', range: '1.x.x'})); | ||||
|   t.true(maintenance.filter({name: 'some-name', range: '1.1.x'})); | ||||
|   t.true(maintenance.filter({name: 'some-name', range: ''})); | ||||
|   t.true(maintenance.filter({name: 'some-name', range: true})); | ||||
|   t.true(maintenance.filter({ name: "1.x.x" })); | ||||
|   t.true(maintenance.filter({ name: "1.0.x" })); | ||||
|   t.true(maintenance.filter({ name: "1.x" })); | ||||
|   t.true(maintenance.filter({ name: "some-name", range: "1.x.x" })); | ||||
|   t.true(maintenance.filter({ name: "some-name", range: "1.1.x" })); | ||||
|   t.true(maintenance.filter({ name: "some-name", range: "" })); | ||||
|   t.true(maintenance.filter({ name: "some-name", range: true })); | ||||
| 
 | ||||
|   t.false(maintenance.filter({name: 'some-name', range: null})); | ||||
|   t.false(maintenance.filter({name: 'some-name', range: false})); | ||||
|   t.false(maintenance.filter({name: 'some-name'})); | ||||
|   t.false(maintenance.filter({name: '1.0.0'})); | ||||
|   t.false(maintenance.filter({name: 'x.x.x'})); | ||||
|   t.false(maintenance.filter({ name: "some-name", range: null })); | ||||
|   t.false(maintenance.filter({ name: "some-name", range: false })); | ||||
|   t.false(maintenance.filter({ name: "some-name" })); | ||||
|   t.false(maintenance.filter({ name: "1.0.0" })); | ||||
|   t.false(maintenance.filter({ name: "x.x.x" })); | ||||
|   /* eslint-enable unicorn/no-fn-reference-in-iterator */ | ||||
| }); | ||||
| 
 | ||||
| test('A "maintenance" branches must have a "range" property formatted like "N.x", "N.x.x" or "N.N.x"', (t) => { | ||||
|   t.true(maintenance.branchValidator({name: 'some-name', range: '1.x.x'})); | ||||
|   t.true(maintenance.branchValidator({name: 'some-name', range: '1.1.x'})); | ||||
|   t.true(maintenance.branchValidator({ name: "some-name", range: "1.x.x" })); | ||||
|   t.true(maintenance.branchValidator({ name: "some-name", range: "1.1.x" })); | ||||
| 
 | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: '^1.0.0'})); | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: '>=1.0.0 <2.0.0'})); | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: '1.0.0'})); | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: 'wrong-range'})); | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: true})); | ||||
|   t.false(maintenance.branchValidator({name: 'some-name', range: ''})); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: "^1.0.0" })); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: ">=1.0.0 <2.0.0" })); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: "1.0.0" })); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: "wrong-range" })); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: true })); | ||||
|   t.false(maintenance.branchValidator({ name: "some-name", range: "" })); | ||||
| }); | ||||
| 
 | ||||
| test('The "maintenance" branches must have unique ranges', (t) => { | ||||
|   t.true(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.0.x'}])); | ||||
|   t.true(maintenance.branchesValidator([{ range: "1.x.x" }, { range: "1.0.x" }])); | ||||
| 
 | ||||
|   t.false(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.x.x'}])); | ||||
|   t.false(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.x'}])); | ||||
|   t.false(maintenance.branchesValidator([{ range: "1.x.x" }, { range: "1.x.x" }])); | ||||
|   t.false(maintenance.branchesValidator([{ range: "1.x.x" }, { range: "1.x" }])); | ||||
| }); | ||||
| 
 | ||||
| test('A "prerelease" branch is identified by having a thruthy "prerelease" property', (t) => { | ||||
|   /* eslint-disable unicorn/no-fn-reference-in-iterator */ | ||||
|   t.true(prerelease.filter({name: 'some-name', prerelease: true})); | ||||
|   t.true(prerelease.filter({name: 'some-name', prerelease: 'beta'})); | ||||
|   t.true(prerelease.filter({name: 'some-name', prerelease: ''})); | ||||
|   t.true(prerelease.filter({ name: "some-name", prerelease: true })); | ||||
|   t.true(prerelease.filter({ name: "some-name", prerelease: "beta" })); | ||||
|   t.true(prerelease.filter({ name: "some-name", prerelease: "" })); | ||||
| 
 | ||||
|   t.false(prerelease.filter({name: 'some-name', prerelease: null})); | ||||
|   t.false(prerelease.filter({name: 'some-name', prerelease: false})); | ||||
|   t.false(prerelease.filter({name: 'some-name'})); | ||||
|   t.false(prerelease.filter({ name: "some-name", prerelease: null })); | ||||
|   t.false(prerelease.filter({ name: "some-name", prerelease: false })); | ||||
|   t.false(prerelease.filter({ name: "some-name" })); | ||||
|   /* eslint-enable unicorn/no-fn-reference-in-iterator */ | ||||
| }); | ||||
| 
 | ||||
| test('A "prerelease" branch must have a valid prerelease detonation in "prerelease" property or in "name" if "prerelease" is "true"', (t) => { | ||||
|   t.true(prerelease.branchValidator({name: 'beta', prerelease: true})); | ||||
|   t.true(prerelease.branchValidator({name: 'some-name', prerelease: 'beta'})); | ||||
|   t.true(prerelease.branchValidator({ name: "beta", prerelease: true })); | ||||
|   t.true(prerelease.branchValidator({ name: "some-name", prerelease: "beta" })); | ||||
| 
 | ||||
|   t.false(prerelease.branchValidator({name: 'some-name', prerelease: ''})); | ||||
|   t.false(prerelease.branchValidator({name: 'some-name', prerelease: null})); | ||||
|   t.false(prerelease.branchValidator({name: 'some-name', prerelease: false})); | ||||
|   t.false(prerelease.branchValidator({name: 'some-name', prerelease: '000'})); | ||||
|   t.false(prerelease.branchValidator({name: 'some-name', prerelease: '#beta'})); | ||||
|   t.false(prerelease.branchValidator({name: '000', prerelease: true})); | ||||
|   t.false(prerelease.branchValidator({name: '#beta', prerelease: true})); | ||||
|   t.false(prerelease.branchValidator({ name: "some-name", prerelease: "" })); | ||||
|   t.false(prerelease.branchValidator({ name: "some-name", prerelease: null })); | ||||
|   t.false(prerelease.branchValidator({ name: "some-name", prerelease: false })); | ||||
|   t.false(prerelease.branchValidator({ name: "some-name", prerelease: "000" })); | ||||
|   t.false(prerelease.branchValidator({ name: "some-name", prerelease: "#beta" })); | ||||
|   t.false(prerelease.branchValidator({ name: "000", prerelease: true })); | ||||
|   t.false(prerelease.branchValidator({ name: "#beta", prerelease: true })); | ||||
| }); | ||||
| 
 | ||||
| test('The "prerelease" branches must have unique "prerelease" property', (t) => { | ||||
|   t.true(prerelease.branchesValidator([{prerelease: 'beta'}, {prerelease: 'alpha'}])); | ||||
|   t.true(prerelease.branchesValidator([{ prerelease: "beta" }, { prerelease: "alpha" }])); | ||||
| 
 | ||||
|   t.false(prerelease.branchesValidator([{range: 'beta'}, {range: 'beta'}, {range: 'alpha'}])); | ||||
|   t.false(prerelease.branchesValidator([{ range: "beta" }, { range: "beta" }, { range: "alpha" }])); | ||||
| }); | ||||
| 
 | ||||
| test('A "release" branch is identified by not havin a "range" or "prerelease" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', (t) => { | ||||
|   /* eslint-disable unicorn/no-fn-reference-in-iterator */ | ||||
|   t.true(release.filter({name: 'some-name'})); | ||||
|   t.true(release.filter({ name: "some-name" })); | ||||
| 
 | ||||
|   t.false(release.filter({name: '1.x.x'})); | ||||
|   t.false(release.filter({name: '1.0.x'})); | ||||
|   t.false(release.filter({name: 'some-name', range: '1.x.x'})); | ||||
|   t.false(release.filter({name: 'some-name', range: '1.1.x'})); | ||||
|   t.false(release.filter({name: 'some-name', prerelease: true})); | ||||
|   t.false(release.filter({name: 'some-name', prerelease: 'beta'})); | ||||
|   t.false(release.filter({ name: "1.x.x" })); | ||||
|   t.false(release.filter({ name: "1.0.x" })); | ||||
|   t.false(release.filter({ name: "some-name", range: "1.x.x" })); | ||||
|   t.false(release.filter({ name: "some-name", range: "1.1.x" })); | ||||
|   t.false(release.filter({ name: "some-name", prerelease: true })); | ||||
|   t.false(release.filter({ name: "some-name", prerelease: "beta" })); | ||||
|   /* eslint-enable unicorn/no-fn-reference-in-iterator */ | ||||
| }); | ||||
| 
 | ||||
| test('There must be between 1 and 3 release branches', (t) => { | ||||
|   t.true(release.branchesValidator([{name: 'branch1'}])); | ||||
|   t.true(release.branchesValidator([{name: 'branch1'}, {name: 'branch2'}])); | ||||
|   t.true(release.branchesValidator([{name: 'branch1'}, {name: 'branch2'}, {name: 'branch3'}])); | ||||
| test("There must be between 1 and 3 release branches", (t) => { | ||||
|   t.true(release.branchesValidator([{ name: "branch1" }])); | ||||
|   t.true(release.branchesValidator([{ name: "branch1" }, { name: "branch2" }])); | ||||
|   t.true(release.branchesValidator([{ name: "branch1" }, { name: "branch2" }, { name: "branch3" }])); | ||||
| 
 | ||||
|   t.false(release.branchesValidator([])); | ||||
|   t.false(release.branchesValidator([{name: 'branch1'}, {name: 'branch2'}, {name: 'branch3'}, {name: 'branch4'}])); | ||||
|   t.false( | ||||
|     release.branchesValidator([{ name: "branch1" }, { name: "branch2" }, { name: "branch3" }, { name: "branch4" }]) | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| import test from 'ava'; | ||||
| import plugins from '../../lib/definitions/plugins.js'; | ||||
| import {RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT} from '../../lib/definitions/constants.js'; | ||||
| import test from "ava"; | ||||
| import plugins from "../../lib/definitions/plugins.js"; | ||||
| import { RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT } from "../../lib/definitions/constants.js"; | ||||
| 
 | ||||
| test('The "analyzeCommits" plugin output must be either undefined or a valid semver release type', (t) => { | ||||
|   t.false(plugins.analyzeCommits.outputValidator('invalid')); | ||||
|   t.false(plugins.analyzeCommits.outputValidator("invalid")); | ||||
|   t.false(plugins.analyzeCommits.outputValidator(1)); | ||||
|   t.false(plugins.analyzeCommits.outputValidator({})); | ||||
| 
 | ||||
|   t.true(plugins.analyzeCommits.outputValidator()); | ||||
|   t.true(plugins.analyzeCommits.outputValidator(null)); | ||||
|   t.true(plugins.analyzeCommits.outputValidator('major')); | ||||
|   t.true(plugins.analyzeCommits.outputValidator("major")); | ||||
| }); | ||||
| 
 | ||||
| test('The "generateNotes" plugin output, if defined, must be a string', (t) => { | ||||
| @ -18,57 +18,57 @@ test('The "generateNotes" plugin output, if defined, must be a string', (t) => { | ||||
| 
 | ||||
|   t.true(plugins.generateNotes.outputValidator()); | ||||
|   t.true(plugins.generateNotes.outputValidator(null)); | ||||
|   t.true(plugins.generateNotes.outputValidator('')); | ||||
|   t.true(plugins.generateNotes.outputValidator('string')); | ||||
|   t.true(plugins.generateNotes.outputValidator("")); | ||||
|   t.true(plugins.generateNotes.outputValidator("string")); | ||||
| }); | ||||
| 
 | ||||
| test('The "publish" plugin output, if defined, must be an object or "false"', (t) => { | ||||
|   t.false(plugins.publish.outputValidator(1)); | ||||
|   t.false(plugins.publish.outputValidator('string')); | ||||
|   t.false(plugins.publish.outputValidator("string")); | ||||
| 
 | ||||
|   t.true(plugins.publish.outputValidator({})); | ||||
|   t.true(plugins.publish.outputValidator()); | ||||
|   t.true(plugins.publish.outputValidator(null)); | ||||
|   t.true(plugins.publish.outputValidator('')); | ||||
|   t.true(plugins.publish.outputValidator("")); | ||||
|   t.true(plugins.publish.outputValidator(false)); | ||||
| }); | ||||
| 
 | ||||
| test('The "addChannel" plugin output, if defined, must be an object', (t) => { | ||||
|   t.false(plugins.addChannel.outputValidator(1)); | ||||
|   t.false(plugins.addChannel.outputValidator('string')); | ||||
|   t.false(plugins.addChannel.outputValidator("string")); | ||||
| 
 | ||||
|   t.true(plugins.addChannel.outputValidator({})); | ||||
|   t.true(plugins.addChannel.outputValidator()); | ||||
|   t.true(plugins.addChannel.outputValidator(null)); | ||||
|   t.true(plugins.addChannel.outputValidator('')); | ||||
|   t.true(plugins.addChannel.outputValidator("")); | ||||
| }); | ||||
| 
 | ||||
| test('The "generateNotes" plugins output are concatenated with separator and sensitive data is hidden', (t) => { | ||||
|   const env = {MY_TOKEN: 'secret token'}; | ||||
|   t.is(plugins.generateNotes.postprocess(['note 1', 'note 2'], {env}), `note 1${RELEASE_NOTES_SEPARATOR}note 2`); | ||||
|   t.is(plugins.generateNotes.postprocess(['', 'note'], {env}), 'note'); | ||||
|   t.is(plugins.generateNotes.postprocess([undefined, 'note'], {env}), 'note'); | ||||
|   t.is(plugins.generateNotes.postprocess(['note 1', '', 'note 2'], {env}), `note 1${RELEASE_NOTES_SEPARATOR}note 2`); | ||||
|   const env = { MY_TOKEN: "secret token" }; | ||||
|   t.is(plugins.generateNotes.postprocess(["note 1", "note 2"], { env }), `note 1${RELEASE_NOTES_SEPARATOR}note 2`); | ||||
|   t.is(plugins.generateNotes.postprocess(["", "note"], { env }), "note"); | ||||
|   t.is(plugins.generateNotes.postprocess([undefined, "note"], { env }), "note"); | ||||
|   t.is(plugins.generateNotes.postprocess(["note 1", "", "note 2"], { env }), `note 1${RELEASE_NOTES_SEPARATOR}note 2`); | ||||
|   t.is( | ||||
|     plugins.generateNotes.postprocess(['note 1', undefined, 'note 2'], {env}), | ||||
|     plugins.generateNotes.postprocess(["note 1", undefined, "note 2"], { env }), | ||||
|     `note 1${RELEASE_NOTES_SEPARATOR}note 2` | ||||
|   ); | ||||
| 
 | ||||
|   t.is( | ||||
|     plugins.generateNotes.postprocess( | ||||
|       [`Note 1: Exposing token ${env.MY_TOKEN}`, `Note 2: Exposing token ${SECRET_REPLACEMENT}`], | ||||
|       {env} | ||||
|       { env } | ||||
|     ), | ||||
|     `Note 1: Exposing token ${SECRET_REPLACEMENT}${RELEASE_NOTES_SEPARATOR}Note 2: Exposing token ${SECRET_REPLACEMENT}` | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('The "analyzeCommits" plugins output are reduced to the highest release type', (t) => { | ||||
|   t.is(plugins.analyzeCommits.postprocess(['major', 'minor']), 'major'); | ||||
|   t.is(plugins.analyzeCommits.postprocess(['', 'minor']), 'minor'); | ||||
|   t.is(plugins.analyzeCommits.postprocess([undefined, 'patch']), 'patch'); | ||||
|   t.is(plugins.analyzeCommits.postprocess([null, 'patch']), 'patch'); | ||||
|   t.is(plugins.analyzeCommits.postprocess(['wrong_type', 'minor']), 'minor'); | ||||
|   t.is(plugins.analyzeCommits.postprocess(["major", "minor"]), "major"); | ||||
|   t.is(plugins.analyzeCommits.postprocess(["", "minor"]), "minor"); | ||||
|   t.is(plugins.analyzeCommits.postprocess([undefined, "patch"]), "patch"); | ||||
|   t.is(plugins.analyzeCommits.postprocess([null, "patch"]), "patch"); | ||||
|   t.is(plugins.analyzeCommits.postprocess(["wrong_type", "minor"]), "minor"); | ||||
|   t.is(plugins.analyzeCommits.postprocess([]), undefined); | ||||
|   t.is(plugins.analyzeCommits.postprocess(['wrong_type']), undefined); | ||||
|   t.is(plugins.analyzeCommits.postprocess(["wrong_type"]), undefined); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										2
									
								
								test/fixtures/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/fixtures/index.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| export default () => {} | ||||
| export default () => {}; | ||||
|  | ||||
							
								
								
									
										6
									
								
								test/fixtures/plugin-error-inherited.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								test/fixtures/plugin-error-inherited.js
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| import SemanticReleaseError from '@semantic-release/error'; | ||||
| import SemanticReleaseError from "@semantic-release/error"; | ||||
| 
 | ||||
| class InheritedError extends SemanticReleaseError { | ||||
|   constructor(message, code) { | ||||
| @ -10,5 +10,5 @@ class InheritedError extends SemanticReleaseError { | ||||
| } | ||||
| 
 | ||||
| export default () => { | ||||
|   throw new InheritedError('Inherited error', 'EINHERITED'); | ||||
| } | ||||
|   throw new InheritedError("Inherited error", "EINHERITED"); | ||||
| }; | ||||
|  | ||||
							
								
								
									
										6
									
								
								test/fixtures/plugin-error.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								test/fixtures/plugin-error.js
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| export default () => { | ||||
|   const error = new Error('a'); | ||||
|   error.errorProperty = 'errorProperty'; | ||||
|   const error = new Error("a"); | ||||
|   error.errorProperty = "errorProperty"; | ||||
|   throw error; | ||||
| } | ||||
| }; | ||||
|  | ||||
							
								
								
									
										6
									
								
								test/fixtures/plugin-errors.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								test/fixtures/plugin-errors.js
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| import AggregateError from 'aggregate-error'; | ||||
| import AggregateError from "aggregate-error"; | ||||
| 
 | ||||
| export default () => { | ||||
|   throw new AggregateError([new Error('a'), new Error('b')]); | ||||
| } | ||||
|   throw new AggregateError([new Error("a"), new Error("b")]); | ||||
| }; | ||||
|  | ||||
							
								
								
									
										2
									
								
								test/fixtures/plugin-identity.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/fixtures/plugin-identity.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| export default (pluginConfig, context) => context | ||||
| export default (pluginConfig, context) => context; | ||||
|  | ||||
							
								
								
									
										4
									
								
								test/fixtures/plugin-log-env.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								test/fixtures/plugin-log-env.js
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| export default (pluginConfig, {env, logger}) => { | ||||
| export default (pluginConfig, { env, logger }) => { | ||||
|   console.log(`Console: Exposing token ${env.MY_TOKEN}`); | ||||
|   logger.log(`Log: Exposing token ${env.MY_TOKEN}`); | ||||
|   logger.error(`Error: Console token ${env.MY_TOKEN}`); | ||||
|   throw new Error(`Throw error: Exposing ${env.MY_TOKEN}`); | ||||
| } | ||||
| }; | ||||
|  | ||||
							
								
								
									
										2
									
								
								test/fixtures/plugin-result-config.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/fixtures/plugin-result-config.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| export default (pluginConfig, context) => ({pluginConfig, context}) | ||||
| export default (pluginConfig, context) => ({ pluginConfig, context }); | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| import {temporaryDirectory} from 'tempy'; | ||||
| import {execa} from 'execa'; | ||||
| import fileUrl from 'file-url'; | ||||
| import pEachSeries from 'p-each-series'; | ||||
| import gitLogParser from 'git-log-parser'; | ||||
| import getStream from 'get-stream'; | ||||
| import {GIT_NOTE_REF} from '../../lib/definitions/constants.js'; | ||||
| import { temporaryDirectory } from "tempy"; | ||||
| import { execa } from "execa"; | ||||
| import fileUrl from "file-url"; | ||||
| import pEachSeries from "p-each-series"; | ||||
| import gitLogParser from "git-log-parser"; | ||||
| import getStream from "get-stream"; | ||||
| import { GIT_NOTE_REF } from "../../lib/definitions/constants.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Commit message information. | ||||
| @ -25,14 +25,14 @@ import {GIT_NOTE_REF} from '../../lib/definitions/constants.js'; | ||||
|  */ | ||||
| export async function initGit(withRemote) { | ||||
|   const cwd = temporaryDirectory(); | ||||
|   const args = withRemote ? ['--bare', '--initial-branch=master'] : ['--initial-branch=master']; | ||||
|   const args = withRemote ? ["--bare", "--initial-branch=master"] : ["--initial-branch=master"]; | ||||
| 
 | ||||
|   await execa('git', ['init', ...args], {cwd}).catch(() => { | ||||
|     const args = withRemote ? ['--bare'] : []; | ||||
|     return execa('git', ['init', ...args], {cwd}); | ||||
|   await execa("git", ["init", ...args], { cwd }).catch(() => { | ||||
|     const args = withRemote ? ["--bare"] : []; | ||||
|     return execa("git", ["init", ...args], { cwd }); | ||||
|   }); | ||||
|   const repositoryUrl = fileUrl(cwd); | ||||
|   return {cwd, repositoryUrl}; | ||||
|   return { cwd, repositoryUrl }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -45,18 +45,18 @@ export async function initGit(withRemote) { | ||||
|  * @param {String} [branch='master'] The branch to initialize. | ||||
|  * @return {String} The path of the clone if `withRemote` is `true`, the path of the repository otherwise. | ||||
|  */ | ||||
| export async function gitRepo(withRemote, branch = 'master') { | ||||
|   let {cwd, repositoryUrl} = await initGit(withRemote); | ||||
| export async function gitRepo(withRemote, branch = "master") { | ||||
|   let { cwd, repositoryUrl } = await initGit(withRemote); | ||||
|   if (withRemote) { | ||||
|     await initBareRepo(repositoryUrl, branch); | ||||
|     cwd = await gitShallowClone(repositoryUrl, branch); | ||||
|   } else { | ||||
|     await gitCheckout(branch, true, {cwd}); | ||||
|     await gitCheckout(branch, true, { cwd }); | ||||
|   } | ||||
| 
 | ||||
|   await execa('git', ['config', 'commit.gpgsign', false], {cwd}); | ||||
|   await execa("git", ["config", "commit.gpgsign", false], { cwd }); | ||||
| 
 | ||||
|   return {cwd, repositoryUrl}; | ||||
|   return { cwd, repositoryUrl }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -70,12 +70,12 @@ export async function gitRepo(withRemote, branch = 'master') { | ||||
|  * @param {String} repositoryUrl The URL of the bare repository. | ||||
|  * @param {String} [branch='master'] the branch to initialize. | ||||
|  */ | ||||
| export async function initBareRepo(repositoryUrl, branch = 'master') { | ||||
| export async function initBareRepo(repositoryUrl, branch = "master") { | ||||
|   const cwd = temporaryDirectory(); | ||||
|   await execa('git', ['clone', '--no-hardlinks', repositoryUrl, cwd], {cwd}); | ||||
|   await gitCheckout(branch, true, {cwd}); | ||||
|   await gitCommits(['Initial commit'], {cwd}); | ||||
|   await execa('git', ['push', repositoryUrl, branch], {cwd}); | ||||
|   await execa("git", ["clone", "--no-hardlinks", repositoryUrl, cwd], { cwd }); | ||||
|   await gitCheckout(branch, true, { cwd }); | ||||
|   await gitCommits(["Initial commit"], { cwd }); | ||||
|   await execa("git", ["push", repositoryUrl, branch], { cwd }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -90,7 +90,9 @@ export async function gitCommits(messages, execaOptions) { | ||||
|   await pEachSeries( | ||||
|     messages, | ||||
|     async (message) => | ||||
|       (await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOptions)).stdout | ||||
|       ( | ||||
|         await execa("git", ["commit", "-m", message, "--allow-empty", "--no-gpg-sign"], execaOptions) | ||||
|       ).stdout | ||||
|   ); | ||||
|   return (await gitGetCommits(undefined, execaOptions)).slice(0, messages.length); | ||||
| } | ||||
| @ -104,12 +106,17 @@ export async function gitCommits(messages, execaOptions) { | ||||
|  * @return {Array<Object>} The list of parsed commits. | ||||
|  */ | ||||
| export async function gitGetCommits(from, execaOptions) { | ||||
|   Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}}); | ||||
|   Object.assign(gitLogParser.fields, { | ||||
|     hash: "H", | ||||
|     message: "B", | ||||
|     gitTags: "d", | ||||
|     committerDate: { key: "ci", type: Date }, | ||||
|   }); | ||||
|   return ( | ||||
|     await getStream.array( | ||||
|       gitLogParser.parse( | ||||
|         {_: `${from ? from + '..' : ''}HEAD`}, | ||||
|         {...execaOptions, env: {...process.env, ...execaOptions.env}} | ||||
|         { _: `${from ? from + ".." : ""}HEAD` }, | ||||
|         { ...execaOptions, env: { ...process.env, ...execaOptions.env } } | ||||
|       ) | ||||
|     ) | ||||
|   ).map((commit) => { | ||||
| @ -127,7 +134,7 @@ export async function gitGetCommits(from, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitCheckout(branch, create, execaOptions) { | ||||
|   await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOptions); | ||||
|   await execa("git", create ? ["checkout", "-b", branch] : ["checkout", branch], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -137,7 +144,7 @@ export async function gitCheckout(branch, create, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitFetch(repositoryUrl, execaOptions) { | ||||
|   await execa('git', ['fetch', repositoryUrl], execaOptions); | ||||
|   await execa("git", ["fetch", repositoryUrl], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -148,7 +155,7 @@ export async function gitFetch(repositoryUrl, execaOptions) { | ||||
|  * @return {String} The sha of the head commit in the current git repository. | ||||
|  */ | ||||
| export async function gitHead(execaOptions) { | ||||
|   return (await execa('git', ['rev-parse', 'HEAD'], execaOptions)).stdout; | ||||
|   return (await execa("git", ["rev-parse", "HEAD"], execaOptions)).stdout; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -159,7 +166,7 @@ export async function gitHead(execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitTagVersion(tagName, sha, execaOptions) { | ||||
|   await execa('git', sha ? ['tag', '-f', tagName, sha] : ['tag', tagName], execaOptions); | ||||
|   await execa("git", sha ? ["tag", "-f", tagName, sha] : ["tag", tagName], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -171,10 +178,10 @@ export async function gitTagVersion(tagName, sha, execaOptions) { | ||||
|  * @param {Number} [depth=1] The number of commit to clone. | ||||
|  * @return {String} The path of the cloned repository. | ||||
|  */ | ||||
| export async function gitShallowClone(repositoryUrl, branch = 'master', depth = 1) { | ||||
| export async function gitShallowClone(repositoryUrl, branch = "master", depth = 1) { | ||||
|   const cwd = temporaryDirectory(); | ||||
| 
 | ||||
|   await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, repositoryUrl, cwd], { | ||||
|   await execa("git", ["clone", "--no-hardlinks", "--no-tags", "-b", branch, "--depth", depth, repositoryUrl, cwd], { | ||||
|     cwd, | ||||
|   }); | ||||
|   return cwd; | ||||
| @ -190,21 +197,21 @@ export async function gitShallowClone(repositoryUrl, branch = 'master', depth = | ||||
| export async function gitDetachedHead(repositoryUrl, head) { | ||||
|   const cwd = temporaryDirectory(); | ||||
| 
 | ||||
|   await execa('git', ['init'], {cwd}); | ||||
|   await execa('git', ['remote', 'add', 'origin', repositoryUrl], {cwd}); | ||||
|   await execa('git', ['fetch', repositoryUrl], {cwd}); | ||||
|   await execa('git', ['checkout', head], {cwd}); | ||||
|   await execa("git", ["init"], { cwd }); | ||||
|   await execa("git", ["remote", "add", "origin", repositoryUrl], { cwd }); | ||||
|   await execa("git", ["fetch", repositoryUrl], { cwd }); | ||||
|   await execa("git", ["checkout", head], { cwd }); | ||||
|   return cwd; | ||||
| } | ||||
| 
 | ||||
| export async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) { | ||||
|   const cwd = temporaryDirectory(); | ||||
| 
 | ||||
|   await execa('git', ['init'], {cwd}); | ||||
|   await execa('git', ['remote', 'add', 'origin', repositoryUrl], {cwd}); | ||||
|   await execa('git', ['fetch', '--force', repositoryUrl, `${branch}:remotes/origin/${branch}`], {cwd}); | ||||
|   await execa('git', ['reset', '--hard', head], {cwd}); | ||||
|   await execa('git', ['checkout', '-q', '-B', branch], {cwd}); | ||||
|   await execa("git", ["init"], { cwd }); | ||||
|   await execa("git", ["remote", "add", "origin", repositoryUrl], { cwd }); | ||||
|   await execa("git", ["fetch", "--force", repositoryUrl, `${branch}:remotes/origin/${branch}`], { cwd }); | ||||
|   await execa("git", ["reset", "--hard", head], { cwd }); | ||||
|   await execa("git", ["checkout", "-q", "-B", branch], { cwd }); | ||||
|   return cwd; | ||||
| } | ||||
| 
 | ||||
| @ -216,7 +223,7 @@ export async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitAddConfig(name, value, execaOptions) { | ||||
|   await execa('git', ['config', '--add', name, value], execaOptions); | ||||
|   await execa("git", ["config", "--add", name, value], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -228,7 +235,7 @@ export async function gitAddConfig(name, value, execaOptions) { | ||||
|  * @return {String} The sha of the commit associated with `tagName` on the local repository. | ||||
|  */ | ||||
| export async function gitTagHead(tagName, execaOptions) { | ||||
|   return (await execa('git', ['rev-list', '-1', tagName], execaOptions)).stdout; | ||||
|   return (await execa("git", ["rev-list", "-1", tagName], execaOptions)).stdout; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -241,8 +248,8 @@ export async function gitTagHead(tagName, execaOptions) { | ||||
|  * @return {String} The sha of the commit associated with `tagName` on the remote repository. | ||||
|  */ | ||||
| export async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) { | ||||
|   return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOptions)).stdout | ||||
|     .split('\n') | ||||
|   return (await execa("git", ["ls-remote", "--tags", repositoryUrl, tagName], execaOptions)).stdout | ||||
|     .split("\n") | ||||
|     .filter((tag) => Boolean(tag)) | ||||
|     .map((tag) => tag.match(/^(?<tag>\S+)/)[1])[0]; | ||||
| } | ||||
| @ -256,7 +263,7 @@ export async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) { | ||||
|  * @return {String} The tag associatedwith the sha in parameter or `null`. | ||||
|  */ | ||||
| export async function gitCommitTag(gitHead, execaOptions) { | ||||
|   return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOptions)).stdout; | ||||
|   return (await execa("git", ["describe", "--tags", "--exact-match", gitHead], execaOptions)).stdout; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -269,7 +276,7 @@ export async function gitCommitTag(gitHead, execaOptions) { | ||||
|  * @throws {Error} if the push failed. | ||||
|  */ | ||||
| export async function gitPush(repositoryUrl, branch, execaOptions) { | ||||
|   await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`], execaOptions); | ||||
|   await execa("git", ["push", "--tags", repositoryUrl, `HEAD:${branch}`], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -279,7 +286,7 @@ export async function gitPush(repositoryUrl, branch, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function merge(ref, execaOptions) { | ||||
|   await execa('git', ['merge', '--no-ff', ref], execaOptions); | ||||
|   await execa("git", ["merge", "--no-ff", ref], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -289,7 +296,7 @@ export async function merge(ref, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function mergeFf(ref, execaOptions) { | ||||
|   await execa('git', ['merge', '--ff', ref], execaOptions); | ||||
|   await execa("git", ["merge", "--ff", ref], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -299,7 +306,7 @@ export async function mergeFf(ref, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function rebase(ref, execaOptions) { | ||||
|   await execa('git', ['rebase', ref], execaOptions); | ||||
|   await execa("git", ["rebase", ref], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -310,7 +317,7 @@ export async function rebase(ref, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitAddNote(note, ref, execaOptions) { | ||||
|   await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-m', note, ref], execaOptions); | ||||
|   await execa("git", ["notes", "--ref", GIT_NOTE_REF, "add", "-m", note, ref], execaOptions); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -320,5 +327,5 @@ export async function gitAddNote(note, ref, execaOptions) { | ||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||
|  */ | ||||
| export async function gitGetNote(ref, execaOptions) { | ||||
|   return (await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOptions)).stdout; | ||||
|   return (await execa("git", ["notes", "--ref", GIT_NOTE_REF, "show", ref], execaOptions)).stdout; | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,14 @@ | ||||
| import Docker from 'dockerode'; | ||||
| import getStream from 'get-stream'; | ||||
| import pRetry from 'p-retry'; | ||||
| import {gitShallowClone, initBareRepo} from './git-utils.js'; | ||||
| import Docker from "dockerode"; | ||||
| import getStream from "get-stream"; | ||||
| import pRetry from "p-retry"; | ||||
| import { gitShallowClone, initBareRepo } from "./git-utils.js"; | ||||
| 
 | ||||
| const IMAGE = 'semanticrelease/docker-gitbox:latest'; | ||||
| const IMAGE = "semanticrelease/docker-gitbox:latest"; | ||||
| const SERVER_PORT = 80; | ||||
| const HOST_PORT = 2080; | ||||
| const SERVER_HOST = 'localhost'; | ||||
| const GIT_USERNAME = 'integration'; | ||||
| const GIT_PASSWORD = 'suchsecure'; | ||||
| const SERVER_HOST = "localhost"; | ||||
| const GIT_USERNAME = "integration"; | ||||
| const GIT_PASSWORD = "suchsecure"; | ||||
| const docker = new Docker(); | ||||
| let container; | ||||
| 
 | ||||
| @ -24,14 +24,14 @@ export async function start() { | ||||
|     Tty: true, | ||||
|     Image: IMAGE, | ||||
|     HostConfig: { | ||||
|         PortBindings: {[`${SERVER_PORT}/tcp`]: [{HostPort: `${HOST_PORT}`}]} | ||||
|       PortBindings: { [`${SERVER_PORT}/tcp`]: [{ HostPort: `${HOST_PORT}` }] }, | ||||
|     }, | ||||
|     ExposedPorts: {[`${SERVER_PORT}/tcp`]: {}} | ||||
|     ExposedPorts: { [`${SERVER_PORT}/tcp`]: {} }, | ||||
|   }); | ||||
|   await container.start(); | ||||
| 
 | ||||
|   const exec = await container.exec({ | ||||
|     Cmd: ['ng-auth', '-u', GIT_USERNAME, '-p', GIT_PASSWORD], | ||||
|     Cmd: ["ng-auth", "-u", GIT_USERNAME, "-p", GIT_PASSWORD], | ||||
|     AttachStdout: true, | ||||
|     AttachStderr: true, | ||||
|   }); | ||||
| @ -54,9 +54,9 @@ export async function stop() { | ||||
|  * @param {String} [description=`Repository ${name}`] The repository description. | ||||
|  * @return {Object} The `repositoryUrl` (URL without auth) and `authUrl` (URL with auth). | ||||
|  */ | ||||
| export async function createRepo(name, branch = 'master', description = `Repository ${name}`) { | ||||
| export async function createRepo(name, branch = "master", description = `Repository ${name}`) { | ||||
|   const exec = await container.exec({ | ||||
|     Cmd: ['repo-admin', '-n', name, '-d', description], | ||||
|     Cmd: ["repo-admin", "-n", name, "-d", description], | ||||
|     AttachStdout: true, | ||||
|     AttachStderr: true, | ||||
|   }); | ||||
| @ -66,8 +66,8 @@ export async function createRepo(name, branch = 'master', description = `Reposit | ||||
|   const authUrl = `http://${gitCredential}@${SERVER_HOST}:${HOST_PORT}/git/${name}.git`; | ||||
| 
 | ||||
|   // Retry as the server might take a few ms to make the repo available push
 | ||||
|   await pRetry(() => initBareRepo(authUrl, branch), {retries: 5, minTimeout: 500, factor: 2}); | ||||
|   await pRetry(() => initBareRepo(authUrl, branch), { retries: 5, minTimeout: 500, factor: 2 }); | ||||
|   const cwd = await gitShallowClone(authUrl); | ||||
| 
 | ||||
|   return {cwd, repositoryUrl, authUrl}; | ||||
|   return { cwd, repositoryUrl, authUrl }; | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| import Docker from 'dockerode'; | ||||
| import getStream from 'get-stream'; | ||||
| import got from 'got'; | ||||
| import pRetry from 'p-retry'; | ||||
| import {mockServerClient} from 'mockserver-client'; | ||||
| import Docker from "dockerode"; | ||||
| import getStream from "get-stream"; | ||||
| import got from "got"; | ||||
| import pRetry from "p-retry"; | ||||
| import { mockServerClient } from "mockserver-client"; | ||||
| 
 | ||||
| const IMAGE = 'mockserver/mockserver:latest'; | ||||
| const IMAGE = "mockserver/mockserver:latest"; | ||||
| const MOCK_SERVER_PORT = 1080; | ||||
| const MOCK_SERVER_HOST = 'localhost'; | ||||
| const MOCK_SERVER_HOST = "localhost"; | ||||
| const docker = new Docker(); | ||||
| let container; | ||||
| 
 | ||||
| @ -20,15 +20,15 @@ export async function start() { | ||||
|     Tty: true, | ||||
|     Image: IMAGE, | ||||
|     HostConfig: { | ||||
|         PortBindings: {[`${MOCK_SERVER_PORT}/tcp`]: [{HostPort: `${MOCK_SERVER_PORT}`}]} | ||||
|       PortBindings: { [`${MOCK_SERVER_PORT}/tcp`]: [{ HostPort: `${MOCK_SERVER_PORT}` }] }, | ||||
|     }, | ||||
|     ExposedPorts: {[`${MOCK_SERVER_PORT}/tcp`]: {}} | ||||
|     ExposedPorts: { [`${MOCK_SERVER_PORT}/tcp`]: {} }, | ||||
|   }); | ||||
|   await container.start(); | ||||
| 
 | ||||
|   try { | ||||
|     // Wait for the mock server to be ready
 | ||||
|     await pRetry(() => got.put(`http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}/status`, {cache: false}), { | ||||
|     await pRetry(() => got.put(`http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}/status`, { cache: false }), { | ||||
|       retries: 7, | ||||
|       minTimeout: 1000, | ||||
|       factor: 2, | ||||
| @ -70,17 +70,17 @@ export const url = `http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}`; | ||||
|  */ | ||||
| export async function mock( | ||||
|   path, | ||||
|   {body: requestBody, headers: requestHeaders}, | ||||
|   {method = 'POST', statusCode = 200, body: responseBody} | ||||
|   { body: requestBody, headers: requestHeaders }, | ||||
|   { method = "POST", statusCode = 200, body: responseBody } | ||||
| ) { | ||||
|   await client.mockAnyResponse({ | ||||
|     httpRequest: {path, method}, | ||||
|     httpRequest: { path, method }, | ||||
|     httpResponse: { | ||||
|       statusCode, | ||||
|       headers: [{name: 'Content-Type', values: ['application/json; charset=utf-8']}], | ||||
|       headers: [{ name: "Content-Type", values: ["application/json; charset=utf-8"] }], | ||||
|       body: JSON.stringify(responseBody), | ||||
|     }, | ||||
|     times: {remainingTimes: 1, unlimited: false}, | ||||
|     times: { remainingTimes: 1, unlimited: false }, | ||||
|   }); | ||||
| 
 | ||||
|   return { | ||||
| @ -88,7 +88,7 @@ export async function mock( | ||||
|     path, | ||||
|     headers: requestHeaders, | ||||
|     body: requestBody | ||||
|       ? {type: 'JSON', json: JSON.stringify(requestBody), matchType: 'ONLY_MATCHING_FIELDS'} | ||||
|       ? { type: "JSON", json: JSON.stringify(requestBody), matchType: "ONLY_MATCHING_FIELDS" } | ||||
|       : undefined, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| import path, {dirname} from 'node:path'; | ||||
| import {fileURLToPath} from 'node:url'; | ||||
| import Docker from 'dockerode'; | ||||
| import getStream from 'get-stream'; | ||||
| import got from 'got'; | ||||
| import delay from 'delay'; | ||||
| import pRetry from 'p-retry'; | ||||
| import path, { dirname } from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
| import Docker from "dockerode"; | ||||
| import getStream from "get-stream"; | ||||
| import got from "got"; | ||||
| import delay from "delay"; | ||||
| import pRetry from "p-retry"; | ||||
| 
 | ||||
| const IMAGE = 'verdaccio/verdaccio:5'; | ||||
| const IMAGE = "verdaccio/verdaccio:5"; | ||||
| const REGISTRY_PORT = 4873; | ||||
| const REGISTRY_HOST = 'localhost'; | ||||
| const NPM_USERNAME = 'integration'; | ||||
| const NPM_PASSWORD = 'suchsecure'; | ||||
| const NPM_EMAIL = 'integration@test.com'; | ||||
| const REGISTRY_HOST = "localhost"; | ||||
| const NPM_USERNAME = "integration"; | ||||
| const NPM_PASSWORD = "suchsecure"; | ||||
| const NPM_EMAIL = "integration@test.com"; | ||||
| const docker = new Docker(); | ||||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||||
| let container, npmToken; | ||||
| @ -26,10 +26,10 @@ export async function start() { | ||||
|     Tty: true, | ||||
|     Image: IMAGE, | ||||
|     HostConfig: { | ||||
|         PortBindings: {[`${REGISTRY_PORT}/tcp`]: [{HostPort: `${REGISTRY_PORT}`}]}, | ||||
|         Binds: [`${path.join(__dirname, 'config.yaml')}:/verdaccio/conf/config.yaml`], | ||||
|       PortBindings: { [`${REGISTRY_PORT}/tcp`]: [{ HostPort: `${REGISTRY_PORT}` }] }, | ||||
|       Binds: [`${path.join(__dirname, "config.yaml")}:/verdaccio/conf/config.yaml`], | ||||
|     }, | ||||
|     ExposedPorts: {[`${REGISTRY_PORT}/tcp`]: {}} | ||||
|     ExposedPorts: { [`${REGISTRY_PORT}/tcp`]: {} }, | ||||
|   }); | ||||
| 
 | ||||
|   await container.start(); | ||||
| @ -37,7 +37,7 @@ export async function start() { | ||||
| 
 | ||||
|   try { | ||||
|     // Wait for the registry to be ready
 | ||||
|     await pRetry(() => got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/`, {cache: false}), { | ||||
|     await pRetry(() => got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/`, { cache: false }), { | ||||
|       retries: 7, | ||||
|       minTimeout: 1000, | ||||
|       factor: 2, | ||||
| @ -48,24 +48,24 @@ export async function start() { | ||||
| 
 | ||||
|   // Create user
 | ||||
|   await got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/-/user/org.couchdb.user:${NPM_USERNAME}`, { | ||||
|     method: 'PUT', | ||||
|     method: "PUT", | ||||
|     json: { | ||||
|       _id: `org.couchdb.user:${NPM_USERNAME}`, | ||||
|       name: NPM_USERNAME, | ||||
|       roles: [], | ||||
|       type: 'user', | ||||
|       type: "user", | ||||
|       password: NPM_PASSWORD, | ||||
|       email: NPM_EMAIL, | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   // Create token for user
 | ||||
|   ({token: npmToken} = await got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/-/npm/v1/tokens`, { | ||||
|   ({ token: npmToken } = await got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/-/npm/v1/tokens`, { | ||||
|     username: NPM_USERNAME, | ||||
|     password: NPM_PASSWORD, | ||||
|     method: 'POST', | ||||
|     headers: {'content-type': 'application/json'}, | ||||
|     json: {password: NPM_PASSWORD, readonly: false, cidr_whitelist: []} | ||||
|     method: "POST", | ||||
|     headers: { "content-type": "application/json" }, | ||||
|     json: { password: NPM_PASSWORD, readonly: false, cidr_whitelist: [] }, | ||||
|   }).json()); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import {execa} from 'execa'; | ||||
| import { execa } from "execa"; | ||||
| 
 | ||||
| export async function npmView(packageName, env) { | ||||
|   return JSON.parse((await execa('npm', ['view', packageName, '--json'], {env})).stdout); | ||||
|   return JSON.parse((await execa("npm", ["view", packageName, "--json"], { env })).stdout); | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import test from 'ava'; | ||||
| import {noop} from 'lodash-es'; | ||||
| import {stub} from 'sinon'; | ||||
| import normalize from '../../lib/plugins/normalize.js'; | ||||
| import test from "ava"; | ||||
| import { noop } from "lodash-es"; | ||||
| import { stub } from "sinon"; | ||||
| import normalize from "../../lib/plugins/normalize.js"; | ||||
| 
 | ||||
| const cwd = process.cwd(); | ||||
| 
 | ||||
| @ -10,7 +10,7 @@ test.beforeEach((t) => { | ||||
|   t.context.log = stub(); | ||||
|   t.context.error = stub(); | ||||
|   t.context.success = stub(); | ||||
|   t.context.stderr = {write: stub()}; | ||||
|   t.context.stderr = { write: stub() }; | ||||
|   t.context.logger = { | ||||
|     log: t.context.log, | ||||
|     error: t.context.error, | ||||
| @ -19,98 +19,108 @@ test.beforeEach((t) => { | ||||
|   }; | ||||
| }); | ||||
| 
 | ||||
| test('Normalize and load plugin from string', async (t) => { | ||||
| test("Normalize and load plugin from string", async (t) => { | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, logger: t.context.logger}, | ||||
|     'verifyConditions', | ||||
|     './test/fixtures/plugin-noop.cjs', | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "verifyConditions", | ||||
|     "./test/fixtures/plugin-noop.cjs", | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   t.is(plugin.pluginName, './test/fixtures/plugin-noop.cjs'); | ||||
|   t.is(typeof plugin, 'function'); | ||||
|   t.is(plugin.pluginName, "./test/fixtures/plugin-noop.cjs"); | ||||
|   t.is(typeof plugin, "function"); | ||||
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/plugin-noop.cjs"']); | ||||
| }); | ||||
| 
 | ||||
| test('Normalize and load plugin from object', async (t) => { | ||||
| test("Normalize and load plugin from object", async (t) => { | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, logger: t.context.logger}, | ||||
|     'publish', | ||||
|     {path: './test/fixtures/plugin-noop.cjs'}, | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "publish", | ||||
|     { path: "./test/fixtures/plugin-noop.cjs" }, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   t.is(plugin.pluginName, './test/fixtures/plugin-noop.cjs'); | ||||
|   t.is(typeof plugin, 'function'); | ||||
|   t.is(plugin.pluginName, "./test/fixtures/plugin-noop.cjs"); | ||||
|   t.is(typeof plugin, "function"); | ||||
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "publish" from "./test/fixtures/plugin-noop.cjs"']); | ||||
| }); | ||||
| 
 | ||||
| test('Normalize and load plugin from a base file path', async (t) => { | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-noop.cjs', { | ||||
|     './plugin-noop.cjs': './test/fixtures', | ||||
|   }); | ||||
| test("Normalize and load plugin from a base file path", async (t) => { | ||||
|   const plugin = await normalize( | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "verifyConditions", | ||||
|     "./plugin-noop.cjs", | ||||
|     { | ||||
|       "./plugin-noop.cjs": "./test/fixtures", | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   t.is(plugin.pluginName, './plugin-noop.cjs'); | ||||
|   t.is(typeof plugin, 'function'); | ||||
|   t.is(plugin.pluginName, "./plugin-noop.cjs"); | ||||
|   t.is(typeof plugin, "function"); | ||||
|   t.deepEqual(t.context.success.args[0], [ | ||||
|     'Loaded plugin "verifyConditions" from "./plugin-noop.cjs" in shareable config "./test/fixtures"', | ||||
|   ]); | ||||
| }); | ||||
| 
 | ||||
| test('Wrap plugin in a function that add the "pluginName" to the error"', async (t) => { | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-error', { | ||||
|     './plugin-error': './test/fixtures', | ||||
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "verifyConditions", "./plugin-error", { | ||||
|     "./plugin-error": "./test/fixtures", | ||||
|   }); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(plugin({options: {}})); | ||||
|   const error = await t.throwsAsync(plugin({ options: {} })); | ||||
| 
 | ||||
|   t.is(error.pluginName, './plugin-error'); | ||||
|   t.is(error.pluginName, "./plugin-error"); | ||||
| }); | ||||
| 
 | ||||
| test('Wrap plugin in a function that add the "pluginName" to multiple errors"', async (t) => { | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-errors', { | ||||
|     './plugin-errors': './test/fixtures', | ||||
|   }); | ||||
|   const plugin = await normalize( | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "verifyConditions", | ||||
|     "./plugin-errors", | ||||
|     { | ||||
|       "./plugin-errors": "./test/fixtures", | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   const errors = [...(await t.throwsAsync(plugin({options: {}}))).errors]; | ||||
|   const errors = [...(await t.throwsAsync(plugin({ options: {} }))).errors]; | ||||
|   for (const error of errors) { | ||||
|     t.is(error.pluginName, './plugin-errors'); | ||||
|     t.is(error.pluginName, "./plugin-errors"); | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| test('Normalize and load plugin from function', async (t) => { | ||||
| test("Normalize and load plugin from function", async (t) => { | ||||
|   const pluginFunction = () => {}; | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, '', pluginFunction, {}); | ||||
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "", pluginFunction, {}); | ||||
| 
 | ||||
|   t.is(plugin.pluginName, '[Function: pluginFunction]'); | ||||
|   t.is(typeof plugin, 'function'); | ||||
|   t.is(plugin.pluginName, "[Function: pluginFunction]"); | ||||
|   t.is(typeof plugin, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Normalize and load plugin that retuns multiple functions', async (t) => { | ||||
| test("Normalize and load plugin that retuns multiple functions", async (t) => { | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, logger: t.context.logger}, | ||||
|     'verifyConditions', | ||||
|     './test/fixtures/multi-plugin.cjs', | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "verifyConditions", | ||||
|     "./test/fixtures/multi-plugin.cjs", | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   t.is(typeof plugin, 'function'); | ||||
|   t.is(typeof plugin, "function"); | ||||
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/multi-plugin.cjs"']); | ||||
| }); | ||||
| 
 | ||||
| test('Wrap "analyzeCommits" plugin in a function that validate the output of the plugin', async (t) => { | ||||
|   const analyzeCommits = stub().resolves(2); | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger}, | ||||
|     'analyzeCommits', | ||||
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger }, | ||||
|     "analyzeCommits", | ||||
|     analyzeCommits, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(plugin({options: {}})); | ||||
|   const error = await t.throwsAsync(plugin({ options: {} })); | ||||
| 
 | ||||
|   t.is(error.code, 'EANALYZECOMMITSOUTPUT'); | ||||
|   t.is(error.name, 'SemanticReleaseError'); | ||||
|   t.is(error.code, "EANALYZECOMMITSOUTPUT"); | ||||
|   t.is(error.name, "SemanticReleaseError"); | ||||
|   t.truthy(error.message); | ||||
|   t.truthy(error.details); | ||||
|   t.regex(error.details, /2/); | ||||
| @ -119,16 +129,16 @@ test('Wrap "analyzeCommits" plugin in a function that validate the output of the | ||||
| test('Wrap "generateNotes" plugin in a function that validate the output of the plugin', async (t) => { | ||||
|   const generateNotes = stub().resolves(2); | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger}, | ||||
|     'generateNotes', | ||||
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger }, | ||||
|     "generateNotes", | ||||
|     generateNotes, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(plugin({options: {}})); | ||||
|   const error = await t.throwsAsync(plugin({ options: {} })); | ||||
| 
 | ||||
|   t.is(error.code, 'EGENERATENOTESOUTPUT'); | ||||
|   t.is(error.name, 'SemanticReleaseError'); | ||||
|   t.is(error.code, "EGENERATENOTESOUTPUT"); | ||||
|   t.is(error.name, "SemanticReleaseError"); | ||||
|   t.truthy(error.message); | ||||
|   t.truthy(error.details); | ||||
|   t.regex(error.details, /2/); | ||||
| @ -137,16 +147,16 @@ test('Wrap "generateNotes" plugin in a function that validate the output of the | ||||
| test('Wrap "publish" plugin in a function that validate the output of the plugin', async (t) => { | ||||
|   const publish = stub().resolves(2); | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger}, | ||||
|     'publish', | ||||
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger }, | ||||
|     "publish", | ||||
|     publish, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(plugin({options: {}})); | ||||
|   const error = await t.throwsAsync(plugin({ options: {} })); | ||||
| 
 | ||||
|   t.is(error.code, 'EPUBLISHOUTPUT'); | ||||
|   t.is(error.name, 'SemanticReleaseError'); | ||||
|   t.is(error.code, "EPUBLISHOUTPUT"); | ||||
|   t.is(error.name, "SemanticReleaseError"); | ||||
|   t.truthy(error.message); | ||||
|   t.truthy(error.details); | ||||
|   t.regex(error.details, /2/); | ||||
| @ -155,16 +165,16 @@ test('Wrap "publish" plugin in a function that validate the output of the plugin | ||||
| test('Wrap "addChannel" plugin in a function that validate the output of the plugin', async (t) => { | ||||
|   const addChannel = stub().resolves(2); | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger}, | ||||
|     'addChannel', | ||||
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger }, | ||||
|     "addChannel", | ||||
|     addChannel, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(plugin({options: {}})); | ||||
|   const error = await t.throwsAsync(plugin({ options: {} })); | ||||
| 
 | ||||
|   t.is(error.code, 'EADDCHANNELOUTPUT'); | ||||
|   t.is(error.name, 'SemanticReleaseError'); | ||||
|   t.is(error.code, "EADDCHANNELOUTPUT"); | ||||
|   t.is(error.name, "SemanticReleaseError"); | ||||
|   t.truthy(error.message); | ||||
|   t.truthy(error.details); | ||||
|   t.regex(error.details, /2/); | ||||
| @ -172,60 +182,60 @@ test('Wrap "addChannel" plugin in a function that validate the output of the plu | ||||
| 
 | ||||
| test('Plugin is called with "pluginConfig" (with object definition) and input', async (t) => { | ||||
|   const pluginFunction = stub().resolves(); | ||||
|   const pluginConf = {path: pluginFunction, conf: 'confValue'}; | ||||
|   const options = {global: 'globalValue'}; | ||||
|   const plugin = await normalize({cwd, options, logger: t.context.logger}, '', pluginConf, {}); | ||||
|   await plugin({options: {}, param: 'param'}); | ||||
|   const pluginConf = { path: pluginFunction, conf: "confValue" }; | ||||
|   const options = { global: "globalValue" }; | ||||
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {}); | ||||
|   await plugin({ options: {}, param: "param" }); | ||||
| 
 | ||||
|   t.true( | ||||
|     pluginFunction.calledWithMatch( | ||||
|       {conf: 'confValue', global: 'globalValue'}, | ||||
|       {param: 'param', logger: t.context.logger} | ||||
|       { conf: "confValue", global: "globalValue" }, | ||||
|       { param: "param", logger: t.context.logger } | ||||
|     ) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Plugin is called with "pluginConfig" (with array definition) and input', async (t) => { | ||||
|   const pluginFunction = stub().resolves(); | ||||
|   const pluginConf = [pluginFunction, {conf: 'confValue'}]; | ||||
|   const options = {global: 'globalValue'}; | ||||
|   const plugin = await normalize({cwd, options, logger: t.context.logger}, '', pluginConf, {}); | ||||
|   await plugin({options: {}, param: 'param'}); | ||||
|   const pluginConf = [pluginFunction, { conf: "confValue" }]; | ||||
|   const options = { global: "globalValue" }; | ||||
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {}); | ||||
|   await plugin({ options: {}, param: "param" }); | ||||
| 
 | ||||
|   t.true( | ||||
|     pluginFunction.calledWithMatch( | ||||
|       {conf: 'confValue', global: 'globalValue'}, | ||||
|       {param: 'param', logger: t.context.logger} | ||||
|       { conf: "confValue", global: "globalValue" }, | ||||
|       { param: "param", logger: t.context.logger } | ||||
|     ) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('Prevent plugins to modify "pluginConfig"', async (t) => { | ||||
|   const pluginFunction = stub().callsFake((pluginConfig) => { | ||||
|     pluginConfig.conf.subConf = 'otherConf'; | ||||
|     pluginConfig.conf.subConf = "otherConf"; | ||||
|   }); | ||||
|   const pluginConf = {path: pluginFunction, conf: {subConf: 'originalConf'}}; | ||||
|   const options = {globalConf: {globalSubConf: 'originalGlobalConf'}}; | ||||
|   const plugin = await normalize({cwd, options, logger: t.context.logger}, '', pluginConf, {}); | ||||
|   await plugin({options: {}}); | ||||
|   const pluginConf = { path: pluginFunction, conf: { subConf: "originalConf" } }; | ||||
|   const options = { globalConf: { globalSubConf: "originalGlobalConf" } }; | ||||
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {}); | ||||
|   await plugin({ options: {} }); | ||||
| 
 | ||||
|   t.is(pluginConf.conf.subConf, 'originalConf'); | ||||
|   t.is(options.globalConf.globalSubConf, 'originalGlobalConf'); | ||||
|   t.is(pluginConf.conf.subConf, "originalConf"); | ||||
|   t.is(options.globalConf.globalSubConf, "originalGlobalConf"); | ||||
| }); | ||||
| 
 | ||||
| test('Prevent plugins to modify its input', async (t) => { | ||||
| test("Prevent plugins to modify its input", async (t) => { | ||||
|   const pluginFunction = stub().callsFake((pluginConfig, options) => { | ||||
|     options.param.subParam = 'otherParam'; | ||||
|     options.param.subParam = "otherParam"; | ||||
|   }); | ||||
|   const input = {param: {subParam: 'originalSubParam'}, options: {}}; | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, '', pluginFunction, {}); | ||||
|   const input = { param: { subParam: "originalSubParam" }, options: {} }; | ||||
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "", pluginFunction, {}); | ||||
|   await plugin(input); | ||||
| 
 | ||||
|   t.is(input.param.subParam, 'originalSubParam'); | ||||
|   t.is(input.param.subParam, "originalSubParam"); | ||||
| }); | ||||
| 
 | ||||
| test('Return noop if the plugin is not defined', async (t) => { | ||||
|   const plugin = await normalize({cwd, options: {}, logger: t.context.logger}); | ||||
| test("Return noop if the plugin is not defined", async (t) => { | ||||
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }); | ||||
| 
 | ||||
|   t.is(plugin, noop); | ||||
| }); | ||||
| @ -233,12 +243,12 @@ test('Return noop if the plugin is not defined', async (t) => { | ||||
| test('Always pass a defined "pluginConfig" for plugin defined with string', async (t) => { | ||||
|   // Call the normalize function with the path of a plugin that returns its config
 | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, logger: t.context.logger}, | ||||
|     '', | ||||
|     './test/fixtures/plugin-result-config', | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "", | ||||
|     "./test/fixtures/plugin-result-config", | ||||
|     {} | ||||
|   ); | ||||
|   const pluginResult = await plugin({options: {}}); | ||||
|   const pluginResult = await plugin({ options: {} }); | ||||
| 
 | ||||
|   t.deepEqual(pluginResult.pluginConfig, {}); | ||||
| }); | ||||
| @ -246,33 +256,38 @@ test('Always pass a defined "pluginConfig" for plugin defined with string', asyn | ||||
| test('Always pass a defined "pluginConfig" for plugin defined with path', async (t) => { | ||||
|   // Call the normalize function with the path of a plugin that returns its config
 | ||||
|   const plugin = await normalize( | ||||
|     {cwd, options: {}, logger: t.context.logger}, | ||||
|     '', | ||||
|     {path: './test/fixtures/plugin-result-config'}, | ||||
|     { cwd, options: {}, logger: t.context.logger }, | ||||
|     "", | ||||
|     { path: "./test/fixtures/plugin-result-config" }, | ||||
|     {} | ||||
|   ); | ||||
|   const pluginResult = await plugin({options: {}}); | ||||
|   const pluginResult = await plugin({ options: {} }); | ||||
| 
 | ||||
|   t.deepEqual(pluginResult.pluginConfig, {}); | ||||
| }); | ||||
| 
 | ||||
| test('Throws an error if the plugin return an object without the expected plugin function', async (t) => { | ||||
| test("Throws an error if the plugin return an object without the expected plugin function", async (t) => { | ||||
|   const error = await t.throwsAsync(() => | ||||
|     normalize({cwd, options: {}, logger: t.context.logger}, 'nonExistentPlugin', './test/fixtures/multi-plugin.cjs', {}) | ||||
|     normalize( | ||||
|       { cwd, options: {}, logger: t.context.logger }, | ||||
|       "nonExistentPlugin", | ||||
|       "./test/fixtures/multi-plugin.cjs", | ||||
|       {} | ||||
|     ) | ||||
|   ); | ||||
| 
 | ||||
|   t.is(error.code, 'EPLUGIN'); | ||||
|   t.is(error.name, 'SemanticReleaseError'); | ||||
|   t.is(error.code, "EPLUGIN"); | ||||
|   t.is(error.name, "SemanticReleaseError"); | ||||
|   t.truthy(error.message); | ||||
|   t.truthy(error.details); | ||||
| }); | ||||
| 
 | ||||
| test('Throws an error if the plugin is not found', async (t) => { | ||||
| test("Throws an error if the plugin is not found", async (t) => { | ||||
|   await t.throwsAsync( | ||||
|     () => normalize({cwd, options: {}, logger: t.context.logger}, 'nonExistentPlugin', 'non-existing-path', {}), | ||||
|     () => normalize({ cwd, options: {}, logger: t.context.logger }, "nonExistentPlugin", "non-existing-path", {}), | ||||
|     { | ||||
|       message: /Cannot find module 'non-existing-path'/, | ||||
|       code: 'MODULE_NOT_FOUND', | ||||
|       code: "MODULE_NOT_FOUND", | ||||
|       instanceOf: Error, | ||||
|     } | ||||
|   ); | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import test from 'ava'; | ||||
| import {stub} from 'sinon'; | ||||
| import AggregateError from 'aggregate-error'; | ||||
| import pipeline from '../../lib/plugins/pipeline.js'; | ||||
| import test from "ava"; | ||||
| import { stub } from "sinon"; | ||||
| import AggregateError from "aggregate-error"; | ||||
| import pipeline from "../../lib/plugins/pipeline.js"; | ||||
| 
 | ||||
| test('Execute each function in series passing the same input', async (t) => { | ||||
| test("Execute each function in series passing the same input", async (t) => { | ||||
|   const step1 = stub().resolves(1); | ||||
|   const step2 = stub().resolves(2); | ||||
|   const step3 = stub().resolves(3); | ||||
| @ -25,7 +25,7 @@ test('Execute each function in series passing a transformed input from "getNextI | ||||
|   const step4 = stub().resolves(4); | ||||
|   const getNextInput = (lastResult, result) => lastResult + result; | ||||
| 
 | ||||
|   const result = await pipeline([step1, step2, step3, step4], {settleAll: false, getNextInput})(0); | ||||
|   const result = await pipeline([step1, step2, step3, step4], { settleAll: false, getNextInput })(0); | ||||
| 
 | ||||
|   t.deepEqual(result, [1, 2, 3, 4]); | ||||
|   t.true(step1.calledWith(0)); | ||||
| @ -44,7 +44,7 @@ test('Execute each function in series passing the "lastResult" and "result" to " | ||||
|   const step4 = stub().resolves(4); | ||||
|   const getNextInput = stub().returnsArg(0); | ||||
| 
 | ||||
|   const result = await pipeline([step1, step2, step3, step4], {settleAll: false, getNextInput})(5); | ||||
|   const result = await pipeline([step1, step2, step3, step4], { settleAll: false, getNextInput })(5); | ||||
| 
 | ||||
|   t.deepEqual(result, [1, 2, 3, 4]); | ||||
|   t.deepEqual(getNextInput.args, [ | ||||
| @ -63,7 +63,7 @@ test('Execute each function in series calling "transform" to modify the results' | ||||
|   const getNextInput = stub().returnsArg(0); | ||||
|   const transform = stub().callsFake((result) => result + 1); | ||||
| 
 | ||||
|   const result = await pipeline([step1, step2, step3, step4], {getNextInput, transform})(5); | ||||
|   const result = await pipeline([step1, step2, step3, step4], { getNextInput, transform })(5); | ||||
| 
 | ||||
|   t.deepEqual(result, [1 + 1, 2 + 1, 3 + 1, 4 + 1]); | ||||
|   t.deepEqual(getNextInput.args, [ | ||||
| @ -82,7 +82,7 @@ test('Execute each function in series calling "transform" to modify the results | ||||
|   const getNextInput = stub().returnsArg(0); | ||||
|   const transform = stub().callsFake((result) => result + 1); | ||||
| 
 | ||||
|   const result = await pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput, transform})(5); | ||||
|   const result = await pipeline([step1, step2, step3, step4], { settleAll: true, getNextInput, transform })(5); | ||||
| 
 | ||||
|   t.deepEqual(result, [1 + 1, 2 + 1, 3 + 1, 4 + 1]); | ||||
|   t.deepEqual(getNextInput.args, [ | ||||
| @ -93,24 +93,24 @@ test('Execute each function in series calling "transform" to modify the results | ||||
|   ]); | ||||
| }); | ||||
| 
 | ||||
| test('Stop execution and throw error if a step rejects', async (t) => { | ||||
| test("Stop execution and throw error if a step rejects", async (t) => { | ||||
|   const step1 = stub().resolves(1); | ||||
|   const step2 = stub().rejects(new Error('test error')); | ||||
|   const step2 = stub().rejects(new Error("test error")); | ||||
|   const step3 = stub().resolves(3); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2, step3])(0), { | ||||
|     instanceOf: Error, | ||||
|     message: 'test error', | ||||
|     message: "test error", | ||||
|   }); | ||||
|   t.is(error.message, 'test error'); | ||||
|   t.is(error.message, "test error"); | ||||
|   t.true(step1.calledWith(0)); | ||||
|   t.true(step2.calledWith(0)); | ||||
|   t.true(step3.notCalled); | ||||
| }); | ||||
| 
 | ||||
| test('Throw all errors from the first step throwing an AggregateError', async (t) => { | ||||
|   const error1 = new Error('test error 1'); | ||||
|   const error2 = new Error('test error 2'); | ||||
| test("Throw all errors from the first step throwing an AggregateError", async (t) => { | ||||
|   const error1 = new Error("test error 1"); | ||||
|   const error2 = new Error("test error 2"); | ||||
| 
 | ||||
|   const step1 = stub().resolves(1); | ||||
|   const step2 = stub().rejects(new AggregateError([error1, error2])); | ||||
| @ -124,14 +124,14 @@ test('Throw all errors from the first step throwing an AggregateError', async (t | ||||
|   t.true(step3.notCalled); | ||||
| }); | ||||
| 
 | ||||
| test('Execute all even if a Promise rejects', async (t) => { | ||||
|   const error1 = new Error('test error 1'); | ||||
|   const error2 = new Error('test error 2'); | ||||
| test("Execute all even if a Promise rejects", async (t) => { | ||||
|   const error1 = new Error("test error 1"); | ||||
|   const error2 = new Error("test error 2"); | ||||
|   const step1 = stub().resolves(1); | ||||
|   const step2 = stub().rejects(error1); | ||||
|   const step3 = stub().rejects(error2); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2, step3], {settleAll: true})(0)); | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2, step3], { settleAll: true })(0)); | ||||
| 
 | ||||
|   t.deepEqual([...error.errors], [error1, error2]); | ||||
|   t.true(step1.calledWith(0)); | ||||
| @ -139,31 +139,31 @@ test('Execute all even if a Promise rejects', async (t) => { | ||||
|   t.true(step3.calledWith(0)); | ||||
| }); | ||||
| 
 | ||||
| test('Throw all errors from all steps throwing an AggregateError', async (t) => { | ||||
|   const error1 = new Error('test error 1'); | ||||
|   const error2 = new Error('test error 2'); | ||||
|   const error3 = new Error('test error 3'); | ||||
|   const error4 = new Error('test error 4'); | ||||
| test("Throw all errors from all steps throwing an AggregateError", async (t) => { | ||||
|   const error1 = new Error("test error 1"); | ||||
|   const error2 = new Error("test error 2"); | ||||
|   const error3 = new Error("test error 3"); | ||||
|   const error4 = new Error("test error 4"); | ||||
|   const step1 = stub().rejects(new AggregateError([error1, error2])); | ||||
|   const step2 = stub().rejects(new AggregateError([error3, error4])); | ||||
| 
 | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2], {settleAll: true})(0)); | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2], { settleAll: true })(0)); | ||||
| 
 | ||||
|   t.deepEqual([...error.errors], [error1, error2, error3, error4]); | ||||
|   t.true(step1.calledWith(0)); | ||||
|   t.true(step2.calledWith(0)); | ||||
| }); | ||||
| 
 | ||||
| test('Execute each function in series passing a transformed input even if a step rejects', async (t) => { | ||||
|   const error2 = new Error('test error 2'); | ||||
|   const error3 = new Error('test error 3'); | ||||
| test("Execute each function in series passing a transformed input even if a step rejects", async (t) => { | ||||
|   const error2 = new Error("test error 2"); | ||||
|   const error3 = new Error("test error 3"); | ||||
|   const step1 = stub().resolves(1); | ||||
|   const step2 = stub().rejects(error2); | ||||
|   const step3 = stub().rejects(error3); | ||||
|   const step4 = stub().resolves(4); | ||||
|   const getNextInput = (previousResult, result) => previousResult + result; | ||||
| 
 | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput})(0)); | ||||
|   const error = await t.throwsAsync(pipeline([step1, step2, step3, step4], { settleAll: true, getNextInput })(0)); | ||||
| 
 | ||||
|   t.deepEqual([...error.errors], [error2, error3]); | ||||
|   t.true(step1.calledWith(0)); | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import path from 'path'; | ||||
| import test from 'ava'; | ||||
| import {copy, outputFile} from 'fs-extra'; | ||||
| import {stub} from 'sinon'; | ||||
| import {temporaryDirectory} from 'tempy'; | ||||
| import getPlugins from '../../lib/plugins/index.js'; | ||||
| import path from "path"; | ||||
| import test from "ava"; | ||||
| import { copy, outputFile } from "fs-extra"; | ||||
| import { stub } from "sinon"; | ||||
| import { temporaryDirectory } from "tempy"; | ||||
| import getPlugins from "../../lib/plugins/index.js"; | ||||
| 
 | ||||
| // Save the current working directory
 | ||||
| const cwd = process.cwd(); | ||||
| @ -12,32 +12,32 @@ test.beforeEach((t) => { | ||||
|   // Stub the logger functions
 | ||||
|   t.context.log = stub(); | ||||
|   t.context.success = stub(); | ||||
|   t.context.logger = {log: t.context.log, success: t.context.success, scope: () => t.context.logger}; | ||||
|   t.context.logger = { log: t.context.log, success: t.context.success, scope: () => t.context.logger }; | ||||
| }); | ||||
| 
 | ||||
| test('Export default plugins', async (t) => { | ||||
|   const plugins = await getPlugins({cwd, options: {}, logger: t.context.logger}, {}); | ||||
| test("Export default plugins", async (t) => { | ||||
|   const plugins = await getPlugins({ cwd, options: {}, logger: t.context.logger }, {}); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Export plugins based on steps config', async (t) => { | ||||
| test("Export plugins based on steps config", async (t) => { | ||||
|   const plugins = await getPlugins( | ||||
|     { | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: { | ||||
|         verifyConditions: ['./test/fixtures/plugin-noop.cjs', {path: './test/fixtures/plugin-noop.cjs'}], | ||||
|         generateNotes: './test/fixtures/plugin-noop.cjs', | ||||
|         analyzeCommits: {path: './test/fixtures/plugin-noop.cjs'}, | ||||
|         verifyConditions: ["./test/fixtures/plugin-noop.cjs", { path: "./test/fixtures/plugin-noop.cjs" }], | ||||
|         generateNotes: "./test/fixtures/plugin-noop.cjs", | ||||
|         analyzeCommits: { path: "./test/fixtures/plugin-noop.cjs" }, | ||||
|         verifyRelease: () => {}, | ||||
|       }, | ||||
|     }, | ||||
| @ -45,161 +45,161 @@ test('Export plugins based on steps config', async (t) => { | ||||
|   ); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Export plugins based on "plugins" config (array)', async (t) => { | ||||
|   const plugin1 = {verifyConditions: stub(), publish: stub()}; | ||||
|   const plugin2 = {verifyConditions: stub(), verifyRelease: stub()}; | ||||
|   const plugin1 = { verifyConditions: stub(), publish: stub() }; | ||||
|   const plugin2 = { verifyConditions: stub(), verifyRelease: stub() }; | ||||
|   const plugins = await getPlugins( | ||||
|     {cwd, logger: t.context.logger, options: {plugins: [plugin1, [plugin2, {}]], verifyRelease: () => {}}}, | ||||
|     { cwd, logger: t.context.logger, options: { plugins: [plugin1, [plugin2, {}]], verifyRelease: () => {} } }, | ||||
|     {} | ||||
|   ); | ||||
|   await plugins.verifyConditions({options: {}}); | ||||
|   await plugins.verifyConditions({ options: {} }); | ||||
|   t.true(plugin1.verifyConditions.calledOnce); | ||||
|   t.true(plugin2.verifyConditions.calledOnce); | ||||
| 
 | ||||
|   await plugins.publish({options: {}}); | ||||
|   await plugins.publish({ options: {} }); | ||||
|   t.true(plugin1.publish.calledOnce); | ||||
| 
 | ||||
|   await plugins.verifyRelease({options: {}}); | ||||
|   await plugins.verifyRelease({ options: {} }); | ||||
|   t.true(plugin2.verifyRelease.notCalled); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Export plugins based on "plugins" config (single definition)', async (t) => { | ||||
|   const plugin1 = {verifyConditions: stub(), publish: stub()}; | ||||
|   const plugins = await getPlugins({cwd, logger: t.context.logger, options: {plugins: plugin1}}, {}); | ||||
|   const plugin1 = { verifyConditions: stub(), publish: stub() }; | ||||
|   const plugins = await getPlugins({ cwd, logger: t.context.logger, options: { plugins: plugin1 } }, {}); | ||||
| 
 | ||||
|   await plugins.verifyConditions({options: {}}); | ||||
|   await plugins.verifyConditions({ options: {} }); | ||||
|   t.true(plugin1.verifyConditions.calledOnce); | ||||
| 
 | ||||
|   await plugins.publish({options: {}}); | ||||
|   await plugins.publish({ options: {} }); | ||||
|   t.true(plugin1.publish.calledOnce); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Merge global options, "plugins" options and step options', async (t) => { | ||||
|   const plugin1 = [{verifyConditions: stub(), publish: stub()}, {pluginOpt1: 'plugin1'}]; | ||||
|   const plugin2 = [{verifyConditions: stub()}, {pluginOpt2: 'plugin2'}]; | ||||
|   const plugin3 = [stub(), {pluginOpt3: 'plugin3'}]; | ||||
|   const plugin1 = [{ verifyConditions: stub(), publish: stub() }, { pluginOpt1: "plugin1" }]; | ||||
|   const plugin2 = [{ verifyConditions: stub() }, { pluginOpt2: "plugin2" }]; | ||||
|   const plugin3 = [stub(), { pluginOpt3: "plugin3" }]; | ||||
|   const plugins = await getPlugins( | ||||
|     { | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: {globalOpt: 'global', plugins: [plugin1, plugin2], verifyRelease: [plugin3]}, | ||||
|       options: { globalOpt: "global", plugins: [plugin1, plugin2], verifyRelease: [plugin3] }, | ||||
|     }, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   await plugins.verifyConditions({options: {}}); | ||||
|   t.deepEqual(plugin1[0].verifyConditions.args[0][0], {globalOpt: 'global', pluginOpt1: 'plugin1'}); | ||||
|   t.deepEqual(plugin2[0].verifyConditions.args[0][0], {globalOpt: 'global', pluginOpt2: 'plugin2'}); | ||||
|   await plugins.verifyConditions({ options: {} }); | ||||
|   t.deepEqual(plugin1[0].verifyConditions.args[0][0], { globalOpt: "global", pluginOpt1: "plugin1" }); | ||||
|   t.deepEqual(plugin2[0].verifyConditions.args[0][0], { globalOpt: "global", pluginOpt2: "plugin2" }); | ||||
| 
 | ||||
|   await plugins.publish({options: {}}); | ||||
|   t.deepEqual(plugin1[0].publish.args[0][0], {globalOpt: 'global', pluginOpt1: 'plugin1'}); | ||||
|   await plugins.publish({ options: {} }); | ||||
|   t.deepEqual(plugin1[0].publish.args[0][0], { globalOpt: "global", pluginOpt1: "plugin1" }); | ||||
| 
 | ||||
|   await plugins.verifyRelease({options: {}}); | ||||
|   t.deepEqual(plugin3[0].args[0][0], {globalOpt: 'global', pluginOpt3: 'plugin3'}); | ||||
|   await plugins.verifyRelease({ options: {} }); | ||||
|   t.deepEqual(plugin3[0].args[0][0], { globalOpt: "global", pluginOpt3: "plugin3" }); | ||||
| }); | ||||
| 
 | ||||
| test('Unknown steps of plugins configured in "plugins" are ignored', async (t) => { | ||||
|   const plugin1 = {verifyConditions: () => {}, unknown: () => {}}; | ||||
|   const plugins = await getPlugins({cwd, logger: t.context.logger, options: {plugins: [plugin1]}}, {}); | ||||
|   const plugin1 = { verifyConditions: () => {}, unknown: () => {} }; | ||||
|   const plugins = await getPlugins({ cwd, logger: t.context.logger, options: { plugins: [plugin1] } }, {}); | ||||
| 
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(plugins.unknown, undefined); | ||||
| }); | ||||
| 
 | ||||
| test('Export plugins loaded from the dependency of a shareable config module', async (t) => { | ||||
| test("Export plugins loaded from the dependency of a shareable config module", async (t) => { | ||||
|   const cwd = temporaryDirectory(); | ||||
|   await copy( | ||||
|     './test/fixtures/plugin-noop.cjs', | ||||
|     path.resolve(cwd, 'node_modules/shareable-config/node_modules/custom-plugin/index.js') | ||||
|     "./test/fixtures/plugin-noop.cjs", | ||||
|     path.resolve(cwd, "node_modules/shareable-config/node_modules/custom-plugin/index.js") | ||||
|   ); | ||||
|   await outputFile(path.resolve(cwd, 'node_modules/shareable-config/index.js'), ''); | ||||
|   await outputFile(path.resolve(cwd, "node_modules/shareable-config/index.js"), ""); | ||||
| 
 | ||||
|   const plugins = await getPlugins( | ||||
|     { | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: { | ||||
|         verifyConditions: ['custom-plugin', {path: 'custom-plugin'}], | ||||
|         generateNotes: 'custom-plugin', | ||||
|         analyzeCommits: {path: 'custom-plugin'}, | ||||
|         verifyConditions: ["custom-plugin", { path: "custom-plugin" }], | ||||
|         generateNotes: "custom-plugin", | ||||
|         analyzeCommits: { path: "custom-plugin" }, | ||||
|         verifyRelease: () => {}, | ||||
|       }, | ||||
|     }, | ||||
|     {'custom-plugin': 'shareable-config'} | ||||
|     { "custom-plugin": "shareable-config" } | ||||
|   ); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Export plugins loaded from the dependency of a shareable config file', async (t) => { | ||||
| test("Export plugins loaded from the dependency of a shareable config file", async (t) => { | ||||
|   const cwd = temporaryDirectory(); | ||||
|   await copy('./test/fixtures/plugin-noop.cjs', path.resolve(cwd, 'plugin/plugin-noop.cjs')); | ||||
|   await outputFile(path.resolve(cwd, 'shareable-config.js'), ''); | ||||
|   await copy("./test/fixtures/plugin-noop.cjs", path.resolve(cwd, "plugin/plugin-noop.cjs")); | ||||
|   await outputFile(path.resolve(cwd, "shareable-config.js"), ""); | ||||
| 
 | ||||
|   const plugins = await getPlugins( | ||||
|     { | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: { | ||||
|         verifyConditions: ['./plugin/plugin-noop.cjs', {path: './plugin/plugin-noop.cjs'}], | ||||
|         generateNotes: './plugin/plugin-noop.cjs', | ||||
|         analyzeCommits: {path: './plugin/plugin-noop.cjs'}, | ||||
|         verifyConditions: ["./plugin/plugin-noop.cjs", { path: "./plugin/plugin-noop.cjs" }], | ||||
|         generateNotes: "./plugin/plugin-noop.cjs", | ||||
|         analyzeCommits: { path: "./plugin/plugin-noop.cjs" }, | ||||
|         verifyRelease: () => {}, | ||||
|       }, | ||||
|     }, | ||||
|     {'./plugin/plugin-noop': './shareable-config.js'} | ||||
|     { "./plugin/plugin-noop": "./shareable-config.js" } | ||||
|   ); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.verifyConditions, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.verifyRelease, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.prepare, 'function'); | ||||
|   t.is(typeof plugins.publish, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.verifyConditions, "function"); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.verifyRelease, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.prepare, "function"); | ||||
|   t.is(typeof plugins.publish, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| }); | ||||
| 
 | ||||
| test('Use default when only options are passed for a single plugin', async (t) => { | ||||
| test("Use default when only options are passed for a single plugin", async (t) => { | ||||
|   const analyzeCommits = {}; | ||||
|   const generateNotes = {}; | ||||
|   const publish = {}; | ||||
| @ -211,7 +211,7 @@ test('Use default when only options are passed for a single plugin', async (t) = | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: { | ||||
|         plugins: ['@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator'], | ||||
|         plugins: ["@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator"], | ||||
|         analyzeCommits, | ||||
|         generateNotes, | ||||
|         publish, | ||||
| @ -223,96 +223,102 @@ test('Use default when only options are passed for a single plugin', async (t) = | ||||
|   ); | ||||
| 
 | ||||
|   // Verify the module returns a function for each plugin
 | ||||
|   t.is(typeof plugins.analyzeCommits, 'function'); | ||||
|   t.is(typeof plugins.generateNotes, 'function'); | ||||
|   t.is(typeof plugins.success, 'function'); | ||||
|   t.is(typeof plugins.fail, 'function'); | ||||
|   t.is(typeof plugins.analyzeCommits, "function"); | ||||
|   t.is(typeof plugins.generateNotes, "function"); | ||||
|   t.is(typeof plugins.success, "function"); | ||||
|   t.is(typeof plugins.fail, "function"); | ||||
| 
 | ||||
|   // Verify only the plugins defined as an object with no `path` are set to the default value
 | ||||
|   t.falsy(success.path); | ||||
|   t.falsy(fail.path); | ||||
| }); | ||||
| 
 | ||||
| test('Merge global options with plugin options', async (t) => { | ||||
| test("Merge global options with plugin options", async (t) => { | ||||
|   const plugins = await getPlugins( | ||||
|     { | ||||
|       cwd, | ||||
|       logger: t.context.logger, | ||||
|       options: { | ||||
|         globalOpt: 'global', | ||||
|         otherOpt: 'globally-defined', | ||||
|         verifyRelease: {path: './test/fixtures/plugin-result-config', localOpt: 'local', otherOpt: 'locally-defined'}, | ||||
|         globalOpt: "global", | ||||
|         otherOpt: "globally-defined", | ||||
|         verifyRelease: { path: "./test/fixtures/plugin-result-config", localOpt: "local", otherOpt: "locally-defined" }, | ||||
|       }, | ||||
|     }, | ||||
|     {} | ||||
|   ); | ||||
| 
 | ||||
|   const [result] = await plugins.verifyRelease({options: {}}); | ||||
|   const [result] = await plugins.verifyRelease({ options: {} }); | ||||
| 
 | ||||
|   t.deepEqual(result.pluginConfig, {localOpt: 'local', globalOpt: 'global', otherOpt: 'locally-defined'}); | ||||
|   t.deepEqual(result.pluginConfig, { localOpt: "local", globalOpt: "global", otherOpt: "locally-defined" }); | ||||
| }); | ||||
| 
 | ||||
| test('Throw an error for each invalid plugin configuration', async (t) => { | ||||
| test("Throw an error for each invalid plugin configuration", async (t) => { | ||||
|   const errors = [ | ||||
|     ...(await t.throwsAsync(() => | ||||
|     ...( | ||||
|       await t.throwsAsync(() => | ||||
|         getPlugins( | ||||
|           { | ||||
|             cwd, | ||||
|             logger: t.context.logger, | ||||
|             options: { | ||||
|             plugins: ['@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator'], | ||||
|               plugins: ["@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator"], | ||||
|               verifyConditions: 1, | ||||
|               analyzeCommits: [], | ||||
|               verifyRelease: [{}], | ||||
|             generateNotes: [{path: null}], | ||||
|               generateNotes: [{ path: null }], | ||||
|             }, | ||||
|           }, | ||||
|           {} | ||||
|         ) | ||||
|     )).errors, | ||||
|       ) | ||||
|     ).errors, | ||||
|   ]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EPLUGINCONF'); | ||||
|   t.is(errors[1].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[1].code, 'EPLUGINCONF'); | ||||
|   t.is(errors[2].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[2].code, 'EPLUGINCONF'); | ||||
|   t.is(errors[3].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[3].code, 'EPLUGINCONF'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EPLUGINCONF"); | ||||
|   t.is(errors[1].name, "SemanticReleaseError"); | ||||
|   t.is(errors[1].code, "EPLUGINCONF"); | ||||
|   t.is(errors[2].name, "SemanticReleaseError"); | ||||
|   t.is(errors[2].code, "EPLUGINCONF"); | ||||
|   t.is(errors[3].name, "SemanticReleaseError"); | ||||
|   t.is(errors[3].code, "EPLUGINCONF"); | ||||
| }); | ||||
| 
 | ||||
| test('Throw EPLUGINSCONF error if the "plugins" option contains an old plugin definition (returns a function)', async (t) => { | ||||
|   const errors = [ | ||||
|     ...(await t.throwsAsync(() => | ||||
|     ...( | ||||
|       await t.throwsAsync(() => | ||||
|         getPlugins( | ||||
|           { | ||||
|             cwd, | ||||
|             logger: t.context.logger, | ||||
|           options: {plugins: ['./test/fixtures/multi-plugin.cjs', './test/fixtures/plugin-noop.cjs', () => {}]}, | ||||
|             options: { plugins: ["./test/fixtures/multi-plugin.cjs", "./test/fixtures/plugin-noop.cjs", () => {}] }, | ||||
|           }, | ||||
|           {} | ||||
|         ) | ||||
|     )).errors, | ||||
|       ) | ||||
|     ).errors, | ||||
|   ]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EPLUGINSCONF'); | ||||
|   t.is(errors[1].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[1].code, 'EPLUGINSCONF'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EPLUGINSCONF"); | ||||
|   t.is(errors[1].name, "SemanticReleaseError"); | ||||
|   t.is(errors[1].code, "EPLUGINSCONF"); | ||||
| }); | ||||
| 
 | ||||
| test('Throw EPLUGINSCONF error for each invalid definition if the "plugins" option', async (t) => { | ||||
|   const errors = [ | ||||
|     ...(await t.throwsAsync(() => | ||||
|       getPlugins({cwd, logger: t.context.logger, options: {plugins: [1, {path: 1}, [() => {}, {}, {}]]}}, {}) | ||||
|     )).errors, | ||||
|     ...( | ||||
|       await t.throwsAsync(() => | ||||
|         getPlugins({ cwd, logger: t.context.logger, options: { plugins: [1, { path: 1 }, [() => {}, {}, {}]] } }, {}) | ||||
|       ) | ||||
|     ).errors, | ||||
|   ]; | ||||
| 
 | ||||
|   t.is(errors[0].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[0].code, 'EPLUGINSCONF'); | ||||
|   t.is(errors[1].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[1].code, 'EPLUGINSCONF'); | ||||
|   t.is(errors[2].name, 'SemanticReleaseError'); | ||||
|   t.is(errors[2].code, 'EPLUGINSCONF'); | ||||
|   t.is(errors[0].name, "SemanticReleaseError"); | ||||
|   t.is(errors[0].code, "EPLUGINSCONF"); | ||||
|   t.is(errors[1].name, "SemanticReleaseError"); | ||||
|   t.is(errors[1].code, "EPLUGINSCONF"); | ||||
|   t.is(errors[2].name, "SemanticReleaseError"); | ||||
|   t.is(errors[2].code, "EPLUGINSCONF"); | ||||
| }); | ||||
|  | ||||
| @ -1,203 +1,202 @@ | ||||
| import test from 'ava'; | ||||
| import {loadPlugin, parseConfig, validatePlugin, validateStep} from '../../lib/plugins/utils.js'; | ||||
| import test from "ava"; | ||||
| import { loadPlugin, parseConfig, validatePlugin, validateStep } from "../../lib/plugins/utils.js"; | ||||
| 
 | ||||
| test('validatePlugin', (t) => { | ||||
|   const path = 'plugin-module'; | ||||
|   const options = {option1: 'value1', option2: 'value2'}; | ||||
| test("validatePlugin", (t) => { | ||||
|   const path = "plugin-module"; | ||||
|   const options = { option1: "value1", option2: "value2" }; | ||||
| 
 | ||||
|   t.true(validatePlugin(path), 'String definition'); | ||||
|   t.true(validatePlugin({publish: () => {}}), 'Object definition'); | ||||
|   t.true(validatePlugin([path]), 'Array definition'); | ||||
|   t.true(validatePlugin([path, options]), 'Array definition with options'); | ||||
|   t.true(validatePlugin([{publish: () => {}}, options]), 'Array definition with options and path as object'); | ||||
|   t.true(validatePlugin({path}), 'Object with path definition'); | ||||
|   t.true(validatePlugin({path, ...options}), 'Object with path definition with options'); | ||||
|   t.true(validatePlugin(path), "String definition"); | ||||
|   t.true(validatePlugin({ publish: () => {} }), "Object definition"); | ||||
|   t.true(validatePlugin([path]), "Array definition"); | ||||
|   t.true(validatePlugin([path, options]), "Array definition with options"); | ||||
|   t.true(validatePlugin([{ publish: () => {} }, options]), "Array definition with options and path as object"); | ||||
|   t.true(validatePlugin({ path }), "Object with path definition"); | ||||
|   t.true(validatePlugin({ path, ...options }), "Object with path definition with options"); | ||||
|   t.true( | ||||
|     validatePlugin({path: {publish: () => {}}, ...options}), | ||||
|     'Object with path  definition with options and path as object' | ||||
|     validatePlugin({ path: { publish: () => {} }, ...options }), | ||||
|     "Object with path  definition with options and path as object" | ||||
|   ); | ||||
| 
 | ||||
|   t.false(validatePlugin(1), 'String definition, wrong path'); | ||||
|   t.false(validatePlugin([]), 'Array definition, missing path'); | ||||
|   t.false(validatePlugin([path, options, {}]), 'Array definition, additional parameter'); | ||||
|   t.false(validatePlugin([1]), 'Array definition, wrong path'); | ||||
|   t.false(validatePlugin([path, 1]), 'Array definition, wrong options'); | ||||
|   t.false(validatePlugin({path: 1}), 'Object definition, wrong path'); | ||||
|   t.false(validatePlugin(1), "String definition, wrong path"); | ||||
|   t.false(validatePlugin([]), "Array definition, missing path"); | ||||
|   t.false(validatePlugin([path, options, {}]), "Array definition, additional parameter"); | ||||
|   t.false(validatePlugin([1]), "Array definition, wrong path"); | ||||
|   t.false(validatePlugin([path, 1]), "Array definition, wrong options"); | ||||
|   t.false(validatePlugin({ path: 1 }), "Object definition, wrong path"); | ||||
| }); | ||||
| 
 | ||||
| test('validateStep: optional plugin configuration', (t) => { | ||||
|   const type = {multiple: true, required: false}; | ||||
| test("validateStep: optional plugin configuration", (t) => { | ||||
|   const type = { multiple: true, required: false }; | ||||
| 
 | ||||
|   // Empty config
 | ||||
|   t.true(validateStep(type)); | ||||
|   t.true(validateStep(type, [])); | ||||
| 
 | ||||
|   // Single value definition
 | ||||
|   t.true(validateStep(type, 'plugin-path.js')); | ||||
|   t.true(validateStep(type, "plugin-path.js")); | ||||
|   t.true(validateStep(type, () => {})); | ||||
|   t.true(validateStep(type, ['plugin-path.js'])); | ||||
|   t.true(validateStep(type, ["plugin-path.js"])); | ||||
|   t.true(validateStep(type, [() => {}])); | ||||
|   t.false(validateStep(type, {})); | ||||
|   t.false(validateStep(type, [{}])); | ||||
| 
 | ||||
|   // Array type definition
 | ||||
|   t.true(validateStep(type, [['plugin-path.js']])); | ||||
|   t.true(validateStep(type, [['plugin-path.js', {options: 'value'}]])); | ||||
|   t.true(validateStep(type, [[() => {}, {options: 'value'}]])); | ||||
|   t.false(validateStep(type, [['plugin-path.js', 1]])); | ||||
|   t.true(validateStep(type, [["plugin-path.js"]])); | ||||
|   t.true(validateStep(type, [["plugin-path.js", { options: "value" }]])); | ||||
|   t.true(validateStep(type, [[() => {}, { options: "value" }]])); | ||||
|   t.false(validateStep(type, [["plugin-path.js", 1]])); | ||||
| 
 | ||||
|   // Object type definition
 | ||||
|   t.true(validateStep(type, {path: 'plugin-path.js'})); | ||||
|   t.true(validateStep(type, {path: 'plugin-path.js', options: 'value'})); | ||||
|   t.true(validateStep(type, {path: () => {}, options: 'value'})); | ||||
|   t.false(validateStep(type, {path: null})); | ||||
|   t.true(validateStep(type, { path: "plugin-path.js" })); | ||||
|   t.true(validateStep(type, { path: "plugin-path.js", options: "value" })); | ||||
|   t.true(validateStep(type, { path: () => {}, options: "value" })); | ||||
|   t.false(validateStep(type, { path: null })); | ||||
| 
 | ||||
|   // Considered as an Array of 2 definitions and not as one Array definition in case of a muliple plugin type
 | ||||
|   t.false(validateStep(type, [() => {}, {options: 'value'}])); | ||||
|   t.false(validateStep(type, ['plugin-path.js', {options: 'value'}])); | ||||
|   t.false(validateStep(type, [() => {}, { options: "value" }])); | ||||
|   t.false(validateStep(type, ["plugin-path.js", { options: "value" }])); | ||||
| 
 | ||||
|   // Multiple definitions
 | ||||
|   t.true( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', 1], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", 1], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: null}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: null }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('validateStep: required plugin configuration', (t) => { | ||||
|   const type = {required: true}; | ||||
| test("validateStep: required plugin configuration", (t) => { | ||||
|   const type = { required: true }; | ||||
| 
 | ||||
|   // Empty config
 | ||||
|   t.false(validateStep(type)); | ||||
|   t.false(validateStep(type, [])); | ||||
| 
 | ||||
|   // Single value definition
 | ||||
|   t.true(validateStep(type, 'plugin-path.js')); | ||||
|   t.true(validateStep(type, "plugin-path.js")); | ||||
|   t.true(validateStep(type, () => {})); | ||||
|   t.true(validateStep(type, ['plugin-path.js'])); | ||||
|   t.true(validateStep(type, ["plugin-path.js"])); | ||||
|   t.true(validateStep(type, [() => {}])); | ||||
|   t.false(validateStep(type, {})); | ||||
|   t.false(validateStep(type, [{}])); | ||||
| 
 | ||||
|   // Array type definition
 | ||||
|   t.true(validateStep(type, [['plugin-path.js']])); | ||||
|   t.true(validateStep(type, [['plugin-path.js', {options: 'value'}]])); | ||||
|   t.true(validateStep(type, [[() => {}, {options: 'value'}]])); | ||||
|   t.false(validateStep(type, [['plugin-path.js', 1]])); | ||||
|   t.true(validateStep(type, [["plugin-path.js"]])); | ||||
|   t.true(validateStep(type, [["plugin-path.js", { options: "value" }]])); | ||||
|   t.true(validateStep(type, [[() => {}, { options: "value" }]])); | ||||
|   t.false(validateStep(type, [["plugin-path.js", 1]])); | ||||
| 
 | ||||
|   // Object type definition
 | ||||
|   t.true(validateStep(type, {path: 'plugin-path.js'})); | ||||
|   t.true(validateStep(type, {path: 'plugin-path.js', options: 'value'})); | ||||
|   t.true(validateStep(type, {path: () => {}, options: 'value'})); | ||||
|   t.false(validateStep(type, {path: null})); | ||||
|   t.true(validateStep(type, { path: "plugin-path.js" })); | ||||
|   t.true(validateStep(type, { path: "plugin-path.js", options: "value" })); | ||||
|   t.true(validateStep(type, { path: () => {}, options: "value" })); | ||||
|   t.false(validateStep(type, { path: null })); | ||||
| 
 | ||||
|   // Considered as an Array of 2 definitions and not as one Array definition in the case of a muliple plugin type
 | ||||
|   t.false(validateStep(type, [() => {}, {options: 'value'}])); | ||||
|   t.false(validateStep(type, ['plugin-path.js', {options: 'value'}])); | ||||
|   t.false(validateStep(type, [() => {}, { options: "value" }])); | ||||
|   t.false(validateStep(type, ["plugin-path.js", { options: "value" }])); | ||||
| 
 | ||||
|   // Multiple definitions
 | ||||
|   t.true( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', 1], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", 1], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: 'plugin-path.js'}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: "plugin-path.js" }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
|   t.false( | ||||
|     validateStep(type, [ | ||||
|       'plugin-path.js', | ||||
|       "plugin-path.js", | ||||
|       () => {}, | ||||
|       ['plugin-path.js'], | ||||
|       ['plugin-path.js', {options: 'value'}], | ||||
|       [() => {}, {options: 'value'}], | ||||
|       {path: null}, | ||||
|       {path: 'plugin-path.js', options: 'value'}, | ||||
|       {path: () => {}, options: 'value'}, | ||||
|       ["plugin-path.js"], | ||||
|       ["plugin-path.js", { options: "value" }], | ||||
|       [() => {}, { options: "value" }], | ||||
|       { path: null }, | ||||
|       { path: "plugin-path.js", options: "value" }, | ||||
|       { path: () => {}, options: "value" }, | ||||
|     ]) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test('loadPlugin', async (t) => { | ||||
| test("loadPlugin", async (t) => { | ||||
|   const cwd = process.cwd(); | ||||
|   const func = () => {}; | ||||
| 
 | ||||
|   t.is((await import('../fixtures/plugin-noop.cjs')).default, await loadPlugin({cwd: './test/fixtures'}, './plugin-noop.cjs', {}), 'From cwd'); | ||||
|   t.is( | ||||
|     (await import('../fixtures/plugin-noop.cjs')).default, | ||||
|     await loadPlugin({cwd}, './plugin-noop.cjs', {'./plugin-noop.cjs': './test/fixtures'}), | ||||
|     'From a shareable config context' | ||||
|     (await import("../fixtures/plugin-noop.cjs")).default, | ||||
|     await loadPlugin({ cwd: "./test/fixtures" }, "./plugin-noop.cjs", {}), | ||||
|     "From cwd" | ||||
|   ); | ||||
|   t.is( | ||||
|     (await import("../fixtures/plugin-noop.cjs")).default, | ||||
| @ -213,13 +212,13 @@ test('loadPlugin', async (t) => { | ||||
|   t.is(func, await loadPlugin({ cwd }, func, {}), "Defined as a function"); | ||||
| }); | ||||
| 
 | ||||
| test('parseConfig', (t) => { | ||||
|   const path = 'plugin-module'; | ||||
|   const options = {option1: 'value1', option2: 'value2'}; | ||||
| test("parseConfig", (t) => { | ||||
|   const path = "plugin-module"; | ||||
|   const options = { option1: "value1", option2: "value2" }; | ||||
| 
 | ||||
|   t.deepEqual(parseConfig(path), [path, {}], 'String definition'); | ||||
|   t.deepEqual(parseConfig({path}), [path, {}], 'Object definition'); | ||||
|   t.deepEqual(parseConfig({path, ...options}), [path, options], 'Object definition with options'); | ||||
|   t.deepEqual(parseConfig([path]), [path, {}], 'Array definition'); | ||||
|   t.deepEqual(parseConfig([path, options]), [path, options], 'Array definition with options'); | ||||
|   t.deepEqual(parseConfig(path), [path, {}], "String definition"); | ||||
|   t.deepEqual(parseConfig({ path }), [path, {}], "Object definition"); | ||||
|   t.deepEqual(parseConfig({ path, ...options }), [path, options], "Object definition with options"); | ||||
|   t.deepEqual(parseConfig([path]), [path, {}], "Array definition"); | ||||
|   t.deepEqual(parseConfig([path, options]), [path, options], "Array definition with options"); | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user