Skip to content

Commit

Permalink
fix: allow multiple branches with same channel
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Nov 20, 2019
1 parent 20e7a38 commit 63f51ae
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 6 deletions.
8 changes: 5 additions & 3 deletions docs/usage/workflow-configuration.md
Expand Up @@ -48,8 +48,10 @@ For example the configuration `['+([0-9])?(.{+([0-9]),x}).x', 'master', 'next']`

### channel

The `channel` can be defined for any branch type. If it's not defined, releases will be done on the default distribution channel (for example the `@latest` [dist-tag](https://docs.npmjs.com/cli/dist-tag) for npm).
The value of `channel`, if defined, is generated with [Lodash template](https://lodash.com/docs#template) with the variable `name` available.
The `channel` can be defined for any branch type. By default releases will be done on the default distribution channel (for example the `@latest` [dist-tag](https://docs.npmjs.com/cli/dist-tag) for npm) for the first [release branch](#release-branches) and on a distribution channel named based on the branch `name` for any other branch.
If the `channel` property is set to `false` the default channel will be used.

The value of `channel`, if defined as a string, is generated with [Lodash template](https://lodash.com/docs#template) with the variable `name` available.

For example the configuration `['master', {name: 'next', channel: 'channel-${name}'}]` will be expanded as:
```js
Expand Down Expand Up @@ -78,7 +80,7 @@ For example the configuration `['1.1.x', '1.2.x', 'master']` will be expanded as

### prerelease

A `prerelease` property applies only to pre-release branches, is required and The `prerelease` value must be valid per the [Semantic Versioning Specification](https://semver.org/#spec-item-9). It will determine the name of versions (for example if `prerelease` is set to `beta` the version be formatted like `2.0.0-beta.1`, `2.0.0-beta.2` etc...).
A `prerelease` property applies only to pre-release branches and the `prerelease` value must be valid per the [Semantic Versioning Specification](https://semver.org/#spec-item-9). It will determine the name of versions (for example if `prerelease` is set to `beta` the version be formatted like `2.0.0-beta.1`, `2.0.0-beta.2` etc...).
If the `prerelease` property is set to `true` the `name` value will be used.

The value of `prerelease`, if defined as a string, is generated with [Lodash template](https://lodash.com/docs#template) with the variable `name` available.
Expand Down
7 changes: 5 additions & 2 deletions lib/get-last-release.js
@@ -1,6 +1,6 @@
const {isUndefined} = require('lodash');
const semver = require('semver');
const {makeTag} = require('./utils');
const {makeTag, isSameChannel} = require('./utils');

/**
* Last release.
Expand Down Expand Up @@ -28,7 +28,10 @@ const {makeTag} = require('./utils');
*/
module.exports = ({branch, options: {tagFormat}}, {before} = {}) => {
const [{version, gitTag, channel} = {}] = branch.tags
.filter(tag => (branch.type === 'prerelease' && branch.channel === tag.channel) || !semver.prerelease(tag.version))
.filter(
tag =>
(branch.type === 'prerelease' && isSameChannel(branch.channel, tag.channel)) || !semver.prerelease(tag.version)
)
.filter(tag => isUndefined(before) || semver.lt(tag.version, before))
.sort((a, b) => semver.rcompare(a.version, b.version));

Expand Down
3 changes: 2 additions & 1 deletion lib/get-next-version.js
@@ -1,13 +1,14 @@
const semver = require('semver');
const {FIRST_RELEASE, FIRSTPRERELEASE} = require('./definitions/constants');
const {isSameChannel} = require('./utils');

module.exports = ({branch, nextRelease: {type, channel}, lastRelease, logger}) => {
let version;
if (lastRelease.version) {
const {major, minor, patch} = semver.parse(lastRelease.version);
version =
branch.type === 'prerelease'
? semver.prerelease(lastRelease.version) && lastRelease.channel === channel
? semver.prerelease(lastRelease.version) && isSameChannel(lastRelease.channel, channel)
? semver.inc(lastRelease.version, 'prerelease')
: `${semver.inc(`${major}.${minor}.${patch}`, type)}-${branch.prerelease}.${FIRSTPRERELEASE}`
: semver.inc(lastRelease.version, type);
Expand Down
5 changes: 5 additions & 0 deletions lib/utils.js
Expand Up @@ -71,6 +71,10 @@ function makeTag(tagFormat, version, channel) {
return template(tagFormat)({version: `${version}${channel ? `@${channel}` : ''}`});
}

function isSameChannel(channel, otherChannel) {
return channel === otherChannel || (!channel && !otherChannel);
}

module.exports = {
extractErrors,
hideSensitiveValues,
Expand All @@ -86,4 +90,5 @@ module.exports = {
getFirstVersion,
getRange,
makeTag,
isSameChannel,
};
111 changes: 111 additions & 0 deletions test/index.test.js
Expand Up @@ -581,6 +581,117 @@ test('Publish a pre-release version', async t => {
t.is(releases[0].gitTag, 'v1.1.0-beta.2@beta');
});

test('Publish releases from different branch on the same channel', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
await gitCommits(['feat: initial commit'], {cwd});
await gitTagVersion('v1.0.0', undefined, {cwd});
await gitPush(repositoryUrl, 'master', {cwd});
await gitCheckout('next-major', true, {cwd});
await gitPush(repositoryUrl, 'next-major', {cwd});
await gitCheckout('next', true, {cwd});
await gitCommits(['feat: a feature'], {cwd});
await gitPush(repositoryUrl, 'next', {cwd});

const config = {
branches: ['master', {name: 'next', channel: false}, {name: 'next-major', channel: false}],
repositoryUrl,
};
const addChannel = stub().resolves({});
const options = {
...config,
verifyConditions: stub().resolves(),
verifyRelease: stub().resolves(),
generateNotes: stub().resolves(''),
addChannel,
prepare: stub().resolves(),
publish: stub().resolves(),
success: stub().resolves(),
fail: stub().resolves(),
};

let semanticRelease = requireNoCache('..', {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'next', isPr: false}),
});
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});

t.is(releases.length, 1);
t.is(releases[0].version, '1.1.0');
t.is(releases[0].gitTag, 'v1.1.0');

await gitCommits(['fix: a fix'], {cwd});
({releases} = await semanticRelease(options, {
cwd,
env: {},
stdout: {write: () => {}},
stderr: {write: () => {}},
}));

t.is(releases.length, 1);
t.is(releases[0].version, '1.1.1');
t.is(releases[0].gitTag, 'v1.1.1');

await gitCheckout('master', false, {cwd});
await merge('next', {cwd});
await gitPush('origin', 'master', {cwd});

semanticRelease = requireNoCache('..', {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});

t.falsy(await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}}));
t.is(addChannel.callCount, 0);
});

test('Publish pre-releases the same channel as regular releases', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
await gitCommits(['feat: initial commit'], {cwd});
await gitTagVersion('v1.0.0', undefined, {cwd});
await gitPush(repositoryUrl, 'master', {cwd});
await gitCheckout('beta', true, {cwd});
await gitCommits(['feat: a feature'], {cwd});
await gitPush(repositoryUrl, 'beta', {cwd});

const config = {
branches: ['master', {name: 'beta', channel: false, prerelease: true}],
repositoryUrl,
};
const options = {
...config,
verifyConditions: stub().resolves(),
verifyRelease: stub().resolves(),
generateNotes: stub().resolves(''),
addChannel: false,
prepare: stub().resolves(),
publish: stub().resolves(),
success: stub().resolves(),
fail: stub().resolves(),
};

const semanticRelease = requireNoCache('..', {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'beta', isPr: false}),
});
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});

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');

await gitCommits(['fix: a fix'], {cwd});
({releases} = await semanticRelease(options, {
cwd,
env: {},
stdout: {write: () => {}},
stderr: {write: () => {}},
}));

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');
});

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});
Expand Down
10 changes: 10 additions & 0 deletions test/utils.test.js
Expand Up @@ -14,6 +14,7 @@ import {
getFirstVersion,
getRange,
makeTag,
isSameChannel,
} from '../lib/utils';

test('extractErrors', t => {
Expand Down Expand Up @@ -178,3 +179,12 @@ test('makeTag', t => {
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 => {
t.true(isSameChannel('next', 'next'));
t.true(isSameChannel(null, undefined));
t.true(isSameChannel(false, undefined));
t.true(isSameChannel('', false));

t.false(isSameChannel('next', false));
});

0 comments on commit 63f51ae

Please sign in to comment.