contensis-cli 1.0.0-beta.96 → 1.0.0-beta.98

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 (36) hide show
  1. package/dist/commands/dev.js +2 -6
  2. package/dist/commands/dev.js.map +2 -2
  3. package/dist/commands/import.js +18 -0
  4. package/dist/commands/import.js.map +2 -2
  5. package/dist/localisation/en-GB.js +24 -8
  6. package/dist/localisation/en-GB.js.map +2 -2
  7. package/dist/mappers/DevInit-to-CIWorkflow.js +25 -23
  8. package/dist/mappers/DevInit-to-CIWorkflow.js.map +2 -2
  9. package/dist/mappers/DevInit-to-RolePermissions.js +4 -2
  10. package/dist/mappers/DevInit-to-RolePermissions.js.map +2 -2
  11. package/dist/models/DevService.d.js.map +1 -1
  12. package/dist/services/ContensisCliService.js +64 -0
  13. package/dist/services/ContensisCliService.js.map +2 -2
  14. package/dist/services/ContensisDevService.js +63 -17
  15. package/dist/services/ContensisDevService.js.map +2 -2
  16. package/dist/services/ContensisRoleService.js +31 -4
  17. package/dist/services/ContensisRoleService.js.map +3 -3
  18. package/dist/util/diff.js +1 -3
  19. package/dist/util/diff.js.map +2 -2
  20. package/dist/util/git.js +4 -2
  21. package/dist/util/git.js.map +2 -2
  22. package/dist/version.js +1 -1
  23. package/dist/version.js.map +1 -1
  24. package/package.json +3 -1
  25. package/src/commands/dev.ts +7 -7
  26. package/src/commands/import.ts +25 -0
  27. package/src/localisation/en-GB.ts +37 -7
  28. package/src/mappers/DevInit-to-CIWorkflow.ts +49 -23
  29. package/src/mappers/DevInit-to-RolePermissions.ts +4 -5
  30. package/src/models/DevService.d.ts +2 -0
  31. package/src/services/ContensisCliService.ts +84 -0
  32. package/src/services/ContensisDevService.ts +78 -21
  33. package/src/services/ContensisRoleService.ts +33 -1
  34. package/src/util/diff.ts +2 -3
  35. package/src/util/git.ts +4 -3
  36. package/src/version.ts +1 -1
@@ -102,8 +102,14 @@ const mapGitLabCIWorkflowContent = async (
102
102
  block_id: blockId,
103
103
  alias: cli.currentEnv,
104
104
  project_id: cli.currentProject,
105
- client_id: '$CONTENSIS_CLIENT_ID',
106
- shared_secret: '$CONTENSIS_SHARED_SECRET',
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',
107
113
  },
108
114
  };
109
115
 
@@ -174,12 +180,16 @@ const mapGitLabCIWorkflowContent = async (
174
180
  setWorkflowElement(
175
181
  workflowDoc,
176
182
  `${stepPath}.variables.client_id`,
177
- '$CONTENSIS_CLIENT_ID'
183
+ cli.clientDetailsLocation === 'env'
184
+ ? cli.clientId
185
+ : '$CONTENSIS_CLIENT_ID'
178
186
  );
179
187
  setWorkflowElement(
180
188
  workflowDoc,
181
189
  `${stepPath}.variables.shared_secret`,
182
- '$CONTENSIS_SHARED_SECRET'
190
+ cli.clientDetailsLocation === 'env'
191
+ ? cli.clientSecret
192
+ : '$CONTENSIS_SHARED_SECRET'
183
193
  );
184
194
  } else {
185
195
  // create job with push step
@@ -225,8 +235,14 @@ const mapGitHubActionCIWorkflowContent = async (
225
235
  // 'image-uri': '${{ steps.build.outputs.image-uri }}',
226
236
  alias: cli.currentEnv,
227
237
  'project-id': cli.currentProject,
228
- 'client-id': '${{ secrets.CONTENSIS_CLIENT_ID }}',
229
- 'shared-secret': '${{ secrets.CONTENSIS_SHARED_SECRET }}',
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 }}',
230
246
  },
231
247
  };
232
248
 
@@ -296,12 +312,16 @@ const mapGitHubActionCIWorkflowContent = async (
296
312
  setWorkflowElement(
297
313
  workflowDoc,
298
314
  `${stepPath}.with.client-id`,
299
- '${{ secrets.CONTENSIS_CLIENT_ID }}'
315
+ cli.clientDetailsLocation === 'env'
316
+ ? '${{ env.CONTENSIS_CLIENT_ID }}'
317
+ : '${{ secrets.CONTENSIS_CLIENT_ID }}'
300
318
  );
301
319
  setWorkflowElement(
302
320
  workflowDoc,
303
321
  `${stepPath}.with.shared-secret`,
304
- '${{ secrets.CONTENSIS_SHARED_SECRET }}'
322
+ cli.clientDetailsLocation === 'env'
323
+ ? '${{ env.CONTENSIS_SHARED_SECRET }}'
324
+ : '${{ secrets.CONTENSIS_SHARED_SECRET }}'
305
325
  );
306
326
  } else {
307
327
  // create job with push step
@@ -320,19 +340,22 @@ const mapGitHubActionCIWorkflowContent = async (
320
340
  const choices = existingBuildJobStep.map(s => s.parentProperty);
321
341
  choices.push(new inquirer.Separator() as any);
322
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
+ }
323
358
 
324
- ({ needs } = await inquirer.prompt([
325
- {
326
- type: 'list',
327
- prefix: '⌛',
328
- message: cli.messages.devinit.ciMultipleBuildJobChoices(),
329
- name: 'needs',
330
- choices,
331
- default: choices.find(
332
- s => typeof s === 'string' && s.includes('docker')
333
- ),
334
- },
335
- ]));
336
359
  cli.log.raw('');
337
360
  } else if (existingBuildJobStep.length === 1)
338
361
  // Exactly one job step found containing a property name of *build*
@@ -399,9 +422,12 @@ const mapGitHubActionCIWorkflowContent = async (
399
422
  `GitHub workflow YAML did not pass validation check`
400
423
  );
401
424
  }
402
- const newWorkflow = normaliseLineEndings(
403
- workflowDoc.toString({ lineWidth: 0 })
404
- );
425
+
426
+ // const newWorkflow = normaliseLineEndings(
427
+ // workflowDoc.toString({ lineWidth: 0 })
428
+ // );
429
+
430
+ const newWorkflow = workflowDoc.toString();
405
431
 
406
432
  return newWorkflow;
407
433
  };
@@ -1,10 +1,9 @@
1
1
  import { Role } from 'contensis-management-api/lib/models';
2
2
 
3
- export const devKeyPermissions = { blocks: [] } as Partial<Role['permissions']>;
4
-
5
- export const deployKeyPermissions = { blocks: ['push', 'release'] } as Partial<
6
- Role['permissions']
7
- >;
3
+ export const devKeyPermissions = {} as Partial<Role['permissions']>;
4
+ export const deployKeyPermissions = {
5
+ blocks: { actions: ['push', 'release', 'view'] },
6
+ } as Role['permissions'];
8
7
 
9
8
  export const devKeyRole = (
10
9
  keyName: string,
@@ -2,6 +2,8 @@ export type EnvContentsToAdd = {
2
2
  ALIAS: string;
3
3
  PROJECT: string;
4
4
  ACCESS_TOKEN?: string;
5
+ CONTENSIS_CLIENT_ID?: string;
6
+ CONTENSIS_CLIENT_SECRET?: string;
5
7
  };
6
8
 
7
9
  export type GitHubActionPushBlockJobStep = {
@@ -71,6 +71,10 @@ class ContensisCli {
71
71
  contensisOpts: Partial<MigrateRequest>;
72
72
  currentProject: string;
73
73
 
74
+ clientDetailsLocation?: 'env' | 'git';
75
+ clientId?: string;
76
+ clientSecret?: string;
77
+
74
78
  sourceAlias?: string;
75
79
  targetEnv?: string;
76
80
  urls:
@@ -290,6 +294,7 @@ class ContensisCli {
290
294
  | 'contentTypes'
291
295
  | 'components'
292
296
  | 'models'
297
+ | 'nodes'
293
298
  | 'user-input';
294
299
  }) => {
295
300
  const source: 'contensis' | 'file' = fromFile ? 'file' : 'contensis';
@@ -1736,6 +1741,85 @@ class ContensisCli {
1736
1741
  }
1737
1742
  };
1738
1743
 
1744
+ ImportNodes = async ({
1745
+ commit,
1746
+ fromFile,
1747
+ logOutput,
1748
+ }: {
1749
+ commit: boolean;
1750
+ fromFile: string;
1751
+ logOutput: string;
1752
+ }) => {
1753
+ const { currentEnv, currentProject, log, messages } = this;
1754
+
1755
+ const contensis = await this.ConnectContensisImport({
1756
+ commit,
1757
+ fromFile,
1758
+ importDataType: 'nodes',
1759
+ });
1760
+
1761
+ if (contensis) {
1762
+ log.line();
1763
+ if (contensis.isPreview) {
1764
+ console.log(log.successText(` -- IMPORT PREVIEW -- `));
1765
+ } else {
1766
+ console.log(log.warningText(` *** COMMITTING IMPORT *** `));
1767
+ }
1768
+
1769
+ const [err, result] = await contensis.MigrateNodes();
1770
+
1771
+ if (err) logError(err);
1772
+ else
1773
+ this.HandleFormattingAndOutput(result, () => {
1774
+ // print the migrateResult to console
1775
+ // TODO: fix
1776
+ printMigrateResult(this, result, {
1777
+ showAllEntries: logOutput === 'all',
1778
+ showChangedEntries: logOutput === 'changes',
1779
+ });
1780
+ });
1781
+
1782
+ const nodesTotalCount = result?.nodesToMigrate[currentProject].totalCount;
1783
+ const nodesCreated = result?.nodesResult?.['created'] || 0;
1784
+ const nodesUpdated = result?.nodesResult?.['updated'] || 0;
1785
+ const noChange = result.nodesToMigrate[currentProject]['no change'] !== 0;
1786
+
1787
+ if (
1788
+ !err &&
1789
+ !result.errors?.length &&
1790
+ ((!commit && nodesTotalCount) ||
1791
+ (commit && (nodesCreated || nodesUpdated)))
1792
+ ) {
1793
+ let totalCount: number;
1794
+ if (commit) {
1795
+ let created = typeof nodesCreated === 'number' ? nodesCreated : 0;
1796
+ let updated = typeof nodesUpdated === 'number' ? nodesUpdated : 0;
1797
+
1798
+ totalCount = created + updated;
1799
+ } else {
1800
+ totalCount =
1801
+ typeof nodesTotalCount === 'number' ? nodesTotalCount : 0;
1802
+ }
1803
+
1804
+ log.success(messages.nodes.imported(currentEnv, commit, totalCount));
1805
+ if (!commit) {
1806
+ log.raw(``);
1807
+ log.help(messages.nodes.commitTip());
1808
+ }
1809
+ } else {
1810
+ if (noChange) {
1811
+ log.help(messages.nodes.noChange(currentEnv), err);
1812
+ } else {
1813
+ log.error(messages.nodes.failedImport(currentEnv), err);
1814
+ if (!nodesTotalCount) log.help(messages.nodes.notFound(currentEnv));
1815
+ }
1816
+ }
1817
+ } else {
1818
+ log.warning(messages.models.noList(currentProject));
1819
+ log.help(messages.connect.tip());
1820
+ }
1821
+ };
1822
+
1739
1823
  PrintWebhookSubscriptions = async (subscriptionIdsOrNames?: string[]) => {
1740
1824
  const { currentEnv, log, messages } = this;
1741
1825
  const contensis = await this.ConnectContensis();
@@ -1,5 +1,5 @@
1
1
  import to from 'await-to-js';
2
- import { execFile, spawn } from 'child_process';
2
+ import { spawn } from 'child_process';
3
3
  import inquirer from 'inquirer';
4
4
  import path from 'path';
5
5
 
@@ -21,8 +21,9 @@ import { mergeDotEnvFileContents } from '~/util/dotenv';
21
21
  import { findByIdOrName } from '~/util/find';
22
22
  import { GitHelper } from '~/util/git';
23
23
  import { jsonFormatter } from '~/util/json.formatter';
24
- import { normaliseLineEndings, winSlash } from '~/util/os';
24
+ import { winSlash } from '~/util/os';
25
25
  import { stringifyYaml } from '~/util/yaml';
26
+ import { createSpinner } from 'nanospinner';
26
27
 
27
28
  class ContensisDev extends ContensisRole {
28
29
  git!: GitHelper;
@@ -54,7 +55,7 @@ class ContensisDev extends ContensisRole {
54
55
  );
55
56
 
56
57
  // Retrieve git info
57
- const git = this.git = new GitHelper(projectHome);
58
+ const git = (this.git = new GitHelper(projectHome));
58
59
 
59
60
  // Retrieve ci workflow info
60
61
  const workflowFiles = git.workflows;
@@ -118,10 +119,39 @@ class ContensisDev extends ContensisRole {
118
119
 
119
120
  log.raw(log.infoText(messages.devinit.ciDetails(ciFileName)));
120
121
 
121
- // Look at the workflow file content and make updates
122
- const mappedWorkflow = await mapCIWorkflowContent(this);
122
+ let mappedWorkflow;
123
+ // Location for Client ID / Secret.
124
+ const { clientDetailsOption } = await inquirer.prompt({
125
+ name: 'clientDetailsOption',
126
+ type: 'list',
127
+ prefix: '🔑',
128
+ // Where would you like to store your Client ID/Secret?
129
+ message: messages.devinit.clientDetailsLocation(),
130
+ choices: [
131
+ messages.devinit.clientDetailsInGit(git),
132
+ messages.devinit.clientDetailsInEnv(),
133
+ ],
134
+ });
135
+
136
+ // global 'clientDetailsLocation' variable stores users input on where client id / secert are stored
137
+ if (clientDetailsOption === messages.devinit.clientDetailsInEnv()) {
138
+ this.clientDetailsLocation = 'env';
139
+ } else {
140
+ this.clientDetailsLocation = 'git';
141
+ }
123
142
 
124
- log.help(messages.devinit.ciIntro(git));
143
+ if (this.clientDetailsLocation === 'env') {
144
+ // Update CI Workflow to pull from ENV variables
145
+ mappedWorkflow = await mapCIWorkflowContent(this);
146
+ // Add client id and secret to global 'this'
147
+ this.clientId = existingDeployKey?.id;
148
+ this.clientSecret = existingDeployKey?.sharedSecret;
149
+ log.help(messages.devinit.ciIntro(git, this.clientDetailsLocation));
150
+ } else {
151
+ // Look at the workflow file content and make updates
152
+ mappedWorkflow = await mapCIWorkflowContent(this);
153
+ log.help(messages.devinit.ciIntro(git, this.clientDetailsLocation));
154
+ }
125
155
 
126
156
  if (!dryRun) {
127
157
  // Confirm prompt
@@ -138,16 +168,37 @@ class ContensisDev extends ContensisRole {
138
168
  }
139
169
 
140
170
  // Access token prompt
141
- const { accessToken }: { accessToken: string } = await inquirer.prompt([
171
+ let accessToken: string | undefined = undefined;
172
+ const { canContinue } = await inquirer.prompt([
142
173
  {
143
- type: 'input',
174
+ type: 'confirm',
144
175
  prefix: '🛡️',
176
+ // We're all set to grab your access token. Can we proceed? (⏎ continue)
145
177
  message: messages.devinit.accessTokenPrompt(),
146
- name: 'accessToken',
178
+ name: 'canContinue',
147
179
  },
148
180
  ]);
149
181
  log.raw('');
150
182
 
183
+ if (!canContinue) return;
184
+ if (canContinue) {
185
+ const spinner = createSpinner(messages.devinit.accessTokenFetch());
186
+ spinner.start();
187
+
188
+ // Fetching access token
189
+ const token = await this.GetDeliveryApiKey();
190
+
191
+ if (token) {
192
+ spinner.success();
193
+ this.log.success(messages.devinit.accessTokenSuccess(token));
194
+ accessToken = token;
195
+ } else {
196
+ spinner.error();
197
+ this.log.error(messages.devinit.accessTokenFailed());
198
+ return;
199
+ }
200
+ }
201
+
151
202
  // Magic happens...
152
203
  const checkpoint = (op: string) => {
153
204
  if (errors.length) throw errors[0];
@@ -209,6 +260,12 @@ class ContensisDev extends ContensisRole {
209
260
  PROJECT: currentProject,
210
261
  };
211
262
  if (accessToken) envContentsToAdd['ACCESS_TOKEN'] = accessToken;
263
+ if (this.clientDetailsLocation === 'env') {
264
+ if (git.type === 'github') {
265
+ envContentsToAdd['CONTENSIS_CLIENT_ID'] = this.clientId;
266
+ envContentsToAdd['CONTENSIS_CLIENT_SECRET'] = this.clientSecret;
267
+ }
268
+ }
212
269
 
213
270
  const envFilePath = `${projectHome}/.env`;
214
271
  const existingEnvFile = readFile(envFilePath);
@@ -216,9 +273,7 @@ class ContensisDev extends ContensisRole {
216
273
  (existingEnvFile || '').split('\n').filter(l => !!l),
217
274
  envContentsToAdd
218
275
  );
219
- const newEnvFileContent = normaliseLineEndings(
220
- `${envFileLines.join('\n')}\n`
221
- );
276
+ const newEnvFileContent = envFileLines.join('\n');
222
277
  const envDiff = diffFileContent(existingEnvFile || '', newEnvFileContent);
223
278
 
224
279
  if (dryRun) {
@@ -260,15 +315,17 @@ class ContensisDev extends ContensisRole {
260
315
  }
261
316
  }
262
317
 
263
- // Echo Deployment API key to console, ask user to add secrets to repo
264
- log.warning(messages.devinit.addGitSecretsIntro());
265
- log.help(
266
- messages.devinit.addGitSecretsHelp(
267
- git,
268
- existingDeployKey?.id,
269
- existingDeployKey?.sharedSecret
270
- )
271
- );
318
+ if (this.clientDetailsLocation === 'git') {
319
+ // Echo Deployment API key to console, ask user to add secrets to repo
320
+ log.warning(messages.devinit.addGitSecretsIntro());
321
+ log.help(
322
+ messages.devinit.addGitSecretsHelp(
323
+ git,
324
+ existingDeployKey?.id,
325
+ existingDeployKey?.sharedSecret
326
+ )
327
+ );
328
+ }
272
329
 
273
330
  if (dryRun) {
274
331
  log.success(messages.devinit.dryRun());
@@ -1,8 +1,9 @@
1
1
  import { Role } from 'contensis-management-api/lib/models';
2
2
  import { ApiKey, MigrateRequest } from 'migratortron';
3
-
3
+ import to from 'await-to-js';
4
4
  import ContensisCli from './ContensisCliService';
5
5
  import { OutputOptionsConstructorArg } from '~/models/CliService';
6
+ import { Logger } from '~/util/logger';
6
7
 
7
8
  class ContensisRole extends ContensisCli {
8
9
  constructor(
@@ -13,6 +14,37 @@ class ContensisRole extends ContensisCli {
13
14
  super(args, outputOpts, contensisOpts);
14
15
  }
15
16
 
17
+ GetDeliveryApiKey = async () => {
18
+ const { contensis, currentEnv } = this;
19
+
20
+ const CMS = `https://cms-${currentEnv}.cloud.contensis.com`;
21
+ const API = 'api/contensis-cli/settings/defaultDeliveryApiAccessToken';
22
+
23
+ if (contensis) {
24
+ const [error, bearerToken] = await to(
25
+ contensis.content.sourceRepo.repo.BearerToken()
26
+ );
27
+ if (error) Logger.error(error.message);
28
+
29
+ const token = fetch(`${CMS}/${API}`, {
30
+ method: 'GET',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ Authorization: `Bearer ${bearerToken}`,
34
+ },
35
+ })
36
+ .then(repsonse => repsonse.json())
37
+ .then(({ value }) => {
38
+ if (value) return value;
39
+ })
40
+ .catch(error => {
41
+ throw new Error(error);
42
+ });
43
+
44
+ return token;
45
+ }
46
+ };
47
+
16
48
  CreateOrUpdateApiKey = async (
17
49
  existingKey: ApiKey | undefined,
18
50
  name: string,
package/src/util/diff.ts CHANGED
@@ -34,9 +34,8 @@ export const diffFileContent = (
34
34
 
35
35
  // Create formatted output for console
36
36
  const output: string[] = [];
37
- const lnSpaceLength = Math.max(
38
- ...diffRanges.map(d => d.startLineNumber.toString().length)
39
- );
37
+ const lnSpaceLength =
38
+ Math.max(...diffRanges.map(d => d.startLineNumber.toString().length)) || 0;
40
39
 
41
40
  const lnSpaces = Array(lnSpaceLength).join(' ');
42
41
 
package/src/util/git.ts CHANGED
@@ -111,13 +111,14 @@ export class GitHelper {
111
111
  githubWorkflows = () => {
112
112
  const workflowPath = path.join(this.gitcwd(), '.github/workflows');
113
113
  const workflowFiles = readFiles(workflowPath, false);
114
- // console.log('gh workflows: ', workflowFiles);
115
114
  const addFolderSuffix = (files: string[]) =>
116
115
  files.map(f => `.github/workflows/${f}`);
117
116
 
118
- if (workflowFiles.some(f => f.includes('build')))
117
+ if (workflowFiles.some(f => f.includes('build'))) {
119
118
  return addFolderSuffix(workflowFiles.filter(f => f.includes('build')));
120
- return addFolderSuffix(workflowFiles);
119
+ } else {
120
+ return addFolderSuffix(workflowFiles);
121
+ }
121
122
  };
122
123
  gitlabWorkflow = (ciFileName = GITLAB_CI_FILENAME) => {
123
124
  const workflowPath = this.gitcwd();
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const LIB_VERSION = "1.0.0-beta.96";
1
+ export const LIB_VERSION = "1.0.0-beta.98";