Skip to content

Commit 1216774

Browse files
committed
[+] DevOps - Automatically send the release note on Slack after a release.
1 parent fc29f7f commit 1216774

File tree

7 files changed

+438
-95
lines changed

7 files changed

+438
-95
lines changed

.env-example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DEVOPS_SLACK_CHANNEL=
2+
DEVOPS_SLACK_TOKEN=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ build/
4040
.rvmrc
4141

4242
node_modules/
43+
.env

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44
### Added
55
- Configurations - Users can specify the directory for Forest Smart Implementation.
6+
- DevOps - Automatically send the release note on Slack after a release.
67

78
### Changed
89
- Readme - Add a community section.

gulpfile.js

Lines changed: 0 additions & 89 deletions
This file was deleted.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "forest-rails",
3-
"version": "0.0.0",
3+
"version": "3.2.0",
44
"description": "The official Rails liana for Forest.",
5-
"main": "gulpfile.js",
65
"directories": {
76
"test": "test"
87
},
98
"scripts": {
10-
"test": "echo \"Error: no test specified\" && exit 1"
9+
"test": "echo \"Error: no test specified\" && exit 1",
10+
"release": "node ./scripts/release.js"
1111
},
1212
"repository": {
1313
"type": "git",
@@ -20,7 +20,8 @@
2020
},
2121
"homepage": "https://github.com/ForestAdmin/forest-rails#readme",
2222
"devDependencies": {
23-
"gulp": "3.9.1",
23+
"@forestadmin/devops": "2.0.0",
24+
"dotenv": "8.1.0",
2425
"moment": "^2.20.1",
2526
"semver": "5.3.0",
2627
"simple-git": "1.65.0"

scripts/release.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
'use strict';
2+
require('dotenv').config();
3+
const moment = require('moment');
4+
const fs = require('fs');
5+
const simpleGit = require('simple-git')();
6+
const semver = require('semver');
7+
const exec = require('child_process').execSync;
8+
const { ReleaseNoteManager } = require('@forestadmin/devops');
9+
10+
const { DEVOPS_SLACK_TOKEN, DEVOPS_SLACK_CHANNEL } = process.env;
11+
const OPTIONS = { releaseIcon: '🌱', withVersion: true };
12+
13+
const BRANCH_MASTER = 'master';
14+
const BRANCH_DEVEL = 'devel';
15+
const PRERELEASE_OPTIONS = ['premajor', 'preminor', 'prepatch', 'prerelease'];
16+
const RELEASE_OPTIONS = ['major', 'minor', 'patch', ...PRERELEASE_OPTIONS];
17+
18+
function build() {
19+
const parseCommandLineArguments = () => {
20+
let releaseType = 'patch';
21+
let prereleaseTag;
22+
23+
if (process.argv) {
24+
if (process.argv[2]) {
25+
const option = process.argv[2].replace('--', '');
26+
if (RELEASE_OPTIONS.includes(option)) {
27+
releaseType = option;
28+
}
29+
}
30+
if (process.argv[3]) {
31+
const option = process.argv[3].replace('--', '');
32+
prereleaseTag = option;
33+
} else if (PRERELEASE_OPTIONS.includes(releaseType)) {
34+
prereleaseTag = 'beta';
35+
}
36+
}
37+
38+
return { releaseType, prereleaseTag };
39+
};
40+
41+
const gitPullErrorCatcher = (error) => {
42+
if (error) {
43+
throw new Error('Git Pull Error');
44+
}
45+
};
46+
47+
const getBranchLabel = (branchName) => branchName || 'current branch';
48+
49+
const pullAndCommitChanges = async (newPackageFile, newVersionFile, newChanges, commitMessage, branchName) => {
50+
if (branchName) {
51+
await simpleGit.checkout(branchName);
52+
}
53+
54+
return simpleGit
55+
.pull(gitPullErrorCatcher)
56+
.exec(() => { console.log(`Pull ${getBranchLabel(branchName)} done.`); })
57+
.exec(() => {
58+
fs.writeFileSync('lib/forest_liana/version.rb', newVersionFile);
59+
fs.writeFileSync('package.json', newPackageFile);
60+
fs.writeFileSync('CHANGELOG.md', newChanges);
61+
})
62+
.add(['CHANGELOG.md', 'package.json'])
63+
.commit(commitMessage)
64+
.push()
65+
.exec(() => { console.log(`Commit Release on ${getBranchLabel(branchName)} done.`); });
66+
};
67+
68+
const addTagToGit = (tag, branchName) => simpleGit
69+
.addTag(tag)
70+
.push('origin', tag)
71+
.exec(() => { console.log(`Tag ${tag} on ${getBranchLabel(branchName)} done.`); });
72+
73+
const mergeDevelOntoMaster = () => simpleGit
74+
.checkout(BRANCH_MASTER)
75+
.pull(gitPullErrorCatcher)
76+
.exec(() => { console.log(`Pull ${BRANCH_MASTER} done.`); })
77+
.mergeFromTo(BRANCH_DEVEL, BRANCH_MASTER)
78+
.exec(() => { console.log(`Merge ${BRANCH_DEVEL} on ${BRANCH_MASTER} done.`); })
79+
.push();
80+
81+
let releaseType = 'patch';
82+
let prereleaseTag;
83+
84+
({ releaseType, prereleaseTag } = parseCommandLineArguments());
85+
86+
// VERSION (version.rb)
87+
let versionFile = fs.readFileSync('lib/forest_liana/version.rb').toString().split('\n');
88+
let version = versionFile[1].match(/\w*VERSION = "(.*)"/)[1];
89+
version = semver.inc(version, releaseType, prereleaseTag);
90+
versionFile[1] = ` VERSION = "${version}"`;
91+
const newVersionFile = versionFile.join('\n');
92+
93+
// VERSION (package.json)
94+
const packageContents = fs.readFileSync('./package.json', 'utf8');
95+
const packageJson = JSON.parse(packageContents);
96+
packageJson.version = version;
97+
const newPackageFile = JSON.stringify(packageJson, null, 2);
98+
99+
// CHANGELOG
100+
const changes = fs.readFileSync('CHANGELOG.md').toString().split('\n');
101+
const today = moment().format('YYYY-MM-DD');
102+
103+
const index = changes.indexOf('## [Unreleased]') + 1;
104+
changes.splice(index, 0, `\n## RELEASE ${version} - ${today}`);
105+
const newChanges = changes.join('\n');
106+
107+
const commitMessage = `Release ${version}`;
108+
const tag = `v${version}`;
109+
110+
return new Promise((resolve) => {
111+
simpleGit.status((_, statusSummary) => {
112+
const currentBranch = statusSummary.current;
113+
114+
let promise;
115+
if (prereleaseTag || /v\d+(\.\d+)?/i.test(currentBranch)) {
116+
promise = pullAndCommitChanges(newPackageFile, newVersionFile, newChanges, commitMessage, currentBranch)
117+
.then(() => addTagToGit(tag, currentBranch));
118+
} else {
119+
promise = pullAndCommitChanges(newPackageFile, newVersionFile, newChanges, commitMessage, BRANCH_DEVEL)
120+
.then(() => mergeDevelOntoMaster())
121+
.then(() => addTagToGit(tag, BRANCH_MASTER))
122+
.then(() => simpleGit.checkout(BRANCH_DEVEL));
123+
}
124+
125+
promise.catch(() => {});
126+
127+
resolve(promise);
128+
});
129+
});
130+
}
131+
132+
return build()
133+
.then(() => new ReleaseNoteManager(DEVOPS_SLACK_TOKEN, DEVOPS_SLACK_CHANNEL, OPTIONS).create())

0 commit comments

Comments
 (0)