contensis-cli 1.0.0-beta.10 → 1.0.0-beta.100

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 +71 -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 +65 -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 +297 -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 +374 -0
  38. package/dist/mappers/DevInit-to-CIWorkflow.js.map +7 -0
  39. package/dist/mappers/DevInit-to-RolePermissions.js +56 -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 +1211 -420
  54. package/dist/services/ContensisCliService.js.map +3 -3
  55. package/dist/services/ContensisDevService.js +368 -0
  56. package/dist/services/ContensisDevService.js.map +7 -0
  57. package/dist/services/ContensisRoleService.js +114 -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 +116 -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 +128 -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 +42 -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 +14 -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 +83 -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 +428 -66
  101. package/src/mappers/ContensisCliService-to-RequestHanderSiteConfigYaml.ts +44 -0
  102. package/src/mappers/DevInit-to-CIWorkflow.ts +526 -0
  103. package/src/mappers/DevInit-to-RolePermissions.ts +32 -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 +40 -0
  107. package/src/models/JsModules.d.ts +2 -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 +1532 -508
  112. package/src/services/ContensisDevService.ts +434 -0
  113. package/src/services/ContensisRoleService.ts +108 -0
  114. package/src/shell.ts +68 -18
  115. package/src/util/console.printer.ts +240 -78
  116. package/src/util/diff.ts +124 -0
  117. package/src/util/dotenv.ts +37 -0
  118. package/src/util/find.ts +8 -0
  119. package/src/util/git.ts +131 -0
  120. package/src/util/index.ts +16 -7
  121. package/src/util/logger.ts +145 -31
  122. package/src/util/os.ts +12 -0
  123. package/src/util/timers.ts +24 -0
  124. package/src/util/yaml.ts +13 -0
  125. package/src/version.ts +1 -1
@@ -0,0 +1,526 @@
1
+ import inquirer from 'inquirer';
2
+ import { JSONPath, JSONPathOptions } from 'jsonpath-plus';
3
+ import { Document } from 'yaml';
4
+ import {
5
+ GitHubActionPushBlockJob,
6
+ GitHubActionPushBlockJobStep,
7
+ GitLabPushBlockJobStage,
8
+ } from '~/models/DevService';
9
+ import { readFile } from '~/providers/file-provider';
10
+ import ContensisDev from '~/services/ContensisDevService';
11
+ import { diffFileContent } from '~/util/diff';
12
+ import { logError } from '~/util/logger';
13
+ import { normaliseLineEndings } from '~/util/os';
14
+ import { parseYamlDocument, validateWorkflowYaml } from '~/util/yaml';
15
+
16
+ type MappedWorkflowOutput = {
17
+ existingWorkflow: string;
18
+ newWorkflow: string;
19
+ diff: string;
20
+ };
21
+
22
+ export const mapCIWorkflowContent = async (
23
+ cli: ContensisDev
24
+ ): Promise<MappedWorkflowOutput | undefined> => {
25
+ // get existing workflow file
26
+ const workflowFile = readFile(cli.git.ciFilePath);
27
+ if (!workflowFile) return undefined;
28
+
29
+ const blockId = cli.git.name;
30
+
31
+ // parse yaml to js
32
+ const workflowDoc = parseYamlDocument(workflowFile);
33
+ const workflowJS = workflowDoc.toJS();
34
+
35
+ if (cli.git.type === 'github') {
36
+ const newWorkflow = await mapGitHubActionCIWorkflowContent(
37
+ cli,
38
+ blockId,
39
+ workflowDoc,
40
+ workflowJS
41
+ );
42
+ return {
43
+ existingWorkflow: workflowFile,
44
+ newWorkflow,
45
+ diff: diffFileContent(workflowFile, newWorkflow),
46
+ };
47
+ } else if (cli.git.type === 'gitlab') {
48
+ const newWorkflow = await mapGitLabCIWorkflowContent(
49
+ cli,
50
+ blockId,
51
+ workflowDoc,
52
+ workflowJS
53
+ );
54
+ return {
55
+ existingWorkflow: workflowFile,
56
+ newWorkflow,
57
+ diff: diffFileContent(workflowFile, newWorkflow),
58
+ };
59
+ }
60
+ };
61
+
62
+ const findExistingJobSteps = (
63
+ path: string,
64
+ json: any,
65
+ resultType: JSONPathOptions['resultType']
66
+ ) => {
67
+ const existingJobStep = JSONPath({
68
+ path,
69
+ json,
70
+ resultType: resultType,
71
+ });
72
+
73
+ return existingJobStep;
74
+ };
75
+
76
+ const setWorkflowElement = (
77
+ workflowDoc: Document,
78
+ path: string | any[],
79
+ value: any
80
+ ) => {
81
+ const findPath =
82
+ typeof path === 'string' && path.includes('.')
83
+ ? path.split('.').map(p => (Number(p) || Number(p) !== 0 ? p : Number(p)))
84
+ : path;
85
+
86
+ if (workflowDoc.hasIn(findPath)) {
87
+ workflowDoc.setIn(findPath, value);
88
+ } else {
89
+ workflowDoc.addIn(findPath, value);
90
+ }
91
+ };
92
+
93
+ const mapGitLabCIWorkflowContent = async (
94
+ cli: ContensisDev,
95
+ blockId: string,
96
+ workflowDoc: Document,
97
+ workflowJS: any
98
+ ) => {
99
+ const addGitLabJobStage: GitLabPushBlockJobStage = {
100
+ stage: 'push-block',
101
+ variables: {
102
+ block_id: blockId,
103
+ alias: cli.currentEnv,
104
+ project_id: cli.currentProject,
105
+ client_id:
106
+ cli.clientDetailsLocation === 'env'
107
+ ? cli.clientId
108
+ : '$CONTENSIS_CLIENT_ID',
109
+ shared_secret:
110
+ cli.clientDetailsLocation === 'env'
111
+ ? cli.clientSecret
112
+ : '$CONTENSIS_SHARED_SECRET',
113
+ },
114
+ };
115
+
116
+ const addAppImageUri = async () => {
117
+ // Look in document level "variables"
118
+ const appImageUri = await determineAppImageUri(
119
+ cli,
120
+ Object.entries(workflowJS.variables || {}),
121
+ '$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME/app:build-$CI_PIPELINE_IID'
122
+ );
123
+
124
+ if (appImageUri.addVar)
125
+ setWorkflowElement(
126
+ workflowDoc,
127
+ `variables.${appImageUri.var}`,
128
+ appImageUri.uri
129
+ );
130
+
131
+ if (appImageUri.var)
132
+ addGitLabJobStage.variables['image_uri'] = `\$${appImageUri.var}`;
133
+ };
134
+
135
+ // look for line in job
136
+ // jobname[stage: push-block]
137
+ const existingJobStep = findExistingJobSteps(
138
+ '$..[?(@property === "stage" && @.match(/^push-block/i))]^',
139
+ workflowJS,
140
+ 'all'
141
+ );
142
+
143
+ // update job step
144
+ if (existingJobStep.length) {
145
+ // cli.log.json(existingJobStep);
146
+
147
+ // The [0] index means we're only looking at updating the first instance in the file
148
+ const step = existingJobStep[0];
149
+
150
+ // Path looks like this "$['job_name']"
151
+ // We want it to look like this "job_name"
152
+ const stepPath = step.path
153
+ .replace('$[', '')
154
+ .replaceAll('][', '.')
155
+ .replace(']', '')
156
+ .replaceAll("'", '');
157
+
158
+ cli.log.info(
159
+ `Found existing Job step:
160
+ ${stepPath}
161
+ stage: ${step.value.stage}\n`
162
+ );
163
+
164
+ setWorkflowElement(
165
+ workflowDoc,
166
+ `${stepPath}.variables.alias`,
167
+ cli.currentEnv
168
+ );
169
+ setWorkflowElement(
170
+ workflowDoc,
171
+ `${stepPath}.variables.project_id`,
172
+ cli.currentProject
173
+ );
174
+ setWorkflowElement(workflowDoc, `${stepPath}.variables.block_id`, blockId);
175
+
176
+ // This is likely not needed when updating an existing push-block job step
177
+ // we are assuming this is a forked/copied workflow with an already working image-uri reference
178
+ // await addAppImageUri();
179
+
180
+ setWorkflowElement(
181
+ workflowDoc,
182
+ `${stepPath}.variables.client_id`,
183
+ cli.clientDetailsLocation === 'env'
184
+ ? cli.clientId
185
+ : '$CONTENSIS_CLIENT_ID'
186
+ );
187
+ setWorkflowElement(
188
+ workflowDoc,
189
+ `${stepPath}.variables.shared_secret`,
190
+ cli.clientDetailsLocation === 'env'
191
+ ? cli.clientSecret
192
+ : '$CONTENSIS_SHARED_SECRET'
193
+ );
194
+ } else {
195
+ // create job with push step
196
+
197
+ // Does a series of checks and prompts to determine the correct image-uri
198
+ // for the app container build
199
+ await addAppImageUri();
200
+
201
+ // Add the new "job" to the Yaml Document
202
+ workflowDoc.addIn(['stages'], 'push-block');
203
+ workflowDoc.addIn([], {
204
+ key: 'push-to-contensis',
205
+ value: addGitLabJobStage,
206
+ });
207
+ setWorkflowElement(
208
+ workflowDoc,
209
+ `include`,
210
+ 'https://gitlab.zengenti.com/ops/contensis-ci/-/raw/main/push-block.yml'
211
+ );
212
+ }
213
+
214
+ cli.log.debug(`New file content to write\n\n${workflowDoc}`);
215
+
216
+ const newWorkflow = normaliseLineEndings(
217
+ workflowDoc.toString({ lineWidth: 0 })
218
+ );
219
+
220
+ return newWorkflow;
221
+ };
222
+
223
+ const mapGitHubActionCIWorkflowContent = async (
224
+ cli: ContensisDev,
225
+ blockId: string,
226
+ workflowDoc: Document,
227
+ workflowJS: any
228
+ ) => {
229
+ const addGitHubActionJobStep: GitHubActionPushBlockJobStep = {
230
+ name: 'Push block to Contensis',
231
+ id: 'push-block',
232
+ uses: 'contensis/block-push@v1',
233
+ with: {
234
+ 'block-id': blockId,
235
+ // 'image-uri': '${{ steps.build.outputs.image-uri }}',
236
+ alias: cli.currentEnv,
237
+ 'project-id': cli.currentProject,
238
+ 'client-id':
239
+ cli.clientDetailsLocation === 'env'
240
+ ? '${{ env.CONTENSIS_CLIENT_ID }}'
241
+ : '${{ secrets.CONTENSIS_CLIENT_ID }}',
242
+ 'shared-secret':
243
+ cli.clientDetailsLocation === 'env'
244
+ ? '${{ env.CONTENSIS_SHARED_SECRET }}'
245
+ : '${{ secrets.CONTENSIS_SHARED_SECRET }}',
246
+ },
247
+ };
248
+
249
+ const addAppImageUri = async () => {
250
+ // Look in document level "env" vars
251
+ const appImageUri = await determineAppImageUri(
252
+ cli,
253
+ Object.entries(workflowJS.env || {}),
254
+ 'ghcr.io/${{ github.repository }}/${{ github.ref_name }}/app'
255
+ );
256
+
257
+ if (appImageUri.addVar)
258
+ setWorkflowElement(
259
+ workflowDoc,
260
+ `env.${appImageUri.var}`,
261
+ appImageUri.uri
262
+ );
263
+ // workflowDoc.addIn(['env'], { [appImageUri.var]: appImageUri.uri });
264
+
265
+ if (appImageUri.var)
266
+ addGitHubActionJobStep.with[
267
+ 'image-uri'
268
+ ] = `\${{ env.${appImageUri.var} }}:build-\${{ github.run_number }}`;
269
+ };
270
+
271
+ // look for line in job
272
+ // jobs.x.steps[uses: contensis/block-push]
273
+ const existingJobStep = findExistingJobSteps(
274
+ '$.jobs..steps.*[?(@property === "uses" && @.match(/^contensis\\/block-push/i))]^',
275
+ workflowJS,
276
+ 'all'
277
+ );
278
+
279
+ // update job step
280
+ if (existingJobStep.length) {
281
+ //cli.log.json(existingJobStep);
282
+
283
+ // The [0] index means we're only looking at updating the first instance in the file
284
+ const step = existingJobStep[0];
285
+
286
+ // Path looks like this "$['jobs']['build']['steps'][3]"
287
+ // We want it to look like this "jobs.build.steps.3"
288
+ const stepPath = step.path
289
+ .replace('$[', '')
290
+ .replaceAll('][', '.')
291
+ .replace(']', '')
292
+ .replaceAll("'", '');
293
+
294
+ cli.log.info(
295
+ `Found existing Job step: ${stepPath}
296
+ - name: ${step.value.name}
297
+ id: ${step.value.id}\n`
298
+ );
299
+
300
+ setWorkflowElement(workflowDoc, `${stepPath}.with.alias`, cli.currentEnv);
301
+ setWorkflowElement(
302
+ workflowDoc,
303
+ `${stepPath}.with.project-id`,
304
+ cli.currentProject
305
+ );
306
+ setWorkflowElement(workflowDoc, `${stepPath}.with.block-id`, blockId);
307
+
308
+ // This is likely not needed when updating an existing push-block job step
309
+ // we are assuming this is a forked/copied workflow with an already working image-uri reference
310
+ // await addAppImageUri();
311
+
312
+ setWorkflowElement(
313
+ workflowDoc,
314
+ `${stepPath}.with.client-id`,
315
+ cli.clientDetailsLocation === 'env'
316
+ ? '${{ env.CONTENSIS_CLIENT_ID }}'
317
+ : '${{ secrets.CONTENSIS_CLIENT_ID }}'
318
+ );
319
+ setWorkflowElement(
320
+ workflowDoc,
321
+ `${stepPath}.with.shared-secret`,
322
+ cli.clientDetailsLocation === 'env'
323
+ ? '${{ env.CONTENSIS_SHARED_SECRET }}'
324
+ : '${{ secrets.CONTENSIS_SHARED_SECRET }}'
325
+ );
326
+ } else {
327
+ // create job with push step
328
+
329
+ // is there already a job with a property name containing "build"?
330
+ const existingBuildJobStep = findExistingJobSteps(
331
+ '$.jobs[?(@property.match(/build/i))]',
332
+ workflowJS,
333
+ 'all'
334
+ ) as JSONPathOptions[]; // This isn't the correct type for this object
335
+
336
+ let needs: string | undefined;
337
+ // There are multiple jobs called *build*
338
+ if (existingBuildJobStep.length > 1) {
339
+ // prompt which build job we should depend on before pushing the block
340
+ const choices = existingBuildJobStep.map(s => s.parentProperty);
341
+ choices.push(new inquirer.Separator() as any);
342
+ choices.push('none');
343
+ if (choices.includes('build-app')) needs = 'build-app';
344
+ else {
345
+ ({ needs } = await inquirer.prompt([
346
+ {
347
+ type: 'list',
348
+ prefix: '⌛',
349
+ message: cli.messages.devinit.ciMultipleBuildJobChoices(),
350
+ name: 'needs',
351
+ choices,
352
+ default: choices.find(
353
+ s => typeof s === 'string' && s.includes('docker')
354
+ ),
355
+ },
356
+ ]));
357
+ }
358
+
359
+ cli.log.raw('');
360
+ } else if (existingBuildJobStep.length === 1)
361
+ // Exactly one job step found containing a property name of *build*
362
+ // we'll assume that is the one the push-block job depends on
363
+ needs = existingBuildJobStep[0].parentProperty;
364
+
365
+ if (existingBuildJobStep.length === 0 || needs === 'none') {
366
+ // No existing build step found or chosen, offer all job steps in prompt
367
+ const choices = Object.keys(workflowJS.jobs);
368
+ choices.push(new inquirer.Separator() as any);
369
+ choices.push('none');
370
+
371
+ ({ needs } = await inquirer.prompt([
372
+ {
373
+ type: 'list',
374
+ prefix: '⌛',
375
+ message: cli.messages.devinit.ciMultipleJobChoices(),
376
+ name: 'needs',
377
+ choices,
378
+ default: choices.find(
379
+ j => typeof j === 'string' && j.includes('docker')
380
+ ),
381
+ },
382
+ ]));
383
+ if (needs === 'none') needs = undefined;
384
+ cli.log.raw('');
385
+ }
386
+
387
+ // Does a series of checks and prompts to determine the correct image-uri
388
+ // for the app container build
389
+ await addAppImageUri();
390
+
391
+ const newJob: GitHubActionPushBlockJob = {
392
+ name: 'Deploy container image to Contensis',
393
+ 'runs-on': 'ubuntu-latest',
394
+ needs,
395
+ steps: [addGitHubActionJobStep],
396
+ };
397
+
398
+ // Add the new "job" to the Yaml Document
399
+ workflowDoc.addIn(['jobs'], {
400
+ key: 'deploy',
401
+ value: newJob,
402
+ });
403
+ }
404
+
405
+ // Workflow validation provided by @action-validator/core package
406
+ const workflowIsValid = validateWorkflowYaml(workflowDoc.toString());
407
+
408
+ // We could expand validation to check for having a build step to wait for
409
+ // or if a valid image-uri attribute is set
410
+ if (workflowIsValid === true) {
411
+ cli.log.success(`GitHub workflow YAML is valid`);
412
+ cli.log.debug(`New file content to write\n\n${workflowDoc}`);
413
+ } else if (Array.isArray(workflowIsValid)) {
414
+ // Errors
415
+ logError(
416
+ [
417
+ ...workflowIsValid.map(
418
+ res => new Error(`${res.code}: ${res.detail || res.title}`)
419
+ ),
420
+ workflowDoc.toString(),
421
+ ],
422
+ `GitHub workflow YAML did not pass validation check`
423
+ );
424
+ }
425
+
426
+ // const newWorkflow = normaliseLineEndings(
427
+ // workflowDoc.toString({ lineWidth: 0 })
428
+ // );
429
+
430
+ const newWorkflow = workflowDoc.toString();
431
+
432
+ return newWorkflow;
433
+ };
434
+
435
+ const determineAppImageUri = async (
436
+ cli: ContensisDev,
437
+ vars: [string, string][],
438
+ defaultUri: string
439
+ ) => {
440
+ // Determine container image-uri via variables and/or prompts
441
+
442
+ // Find vars including the word "image"
443
+ const imageVars = vars.filter(([varname]) =>
444
+ varname.toLowerCase().includes('image')
445
+ );
446
+ // Find vars named "image" that include the word "app"
447
+ const appImageVars = imageVars.filter(
448
+ ([varname, value]) =>
449
+ !varname.toLowerCase().includes('builder_') &&
450
+ (varname.toLowerCase().includes('app_') ||
451
+ varname.toLowerCase().includes('build_') ||
452
+ value?.toLowerCase().includes('/app'))
453
+ );
454
+
455
+ const appImageUriGuess = appImageVars?.[0];
456
+
457
+ let appImageUri: string | undefined;
458
+ let appImageVar: string | undefined;
459
+
460
+ if (appImageUriGuess) {
461
+ cli.log.success(
462
+ `Found variable ${cli.log.standardText(
463
+ appImageUriGuess[0]
464
+ )} we'll use for pulling the block image from: ${cli.log.infoText(
465
+ appImageUriGuess[1]
466
+ )}`
467
+ );
468
+ appImageVar = appImageUriGuess[0];
469
+ } else {
470
+ // Could not find a suitable var to use for block image-uri
471
+ // prompt for an app image uri
472
+ const choices = vars.map(v => v[0]);
473
+ const enterOwnMsg = 'enter my own / use default';
474
+
475
+ choices.push(new inquirer.Separator() as any);
476
+ choices.push(enterOwnMsg);
477
+ choices.push(defaultUri);
478
+
479
+ ({ appImageVar, appImageUri } = await inquirer.prompt([
480
+ // First question determines if an existing env variable
481
+ // already containes the tagged app image uri
482
+ {
483
+ type: 'list',
484
+ prefix: '🐳',
485
+ message: cli.messages.devinit.ciMultipleAppImageVarChoices(),
486
+ name: 'appImageVar',
487
+ choices,
488
+ default: choices.find(
489
+ v => typeof v === 'string' && v.toLowerCase().includes('image')
490
+ ),
491
+ },
492
+ // Subsequent prompt allows input of an image-uri if needed
493
+ {
494
+ type: 'input',
495
+ when(answers) {
496
+ return [enterOwnMsg, defaultUri].includes(answers.appImageVar);
497
+ },
498
+ prefix: `\n \n🔗`,
499
+ message: cli.messages.devinit.ciEnterOwnAppImagePrompt(cli.git),
500
+ name: 'appImageUri',
501
+ default: defaultUri,
502
+ },
503
+ ]));
504
+ cli.log.raw('');
505
+
506
+ // this indicates a uri has been added and we will add a new var
507
+ // to the workflow to encourage users to update the docker tag part
508
+ // of their build workflow to use the same var as the push-block job/step
509
+ if ([enterOwnMsg, defaultUri].includes(appImageVar || ''))
510
+ appImageVar = undefined;
511
+ }
512
+
513
+ if (appImageVar)
514
+ appImageUri =
515
+ cli.git.type === 'github'
516
+ ? `\${{ env.${appImageVar} }}`
517
+ : `\$${appImageVar}`;
518
+
519
+ // TODO: prompt further for image tag
520
+
521
+ return {
522
+ addVar: !appImageVar,
523
+ uri: appImageUri,
524
+ var: appImageVar || 'BUILD_IMAGE',
525
+ };
526
+ };
@@ -0,0 +1,32 @@
1
+ import { Role } from 'contensis-management-api/lib/models';
2
+
3
+ export const devKeyPermissions = {} as Partial<Role['permissions']>;
4
+ export const deployKeyPermissions = {
5
+ blocks: { actions: ['push', 'release', 'view'] },
6
+ } as Role['permissions'];
7
+
8
+ export const devKeyRole = (
9
+ keyName: string,
10
+ description: string
11
+ ): Partial<Role> => ({
12
+ name: keyName,
13
+ description,
14
+ assignments: {
15
+ apiKeys: [keyName],
16
+ },
17
+ permissions: devKeyPermissions,
18
+ enabled: true,
19
+ });
20
+
21
+ export const deployKeyRole = (
22
+ keyName: string,
23
+ description: string
24
+ ): Partial<Role> => ({
25
+ name: keyName,
26
+ description,
27
+ assignments: {
28
+ apiKeys: [keyName],
29
+ },
30
+ permissions: deployKeyPermissions,
31
+ enabled: true,
32
+ });
@@ -0,0 +1,44 @@
1
+ import ContensisCli from '~/services/ContensisCliService';
2
+
3
+ interface ISiteConfigYaml {
4
+ alias: string;
5
+ projectId: string;
6
+ accessToken: string;
7
+ clientId: string;
8
+ sharedSecret: string;
9
+ blocks: {
10
+ id: string;
11
+ baseUri: string;
12
+ }[];
13
+ }
14
+
15
+ export const mapSiteConfigYaml = async (cli: ContensisCli) => {
16
+ const credentials = await cli.GetCredentials(cli.env.lastUserId);
17
+
18
+ const blocks = [];
19
+ const blocksRaw = await cli.PrintBlocks();
20
+
21
+ for (const block of blocksRaw || []) {
22
+ const versions = await cli.PrintBlockVersions(
23
+ block.id,
24
+ 'default',
25
+ 'latest'
26
+ );
27
+ if (versions?.[0]) {
28
+ blocks.push({
29
+ id: versions[0].id,
30
+ baseUri: versions[0].previewUrl,
31
+ });
32
+ }
33
+ }
34
+
35
+ const siteConfig: ISiteConfigYaml = {
36
+ alias: cli.currentEnv,
37
+ projectId: cli.currentProject,
38
+ accessToken: '',
39
+ clientId: credentials?.current?.account || '',
40
+ sharedSecret: credentials?.current?.password || '',
41
+ blocks,
42
+ };
43
+ return siteConfig;
44
+ };
@@ -0,0 +1,36 @@
1
+
2
+ export type OutputFormat = 'json' | 'csv' | 'xml';
3
+
4
+ export type OutputOptions = {
5
+ format?: OutputFormat;
6
+ output?: string;
7
+ };
8
+
9
+ export interface IConnectOptions extends IAuthOptions {
10
+ alias?: string;
11
+ projectId?: string;
12
+ }
13
+
14
+ export interface IAuthOptions {
15
+ user?: string;
16
+ password?: string;
17
+ clientId?: string;
18
+ sharedSecret?: string;
19
+ }
20
+
21
+ export interface IImportOptions {
22
+ sourceAlias?: string;
23
+ sourceProjectId?: string;
24
+ }
25
+
26
+ export type OutputOptionsConstructorArg = OutputOptions &
27
+ IConnectOptions &
28
+ IImportOptions;
29
+
30
+ export interface ContensisCliConstructor {
31
+ new (
32
+ args: string[],
33
+ outputOpts?: OutputOptionsConstructorArg,
34
+ contensisOpts?: Partial<MigrateRequest>
35
+ ): ContensisCli;
36
+ }
@@ -0,0 +1,40 @@
1
+ export type EnvContentsToAdd = {
2
+ ALIAS: string;
3
+ PROJECT: string;
4
+ ACCESS_TOKEN?: string;
5
+ CONTENSIS_CLIENT_ID?: string;
6
+ CONTENSIS_CLIENT_SECRET?: string;
7
+ };
8
+
9
+ export type GitHubActionPushBlockJobStep = {
10
+ name: string;
11
+ id: 'push-block';
12
+ uses: string;
13
+ with: {
14
+ 'block-id': string;
15
+ alias: string;
16
+ 'project-id': string;
17
+ 'client-id': string;
18
+ 'shared-secret': string;
19
+ 'image-uri'?: string;
20
+ };
21
+ };
22
+
23
+ export type GitHubActionPushBlockJob = {
24
+ name: string;
25
+ 'runs-on': string;
26
+ needs?: string;
27
+ steps: GitHubActionPushBlockJobStep[];
28
+ };
29
+
30
+ export type GitLabPushBlockJobStage = {
31
+ stage: string;
32
+ variables: {
33
+ alias: string;
34
+ project_id: string;
35
+ block_id: string;
36
+ image_uri?: string;
37
+ client_id: string;
38
+ shared_secret: string;
39
+ };
40
+ };
@@ -1 +1,3 @@
1
+ declare module 'giturl';
1
2
  declare module 'inquirer-command-prompt';
3
+ declare module 'printable-characters';