contensis-cli 1.0.0-beta.9 → 1.0.0-beta.90

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.
Files changed (125) hide show
  1. package/README.md +1146 -78
  2. package/cli.js +3 -0
  3. package/dist/commands/connect.js +3 -3
  4. package/dist/commands/connect.js.map +2 -2
  5. package/dist/commands/create.js +45 -10
  6. package/dist/commands/create.js.map +2 -2
  7. package/dist/commands/dev.js +75 -0
  8. package/dist/commands/dev.js.map +7 -0
  9. package/dist/commands/diff.js +57 -0
  10. package/dist/commands/diff.js.map +7 -0
  11. package/dist/commands/execute.js +103 -0
  12. package/dist/commands/execute.js.map +7 -0
  13. package/dist/commands/get.js +169 -32
  14. package/dist/commands/get.js.map +3 -3
  15. package/dist/commands/globalOptions.js +37 -12
  16. package/dist/commands/globalOptions.js.map +2 -2
  17. package/dist/commands/import.js +47 -12
  18. package/dist/commands/import.js.map +2 -2
  19. package/dist/commands/index.js +22 -2
  20. package/dist/commands/index.js.map +2 -2
  21. package/dist/commands/list.js +53 -10
  22. package/dist/commands/list.js.map +2 -2
  23. package/dist/commands/login.js +2 -2
  24. package/dist/commands/login.js.map +2 -2
  25. package/dist/commands/push.js +17 -13
  26. package/dist/commands/push.js.map +2 -2
  27. package/dist/commands/remove.js +51 -8
  28. package/dist/commands/remove.js.map +2 -2
  29. package/dist/commands/set.js +139 -12
  30. package/dist/commands/set.js.map +2 -2
  31. package/dist/index.js +1 -1
  32. package/dist/index.js.map +2 -2
  33. package/dist/localisation/en-GB.js +259 -49
  34. package/dist/localisation/en-GB.js.map +2 -2
  35. package/dist/mappers/ContensisCliService-to-RequestHanderSiteConfigYaml.js +56 -0
  36. package/dist/mappers/ContensisCliService-to-RequestHanderSiteConfigYaml.js.map +7 -0
  37. package/dist/mappers/DevInit-to-CIWorkflow.js +127 -0
  38. package/dist/mappers/DevInit-to-CIWorkflow.js.map +7 -0
  39. package/dist/mappers/DevInit-to-RolePermissions.js +54 -0
  40. package/dist/mappers/DevInit-to-RolePermissions.js.map +7 -0
  41. package/dist/mappers/DevRequests-to-RequestHanderSiteConfigYaml.js +56 -0
  42. package/dist/mappers/DevRequests-to-RequestHanderSiteConfigYaml.js.map +7 -0
  43. package/dist/models/CliService.d.js +17 -0
  44. package/dist/models/CliService.d.js.map +7 -0
  45. package/dist/models/DevService.d.js +17 -0
  46. package/dist/models/DevService.d.js.map +7 -0
  47. package/dist/providers/CredentialProvider.js +46 -14
  48. package/dist/providers/CredentialProvider.js.map +3 -3
  49. package/dist/providers/SessionCacheProvider.js +21 -1
  50. package/dist/providers/SessionCacheProvider.js.map +2 -2
  51. package/dist/providers/file-provider.js +12 -6
  52. package/dist/providers/file-provider.js.map +3 -3
  53. package/dist/services/ContensisCliService.js +1148 -421
  54. package/dist/services/ContensisCliService.js.map +3 -3
  55. package/dist/services/ContensisDevService.js +309 -0
  56. package/dist/services/ContensisDevService.js.map +7 -0
  57. package/dist/services/ContensisRoleService.js +87 -0
  58. package/dist/services/ContensisRoleService.js.map +7 -0
  59. package/dist/shell.js +58 -18
  60. package/dist/shell.js.map +3 -3
  61. package/dist/util/console.printer.js +171 -55
  62. package/dist/util/console.printer.js.map +2 -2
  63. package/dist/util/diff.js +102 -0
  64. package/dist/util/diff.js.map +7 -0
  65. package/dist/util/dotenv.js +57 -0
  66. package/dist/util/dotenv.js.map +7 -0
  67. package/dist/util/find.js +31 -0
  68. package/dist/util/find.js.map +7 -0
  69. package/dist/util/git.js +126 -0
  70. package/dist/util/git.js.map +7 -0
  71. package/dist/util/index.js +8 -2
  72. package/dist/util/index.js.map +3 -3
  73. package/dist/util/logger.js +90 -29
  74. package/dist/util/logger.js.map +3 -3
  75. package/dist/util/os.js +39 -0
  76. package/dist/util/os.js.map +7 -0
  77. package/dist/util/timers.js +49 -0
  78. package/dist/util/timers.js.map +7 -0
  79. package/dist/util/yaml.js +45 -0
  80. package/dist/util/yaml.js.map +7 -0
  81. package/dist/version.js +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/esbuild.config.js +3 -1
  84. package/package.json +12 -3
  85. package/src/commands/connect.ts +3 -2
  86. package/src/commands/create.ts +61 -8
  87. package/src/commands/dev.ts +69 -0
  88. package/src/commands/diff.ts +41 -0
  89. package/src/commands/execute.ts +117 -0
  90. package/src/commands/get.ts +242 -28
  91. package/src/commands/globalOptions.ts +42 -12
  92. package/src/commands/import.ts +58 -8
  93. package/src/commands/index.ts +22 -1
  94. package/src/commands/list.ts +85 -11
  95. package/src/commands/login.ts +2 -1
  96. package/src/commands/push.ts +18 -11
  97. package/src/commands/remove.ts +66 -4
  98. package/src/commands/set.ts +189 -9
  99. package/src/index.ts +1 -4
  100. package/src/localisation/en-GB.ts +374 -66
  101. package/src/mappers/ContensisCliService-to-RequestHanderSiteConfigYaml.ts +44 -0
  102. package/src/mappers/DevInit-to-CIWorkflow.ts +150 -0
  103. package/src/mappers/DevInit-to-RolePermissions.ts +33 -0
  104. package/src/mappers/DevRequests-to-RequestHanderSiteConfigYaml.ts +44 -0
  105. package/src/models/CliService.d.ts +36 -0
  106. package/src/models/DevService.d.ts +5 -0
  107. package/src/models/JsModules.d.ts +1 -0
  108. package/src/providers/CredentialProvider.ts +51 -18
  109. package/src/providers/SessionCacheProvider.ts +29 -2
  110. package/src/providers/file-provider.ts +17 -6
  111. package/src/services/ContensisCliService.ts +1458 -518
  112. package/src/services/ContensisDevService.ts +365 -0
  113. package/src/services/ContensisRoleService.ts +76 -0
  114. package/src/shell.ts +68 -18
  115. package/src/util/console.printer.ts +240 -78
  116. package/src/util/diff.ts +113 -0
  117. package/src/util/dotenv.ts +37 -0
  118. package/src/util/find.ts +8 -0
  119. package/src/util/git.ts +130 -0
  120. package/src/util/index.ts +16 -7
  121. package/src/util/logger.ts +145 -31
  122. package/src/util/os.ts +7 -0
  123. package/src/util/timers.ts +24 -0
  124. package/src/util/yaml.ts +13 -0
  125. package/src/version.ts +1 -1
@@ -1,6 +1,7 @@
1
1
  import dayjs from 'dayjs';
2
- import { BlockVersion, MigrateStatus } from 'migratortron';
2
+ import { BlockVersion, MigrateModelsResult, MigrateStatus } from 'migratortron';
3
3
  import ContensisCli from '~/services/ContensisCliService';
4
+ import { Logger } from './logger';
4
5
 
5
6
  const formatDate = (date: Date | string, format = 'DD/MM/YYYY HH:mm') =>
6
7
  dayjs(date).format(format);
@@ -104,57 +105,22 @@ export const printBlockVersion = (
104
105
  };
105
106
 
106
107
  export const printMigrateResult = (
107
- { log, messages, contensis, currentProject }: ContensisCli,
108
+ { log, messages, currentProject }: ContensisCli,
108
109
  migrateResult: any,
109
- { action = 'import' }: { action?: 'import' | 'delete' } = {}
110
+ {
111
+ action = 'import',
112
+ showDiff = false,
113
+ showAllEntries = false,
114
+ showChangedEntries = false,
115
+ }: {
116
+ action?: 'import' | 'delete';
117
+ showDiff?: boolean;
118
+ showAllEntries?: boolean;
119
+ showChangedEntries?: boolean;
120
+ } = {}
110
121
  ) => {
111
122
  console.log(``);
112
123
 
113
- if (action === 'import') {
114
- for (const [projectId, contentTypeCounts] of Object.entries(
115
- migrateResult.entries || {}
116
- ) as [string, any][]) {
117
- console.log(
118
- `import from project ${log.highlightText(projectId)} to ${log.boldText(
119
- log.successText(currentProject)
120
- )}`
121
- );
122
- for (const [contentTypeId, count] of Object.entries(
123
- contentTypeCounts
124
- ) as [string, number][]) {
125
- const entriesToMigrate =
126
- migrateResult.entriesToMigrate?.[projectId]?.[contentTypeId];
127
-
128
- console.log(
129
- ` - ${
130
- contentTypeId === 'totalCount'
131
- ? log.warningText(`${contentTypeId}: ${count}`)
132
- : log.helpText(`${contentTypeId}: ${count}`)
133
- } ${log.infoText`[existing: ${(
134
- ((migrateResult.existing?.[projectId]?.[contentTypeId] || 0) /
135
- count) *
136
- 100
137
- ).toFixed(0)}%]`} [${
138
- typeof entriesToMigrate !== 'number' ? `unchanged` : `to update`
139
- }: ${(
140
- ((typeof entriesToMigrate !== 'number'
141
- ? entriesToMigrate?.['no change'] || 0
142
- : entriesToMigrate) /
143
- count) *
144
- 100
145
- ).toFixed(0)}%]`
146
- );
147
- }
148
- console.log(``);
149
- }
150
- }
151
- if (
152
- contensis?.isPreview &&
153
- migrateResult.entriesToMigrate?.[currentProject]?.totalCount > 0 &&
154
- !migrateResult.errors
155
- ) {
156
- log.help(messages.entries.commitTip());
157
- }
158
124
  for (const [contentTypeId, entryRes] of Object.entries(
159
125
  migrateResult.entriesToMigrate.entryIds
160
126
  ) as [string, any]) {
@@ -162,42 +128,238 @@ export const printMigrateResult = (
162
128
  string,
163
129
  any
164
130
  ][]) {
131
+ if (
132
+ showAllEntries ||
133
+ (showChangedEntries &&
134
+ (
135
+ Object.entries(
136
+ Object.entries(entryStatus[currentProject])[0]
137
+ )[1][1] as any
138
+ ).status !== 'no change')
139
+ ) {
140
+ console.log(
141
+ log.infoText(
142
+ `${originalId} ${Object.entries(entryStatus || {})
143
+ .filter(x => x[0] !== 'entryTitle')
144
+ .map(([projectId, projectStatus]) => {
145
+ const [targetGuid, { status }] = (Object.entries(
146
+ projectStatus || {}
147
+ )?.[0] as [string, { status: MigrateStatus }]) || [
148
+ '',
149
+ { x: { status: undefined } },
150
+ ];
151
+ return `${messages.migrate.status(status)(`${status}`)}${
152
+ targetGuid !== originalId ? `-> ${targetGuid}` : ''
153
+ }`;
154
+ })}`
155
+ ) + ` ${log.helpText(contentTypeId)} ${entryStatus.entryTitle}`
156
+ );
157
+
158
+ for (const [projectId, projectStatus] of Object.entries(
159
+ entryStatus
160
+ ).filter(([key]) => key !== 'entryTitle') as [string, any][]) {
161
+ const [targetGuid, { error, diff, status }] = Object.entries(
162
+ projectStatus
163
+ )[0] as [string, any];
164
+ if (error) log.error(error);
165
+ if (diff && showDiff) {
166
+ console.log(
167
+ ` ${log.highlightText(`diff:`)} ${log.infoText(
168
+ highlightDiffText(diff)
169
+ )}\n`
170
+ );
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ if (showAllEntries || showChangedEntries) console.log(``);
177
+
178
+ for (const [projectId, contentTypeCounts] of Object.entries(
179
+ migrateResult.entries || {}
180
+ ) as [string, any][]) {
181
+ log.help(
182
+ `${action} from project ${
183
+ action === 'delete'
184
+ ? log.warningText(currentProject)
185
+ : `${log.highlightText(projectId)} to ${log.boldText(
186
+ log.warningText(currentProject)
187
+ )}`
188
+ }`
189
+ );
190
+ for (const [contentTypeId, count] of Object.entries(contentTypeCounts) as [
191
+ string,
192
+ number
193
+ ][]) {
194
+ const isTotalCountRow = contentTypeId === 'totalCount';
195
+ const migrateStatusAndCount =
196
+ migrateResult.entriesToMigrate[currentProject][contentTypeId];
197
+ const existingCount =
198
+ migrateResult.existing?.[currentProject]?.[contentTypeId] || 0;
199
+ const existingPercent = ((existingCount / count) * 100).toFixed(0);
200
+ const noChangeOrTotalEntriesCount =
201
+ typeof migrateStatusAndCount !== 'number'
202
+ ? migrateStatusAndCount?.['no change'] || 0
203
+ : migrateStatusAndCount;
204
+
205
+ const changedPercentage = (
206
+ (noChangeOrTotalEntriesCount / count) *
207
+ 100
208
+ ).toFixed(0);
209
+
210
+ const existingColor =
211
+ existingPercent === '0' || action === 'delete'
212
+ ? log.warningText
213
+ : log.infoText;
214
+
215
+ const changedColor = isTotalCountRow
216
+ ? log.helpText
217
+ : changedPercentage === '100'
218
+ ? log.successText
219
+ : log.warningText;
220
+
165
221
  console.log(
166
- log.infoText(
167
- `${originalId} [${Object.entries(entryStatus || {})
168
- .filter(x => x[0] !== 'entryTitle')
169
- .map(([projectId, projectStatus]) => {
170
- const [targetGuid, { status }] = (Object.entries(
171
- projectStatus || {}
172
- )?.[0] as [string, { status: MigrateStatus }]) || [
173
- '',
174
- { x: { status: undefined } },
175
- ];
176
- return `${messages.entries.migrateStatus(status)(
177
- `${projectId}: ${status}`
178
- )}${targetGuid !== originalId ? `-> ${targetGuid}` : ''}`;
179
- })}]`
180
- )
222
+ ` - ${
223
+ isTotalCountRow
224
+ ? log.highlightText(
225
+ `${contentTypeId}: ${noChangeOrTotalEntriesCount}`
226
+ )
227
+ : `${contentTypeId}: ${log.helpText(count)}`
228
+ }${
229
+ changedPercentage === '100' || isTotalCountRow
230
+ ? ''
231
+ : existingColor(` [existing: ${`${existingPercent}%`}]`)
232
+ }${
233
+ existingPercent === '0' || (action === 'import' && isTotalCountRow)
234
+ ? ''
235
+ : changedColor(
236
+ ` ${
237
+ isTotalCountRow
238
+ ? `[to ${action}: ${noChangeOrTotalEntriesCount}]`
239
+ : changedPercentage === '100'
240
+ ? 'up to date'
241
+ : `[needs update: ${100 - Number(changedPercentage)}%]`
242
+ }`
243
+ )
244
+ }`
245
+ );
246
+ }
247
+ }
248
+ if (migrateResult.errors?.length) {
249
+ console.log(
250
+ ` - ${log.errorText(`errors: ${migrateResult.errors.length}`)}\n`
251
+ );
252
+ for (const error of migrateResult.errors)
253
+ log.error(error.message || error, null, '');
254
+ }
255
+ };
256
+
257
+ const highlightDiffText = (str: string) => {
258
+ const addedRegex = new RegExp(/<<\+>>(.*?)<<\/\+>>/, 'g');
259
+ const removedRegex = new RegExp(/<<->>(.*?)<<\/->>/, 'g');
260
+ return str
261
+ .replace(addedRegex, match => {
262
+ return Logger.successText(
263
+ match.replace(/<<\+>>/g, '<+>').replace(/<<\/\+>>/g, '</+>')
264
+ );
265
+ })
266
+ .replace(removedRegex, match => {
267
+ return Logger.errorText(
268
+ match.replace(/<<->>/g, '<->').replace(/<<\/->>/g, '</->')
181
269
  );
182
- console.log(` ${log.helpText(contentTypeId)} ${entryStatus.entryTitle}`);
270
+ });
271
+ };
183
272
 
184
- for (const [projectId, projectStatus] of Object.entries(
185
- entryStatus
186
- ).filter(([key]) => key !== 'entryTitle') as [string, any][]) {
187
- const [targetGuid, { error, diff, status }] = Object.entries(
188
- projectStatus
189
- )[0] as [string, any];
190
- if (error) log.error(error);
191
- if (diff) {
192
- console.log(
193
- ` ${messages.entries.migrateStatus(status)(status)} ${log.infoText(
194
- targetGuid
195
- )} ${log.infoText(contentTypeId)} ${log.infoText(projectId)}`
273
+ export const printModelMigrationAnalysis = (
274
+ { log, messages }: ContensisCli,
275
+ result: any = {}
276
+ ) => {
277
+ for (const [contentTypeId, model] of Object.entries(result) as [
278
+ string,
279
+ any
280
+ ][]) {
281
+ let mainOutput = log.standardText(` - ${contentTypeId}`);
282
+ let extraOutput = '';
283
+ let errorOutput = '';
284
+ let diffOutput = '';
285
+ for (const [key, details] of Object.entries(model) as [string, any][]) {
286
+ if (key === 'dependencies') {
287
+ extraOutput += log.infoText(
288
+ ` references: [${details?.join(', ')}]\n`
289
+ );
290
+ }
291
+ if (key === 'dependencyOf') {
292
+ extraOutput += log.infoText(
293
+ ` required by: [${details?.join(', ')}]\n`
294
+ );
295
+ }
296
+ if (key === 'projects') {
297
+ for (const [projectId, projectDetails] of Object.entries(details) as [
298
+ string,
299
+ any
300
+ ][]) {
301
+ mainOutput += log.infoText(
302
+ ` [${messages.migrate.status(projectDetails.status)(
303
+ `${projectId}: ${projectDetails.status}`
304
+ )}] v${projectDetails.versionNo}`
196
305
  );
197
- console.log(``);
198
- console.log(log.highlightText(diff));
306
+ if (projectDetails.diff)
307
+ diffOutput += ` ${log.highlightText(`diff:`)} ${log.infoText(
308
+ highlightDiffText(projectDetails.diff)
309
+ )}\n`;
310
+ if (projectDetails.error)
311
+ errorOutput += ` ${log.highlightText(
312
+ `error::`
313
+ )} ${log.errorText(projectDetails.error)}`;
199
314
  }
200
315
  }
201
316
  }
317
+ console.log(mainOutput);
318
+ if (extraOutput) {
319
+ const search = '\n';
320
+ const replace = '';
321
+ console.log(
322
+ extraOutput.replace(
323
+ new RegExp(search + '([^' + search + ']*)$'),
324
+ replace + '$1'
325
+ )
326
+ );
327
+ }
328
+ if (diffOutput) console.log(diffOutput);
329
+ if (errorOutput) console.log(errorOutput);
330
+ }
331
+ };
332
+
333
+ type MigrateResultSummary = MigrateModelsResult['']['contentTypes'];
334
+ type MigrateResultStatus = keyof MigrateResultSummary;
335
+
336
+ export const printModelMigrationResult = (
337
+ { log, messages }: ContensisCli,
338
+ result: MigrateResultSummary
339
+ ) => {
340
+ for (const [status, ids] of Object.entries(result) as [
341
+ MigrateResultStatus,
342
+ string[]
343
+ ][]) {
344
+ if (ids?.length) {
345
+ if (status === 'errors') {
346
+ const errors: [string, MappedError][] = ids as any;
347
+ log.raw(
348
+ ` - ${status}: [ ${messages.migrate.models.result(status)(
349
+ ids.map(id => id[0]).join(', ')
350
+ )} ]\n`
351
+ );
352
+ for (const [contentTypeId, error] of errors)
353
+ log.error(
354
+ `${log.highlightText(contentTypeId)}: ${error.message}`,
355
+ error
356
+ );
357
+ } else
358
+ log.raw(
359
+ ` - ${status}: [ ${messages.migrate.models.result(status)(
360
+ ids.join(', ')
361
+ )} ]`
362
+ );
363
+ }
202
364
  }
203
365
  };
@@ -0,0 +1,113 @@
1
+ import chalk from 'chalk';
2
+ import { Change, diffLines } from 'diff';
3
+ import { add } from 'lodash';
4
+
5
+ export const diffLogStrings = (updates: string, previous: string) => {
6
+ const lastFewLines = previous.split('\n').slice(-10);
7
+ const incomingLines = updates.split('\n');
8
+
9
+ // Find the line indices in the incoming lines
10
+ // of the last few lines previously rendered
11
+ const incomingLineIndices = [];
12
+ for (const lastRenderedLine of lastFewLines) {
13
+ if (lastRenderedLine.length > 10)
14
+ incomingLineIndices.push(incomingLines.lastIndexOf(lastRenderedLine));
15
+ }
16
+
17
+ // Get the new lines from the next position on from the last of the already shown lines
18
+ const differentFromPos = Math.max(...incomingLineIndices) + 1 || 0;
19
+ // Return just the incoming lines from the position we matched
20
+ return incomingLines.slice(differentFromPos).join('\n');
21
+ };
22
+
23
+ export const diffFileContent = (
24
+ existingContent: string,
25
+ newContent: string
26
+ ) => {
27
+ const diff = diffLines(existingContent, newContent, { newlineIsToken: true });
28
+ const diffRanges = addDiffPositionInfo(diff);
29
+
30
+ // Create formatted output for console
31
+ const output: string[] = [];
32
+ const lnSpaceLength = Math.max(
33
+ ...diffRanges.map(d => d.startLineNumber.toString().length)
34
+ );
35
+
36
+ const lnSpaces = Array(lnSpaceLength).join(' ');
37
+
38
+ for (let i = 0; i < diffRanges.length; i++) {
39
+ const part = diffRanges[i];
40
+ if (part.added || part.removed) {
41
+ const colour = part.added ? 'green' : part.removed ? 'red' : 'grey';
42
+
43
+ if (part.value !== '\n')
44
+ output.push(
45
+ `\n${part.value
46
+ .split('\n')
47
+ .map((ln, idx) =>
48
+ ln.trim() !== ''
49
+ ? `${
50
+ part.startLineNumber ? part.startLineNumber + idx : lnSpaces
51
+ }${part.added ? '+' : part.removed ? '-' : ' '} ${chalk[
52
+ colour
53
+ ](`${ln}`)}`
54
+ : ln
55
+ )
56
+ .join('\n')}`
57
+ );
58
+ }
59
+ }
60
+
61
+ return output.join('');
62
+ // return retOutput.endsWith('\n') ? retOutput : `${retOutput}\n`;
63
+ };
64
+
65
+ const addDiffPositionInfo = (diff: Change[]) => {
66
+ const diffRanges: (Change & {
67
+ startLineNumber: number;
68
+ startColumn: number;
69
+ endLineNumber: number;
70
+ endColumn: number;
71
+ })[] = [];
72
+
73
+ let lineNumber = 0;
74
+ let column = 0;
75
+ for (let partIndex = 0; partIndex < diff.length; partIndex++) {
76
+ const part = diff[partIndex];
77
+
78
+ // // Skip any parts that aren't in `after`
79
+ // if (part.removed === true) {
80
+ // continue;
81
+ // }
82
+
83
+ const startLineNumber = lineNumber;
84
+ const startColumn = column;
85
+
86
+ // Split the part into lines. Loop throug these lines to find
87
+ // the line no. and column at the end of this part.
88
+ const substring = part.value;
89
+ const lines = substring.split('\n');
90
+ lines.forEach((line, lineIndex) => {
91
+ // The first `line` is actually just a continuation of the last line
92
+ if (lineIndex === 0) {
93
+ column += line.length;
94
+ // All other lines come after a line break.
95
+ } else if (lineIndex > 0) {
96
+ lineNumber += 1;
97
+ column = line.length;
98
+ }
99
+ });
100
+
101
+ // Save a range for all of the parts with position info added
102
+ if (part.added === true || part.removed === true) {
103
+ diffRanges.push({
104
+ startLineNumber: startLineNumber + 1,
105
+ startColumn: startColumn,
106
+ endLineNumber: lineNumber,
107
+ endColumn: column,
108
+ ...part,
109
+ });
110
+ }
111
+ }
112
+ return diffRanges;
113
+ };
@@ -0,0 +1,37 @@
1
+ import { EnvContentsToAdd } from "~/models/DevService";
2
+
3
+ export const mergeDotEnvFileContents = (
4
+ existingFileLines: string[],
5
+ envContentsToAdd: EnvContentsToAdd
6
+ ): string[] => {
7
+ const envFileLines: string[] = []; // the new .env file
8
+ if (existingFileLines.length === 0) {
9
+ // There is no env file, just create one from envContentsToAdd
10
+ envFileLines.push(
11
+ ...Object.entries(envContentsToAdd).map(([k, v]) => `${k}=${v}`)
12
+ );
13
+ } else {
14
+ const updatedEnvKeys: string[] = [];
15
+ // Find lines in env that already exist for the keys in envContentsToAdd
16
+ // update them if they exist and add them to envFileLines
17
+ for (const ln of existingFileLines) {
18
+ let newline = '';
19
+ for (const [k, v] of Object.entries(envContentsToAdd))
20
+ if (ln.startsWith(`${k}=`)) {
21
+ newline = `${k}=${v}`;
22
+ updatedEnvKeys.push(k);
23
+ }
24
+ // Ensure an updated line or other lines from the existing env file are re-added
25
+ if (newline || ln) envFileLines.push(newline || ln);
26
+ }
27
+
28
+ // Add the envContentsToAdd lines to the file that did not previously exist or had an update
29
+ for (const addKey of Object.keys(envContentsToAdd).filter(
30
+ efl =>
31
+ !updatedEnvKeys.find(uek => uek.startsWith(`${efl.split('=')?.[0]}`))
32
+ ) as (keyof typeof envContentsToAdd)[]) {
33
+ envFileLines.push(`${addKey}=${envContentsToAdd[addKey]}`);
34
+ }
35
+ }
36
+ return envFileLines;
37
+ };
@@ -0,0 +1,8 @@
1
+ export const findByIdOrName = (arr: any[], idOrName: string, exact = false) =>
2
+ arr.find(
3
+ r =>
4
+ r.id === idOrName ||
5
+ r.name.toLowerCase() === idOrName.toLowerCase()
6
+ ) ||
7
+ (!exact &&
8
+ arr.find(r => r.name.toLowerCase().includes(idOrName.toLowerCase())));
@@ -0,0 +1,130 @@
1
+ import giturl from 'giturl';
2
+ import hostedGitInfo from 'hosted-git-info';
3
+ import parseGitConfig from 'parse-git-config';
4
+ import path from 'path';
5
+
6
+ import { linuxSlash } from './os';
7
+ import { readFile, readFiles } from '~/providers/file-provider';
8
+
9
+ const GITLAB_CI_FILENAME = '.gitlab-ci.yml';
10
+
11
+ type GitConfig = parseGitConfig.Config;
12
+
13
+ export type GitTypes = hostedGitInfo.Hosts;
14
+
15
+ export class GitHelper {
16
+ private gitRepoPath: string;
17
+ private ciFile?: string;
18
+
19
+ config = {} as GitConfig;
20
+ info: hostedGitInfo | undefined;
21
+ home: string | undefined;
22
+
23
+ set ciFileName(fileName: string) {
24
+ this.ciFile = fileName;
25
+ }
26
+
27
+ get ciFileName() {
28
+ return (
29
+ this.ciFile ||
30
+ (this.workflows
31
+ ? this.type === 'github'
32
+ ? this.workflows.length > 1
33
+ ? '[multiple workflows]'
34
+ : this.workflows?.[0]
35
+ : GITLAB_CI_FILENAME
36
+ : '[unknown]')
37
+ );
38
+ }
39
+ get ciFilePath() {
40
+ return `${this.gitRepoPath}/${this.ciFileName}`;
41
+ }
42
+ get name() {
43
+ return (
44
+ this.info?.project || this.home?.split('/').pop() || '[set arg --name]'
45
+ );
46
+ }
47
+ get originUrl() {
48
+ return this.config.remote.origin.url;
49
+ }
50
+ get secretsUri() {
51
+ return `${
52
+ this.type === 'github'
53
+ ? `${this.home}/settings/secrets/actions`
54
+ : `${this.home}/-/settings/ci_cd`
55
+ }`;
56
+ }
57
+ get type() {
58
+ return this.info?.type || this.hostType();
59
+ }
60
+ get workflows() {
61
+ return this.type === 'github'
62
+ ? this.githubWorkflows()
63
+ : this.gitlabWorkflow();
64
+ }
65
+ constructor(gitRepoPath: string = process.cwd()) {
66
+ this.gitRepoPath = gitRepoPath;
67
+ this.config = this.gitConfig();
68
+ this.home = giturl.parse(this.originUrl);
69
+ this.info = this.gitInfo();
70
+ // console.log(this.config);
71
+ // console.log(this.home);
72
+ // console.log(this.info);
73
+ }
74
+ gitcwd = () => path.join(this.gitRepoPath);
75
+ gitInfo = (url: string = this.originUrl) => hostedGitInfo.fromUrl(url);
76
+ hostType = (url: string = this.originUrl): GitTypes => {
77
+ if (url.includes('github.com')) return 'github';
78
+ return 'gitlab';
79
+ // if (url.includes('gitlab.com')) return 'gl';
80
+ // if (url.includes('gitlab.zengenti.com')) return 'gl';
81
+ };
82
+ gitConfig = (cwd = this.gitRepoPath) => {
83
+ // Find .git/config in project cwd
84
+ const config = parseGitConfig.sync({
85
+ path: '.git/config',
86
+ expandKeys: true,
87
+ });
88
+ // console.log(cwd, config);
89
+ if (Object.keys(config || {}).length) return config;
90
+
91
+ // Recursively check the directory heirarchy for existance of a .git/config
92
+ const pathParts = linuxSlash(cwd).split('/');
93
+ for (let i = 1; i <= pathParts.length; i++) {
94
+ const relPath = `${Array(i).fill('..').join('/')}/.git/config`;
95
+ // Does not appear to work when using a shortened cwd, using relative path instead
96
+ const config = parseGitConfig.sync({
97
+ path: relPath,
98
+ expandKeys: true,
99
+ });
100
+ // console.log(relPath, config);
101
+ if (Object.keys(config || {}).length) {
102
+ this.gitRepoPath = path.join(
103
+ this.gitRepoPath,
104
+ Array(i).fill('..').join('/')
105
+ );
106
+ return config;
107
+ }
108
+ }
109
+ return config;
110
+ };
111
+ githubWorkflows = () => {
112
+ const workflowPath = path.join(this.gitcwd(), '.github/workflows');
113
+ const workflowFiles = readFiles(workflowPath, false);
114
+ // console.log('gh workflows: ', workflowFiles);
115
+ const addFolderSuffix = (files: string[]) =>
116
+ files.map(f => `.github/workflows/${f}`);
117
+
118
+ if (workflowFiles.some(f => f.includes('build')))
119
+ return addFolderSuffix(workflowFiles.filter(f => f.includes('build')));
120
+ return addFolderSuffix(workflowFiles);
121
+ };
122
+ gitlabWorkflow = (ciFileName = GITLAB_CI_FILENAME) => {
123
+ const workflowPath = this.gitcwd();
124
+ const workflowFilePath = path.join(workflowPath, ciFileName);
125
+ const workflowFile = readFile(workflowFilePath);
126
+ // console.log(ciFileName, workflowFile);
127
+
128
+ return workflowFile;
129
+ };
130
+ }
package/src/util/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import mergeWith from 'lodash/mergeWith';
2
2
  import { Logger } from './logger';
3
+ import { LogMessages as enGB } from '../localisation/en-GB.js';
3
4
 
4
5
  export const isSharedSecret = (str = '') =>
5
6
  str.length > 80 && str.split('-').length === 3 ? str : undefined;
@@ -7,7 +8,7 @@ export const isSharedSecret = (str = '') =>
7
8
  export const isPassword = (str = '') =>
8
9
  !isSharedSecret(str) ? str : undefined;
9
10
 
10
- export const tryParse = (str: string) => {
11
+ export const tryParse = (str: any) => {
11
12
  try {
12
13
  return typeof str === 'object' ? str : JSON.parse(str);
13
14
  } catch (e) {
@@ -26,6 +27,9 @@ export const tryStringify = (obj: any) => {
26
27
  }
27
28
  };
28
29
 
30
+ export const isSysError = (error: any): error is Error =>
31
+ error?.message !== undefined && error.stack;
32
+
29
33
  export const isUuid = (str: string) => {
30
34
  // Regular expression to check if string is a valid UUID
31
35
  const regexExp =
@@ -50,12 +54,17 @@ export const url = (alias: string, project: string) => {
50
54
  };
51
55
 
52
56
  export const Logging = async (language = 'en-GB') => {
53
- const { LogMessages: defaultMessages } = await import(
54
- `../localisation/en-GB.js`
55
- );
56
- const { LogMessages: localisedMessages } = await import(
57
- `../localisation/${language}.js`
58
- );
57
+ const defaultMessages = enGB;
58
+ // const { LogMessages: defaultMessages } = await import(
59
+ // `../localisation/en-GB.js`
60
+ // );
61
+ let localisedMessages = defaultMessages;
62
+
63
+ if (language === 'en-GB') {
64
+ // Using a variable import e.g. `import(`../localisation/${language}.js`);`
65
+ // does not play well with packaged executables
66
+ // So we have to hard code the import for each language individually
67
+ }
59
68
  return {
60
69
  messages: mergeWith(
61
70
  localisedMessages,