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

Docs: Update and refactor release workflow recipe #2498

Merged
merged 2 commits into from
Oct 21, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 94 additions & 73 deletions docs/recipes/automate-release-workflow.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,100 @@
# Automate release workflow
<!-- front-matter
id: automate-releases
title: Automate Releases
hide_title: true
sidebar_label: Automate Releases
-->

# Automate Releases

If your project follows a semantic versioning, it may be a good idea to automatize the steps needed to do a release.
Below you have a simple recipe that bumps the project version, commits the changes to git and creates a new tag.

``` javascript

var gulp = require('gulp');
var conventionalChangelog = require('gulp-conventional-changelog');
var conventionalGithubReleaser = require('conventional-github-releaser');
var bump = require('gulp-bump');
var log = require('gulplog');
var git = require('gulp-git');
var fs = require('fs');

gulp.task('changelog', function () {
return gulp.src('CHANGELOG.md', {
buffer: false
})
.pipe(conventionalChangelog({
preset: 'angular' // Or to any other commit message convention you use.
}))
.pipe(gulp.dest('./'));
});

gulp.task('github-release', function(done) {
conventionalGithubReleaser({
type: "oauth",
token: 'abcdefghijklmnopqrstuvwxyz1234567890' // change this to your own GitHub token or use an environment variable
}, {
preset: 'angular' // Or to any other commit message convention you use.
}, done);
});

gulp.task('bump-version', function () {
// We hardcode the version change type to 'patch' but it may be a good idea to
// use minimist (https://www.npmjs.com/package/minimist) to determine with a
// command argument whether you are doing a 'major', 'minor' or a 'patch' change.
return gulp.src(['./bower.json', './package.json'])
.pipe(bump({type: "patch"}).on('error', log.error))
.pipe(gulp.dest('./'));
});

gulp.task('commit-changes', function () {
return gulp.src('.')
.pipe(git.add())
.pipe(git.commit('[Prerelease] Bumped version number'));
});

gulp.task('push-changes', function (done) {
git.push('origin', 'master', done);
});

gulp.task('create-new-tag', function (done) {
var version = getPackageJsonVersion();
git.tag(version, 'Created Tag for version: ' + version, function (error) {
if (error) {
return done(error);
}
git.push('origin', 'master', {args: '--tags'}, done);
The recipe below bumps the project version, commits the changes to git and creates a new GitHub release.

For publishing a GitHub release you'll need to [create a personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) and add it to your project. However, we don't want to commit it, so we'll use [`dotenv`](https://www.npmjs.com/package/dotenv) to load it from a git-ignored `.env` file:

```
GH_TOKEN=ff34885...
```

Don't forget to add `.env` to your `.gitignore`.

Next, install all the necessary dependencies for this recipe:

```sh
npm install --save-dev conventional-recommended-bump conventional-changelog-cli conventional-github-releaser dotenv execa
```

Based on your environment, setup and preferences, your release workflow might look something like this:

``` js
const gulp = require('gulp');
const conventionalRecommendedBump = require('conventional-recommended-bump');
const conventionalGithubReleaser = require('conventional-github-releaser');
const execa = require('execa');
const fs = require('fs');
const { promisify } = require('util');
const dotenv = require('dotenv');

// load environment variables
const result = dotenv.config();

if (result.error) {
throw result.error;
}

// Conventional Changelog preset
const preset = 'angular';
// print output of commands into the terminal
const stdio = 'inherit';

async function bumpVersion() {
// get recommended version bump based on commits
const { releaseType } = await promisify(conventionalRecommendedBump)({ preset });
// bump version without committing and tagging
await execa('npm', ['version', releaseType, '--no-git-tag-version'], {
stdio,
});
}

async function changelog() {
await execa(
'npx',
[
'conventional-changelog',
'--preset',
preset,
'--infile',
'CHANGELOG.md',
'--same-file',
],
{ stdio }
);
}

async function commitTagPush() {
// even though we could get away with "require" in this case, we're taking the safe route
// because "require" caches the value, so if we happen to use "require" again somewhere else
// we wouldn't get the current value, but the value of the last time we called "require"
const { version } = JSON.parse(await promisify(fs.readFile)('package.json'));
const commitMsg = `chore: release ${version}`;
await execa('git', ['add', '.'], { stdio });
await execa('git', ['commit', '--message', commitMsg], { stdio });
await execa('git', ['tag', `v${version}`], { stdio });
await execa('git', ['push', '--follow-tags'], { stdio });
}

function getPackageJsonVersion () {
// We parse the json file instead of using require because require caches
// multiple calls so the version number won't be updated
return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
};
});

gulp.task('release', gulp.series(
'bump-version',
'changelog',
'commit-changes',
'push-changes',
'create-new-tag',
'github-release'
));
function githubRelease(done) {
conventionalGithubReleaser(
{ type: 'oauth', token: process.env.GH_TOKEN },
{ preset },
done
);
}

exports.release = gulp.series(
bumpVersion,
changelog,
commitTagPush,
githubRelease
);
```