Skip to content

Commit

Permalink
feat: allow to release from local machine
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Jan 2, 2018
1 parent 5cc62e4 commit 5bc46a0
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -168,6 +168,7 @@ semantic-release
These options are currently available:
- `branch`: The branch on which releases should happen. Default: `'master'`
- `repositoryUrl`: The git repository URL. Default: `repository` property in `package.json` or git origin url. Any valid git url format is supported (See [Git protocols](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols)). If the [Github plugin](https://github.com/semantic-release/github) is used the URL must be a valid Github URL that include the `owner`, the `repository` name and the `host`. The Github shorthand URL is not supported.
- `no-ci`: Skip Continuous Integration environment verifications, allowing to make releases from a local machine
- `dry-run`: Dry-run mode, skip publishing, print next version and release notes
- `extends`: Array of module or files path containing a shareable configuration. Options defined via CLI or in the `release` property will take precedence over the one defined in a shareable configuration.
- `debug`: Output debugging information
Expand Down Expand Up @@ -269,7 +270,8 @@ If you run `npm run semantic-release` locally a dry run gets performed, which lo

### Can I run this on my own machine rather than on a CI server?

Of course you can, but this doesn’t necessarily mean you should. Running your tests on an independent machine before releasing software is a crucial part of this workflow. Also it is a pain to set this up locally, with tokens lying around and everything. That said, you can run the scripts with `--debug=false` explicitly. You have to export `GH_TOKEN=<your_token>` and `NPM_TOKEN=<your_other_token>`.
Yes, you can by explicitly setting the [`--no-ci` CLI option](#options), but this doesn’t necessarily mean you should. Running your tests on an independent machine before releasing software is a crucial part of this workflow.
You will need to set all necessary authentication token (like `GH_TOKEN` and `NPM_TOKEN`) on your local machine.

### Can I manually trigger the release of a specific version?

Expand Down
4 changes: 4 additions & 0 deletions cli.js
Expand Up @@ -27,6 +27,10 @@ module.exports = async () => {
)
.option('--generate-notes <path>', 'Path or package name for the generateNotes plugin')
.option('--publish <paths>', 'Comma separated list of paths or packages name for the publish plugin(s)', list)
.option(
'--no-ci',
'Skip Continuous Integration environment verifications, allowing to make releases from a local machine'
)
.option('--debug', 'Output debugging information')
.option(
'-d, --dry-run',
Expand Down
6 changes: 3 additions & 3 deletions index.js
Expand Up @@ -10,13 +10,13 @@ const {gitHead: getGitHead, isGitRepo} = require('./lib/git');
module.exports = async opts => {
const {isCi, branch, isPr} = envCi();

if (!isCi && !opts.dryRun) {
if (!isCi && !opts.dryRun && !opts.noCi) {
logger.log('This run was not triggered in a known CI environment, running in dry-run mode.');
opts.dryRun = true;
}

if (isCi && isPr) {
logger.log('This run was triggered by a pull request and therefore a new version wont be published.');
if (isCi && isPr && !opts.noCi) {
logger.log("This run was triggered by a pull request and therefore a new version won't be published.");
return;
}

Expand Down
56 changes: 54 additions & 2 deletions test/index.test.js
Expand Up @@ -209,7 +209,7 @@ test.serial('Dry-run skips publish', async t => {
t.is(publish.callCount, 0);
});

test.serial('Force a dry-run if not on a CI', async t => {
test.serial('Force a dry-run if not on a CI and ignore "noCi" is not explicitly set', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
// Add commits to the master branch
Expand Down Expand Up @@ -257,6 +257,58 @@ test.serial('Force a dry-run if not on a CI', async t => {
t.is(publish.callCount, 0);
});

test.serial('Allow local releases with "noCi" option', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
// Add commits to the master branch
let commits = await gitCommits(['First']);
// Create the tag corresponding to version 1.0.0
await gitTagVersion('v1.0.0');
// Add new commits to the master branch
commits = (await gitCommits(['Second'])).concat(commits);

const lastRelease = {version: '1.0.0', gitHead: commits[commits.length - 1].hash, gitTag: 'v1.0.0'};
const nextRelease = {type: 'major', version: '2.0.0', gitHead: await getGitHead(), gitTag: 'v2.0.0'};
const notes = 'Release notes';

const verifyConditions = stub().resolves();
const getLastRelease = stub().resolves(lastRelease);
const analyzeCommits = stub().resolves(nextRelease.type);
const verifyRelease = stub().resolves();
const generateNotes = stub().resolves(notes);
const publish = stub().resolves();

const options = {
noCi: true,
branch: 'master',
repositoryUrl: 'git@hostname.com:owner/module.git',
verifyConditions,
getLastRelease,
analyzeCommits,
verifyRelease,
generateNotes,
publish,
};

const semanticRelease = proxyquire('..', {
'./lib/logger': t.context.logger,
'env-ci': () => ({isCi: false, branch: 'master', isPr: true}),
});
t.truthy(await semanticRelease(options));

t.not(t.context.log.args[0][0], 'This run was not triggered in a known CI environment, running in dry-run mode.');
t.not(
t.context.log.args[0][0],
"This run was triggered by a pull request and therefore a new version won't be published."
);
t.is(verifyConditions.callCount, 1);
t.is(getLastRelease.callCount, 1);
t.is(analyzeCommits.callCount, 1);
t.is(verifyRelease.callCount, 1);
t.is(generateNotes.callCount, 1);
t.is(publish.callCount, 1);
});

test.serial('Accept "undefined" values for the "getLastRelease" and "generateNotes" plugins', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
Expand Down Expand Up @@ -333,7 +385,7 @@ test.serial('Returns falsy value if triggered by a PR', async t => {
t.falsy(await semanticRelease({repositoryUrl: 'git@hostname.com:owner/module.git'}));
t.is(
t.context.log.args[0][0],
'This run was triggered by a pull request and therefore a new version wont be published.'
"This run was triggered by a pull request and therefore a new version won't be published."
);
});

Expand Down

0 comments on commit 5bc46a0

Please sign in to comment.