@vida-global/core 1.1.13 → 1.2.1

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.
@@ -4,6 +4,7 @@ const { ConnectionConfiguration } = require('../../lib/active_record/db/connecti
4
4
  const { getActiveRecordSchema } = require('../../lib/active_record/db/schema');
5
5
  const importSchema = require('../../lib/active_record/db/importSchema');
6
6
  const { Model, Sequelize } = require('sequelize');
7
+ const { redisClientFactory } = require('../../lib/redis');
7
8
  const TestHelpers = require('@vida-global/test-helpers');
8
9
 
9
10
 
@@ -17,12 +18,25 @@ connectionSpy.mockImplementation(() => mockSequelize);
17
18
 
18
19
 
19
20
  jest.mock('sequelize', () => {
21
+ const TestHelpers = require('@vida-global/test-helpers');
20
22
  class MockModel {
21
- static init = jest.fn()
22
- static findByPk = jest.fn();
23
- static findAll = jest.fn();
23
+ constructor(data, opts={}) {
24
+ this.dataValues = data || {}
25
+ this.options = opts;
26
+ for (const [k,v] of Object.entries(this.dataValues)) {
27
+ this[k] = v;
28
+ }
29
+ };
30
+ static addHook = jest.fn()
31
+ static init = jest.fn()
32
+ static findByPk = jest.fn();
33
+ static findAll = jest.fn();
34
+ static primaryKeyAttribute = TestHelpers.Faker.Text.randomString();
35
+ toJSON() { return this.dataValues }
24
36
  }
25
- const TestHelpers = require('@vida-global/test-helpers');
37
+
38
+ MockModel.findAll.mockImplementation(() => []);
39
+
26
40
  const dataTypeKey1 = TestHelpers.Faker.Text.randomString();
27
41
  const dataTypeValue1 = TestHelpers.Faker.Text.randomString();
28
42
  const dataTypeKey2 = TestHelpers.Faker.Text.randomString();
@@ -41,6 +55,17 @@ const dataTypeValue1 = Sequelize.DataTypes[dataTypeKey1].types.postgres[0];
41
55
  const dataTypeValue2 = Sequelize.DataTypes[dataTypeKey2].types.postgres[0];
42
56
 
43
57
 
58
+ jest.mock('../../lib/redis', () => {
59
+ const client = {
60
+ del: jest.fn(),
61
+ mGet: jest.fn(),
62
+ mSet: jest.fn(),
63
+ }
64
+ const redisClientFactory = () => client;
65
+
66
+ return { redisClientFactory };
67
+ });
68
+
44
69
  jest.mock('../../lib/active_record/db/importSchema', () => jest.fn());
45
70
  importSchema.mockImplementation(() => ({created_at: {type: dataTypeValue1}, updated_at: {type: dataTypeValue2}}));
46
71
 
@@ -50,16 +75,16 @@ afterEach(() => {
50
75
  });
51
76
 
52
77
 
53
- describe('ActiveRecord', () => {
54
- describe('ActiveRecord#constructor', () => {
78
+ describe('BaseRecord', () => {
79
+ describe('BaseRecord.constructor', () => {
55
80
  it ('will throw an error when creating an instance of BaseRecord', () => {
56
81
  expect(() => new BaseRecord()).toThrow();
57
82
  });
58
83
  });
59
84
 
60
85
 
61
- describe('ActiveRecord.initialize', () => {
62
- it ('calls `getActiveRecordSchema` and passes the response to init', () => {
86
+ describe('BaseRecord.initialize', () => {
87
+ it ('calls `getBaseRecordSchema` and passes the response to init', () => {
63
88
  class User extends BaseRecord {}
64
89
  User.initialize();
65
90
  const tableDetails = {created_at: {type: Sequelize.DataTypes[dataTypeKey1]},
@@ -115,7 +140,7 @@ describe('ActiveRecord', () => {
115
140
  });
116
141
 
117
142
 
118
- describe('ActiveRecord.tableName', () => {
143
+ describe('BaseRecord.tableName', () => {
119
144
  it ('defaults to the puralized version of the class name', () => {
120
145
  class User extends BaseRecord {}
121
146
  expect(User._tableName).toEqual('users');
@@ -140,19 +165,30 @@ describe('ActiveRecord', () => {
140
165
  });
141
166
 
142
167
 
143
- describe('ActiveRecord#find', () => {
144
- it ('calls the sequelize `findByPk` method', async () => {
145
- class Person extends BaseRecord {}
146
- Person.initialize();
147
- const id = Math.random();
168
+ describe('BaseRecord.find', () => {
169
+ const pk = TestHelpers.Faker.Text.randomString();
170
+ class Person extends BaseRecord {}
171
+ Person.initialize();
172
+ Person.primaryKeyAttribute = pk;
173
+
174
+ it ('calls the sequelize `findByPk` method when passed one value', async () => {
175
+ const id = Math.random();
148
176
  await Person.find(id);
149
177
  expect(Person.findByPk).toHaveBeenCalledTimes(1);
150
178
  expect(Person.findByPk).toHaveBeenCalledWith(id);
151
179
  });
180
+
181
+ it ('calls the sequelize `findAll` method when passed multiple value', async () => {
182
+ const id1 = Math.random();
183
+ const id2 = Math.random();
184
+ await Person.find([id1, id2]);
185
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
186
+ expect(Person.findAll).toHaveBeenCalledWith({where: {[pk]: [id1, id2]}});
187
+ });
152
188
  });
153
189
 
154
190
 
155
- describe('ActiveRecord#where', () => {
191
+ describe('BaseRecord.where', () => {
156
192
  const column = TestHelpers.Faker.Text.randomString();
157
193
  const value = TestHelpers.Faker.Text.randomString();
158
194
  const condition = {[column]: value};
@@ -176,4 +212,319 @@ describe('ActiveRecord', () => {
176
212
  expect(Person.findAll).toHaveBeenCalledWith(expected);
177
213
  });
178
214
  });
215
+
216
+
217
+ describe('Caching', () => {
218
+ class Person extends BaseRecord {
219
+ static get isCacheable() { return true; }
220
+ }
221
+ let redisClient
222
+ beforeEach(async () => {
223
+ redisClient = await redisClientFactory();
224
+ redisClient.mGet.mockImplementation(keys => new Array(keys.length));
225
+ });
226
+
227
+ describe('BaseRecord.find', () => {
228
+ const findSpy = jest.spyOn(Person, '_cachedFind');
229
+ it ('calls _cachedFindByPk when one id is passed', async () => {
230
+ const id = Math.random();
231
+ await Person.find(id);
232
+ expect(Person.findByPk).toHaveBeenCalledTimes(0);
233
+ expect(findSpy).toHaveBeenCalledTimes(1);
234
+ expect(findSpy).toHaveBeenCalledWith(id);
235
+ });
236
+
237
+ it ('calls _cachedFind when multiple ids are passed', async () => {
238
+ const id1 = Math.random();
239
+ const id2 = Math.random();
240
+ await Person.find([id1, id2]);
241
+ expect(Person.findByPk).toHaveBeenCalledTimes(0);
242
+ expect(findSpy).toHaveBeenCalledTimes(1);
243
+ expect(findSpy).toHaveBeenCalledWith([id1, id2]);
244
+ });
245
+ });
246
+
247
+
248
+ describe('BaseRecord._cachedFind', () => {
249
+ const pk = Person.primaryKeyAttribute;
250
+ const id1 = Math.random();
251
+ const id2 = Math.random();
252
+ const id3 = Math.random();
253
+ const id4 = Math.random();
254
+
255
+ it ('converts ids to cache keys and fetches from cache', async () => {
256
+ await Person._cachedFind([id1, id2]);
257
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
258
+ expect(redisClient.mGet).toHaveBeenCalledWith([
259
+ `Person#find:${id1}`,
260
+ `Person#find:${id2}`,
261
+ ]);
262
+ });
263
+
264
+ it ('fetches misseds id from the database and caches them', async () => {
265
+ const key1 = `Person#find:${id1}`;
266
+ const key2 = `Person#find:${id2}`;
267
+ const key3 = `Person#find:${id3}`;
268
+ Person.findAll.mockImplementation(() => [new Person({[pk]:id1}), new Person({[pk]:id3})]);
269
+ redisClient.mGet.mockImplementation(keys => [null, '{}', null]);
270
+ await Person._cachedFind([id1, id2, id3]);
271
+
272
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
273
+ expect(redisClient.mGet).toHaveBeenCalledWith([key1, key2, key3]);
274
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
275
+ expect(Person.findAll).toHaveBeenCalledWith({where: {[pk]: [id1, id3]}});
276
+ expect(redisClient.mSet).toHaveBeenCalledTimes(1);
277
+ expect(redisClient.mSet).toHaveBeenCalledWith({
278
+ [key1]: `AR:Person:{"${pk}":${id1}}`,
279
+ [key3]: `AR:Person:{"${pk}":${id3}}`,
280
+ });
281
+ });
282
+
283
+ it ('does not hit the database for cache hits', async () => {
284
+ redisClient.mGet.mockImplementation(keys => [`AR:Person:{}`, `AR:Person:{}`]);
285
+ await Person._cachedFind([id1, id2]);
286
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
287
+ expect(redisClient.mGet).toHaveBeenCalledWith([`Person#find:${id1}`, `Person#find:${id2}`]);
288
+ expect(Person.findAll).not.toHaveBeenCalled();
289
+ });
290
+
291
+ it ('does hit the database when `clear` is true', async () => {
292
+ redisClient.mGet.mockImplementation(keys => [`AR:Person:{}`, `AR:Person:{}`]);
293
+ await Person._cachedFind([id1, id2], {clear: true});
294
+ expect(redisClient.mGet).not.toHaveBeenCalled();
295
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
296
+ expect(Person.findAll).toHaveBeenCalledWith({where: {[pk]: [id1, id2]}});
297
+ });
298
+
299
+ it ('returns an array of active record objects if an array was passed', async () => {
300
+ const p1 = new Person({[pk]: id1});
301
+ const p2 = new Person({[pk]: id2});
302
+ const p3 = new Person({[pk]: id3});
303
+ Person.findAll.mockImplementation(() => [p1, p3]);
304
+ redisClient.mGet.mockImplementation(keys => [null, `AR:Person:${JSON.stringify(p2.toJSON())}`, null]);
305
+
306
+ const results = await Person._cachedFind([id1, id2, id3, id4]);
307
+ expect(results.length).toEqual(3);
308
+ expect(results).toContain(p1);
309
+ expect(results).toContain(p3);
310
+ expect(results.filter(r => r[pk] == id2).length).toEqual(1);
311
+ });
312
+
313
+ it ('returns a single active record object if an array was not passed', async () => {
314
+ const p1 = new Person({[pk]: id1});
315
+ Person.findAll.mockImplementation(() => [p1]);
316
+ const results = await Person._cachedFind(id1);
317
+ expect(results).toEqual(p1);
318
+ });
319
+
320
+ it ('sets `isNewRecord` to false for records from cache', async () => {
321
+ redisClient.mGet.mockImplementation(keys => [`AR:Person:{}`]);
322
+ const person = await Person._cachedFind(id1);
323
+ expect(person.options.isNewRecord).toBeFalsy();
324
+ });
325
+ });
326
+
327
+
328
+ describe('BaseRecord.cachedIds', () => {
329
+ const key = TestHelpers.Faker.Text.randomString();
330
+ const colName = TestHelpers.Faker.Text.randomString();
331
+ const val = TestHelpers.Faker.Text.randomString();
332
+ const where = {[colName]: val};
333
+ const ids = [Math.random(), Math.random(), Math.random()];
334
+ const pk = Person.primaryKeyAttribute;
335
+ const rows = ids.map(id => ({[pk]: id}));
336
+
337
+ it ('fetches ids from the database and sets them in cache', async () => {
338
+ Person.findAll.mockImplementation(() => rows);
339
+ await Person.cachedIds(key, where)
340
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
341
+ expect(redisClient.mGet).toHaveBeenCalledWith([key]);
342
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
343
+ expect(Person.findAll).toHaveBeenCalledWith({ where, attributes: [pk], raw: true });
344
+ expect(redisClient.mSet).toHaveBeenCalledTimes(1);
345
+ expect(redisClient.mSet).toHaveBeenCalledWith({
346
+ [key]: JSON.stringify(ids)
347
+ });
348
+ });
349
+
350
+ it ('does not hit the database for cache hits', async () => {
351
+ redisClient.mGet.mockImplementation(keys => [JSON.stringify(ids)]);
352
+ await Person.cachedIds(key, where)
353
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
354
+ expect(redisClient.mGet).toHaveBeenCalledWith([key]);
355
+ expect(Person.findAll).not.toHaveBeenCalled();
356
+ });
357
+
358
+
359
+ it ('does hit the database when `clear` is true', async () => {
360
+ redisClient.mGet.mockImplementation(keys => [JSON.stringify(ids)]);
361
+ await Person.cachedIds(key, where, {clear: true})
362
+ expect(redisClient.mGet).not.toHaveBeenCalled();
363
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
364
+ expect(Person.findAll).toHaveBeenCalledWith({ where, attributes: [pk], raw: true });
365
+ });
366
+
367
+ it ('does hit the database when `clear` is true', async () => {
368
+ redisClient.mGet.mockImplementation(keys => [JSON.stringify(ids)]);
369
+ await Person.cachedIds(key, where, {clear: true})
370
+ expect(redisClient.mGet).not.toHaveBeenCalled();
371
+ expect(Person.findAll).toHaveBeenCalledTimes(1);
372
+ expect(Person.findAll).toHaveBeenCalledWith({ where, attributes: [pk], raw: true });
373
+ });
374
+ });
375
+
376
+
377
+ describe('BaseRecord.cachedFetch', () => {
378
+ const key1 = Math.random();
379
+ const key2 = Math.random();
380
+ const key3 = Math.random();
381
+ const fetcher = jest.fn();
382
+
383
+ fetcher.mockImplementation(() => ({}));
384
+
385
+ it ('does a redis multi get with all keys', async () => {
386
+ await Person.cachedFetch([key1, key2, key3], {}, fetcher);
387
+ expect(redisClient.mGet).toHaveBeenCalledTimes(1);
388
+ expect(redisClient.mGet).toHaveBeenCalledWith([key1, key2, key3]);
389
+ });
390
+
391
+ it ('calls the fetcher for any cache misses', async () => {
392
+ redisClient.mGet.mockImplementation(() => [null, '{}', null]);
393
+ await Person.cachedFetch([key1, key2, key3], {}, fetcher);
394
+ expect(fetcher).toHaveBeenCalledTimes(1);
395
+ expect(fetcher).toHaveBeenCalledWith([key1, key3]);
396
+ });
397
+
398
+ it ('does not call the fetcher if there are no cache misses', async () => {
399
+ redisClient.mGet.mockImplementation(() => ['{}', '{}', '{}']);
400
+ await Person.cachedFetch([key1, key2, key3], {}, fetcher);
401
+ expect(fetcher).toHaveBeenCalledTimes(0);
402
+ });
403
+
404
+ it ('ignores the cache if `clear` is true', async () => {
405
+ redisClient.mGet.mockImplementation(() => ['{}', '{}', '{}']);
406
+ await Person.cachedFetch([key1, key2, key3], {clear: true}, fetcher);
407
+ expect(redisClient.mGet).not.toHaveBeenCalled();
408
+ expect(fetcher).toHaveBeenCalledTimes(1);
409
+ expect(fetcher).toHaveBeenCalledWith([key1, key2, key3]);
410
+ });
411
+
412
+ it ('calls BaseRecord._cacheFetchedData with data returned by the fetcher', async () => {
413
+ const cacheSpy = jest.spyOn(Person, '_cacheFetchedData');
414
+ const obj1 = {};
415
+ const obj2 = {};
416
+ const data = {[key1]: obj1, [key2]: obj2};
417
+ redisClient.mGet.mockImplementation(() => [null, null]);
418
+ fetcher.mockImplementation(() => data);
419
+
420
+ await Person.cachedFetch([key1, key2], {}, fetcher);
421
+
422
+ expect(cacheSpy).toHaveBeenCalledTimes(1);
423
+ expect(cacheSpy).toHaveBeenCalledWith(data);
424
+ });
425
+
426
+ it ('returns data from cache hits and misses', async () => {
427
+ const pk = Person.primaryKeyAttribute;
428
+ const id1 = Math.random();
429
+ const id2 = Math.random();
430
+ const id3 = Math.random();
431
+ const obj1 = {[pk]: id1};
432
+ const obj2 = {[pk]: id2};
433
+ const obj3 = {[pk]: id3};
434
+ const data = {[key1]: obj1};
435
+ redisClient.mGet.mockImplementation(() => [null, JSON.stringify(obj2), JSON.stringify(obj3)]);
436
+ fetcher.mockImplementation(() => data);
437
+
438
+ const results = await Person.cachedFetch([key1, key2, key3], {}, fetcher);
439
+
440
+ expect(Object.keys(results).length).toEqual(3);
441
+ expect(results[key1]).toBe(obj1);
442
+
443
+ expect(results[key2]).not.toBe(obj2);
444
+ expect(results[key2]).toEqual(obj2);
445
+
446
+ expect(results[key3]).not.toBe(obj3);
447
+ expect(results[key3]).toEqual(obj3);
448
+ });
449
+ });
450
+
451
+
452
+ describe('BaseRecord._cacheFetchedData', () => {
453
+ const key1 = TestHelpers.Faker.Text.randomString();
454
+ const key2 = TestHelpers.Faker.Text.randomString();
455
+ const key3 = TestHelpers.Faker.Text.randomString();
456
+ const key4 = TestHelpers.Faker.Text.randomString();
457
+
458
+ it ('marshalls data and does a redis multi set', async () => {
459
+ const obj1Key = TestHelpers.Faker.Text.randomString();
460
+ const obj1Val = TestHelpers.Faker.Text.randomString();
461
+ const obj1 = {[obj1Key]: obj1Val};
462
+ const obj2Key = TestHelpers.Faker.Text.randomString();
463
+ const obj2Val = TestHelpers.Faker.Text.randomString();
464
+ const obj2 = {[obj2Key]: obj2Val};
465
+
466
+ const person1 = new Person(obj1)
467
+ const person2 = new Person(obj2)
468
+
469
+ await Person._cacheFetchedData({
470
+ [key1]: obj1,
471
+ [key2]: obj2,
472
+ [key3]: person1,
473
+ [key4]: person2,
474
+ });
475
+ const cachedData = {
476
+ [key1]: JSON.stringify(obj1),
477
+ [key2]: JSON.stringify(obj2),
478
+ [key3]: `AR:Person:${JSON.stringify(obj1)}`,
479
+ [key4]: `AR:Person:${JSON.stringify(obj2)}`,
480
+ }
481
+
482
+ expect(redisClient.mSet).toHaveBeenCalledTimes(1);
483
+ expect(redisClient.mSet).toHaveBeenCalledWith(cachedData);
484
+ });
485
+ });
486
+
487
+
488
+ describe('BaseRecord.#clearSelfCache', () => {
489
+ it ('deletes the key from cache', async () => {
490
+ const pk = Person.primaryKeyAttribute;
491
+ const id = Math.random();
492
+ const key = Person._recordCacheKey(id);
493
+ const person = new Person({[pk]: id});
494
+
495
+ await person.clearSelfCache();
496
+ expect(redisClient.del).toHaveBeenCalledTimes(1);
497
+ expect(redisClient.del).toHaveBeenCalledWith(key);
498
+ });
499
+ });
500
+
501
+
502
+ describe('BaseRecord._afterSaveCacheHook', () => {
503
+ it ('is called as an after save hook for cached models', () => {
504
+ expect(Person.addHook).toHaveBeenCalledTimes(0);
505
+ Person.initialize();
506
+ expect(Person.addHook).toHaveBeenCalledTimes(1);
507
+ expect(Person.addHook).toHaveBeenCalledWith('afterSave', Person._afterSaveCacheHook);
508
+ });
509
+
510
+ it ('is not called as an after save hook for non cached models', () => {
511
+ class Foo extends BaseRecord {}
512
+ Foo.initialize();
513
+ expect(Person.addHook).toHaveBeenCalledTimes(0);
514
+ });
515
+
516
+ it ('calls BaseRecord#clearSelfCache and BaseRecord#updateCache', async () => {
517
+ const person = new Person();
518
+ const clearSpy = jest.spyOn(person, 'clearSelfCache');
519
+ const updateSpy = jest.spyOn(person, 'updateCache');
520
+ const options = {};
521
+
522
+ await Person._afterSaveCacheHook(person,options);
523
+
524
+ expect(clearSpy).toHaveBeenCalledTimes(1);
525
+ expect(updateSpy).toHaveBeenCalledTimes(1);
526
+ expect(updateSpy).toHaveBeenCalledWith(options);
527
+ });
528
+ });
529
+ });
179
530
  });
@@ -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.
@@ -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
- }
@@ -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
- };
@@ -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
- }
@@ -1,10 +0,0 @@
1
- const { increment } = require('./increment');
2
- const { release } = require('./release');
3
- const { develop } = require('./develop');
4
-
5
-
6
- module.exports = {
7
- develop,
8
- increment,
9
- release,
10
- }