Skip to content

Commit 002dd1f

Browse files
feat: Enable GitHub actions. (#150)
* feat: Enable GitHub actions. Changes: 1. Added workflow file 2. Added scripts to update the changelog in cli-core 3. Added the actions to bump the version and release to GitHub * Updated the github email and username * Code refactoring - Removing the unneccesary runs * Added changes to trigger cli workflow with changelog input * Fixing the json issue and added versionType * Changed the naming convention as per the GitHub actions * Update release.yml * Added missing changes * Added pre release tag and some conditions * Refactored the code and addressed the review comments * Added semantic release output to update release job * Update release.yml * Added test case for changeLogHelper * Added test cases for get-version-type * Update release.yml * Added changed to fix the failed test cases in travis * Empty commit for Travis testing * Update package.json * Update release.yml * Added change to publish to npm * Added inputs to homebrew in cli-core * Added npm token in semantic release action. * Added release_feature_branch as prerelease branch Co-authored-by: lakshmiravali <[email protected]>
1 parent 5c579b9 commit 002dd1f

9 files changed

+636
-17
lines changed

.github/scripts/change-log-helper.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
const fs = require('fs');
2+
const readline = require('readline');
3+
4+
const { logger } = require('../../src/services/messaging/logging');
5+
6+
const defaultVersionRegex = /(\d+)\.(\d+)\.(\d+)/;
7+
const defaultDateRegex = /\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])/;
8+
const cliCoreChangelogFile = 'CHANGES.md';
9+
const oaiChangelogFile = 'OAI_CHANGES.md';
10+
11+
class ChangeLogHelper {
12+
constructor(
13+
cliCoreChangelogFilename = cliCoreChangelogFile,
14+
oaiChangelogFilename = oaiChangelogFile,
15+
versionRegex = defaultVersionRegex,
16+
dateRegex = defaultDateRegex,
17+
) {
18+
this.versionRegex = versionRegex;
19+
this.dateRegex = dateRegex;
20+
this.cliCoreChangelogFilename = cliCoreChangelogFilename;
21+
this.oaiChangelogFilename = oaiChangelogFilename;
22+
this.logger = logger;
23+
}
24+
25+
async getAllReleaseVersionsFromGivenDate(date) {
26+
this.logger.info(`Started detecting the versions from the date: ${date}`);
27+
const versions = [];
28+
const readLine = await this.getReadLiner(this.oaiChangelogFilename);
29+
for await (const line of readLine) {
30+
const currentDate = this.dateRegex.exec(line);
31+
if (currentDate) {
32+
const version = this.versionRegex.exec(line);
33+
if (version) {
34+
versions.push(version[0]);
35+
}
36+
if (currentDate[0] <= date) {
37+
break;
38+
}
39+
}
40+
}
41+
this.logger.info(`Detected Versions: ${versions}`);
42+
return versions;
43+
}
44+
45+
async getLatestChangelogGeneratedDate() {
46+
this.logger.info('Started detecting the latest date in cli core changelog');
47+
let latestDate;
48+
const readLine = await this.getReadLiner(this.cliCoreChangelogFilename);
49+
for await (const line of readLine) {
50+
latestDate = this.dateRegex.exec(line);
51+
if (latestDate) {
52+
latestDate = latestDate[0];
53+
this.logger.info(`Detected the latest Date: ${latestDate}`);
54+
break;
55+
}
56+
}
57+
return latestDate;
58+
}
59+
60+
async getChangesAfterGivenDate(date) {
61+
this.logger.info(`Started getting the changelog from given date: ${date}`);
62+
let readLines = false;
63+
let fileData = '';
64+
const readLine = await this.getReadLiner(this.oaiChangelogFilename);
65+
for await (const line of readLine) {
66+
const currentDate = this.dateRegex.exec(line);
67+
if (currentDate) {
68+
if (currentDate[0] > date) {
69+
this.logger.info('Reading the lines');
70+
readLines = true;
71+
} else {
72+
this.logger.info(`Changes from OpenAPI specs: ${fileData}`);
73+
break;
74+
}
75+
} else if (readLines) {
76+
fileData += `${line}\n`;
77+
}
78+
}
79+
return fileData;
80+
}
81+
82+
async appendChangesToChangelog() {
83+
this.logger.info('Started appendChangesToChangelog');
84+
try {
85+
const latestDate = await this.getLatestChangelogGeneratedDate(); // changes.md
86+
if (latestDate) {
87+
const changeLog = await this.getChangesAfterGivenDate(latestDate); // oai_changes.md
88+
if (changeLog) {
89+
this.logger.info('Updating the CHANGES.md');
90+
const data = fs.readFileSync(this.cliCoreChangelogFilename);
91+
if (data.toString().includes(changeLog)) {
92+
this.logger.info(`Provided changes are already in cli core changeLog: ${changeLog}`);
93+
return;
94+
}
95+
const fd = fs.openSync(this.cliCoreChangelogFilename, 'w+');
96+
const insert = Buffer.from(changeLog);
97+
fs.writeSync(fd, insert, 0, insert.length, 0);
98+
fs.writeSync(fd, data, 0, data.length, insert.length);
99+
fs.close(fd, (err) => {
100+
if (err) throw err;
101+
});
102+
fs.writeFileSync('changeLog.md', changeLog);
103+
}
104+
}
105+
} catch (error) {
106+
this.logger.error(`Error while updating the changelog: ${error.message}`);
107+
throw new Error(error);
108+
}
109+
}
110+
111+
async getReadLiner(filename) {
112+
if (!fs.existsSync(filename)) {
113+
throw new Error(`File not found: ${filename}`);
114+
}
115+
const fileStream = fs.createReadStream(filename);
116+
return readline.createInterface({
117+
input: fileStream,
118+
});
119+
}
120+
}
121+
module.exports = {
122+
ChangeLogHelper,
123+
};

.github/scripts/get-version-type.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable no-console */
2+
const { ChangeLogHelper } = require('./change-log-helper');
3+
4+
const ch = new ChangeLogHelper();
5+
6+
const getVersionType = async () => {
7+
const latestDate = await ch.getLatestChangelogGeneratedDate();
8+
const versions = await ch.getAllReleaseVersionsFromGivenDate(latestDate);
9+
if (versions.length >= 2) {
10+
const version1 = versions[0].split('.');
11+
const version2 = versions[versions.length - 1].split('.');
12+
for (let i = 0; i < 3; i++) {
13+
if (version1[i] !== version2[i]) return i;
14+
}
15+
}
16+
return -1;
17+
};
18+
(async () => {
19+
console.log(await getVersionType());
20+
})();
21+
module.exports = {
22+
getVersionType,
23+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/sh
2+
echo "Copying api-definitions"
3+
cp -R ~/oai_definitions/json/. src/services/twilio-api/
4+
echo "Running update changelog script"
5+
node .github/scripts/update-change-log.js
6+
changeLog=''
7+
versionType=-1
8+
if [ -f changeLog.md ]; then
9+
changeLog=$(cat changeLog.md)
10+
rm -rf changeLog.md
11+
if [ "$changeLog" != '' ]; then
12+
changeLog="${changeLog//'%'/'%25'}"
13+
changeLog="${changeLog//$'\n'/'%0A'}"
14+
changeLog="${changeLog//$'\r'/'%0D'}"
15+
versionType=$(node .github/scripts/get-version-type.js | tail -n -1)
16+
fi
17+
fi
18+
echo "Changelog: $changeLog"
19+
echo "Version type: $versionType"
20+
rm -rf OAI_CHANGES.md
21+
echo "Git configurations"
22+
git config --global user.email "[email protected]"
23+
git config --global user.name "twilio-dx"
24+
branch=$(git branch --show-current)
25+
echo "Current branch: $branch"
26+
git add -A
27+
if [ -n "$(git status --porcelain)" ]; then
28+
echo "There are changes to commit.";
29+
commitMessage=''
30+
if [ "$versionType" == 0 ] || [ "$versionType" == 1 ]
31+
then
32+
commitMessage='feat: Updated api definitions'
33+
elif [ "$versionType" == 2 ]
34+
then
35+
commitMessage='fix: Updated api definitions'
36+
else
37+
echo "Invalid versionType: $versionType";
38+
exit
39+
fi
40+
echo "Commit message:$commitMessage"
41+
git commit -m "$commitMessage"
42+
git push origin "$branch"
43+
else
44+
echo "No changes to commit";
45+
fi

.github/scripts/update-change-log.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const { ChangeLogHelper } = require('./change-log-helper');
2+
3+
const ch = new ChangeLogHelper();
4+
5+
const updateChangeLog = async () => {
6+
return ch.appendChangesToChangelog();
7+
};
8+
(async () => {
9+
await updateChangeLog();
10+
})();

.github/workflows/release.yml

+98-17
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,102 @@
1-
21
name: Cli-core Release
32
on:
43
workflow_dispatch:
4+
inputs:
5+
cli-branch:
6+
description: 'Run cli workflow in the given branch'
7+
default: main
8+
homebrew-branch:
9+
description: 'HomeBrew branch name'
10+
default: main
11+
homebrew-prerelease:
12+
description: 'HomeBrew prerelease'
13+
default: 'false'
514
jobs:
6-
test:
7-
runs-on: ubuntu-latest
8-
strategy:
9-
matrix:
10-
node-version: [14.x]
11-
steps:
12-
- name: Checkout cli-core repo
13-
uses: actions/checkout@v2
14-
- run: npm install
15-
- name: Use Node.js ${{ matrix.node-version }}
16-
uses: actions/setup-node@v2
17-
with:
18-
node-version: ${{ matrix.node-version }}
19-
cache: 'npm'
20-
- name: Run tests
21-
run: npm test
15+
test:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
node-version: [10.x,14.x]
20+
steps:
21+
- name: Checkout cli-core repo
22+
uses: actions/checkout@v2
23+
- run: npm install
24+
- name: Use Node.js ${{ matrix.node-version }}
25+
uses: actions/setup-node@v2
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
cache: 'npm'
29+
- name: Run tests
30+
run: npm test
31+
update-api-specs:
32+
runs-on: ubuntu-latest
33+
needs: [test]
34+
outputs:
35+
change-log: ${{ steps.update-specs.outputs.change-log }}
36+
version-type: ${{ steps.update-specs.outputs.version-type }}
37+
steps:
38+
- name: Create temporary folder for copying json files from OAI repo
39+
run: mkdir -p ~/oai_definitions/json
40+
- name: Checkout OAI repo
41+
uses: actions/checkout@v2
42+
with:
43+
repository: 'twilio/twilio-oai'
44+
token: ${{ secrets.REPO_ACCESS_TOKEN }}
45+
- run: |
46+
cp -R spec/json/. ~/oai_definitions/json/
47+
cp -R CHANGES.md ~/oai_definitions/CHANGES.md
48+
- name: Checkout cli-core repo
49+
uses: actions/checkout@v2
50+
- name: Update OAI specs
51+
id: update-specs
52+
run: |
53+
npm install
54+
cp -R ~/oai_definitions/CHANGES.md OAI_CHANGES.md
55+
source .github/scripts/update-api-spec-with-changelog.sh
56+
echo "::set-output name=change-log::$changeLog"
57+
echo "::set-output name=version-type::$versionType"
58+
release:
59+
runs-on: ubuntu-latest
60+
needs: [update-api-specs]
61+
outputs:
62+
tag-name: ${{ steps.semantic-release.outputs.TAG_NAME }}
63+
steps:
64+
- name: Checkout cli-core repo
65+
uses: actions/checkout@v2
66+
- run: |
67+
git pull
68+
npm install
69+
- name: semanticRelease
70+
id: semantic-release
71+
run: npx semantic-release -t \${version}
72+
env:
73+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
75+
update-release:
76+
runs-on: ubuntu-latest
77+
needs: [update-api-specs, release]
78+
steps:
79+
- name: Checkout cli-core repo
80+
uses: actions/checkout@v2
81+
- name: Update release
82+
if: ${{needs.release.outputs.tag-name != ''}}
83+
uses: tubone24/[email protected]
84+
env:
85+
GITHUB_TOKEN: ${{ github.token }}
86+
TAG_NAME: ${{needs.release.outputs.tag-name}}
87+
with:
88+
is_append_body: true
89+
body: ${{needs.update-api-specs.outputs.change-log}}
90+
triggerCliWorkflow:
91+
runs-on: ubuntu-latest
92+
needs: [ update-api-specs, update-release]
93+
steps:
94+
- name: Invoke CLI workflow with changelog and version-type
95+
if: ${{toJSON(needs.update-api-specs.outputs.change-log) != null && needs.update-api-specs.outputs.version-type != -1}}
96+
uses: benc-uk/workflow-dispatch@v1
97+
with:
98+
workflow: Cli Release
99+
token: ${{ secrets.REPO_ACCESS_TOKEN }}
100+
repo: twilio/twilio-cli
101+
ref: ${{ github.event.inputs.cli-branch }}
102+
inputs: '{ "change-log": ${{ toJSON(needs.update-api-specs.outputs.change-log) }}, "version-type": "${{needs.update-api-specs.outputs.version-type}}", "home-brew-branch": "${{github.event.inputs.homebrew-branch}}", "pre-release": "${{github.event.inputs.homebrew-prerelease}}" }'

.releaserc.json

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"branches": [
3+
"main",
4+
{
5+
"name": "release_feature_branch",
6+
"prerelease": "rc"
7+
}
8+
],
9+
"plugins": [
10+
[
11+
"@semantic-release/commit-analyzer",
12+
{
13+
"preset": "angular",
14+
"releaseRules": [{
15+
"type": "chore",
16+
"release": "patch"
17+
}]
18+
}
19+
],
20+
[
21+
"@semantic-release/release-notes-generator",
22+
{
23+
"preset": "conventionalcommits",
24+
"presetConfig": {
25+
"types": [{
26+
"type": "feat",
27+
"section": "Library - Features"
28+
},
29+
{
30+
"type": "fix",
31+
"section": "Library - Fixes"
32+
},
33+
{
34+
"type": "chore",
35+
"section": "Library - Chores"
36+
},
37+
{
38+
"type": "test",
39+
"section": "Library - Test"
40+
},
41+
{
42+
"type": "docs",
43+
"section": "Library - Docs"
44+
}
45+
]
46+
}
47+
}
48+
],
49+
[
50+
"@semantic-release/changelog",
51+
{
52+
"changelogFile": "CHANGES.md"
53+
}
54+
],
55+
"@semantic-release/npm",
56+
"@semantic-release/github",
57+
[
58+
"@semantic-release/git",
59+
{
60+
"assets": [
61+
"CHANGES.md",
62+
"package.json"
63+
],
64+
"message": "chore(release): set `package.json` to ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
65+
}
66+
],
67+
[
68+
"@semantic-release/exec",
69+
{
70+
"successCmd": "echo '::set-output name=TAG_NAME::${nextRelease.version}'"
71+
}
72+
]
73+
]
74+
}

0 commit comments

Comments
 (0)