@vida-global/core 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/lib/active_record/baseRecord.js +25 -4
- package/package.json +5 -4
- package/test/active_record/baseRecord.test.js +38 -2
- package/lib/release/README.md +0 -66
- package/lib/release/develop.js +0 -27
- package/lib/release/git.js +0 -86
- package/lib/release/increment.js +0 -56
- package/lib/release/index.js +0 -10
- package/lib/release/release.js +0 -30
- package/lib/release/utils.js +0 -44
- package/scripts/release.js +0 -75
- package/test/release/develop.test.js +0 -57
- package/test/release/git.test.js +0 -189
- package/test/release/increment.test.js +0 -145
- package/test/release/release.test.js +0 -72
- package/test/release/utils.test.js +0 -148
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ const { logger } = require('../logger');
|
|
|
4
4
|
const { Model, Op } = require('sequelize');
|
|
5
5
|
const nodeUtil = require('util');
|
|
6
6
|
const { redisClientFactory } = require('../redis');
|
|
7
|
-
const { tableize }
|
|
7
|
+
const { camelize, tableize } = require('inflection');
|
|
8
8
|
const utils = require('./utils');
|
|
9
9
|
|
|
10
10
|
|
|
@@ -53,6 +53,15 @@ class BaseRecord extends Model {
|
|
|
53
53
|
|
|
54
54
|
if (this._initialized) return;
|
|
55
55
|
|
|
56
|
+
const { schema, options } = this.initializationOptions;
|
|
57
|
+
this.init(schema, options);
|
|
58
|
+
this.initializeHooks();
|
|
59
|
+
|
|
60
|
+
this._initialized = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
static get initializationOptions() {
|
|
56
65
|
const schema = getActiveRecordSchema(this._tableName, this.databaseId, this.connection.dialect);
|
|
57
66
|
const options = {
|
|
58
67
|
createdAt: 'created_at',
|
|
@@ -67,11 +76,23 @@ class BaseRecord extends Model {
|
|
|
67
76
|
options.timestamps = false;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
this.
|
|
79
|
+
this.configureOverridenAccessors(schema);
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
return { schema, options };
|
|
82
|
+
}
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
|
|
85
|
+
static configureOverridenAccessors(schema) {
|
|
86
|
+
for (const [colName, colOptions] of Object.entries(schema)) {
|
|
87
|
+
const getterName = `_get${camelize(colName)}`;
|
|
88
|
+
const setterName = `_set${camelize(colName)}`;
|
|
89
|
+
if (this.prototype[getterName]) {
|
|
90
|
+
colOptions.get = this.prototype[getterName]
|
|
91
|
+
}
|
|
92
|
+
if (this.prototype[setterName]) {
|
|
93
|
+
colOptions.set = this.prototype[setterName]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
75
96
|
}
|
|
76
97
|
|
|
77
98
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vida-global/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Core libraries for supporting Vida development",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
10
|
-
"release": "node
|
|
11
|
-
"develop": "node
|
|
10
|
+
"release": "node node_modules/@vida-global/release/scripts/release.js",
|
|
11
|
+
"develop": "node node_modules/@vida-global/release/scripts/release.js develop"
|
|
12
12
|
},
|
|
13
13
|
"jest": {
|
|
14
14
|
"collectCoverage": true,
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"response-time": "^2.3.3",
|
|
30
30
|
"sequelize": "^6.37.7",
|
|
31
31
|
"sequelize-cli": "^6.6.3",
|
|
32
|
-
"socket.io": "^4.4.0"
|
|
32
|
+
"socket.io": "^4.4.0",
|
|
33
|
+
"@vida-global/release": "^1.0.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"jest": "^29.6.2",
|
|
@@ -6,6 +6,7 @@ const importSchema = require('../../lib/active_record/db/importSc
|
|
|
6
6
|
const { Model, Sequelize } = require('sequelize');
|
|
7
7
|
const { redisClientFactory } = require('../../lib/redis');
|
|
8
8
|
const TestHelpers = require('@vida-global/test-helpers');
|
|
9
|
+
const { camelize } = require('inflection');
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
const connectionConfig = {database: ' ', host: ' ', password: ' ', username: ' '};
|
|
@@ -53,6 +54,7 @@ const dataTypeKey1 = Object.keys(Sequelize.DataTypes)[0];
|
|
|
53
54
|
const dataTypeKey2 = Object.keys(Sequelize.DataTypes)[1];
|
|
54
55
|
const dataTypeValue1 = Sequelize.DataTypes[dataTypeKey1].types.postgres[0];
|
|
55
56
|
const dataTypeValue2 = Sequelize.DataTypes[dataTypeKey2].types.postgres[0];
|
|
57
|
+
const col1Name = `${TestHelpers.Faker.Text.randomString()}_${TestHelpers.Faker.Text.randomString()}_${TestHelpers.Faker.Text.randomString()}`;
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
jest.mock('../../lib/redis', () => {
|
|
@@ -67,7 +69,12 @@ jest.mock('../../lib/redis', () => {
|
|
|
67
69
|
});
|
|
68
70
|
|
|
69
71
|
jest.mock('../../lib/active_record/db/importSchema', () => jest.fn());
|
|
70
|
-
|
|
72
|
+
const schema = {created_at: {type: dataTypeValue1},
|
|
73
|
+
updated_at: {type: dataTypeValue2},
|
|
74
|
+
[col1Name]: {type: dataTypeValue2}
|
|
75
|
+
};
|
|
76
|
+
importSchema.mockImplementation(() => structuredClone(schema));
|
|
77
|
+
beforeEach(() => importSchema.mockImplementation(() => structuredClone(schema)));
|
|
71
78
|
|
|
72
79
|
|
|
73
80
|
afterEach(() => {
|
|
@@ -88,7 +95,8 @@ describe('BaseRecord', () => {
|
|
|
88
95
|
class User extends BaseRecord {}
|
|
89
96
|
User.initialize();
|
|
90
97
|
const tableDetails = {created_at: {type: Sequelize.DataTypes[dataTypeKey1]},
|
|
91
|
-
updated_at: {type: Sequelize.DataTypes[dataTypeKey2]}
|
|
98
|
+
updated_at: {type: Sequelize.DataTypes[dataTypeKey2]},
|
|
99
|
+
[col1Name]: {type: Sequelize.DataTypes[dataTypeKey2]}
|
|
92
100
|
};
|
|
93
101
|
expect(importSchema).toHaveBeenCalledTimes(1);
|
|
94
102
|
expect(Model.init).toHaveBeenCalledTimes(1);
|
|
@@ -137,6 +145,34 @@ describe('BaseRecord', () => {
|
|
|
137
145
|
it ('will throw an error when run on BaseRecord', () => {
|
|
138
146
|
expect(() => BaseRecord.initialize()).toThrow();
|
|
139
147
|
});
|
|
148
|
+
|
|
149
|
+
it ('allows for overriding getters and setters', () => {
|
|
150
|
+
class User extends BaseRecord {}
|
|
151
|
+
const getterMethodName = `_get${camelize(col1Name)}`;
|
|
152
|
+
const setterMethodName = `_set${camelize(col1Name)}`;
|
|
153
|
+
const getter = jest.fn();
|
|
154
|
+
const setter = jest.fn();
|
|
155
|
+
User.prototype[getterMethodName] = getter;
|
|
156
|
+
User.prototype[setterMethodName] = setter;
|
|
157
|
+
User.initialize();
|
|
158
|
+
const tableDetails = {created_at: {type: Sequelize.DataTypes[dataTypeKey1]},
|
|
159
|
+
updated_at: {type: Sequelize.DataTypes[dataTypeKey2]},
|
|
160
|
+
[col1Name]: {
|
|
161
|
+
type: Sequelize.DataTypes[dataTypeKey2],
|
|
162
|
+
get: getter,
|
|
163
|
+
set: setter,
|
|
164
|
+
}};
|
|
165
|
+
expect(Model.init).toHaveBeenCalledWith(tableDetails,
|
|
166
|
+
{
|
|
167
|
+
createdAt: 'created_at',
|
|
168
|
+
deletedAt: 'deleted_at',
|
|
169
|
+
modelName: 'User',
|
|
170
|
+
sequelize: mockSequelize,
|
|
171
|
+
tableName: 'users',
|
|
172
|
+
updatedAt: 'updated_at'
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
});
|
|
140
176
|
});
|
|
141
177
|
|
|
142
178
|
|
package/lib/release/README.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Configuration
|
|
2
|
-
Add the following scripts to your package.json
|
|
3
|
-
```
|
|
4
|
-
"release": "node node_modules/@vida-global/core/scripts/release.js",
|
|
5
|
-
"develop": "node node_modules/@vida-global/core/scripts/release.js develop"
|
|
6
|
-
```
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Release Management
|
|
10
|
-
The release script provides serves two purposes, version management and production release management.
|
|
11
|
-
|
|
12
|
-
## Version Management
|
|
13
|
-
```
|
|
14
|
-
npm run release increment <major,minor,point>
|
|
15
|
-
```
|
|
16
|
-
Running this command will find the current `main` or `master` branch on your remote repo,
|
|
17
|
-
search for the highest remote version, create a new release branch with the incremented version,
|
|
18
|
-
and update the version in `package.json`
|
|
19
|
-
|
|
20
|
-
## Production Release Management
|
|
21
|
-
```
|
|
22
|
-
npm run release production [version]
|
|
23
|
-
```
|
|
24
|
-
Running this command will find the current highest version on the remote repo, or the one specified
|
|
25
|
-
by the version option, and replace `release/production` with that branch.
|
|
26
|
-
|
|
27
|
-
## Example
|
|
28
|
-
Given a current highest version of `release/1.5.3`
|
|
29
|
-
|
|
30
|
-
#### Increment point version
|
|
31
|
-
```
|
|
32
|
-
npm run release increment point
|
|
33
|
-
```
|
|
34
|
-
This will create a new branch of `origin/master` named `release/1.6.0` and will update `package.json`
|
|
35
|
-
to use `1.6.0` as the "version."
|
|
36
|
-
|
|
37
|
-
#### Release
|
|
38
|
-
```
|
|
39
|
-
npm run release production
|
|
40
|
-
```
|
|
41
|
-
This will replace `release/production` with a branch of `release/1.6.0`
|
|
42
|
-
|
|
43
|
-
#### Rollback a release
|
|
44
|
-
```
|
|
45
|
-
npm run release production
|
|
46
|
-
```
|
|
47
|
-
This will replace `release/production` with a branch of `release/1.6.0`
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# Development
|
|
51
|
-
```
|
|
52
|
-
npm run develop -- this is my new branch -i123
|
|
53
|
-
```
|
|
54
|
-
This will offer to create a new branch `feature/123/<YOUR_INITIALS>/this-is-my-new-branch`
|
|
55
|
-
|
|
56
|
-
## Options
|
|
57
|
-
#### -i --issue (required)
|
|
58
|
-
Provide the issue number this branch is associated with
|
|
59
|
-
|
|
60
|
-
#### -t --type (optional)
|
|
61
|
-
By default, the script creates a branch of type `feature`. Using the `-t` option, you can create a
|
|
62
|
-
branch of type `bugfix`, `chore`, `feature`, `hotfix`, or `refactor`
|
|
63
|
-
|
|
64
|
-
#### -s --source (optional)
|
|
65
|
-
By default, this script creates a branch of `origin/master`. By specifying the `-s` option and
|
|
66
|
-
providing a branch name, you will create a branch from the provided source.
|
package/lib/release/develop.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const Git = require('./git');
|
|
2
|
-
const utils = require('./utils');
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
async function develop(branchType, issueNumber, description, sourceBranch) {
|
|
6
|
-
const initials = devInitials();
|
|
7
|
-
const parsedDescription = description.replace(/\s+/g, '-').toLowerCase().replace(/[^a-z0-9-]/g, '');;
|
|
8
|
-
|
|
9
|
-
const branchName = `${branchType}/${issueNumber}/${initials}/${parsedDescription}`;
|
|
10
|
-
|
|
11
|
-
const question = `Do you want to create branch ${branchName}?`;
|
|
12
|
-
const _confirm = await utils.confirmContinue(question);
|
|
13
|
-
if (!_confirm) return;
|
|
14
|
-
|
|
15
|
-
Git.createBranch(branchName, sourceBranch);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function devInitials() {
|
|
20
|
-
const name = Git.getCurrentUser()
|
|
21
|
-
return name.split(' ').map(piece => piece[0]).join('').toLowerCase();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
module.exports = {
|
|
26
|
-
develop
|
|
27
|
-
}
|
package/lib/release/git.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function createBranch(branchName, branchToCopy=null) {
|
|
5
|
-
if (!branchToCopy) branchToCopy = getPrimaryBranch();
|
|
6
|
-
|
|
7
|
-
executeShellCommand(`git branch ${branchName} ${branchToCopy}`);
|
|
8
|
-
checkout(branchName)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function getPrimaryBranch() {
|
|
13
|
-
const allBranches = branches();
|
|
14
|
-
if (allBranches.includes('origin/master')) return 'origin/master';
|
|
15
|
-
if (allBranches.includes('origin/main')) return 'origin/main';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function checkout(branchName) {
|
|
20
|
-
executeShellCommand(`git checkout ${branchName}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
function pull() {
|
|
25
|
-
executeShellCommand('git pull --prune');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function push() {
|
|
30
|
-
const currBranch = getCurrentBranch();
|
|
31
|
-
executeShellCommand(`git push -u origin ${currBranch}`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function getCurrentBranch() {
|
|
36
|
-
const allBranches = branches(false);
|
|
37
|
-
return allBranches.find(b => /^\s*\*\s+/.test(b)).replace('*', '').trim();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function forceRemotePush(sourceBranch, destBranch) {
|
|
42
|
-
executeShellCommand(`git push origin origin/${sourceBranch}:${destBranch} --force`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
function add(file) {
|
|
47
|
-
executeShellCommand(`git add ${file}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function commit(msg) {
|
|
52
|
-
executeShellCommand(`git commit -m '${msg}'`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
function branches(remote=true) {
|
|
57
|
-
let cmd = 'git branch';
|
|
58
|
-
if (remote) cmd = `${cmd} -r`;
|
|
59
|
-
const branches = executeShellCommand(cmd);
|
|
60
|
-
return branches.toString().split("\n").map(b => b.trim());
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
function getCurrentUser() {
|
|
65
|
-
return executeShellCommand(`git config user.name`).toString();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
function executeShellCommand(command) {
|
|
70
|
-
return execSync(command);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
module.exports = {
|
|
75
|
-
add,
|
|
76
|
-
branches,
|
|
77
|
-
checkout,
|
|
78
|
-
commit,
|
|
79
|
-
createBranch,
|
|
80
|
-
forceRemotePush,
|
|
81
|
-
getCurrentBranch,
|
|
82
|
-
getCurrentUser,
|
|
83
|
-
getPrimaryBranch,
|
|
84
|
-
pull,
|
|
85
|
-
push
|
|
86
|
-
};
|
package/lib/release/increment.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const Git = require('./git');
|
|
3
|
-
const utils = require('./utils');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const packageJsonPath = `${process.cwd()}/package.json`;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async function increment(type) {
|
|
10
|
-
Git.pull();
|
|
11
|
-
const currVersion = utils.getCurrentVersion();
|
|
12
|
-
const newVersion = getNewVersion(currVersion, type);
|
|
13
|
-
|
|
14
|
-
const _confirm = await utils.confirmContinue(`Do you want to create version ${newVersion}?`);
|
|
15
|
-
if (!_confirm) return;
|
|
16
|
-
|
|
17
|
-
Git.createBranch(`release/${newVersion}`);
|
|
18
|
-
updatePackageVersion(newVersion)
|
|
19
|
-
Git.push();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
function getNewVersion(currVersion, type) {
|
|
24
|
-
const newVersion = currVersion.map(v => parseInt(v));
|
|
25
|
-
switch(type) {
|
|
26
|
-
case 'major':
|
|
27
|
-
newVersion[0] += 1;
|
|
28
|
-
newVersion[1] = 0;
|
|
29
|
-
newVersion[2] = 0;
|
|
30
|
-
break;
|
|
31
|
-
case 'minor':
|
|
32
|
-
newVersion[1] += 1;
|
|
33
|
-
newVersion[2] = 0;
|
|
34
|
-
break;
|
|
35
|
-
case 'point':
|
|
36
|
-
newVersion[2] += 1;
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
throw new Error('Invalid increment type');
|
|
40
|
-
}
|
|
41
|
-
return newVersion.join('.');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
function updatePackageVersion(newVersion) {
|
|
46
|
-
let packageJson = fs.readFileSync(packageJsonPath, {encoding: 'utf8'});
|
|
47
|
-
packageJson = packageJson.replace(/"version":\s*"\d+\.\d+\.\d+"/, `"version": "${newVersion}"`);
|
|
48
|
-
fs.writeFileSync(packageJsonPath, packageJson);
|
|
49
|
-
Git.add(packageJsonPath);
|
|
50
|
-
Git.commit(`build: incrementing build to ${newVersion}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
module.exports = {
|
|
55
|
-
increment
|
|
56
|
-
}
|
package/lib/release/index.js
DELETED
package/lib/release/release.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const Git = require('./git');
|
|
2
|
-
const utils = require('./utils');
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
async function release(env, versionToRelease=null) {
|
|
6
|
-
Git.pull();
|
|
7
|
-
|
|
8
|
-
if (versionToRelease) {
|
|
9
|
-
const branchName = `origin/release/${versionToRelease}`;
|
|
10
|
-
const allBranches = Git.branches();
|
|
11
|
-
if (!allBranches.includes(branchName)) {
|
|
12
|
-
throw new Error(`release/${versionToRelease} does not exist`);
|
|
13
|
-
}
|
|
14
|
-
} else {
|
|
15
|
-
versionToRelease = utils.getCurrentVersion().join('.');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const question = `Do you want to release version ${versionToRelease} to ${env}?`;
|
|
19
|
-
const _confirm = await utils.confirmContinue(question);
|
|
20
|
-
if (!_confirm) return;
|
|
21
|
-
|
|
22
|
-
const sourceBranch = `release/${versionToRelease}`;
|
|
23
|
-
const releaseBranch = `release/${env}`;
|
|
24
|
-
Git.forceRemotePush(sourceBranch, releaseBranch);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
module.exports = {
|
|
29
|
-
release
|
|
30
|
-
}
|
package/lib/release/utils.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const Git = require('./git');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function getCurrentVersion() {
|
|
5
|
-
const branches = Git.branches();
|
|
6
|
-
const releases = branches.filter(branchName => /release\/\d+\.\d+\.\d+/.test(branchName));
|
|
7
|
-
const versions = releases.map(branchName => {
|
|
8
|
-
return branchName.replace('origin/release/', '').split('.').map(n => parseInt(n));
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
versions.sort((v1,v2) => {
|
|
12
|
-
if (v1[0] != v2[0]) return v2[0] - v1[0];
|
|
13
|
-
if (v1[1] != v2[1]) return v2[1] - v1[1];
|
|
14
|
-
return v2[2] - v1[2];
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return versions[0] || [1,0,0];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async function confirmContinue(question) {
|
|
22
|
-
return await getInput(question, ['y','n']) == 'y';
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
async function getInput(question, options) {
|
|
27
|
-
const readline = require('node:readline/promises').createInterface({
|
|
28
|
-
input: process.stdin,
|
|
29
|
-
output: process.stdout
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const response = await readline.question(`${question} [${options.join(',')}] `);
|
|
33
|
-
readline.close();
|
|
34
|
-
|
|
35
|
-
if (options.includes(response)) return response;
|
|
36
|
-
return getInput(question, options);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
module.exports = {
|
|
41
|
-
confirmContinue,
|
|
42
|
-
getCurrentVersion,
|
|
43
|
-
getInput,
|
|
44
|
-
}
|
package/scripts/release.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
const commander = require('commander');
|
|
2
|
-
const { logger } = require('../lib/logger');
|
|
3
|
-
const Release = require('../lib/release');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function validateIssueNumber(val) {
|
|
7
|
-
const issueNumber = parseInt(val, 10);
|
|
8
|
-
if (isNaN(issueNumber)) {
|
|
9
|
-
throw new commander.InvalidArgumentError('Invalid issue number');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return issueNumber;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function validateProjectType(val) {
|
|
17
|
-
const types = ['bugfix', 'chore', 'feature', 'hotfix', 'refactor'];
|
|
18
|
-
if (!types.includes(val)) {
|
|
19
|
-
throw new commander.InvalidArgumentError(`must be one of: ${types.join(', ')}`);
|
|
20
|
-
}
|
|
21
|
-
return val;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function validateVersionType(val) {
|
|
26
|
-
const types = ['major', 'minor', 'point'];
|
|
27
|
-
if (!types.includes(val)) {
|
|
28
|
-
throw new commander.InvalidArgumentError(`must be one of: ${types.join(', ')}`);
|
|
29
|
-
}
|
|
30
|
-
return val;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const program = new commander.Command();
|
|
35
|
-
program.name('Vida Release')
|
|
36
|
-
.description('A CLI for generating vida releases')
|
|
37
|
-
.version('1.0.0');
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
program.command('increment')
|
|
41
|
-
.argument('<type>', 'version number to increment', validateVersionType)
|
|
42
|
-
.action(async (type) => {
|
|
43
|
-
try {
|
|
44
|
-
await Release.increment(type);
|
|
45
|
-
} catch(err) {
|
|
46
|
-
logger.error(err.message);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
program.command('production')
|
|
52
|
-
.argument('[version]', 'optional version release')
|
|
53
|
-
.action(async (version) => {
|
|
54
|
-
try {
|
|
55
|
-
await Release.release('production', version);
|
|
56
|
-
} catch(err) {
|
|
57
|
-
logger.error(err.message);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
program.command('develop')
|
|
63
|
-
.requiredOption('-i, --issue <issueNumber>', 'Must provide an issue number', validateIssueNumber)
|
|
64
|
-
.option('-t, --type [projectType]', 'Project type', validateProjectType, 'feature')
|
|
65
|
-
.option('-s, --source [sourceBranch]', 'Branch to copy')
|
|
66
|
-
.argument('<description...>', 'a description of your project')
|
|
67
|
-
.action(async (description, { issue, type, source }) => {
|
|
68
|
-
try {
|
|
69
|
-
await Release.develop(type, issue, description.join(' '), source);
|
|
70
|
-
} catch(err) {
|
|
71
|
-
logger.error(err.message);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
program.parse();
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const { develop } = require('../../lib/release/develop');
|
|
2
|
-
const Git = require('../../lib/release/git');
|
|
3
|
-
const TestHelpers = require('@vida-global/test-helpers');
|
|
4
|
-
const utils = require('../../lib/release/utils');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
jest.mock('../../lib/release/git', () => {
|
|
8
|
-
return { createBranch: jest.fn(), getCurrentUser: jest.fn() }
|
|
9
|
-
});
|
|
10
|
-
jest.mock('../../lib/release/utils', () => {
|
|
11
|
-
return { confirmContinue: jest.fn() };
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let branchType;
|
|
16
|
-
let description;
|
|
17
|
-
let firstName;
|
|
18
|
-
let issueNumber;
|
|
19
|
-
let lastName;
|
|
20
|
-
let sourceBranch;
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
jest.resetAllMocks();
|
|
23
|
-
utils.confirmContinue.mockImplementation(() => true);
|
|
24
|
-
|
|
25
|
-
firstName = TestHelpers.Faker.User.randomFirstName();
|
|
26
|
-
lastName = TestHelpers.Faker.User.randomLastName();
|
|
27
|
-
Git.getCurrentUser.mockImplementation(() => `${firstName} ${lastName}`);
|
|
28
|
-
|
|
29
|
-
branchType = TestHelpers.Faker.Text.randomString();
|
|
30
|
-
sourceBranch = TestHelpers.Faker.Text.randomString();
|
|
31
|
-
description = TestHelpers.Faker.Text.randomSentence();
|
|
32
|
-
issueNumber = TestHelpers.Faker.Math.randomNumber();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
describe('develop', () => {
|
|
37
|
-
it ('confirms and creates a branch once', async () => {
|
|
38
|
-
await develop(branchType, issueNumber, description, sourceBranch);
|
|
39
|
-
expect(utils.confirmContinue).toHaveBeenCalledTimes(1);
|
|
40
|
-
expect(Git.createBranch).toHaveBeenCalledTimes(1);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it ('generates the correct branch name', async () => {
|
|
44
|
-
const initials = `${firstName[0]}${lastName[0]}`.toLowerCase();
|
|
45
|
-
const branchName = `${branchType}/${issueNumber}/${initials}/${description.replace(/\s+/g, '-').toLowerCase().replace(/[^a-z0-9-]/g, '')}`;
|
|
46
|
-
await develop(branchType, issueNumber, description, sourceBranch);
|
|
47
|
-
|
|
48
|
-
expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to create branch ${branchName}?`);
|
|
49
|
-
expect(Git.createBranch).toHaveBeenCalledWith(branchName, sourceBranch);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it ('does not create a branch if the user does not confirm', async () => {
|
|
53
|
-
utils.confirmContinue.mockImplementation(() => false);
|
|
54
|
-
await develop(branchType, issueNumber, description, sourceBranch);
|
|
55
|
-
expect(Git.createBranch).not.toHaveBeenCalled();
|
|
56
|
-
});
|
|
57
|
-
});
|
package/test/release/git.test.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
2
|
-
const Git = require('../../lib/release/git');
|
|
3
|
-
const TestHelpers = require('@vida-global/test-helpers');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
jest.mock('child_process', () => {
|
|
7
|
-
const execSync = jest.fn();
|
|
8
|
-
return { execSync };
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
jest.resetAllMocks();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
describe('Git.add', () => {
|
|
18
|
-
it ('calls git add with the file name', () => {
|
|
19
|
-
const fileName = TestHelpers.Faker.Text.randomString();
|
|
20
|
-
Git.add(fileName);
|
|
21
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
22
|
-
expect(execSync).toHaveBeenCalledWith(`git add ${fileName}`);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
describe('Git.branches', () => {
|
|
28
|
-
it ('calls git branch and returns an array of the resulting strings', () => {
|
|
29
|
-
const b1 = TestHelpers.Faker.Text.randomString();
|
|
30
|
-
const b2 = TestHelpers.Faker.Text.randomString();
|
|
31
|
-
const b3 = TestHelpers.Faker.Text.randomString();
|
|
32
|
-
const branchStr = ` ${b1} \n ${b2}\n${b3} `;
|
|
33
|
-
execSync.mockImplementation(() => branchStr);
|
|
34
|
-
|
|
35
|
-
const branches = Git.branches();
|
|
36
|
-
|
|
37
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
38
|
-
expect(branches).toEqual([b1, b2, b3]);
|
|
39
|
-
expect(execSync).toHaveBeenCalledWith(`git branch -r`);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it ('does not include the remote option when needed', () => {
|
|
43
|
-
execSync.mockImplementation(() => '');
|
|
44
|
-
const branches = Git.branches(false);
|
|
45
|
-
expect(execSync).toHaveBeenCalledWith(`git branch`);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
describe('Git.checkout', () => {
|
|
51
|
-
it ('calls git checkout with the branch name', () => {
|
|
52
|
-
const branchName = TestHelpers.Faker.Text.randomString();
|
|
53
|
-
Git.checkout(branchName);
|
|
54
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
55
|
-
expect(execSync).toHaveBeenCalledWith(`git checkout ${branchName}`);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
describe('Git.commit', () => {
|
|
61
|
-
it ('calls git commit with comment', () => {
|
|
62
|
-
const comment = TestHelpers.Faker.Text.randomString();
|
|
63
|
-
Git.commit(comment);
|
|
64
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
65
|
-
expect(execSync).toHaveBeenCalledWith(`git commit -m '${comment}'`);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
describe('Git.createBranch', () => {
|
|
71
|
-
it ('creates a branch and switches to it', () => {
|
|
72
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
73
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
74
|
-
|
|
75
|
-
Git.createBranch(branch1, branch2);
|
|
76
|
-
|
|
77
|
-
expect(execSync).toHaveBeenCalledTimes(2);
|
|
78
|
-
expect(execSync).toHaveBeenNthCalledWith(1, `git branch ${branch1} ${branch2}`);
|
|
79
|
-
expect(execSync).toHaveBeenNthCalledWith(2, `git checkout ${branch1}`);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it ('defaults to the primary branch', () => {
|
|
83
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
84
|
-
|
|
85
|
-
execSync.mockImplementation(() => 'origin/main');
|
|
86
|
-
|
|
87
|
-
Git.createBranch(branch1);
|
|
88
|
-
|
|
89
|
-
expect(execSync).toHaveBeenCalledTimes(3);
|
|
90
|
-
expect(execSync).toHaveBeenNthCalledWith(2, `git branch ${branch1} origin/main`);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
describe('Git.forceRemotePush', () => {
|
|
96
|
-
it ('calls git push with the origin', () => {
|
|
97
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
98
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
99
|
-
|
|
100
|
-
Git.forceRemotePush(branch1, branch2);
|
|
101
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
102
|
-
expect(execSync).toHaveBeenCalledWith(`git push origin origin/${branch1}:${branch2} --force`)
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
describe('Git.getCurrentUser', () => {
|
|
108
|
-
it ('returns the result of git config.name', () => {
|
|
109
|
-
const name = TestHelpers.Faker.User.randomName();
|
|
110
|
-
execSync.mockImplementation(() => name);
|
|
111
|
-
|
|
112
|
-
const response = Git.getCurrentUser();
|
|
113
|
-
|
|
114
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
115
|
-
expect(execSync).toHaveBeenCalledWith(`git config user.name`);
|
|
116
|
-
expect(response).toEqual(name);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
describe('Git.getPrimaryBranch', () => {
|
|
122
|
-
it ('returns master when that is the primary branch', () => {
|
|
123
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
124
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
125
|
-
const branchStr = ` origin/${branch1}\n origin/master \n origin/${branch2}`;
|
|
126
|
-
execSync.mockImplementation(() => branchStr);
|
|
127
|
-
|
|
128
|
-
const branch = Git.getPrimaryBranch();
|
|
129
|
-
|
|
130
|
-
expect(branch).toEqual('origin/master');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it ('returns main when that is the primary branch', () => {
|
|
134
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
135
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
136
|
-
const branchStr = `origin/${branch1}\norigin/${branch2}\n origin/main `;
|
|
137
|
-
execSync.mockImplementation(() => branchStr);
|
|
138
|
-
|
|
139
|
-
const branch = Git.getPrimaryBranch();
|
|
140
|
-
|
|
141
|
-
expect(branch).toEqual('origin/main');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it ('returns undefined when it cannot determine the primary branch', () => {
|
|
145
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
146
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
147
|
-
const branch3 = TestHelpers.Faker.Text.randomString();
|
|
148
|
-
const branchStr = `origin/${branch1}\norigin/${branch2}\n origin/${branch3} `;
|
|
149
|
-
execSync.mockImplementation(() => branchStr);
|
|
150
|
-
|
|
151
|
-
const branch = Git.getPrimaryBranch();
|
|
152
|
-
|
|
153
|
-
expect(branch).toBeUndefined();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
describe('Git.getCurrentBranch', () => {
|
|
159
|
-
it ('returns the branch with the *', () => {
|
|
160
|
-
const branch1 = TestHelpers.Faker.Text.randomString();
|
|
161
|
-
const branch2 = TestHelpers.Faker.Text.randomString();
|
|
162
|
-
const branch3 = TestHelpers.Faker.Text.randomString();
|
|
163
|
-
const branchStr = ` origin/${branch1}\n * ${branch3} \n origin/${branch2}`;
|
|
164
|
-
execSync.mockImplementation(() => branchStr);
|
|
165
|
-
|
|
166
|
-
const branch = Git.getCurrentBranch();
|
|
167
|
-
|
|
168
|
-
expect(branch).toEqual(branch3);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
describe('Git.pull', () => {
|
|
174
|
-
it ('calls git pull with the prune option', () => {
|
|
175
|
-
Git.pull();
|
|
176
|
-
expect(execSync).toHaveBeenCalledTimes(1);
|
|
177
|
-
expect(execSync).toHaveBeenCalledWith(`git pull --prune`);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
describe('Git.push', () => {
|
|
183
|
-
it ('pushes to the current branch', () => {
|
|
184
|
-
const branch = TestHelpers.Faker.Text.randomString();
|
|
185
|
-
execSync.mockImplementation(() => `* ${branch}`);
|
|
186
|
-
Git.push();
|
|
187
|
-
expect(execSync).toHaveBeenCalledWith(`git push -u origin ${branch}`);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { increment } = require('../../lib/release/increment');
|
|
3
|
-
const Git = require('../../lib/release/git');
|
|
4
|
-
const TestHelpers = require('@vida-global/test-helpers');
|
|
5
|
-
const utils = require('../../lib/release/utils');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
jest.mock('../../lib/release/git', () => {
|
|
9
|
-
return { add: jest.fn(), createBranch: jest.fn(), commit: jest.fn(), pull: jest.fn(), push: jest.fn() };
|
|
10
|
-
});
|
|
11
|
-
jest.mock('../../lib/release/utils', () => {
|
|
12
|
-
return { getCurrentVersion: jest.fn(), confirmContinue: jest.fn() };
|
|
13
|
-
});
|
|
14
|
-
jest.mock('fs', () => {
|
|
15
|
-
return { readFileSync: jest.fn(), writeFileSync: jest.fn() };
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let config;
|
|
20
|
-
let version;
|
|
21
|
-
const packageJsonPath = `${process.cwd()}/package.json`;
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
jest.resetAllMocks();
|
|
24
|
-
version = [TestHelpers.Faker.Math.randomNumber(),
|
|
25
|
-
TestHelpers.Faker.Math.randomNumber(),
|
|
26
|
-
TestHelpers.Faker.Math.randomNumber()];
|
|
27
|
-
utils.getCurrentVersion.mockImplementation(() => version);
|
|
28
|
-
utils.confirmContinue.mockImplementation(() => true);
|
|
29
|
-
|
|
30
|
-
config = {
|
|
31
|
-
[TestHelpers.Faker.Text.randomString()]: TestHelpers.Faker.Text.randomString(),
|
|
32
|
-
[TestHelpers.Faker.Text.randomString()]: TestHelpers.Faker.Text.randomString(),
|
|
33
|
-
version: version.join('.')
|
|
34
|
-
}
|
|
35
|
-
fs.readFileSync.mockImplementation(() => JSON.stringify(config, null, 4));
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
describe('increment', () => {
|
|
40
|
-
it ('runs steps in the proper order', async () => {
|
|
41
|
-
await increment('point');
|
|
42
|
-
|
|
43
|
-
expect(Git.pull).toHaveBeenCalledTimes(1);
|
|
44
|
-
expect(utils.getCurrentVersion).toHaveBeenCalledTimes(1);
|
|
45
|
-
expect(Git.pull).toHaveBeenCalledBefore(utils.getCurrentVersion);
|
|
46
|
-
|
|
47
|
-
expect(utils.confirmContinue).toHaveBeenCalledTimes(1);
|
|
48
|
-
expect(utils.getCurrentVersion).toHaveBeenCalledBefore(utils.confirmContinue);
|
|
49
|
-
|
|
50
|
-
expect(Git.createBranch).toHaveBeenCalledTimes(1);
|
|
51
|
-
expect(utils.confirmContinue).toHaveBeenCalledBefore(Git.createBranch);
|
|
52
|
-
|
|
53
|
-
expect(fs.readFileSync).toHaveBeenCalledTimes(1);
|
|
54
|
-
expect(Git.createBranch).toHaveBeenCalledBefore(fs.readFileSync);
|
|
55
|
-
|
|
56
|
-
expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
|
|
57
|
-
expect(fs.readFileSync).toHaveBeenCalledBefore(fs.writeFileSync);
|
|
58
|
-
|
|
59
|
-
expect(Git.push).toHaveBeenCalledTimes(1);
|
|
60
|
-
expect(fs.writeFileSync).toHaveBeenCalledBefore(Git.push);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
describe('getNewVersion', () => {
|
|
65
|
-
it ('increments the major version', async () => {
|
|
66
|
-
await increment('major');
|
|
67
|
-
const newVersion = [...version];
|
|
68
|
-
newVersion[0] = newVersion[0] + 1;
|
|
69
|
-
newVersion[1] = 0;
|
|
70
|
-
newVersion[2] = 0;
|
|
71
|
-
expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it ('increments the minor version', async () => {
|
|
75
|
-
await increment('minor');
|
|
76
|
-
const newVersion = [...version];
|
|
77
|
-
newVersion[1] = newVersion[1] + 1;
|
|
78
|
-
newVersion[2] = 0;
|
|
79
|
-
expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it ('increments the point version', async () => {
|
|
83
|
-
await increment('point');
|
|
84
|
-
const newVersion = [...version];
|
|
85
|
-
newVersion[2] = newVersion[2] + 1;
|
|
86
|
-
expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it ('throws an error for an invalid type', async () => {
|
|
90
|
-
expect(async () => {await increment('foo')}).rejects.toThrow();;
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
describe('confirmation', () => {
|
|
96
|
-
it ('asks the user if they want to upgrade to the new version', async () => {
|
|
97
|
-
await increment('point');
|
|
98
|
-
const newVersion = [version[0], version[1], version[2]+1].join('.');
|
|
99
|
-
expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to create version ${newVersion}?`);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it ('does not continue if the user responds "n"', async () => {
|
|
103
|
-
utils.confirmContinue.mockImplementation(() => false);
|
|
104
|
-
await increment('point');
|
|
105
|
-
|
|
106
|
-
expect(Git.createBranch).not.toHaveBeenCalled();
|
|
107
|
-
expect(fs.readFileSync).not.toHaveBeenCalled();
|
|
108
|
-
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
|
109
|
-
expect(Git.push).not.toHaveBeenCalled();
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
describe('createBranch', () => {
|
|
115
|
-
it ('creates a release branch for the new version', async () => {
|
|
116
|
-
await increment('point');
|
|
117
|
-
const newVersion = [version[0], version[1], version[2]+1].join('.');
|
|
118
|
-
expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion}`);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
describe('updatePackageVersion', () => {
|
|
124
|
-
it ('writes the new package version to package.json', async () => {
|
|
125
|
-
const newVersion = [version[0], version[1], version[2]+1].join('.');
|
|
126
|
-
const newConfig = structuredClone(config);
|
|
127
|
-
newConfig.version = newVersion;
|
|
128
|
-
|
|
129
|
-
await increment('point');
|
|
130
|
-
|
|
131
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(packageJsonPath, JSON.stringify(newConfig, null, 4));
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it ('adds package.json to be committed', async () => {
|
|
135
|
-
await increment('point');
|
|
136
|
-
expect(Git.add).toHaveBeenCalledWith(packageJsonPath);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it ('adds a descriptive commit message', async () => {
|
|
140
|
-
const newVersion = [version[0], version[1], version[2]+1].join('.');
|
|
141
|
-
await increment('point');
|
|
142
|
-
expect(Git.commit).toHaveBeenCalledWith(`build: incrementing build to ${newVersion}`);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
const { release } = require('../../lib/release/release');
|
|
2
|
-
const Git = require('../../lib/release/git');
|
|
3
|
-
const TestHelpers = require('@vida-global/test-helpers');
|
|
4
|
-
const utils = require('../../lib/release/utils');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
jest.mock('../../lib/release/git', () => {
|
|
8
|
-
return { pull: jest.fn(), forceRemotePush: jest.fn(), branches: jest.fn() };
|
|
9
|
-
});
|
|
10
|
-
jest.mock('../../lib/release/utils', () => {
|
|
11
|
-
return { getCurrentVersion: jest.fn(), confirmContinue: jest.fn() };
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let env;
|
|
16
|
-
let version;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
jest.resetAllMocks();
|
|
19
|
-
env = TestHelpers.Faker.Text.randomString();
|
|
20
|
-
version = [TestHelpers.Faker.Math.randomNumber(),
|
|
21
|
-
TestHelpers.Faker.Math.randomNumber(),
|
|
22
|
-
TestHelpers.Faker.Math.randomNumber()];
|
|
23
|
-
utils.getCurrentVersion.mockImplementation(() => version);
|
|
24
|
-
utils.confirmContinue.mockImplementation(() => true);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
describe('release', () => {
|
|
29
|
-
it ('runs steps in the proper order', async () => {
|
|
30
|
-
await release(env);
|
|
31
|
-
|
|
32
|
-
expect(Git.pull).toHaveBeenCalledTimes(1)
|
|
33
|
-
expect(utils.confirmContinue).toHaveBeenCalledTimes(1)
|
|
34
|
-
expect(Git.pull).toHaveBeenCalledBefore(utils.confirmContinue)
|
|
35
|
-
|
|
36
|
-
expect(Git.forceRemotePush).toHaveBeenCalledTimes(1)
|
|
37
|
-
expect(utils.confirmContinue).toHaveBeenCalledBefore(Git.forceRemotePush)
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it ('defaults to the current version', async () => {
|
|
41
|
-
await release(env);
|
|
42
|
-
const versionStr = version.join('.');
|
|
43
|
-
expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to release version ${versionStr} to ${env}?`);
|
|
44
|
-
expect(Git.forceRemotePush).toHaveBeenCalledWith(`release/${versionStr}`, `release/${env}`);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it ('accepts an alternate version', async () => {
|
|
48
|
-
const altVersion = [TestHelpers.Faker.Math.randomNumber(),
|
|
49
|
-
TestHelpers.Faker.Math.randomNumber(),
|
|
50
|
-
TestHelpers.Faker.Math.randomNumber()].join('.');
|
|
51
|
-
Git.branches.mockImplementation(() => [`origin/release/${version.join('.')}`, `origin/release/${altVersion}`, TestHelpers.Faker.Text.randomString()]);
|
|
52
|
-
await release(env, altVersion);
|
|
53
|
-
expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to release version ${altVersion} to ${env}?`);
|
|
54
|
-
expect(Git.forceRemotePush).toHaveBeenCalledWith(`release/${altVersion}`, `release/${env}`);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it ('does not continue if the user responds "n"', async () => {
|
|
58
|
-
utils.confirmContinue.mockImplementation(() => false);
|
|
59
|
-
await release(env);
|
|
60
|
-
|
|
61
|
-
expect(Git.forceRemotePush).not.toHaveBeenCalled();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it ('throws an error if the version does not exist', async () => {
|
|
65
|
-
const altVersion = [TestHelpers.Faker.Math.randomNumber(),
|
|
66
|
-
TestHelpers.Faker.Math.randomNumber(),
|
|
67
|
-
TestHelpers.Faker.Math.randomNumber()].join('.');
|
|
68
|
-
Git.branches.mockImplementation(() => [`origin/release/${version.join('.')}`, TestHelpers.Faker.Text.randomString()]);
|
|
69
|
-
expect(async () => { await release(env, altVersion)}).rejects.toThrow();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
const Git = require('../../lib/release/git');
|
|
2
|
-
const TestHelpers = require('@vida-global/test-helpers');
|
|
3
|
-
const utils = require('../../lib/release/utils');
|
|
4
|
-
const readline = require('node:readline/promises');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
jest.mock('node:readline/promises', () => {
|
|
8
|
-
return { createInterface: jest.fn(), question: jest.fn(), close: jest.fn() };
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
jest.mock('../../lib/release/git', () => {
|
|
12
|
-
return {branches: jest.fn()};
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
jest.resetAllMocks();
|
|
18
|
-
readline.createInterface.mockImplementation(() => {
|
|
19
|
-
return {question: readline.question, close: readline.close};
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
describe('getCurrentVersion', () => {
|
|
25
|
-
const major1 = TestHelpers.Faker.Math.randomNumber();
|
|
26
|
-
const minor1 = TestHelpers.Faker.Math.randomNumber();
|
|
27
|
-
const point1 = TestHelpers.Faker.Math.randomNumber();
|
|
28
|
-
const diff1 = TestHelpers.Faker.Math.randomNumber();
|
|
29
|
-
const diff2 = TestHelpers.Faker.Math.randomNumber();
|
|
30
|
-
|
|
31
|
-
const b1 = `origin/release/${major1}/${minor1}/${point1}`;
|
|
32
|
-
|
|
33
|
-
it ('sorts branches by primary version first', () => {
|
|
34
|
-
const b2 = `origin/release/${major1 + diff1 }.${minor1 + diff2}.${point1 + diff1}`;
|
|
35
|
-
const b3 = `origin/release/${major1 + diff1 + diff2}.${minor1 }.${point1}`;
|
|
36
|
-
Git.branches.mockImplementation(() => [b1, b3, b2]);
|
|
37
|
-
expect(utils.getCurrentVersion()).toEqual([major1+diff1+diff2, minor1, point1]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it ('sorts branches by minor version second', () => {
|
|
41
|
-
const b2 = `origin/release/${major1 + diff1}.${minor1 + diff1 + diff2}.${point1}`;
|
|
42
|
-
const b3 = `origin/release/${major1 + diff1}.${minor1 + diff1 }.${point1 + diff1}`;
|
|
43
|
-
Git.branches.mockImplementation(() => [b1, b3, b2]);
|
|
44
|
-
expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1+diff1+diff2, point1]);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it ('sorts branches by point version last', () => {
|
|
48
|
-
const b2 = `origin/release/${major1 + diff1}.${minor1 + diff1}.${point1 + diff1}`;
|
|
49
|
-
const b3 = `origin/release/${major1 + diff1}.${minor1 + diff1}.${point1 + diff1 + diff2}`;
|
|
50
|
-
Git.branches.mockImplementation(() => [b1, b3, b2]);
|
|
51
|
-
expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1+diff1, point1+diff1+diff2]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it ('ignores branches that don not fit the pattern', () => {
|
|
55
|
-
const b2 = `origin/release/${major1 + diff1}.${minor1}.${point1}`;
|
|
56
|
-
const b3 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
|
|
57
|
-
const b4 = TestHelpers.Faker.Text.randomString();
|
|
58
|
-
Git.branches.mockImplementation(() => [b1, b3, b2, b4]);
|
|
59
|
-
expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1, point1]);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it ('defaults to 1.0.0', () => {
|
|
63
|
-
const b2 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
|
|
64
|
-
const b3 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
|
|
65
|
-
const b4 = TestHelpers.Faker.Text.randomString();
|
|
66
|
-
Git.branches.mockImplementation(() => [b3, b2, b4]);
|
|
67
|
-
expect(utils.getCurrentVersion()).toEqual([1, 0, 0]);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
describe('getInput', () => {
|
|
73
|
-
let question;
|
|
74
|
-
let numOptions;
|
|
75
|
-
let options;
|
|
76
|
-
let answer;
|
|
77
|
-
beforeEach(() => {
|
|
78
|
-
question = TestHelpers.Faker.Text.randomSentence();
|
|
79
|
-
numOptions = TestHelpers.Faker.Math.randomNumber(10);
|
|
80
|
-
options = [...Array(numOptions)].map(() => TestHelpers.Faker.Text.randomString());
|
|
81
|
-
answer = options[Math.floor(Math.random() * numOptions)];
|
|
82
|
-
readline.question.mockImplementation(() => answer);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it ('asks the question and appends the options', async () => {
|
|
86
|
-
await utils.getInput(question, options)
|
|
87
|
-
expect(readline.question).toHaveBeenCalledWith(`${question} [${options.join(',')}] `);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it ('asks the question once if it gets a valid answer', async () => {
|
|
91
|
-
const theAnswer = await utils.getInput(question, options)
|
|
92
|
-
expect(readline.question).toHaveBeenCalledTimes(1);
|
|
93
|
-
expect(theAnswer).toEqual(answer);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it ('closes the input after getting an answer', async () => {
|
|
97
|
-
await utils.getInput(question, options)
|
|
98
|
-
expect(readline.question).toHaveBeenCalledTimes(1);
|
|
99
|
-
expect(readline.close).toHaveBeenCalledTimes(1);
|
|
100
|
-
expect(readline.question).toHaveBeenCalledBefore(readline.close);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it ('repeats the question until it gets a valid answer', async () => {
|
|
104
|
-
const numAttempts = TestHelpers.Faker.Math.randomNumber(10);
|
|
105
|
-
let attemptNumber = 1;
|
|
106
|
-
|
|
107
|
-
readline.question.mockImplementation(() => {
|
|
108
|
-
if (attemptNumber == numAttempts) return answer;
|
|
109
|
-
attemptNumber = attemptNumber + 1;
|
|
110
|
-
return TestHelpers.Faker.Text.randomString();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const theAnswer = await utils.getInput(question, options)
|
|
114
|
-
expect(readline.question).toHaveBeenCalledTimes(numAttempts);
|
|
115
|
-
expect(readline.close).toHaveBeenCalledTimes(numAttempts);
|
|
116
|
-
expect(theAnswer).toEqual(answer);
|
|
117
|
-
|
|
118
|
-
for(let i=1; i <= numAttempts; i++){
|
|
119
|
-
expect(readline.question).toHaveBeenNthCalledWith(i, `${question} [${options.join(',')}] `);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
describe('confirmContinue', () => {
|
|
126
|
-
let question;
|
|
127
|
-
beforeEach(() => {
|
|
128
|
-
question = TestHelpers.Faker.Text.randomSentence();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it ('asks the question with "y" or "n" as options', async () => {
|
|
132
|
-
readline.question.mockImplementation(() => 'y');
|
|
133
|
-
await utils.confirmContinue(question);
|
|
134
|
-
expect(readline.question).toHaveBeenCalledWith(`${question} [y,n] `);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it ('returns true if the user answers "y"', async () => {
|
|
138
|
-
readline.question.mockImplementation(() => 'y');
|
|
139
|
-
const confirmed = await utils.confirmContinue(question);
|
|
140
|
-
expect(confirmed).toBeTruthy();
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it ('returns false if the user answers "n"', async () => {
|
|
144
|
-
readline.question.mockImplementation(() => 'n');
|
|
145
|
-
const confirmed = await utils.confirmContinue(question);
|
|
146
|
-
expect(confirmed).toBeFalsy();
|
|
147
|
-
});
|
|
148
|
-
});
|