Compare commits
21
Commits
@@ -1 +0,0 @@
|
||||
base: app0ZOxG0FnHmOiuU
|
||||
@@ -91,3 +91,7 @@
|
||||
- [semantic-release-github-pages](https://github.com/qiwi/semantic-release-gh-pages-plugin)
|
||||
- `verifyConditions`: Verify the presence of the auth token set via environment variables.
|
||||
- `publish`: Pushes commit to the documentation branch.
|
||||
- [leiningen-semantic-release](https://github.com/NoxHarmonium/leiningen-semantic-release)
|
||||
- `verifyConditions`: Checks the project.clj is syntactically valid.
|
||||
- `prepare`: Update the project.clj version and package the output jar file.
|
||||
- `publish`: Publish the jar (and generated Maven metadata) to a maven repository (or clojars).
|
||||
|
||||
@@ -11,3 +11,11 @@
|
||||
- Publishes the same tarball to [npm](https://github.com/semantic-release/npm).
|
||||
- Commits the version change in `package.json`.
|
||||
- Creates or updates a [changelog](https://github.com/semantic-release/changelog) file.
|
||||
- [semantic-release-npm-github-publish](https://github.com/oleg-koval/semantic-release-npm-github-publish)
|
||||
- Based on [angular preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
|
||||
- Adds more keywords for the `chore` **PATCH** release.
|
||||
- Generates or updates a [changelog](https://github.com/semantic-release/changelog) file including all **PATCH** keywords (not included in default angular package).
|
||||
- Updates GitHub release with release-notes.
|
||||
- Bumps a version in package.json.
|
||||
- Publishes the new version to [NPM](https://npmjs.org).
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured with [Secret variables](https://docs.gitlab.com/ce/ci/variables/README.html#secret-variables).
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured with [Protected variables](https://docs.gitlab.com/ce/ci/variables/README.html#protected-environment-variables).
|
||||
|
||||
**Note**: Make sure to configure your release branch as [protected](https://docs.gitlab.com/ce/user/project/protected_branches.html) in order for the CI/CD build to access the protected variables.
|
||||
|
||||
## Node project configuration
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ For example the configuration `['master', {name: 'pre/rc', prerelease: '${name.r
|
||||
|
||||
### Release branches
|
||||
|
||||
A release branch is the base type of branch used by **semantic-release** that allows to publish releases with a [semantic version](https://semver.org), optionally on a specific distribution channel. Distribution channels (for example [npm dist-tags](https://docs.npmjs.com/cli/dist-tag) or [Chrome release channels](https://www.chromium.org/getting-involved/dev-channel)) are a way to distribute new releases only to a subset of users in order to get early feedback. Later on those releases can be added to the general distribution channel to be made available to all users.
|
||||
A release branch is the base type of branch used by **semantic-release** that allows to publish releases with a [semantic version](https://semver.org), optionally on a specific distribution channel. Distribution channels (for example [npm dist-tags](https://docs.npmjs.com/cli/dist-tag) or [Chrome release channels](https://www.chromium.org/getting-involved/dev-channel)) are a way to distribute new releases only to a subset of users in order to get early feedback. Later on, those releases can be added to the general distribution channel to be made available to all users.
|
||||
|
||||
**semantic-release** will automatically add releases to the corresponding distribution channel when code is [merged from a release branch to another](#merging-into-a-release-branch).
|
||||
|
||||
@@ -112,20 +112,17 @@ See [publishing on distribution channels recipe](../recipes/distribution-channel
|
||||
|
||||
#### Pushing to a release branch
|
||||
|
||||
With the configuration `"branches": ["master", "next"]`, if the last release published from `master` is `1.0.0` then:
|
||||
With the configuration `"branches": ["master", "next"]`, if the last release published from `master` is `1.0.0` and the last one from `next` is `2.0.0` then:
|
||||
- Only versions in range `1.x.x` can be published from `master`, so only `fix` and `feat` commits can be pushed to `master`
|
||||
- Only versions in range `>=2.0.0` release can be published from `next`, so a `BREAKING CHANGE` commit must be pushed first on `next` and can be followed by any type of commits
|
||||
- Once `next` get merged into `master` the release `2.0.0` will be made available on the channel associated with `master` and both `master` and `next` will accept any commit type
|
||||
|
||||
With the configuration `"branches": ["master", "next", "next-major"]`, if the last release published from `master` is `1.0.0` then:
|
||||
- Only versions in range `1.0.x` can be published from `master`, so only `fix` commits can be pushed to `master`
|
||||
- Only versions in range `>=1.1.0` can be published from `next`, so a `feat` commit must be pushed first on `next` and can be followed by `fix` and `feat` commits
|
||||
- Only versions in range `>=2.0.0` release can be published from `next`, so a `BREAKING CHANGE` commit must be pushed first on `next-major` and can be followed by any type of commits
|
||||
|
||||
Those verifications prevent situations such as:
|
||||
1. Create a `feat` commit on `next` which triggers the release of version `1.0.0` on channel `next`
|
||||
This verification prevent scenario such as:
|
||||
1. Create a `feat` commit on `next` which triggers the release of version `1.0.0` on the `next` channel
|
||||
2. Merge `next` into `master` which adds `1.0.0` on the default channel
|
||||
3. Push a `fix` commit to `master` which triggers the release of version `1.0.1` on the default channel
|
||||
4. Push a `fix` commit to `next` which would attempt to release the version `1.0.1` on channel `next` and fails as this version already exists
|
||||
3. Create a `feat` commit on `next` which triggers the release of version `1.1.0` on the `next` channel
|
||||
4. Create a `feat` commit on `master` which would attempt to release the version `1.1.0` on the default channel
|
||||
|
||||
In step 4 **semantic-release** will throw an `EINVALIDNEXTVERSION` error to prevent the attempt at releasing version `1.1.0` which was already released on step 3 with a different codebase. The error will indicate that the commit should be created on `next` instead. Alternatively if the `next` branch is merged into `master`, the version `1.1.0` will be made available on the default channel and the `feat` commit would be allowed on `master` to release `1.2.0`.
|
||||
|
||||
#### Merging into a release branch
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ const marked = require('marked');
|
||||
const TerminalRenderer = require('marked-terminal');
|
||||
const envCi = require('env-ci');
|
||||
const hookStd = require('hook-std');
|
||||
const pEachSeries = require('p-each-series');
|
||||
const semver = require('semver');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const pkg = require('./package.json');
|
||||
@@ -13,18 +12,18 @@ const verify = require('./lib/verify');
|
||||
const getNextVersion = require('./lib/get-next-version');
|
||||
const getCommits = require('./lib/get-commits');
|
||||
const getLastRelease = require('./lib/get-last-release');
|
||||
const getReleasesToAdd = require('./lib/get-releases-to-add');
|
||||
const getReleaseToAdd = require('./lib/get-release-to-add');
|
||||
const {extractErrors, makeTag} = require('./lib/utils');
|
||||
const getGitAuthUrl = require('./lib/get-git-auth-url');
|
||||
const getBranches = require('./lib/branches');
|
||||
const getLogger = require('./lib/get-logger');
|
||||
const {verifyAuth, isBranchUpToDate, getGitHead, tag, push, getTagHead} = require('./lib/git');
|
||||
const {verifyAuth, isBranchUpToDate, getGitHead, tag, push, pushNotes, getTagHead, addNote} = require('./lib/git');
|
||||
const getError = require('./lib/get-error');
|
||||
const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants');
|
||||
|
||||
marked.setOptions({renderer: new TerminalRenderer()});
|
||||
|
||||
/* eslint complexity: ["warn", 25] */
|
||||
/* eslint complexity: off */
|
||||
async function run(context, plugins) {
|
||||
const {cwd, env, options, logger} = context;
|
||||
const {isCi, branch: ciBranch, isPr} = envCi({env, cwd});
|
||||
@@ -54,7 +53,7 @@ async function run(context, plugins) {
|
||||
await verify(context);
|
||||
|
||||
options.repositoryUrl = await getGitAuthUrl(context);
|
||||
context.branches = await getBranches(options.repositoryUrl, context);
|
||||
context.branches = await getBranches(options.repositoryUrl, ciBranch, context);
|
||||
context.branch = context.branches.find(({name}) => name === ciBranch);
|
||||
|
||||
if (!context.branch) {
|
||||
@@ -92,35 +91,42 @@ async function run(context, plugins) {
|
||||
|
||||
await plugins.verifyConditions(context);
|
||||
|
||||
const releasesToAdd = getReleasesToAdd(context);
|
||||
const errors = [];
|
||||
context.releases = [];
|
||||
const releaseToAdd = getReleaseToAdd(context);
|
||||
|
||||
if (releaseToAdd) {
|
||||
const {lastRelease, currentRelease, nextRelease} = releaseToAdd;
|
||||
|
||||
await pEachSeries(releasesToAdd, async ({lastRelease, currentRelease, nextRelease}) => {
|
||||
nextRelease.gitHead = await getTagHead(nextRelease.gitHead, {cwd, env});
|
||||
currentRelease.gitHead = await getTagHead(currentRelease.gitHead, {cwd, env});
|
||||
if (context.branch.mergeRange && !semver.satisfies(nextRelease.version, context.branch.mergeRange)) {
|
||||
errors.push(getError('EINVALIDMAINTENANCEMERGE', {...context, nextRelease}));
|
||||
return;
|
||||
} else {
|
||||
const commits = await getCommits({...context, lastRelease, nextRelease});
|
||||
nextRelease.notes = await plugins.generateNotes({...context, commits, lastRelease, nextRelease});
|
||||
|
||||
if (options.dryRun) {
|
||||
logger.warn(`Skip ${nextRelease.gitTag} tag creation in dry-run mode`);
|
||||
} else {
|
||||
await addNote({channels: [...currentRelease.channels, nextRelease.channel]}, nextRelease.gitHead, {cwd, env});
|
||||
await push(options.repositoryUrl, {cwd, env});
|
||||
await pushNotes(options.repositoryUrl, {cwd, env});
|
||||
logger.success(`Add channel ${nextRelease.channel} to tag ${nextRelease.gitTag}`);
|
||||
}
|
||||
|
||||
context.branch.tags.push({
|
||||
version: nextRelease.version,
|
||||
channel: nextRelease.channel,
|
||||
gitTag: nextRelease.gitTag,
|
||||
gitHead: nextRelease.gitHead,
|
||||
});
|
||||
|
||||
const releases = await plugins.addChannel({...context, commits, lastRelease, currentRelease, nextRelease});
|
||||
context.releases.push(...releases);
|
||||
await plugins.success({...context, lastRelease, commits, nextRelease, releases});
|
||||
}
|
||||
|
||||
const commits = await getCommits({...context, lastRelease, nextRelease});
|
||||
nextRelease.notes = await plugins.generateNotes({...context, commits, lastRelease, nextRelease});
|
||||
|
||||
logger.log('Create tag %s', nextRelease.gitTag);
|
||||
await tag(nextRelease.gitTag, nextRelease.gitHead, {cwd, env});
|
||||
await push(options.repositoryUrl, {cwd, env});
|
||||
context.branch.tags.push({
|
||||
version: nextRelease.version,
|
||||
channel: nextRelease.channel,
|
||||
gitTag: nextRelease.gitTag,
|
||||
gitHead: nextRelease.gitHead,
|
||||
});
|
||||
|
||||
const releases = await plugins.addChannel({...context, commits, lastRelease, currentRelease, nextRelease});
|
||||
context.releases.push(...releases);
|
||||
await plugins.success({...context, lastRelease, commits, nextRelease, releases});
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateError(errors);
|
||||
@@ -143,7 +149,7 @@ async function run(context, plugins) {
|
||||
|
||||
const nextRelease = {
|
||||
type: await plugins.analyzeCommits(context),
|
||||
channel: context.branch.channel,
|
||||
channel: context.branch.channel || null,
|
||||
gitHead: await getGitHead({cwd, env}),
|
||||
};
|
||||
if (!nextRelease.type) {
|
||||
@@ -153,8 +159,8 @@ async function run(context, plugins) {
|
||||
|
||||
context.nextRelease = nextRelease;
|
||||
nextRelease.version = getNextVersion(context);
|
||||
nextRelease.gitTag = makeTag(options.tagFormat, nextRelease.version, nextRelease.channel);
|
||||
nextRelease.name = makeTag(options.tagFormat, nextRelease.version);
|
||||
nextRelease.gitTag = makeTag(options.tagFormat, nextRelease.version);
|
||||
nextRelease.name = nextRelease.gitTag;
|
||||
|
||||
if (context.branch.type !== 'prerelease' && !semver.satisfies(nextRelease.version, context.branch.range)) {
|
||||
throw getError('EINVALIDNEXTVERSION', {
|
||||
@@ -176,7 +182,9 @@ async function run(context, plugins) {
|
||||
} else {
|
||||
// Create the tag before calling the publish plugins as some require the tag to exists
|
||||
await tag(nextRelease.gitTag, nextRelease.gitHead, {cwd, env});
|
||||
await addNote({channels: [nextRelease.channel]}, nextRelease.gitHead, {cwd, env});
|
||||
await push(options.repositoryUrl, {cwd, env});
|
||||
await pushNotes(options.repositoryUrl, {cwd, env});
|
||||
logger.success(`Created tag ${nextRelease.gitTag}`);
|
||||
}
|
||||
|
||||
@@ -185,7 +193,9 @@ async function run(context, plugins) {
|
||||
|
||||
await plugins.success({...context, releases});
|
||||
|
||||
logger.success(`Published release ${nextRelease.version}`);
|
||||
logger.success(
|
||||
`Published release ${nextRelease.version} on ${nextRelease.channel ? nextRelease.channel : 'default'} channel`
|
||||
);
|
||||
|
||||
if (options.dryRun) {
|
||||
logger.log(`Release note for version ${nextRelease.version}:`);
|
||||
|
||||
@@ -2,22 +2,28 @@ const {template, escapeRegExp} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const pReduce = require('p-reduce');
|
||||
const debug = require('debug')('semantic-release:get-tags');
|
||||
const {getTags} = require('../../lib/git');
|
||||
const {getTags, getNote} = require('../../lib/git');
|
||||
|
||||
module.exports = 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 getTags(branch.name, {cwd, env})).reduce((tags, tag) => {
|
||||
const [, version, channel] = tag.match(tagRegexp) || [];
|
||||
return version && semver.valid(semver.clean(version)) ? [...tags, {gitTag: tag, version, channel}] : tags;
|
||||
}, []);
|
||||
const branchTags = await pReduce(
|
||||
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;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
debug('found tags for branch %s: %o', branch.name, branchTags);
|
||||
return [...branches, {...branch, tags: branchTags}];
|
||||
|
||||
@@ -3,12 +3,12 @@ const AggregateError = require('aggregate-error');
|
||||
const pEachSeries = require('p-each-series');
|
||||
const DEFINITIONS = require('../definitions/branches');
|
||||
const getError = require('../get-error');
|
||||
const {fetch, verifyBranchName} = require('../git');
|
||||
const {fetch, fetchNotes, verifyBranchName} = require('../git');
|
||||
const expand = require('./expand');
|
||||
const getTags = require('./get-tags');
|
||||
const normalize = require('./normalize');
|
||||
|
||||
module.exports = async (repositoryUrl, context) => {
|
||||
module.exports = async (repositoryUrl, ciBranch, context) => {
|
||||
const {cwd, env} = context;
|
||||
|
||||
const remoteBranches = await expand(
|
||||
@@ -18,9 +18,11 @@ module.exports = async (repositoryUrl, context) => {
|
||||
);
|
||||
|
||||
await pEachSeries(remoteBranches, async ({name}) => {
|
||||
await fetch(repositoryUrl, name, {cwd, env});
|
||||
await fetch(repositoryUrl, name, ciBranch, {cwd, env});
|
||||
});
|
||||
|
||||
await fetchNotes(repositoryUrl, {cwd, env});
|
||||
|
||||
const branches = await getTags(context, remoteBranches);
|
||||
|
||||
const errors = [];
|
||||
|
||||
+14
-20
@@ -1,5 +1,4 @@
|
||||
const {sortBy, isNil} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const semverDiff = require('semver-diff');
|
||||
const {FIRST_RELEASE, RELEASE_TYPE} = require('../definitions/constants');
|
||||
const {
|
||||
@@ -61,36 +60,31 @@ function release({release}) {
|
||||
return release;
|
||||
}
|
||||
|
||||
const breakpoints = release.length > 2 ? ['minor', 'major'] : ['major'];
|
||||
|
||||
// The intial bound is the last release from the base branch of `FIRST_RELEASE` (1.0.0)
|
||||
let bound = getLatestVersion(tagsToVersions(release[0].tags)) || FIRST_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) => {
|
||||
const versions = tagsToVersions(tags);
|
||||
// The lower bound is the highest version between the current branch last release and the previous branch upper bound (`bound`)
|
||||
const min = highest(getLatestVersion(versions), bound);
|
||||
if (release.length - 1 === idx) {
|
||||
// If the current branch is the last one of the release branch, there is no upper bound
|
||||
bound = undefined;
|
||||
} else {
|
||||
// The default upper bound is the lower bound increment with the release type of the current branch position
|
||||
const upperBound = semver.inc(min, breakpoints[idx]);
|
||||
// Find the lowest version that is present on the current branch but none of the previous ones
|
||||
const nextFirstVersion = getFirstVersion(tagsToVersions(release[idx + 1].tags), release.slice(0, idx + 1));
|
||||
// The upper bound is the lowest version between `nextFirstVersion` and the default upper bound
|
||||
bound = lowest(nextFirstVersion, upperBound);
|
||||
}
|
||||
// The new lastVersion is the highest version between the current branch last release and the previous branch lastVersion
|
||||
lastVersion = highest(getLatestVersion(versions), lastVersion);
|
||||
// The upper bound is:
|
||||
// - None if the current branch is the last one of the release branches
|
||||
// - Otherwise, The upper bound is the lowest version that is present on the current branch but none of the previous ones
|
||||
const bound =
|
||||
release.length - 1 === idx
|
||||
? undefined
|
||||
: getFirstVersion(tagsToVersions(release[idx + 1].tags), release.slice(0, idx + 1));
|
||||
|
||||
const diff = bound ? semverDiff(min, bound) : null;
|
||||
const diff = bound ? semverDiff(lastVersion, bound) : null;
|
||||
return {
|
||||
...rest,
|
||||
channel: idx === 0 ? channel : isNil(channel) ? name : channel,
|
||||
tags,
|
||||
type: 'release',
|
||||
name,
|
||||
range: getRange(min, bound),
|
||||
range: getRange(lastVersion, bound),
|
||||
accept: bound ? RELEASE_TYPE.slice(0, RELEASE_TYPE.indexOf(diff)) : RELEASE_TYPE,
|
||||
main: idx === 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ const SECRET_REPLACEMENT = '[secure]';
|
||||
|
||||
const SECRET_MIN_SIZE = 5;
|
||||
|
||||
const GIT_NOTE_REF = 'semantic-release';
|
||||
|
||||
module.exports = {
|
||||
RELEASE_TYPE,
|
||||
FIRST_RELEASE,
|
||||
@@ -23,4 +25,5 @@ module.exports = {
|
||||
RELEASE_NOTES_SEPARATOR,
|
||||
SECRET_REPLACEMENT,
|
||||
SECRET_MIN_SIZE,
|
||||
GIT_NOTE_REF,
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
const {castArray, pickBy, isNil, isString, isPlainObject} = require('lodash');
|
||||
const readPkgUp = require('read-pkg-up');
|
||||
const cosmiconfig = require('cosmiconfig');
|
||||
const {cosmiconfig} = require('cosmiconfig');
|
||||
const resolveFrom = require('resolve-from');
|
||||
const debug = require('debug')('semantic-release:config');
|
||||
const {repoUrl} = require('./git');
|
||||
|
||||
@@ -27,16 +27,17 @@ const {makeTag, isSameChannel} = require('./utils');
|
||||
* @return {LastRelease} The last tagged release or empty object if none is found.
|
||||
*/
|
||||
module.exports = ({branch, options: {tagFormat}}, {before} = {}) => {
|
||||
const [{version, gitTag, channel} = {}] = branch.tags
|
||||
const [{version, gitTag, channels} = {}] = branch.tags
|
||||
.filter(
|
||||
tag =>
|
||||
(branch.type === 'prerelease' && isSameChannel(branch.channel, tag.channel)) || !semver.prerelease(tag.version)
|
||||
((branch.type === 'prerelease' && tag.channels.some(channel => isSameChannel(branch.channel, channel))) ||
|
||||
!semver.prerelease(tag.version)) &&
|
||||
(isUndefined(before) || semver.lt(tag.version, before))
|
||||
)
|
||||
.filter(tag => isUndefined(before) || semver.lt(tag.version, before))
|
||||
.sort((a, b) => semver.rcompare(a.version, b.version));
|
||||
|
||||
if (gitTag) {
|
||||
return {version, gitTag, channel, gitHead: gitTag, name: makeTag(tagFormat, version)};
|
||||
return {version, gitTag, channels, gitHead: gitTag, name: makeTag(tagFormat, version)};
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -8,7 +8,8 @@ module.exports = ({branch, nextRelease: {type, channel}, lastRelease, logger}) =
|
||||
const {major, minor, patch} = semver.parse(lastRelease.version);
|
||||
version =
|
||||
branch.type === 'prerelease'
|
||||
? semver.prerelease(lastRelease.version) && isSameChannel(lastRelease.channel, channel)
|
||||
? semver.prerelease(lastRelease.version) &&
|
||||
lastRelease.channels.some(lastReleaseChannel => isSameChannel(lastReleaseChannel, channel))
|
||||
? semver.inc(lastRelease.version, 'prerelease')
|
||||
: `${semver.inc(`${major}.${minor}.${patch}`, type)}-${branch.prerelease}.${FIRSTPRERELEASE}`
|
||||
: semver.inc(lastRelease.version, type);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
const {uniqBy, intersection} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const semverDiff = require('semver-diff');
|
||||
const getLastRelease = require('./get-last-release');
|
||||
const {makeTag, getLowerBound} = require('./utils');
|
||||
|
||||
/**
|
||||
* Find releases that have been merged from from a higher branch but not added on the channel of the current branch.
|
||||
*
|
||||
* @param {Object} context semantic-release context.
|
||||
*
|
||||
* @return {Array<Object>} Last release and next release to be added on the channel of the current branch.
|
||||
*/
|
||||
module.exports = context => {
|
||||
const {
|
||||
branch,
|
||||
branches,
|
||||
options: {tagFormat},
|
||||
} = context;
|
||||
|
||||
const higherChannels = branches
|
||||
// Consider only releases of higher branches
|
||||
.slice(branches.findIndex(({name}) => name === branch.name) + 1)
|
||||
// Exclude prerelease branches
|
||||
.filter(({type}) => type !== 'prerelease')
|
||||
.map(({channel}) => channel || null);
|
||||
|
||||
const versiontoAdd = uniqBy(
|
||||
branch.tags.filter(
|
||||
({channels, version}) =>
|
||||
!channels.includes(branch.channel || null) &&
|
||||
intersection(channels, higherChannels).length > 0 &&
|
||||
(branch.type !== 'maintenance' || semver.gte(version, getLowerBound(branch.mergeRange)))
|
||||
),
|
||||
'version'
|
||||
).sort((a, b) => semver.compare(b.version, a.version))[0];
|
||||
|
||||
if (versiontoAdd) {
|
||||
const {version, gitTag, channels} = versiontoAdd;
|
||||
const lastRelease = getLastRelease(context, {before: version});
|
||||
if (semver.gt(getLastRelease(context).version, version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = lastRelease.version ? semverDiff(lastRelease.version, version) : 'major';
|
||||
const name = makeTag(tagFormat, version);
|
||||
return {
|
||||
lastRelease,
|
||||
currentRelease: {type, version, channels, gitTag, name, gitHead: gitTag},
|
||||
nextRelease: {
|
||||
type,
|
||||
version,
|
||||
channel: branch.channel || null,
|
||||
gitTag: makeTag(tagFormat, version),
|
||||
name,
|
||||
gitHead: gitTag,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,71 +0,0 @@
|
||||
const {uniq} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const semverDiff = require('semver-diff');
|
||||
const getLastRelease = require('./get-last-release');
|
||||
const {makeTag, getLowerBound} = require('./utils');
|
||||
|
||||
/**
|
||||
* Find releases that have been merged from from a higher branch but not added on the channel of the current branch.
|
||||
*
|
||||
* @param {Object} context semantic-release context.
|
||||
*
|
||||
* @return {Array<Object>} Last release and next release to be added on the channel of the current branch.
|
||||
*/
|
||||
module.exports = context => {
|
||||
const {
|
||||
branch,
|
||||
branches,
|
||||
options: {tagFormat},
|
||||
} = context;
|
||||
|
||||
return (
|
||||
branches
|
||||
// Consider only releases of higher branches
|
||||
.slice(branches.findIndex(({name}) => name === branch.name) + 1)
|
||||
// Exclude prerelease branches
|
||||
.filter(({type}) => type !== 'prerelease')
|
||||
// Find higher branch releases merged to building branch but not released on associated channel
|
||||
.reduce(
|
||||
(releases, higherBranch) => [
|
||||
...releases,
|
||||
// For all unique release version of the higher branch merged on current branch, excluding lower than start range version for maintenance branches
|
||||
...uniq(
|
||||
branch.tags.filter(
|
||||
({channel, version}) =>
|
||||
channel === higherBranch.channel &&
|
||||
channel !== branch.channel &&
|
||||
(branch.type !== 'maintenance' || semver.gte(version, getLowerBound(branch.mergeRange)))
|
||||
)
|
||||
)
|
||||
// Find ones that are not released on the building branch channel
|
||||
.filter(tag =>
|
||||
branch.tags.every(
|
||||
({version, channel}) =>
|
||||
version !== tag.version || channel === higherBranch.channel || channel !== branch.channel
|
||||
)
|
||||
)
|
||||
// Sort in ascending order to add the most recent release last
|
||||
.sort((a, b) => semver.compare(a.version, b.version))
|
||||
// Construct the last and next release to add to the building branch channel
|
||||
.map(({version, gitTag}) => {
|
||||
const lastRelease = getLastRelease(context, {before: version});
|
||||
const type = lastRelease.version ? semverDiff(lastRelease.version, version) : 'major';
|
||||
const name = makeTag(tagFormat, version);
|
||||
return {
|
||||
lastRelease,
|
||||
currentRelease: {type, version, channel: higherBranch.channel, gitTag, name, gitHead: gitTag},
|
||||
nextRelease: {
|
||||
type,
|
||||
version,
|
||||
channel: branch.channel,
|
||||
gitTag: makeTag(tagFormat, version, branch.channel),
|
||||
name,
|
||||
gitHead: gitTag,
|
||||
},
|
||||
};
|
||||
}),
|
||||
],
|
||||
[]
|
||||
)
|
||||
);
|
||||
};
|
||||
+77
-3
@@ -2,6 +2,7 @@ const gitLogParser = require('git-log-parser');
|
||||
const getStream = require('get-stream');
|
||||
const execa = require('execa');
|
||||
const debug = require('debug')('semantic-release:git');
|
||||
const {GIT_NOTE_REF} = require('./definitions/constants');
|
||||
|
||||
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
|
||||
|
||||
@@ -118,7 +119,7 @@ async function isRefExists(ref, execaOpts) {
|
||||
* @param {String} branch The repository branch to fetch.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function fetch(repositoryUrl, branch, execaOpts) {
|
||||
async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
|
||||
const isLocalExists =
|
||||
(await execa('git', ['rev-parse', '--verify', branch], {...execaOpts, reject: false})).exitCode === 0;
|
||||
|
||||
@@ -129,7 +130,9 @@ async function fetch(repositoryUrl, branch, execaOpts) {
|
||||
'fetch',
|
||||
'--unshallow',
|
||||
'--tags',
|
||||
...(isLocalExists ? [repositoryUrl] : [repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
...(branch === ciBranch && isLocalExists
|
||||
? [repositoryUrl]
|
||||
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
],
|
||||
execaOpts
|
||||
);
|
||||
@@ -139,13 +142,36 @@ async function fetch(repositoryUrl, branch, execaOpts) {
|
||||
[
|
||||
'fetch',
|
||||
'--tags',
|
||||
...(isLocalExists ? [repositoryUrl] : [repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
...(branch === ciBranch && isLocalExists
|
||||
? [repositoryUrl]
|
||||
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
],
|
||||
execaOpts
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unshallow the git repository if necessary and fetch all the notes.
|
||||
*
|
||||
* @param {String} repositoryUrl The remote repository URL.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function fetchNotes(repositoryUrl, execaOpts) {
|
||||
try {
|
||||
await execa(
|
||||
'git',
|
||||
['fetch', '--unshallow', repositoryUrl, `+refs/notes/${GIT_NOTE_REF}:refs/notes/${GIT_NOTE_REF}`],
|
||||
execaOpts
|
||||
);
|
||||
} catch (_) {
|
||||
await execa('git', ['fetch', repositoryUrl, `+refs/notes/${GIT_NOTE_REF}:refs/notes/${GIT_NOTE_REF}`], {
|
||||
...execaOpts,
|
||||
reject: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HEAD sha.
|
||||
*
|
||||
@@ -230,6 +256,18 @@ async function push(repositoryUrl, execaOpts) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push notes to the remote repository.
|
||||
*
|
||||
* @param {String} repositoryUrl The remote repository URL.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function pushNotes(repositoryUrl, execaOpts) {
|
||||
await execa('git', ['push', repositoryUrl, `refs/notes/${GIT_NOTE_REF}`], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a tag name is a valid Git reference.
|
||||
*
|
||||
@@ -280,6 +318,38 @@ async function isBranchUpToDate(repositoryUrl, branch, execaOpts) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and parse the JSON note of a given reference.
|
||||
*
|
||||
* @param {String} ref The Git reference for which to retrieve the note.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @return {Object} the parsed JSON note if there is one, an empty object otherwise.
|
||||
*/
|
||||
async function getNote(ref, execaOpts) {
|
||||
try {
|
||||
return JSON.parse((await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOpts)).stdout);
|
||||
} catch (error) {
|
||||
if (error.exitCode === 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
debug(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and parse the JSON note of a given reference.
|
||||
*
|
||||
* @param {Object} note The object to save in the reference note.
|
||||
* @param {String} ref The Git reference to add the note to.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function addNote(note, ref, execaOpts) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-f', '-m', JSON.stringify(note), ref], execaOpts);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTagHead,
|
||||
getTags,
|
||||
@@ -288,13 +358,17 @@ module.exports = {
|
||||
isRefInHistory,
|
||||
isRefExists,
|
||||
fetch,
|
||||
fetchNotes,
|
||||
getGitHead,
|
||||
repoUrl,
|
||||
isGitRepo,
|
||||
verifyAuth,
|
||||
tag,
|
||||
push,
|
||||
pushNotes,
|
||||
verifyTagName,
|
||||
isBranchUpToDate,
|
||||
verifyBranchName,
|
||||
getNote,
|
||||
addNote,
|
||||
};
|
||||
|
||||
+2
-2
@@ -67,8 +67,8 @@ function getRange(min, max) {
|
||||
return `>=${min}${max ? ` <${max}` : ''}`;
|
||||
}
|
||||
|
||||
function makeTag(tagFormat, version, channel) {
|
||||
return template(tagFormat)({version: `${version}${channel ? `@${channel}` : ''}`});
|
||||
function makeTag(tagFormat, version) {
|
||||
return template(tagFormat)({version});
|
||||
}
|
||||
|
||||
function isSameChannel(channel, otherChannel) {
|
||||
|
||||
+5
-5
@@ -22,13 +22,13 @@
|
||||
"Pierre Vanduynslager (https://twitter.com/@pvdlg_)"
|
||||
],
|
||||
"dependencies": {
|
||||
"@semantic-release/commit-analyzer": "7.0.0-beta.4",
|
||||
"@semantic-release/commit-analyzer": "^7.0.0-beta",
|
||||
"@semantic-release/error": "^2.2.0",
|
||||
"@semantic-release/github": "5.6.0-beta.2",
|
||||
"@semantic-release/npm": "6.0.0-beta.3",
|
||||
"@semantic-release/github": "^5.6.0-beta",
|
||||
"@semantic-release/npm": "^6.0.0-beta",
|
||||
"@semantic-release/release-notes-generator": "^7.1.2",
|
||||
"aggregate-error": "^3.0.0",
|
||||
"cosmiconfig": "^5.0.1",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"debug": "^4.0.0",
|
||||
"env-ci": "^4.0.0",
|
||||
"execa": "^3.2.0",
|
||||
@@ -49,7 +49,7 @@
|
||||
"semver": "^6.0.0",
|
||||
"semver-diff": "^3.1.1",
|
||||
"signale": "^1.2.1",
|
||||
"yargs": "^14.0.0"
|
||||
"yargs": "^15.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^2.0.0",
|
||||
|
||||
@@ -24,51 +24,82 @@ test('Enforce ranges with branching release workflow', async t => {
|
||||
];
|
||||
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
|
||||
|
||||
let result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
let result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).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 <1.1.0', 'Can release only patch on master');
|
||||
t.is(getBranch(result, 'next').range, '>=1.1.0 <2.0.0', 'Can release only minor on next');
|
||||
t.is(getBranch(result, 'next-major').range, '>=2.0.0', 'Can release only major on next-major');
|
||||
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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).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 <1.1.0', 'Can release only patch 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', {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');
|
||||
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');
|
||||
|
||||
merge(branches, 'master', 'next');
|
||||
merge(branches, 'master', 'next-major');
|
||||
result = (await getBranches('repositoryUrl', {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.0 <2.0.0', 'Can release only minor on next');
|
||||
t.is(getBranch(result, 'next-major').range, '>=2.0.0', 'Can release only major on 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');
|
||||
|
||||
release(branches, 'next', '1.1.0');
|
||||
release(branches, 'next', '1.1.1');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'next').range, '>=1.1.1 <2.0.0', 'Can release only patch or minor, > than 1.1.0 on next');
|
||||
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');
|
||||
|
||||
release(branches, 'next-major', '2.0.0');
|
||||
release(branches, 'next-major', '2.0.1');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
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');
|
||||
|
||||
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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
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');
|
||||
|
||||
merge(branches, 'master', '1.0.x');
|
||||
merge(branches, 'master', '1.x');
|
||||
release(branches, 'master', '1.0.1');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
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, '1.0.x').range,
|
||||
@@ -80,7 +111,10 @@ test('Enforce ranges with branching release workflow', async t => {
|
||||
release(branches, 'master', '1.0.2');
|
||||
release(branches, 'master', '1.0.3');
|
||||
release(branches, 'master', '1.0.4');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
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, '1.0.x').range,
|
||||
@@ -90,9 +124,13 @@ test('Enforce ranges with branching release workflow', async t => {
|
||||
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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on master');
|
||||
t.is(getBranch(result, 'next').range, '>=1.2.0 <2.0.0', 'Can release only patch or minor, > than 1.2.0 on next');
|
||||
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, '1.0.x').range,
|
||||
@@ -102,34 +140,50 @@ test('Enforce ranges with branching release workflow', async t => {
|
||||
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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on 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, '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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on 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, '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', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'master').range, '>=2.0.1 <2.1.0', 'Can release only patch, > than 2.0.1 on master');
|
||||
t.is(getBranch(result, 'next').range, '>=2.1.0 <3.0.0', 'Can release only minor on next');
|
||||
t.is(getBranch(result, 'next-major').range, '>=3.0.0', 'Can release only major on next-major');
|
||||
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');
|
||||
|
||||
merge(branches, 'beta', 'master');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
t.is(getBranch(result, 'master').range, '>=2.0.1 <2.1.0', 'Can release only patch, > than 2.0.1 on master');
|
||||
t.is(getBranch(result, 'next').range, '>=2.1.0 <3.0.0', 'Can release only minor on next');
|
||||
t.is(getBranch(result, 'next-major').range, '>=3.0.0', 'Can release only major on next-major');
|
||||
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');
|
||||
|
||||
branches.push({name: '1.1.x', tags: []});
|
||||
merge(branches, '1.x', '1.1.x');
|
||||
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
|
||||
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');
|
||||
@@ -146,7 +200,7 @@ test('Throw SemanticReleaseError for invalid configurations', async t => {
|
||||
{name: 'preview', prerelease: 'alpha', tags: []},
|
||||
];
|
||||
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EMAINTENANCEBRANCH');
|
||||
@@ -177,7 +231,7 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async t => {
|
||||
];
|
||||
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
|
||||
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EDUPLICATEBRANCHES');
|
||||
@@ -192,7 +246,7 @@ test('Throw a SemanticReleaseError for each invalid branch name', async t => {
|
||||
];
|
||||
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
|
||||
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
|
||||
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EINVALIDBRANCHNAME');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import test from 'ava';
|
||||
import getTags from '../../lib/branches/get-tags';
|
||||
import {gitRepo, gitCommits, gitTagVersion, gitCheckout} from '../helpers/git-utils';
|
||||
import {gitRepo, gitCommits, gitTagVersion, gitCheckout, gitAddNote} from '../helpers/git-utils';
|
||||
|
||||
test('Get the valid tags', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
@@ -20,9 +20,9 @@ test('Get the valid tags', async t => {
|
||||
{
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channel: undefined},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channel: undefined},
|
||||
{gitTag: 'v3.0.0-beta.1', version: '3.0.0-beta.1', channel: undefined},
|
||||
{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]},
|
||||
],
|
||||
},
|
||||
]);
|
||||
@@ -30,20 +30,21 @@ test('Get the valid tags', async t => {
|
||||
|
||||
test('Get the valid tags from multiple branches', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@1.x', undefined, {cwd});
|
||||
commits.push(...(await gitCommits(['Second'], {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 gitTagVersion('v1.1.0@1.x', 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});
|
||||
commits.push(...(await gitCommits(['Third'], {cwd})));
|
||||
await gitCommits(['Third'], {cwd});
|
||||
await gitTagVersion('v2.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v2.0.0@next', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v2.0.0', {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
commits.push(...(await gitCommits(['Fourth'], {cwd})));
|
||||
await gitTagVersion('v3.0.0@next', undefined, {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'},
|
||||
@@ -55,47 +56,17 @@ test('Get the valid tags from multiple branches', async t => {
|
||||
{
|
||||
name: '1.x',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channel: undefined},
|
||||
{gitTag: 'v1.0.0@1.x', version: '1.0.0', channel: '1.x'},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channel: undefined},
|
||||
{gitTag: 'v1.1.0@1.x', version: '1.1.0', channel: '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', channel: undefined},
|
||||
{gitTag: 'v2.0.0@next', version: '2.0.0', channel: 'next'},
|
||||
],
|
||||
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@next', version: '3.0.0', channel: 'next'}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Match the tag name from the begining of the string and the channel from the last "@"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('prefix@v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('prefix@v1.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('prefix@v2.0.0', undefined, {cwd});
|
||||
await gitTagVersion('prefix@v2.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('other-prefix@v3.0.0', undefined, {cwd});
|
||||
|
||||
const result = await getTags({cwd, options: {tagFormat: `prefix@v\${version}`}}, [{name: 'master'}]);
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'prefix@v1.0.0', version: '1.0.0', channel: undefined},
|
||||
{gitTag: 'prefix@v1.0.0@next', version: '1.0.0', channel: 'next'},
|
||||
{gitTag: 'prefix@v2.0.0', version: '2.0.0', channel: undefined},
|
||||
{gitTag: 'prefix@v2.0.0@next', version: '2.0.0', channel: 'next'},
|
||||
],
|
||||
tags: [...result[1].tags, {gitTag: 'v3.0.0', version: '3.0.0', channels: ['next']}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -120,11 +91,13 @@ test('Return branches with and empty tags array if no valid tag is found in hist
|
||||
await gitCheckout('next', true, {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@next', 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 gitTagVersion('v2.0.0@next', 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 gitTagVersion('v3.0.0@next', 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'}]);
|
||||
@@ -141,19 +114,19 @@ test('Get the highest valid tag corresponding to the "tagFormat"', async t => {
|
||||
|
||||
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', channel: undefined}]},
|
||||
{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', channel: undefined}]},
|
||||
{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'}]), [
|
||||
{
|
||||
name: 'master',
|
||||
tags: [{gitTag: 'foo-v1.0.0-bar', version: '1.0.0', channel: undefined}],
|
||||
tags: [{gitTag: 'foo-v1.0.0-bar', version: '1.0.0', channels: [null]}],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -161,7 +134,7 @@ test('Get the highest valid tag corresponding to the "tagFormat"', async t => {
|
||||
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', channel: undefined}],
|
||||
tags: [{gitTag: '(.+)/1.0.0/(a-z)', version: '1.0.0', channels: [null]}],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -169,12 +142,12 @@ test('Get the highest valid tag corresponding to the "tagFormat"', async t => {
|
||||
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', channel: undefined}],
|
||||
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', channel: undefined}]},
|
||||
{name: 'master', tags: [{gitTag: '3.0.0-bar.2', version: '3.0.0', channels: [null]}]},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -155,16 +155,33 @@ test('Release branches - initial state', t => {
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.release({release})
|
||||
.map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})),
|
||||
[
|
||||
{type: 'release', name: 'master', range: '>=1.0.0 <1.1.0', accept: ['patch'], channel: undefined},
|
||||
{type: 'release', name: 'next', range: '>=1.1.0 <2.0.0', accept: ['patch', 'minor'], channel: 'next'},
|
||||
{
|
||||
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',
|
||||
main: false,
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'next-major',
|
||||
range: '>=2.0.0',
|
||||
range: '>=1.0.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
channel: 'next-major',
|
||||
main: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -178,16 +195,26 @@ test('Release branches - 3 release branches', t => {
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.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},
|
||||
{type: 'release', name: 'next', range: '>=1.2.0 <2.0.0', accept: ['patch', 'minor'], channel: 'next'},
|
||||
{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',
|
||||
main: false,
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'next-major',
|
||||
range: '>=2.1.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
channel: 'next-major',
|
||||
main: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -200,10 +227,26 @@ test('Release branches - 2 release branches', t => {
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.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'], channel: undefined},
|
||||
{type: 'release', name: 'next', range: '>=2.1.0', accept: ['patch', 'minor', 'major'], channel: 'next'},
|
||||
{
|
||||
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',
|
||||
main: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
@@ -225,16 +268,19 @@ test('Release branches - cap ranges to first release only present on following b
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.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},
|
||||
{type: 'release', name: 'next', range: '>=2.1.0 <2.2.0', accept: ['patch'], channel: 'next'},
|
||||
{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',
|
||||
main: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -247,38 +293,31 @@ test('Release branches - Handle missing previous tags in branch history', t => {
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.release({release})
|
||||
.map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})),
|
||||
[
|
||||
{type: 'release', name: 'master', range: '>=2.0.0 <3.0.0', accept: ['patch', 'minor'], channel: undefined},
|
||||
{type: 'release', name: 'next', range: '>=3.0.0', accept: ['patch', 'minor', 'major'], channel: 'next'},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - enforce release gaps after downstream merge', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '1.1.0', '2.0.0'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.1.0', '2.0.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}) => ({type, name, range, accept, channel})),
|
||||
[
|
||||
{type: 'release', name: 'master', range: '>=2.0.0 <2.1.0', accept: ['patch'], channel: undefined},
|
||||
{type: 'release', name: 'next', range: '>=2.1.0 <3.0.0', accept: ['patch', 'minor'], channel: 'next'},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'next-major',
|
||||
range: '>=3.0.0',
|
||||
name: 'master',
|
||||
range: '>=2.0.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
channel: 'next-major',
|
||||
channel: undefined,
|
||||
main: true,
|
||||
},
|
||||
{
|
||||
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 branche 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'])},
|
||||
@@ -286,16 +325,33 @@ test('Release branches - limit releases on 2nd and 3rd branche based on 1st bran
|
||||
];
|
||||
|
||||
t.deepEqual(
|
||||
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
|
||||
normalize
|
||||
.release({release})
|
||||
.map(({type, name, range, accept, channel, main}) => ({type, name, range, accept, channel, main})),
|
||||
[
|
||||
{type: 'release', name: 'master', range: '>=3.0.0 <3.1.0', accept: ['patch'], channel: undefined},
|
||||
{type: 'release', name: 'next', range: '>=3.1.0 <4.0.0', accept: ['patch', 'minor'], channel: 'next'},
|
||||
{
|
||||
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',
|
||||
main: false,
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'next-major',
|
||||
range: '>=4.0.0',
|
||||
range: '>=3.0.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
channel: 'next-major',
|
||||
main: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -8,14 +8,14 @@ test('Get the highest non-prerelease valid tag', t => {
|
||||
tags: [
|
||||
{version: '2.0.0', gitTag: 'v2.0.0', gitHead: 'v2.0.0'},
|
||||
{version: '1.0.0', gitTag: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
{version: '3.0.0-beta.1', gitTag: 'v3.0.0-beta.1@beta', gitHead: 'v3.0.0-beta.1@beta'},
|
||||
{version: '3.0.0-beta.1', gitTag: 'v3.0.0-beta.1', gitHead: 'v3.0.0-beta.1'},
|
||||
],
|
||||
type: 'release',
|
||||
},
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: 'v2.0.0', channel: undefined});
|
||||
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: 'v2.0.0', channels: undefined});
|
||||
});
|
||||
|
||||
test('Get the highest prerelease valid tag, ignoring other tags from other prerelease channels', t => {
|
||||
@@ -25,9 +25,9 @@ test('Get the highest prerelease valid tag, ignoring other tags from other prere
|
||||
prerelease: 'beta',
|
||||
channel: 'beta',
|
||||
tags: [
|
||||
{version: '1.0.0-beta.1', gitTag: 'v1.0.0-beta.1@beta', gitHead: 'v1.0.0-beta.1@beta', channel: 'beta'},
|
||||
{version: '1.0.0-beta.2', gitTag: 'v1.0.0-beta.2@beta', gitHead: 'v1.0.0-beta.2@beta', channel: 'beta'},
|
||||
{version: '1.0.0-alpha.1', gitTag: 'v1.0.0-alpha.1@alpha', gitHead: 'v1.0.0-alpha.1@alpha', channel: 'alpha'},
|
||||
{version: '1.0.0-beta.1', gitTag: 'v1.0.0-beta.1', gitHead: 'v1.0.0-beta.1', channels: ['beta']},
|
||||
{version: '1.0.0-beta.2', gitTag: 'v1.0.0-beta.2', gitHead: 'v1.0.0-beta.2', channels: ['beta']},
|
||||
{version: '1.0.0-alpha.1', gitTag: 'v1.0.0-alpha.1', gitHead: 'v1.0.0-alpha.1', channels: ['alpha']},
|
||||
],
|
||||
type: 'prerelease',
|
||||
},
|
||||
@@ -36,10 +36,10 @@ test('Get the highest prerelease valid tag, ignoring other tags from other prere
|
||||
|
||||
t.deepEqual(result, {
|
||||
version: '1.0.0-beta.2',
|
||||
gitTag: 'v1.0.0-beta.2@beta',
|
||||
gitTag: 'v1.0.0-beta.2',
|
||||
name: 'v1.0.0-beta.2',
|
||||
gitHead: 'v1.0.0-beta.2@beta',
|
||||
channel: 'beta',
|
||||
gitHead: 'v1.0.0-beta.2',
|
||||
channels: ['beta'],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ test('Return empty object if no valid tag is found', t => {
|
||||
const result = getLastRelease({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [{version: '3.0.0-beta.1', gitTag: 'v3.0.0-beta.1@beta', gitHead: 'v3.0.0-beta.1@beta'}],
|
||||
tags: [{version: '3.0.0-beta.1', gitTag: 'v3.0.0-beta.1', gitHead: 'v3.0.0-beta.1'}],
|
||||
type: 'release',
|
||||
},
|
||||
options: {tagFormat: `v\${version}`},
|
||||
@@ -65,7 +65,7 @@ test('Get the highest non-prerelease valid tag before a certain version', t => {
|
||||
tags: [
|
||||
{version: '2.0.0', gitTag: 'v2.0.0', gitHead: 'v2.0.0'},
|
||||
{version: '1.0.0', gitTag: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
{version: '2.0.0-beta.1', gitTag: 'v2.0.0-beta.1@beta', gitHead: 'v2.0.0-beta.1@beta'},
|
||||
{version: '2.0.0-beta.1', gitTag: 'v2.0.0-beta.1', gitHead: 'v2.0.0-beta.1'},
|
||||
{version: '2.1.0', gitTag: 'v2.1.0', gitHead: 'v2.1.0'},
|
||||
{version: '2.1.1', gitTag: 'v2.1.1', gitHead: 'v2.1.1'},
|
||||
],
|
||||
@@ -76,5 +76,5 @@ test('Get the highest non-prerelease valid tag before a certain version', t => {
|
||||
{before: '2.1.0'}
|
||||
);
|
||||
|
||||
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: 'v2.0.0', channel: undefined});
|
||||
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: 'v2.0.0', channels: undefined});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ test('Increase version for patch release', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release'},
|
||||
nextRelease: {type: 'patch'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.1'
|
||||
@@ -25,7 +25,7 @@ test('Increase version for minor release', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release'},
|
||||
nextRelease: {type: 'minor'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.1.0'
|
||||
@@ -37,7 +37,7 @@ test('Increase version for major release', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release'},
|
||||
nextRelease: {type: 'major'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'2.0.0'
|
||||
@@ -61,7 +61,7 @@ test('Increase version for patch release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'patch'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.1-beta.1'
|
||||
@@ -71,7 +71,7 @@ test('Increase version for patch release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'patch'},
|
||||
lastRelease: {version: '1.0.0-beta.1'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.0-beta.2'
|
||||
@@ -81,7 +81,7 @@ test('Increase version for patch release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
|
||||
nextRelease: {type: 'patch', channel: 'alpha'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: ['beta']},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.1-alpha.1'
|
||||
@@ -93,7 +93,7 @@ test('Increase version for minor release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'minor'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.1.0-beta.1'
|
||||
@@ -103,7 +103,7 @@ test('Increase version for minor release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'minor'},
|
||||
lastRelease: {version: '1.0.0-beta.1'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.0-beta.2'
|
||||
@@ -113,7 +113,7 @@ test('Increase version for minor release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
|
||||
nextRelease: {type: 'minor', channel: 'alpha'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: ['beta']},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.1.0-alpha.1'
|
||||
@@ -125,7 +125,7 @@ test('Increase version for major release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'major'},
|
||||
lastRelease: {version: '1.0.0'},
|
||||
lastRelease: {version: '1.0.0', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'2.0.0-beta.1'
|
||||
@@ -135,7 +135,7 @@ test('Increase version for major release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta'},
|
||||
nextRelease: {type: 'major'},
|
||||
lastRelease: {version: '1.0.0-beta.1'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: [undefined]},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'1.0.0-beta.2'
|
||||
@@ -145,7 +145,7 @@ test('Increase version for major release on prerelease branch', t => {
|
||||
getNextVersion({
|
||||
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
|
||||
nextRelease: {type: 'major', channel: 'alpha'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
|
||||
lastRelease: {version: '1.0.0-beta.1', channels: ['beta']},
|
||||
logger: t.context.logger,
|
||||
}),
|
||||
'2.0.0-alpha.1'
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
import test from 'ava';
|
||||
import getReleaseToAdd from '../lib/get-release-to-add';
|
||||
|
||||
test('Return versions merged from release to maintenance branch, excluding lower than branch start range', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
channel: '2.x',
|
||||
type: 'maintenance',
|
||||
mergeRange: '>=2.0.0 <3.0.0',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: ['2.x']},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: [null]},
|
||||
{gitTag: 'v2.1.0', version: '2.1.0', channels: [null]},
|
||||
{gitTag: 'v2.1.1', version: '2.1.1', channels: [null]},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: [null]},
|
||||
],
|
||||
},
|
||||
branches: [{name: '2.x', channel: '2.x'}, {name: 'master'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease: {version: '2.1.0', channels: [null], gitTag: 'v2.1.0', name: 'v2.1.0', gitHead: 'v2.1.0'},
|
||||
currentRelease: {
|
||||
type: 'patch',
|
||||
version: '2.1.1',
|
||||
channels: [null],
|
||||
gitTag: 'v2.1.1',
|
||||
name: 'v2.1.1',
|
||||
gitHead: 'v2.1.1',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'patch',
|
||||
version: '2.1.1',
|
||||
channel: '2.x',
|
||||
gitTag: 'v2.1.1',
|
||||
name: 'v2.1.1',
|
||||
gitHead: 'v2.1.1',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Return versions merged between release branches', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null, 'next']},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: ['next']},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: ['next-major']},
|
||||
],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}, {name: 'next-major', channel: 'next-major'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease: {
|
||||
version: '1.1.0',
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0',
|
||||
channels: ['next'],
|
||||
},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channels: ['next-major'],
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: null,
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Return releases sorted by ascending order', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: ['next-major']},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: ['next']},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null, 'next']},
|
||||
],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}, {name: 'next-major', channel: 'next-major'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease: {version: '1.1.0', gitTag: 'v1.1.0', name: 'v1.1.0', gitHead: 'v1.1.0', channels: ['next']},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channels: ['next-major'],
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: null,
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('No lastRelease', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [{gitTag: 'v1.0.0', version: '1.0.0', channels: ['next']}],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease: {},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channels: ['next'],
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
gitHead: 'v1.0.0',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
gitHead: 'v1.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Ignore pre-release versions', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null, 'next']},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: ['next']},
|
||||
{gitTag: 'v2.0.0-alpha.1', version: '2.0.0-alpha.1', channels: ['alpha']},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master'},
|
||||
{name: 'next', channel: 'next'},
|
||||
{name: 'alpha', type: 'prerelease', channel: 'alpha'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease: {version: '1.0.0', channels: [null, 'next'], gitTag: 'v1.0.0', name: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
currentRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channels: ['next'],
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: null,
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Exclude versions merged from release to maintenance branch if they have the same "channel"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
channel: 'latest',
|
||||
type: 'maintenance',
|
||||
mergeRange: '>=2.0.0 <3.0.0',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: [null]},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: [null]},
|
||||
{gitTag: 'v2.1.0', version: '2.1.0', channels: [null]},
|
||||
{gitTag: 'v2.1.1', version: '2.1.1', channels: [null]},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: [null]},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: '2.x', channel: 'latest'},
|
||||
{name: 'master', channel: 'latest'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they have the same "channel"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
channel: 'latest',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', channels: ['latest'], version: '1.0.0'},
|
||||
{gitTag: 'v1.1.0', channels: ['latest'], version: '1.1.0'},
|
||||
{gitTag: 'v2.0.0', channels: ['latest'], version: '2.0.0'},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master', channel: 'latest'},
|
||||
{name: 'next', channel: 'latest'},
|
||||
{name: 'next-major', channel: 'latest'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they all have "channel" set to "false"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
channel: false,
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: [null]},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: [null]},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master', channel: false},
|
||||
{name: 'next', channel: false},
|
||||
{name: 'next-major', channel: false},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions number less than the latest version already released on that branch', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
channel: '2.x',
|
||||
type: 'maintenance',
|
||||
mergeRange: '>=2.0.0 <3.0.0',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: ['2.x']},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0', channels: [null]},
|
||||
{gitTag: 'v2.1.0', version: '2.1.0', channels: [null]},
|
||||
{gitTag: 'v2.1.1', version: '2.1.1', channels: [null, '2.x']},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0', channels: [null]},
|
||||
],
|
||||
},
|
||||
branches: [{name: '2.x', channel: '2.x'}, {name: 'master'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.is(result, undefined);
|
||||
});
|
||||
@@ -1,323 +0,0 @@
|
||||
import test from 'ava';
|
||||
import getReleasesToAdd from '../lib/get-releases-to-add';
|
||||
|
||||
test('Return versions merged from release to maintenance branch, excluding lower than branch start range', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
channel: '2.x',
|
||||
type: 'maintenance',
|
||||
mergeRange: '>=2.0.0 <3.0.0',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0@2.x', version: '2.0.0', channel: '2.x'},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0'},
|
||||
{gitTag: 'v2.1.0', version: '2.1.0'},
|
||||
{gitTag: 'v2.1.1', version: '2.1.1'},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0'},
|
||||
],
|
||||
},
|
||||
branches: [{name: '2.x', channel: '2.x'}, {name: 'master'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
lastRelease: {version: '2.0.0', channel: '2.x', gitTag: 'v2.0.0@2.x', name: 'v2.0.0', gitHead: 'v2.0.0@2.x'},
|
||||
currentRelease: {
|
||||
type: 'minor',
|
||||
version: '2.1.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v2.1.0',
|
||||
name: 'v2.1.0',
|
||||
gitHead: 'v2.1.0',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'minor',
|
||||
version: '2.1.0',
|
||||
channel: '2.x',
|
||||
gitTag: 'v2.1.0@2.x',
|
||||
name: 'v2.1.0',
|
||||
gitHead: 'v2.1.0',
|
||||
},
|
||||
},
|
||||
{
|
||||
lastRelease: {version: '2.1.0', channel: undefined, gitTag: 'v2.1.0', name: 'v2.1.0', gitHead: 'v2.1.0'},
|
||||
currentRelease: {
|
||||
type: 'patch',
|
||||
version: '2.1.1',
|
||||
channel: undefined,
|
||||
gitTag: 'v2.1.1',
|
||||
name: 'v2.1.1',
|
||||
gitHead: 'v2.1.1',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'patch',
|
||||
version: '2.1.1',
|
||||
channel: '2.x',
|
||||
gitTag: 'v2.1.1@2.x',
|
||||
name: 'v2.1.1',
|
||||
gitHead: 'v2.1.1',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Return versions merged between release branches', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.0.0@next', version: '1.0.0', channel: 'next'},
|
||||
{gitTag: 'v1.1.0@next', version: '1.1.0', channel: 'next'},
|
||||
{gitTag: 'v2.0.0@next-major', version: '2.0.0', channel: 'next-major'},
|
||||
],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}, {name: 'next-major', channel: 'next-major'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
lastRelease: {version: '1.0.0', channel: undefined, gitTag: 'v1.0.0', name: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
currentRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: 'next',
|
||||
gitTag: 'v1.1.0@next',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
},
|
||||
{
|
||||
lastRelease: {version: '1.1.0', gitTag: 'v1.1.0@next', name: 'v1.1.0', gitHead: 'v1.1.0@next', channel: 'next'},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: 'next-major',
|
||||
gitTag: 'v2.0.0@next-major',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0@next-major',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0@next-major',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Return releases sorted by ascending order', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0@next-major', version: '2.0.0', channel: 'next-major'},
|
||||
{gitTag: 'v1.1.0@next', version: '1.1.0', channel: 'next'},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.0.0@next', version: '1.0.0', channel: 'next'},
|
||||
],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}, {name: 'next-major', channel: 'next-major'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
lastRelease: {version: '1.0.0', channel: undefined, gitTag: 'v1.0.0', name: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
currentRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: 'next',
|
||||
gitTag: 'v1.1.0@next',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
},
|
||||
{
|
||||
lastRelease: {version: '1.1.0', gitTag: 'v1.1.0@next', name: 'v1.1.0', gitHead: 'v1.1.0@next', channel: 'next'},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: 'next-major',
|
||||
gitTag: 'v2.0.0@next-major',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0@next-major',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v2.0.0',
|
||||
name: 'v2.0.0',
|
||||
gitHead: 'v2.0.0@next-major',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('No lastRelease', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [{gitTag: 'v1.0.0@next', version: '1.0.0', channel: 'next'}],
|
||||
},
|
||||
branches: [{name: 'master'}, {name: 'next', channel: 'next'}],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
lastRelease: {},
|
||||
currentRelease: {
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: 'next',
|
||||
gitTag: 'v1.0.0@next',
|
||||
name: 'v1.0.0',
|
||||
gitHead: 'v1.0.0@next',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
gitHead: 'v1.0.0@next',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Ignore pre-release versions', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.0.0@next', version: '1.0.0', channel: 'next'},
|
||||
{gitTag: 'v1.1.0@next', version: '1.1.0', channel: 'next'},
|
||||
{gitTag: 'v2.0.0-alpha.1@alpha', version: '2.0.0', channel: 'alpha'},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master'},
|
||||
{name: 'next', channel: 'next'},
|
||||
{name: 'alpha', type: 'prerelease', channel: 'alpha'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, [
|
||||
{
|
||||
lastRelease: {version: '1.0.0', channel: undefined, gitTag: 'v1.0.0', name: 'v1.0.0', gitHead: 'v1.0.0'},
|
||||
currentRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: 'next',
|
||||
gitTag: 'v1.1.0@next',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
nextRelease: {
|
||||
type: 'minor',
|
||||
version: '1.1.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v1.1.0',
|
||||
name: 'v1.1.0',
|
||||
gitHead: 'v1.1.0@next',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Exclude versions merged from release to maintenance branch if they have the same "channel"', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
channel: 'latest',
|
||||
type: 'maintenance',
|
||||
mergeRange: '>=2.0.0 <3.0.0',
|
||||
tags: [
|
||||
{gitTag: 'v2.0.0', version: '2.0.0'},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0'},
|
||||
{gitTag: 'v2.1.0', version: '2.1.0'},
|
||||
{gitTag: 'v2.1.1', version: '2.1.1'},
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0'},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: '2.x', channel: 'latest'},
|
||||
{name: 'master', channel: 'latest'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, []);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they have the same "channel"', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
channel: 'latest',
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', channel: 'latest', version: '1.0.0'},
|
||||
{gitTag: 'v1.1.0', channel: 'latest', version: '1.1.0'},
|
||||
{gitTag: 'v2.0.0', channel: 'latest', version: '2.0.0'},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master', channel: 'latest'},
|
||||
{name: 'next', channel: 'latest'},
|
||||
{name: 'next-major', channel: 'latest'},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, []);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they all have "channel" set to "false"', t => {
|
||||
const result = getReleasesToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
channel: false,
|
||||
tags: [
|
||||
{gitTag: 'v1.0.0', version: '1.0.0'},
|
||||
{gitTag: 'v1.1.0', version: '1.1.0'},
|
||||
{gitTag: 'v2.0.0', version: '2.0.0'},
|
||||
],
|
||||
},
|
||||
branches: [
|
||||
{name: 'master', channel: false},
|
||||
{name: 'next', channel: false},
|
||||
{name: 'next-major', channel: false},
|
||||
],
|
||||
options: {tagFormat: `v\${version}`},
|
||||
});
|
||||
|
||||
t.deepEqual(result, []);
|
||||
});
|
||||
+123
-4
@@ -14,6 +14,9 @@ import {
|
||||
isGitRepo,
|
||||
verifyTagName,
|
||||
isBranchUpToDate,
|
||||
getNote,
|
||||
addNote,
|
||||
fetchNotes,
|
||||
} from '../lib/git';
|
||||
import {
|
||||
gitRepo,
|
||||
@@ -27,6 +30,9 @@ import {
|
||||
gitRemoteTagHead,
|
||||
gitPush,
|
||||
gitDetachedHead,
|
||||
gitDetachedHeadFromBranch,
|
||||
gitAddNote,
|
||||
gitGetNote,
|
||||
} from './helpers/git-utils';
|
||||
|
||||
test('Get the last commit sha', async t => {
|
||||
@@ -58,7 +64,7 @@ test('Unshallow and fetch repository', async t => {
|
||||
// Verify the shallow clone contains only one commit
|
||||
t.is((await gitGetCommits(undefined, {cwd})).length, 1);
|
||||
|
||||
await fetch(repositoryUrl, 'master', {cwd});
|
||||
await fetch(repositoryUrl, 'master', 'master', {cwd});
|
||||
|
||||
// Verify the shallow clone contains all the commits
|
||||
t.is((await gitGetCommits(undefined, {cwd})).length, 2);
|
||||
@@ -73,8 +79,8 @@ test('Do not throw error when unshallow a complete repository', async t => {
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitPush(repositoryUrl, 'second-branch', {cwd});
|
||||
|
||||
await t.notThrowsAsync(fetch(repositoryUrl, 'master', {cwd}));
|
||||
await t.notThrowsAsync(fetch(repositoryUrl, 'second-branch', {cwd}));
|
||||
await t.notThrowsAsync(fetch(repositoryUrl, 'master', 'master', {cwd}));
|
||||
await t.notThrowsAsync(fetch(repositoryUrl, 'second-branch', 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Fetch all tags on a detached head repository', async t => {
|
||||
@@ -89,11 +95,36 @@ test('Fetch all tags on a detached head repository', async t => {
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
|
||||
|
||||
await fetch(repositoryUrl, 'master', {cwd});
|
||||
await fetch(repositoryUrl, 'master', 'master', {cwd});
|
||||
|
||||
t.deepEqual((await getTags('master', {cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort());
|
||||
});
|
||||
|
||||
test('Fetch all tags on a repository with a detached head from branch', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitTagVersion('v1.0.1', undefined, {cwd});
|
||||
const [commit] = await gitCommits(['Third'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
await gitCheckout('other-branch', true, {cwd});
|
||||
await gitPush(repositoryUrl, 'other-branch', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
await gitCommits(['Fourth'], {cwd});
|
||||
await gitTagVersion('v2.0.0', undefined, {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
cwd = await gitDetachedHeadFromBranch(repositoryUrl, 'other-branch', commit.hash);
|
||||
|
||||
await fetch(repositoryUrl, 'master', 'other-branch', {cwd});
|
||||
await fetch(repositoryUrl, 'other-branch', 'other-branch', {cwd});
|
||||
|
||||
t.deepEqual((await getTags('other-branch', {cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort());
|
||||
t.deepEqual((await getTags('master', {cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0', 'v2.0.0'].sort());
|
||||
});
|
||||
|
||||
test('Verify if the commit `sha` is in the direct history of the current branch', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
@@ -276,3 +307,91 @@ test('Return "true" if local repository is ahead', async t => {
|
||||
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Get a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
|
||||
await gitAddNote(JSON.stringify({note: 'note'}), commits[0].hash, {cwd});
|
||||
|
||||
t.deepEqual(await getNote(commits[0].hash, {cwd}), {note: 'note'});
|
||||
});
|
||||
|
||||
test('Return empty object if there is no commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
|
||||
t.deepEqual(await getNote(commits[0].hash, {cwd}), {});
|
||||
});
|
||||
|
||||
test('Throw error if a commit note in invalid', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
|
||||
await gitAddNote('non-json note', commits[0].hash, {cwd});
|
||||
|
||||
await t.throwsAsync(getNote(commits[0].hash, {cwd}));
|
||||
});
|
||||
|
||||
test('Add a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
|
||||
await addNote({note: 'note'}, commits[0].hash, {cwd});
|
||||
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note"}');
|
||||
});
|
||||
|
||||
test('Overwrite a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
|
||||
await addNote({note: 'note'}, commits[0].hash, {cwd});
|
||||
await addNote({note: 'note2'}, commits[0].hash, {cwd});
|
||||
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note2"}');
|
||||
});
|
||||
|
||||
test('Unshallow and fetch repository with notes', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
const commits = await gitCommits(['First', 'Second'], {cwd});
|
||||
await gitAddNote(JSON.stringify({note: 'note'}), commits[0].hash, {cwd});
|
||||
// Create a shallow clone with only 1 commit
|
||||
cwd = await gitShallowClone(repositoryUrl);
|
||||
|
||||
// Verify the shallow clone doesn't contains the note
|
||||
await t.throwsAsync(gitGetNote(commits[0].hash, {cwd}));
|
||||
|
||||
await fetch(repositoryUrl, 'master', 'master', {cwd});
|
||||
await fetchNotes(repositoryUrl, {cwd});
|
||||
|
||||
// Verify the shallow clone contains the note
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note"}');
|
||||
});
|
||||
|
||||
test('Fetch all notes on a detached head repository', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
const [commit] = await gitCommits(['Second'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
await gitAddNote(JSON.stringify({note: 'note'}), commit.hash, {cwd});
|
||||
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
|
||||
|
||||
await fetch(repositoryUrl, 'master', 'master', {cwd});
|
||||
await fetchNotes(repositoryUrl, {cwd});
|
||||
|
||||
t.is(await gitGetNote(commit.hash, {cwd}), '{"note":"note"}');
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ 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';
|
||||
|
||||
/**
|
||||
* Commit message informations.
|
||||
@@ -165,6 +166,17 @@ export async function gitDetachedHead(repositoryUrl, head) {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
export async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) {
|
||||
const cwd = tempy.directory();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Git configuration.
|
||||
*
|
||||
@@ -258,3 +270,24 @@ export async function mergeFf(ref, execaOpts) {
|
||||
export async function rebase(ref, execaOpts) {
|
||||
await execa('git', ['rebase', ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a note to a Git reference.
|
||||
*
|
||||
* @param {String} note The note to add.
|
||||
* @param {String} ref The ref to add the note to.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
export async function gitAddNote(note, ref, execaOpts) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-m', note, ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the note associated with a Git reference.
|
||||
*
|
||||
* @param {String} ref The ref to get the note from.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
export async function gitGetNote(ref, execaOpts) {
|
||||
return (await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
+83
-87
@@ -1,5 +1,5 @@
|
||||
import test from 'ava';
|
||||
import {escapeRegExp, isString, sortBy} from 'lodash';
|
||||
import {escapeRegExp, isString, sortBy, omit} from 'lodash';
|
||||
import proxyquire from 'proxyquire';
|
||||
import {spy, stub} from 'sinon';
|
||||
import {WritableStreamBuffer} from 'stream-buffers';
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
merge,
|
||||
mergeFf,
|
||||
rebase,
|
||||
gitAddNote,
|
||||
gitGetNote,
|
||||
} from './helpers/git-utils';
|
||||
|
||||
const requireNoCache = proxyquire.noPreserveCache();
|
||||
@@ -45,7 +47,8 @@ test('Plugins are called with expected values', async t => {
|
||||
// Add commits to the master branch
|
||||
let commits = await gitCommits(['First'], {cwd});
|
||||
// Create the tag corresponding to version 1.0.0
|
||||
await gitTagVersion('v1.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v1.0.0', {cwd});
|
||||
commits = (await gitCommits(['Second'], {cwd})).concat(commits);
|
||||
await gitCheckout('next', true, {cwd});
|
||||
await gitPush(repositoryUrl, 'next', {cwd});
|
||||
@@ -55,9 +58,9 @@ test('Plugins are called with expected values', async t => {
|
||||
const lastRelease = {
|
||||
version: '1.0.0',
|
||||
gitHead: commits[commits.length - 1].hash,
|
||||
gitTag: 'v1.0.0@next',
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
channel: 'next',
|
||||
channels: ['next'],
|
||||
};
|
||||
const nextRelease = {
|
||||
name: 'v1.1.0',
|
||||
@@ -65,7 +68,7 @@ test('Plugins are called with expected values', async t => {
|
||||
version: '1.1.0',
|
||||
gitHead: await getGitHead({cwd}),
|
||||
gitTag: 'v1.1.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
};
|
||||
const notes1 = 'Release notes 1';
|
||||
const notes2 = 'Release notes 2';
|
||||
@@ -94,18 +97,20 @@ test('Plugins are called with expected values', async t => {
|
||||
{
|
||||
channel: undefined,
|
||||
name: 'master',
|
||||
range: '>=1.0.0 <2.0.0',
|
||||
accept: ['patch', 'minor'],
|
||||
tags: [{channel: 'next', gitTag: 'v1.0.0@next', version: '1.0.0'}],
|
||||
range: '>=1.0.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
tags: [{channels: ['next'], gitTag: 'v1.0.0', version: '1.0.0'}],
|
||||
type: 'release',
|
||||
main: true,
|
||||
},
|
||||
{
|
||||
channel: 'next',
|
||||
name: 'next',
|
||||
range: '>=2.0.0',
|
||||
range: '>=1.0.0',
|
||||
accept: ['patch', 'minor', 'major'],
|
||||
tags: [{channel: 'next', gitTag: 'v1.0.0@next', version: '1.0.0'}],
|
||||
tags: [{channels: ['next'], gitTag: 'v1.0.0', version: '1.0.0'}],
|
||||
type: 'release',
|
||||
main: false,
|
||||
},
|
||||
];
|
||||
const branch = branches[0];
|
||||
@@ -124,11 +129,11 @@ test('Plugins are called with expected values', async t => {
|
||||
|
||||
const releases = [
|
||||
{
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
...release1,
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
notes: `${notes1}\n\n${notes2}\n\n${notes3}`,
|
||||
pluginName: '[Function: functionStub]',
|
||||
@@ -176,10 +181,10 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes1.args[0][1].commits[0].hash, commits[1].hash);
|
||||
t.deepEqual(generateNotes1.args[0][1].commits[0].message, commits[1].message);
|
||||
t.deepEqual(generateNotes1.args[0][1].nextRelease, {
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
});
|
||||
@@ -193,10 +198,10 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes2.args[0][1].commits[0].hash, commits[1].hash);
|
||||
t.deepEqual(generateNotes2.args[0][1].commits[0].message, commits[1].message);
|
||||
t.deepEqual(generateNotes2.args[0][1].nextRelease, {
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
notes: notes1,
|
||||
@@ -211,10 +216,10 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes3.args[0][1].commits[0].hash, commits[1].hash);
|
||||
t.deepEqual(generateNotes3.args[0][1].commits[0].message, commits[1].message);
|
||||
t.deepEqual(generateNotes3.args[0][1].nextRelease, {
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
notes: `${notes1}\n\n${notes2}`,
|
||||
@@ -222,7 +227,7 @@ test('Plugins are called with expected values', async t => {
|
||||
|
||||
branch.tags.push({
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
gitHead: commits[commits.length - 1].hash,
|
||||
});
|
||||
@@ -236,10 +241,10 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(addChannel.args[0][1].lastRelease, {});
|
||||
t.deepEqual(addChannel.args[0][1].currentRelease, {...lastRelease, type: 'major'});
|
||||
t.deepEqual(addChannel.args[0][1].nextRelease, {
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
notes: `${notes1}\n\n${notes2}\n\n${notes3}`,
|
||||
@@ -330,10 +335,10 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(success.args[0][1].commits[0].hash, commits[1].hash);
|
||||
t.deepEqual(success.args[0][1].commits[0].message, commits[1].message);
|
||||
t.deepEqual(success.args[0][1].nextRelease, {
|
||||
...lastRelease,
|
||||
...omit(lastRelease, 'channels'),
|
||||
type: 'major',
|
||||
version: '1.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v1.0.0',
|
||||
name: 'v1.0.0',
|
||||
notes: `${notes1}\n\n${notes2}\n\n${notes3}`,
|
||||
@@ -433,7 +438,7 @@ test('Use new gitHead, and recreate release notes if a prepare plugin create a c
|
||||
version: '2.0.0',
|
||||
gitHead: await getGitHead({cwd}),
|
||||
gitTag: 'v2.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
};
|
||||
const notes = 'Release notes';
|
||||
|
||||
@@ -492,15 +497,16 @@ test('Use new gitHead, and recreate release notes if a prepare plugin create a c
|
||||
|
||||
test('Make a new release when a commit is forward-ported to an upper branch', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['feat: initial release'], {cwd});
|
||||
await gitCommits(['feat: initial release'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@1.0.x', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, '1.0.x']}), 'v1.0.0', {cwd});
|
||||
await gitCheckout('1.0.x', true, {cwd});
|
||||
commits.push(...(await gitCommits(['fix: fix on maintenance version 1.0.x'], {cwd})));
|
||||
await gitTagVersion('v1.0.1@1.0.x', undefined, {cwd});
|
||||
await gitCommits(['fix: fix on maintenance version 1.0.x'], {cwd});
|
||||
await gitTagVersion('v1.0.1', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['1.0.x']}), 'v1.0.1', {cwd});
|
||||
await gitPush('origin', '1.0.x', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: new feature on master'], {cwd})));
|
||||
await gitCommits(['feat: new feature on master'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await merge('1.0.x', {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
@@ -566,7 +572,8 @@ test('Publish a pre-release version', async t => {
|
||||
|
||||
t.is(releases.length, 1);
|
||||
t.is(releases[0].version, '1.1.0-beta.1');
|
||||
t.is(releases[0].gitTag, 'v1.1.0-beta.1@beta');
|
||||
t.is(releases[0].gitTag, 'v1.1.0-beta.1');
|
||||
t.is(await gitGetNote('v1.1.0-beta.1', {cwd}), '{"channels":["beta"]}');
|
||||
|
||||
await gitCommits(['fix: a fix'], {cwd});
|
||||
({releases} = await semanticRelease(options, {
|
||||
@@ -578,7 +585,8 @@ test('Publish a pre-release version', async t => {
|
||||
|
||||
t.is(releases.length, 1);
|
||||
t.is(releases[0].version, '1.1.0-beta.2');
|
||||
t.is(releases[0].gitTag, 'v1.1.0-beta.2@beta');
|
||||
t.is(releases[0].gitTag, 'v1.1.0-beta.2');
|
||||
t.is(await gitGetNote('v1.1.0-beta.2', {cwd}), '{"channels":["beta"]}');
|
||||
});
|
||||
|
||||
test('Publish releases from different branch on the same channel', async t => {
|
||||
@@ -694,14 +702,16 @@ test('Publish pre-releases the same channel as regular releases', async t => {
|
||||
|
||||
test('Do not add pre-releases to a different channel', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['feat: initial release'], {cwd});
|
||||
await gitCommits(['feat: initial release'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@beta', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, 'beta']}), 'v1.0.0', {cwd});
|
||||
await gitCheckout('beta', true, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: breaking change/n/nBREAKING CHANGE: break something'], {cwd})));
|
||||
await gitTagVersion('v2.0.0-beta.1@beta', undefined, {cwd});
|
||||
commits.push(...(await gitCommits(['fix: a fix'], {cwd})));
|
||||
await gitTagVersion('v2.0.0-beta.2@beta', undefined, {cwd});
|
||||
await gitCommits(['feat: breaking change/n/nBREAKING CHANGE: break something'], {cwd});
|
||||
await gitTagVersion('v2.0.0-beta.1', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['beta']}), 'v2.0.0-beta.1', {cwd});
|
||||
await gitCommits(['fix: a fix'], {cwd});
|
||||
await gitTagVersion('v2.0.0-beta.2', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['beta']}), 'v2.0.0-beta.2', {cwd});
|
||||
await gitPush('origin', 'beta', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
await merge('beta', {cwd});
|
||||
@@ -746,14 +756,18 @@ async function addChannelMacro(t, mergeFunction) {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['feat: initial release'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@next', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v1.0.0', {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: breaking change/n/nBREAKING CHANGE: break something'], {cwd})));
|
||||
await gitTagVersion('v2.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('v2.0.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v2.0.0', {cwd});
|
||||
|
||||
commits.push(...(await gitCommits(['fix: a fix'], {cwd})));
|
||||
await gitTagVersion('v2.0.1@next', undefined, {cwd});
|
||||
await gitTagVersion('v2.0.1', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v2.0.1', {cwd});
|
||||
commits.push(...(await gitCommits(['feat: a feature'], {cwd})));
|
||||
await gitTagVersion('v2.1.0@next', undefined, {cwd});
|
||||
await gitTagVersion('v2.1.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v2.1.0', {cwd});
|
||||
await gitPush('origin', 'next', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
// Merge all commits but last one from next to master
|
||||
@@ -782,19 +796,11 @@ async function addChannelMacro(t, mergeFunction) {
|
||||
publish,
|
||||
success,
|
||||
};
|
||||
const nextRelease1 = {
|
||||
name: 'v2.0.0',
|
||||
type: 'major',
|
||||
version: '2.0.0',
|
||||
channel: undefined,
|
||||
gitTag: 'v2.0.0',
|
||||
gitHead: commits[1].hash,
|
||||
};
|
||||
const nextRelease2 = {
|
||||
name: 'v2.0.1',
|
||||
type: 'patch',
|
||||
version: '2.0.1',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
gitTag: 'v2.0.1',
|
||||
gitHead: commits[2].hash,
|
||||
};
|
||||
@@ -806,15 +812,11 @@ async function addChannelMacro(t, mergeFunction) {
|
||||
const result = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
|
||||
|
||||
t.deepEqual(result.releases, [
|
||||
{...nextRelease1, ...release1, notes, pluginName: '[Function: functionStub]'},
|
||||
{...nextRelease1, notes, pluginName: '[Function: functionStub]'},
|
||||
{...nextRelease2, ...release1, notes, pluginName: '[Function: functionStub]'},
|
||||
{...nextRelease2, notes, pluginName: '[Function: functionStub]'},
|
||||
]);
|
||||
|
||||
// Verify the tag has been created on the local and remote repo and reference
|
||||
t.is(await gitTagHead(nextRelease1.gitTag, {cwd}), nextRelease1.gitHead);
|
||||
t.is(await gitRemoteTagHead(repositoryUrl, nextRelease1.gitTag, {cwd}), nextRelease1.gitHead);
|
||||
t.is(await gitTagHead(nextRelease2.gitTag, {cwd}), nextRelease2.gitHead);
|
||||
t.is(await gitRemoteTagHead(repositoryUrl, nextRelease2.gitTag, {cwd}), nextRelease2.gitHead);
|
||||
}
|
||||
@@ -842,7 +844,7 @@ test('Call all "success" plugins even if one errors out', async t => {
|
||||
version: '2.0.0',
|
||||
gitHead: await getGitHead({cwd}),
|
||||
gitTag: 'v2.0.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
};
|
||||
const notes = 'Release notes';
|
||||
const verifyConditions1 = stub().resolves();
|
||||
@@ -977,9 +979,11 @@ test('Dry-run skips addChannel, prepare, publish and success', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('v1.1.0@next', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v1.0.0', {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v1.1.0', {cwd});
|
||||
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
await gitPush('origin', 'next', {cwd});
|
||||
@@ -1259,8 +1263,10 @@ test('Accept "undefined" value returned by "generateNotes" and "false" by "publi
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@next', undefined, {cwd});
|
||||
await gitTagVersion('v1.1.0@next', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, 'next']}), 'v1.0.0', {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v1.1.0', {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
await gitPush('origin', 'next', {cwd});
|
||||
@@ -1272,7 +1278,7 @@ test('Accept "undefined" value returned by "generateNotes" and "false" by "publi
|
||||
version: '1.2.0',
|
||||
gitHead: await getGitHead({cwd}),
|
||||
gitTag: 'v1.2.0',
|
||||
channel: undefined,
|
||||
channel: null,
|
||||
};
|
||||
const analyzeCommits = stub().resolves(nextRelease.type);
|
||||
const verifyRelease = stub().resolves();
|
||||
@@ -1345,16 +1351,16 @@ test('Returns false if triggered by a PR', async t => {
|
||||
|
||||
test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the current maintenance branch', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['feat: initial commit'], {cwd});
|
||||
await gitCommits(['feat: initial commit'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@1.x', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, '1.x']}), 'v1.0.0', {cwd});
|
||||
await gitCheckout('1.x', true, {cwd});
|
||||
await gitPush('origin', '1.x', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: new feature on master'], {cwd})));
|
||||
await gitCommits(['feat: new feature on master'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitCheckout('1.x', false, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: feature on maintenance version 1.x'], {cwd})));
|
||||
await gitCommits(['feat: feature on maintenance version 1.x'], {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
|
||||
const verifyConditions = stub().resolves();
|
||||
@@ -1396,16 +1402,17 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
|
||||
|
||||
test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the current release branch', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['feat: initial commit'], {cwd});
|
||||
await gitCommits(['feat: initial commit'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: new feature on next'], {cwd})));
|
||||
await gitTagVersion('v1.1.0@next', undefined, {cwd});
|
||||
await gitCommits(['feat: new feature on next'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: ['next']}), 'v1.1.0', {cwd});
|
||||
await gitPush('origin', 'next', {cwd});
|
||||
await gitCheckout('next-major', true, {cwd});
|
||||
await gitPush('origin', 'next-major', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
commits.push(...(await gitCommits(['feat: new feature on master', 'fix: new fix on master'], {cwd})));
|
||||
await gitCommits(['feat: new feature on master', 'fix: new fix on master'], {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
|
||||
const verifyConditions = stub().resolves();
|
||||
@@ -1447,18 +1454,18 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
|
||||
|
||||
test('Throws "EINVALIDMAINTENANCEMERGE" if merge an out of range release in a maintenance branch', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.0.0@1.1.x', undefined, {cwd});
|
||||
commits.push(...(await gitCommits(['Second'], {cwd})));
|
||||
await gitAddNote(JSON.stringify({channels: [null, '1.1.x']}), 'v1.0.0', {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitTagVersion('v1.1.0', undefined, {cwd});
|
||||
await gitTagVersion('v1.1.0@1.1.x', undefined, {cwd});
|
||||
await gitAddNote(JSON.stringify({channels: [null, '1.1.x']}), 'v1.1.0', {cwd});
|
||||
await gitCheckout('1.1.x', 'master', {cwd});
|
||||
await gitPush('origin', '1.1.x', {cwd});
|
||||
await gitCheckout('master', false, {cwd});
|
||||
commits.push(...(await gitCommits(['Third'], {cwd})));
|
||||
await gitCommits(['Third'], {cwd});
|
||||
await gitTagVersion('v1.1.1', undefined, {cwd});
|
||||
commits.push(...(await gitCommits(['Fourth'], {cwd})));
|
||||
await gitCommits(['Fourth'], {cwd});
|
||||
await gitTagVersion('v1.2.0', undefined, {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
await gitCheckout('1.1.x', false, {cwd});
|
||||
@@ -1490,15 +1497,6 @@ test('Throws "EINVALIDMAINTENANCEMERGE" if merge an out of range release in a ma
|
||||
fail,
|
||||
};
|
||||
|
||||
const nextRelease = {
|
||||
type: 'patch',
|
||||
version: '1.1.1',
|
||||
channel: '1.1.x',
|
||||
gitTag: 'v1.1.1@1.1.x',
|
||||
name: 'v1.1.1',
|
||||
gitHead: commits[2].hash,
|
||||
};
|
||||
|
||||
const semanticRelease = proxyquire('..', {
|
||||
'./lib/logger': t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: '1.1.x', isPr: false}),
|
||||
@@ -1509,13 +1507,11 @@ test('Throws "EINVALIDMAINTENANCEMERGE" if merge an out of range release in a ma
|
||||
)),
|
||||
];
|
||||
|
||||
t.is(addChannel.callCount, 1);
|
||||
t.deepEqual(addChannel.args[0][1].nextRelease, {...nextRelease, notes});
|
||||
t.is(addChannel.callCount, 0);
|
||||
|
||||
t.is(publish.callCount, 0);
|
||||
|
||||
t.is(success.callCount, 1);
|
||||
t.deepEqual(success.args[0][1].releases, [{...nextRelease, notes, pluginName: '[Function: functionStub]'}]);
|
||||
t.is(success.callCount, 0);
|
||||
|
||||
t.is(fail.callCount, 1);
|
||||
t.deepEqual(fail.args[0][1].errors, errors);
|
||||
|
||||
+10
-10
@@ -16,6 +16,7 @@ import {
|
||||
gitPush,
|
||||
gitCheckout,
|
||||
merge,
|
||||
gitGetNote,
|
||||
} from './helpers/git-utils';
|
||||
import {npmView} from './helpers/npm-utils';
|
||||
import gitbox from './helpers/gitbox';
|
||||
@@ -220,7 +221,7 @@ test('Release patch, minor and major versions', async t => {
|
||||
createReleaseMock = await mockServer.mock(
|
||||
`/repos/${owner}/${packageName}/releases`,
|
||||
{
|
||||
body: {tag_name: `v${version}@next`, name: `v${version}`},
|
||||
body: {tag_name: `v${version}`, name: `v${version}`},
|
||||
headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}],
|
||||
},
|
||||
{body: {html_url: `release-url/${version}`}}
|
||||
@@ -246,8 +247,9 @@ test('Release patch, minor and major versions', async t => {
|
||||
} = await npmView(packageName, testEnv));
|
||||
head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitTagHead(`v${version}@next`, {cwd}), head);
|
||||
t.is(await gitRemoteTagHead(authUrl, `v${version}@next`, {cwd}), head);
|
||||
t.is(await gitGetNote(`v${version}`, {cwd}), '{"channels":["next"]}');
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), head);
|
||||
t.is(await gitRemoteTagHead(authUrl, `v${version}`, {cwd}), head);
|
||||
t.log(`+ released ${releasedVersion} on @next`);
|
||||
|
||||
await mockServer.verify(verifyMock);
|
||||
@@ -262,14 +264,14 @@ test('Release patch, minor and major versions', async t => {
|
||||
{body: {permissions: {push: true}}, method: 'GET'}
|
||||
);
|
||||
const getReleaseMock = await mockServer.mock(
|
||||
`/repos/${owner}/${packageName}/releases/tags/v2.0.0@next`,
|
||||
`/repos/${owner}/${packageName}/releases/tags/v2.0.0`,
|
||||
{headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}]},
|
||||
{body: {id: releaseId}, method: 'GET'}
|
||||
);
|
||||
const updateReleaseMock = await mockServer.mock(
|
||||
`/repos/${owner}/${packageName}/releases/${releaseId}`,
|
||||
{
|
||||
body: {tag_name: `v${version}`, name: `v${version}`, prerelease: false},
|
||||
body: {name: `v${version}`, prerelease: false},
|
||||
headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}],
|
||||
},
|
||||
{body: {html_url: `release-url/${version}`}, method: 'PATCH'}
|
||||
@@ -292,11 +294,9 @@ test('Release patch, minor and major versions', async t => {
|
||||
'dist-tags': {latest: releasedVersion},
|
||||
} = await npmView(packageName, testEnv));
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), await gitTagHead(`v${version}@next`, {cwd}));
|
||||
t.is(
|
||||
await gitRemoteTagHead(authUrl, `v${version}`, {cwd}),
|
||||
await gitRemoteTagHead(authUrl, `v${version}@next`, {cwd})
|
||||
);
|
||||
t.is(await gitGetNote(`v${version}`, {cwd}), '{"channels":["next",null]}');
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), await gitTagHead(`v${version}`, {cwd}));
|
||||
t.is(await gitRemoteTagHead(authUrl, `v${version}`, {cwd}), await gitRemoteTagHead(authUrl, `v${version}`, {cwd}));
|
||||
t.log(`+ added ${releasedVersion}`);
|
||||
|
||||
await mockServer.verify(verifyMock);
|
||||
|
||||
@@ -174,10 +174,6 @@ test('getRange', t => {
|
||||
|
||||
test('makeTag', t => {
|
||||
t.is(makeTag(`v\${version}`, '1.0.0'), 'v1.0.0');
|
||||
t.is(makeTag(`v\${version}`, '1.0.0', false), 'v1.0.0');
|
||||
t.is(makeTag(`v\${version}`, '1.0.0', null), 'v1.0.0');
|
||||
t.is(makeTag(`v\${version}`, '1.0.0', 'next'), 'v1.0.0@next');
|
||||
t.is(makeTag(`v\${version}@test`, '1.0.0', 'next'), 'v1.0.0@next@test');
|
||||
});
|
||||
|
||||
test('isSameChannel', t => {
|
||||
|
||||
Reference in New Issue
Block a user