Simple email application for Android. Original source code: https://framagit.org/dystopia-project/simple-email
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

175 lines
5.8 KiB

const {template, pick} = require('lodash');
const marked = require('marked');
const TerminalRenderer = require('marked-terminal');
const envCi = require('env-ci');
const hookStd = require('hook-std');
const pkg = require('./package.json');
const hideSensitive = require('./lib/hide-sensitive');
const getConfig = require('./lib/get-config');
const verify = require('./lib/verify');
const getNextVersion = require('./lib/get-next-version');
const getCommits = require('./lib/get-commits');
const getLastRelease = require('./lib/get-last-release');
const {extractErrors} = require('./lib/utils');
const getGitAuthUrl = require('./lib/get-git-auth-url');
const getLogger = require('./lib/get-logger');
const {fetch, verifyAuth, isBranchUpToDate, gitHead: getGitHead, tag, push} = require('./lib/git');
const getError = require('./lib/get-error');
const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants');
marked.setOptions({renderer: new TerminalRenderer()});
async function run(context, plugins) {
const {cwd, env, options, logger} = context;
const {isCi, branch: ciBranch, isPr} = envCi({env, cwd});
if (!isCi && !options.dryRun && !options.noCi) {
logger.log('This run was not triggered in a known CI environment, running in dry-run mode.');
options.dryRun = true;
} else {
// When running on CI, set the commits author and commiter info and prevent the `git` CLI to prompt for username/password. See #703.
Object.assign(env, {
GIT_AUTHOR_NAME: COMMIT_NAME,
GIT_AUTHOR_EMAIL: COMMIT_EMAIL,
GIT_COMMITTER_NAME: COMMIT_NAME,
GIT_COMMITTER_EMAIL: COMMIT_EMAIL,
...env,
GIT_ASKPASS: 'echo',
GIT_TERMINAL_PROMPT: 0,
});
}
if (isCi && isPr && !options.noCi) {
logger.log("This run was triggered by a pull request and therefore a new version won't be published.");
return false;
}
if (ciBranch !== options.branch) {
logger.log(
`This test run was triggered on the branch ${ciBranch}, while semantic-release is configured to only publish from ${
options.branch
}, therefore a new version won’t be published.`
);
return false;
}
logger.success(`Run automated release from branch ${ciBranch}`);
await verify(context);
options.repositoryUrl = await getGitAuthUrl(context);
try {
try {
await verifyAuth(options.repositoryUrl, options.branch, {cwd, env});
} catch (error) {
if (!(await isBranchUpToDate(options.branch, {cwd, env}))) {
logger.log(
`The local branch ${options.branch} is behind the remote one, therefore a new version won't be published.`
);
return false;
}
throw error;
}
} catch (error) {
logger.error(`The command "${error.cmd}" failed with the error message ${error.stderr}.`);
throw getError('EGITNOPERMISSION', {options});
}
logger.success(`Allowed to push to the Git repository`);
await plugins.verifyConditions(context);
await fetch(options.repositoryUrl, {cwd, env});
context.lastRelease = await getLastRelease(context);
context.commits = await getCommits(context);
const nextRelease = {type: await plugins.analyzeCommits(context), gitHead: await getGitHead({cwd, env})};
if (!nextRelease.type) {
logger.log('There are no relevant changes, so no new version is released.');
return false;
}
context.nextRelease = nextRelease;
nextRelease.version = getNextVersion(context);
nextRelease.gitTag = template(options.tagFormat)({version: nextRelease.version});
await plugins.verifyRelease(context);
nextRelease.notes = await plugins.generateNotes(context);
if (options.dryRun) {
logger.log(`Release note for version ${nextRelease.version}:`);
if (nextRelease.notes) {
context.stdout.write(marked(nextRelease.notes));
}
} else {
await plugins.prepare(context);
// Create the tag before calling the publish plugins as some require the tag to exists
await tag(nextRelease.gitTag, {cwd, env});
await push(options.repositoryUrl, options.branch, {cwd, env});
logger.success(`Created tag ${nextRelease.gitTag}`);
context.releases = await plugins.publish(context);
await plugins.success(context);
logger.success(`Published release ${nextRelease.version}`);
}
return pick(context, ['lastRelease', 'commits', 'nextRelease', 'releases']);
}
function logErrors({logger, stderr}, err) {
const errors = extractErrors(err).sort(error => (error.semanticRelease ? -1 : 0));
for (const error of errors) {
if (error.semanticRelease) {
logger.error(`${error.code} ${error.message}`);
if (error.details) {
stderr.write(marked(error.details));
}
} else {
logger.error('An error occurred while running semantic-release: %O', error);
}
}
}
async function callFail(context, plugins, err) {
const errors = extractErrors(err).filter(err => err.semanticRelease);
if (errors.length > 0) {
try {
await plugins.fail({...context, errors});
} catch (error) {
logErrors(context, error);
}
}
}
module.exports = async (opts = {}, {cwd = process.cwd(), env = process.env, stdout, stderr} = {}) => {
const {unhook} = hookStd(
{silent: false, streams: [process.stdout, process.stderr, stdout, stderr].filter(Boolean)},
hideSensitive(env)
);
const context = {cwd, env, stdout: stdout || process.stdout, stderr: stderr || process.stderr};
context.logger = getLogger(context);
context.logger.log(`Running ${pkg.name} version ${pkg.version}`);
try {
const {plugins, options} = await getConfig(context, opts);
context.options = options;
try {
const result = await run(context, plugins);
unhook();
return result;
} catch (error) {
if (!options.dryRun) {
await callFail(context, plugins, error);
}
throw error;
}
} catch (error) {
logErrors(context, error);
unhook();
throw error;
}
};