Skip to content

Commit

Permalink
feat: allow 'git commit' and 'git push' during the prepare and/or pub…
Browse files Browse the repository at this point in the history
…lish step
  • Loading branch information
jfairley committed Jan 26, 2019
1 parent f720fb9 commit 78fb525
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 51 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -22,7 +22,7 @@
.LSOverride

# Icon must end with two \r
Icon
Icon

# Thumbnails
._*
Expand Down Expand Up @@ -129,3 +129,4 @@ $RECYCLE.BIN/

package-lock.json
yarn.lock
.idea/
89 changes: 73 additions & 16 deletions index.js
@@ -1,31 +1,88 @@
const {defaultTo, castArray} = require('lodash');
const {defaultTo, castArray, isNil} = require('lodash');
const verifyGit = require('./lib/verify');
const prepareGit = require('./lib/prepare');
const commitAndPushGit = require('./lib/commit-and-push');

let verified;
let prepareVerified;
let publishVerified;

function verifyConditions(pluginConfig, context) {
function isPrepareConfigured(pluginConfig, context) {
return (
!isPublishConfigured(pluginConfig, context) ||
!isNil(pluginConfig.prepare) ||
castArray(context.options.prepare).some(config => config && config.path === '@semantic-release/git')
);
}

function isPublishConfigured(pluginConfig, context) {
return (
!isNil(pluginConfig.publish) ||
castArray(context.options.publish).some(config => config && config.path === '@semantic-release/git')
);
}

function resolvePluginConfig(step, pluginConfig, context) {
// If the plugin config is defined under its intended step, prefer that
if (pluginConfig[step]) {
pluginConfig = pluginConfig[step];
}

// If the Git prepare plugin or publish plugin is used and has `assets` or `message` configured, validate them now in order to prevent any release if the configuration is wrong
const {options} = context;
// If the Git prepare plugin is used and has `assets` or `message` configured, validate them now in order to prevent any release if the configuration is wrong
if (options.prepare) {
const preparePlugin =
castArray(options.prepare).find(config => config.path && config.path === '@semantic-release/git') || {};
if (options[step]) {
const stepPlugin =
castArray(options[step]).find(config => config.path && config.path === '@semantic-release/git') || {};

pluginConfig.assets = defaultTo(pluginConfig.assets, stepPlugin.assets);
pluginConfig.message = defaultTo(pluginConfig.message, stepPlugin.message);
}

pluginConfig.assets = defaultTo(pluginConfig.assets, preparePlugin.assets);
pluginConfig.message = defaultTo(pluginConfig.message, preparePlugin.message);
return pluginConfig;
}

function verifyConditions(pluginConfig, context) {
const prepareConfig = resolvePluginConfig('prepare', pluginConfig, context);
if (prepareConfig) {
verifyGit(prepareConfig);
prepareVerified = true;
}

verifyGit(pluginConfig);
verified = true;
const publishConfig = resolvePluginConfig('publish', pluginConfig, context);
if (publishConfig) {
verifyGit(publishConfig);
publishVerified = true;
}
}

async function prepare(pluginConfig, context) {
if (!verified) {
if (!isPrepareConfigured(pluginConfig, context)) {
// If a 'publish' config is specifically called out and 'prepare' is not, ignore this step
return;
}

pluginConfig = resolvePluginConfig('prepare', pluginConfig, context);

if (!prepareVerified) {
verifyGit(pluginConfig);
prepareVerified = true;
}

await commitAndPushGit(pluginConfig, context);
}

async function publish(pluginConfig, context) {
if (!isPublishConfigured(pluginConfig, context)) {
// If a 'publish' config isn't specifically called out, ignore this step
return;
}

pluginConfig = resolvePluginConfig('publish', pluginConfig, context);

if (!publishVerified) {
verifyGit(pluginConfig);
verified = true;
publishVerified = true;
}

await prepareGit(pluginConfig, context);
await commitAndPushGit(pluginConfig, context);
}

module.exports = {verifyConditions, prepare};
module.exports = {verifyConditions, prepare, publish};
File renamed without changes.
22 changes: 11 additions & 11 deletions test/prepare.test.js → test/commit-and-push.test.js
Expand Up @@ -2,7 +2,7 @@ import path from 'path';
import test from 'ava';
import {outputFile} from 'fs-extra';
import {stub} from 'sinon';
import prepare from '../lib/prepare';
import commitAndPush from '../lib/commit-and-push';
import {gitRepo, gitGetCommits, gitCommitedFiles} from './helpers/git-utils';

test.beforeEach(t => {
Expand All @@ -28,7 +28,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 commitAndPush(pluginConfig, {cwd, env, options, 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 Down Expand Up @@ -56,7 +56,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 commitAndPush(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});

// Verify no files have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []);
Expand All @@ -76,7 +76,7 @@ Last release: \${lastRelease.version}
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 commitAndPush(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});

// Verify the files that have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']);
Expand Down Expand Up @@ -105,7 +105,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 commitAndPush(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});

// Verify file2 and file1 have been commited
// file4.js is excluded as no glob matching
Expand Down Expand Up @@ -138,7 +138,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 commitAndPush(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});

// Verify file2 and file1 have been commited
// file4.js is excluded as no glob matching
Expand All @@ -162,7 +162,7 @@ test('Commit files matching the patterns in "assets" as single glob', async t =>
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 commitAndPush(pluginConfig, {cwd, env, options, 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 @@ -177,7 +177,7 @@ test('Commit files matching the patterns in "assets", including dot files', asyn
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 commitAndPush(pluginConfig, {cwd, env, options, 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]);
Expand All @@ -196,7 +196,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 commitAndPush({}, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger});

// Verify the files that have been commited
t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']);
Expand All @@ -217,7 +217,7 @@ test('Skip negated pattern if its alone in its group', async t => {
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 commitAndPush(pluginConfig, {cwd, env, options, 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 @@ -231,7 +231,7 @@ test('Skip commit if there is no files to commit', async t => {
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 commitAndPush(pluginConfig, {cwd, env, options, 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 78fb525

Please sign in to comment.