Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow 'git commit' and 'git push' during the prepare and/or publish step #122

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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