Skip to content

Commit

Permalink
feat: support branch parameter from [email protected]
Browse files Browse the repository at this point in the history
  • Loading branch information
iamchrismiller authored and pvdlg committed Dec 12, 2018
1 parent 7f80ca3 commit 02b1f6d
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 43 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ When configuring branches permission on a Git hosting service (e.g. [GitHub prot

The message for the release commit is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:

| Parameter | Description |
|---------------|-------------------------------------------------------------------------------------|
| `branch` | The branch from which the release is done. |
| `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. |
| `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. |
| Parameter | Description |
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| `branch` | The branch from which the release is done. |
| `branch.name` | The branch name. |
| `branch.type` | The [type of branch](https://github.com/semantic-release/semantic-release/blob/beta/docs/usage/workflow-configuration.md#branch-types). |
| `branch.channel` | The distribution channel on which to publish releases from this branch. |
| `branch.range` | The range of [semantic versions](https://semver.org) to support on this branch. |
| `branch.prerelease` | The pre-release detonation to append to [semantic versions](https://semver.org) released from this branch. |
| `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. |
| `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. |

**Note**: It is recommended to include `[skip ci]` in the commit message to not trigger a new build. Some CI service support the `[skip ci]` keyword only in the subject of the message.

Expand Down
7 changes: 4 additions & 3 deletions lib/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ module.exports = async (pluginConfig, context) => {
const {
env,
cwd,
options: {branch, repositoryUrl},
branch,
options: {repositoryUrl},
lastRelease,
nextRelease,
logger,
Expand All @@ -39,13 +40,13 @@ module.exports = async (pluginConfig, context) => {
debug('commited files: %o', filesToCommit);
await commit(
message
? template(message)({branch, lastRelease, nextRelease})
? template(message)({branch: branch.name, lastRelease, nextRelease})
: `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}`,
{env, cwd}
);
}

await push(repositoryUrl, branch, {env, cwd});
await push(repositoryUrl, branch.name, {env, cwd});
logger.log('Prepared Git release: %s', nextRelease.gitTag);
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"all": true
},
"peerDependencies": {
"semantic-release": ">=15.4.0 <16.0.0"
"semantic-release": ">=16.0.0-beta <17.0.0"
},
"prettier": {
"printWidth": 120,
Expand Down
26 changes: 14 additions & 12 deletions test/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ test.beforeEach(t => {
});

test('Prepare from a shallow clone', async t => {
const branch = 'master';
const branch = {name: 'master'};
let {cwd, repositoryUrl} = await gitRepo(true);
await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '1.0.0'}");
await outputFile(path.resolve(cwd, 'dist/file.js'), 'Initial content');
await outputFile(path.resolve(cwd, 'dist/file.css'), 'Initial content');
await add('.', {cwd});
await gitCommits(['First'], {cwd});
await gitTagVersion('v1.0.0', undefined, {cwd});
await push(repositoryUrl, branch, {cwd});
await push(repositoryUrl, branch.name, {cwd});
cwd = await gitShallowClone(repositoryUrl);
await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '2.0.0'}");
await outputFile(path.resolve(cwd, 'dist/file.js'), 'Updated content');
Expand All @@ -45,28 +45,29 @@ test('Prepare from a shallow clone', async t => {
};
await t.context.m.prepare(pluginConfig, {
cwd,
options: {repositoryUrl, branch},
branch,
options: {repositoryUrl},
nextRelease,
logger: t.context.logger,
});

t.deepEqual((await gitCommitedFiles('HEAD', {cwd})).sort(), ['dist/file.js', 'package.json'].sort());
const [commit] = await gitGetCommits(undefined, {cwd});
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch}`);
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`);
t.is(commit.body, `${nextRelease.notes}\n`);
t.is(commit.gitTags, `(HEAD -> ${branch})`);
t.is(commit.gitTags, `(HEAD -> ${branch.name})`);
});

test('Prepare from a detached head repository', async t => {
const branch = 'master';
const branch = {name: 'master'};
let {cwd, repositoryUrl} = await gitRepo(true);
await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '1.0.0'}");
await outputFile(path.resolve(cwd, 'dist/file.js'), 'Initial content');
await outputFile(path.resolve(cwd, 'dist/file.css'), 'Initial content');
await add('.', {cwd});
const [{hash}] = await gitCommits(['First'], {cwd});
await gitTagVersion('v1.0.0', undefined, {cwd});
await push(repositoryUrl, branch, {cwd});
await push(repositoryUrl, branch.name, {cwd});
cwd = await gitDetachedHead(repositoryUrl, hash);
await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '2.0.0'}");
await outputFile(path.resolve(cwd, 'dist/file.js'), 'Updated content');
Expand All @@ -79,26 +80,27 @@ test('Prepare from a detached head repository', async t => {
};
await t.context.m.prepare(pluginConfig, {
cwd,
options: {repositoryUrl, branch},
branch,
options: {repositoryUrl},
nextRelease,
logger: t.context.logger,
});

t.deepEqual((await gitCommitedFiles('HEAD', {cwd})).sort(), ['dist/file.js', 'package.json'].sort());
const [commit] = await gitGetCommits(undefined, {cwd});
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch}`);
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`);
t.is(commit.body, `${nextRelease.notes}\n`);
t.is(commit.gitTags, `(HEAD)`);
});

test('Verify authentication only on the fist call', async t => {
const branch = 'master';
const branch = {name: 'master'};
const {cwd, repositoryUrl} = await gitRepo(true);
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
const options = {repositoryUrl, branch, prepare: ['@semantic-release/npm']};
const options = {repositoryUrl, prepare: ['@semantic-release/npm']};

t.notThrows(() => t.context.m.verifyConditions({}, {cwd, options, logger: t.context.logger}));
await t.context.m.prepare({}, {cwd, options: {repositoryUrl, branch}, nextRelease, logger: t.context.logger});
await t.context.m.prepare({}, {cwd, options: {repositoryUrl}, branch, nextRelease, logger: t.context.logger});
});

test('Throw SemanticReleaseError if prepare config is invalid', t => {
Expand Down
54 changes: 32 additions & 22 deletions test/prepare.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ test.beforeEach(t => {
test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.json if they exists and have been changed', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'};
Expand All @@ -28,7 +29,7 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j
await outputFile(pkgLockPath, "{name: 'test-package'}");
await outputFile(shrinkwrapPath, "{name: 'test-package'}");

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify the remote repo has a the version referencing the same commit sha at the local head
const [commit] = await gitGetCommits(undefined, {cwd, env});
Expand All @@ -39,15 +40,16 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j
);
t.is(commit.subject, `chore(release): ${nextRelease.version} [skip ci]`);
t.is(commit.body, `${nextRelease.notes}\n`);
t.is(commit.gitTags, `(HEAD -> ${options.branch})`);
t.is(commit.gitTags, `(HEAD -> ${branch.name})`);
t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 4]);
t.deepEqual(t.context.log.args[1], ['Prepared Git release: %s', nextRelease.gitTag]);
});

test('Exclude CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.json if "assets" is defined without it', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {assets: []};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
Expand All @@ -56,7 +58,7 @@ test('Exclude CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.
await outputFile(path.resolve(cwd, 'package-lock.json'), "{name: 'test-package'}");
await outputFile(path.resolve(cwd, 'npm-shrinkwrap.json'), "{name: 'test-package'}");

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify no files have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []);
Expand All @@ -70,19 +72,20 @@ test.serial('Allow to customize the commit message', async t => {
Last release: \${lastRelease.version}
\${nextRelease.notes}`,
};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {version: 'v1.0.0'};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'};
await outputFile(path.resolve(cwd, 'CHANGELOG.md'), 'Initial CHANGELOG');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify the files that have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']);
// Verify the commit message contains on the new release notes
const [commit] = await gitGetCommits(undefined, {cwd, env});
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${options.branch}`);
t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`);
t.is(commit.body, `Last release: ${lastRelease.version}\n${nextRelease.notes}\n`);
});

Expand All @@ -91,7 +94,8 @@ test('Commit files matching the patterns in "assets"', async t => {
const pluginConfig = {
assets: ['file1.js', '*1.js', ['dir/*.js', '!dir/*.css'], 'file5.js', 'dir2', ['**/*.js', '!**/*.js']],
};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
Expand All @@ -105,7 +109,7 @@ test('Commit files matching the patterns in "assets"', async t => {
await outputFile(path.resolve(cwd, 'dir2/file6.js'), 'Test content');
await outputFile(path.resolve(cwd, 'dir2/file7.css'), 'Test content');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify file2 and file1 have been commited
// file4.js is excluded as no glob matching
Expand All @@ -124,7 +128,8 @@ test('Commit files matching the patterns in "assets" as Objects', async t => {
const pluginConfig = {
assets: ['file1.js', {path: ['dir/*.js', '!dir/*.css']}, {path: 'file5.js'}, 'dir2'],
};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
Expand All @@ -138,7 +143,7 @@ test('Commit files matching the patterns in "assets" as Objects', async t => {
await outputFile(path.resolve(cwd, 'dir2/file6.js'), 'Test content');
await outputFile(path.resolve(cwd, 'dir2/file7.css'), 'Test content');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify file2 and file1 have been commited
// file4.js is excluded as no glob matching
Expand All @@ -155,14 +160,15 @@ test('Commit files matching the patterns in "assets" as Objects', async t => {
test('Commit files matching the patterns in "assets" as single glob', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {assets: 'dist/**/*.js'};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
await outputFile(path.resolve(cwd, 'dist/file1.js'), 'Test content');
await outputFile(path.resolve(cwd, 'dist/file2.css'), 'Test content');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['dist/file1.js']);
t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]);
Expand All @@ -171,21 +177,23 @@ test('Commit files matching the patterns in "assets" as single glob', async t =>
test('Commit files matching the patterns in "assets", including dot files', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {assets: 'dist'};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
await outputFile(path.resolve(cwd, 'dist/.dotfile'), 'Test content');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['dist/.dotfile']);
t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]);
});

test('Set the commit author and committer name/email based on environment variables', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {
GIT_AUTHOR_NAME: 'author name',
GIT_AUTHOR_EMAIL: 'author email',
Expand All @@ -196,7 +204,7 @@ test('Set the commit author and committer name/email based on environment variab
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'};
await outputFile(path.resolve(cwd, 'CHANGELOG.md'), 'Initial CHANGELOG');

await prepare({}, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare({}, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify the files that have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']);
Expand All @@ -211,13 +219,14 @@ test('Set the commit author and committer name/email based on environment variab
test('Skip negated pattern if its alone in its group', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {assets: ['!**/*', 'file.js']};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'};
await outputFile(path.resolve(cwd, 'file.js'), 'Test content');

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['file.js']);
t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]);
Expand All @@ -226,12 +235,13 @@ test('Skip negated pattern if its alone in its group', async t => {
test('Skip commit if there is no files to commit', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const pluginConfig = {};
const options = {repositoryUrl, branch: 'master'};
const branch = {name: 'master'};
const options = {repositoryUrl};
const env = {};
const lastRelease = {};
const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'};

await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});
await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger});

// Verify the files that have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []);
Expand Down

0 comments on commit 02b1f6d

Please sign in to comment.