nitor 1.3.1 → 1.3.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 CHANGED
@@ -115,14 +115,19 @@ Once enabled, you can use Tab to autocomplete:
115
115
  ```bash
116
116
  nitor refactor <text>
117
117
  ```
118
- - **Backup:**
119
- ```bash
120
- nitor backup <text>
121
- ```
122
- - **Restore:**
118
+ - **MongoDB Backup & Restore:**
119
+
123
120
  ```bash
124
- nitor restore <text>
121
+ # Backup from Kubernetes pods and restore to local MongoDB
122
+ nitor backup
123
+
124
+ # Backup specific projects only
125
+ nitor backup -project <project1> <project2>
126
+
127
+ # Restore to Docker container (default: local MongoDB)
128
+ nitor backup -docker <boolean>
125
129
  ```
130
+
126
131
  - **Merge:**
127
132
  ```bash
128
133
  nitor merge -source <source branch> -target <target branch>
@@ -191,8 +196,7 @@ Once enabled, you can use Tab to autocomplete:
191
196
  - `create-branch` : Create git branch
192
197
  - `review` : AI review specified merge request
193
198
  - `refactor` : AI refactor specified text
194
- - `backup` : Backup specified projects
195
- - `restore` : Restore specified projects
199
+ - `backup` : Backup MongoDB databases from Kubernetes pods and restore to local/Docker
196
200
  - `merge` : Merge source branch into target branch
197
201
  - `cleanup` : Cleanup local git branches (checkout to master and delete all other branches)
198
202
  - `time-init` : Initialize time entry configuration
@@ -208,6 +212,140 @@ Once enabled, you can use Tab to autocomplete:
208
212
  - `task-stats` : View task statistics with GitLab merge request details
209
213
  - `get-task` : Extract Zoho task IDs from GitLab issue descriptions
210
214
 
215
+ ## MongoDB Backup & Restore
216
+
217
+ ### Overview
218
+
219
+ The MongoDB backup service provides automated backup and restore functionality for MongoDB databases running in Kubernetes pods. It supports:
220
+
221
+ - ✅ Backup from Kubernetes pods using `kubectl`
222
+ - ✅ Restore to Docker containers or local MongoDB instances
223
+ - ✅ Cross-platform support (Windows, macOS, Linux)
224
+ - ✅ Multi-project configuration
225
+ - ✅ Automatic cleanup of temporary files
226
+ - ✅ Error handling with detailed logging
227
+
228
+ ### Configuration
229
+
230
+ Configure your backup settings in the `.env.nu` file using JSON format:
231
+
232
+ #### BACKUP_CONFIG
233
+
234
+ Defines the source databases to backup from Kubernetes pods:
235
+
236
+ ```json
237
+ BACKUP_CONFIG={
238
+ "project1": [
239
+ {
240
+ "pod": "mongodb-pod-name",
241
+ "username": "admin",
242
+ "password": "password123",
243
+ "database": "mydb",
244
+ "backupPath": "/data/backup",
245
+ "localBackupPath": "~/backups/mongo"
246
+ }
247
+ ],
248
+ "project2": [
249
+ {
250
+ "pod": "another-pod",
251
+ "username": "dbuser",
252
+ "password": "dbpass",
253
+ "database": "anotherdb"
254
+ }
255
+ ]
256
+ }
257
+ ```
258
+
259
+ **Configuration Options:**
260
+
261
+ - `pod` (required): Name of the Kubernetes pod
262
+ - `username` (required): MongoDB username
263
+ - `password` (required): MongoDB password
264
+ - `database` (required): Database name
265
+ - `backupPath` (optional): Path inside pod for temporary backup (default: `/data/backup`)
266
+ - `localBackupPath` (optional): Local path to store backups (default: `~/backups/mongo`)
267
+
268
+ #### RESTORE_CONFIG
269
+
270
+ Defines where to restore the backup:
271
+
272
+ ```json
273
+ RESTORE_CONFIG={
274
+ "mongoInDocker": false,
275
+ "containerName": "mongodb",
276
+ "localBackupPath": "~/backups/mongo",
277
+ "containerBackupPath": "/data/backup"
278
+ }
279
+ ```
280
+
281
+ **Configuration Options:**
282
+
283
+ - `mongoInDocker` (optional): Set to `true` to restore to Docker container, `false` for local MongoDB (can be overridden with `-docker` flag)
284
+ - `containerName` (optional): Docker container name (default: `mongodb`)
285
+ - `localBackupPath` (optional): Path where backup is stored (default: `~/backups/mongo`)
286
+ - `containerBackupPath` (optional): Path inside container for temporary files (default: `/data/backup`)
287
+
288
+ ### How It Works
289
+
290
+ 1. **Backup Phase:**
291
+
292
+ - Executes `mongodump` inside the Kubernetes pod
293
+ - Copies backup files to your local machine using `kubectl cp`
294
+ - Cleans up temporary files from the pod
295
+ - Saves to `~/backups/mongo/<database-name>` by default
296
+
297
+ 2. **Restore Phase:**
298
+ - **Docker Mode** (`-docker` flag):
299
+ - Copies backup from local machine to Docker container
300
+ - Executes `mongorestore --drop` inside the container
301
+ - Cleans up temporary files from container
302
+ - **Local Mode** (default):
303
+ - Executes `mongorestore --drop` directly on local machine
304
+ - Connects to `mongodb://localhost:27017/`
305
+
306
+ ### Usage Examples
307
+
308
+ **Backup all configured projects:**
309
+
310
+ ```bash
311
+ nitor backup
312
+ ```
313
+
314
+ **Backup specific projects:**
315
+
316
+ ```bash
317
+ nitor backup -project project1 project2
318
+ ```
319
+
320
+ **Backup and restore to Docker container:**
321
+
322
+ ```bash
323
+ nitor backup -docker
324
+ ```
325
+
326
+ ### Prerequisites
327
+
328
+ - **For Backup:** `kubectl` configured with access to your Kubernetes cluster
329
+ - **For Restore (Docker):** Docker running with MongoDB container
330
+ - **For Restore (Local):** MongoDB installed and running locally on port 27017
331
+ - MongoDB tools (`mongodump`, `mongorestore`) must be available in the pod/container
332
+
333
+ ### Cross-Platform Support
334
+
335
+ The backup service is designed to work on Windows, macOS, and Linux:
336
+
337
+ - Automatically detects the operating system
338
+ - Uses appropriate shell (`cmd.exe` on Windows, `/bin/sh` on Unix)
339
+ - Handles path normalization across platforms
340
+ - Adjusts command flags for Windows compatibility (e.g., removes `-it` flags)
341
+
342
+ ### Error Handling
343
+
344
+ - Non-zero exit codes from `mongorestore` are treated as warnings (common with duplicate keys)
345
+ - Detailed error messages with color-coded output
346
+ - Continues processing remaining projects even if one fails
347
+ - Cleanup operations run even if restore encounters errors
348
+
211
349
  ### Options
212
350
 
213
351
  - `-project` or `-p` : Project name (`portal`, `gateway`, `phr`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitor",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "A comprehensive CLI toolkit for automating GitLab operations, AI-powered code review, build/deploy automation, MongoDB backup/restore, and developer productivity tools",
5
5
  "main": "index.js",
6
6
  "author": "Nithin V <mails2nithin@gmail.com>",
@@ -45,12 +45,18 @@ const executeMongoRestore = async (config) => {
45
45
  // Step 2: Execute mongorestore inside the container
46
46
  // Remove -it flag for Windows compatibility (causes issues in non-interactive shells)
47
47
  const restoreCommand = isWindows
48
- ? `docker exec ${containerName} mongorestore ${containerBackupPath}`
49
- : `docker exec -it ${containerName} mongorestore ${containerBackupPath}`;
48
+ ? `docker exec ${containerName} mongorestore --drop --stopOnError=false ${containerBackupPath}`
49
+ : `docker exec -it ${containerName} mongorestore --drop --stopOnError=false ${containerBackupPath}`;
50
50
 
51
51
  console.log('Executing mongorestore...');
52
- execSync(restoreCommand, { stdio: 'inherit', shell: isWindows ? 'cmd.exe' : '/bin/sh' });
53
- console.log(`${colors.green}✓ Mongorestore completed successfully${colors.reset}`);
52
+ try {
53
+ execSync(restoreCommand, { stdio: 'inherit', shell: isWindows ? 'cmd.exe' : '/bin/sh' });
54
+ console.log(`${colors.green}✓ Mongorestore completed successfully${colors.reset}`);
55
+ } catch (error) {
56
+ console.log(
57
+ `${colors.red}⚠ Mongorestore completed with some errors (check output above for details)${colors.reset}`,
58
+ );
59
+ }
54
60
 
55
61
  // Step 3: Remove backup data from container
56
62
  const cleanupCommand = isWindows
@@ -66,12 +72,21 @@ const executeMongoRestore = async (config) => {
66
72
  console.log(`\nStarting MongoDB restore to local machine`);
67
73
 
68
74
  console.log('Executing mongorestore...');
69
- const localRestoreCommand = `mongorestore --uri="mongodb://localhost:27017/" "${normalizedLocalPath}"`;
70
- execSync(localRestoreCommand, {
71
- stdio: 'inherit',
72
- shell: isWindows ? 'cmd.exe' : '/bin/sh',
73
- });
74
- console.log(`${colors.green}✓ Mongorestore completed successfully${colors.reset}`);
75
+ const localRestoreCommand = `mongorestore --drop --stopOnError=false --uri="mongodb://localhost:27017/" "${normalizedLocalPath}"`;
76
+ try {
77
+ execSync(localRestoreCommand, {
78
+ stdio: 'inherit',
79
+ shell: isWindows ? 'cmd.exe' : '/bin/sh',
80
+ });
81
+ console.log(`${colors.green}✓ Mongorestore completed successfully${colors.reset}`);
82
+ } catch (error) {
83
+ // mongorestore returns non-zero even for partial success (e.g., duplicate keys)
84
+ // Log the warning but don't throw unless it's a critical failure
85
+ console.log(
86
+ `${colors.red}⚠ Mongorestore completed with some errors (this may be expected if data already exists)${colors.reset}`,
87
+ );
88
+ console.log(`Check the output above for details`);
89
+ }
75
90
 
76
91
  console.log(`${colors.green}Restore completed successfully!${colors.reset}\n`);
77
92
  }
@@ -11,7 +11,7 @@ const { merge } = require('./merge');
11
11
  const { cleanup } = require('./cleanup');
12
12
 
13
13
  // Time entry imports
14
- const { removeEmpty, getSprints } = require('./time-entry/utils');
14
+ const { getSprints } = require('./time-entry/utils');
15
15
  const { getStatus, getTasksByDate } = require('./time-entry/get-report');
16
16
  const { logTaskHoursAndSync } = require('./time-entry/log-task-hours-and-sync');
17
17
  const { addNewTask } = require('./time-entry/add-task');
@@ -199,7 +199,7 @@ Enhance the provided text for improved clarity, conciseness, and professional qu
199
199
  Backup mongodb for specified project and components.
200
200
 
201
201
  project list:
202
- - configCervice
202
+ - configService
203
203
  - medicaCentralAuth
204
204
  - medicaPortal
205
205
  - phr`);
@@ -387,7 +387,7 @@ Options:
387
387
 
388
388
  // Time entry commands
389
389
  case ACTIONS.TIME_ADD: {
390
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
390
+ const timeValues = value ? value.split(' -') : value;
391
391
 
392
392
  await addNewTask(timeValues);
393
393
 
@@ -395,7 +395,7 @@ Options:
395
395
  }
396
396
 
397
397
  case ACTIONS.TIME_UPDATE: {
398
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
398
+ const timeValues = value ? value.split(' -') : value;
399
399
 
400
400
  await updateTask(timeValues);
401
401
 
@@ -403,7 +403,7 @@ Options:
403
403
  }
404
404
 
405
405
  case ACTIONS.TIME_DELETE: {
406
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
406
+ const timeValues = value ? value.split(' -') : value;
407
407
 
408
408
  console.log(await deleteTask(timeValues));
409
409
 
@@ -411,7 +411,7 @@ Options:
411
411
  }
412
412
 
413
413
  case ACTIONS.TIME_STATUS: {
414
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
414
+ const timeValues = value ? value.split(' -') : value;
415
415
 
416
416
  await getStatus(timeValues);
417
417
 
@@ -419,7 +419,7 @@ Options:
419
419
  }
420
420
 
421
421
  case ACTIONS.TIME_ENTRIES: {
422
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
422
+ const timeValues = value ? value.split(' -') : value;
423
423
 
424
424
  console.table(await getTasksByDate(timeValues));
425
425
 
@@ -433,7 +433,7 @@ Options:
433
433
  }
434
434
 
435
435
  case ACTIONS.TIME_GITLAB: {
436
- const timeValues = value ? removeEmpty(value?.split(' -')) : value;
436
+ const timeValues = value ? value.split(' -') : value;
437
437
 
438
438
  await getGitlabActivities(timeValues);
439
439
 
@@ -505,6 +505,7 @@ Options:
505
505
  \t[${ACTIONS.BUILD}] [${ACTIONS.DEPLOY}] [${ACTIONS.BUILD_DEPLOY}]
506
506
  \t[${ACTIONS.CREATE_BRANCH}] [${ACTIONS.REVIEW}] [${ACTIONS.MERGE}]
507
507
  \t[${ACTIONS.CLEANUP}] [${ACTIONS.BACKUP}] [${ACTIONS.REFACTOR}]
508
+ \t[${ACTIONS.TASK_STATS}] [${ACTIONS.GET_TASK}]
508
509
  \t[${ACTIONS.TIME_INIT}] [${ACTIONS.TIME_ADD}] [${ACTIONS.TIME_STATUS}]\n
509
510
  Available commands:\n
510
511
  backup : Backup MongoDB databases
@@ -514,11 +515,11 @@ Available commands:\n
514
515
  completion : Setup shell autocomplete
515
516
  create-branch : Create git branch
516
517
  deploy : Deploy specified components
517
- get-task : Get task details
518
+ get-task : Get task details from GitLab merge requests
518
519
  merge : Merge source branch into target branch
519
520
  refactor : Refactor the provided text for improved clarity, conciseness, and professional quality
520
521
  review : AI Review specified merge request
521
- task-stats : View task statistics
522
+ task-stats : View task statistics and merge request details
522
523
  version : Show version info
523
524
  help : Show help
524
525
 
@@ -542,9 +543,11 @@ Example usage:\n
542
543
  nitor cleanup
543
544
  nitor create-branch -task <task number> -type <feat|fix> -description <description> -project <project short name>
544
545
  nitor deploy -project <project> -components <components> -instance <instance>
546
+ nitor get-task -task <task numbers with space>
545
547
  nitor merge -source <source branch> -target <target branch>
546
548
  nitor refactor <text>
547
549
  nitor review -project <project short name> -mergeId <merge id> -repository <repository name>
550
+ nitor task-stats -task <task number>
548
551
  nitor time-init
549
552
  nitor time-add
550
553
  nitor time-stats
@@ -561,9 +564,8 @@ Running 'nitor help' will list available subcommands and provide some conceptual
561
564
  }
562
565
  } catch (error) {
563
566
  console.log(error);
567
+ process.exit(1);
564
568
  }
565
-
566
- process.exit(1);
567
569
  };
568
570
 
569
571
  module.exports = { processArgs };
@@ -122,6 +122,8 @@ const getGitMergeRequestDetails = async (issueIid, projectId) => {
122
122
  };
123
123
 
124
124
  const getGitlabIssueMergeRequests = async (gitDetails) => {
125
+ const skippedMrBranches = ['dev-qa-testing', 'qa-testing'];
126
+
125
127
  for (const gitDetail of gitDetails) {
126
128
  try {
127
129
  const mergeRequests = await getGitMergeRequestDetails(
@@ -137,6 +139,10 @@ const getGitlabIssueMergeRequests = async (gitDetails) => {
137
139
  const detailResponse = await axios.request(detailConfig);
138
140
  const mrData = detailResponse.data;
139
141
 
142
+ if (skippedMrBranches.some((branch) => mrData.target_branch.includes(branch))) {
143
+ return;
144
+ }
145
+
140
146
  // Fetch approval details separately
141
147
  try {
142
148
  const approvalConfig = getGitlabConfig(
@@ -150,7 +156,7 @@ const getGitlabIssueMergeRequests = async (gitDetails) => {
150
156
  IssueID: gitDetail.iid,
151
157
  MRID: mrData.iid,
152
158
  MRAssignedTo: mrData.assignee?.name,
153
- MRPointedTo: mrData.target_branch,
159
+ MRTarget: mrData.target_branch,
154
160
  Repo: mr.reference?.split('!')?.[0],
155
161
  ApprovedBy:
156
162
  (approvalData.approved_by || [])
@@ -4,7 +4,6 @@ const {
4
4
  path,
5
5
  readFileSync,
6
6
  userHomeDir,
7
- removeEmpty,
8
7
  } = require('./utils');
9
8
  const getTasksByDate = async (filters) => {
10
9
  try {
@@ -29,7 +28,10 @@ const getTasksByDate = async (filters) => {
29
28
  const item = readFileSync(path.join(userHomeDir, `.${stringDate}`));
30
29
 
31
30
  if (item) {
32
- const timeEntries = removeEmpty(item.split(contentTableSeparator)[1].split('\n'));
31
+ const timeEntries = item
32
+ .split(contentTableSeparator)[1]
33
+ .split('\n')
34
+ .filter((entry) => entry.trim() !== '');
33
35
 
34
36
  data = data.concat(
35
37
  timeEntries.map((entry) =>
@@ -228,29 +228,6 @@ const readFileSync = (path) => {
228
228
  // console.log(error);
229
229
  }
230
230
  };
231
- const removeEmpty = (obj) => {
232
- for (let [key, val] of Object.entries(obj)) {
233
- if (val && typeof val === 'object') {
234
- this.removeEmpty(val);
235
-
236
- if (!(Object.keys(val).length || val instanceof Date)) {
237
- delete obj[key];
238
- }
239
- } else {
240
- if (typeof val === 'string') {
241
- val = val.trim();
242
- }
243
-
244
- if (val === null || val === undefined || val === '') {
245
- delete obj[key];
246
- } else {
247
- obj[key] = val;
248
- }
249
- }
250
- }
251
-
252
- return obj;
253
- };
254
231
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
255
232
  const userConfig = async (jsonData) => {
256
233
  return new Promise((resolve, reject) => {
@@ -293,7 +270,6 @@ module.exports = {
293
270
  mkdirSync,
294
271
  path,
295
272
  readFileSync,
296
- removeEmpty,
297
273
  rl,
298
274
  userConfig,
299
275
  userHomeDir,
package/services/utils.js CHANGED
@@ -22,7 +22,7 @@ const gitlabConfig = {
22
22
  const zohoConfig = {
23
23
  cookie: process.env.ZOHO_COOKIE,
24
24
  customViewId: process.env.ZOHO_CUSTOMVIEW_ID,
25
- defaultProjectId: process.env.ZOHO_DEFAULt_PROJECT_ID,
25
+ defaultProjectId: process.env.ZOHO_DEFAULT_PROJECT_ID,
26
26
  portalId: process.env.ZOHO_PORTAL_ID,
27
27
  projects: process.env.ZOHO_PROJECTS,
28
28
  sessionId: process.env.ZOHO_SESSION_ID,