Installing dependencies.
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
const [homepage] = pkg.homepage.split('#');
|
||||
const linkify = file => `${homepage}/blob/master/${file}`;
|
||||
|
||||
module.exports = {
|
||||
EINVALIDASSETS: ({assets}) => ({
|
||||
message: 'Invalid `assets` option.',
|
||||
details: `The [assets option](${linkify(
|
||||
'README.md#assets'
|
||||
)}) option must be an \`Array\` of \`Strings\` or \`Objects\` with a \`path\` property.
|
||||
|
||||
Your configuration for the \`assets\` option is \`${assets}\`.`,
|
||||
}),
|
||||
EINVALIDMESSAGE: ({message}) => ({
|
||||
message: 'Invalid `message` option.',
|
||||
details: `The [message option](${linkify('README.md#message')}) option, if defined, must be a non empty \`String\`.
|
||||
|
||||
Your configuration for the \`successComment\` option is \`${message}\`.`,
|
||||
}),
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
const SemanticReleaseError = require('@semantic-release/error');
|
||||
const ERROR_DEFINITIONS = require('./definitions/errors');
|
||||
|
||||
module.exports = (code, ctx) => {
|
||||
const {message, details} = ERROR_DEFINITIONS[code](ctx);
|
||||
return new SemanticReleaseError(message, code, details);
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
const execa = require('execa');
|
||||
const debug = require('debug')('semantic-release:git');
|
||||
|
||||
/**
|
||||
* Retrieve the list of files modified on the local repository.
|
||||
*
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @return {Array<String>} Array of modified files path.
|
||||
*/
|
||||
async function getModifiedFiles(execaOptions) {
|
||||
return (await execa('git', ['ls-files', '-m', '-o'], execaOptions)).stdout
|
||||
.split('\n')
|
||||
.map(file => file.trim())
|
||||
.filter(file => Boolean(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list of file to the Git index. `.gitignore` will be ignored.
|
||||
*
|
||||
* @param {Array<String>} files Array of files path to add to the index.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function add(files, execaOptions) {
|
||||
const shell = await execa('git', ['add', '--force', '--ignore-errors', ...files], {...execaOptions, reject: false});
|
||||
debug('add file to git index', shell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit to the local repository.
|
||||
*
|
||||
* @param {String} message Commit message.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @throws {Error} if the commit failed.
|
||||
*/
|
||||
async function commit(message, execaOptions) {
|
||||
await execa('git', ['commit', '-m', message], execaOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to the remote repository.
|
||||
*
|
||||
* @param {String} origin The remote repository URL.
|
||||
* @param {String} branch The branch to push.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function push(origin, branch, execaOptions) {
|
||||
await execa('git', ['push', '--tags', origin, `HEAD:${branch}`], execaOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HEAD sha.
|
||||
*
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @return {String} The sha of the head commit on the local repository
|
||||
*/
|
||||
async function gitHead(execaOptions) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOptions)).stdout;
|
||||
}
|
||||
|
||||
module.exports = {getModifiedFiles, add, gitHead, commit, push};
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
const {isPlainObject, isArray, template, castArray, uniq} = require('lodash');
|
||||
const micromatch = require('micromatch');
|
||||
const dirGlob = require('dir-glob');
|
||||
const pReduce = require('p-reduce');
|
||||
const debug = require('debug')('semantic-release:git');
|
||||
const resolveConfig = require('./resolve-config');
|
||||
const {getModifiedFiles, add, commit, push} = require('./git');
|
||||
|
||||
/**
|
||||
* Prepare a release commit including configurable files.
|
||||
*
|
||||
* @param {Object} pluginConfig The plugin configuration.
|
||||
* @param {String|Array<String>} [pluginConfig.assets] Files to include in the release commit. Can be files path or globs.
|
||||
* @param {String} [pluginConfig.message] The message for the release commit.
|
||||
* @param {Object} context semantic-release context.
|
||||
* @param {Object} context.options `semantic-release` configuration.
|
||||
* @param {Object} context.lastRelease The last release.
|
||||
* @param {Object} context.nextRelease The next release.
|
||||
* @param {Object} logger Global logger.
|
||||
*/
|
||||
module.exports = async (pluginConfig, context) => {
|
||||
const {
|
||||
env,
|
||||
cwd,
|
||||
branch,
|
||||
options: {repositoryUrl},
|
||||
lastRelease,
|
||||
nextRelease,
|
||||
logger,
|
||||
} = context;
|
||||
const {message, assets} = resolveConfig(pluginConfig, logger);
|
||||
|
||||
const modifiedFiles = await getModifiedFiles({env, cwd});
|
||||
|
||||
const filesToCommit = uniq(
|
||||
await pReduce(
|
||||
assets.map(asset => (!isArray(asset) && isPlainObject(asset) ? asset.path : asset)),
|
||||
async (result, asset) => {
|
||||
const glob = castArray(asset);
|
||||
let nonegate;
|
||||
// Skip solo negated pattern (avoid to include every non js file with `!**/*.js`)
|
||||
if (glob.length <= 1 && glob[0].startsWith('!')) {
|
||||
nonegate = true;
|
||||
debug(
|
||||
'skipping the negated glob %o as its alone in its group and would retrieve a large amount of files ',
|
||||
glob[0]
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
...result,
|
||||
...micromatch(modifiedFiles, await dirGlob(glob, {cwd}), {dot: true, nonegate, cwd, expand: true}),
|
||||
];
|
||||
},
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
if (filesToCommit.length > 0) {
|
||||
logger.log('Found %d file(s) to commit', filesToCommit.length);
|
||||
await add(filesToCommit, {env, cwd});
|
||||
debug('commited files: %o', filesToCommit);
|
||||
await commit(
|
||||
message
|
||||
? template(message)({branch: branch.name, lastRelease, nextRelease})
|
||||
: `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}`,
|
||||
{env, cwd}
|
||||
);
|
||||
await push(repositoryUrl, branch.name, {env, cwd});
|
||||
logger.log('Prepared Git release: %s', nextRelease.gitTag);
|
||||
}
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
const {isNil, castArray} = require('lodash');
|
||||
|
||||
module.exports = ({assets, message}) => ({
|
||||
assets: isNil(assets)
|
||||
? ['CHANGELOG.md', 'package.json', 'package-lock.json', 'npm-shrinkwrap.json']
|
||||
: assets
|
||||
? castArray(assets)
|
||||
: assets,
|
||||
message,
|
||||
});
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
const {isString, isNil, isArray, isPlainObject} = require('lodash');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const getError = require('./get-error');
|
||||
const resolveConfig = require('./resolve-config');
|
||||
|
||||
const isNonEmptyString = value => isString(value) && value.trim();
|
||||
const isStringOrStringArray = value => isNonEmptyString(value) || (isArray(value) && value.every(isNonEmptyString));
|
||||
const isArrayOf = validator => array => isArray(array) && array.every(value => validator(value));
|
||||
const canBeDisabled = validator => value => value === false || validator(value);
|
||||
|
||||
const VALIDATORS = {
|
||||
assets: canBeDisabled(
|
||||
isArrayOf(asset => isStringOrStringArray(asset) || (isPlainObject(asset) && isStringOrStringArray(asset.path)))
|
||||
),
|
||||
message: isNonEmptyString,
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the commit `message` format and the `assets` option configuration:
|
||||
* - The commit `message`, is defined, must a non empty `String`.
|
||||
* - The `assets` configuration must be an `Array` of `String` (file path) or `false` (to disable).
|
||||
*
|
||||
* @param {Object} pluginConfig The plugin configuration.
|
||||
* @param {String|Array<String|Object>} [pluginConfig.assets] Files to include in the release commit. Can be files path or globs.
|
||||
* @param {String} [pluginConfig.message] The commit message for the release.
|
||||
*/
|
||||
module.exports = pluginConfig => {
|
||||
const options = resolveConfig(pluginConfig);
|
||||
|
||||
const errors = Object.entries(options).reduce(
|
||||
(errors, [option, value]) =>
|
||||
!isNil(value) && !VALIDATORS[option](value)
|
||||
? [...errors, getError(`EINVALID${option.toUpperCase()}`, {[option]: value})]
|
||||
: errors,
|
||||
[]
|
||||
);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateError(errors);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user