contensis-cli 1.0.0-beta.95 ā 1.0.0-beta.97
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/dist/commands/dev.js +2 -6
- package/dist/commands/dev.js.map +2 -2
- package/dist/commands/import.js +18 -0
- package/dist/commands/import.js.map +2 -2
- package/dist/localisation/en-GB.js +28 -11
- package/dist/localisation/en-GB.js.map +2 -2
- package/dist/mappers/DevInit-to-CIWorkflow.js +251 -136
- package/dist/mappers/DevInit-to-CIWorkflow.js.map +3 -3
- package/dist/models/DevService.d.js.map +1 -1
- package/dist/services/ContensisCliService.js +64 -0
- package/dist/services/ContensisCliService.js.map +2 -2
- package/dist/services/ContensisDevService.js +65 -18
- package/dist/services/ContensisDevService.js.map +2 -2
- package/dist/services/ContensisRoleService.js +31 -4
- package/dist/services/ContensisRoleService.js.map +3 -3
- package/dist/util/diff.js +3 -1
- package/dist/util/diff.js.map +2 -2
- package/dist/util/git.js +4 -2
- package/dist/util/git.js.map +2 -2
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +2 -1
- package/src/commands/dev.ts +7 -7
- package/src/commands/import.ts +25 -0
- package/src/localisation/en-GB.ts +43 -9
- package/src/mappers/DevInit-to-CIWorkflow.ts +386 -196
- package/src/models/DevService.d.ts +14 -0
- package/src/services/ContensisCliService.ts +84 -0
- package/src/services/ContensisDevService.ts +80 -21
- package/src/services/ContensisRoleService.ts +33 -1
- package/src/util/diff.ts +4 -1
- package/src/util/git.ts +4 -3
- package/src/version.ts +1 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { JSONPath, JSONPathOptions } from 'jsonpath-plus';
|
|
3
|
+
import { Document } from 'yaml';
|
|
3
4
|
import {
|
|
4
5
|
GitHubActionPushBlockJob,
|
|
5
6
|
GitHubActionPushBlockJobStep,
|
|
7
|
+
GitLabPushBlockJobStage,
|
|
6
8
|
} from '~/models/DevService';
|
|
7
9
|
import { readFile } from '~/providers/file-provider';
|
|
8
10
|
import ContensisDev from '~/services/ContensisDevService';
|
|
9
11
|
import { diffFileContent } from '~/util/diff';
|
|
10
|
-
import { GitHelper } from '~/util/git';
|
|
11
12
|
import { logError } from '~/util/logger';
|
|
12
13
|
import { normaliseLineEndings } from '~/util/os';
|
|
13
14
|
import { parseYamlDocument, validateWorkflowYaml } from '~/util/yaml';
|
|
@@ -19,144 +20,328 @@ type MappedWorkflowOutput = {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export const mapCIWorkflowContent = async (
|
|
22
|
-
cli: ContensisDev
|
|
23
|
-
git: GitHelper
|
|
23
|
+
cli: ContensisDev
|
|
24
24
|
): Promise<MappedWorkflowOutput | undefined> => {
|
|
25
25
|
// get existing workflow file
|
|
26
|
-
const workflowFile = readFile(git.ciFilePath);
|
|
26
|
+
const workflowFile = readFile(cli.git.ciFilePath);
|
|
27
27
|
if (!workflowFile) return undefined;
|
|
28
28
|
|
|
29
|
-
const blockId = git.name;
|
|
30
|
-
if (git.type === 'github') {
|
|
31
|
-
const addGitHubActionJobStep: GitHubActionPushBlockJobStep = {
|
|
32
|
-
name: 'Push block to Contensis',
|
|
33
|
-
id: 'push-block',
|
|
34
|
-
uses: 'contensis/block-push@v1',
|
|
35
|
-
with: {
|
|
36
|
-
'block-id': blockId,
|
|
37
|
-
// 'image-uri': '${{ steps.build.outputs.image-uri }}',
|
|
38
|
-
alias: cli.currentEnv,
|
|
39
|
-
'project-id': cli.currentProject,
|
|
40
|
-
'client-id': '${{ secrets.CONTENSIS_CLIENT_ID }}',
|
|
41
|
-
'shared-secret': '${{ secrets.CONTENSIS_SHARED_SECRET }}',
|
|
42
|
-
},
|
|
43
|
-
};
|
|
29
|
+
const blockId = cli.git.name;
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// }
|
|
61
|
-
}
|
|
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),
|
|
62
46
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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),
|
|
74
58
|
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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'
|
|
84
122
|
);
|
|
85
123
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
if (appImageUri.addVar)
|
|
125
|
+
setWorkflowElement(
|
|
126
|
+
workflowDoc,
|
|
127
|
+
`variables.${appImageUri.var}`,
|
|
128
|
+
appImageUri.uri
|
|
91
129
|
);
|
|
92
130
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
131
|
+
if (appImageUri.var)
|
|
132
|
+
addGitLabJobStage.variables['image_uri'] = `\$${appImageUri.var}`;
|
|
133
|
+
};
|
|
96
134
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
);
|
|
102
142
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
+
);
|
|
106
163
|
|
|
107
|
-
|
|
108
|
-
|
|
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
|
+
}
|
|
109
213
|
|
|
110
|
-
|
|
111
|
-
// We want it to look like this "jobs.build.steps.3"
|
|
112
|
-
const stepPath = step.path
|
|
113
|
-
.replace('$[', '')
|
|
114
|
-
.replaceAll('][', '.')
|
|
115
|
-
.replace(']', '')
|
|
116
|
-
.replaceAll("'", '');
|
|
214
|
+
cli.log.debug(`New file content to write\n\n${workflowDoc}`);
|
|
117
215
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
id: ${step.value.id}\n`
|
|
122
|
-
);
|
|
216
|
+
const newWorkflow = normaliseLineEndings(
|
|
217
|
+
workflowDoc.toString({ lineWidth: 0 })
|
|
218
|
+
);
|
|
123
219
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
setWorkflowElement(`${stepPath}.with.block-id`, blockId);
|
|
220
|
+
return newWorkflow;
|
|
221
|
+
};
|
|
127
222
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
};
|
|
131
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:build-${{ github.run_number }}'
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (appImageUri.addVar)
|
|
132
258
|
setWorkflowElement(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
setWorkflowElement(
|
|
137
|
-
`${stepPath}.with.shared-secret`,
|
|
138
|
-
'${{ secrets.CONTENSIS_SHARED_SECRET }}'
|
|
259
|
+
workflowDoc,
|
|
260
|
+
`env.${appImageUri.var}`,
|
|
261
|
+
appImageUri.uri
|
|
139
262
|
);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
263
|
+
// workflowDoc.addIn(['env'], { [appImageUri.var]: appImageUri.uri });
|
|
264
|
+
|
|
265
|
+
if (appImageUri.var)
|
|
266
|
+
addGitHubActionJobStep.with[
|
|
267
|
+
'image-uri'
|
|
268
|
+
] = `\${{ env.${appImageUri.var} }}`;
|
|
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
|
+
);
|
|
159
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 {
|
|
160
345
|
({ needs } = await inquirer.prompt([
|
|
161
346
|
{
|
|
162
347
|
type: 'list',
|
|
@@ -169,102 +354,102 @@ export const mapCIWorkflowContent = async (
|
|
|
169
354
|
),
|
|
170
355
|
},
|
|
171
356
|
]));
|
|
172
|
-
cli.log.raw('');
|
|
173
|
-
} else if (existingBuildJobStep.length === 1)
|
|
174
|
-
// Exactly one job step found containing a property name of *build*
|
|
175
|
-
// we'll assume that is the one the push-block job depends on
|
|
176
|
-
needs = existingBuildJobStep[0].parentProperty;
|
|
177
|
-
|
|
178
|
-
if (existingBuildJobStep.length === 0 || needs === 'none') {
|
|
179
|
-
// No existing build step found or chosen, offer all job steps in prompt
|
|
180
|
-
const choices = Object.keys(workflowJS.jobs);
|
|
181
|
-
choices.push(new inquirer.Separator() as any);
|
|
182
|
-
choices.push('none');
|
|
183
|
-
|
|
184
|
-
({ needs } = await inquirer.prompt([
|
|
185
|
-
{
|
|
186
|
-
type: 'list',
|
|
187
|
-
prefix: 'ā',
|
|
188
|
-
message: cli.messages.devinit.ciMultipleJobChoices(),
|
|
189
|
-
name: 'needs',
|
|
190
|
-
choices,
|
|
191
|
-
default: choices.find(
|
|
192
|
-
j => typeof j === 'string' && j.includes('docker')
|
|
193
|
-
),
|
|
194
|
-
},
|
|
195
|
-
]));
|
|
196
|
-
if (needs === 'none') needs = undefined;
|
|
197
|
-
cli.log.raw('');
|
|
198
357
|
}
|
|
199
358
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// We could expand validation to check for having a build step to wait for
|
|
222
|
-
// or if a valid image-uri attribute is set
|
|
223
|
-
if (workflowIsValid === true) {
|
|
224
|
-
cli.log.success(`GitHub workflow YAML is valid`);
|
|
225
|
-
cli.log.debug(
|
|
226
|
-
`New file content to write to ${git.ciFilePath}\n\n${workflowDoc}`
|
|
227
|
-
);
|
|
228
|
-
} else if (Array.isArray(workflowIsValid)) {
|
|
229
|
-
// Errors
|
|
230
|
-
logError(
|
|
231
|
-
[
|
|
232
|
-
...workflowIsValid.map(
|
|
233
|
-
res => new Error(`${res.code}: ${res.detail || res.title}`)
|
|
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')
|
|
234
380
|
),
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
);
|
|
381
|
+
},
|
|
382
|
+
]));
|
|
383
|
+
if (needs === 'none') needs = undefined;
|
|
384
|
+
cli.log.raw('');
|
|
239
385
|
}
|
|
240
|
-
const newWorkflow = normaliseLineEndings(
|
|
241
|
-
workflowDoc.toString({ lineWidth: 0 })
|
|
242
|
-
);
|
|
243
386
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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],
|
|
248
396
|
};
|
|
397
|
+
|
|
398
|
+
// Add the new "job" to the Yaml Document
|
|
399
|
+
workflowDoc.addIn(['jobs'], {
|
|
400
|
+
key: 'deploy',
|
|
401
|
+
value: newJob,
|
|
402
|
+
});
|
|
249
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;
|
|
250
433
|
};
|
|
251
434
|
|
|
252
435
|
const determineAppImageUri = async (
|
|
253
436
|
cli: ContensisDev,
|
|
254
|
-
vars: [string, string][]
|
|
437
|
+
vars: [string, string][],
|
|
438
|
+
defaultUri: string
|
|
255
439
|
) => {
|
|
256
440
|
// Determine container image-uri via variables and/or prompts
|
|
257
441
|
|
|
258
442
|
// Find vars including the word "image"
|
|
259
|
-
const imageVars = vars.filter(([varname
|
|
443
|
+
const imageVars = vars.filter(([varname]) =>
|
|
260
444
|
varname.toLowerCase().includes('image')
|
|
261
445
|
);
|
|
262
446
|
// Find vars named "image" that include the word "app"
|
|
263
447
|
const appImageVars = imageVars.filter(
|
|
264
448
|
([varname, value]) =>
|
|
265
|
-
varname.toLowerCase().includes('
|
|
266
|
-
varname.toLowerCase().includes('
|
|
267
|
-
|
|
449
|
+
!varname.toLowerCase().includes('builder_') &&
|
|
450
|
+
(varname.toLowerCase().includes('app_') ||
|
|
451
|
+
varname.toLowerCase().includes('build_') ||
|
|
452
|
+
value?.toLowerCase().includes('/app'))
|
|
268
453
|
);
|
|
269
454
|
|
|
270
455
|
const appImageUriGuess = appImageVars?.[0];
|
|
@@ -286,8 +471,7 @@ const determineAppImageUri = async (
|
|
|
286
471
|
// prompt for an app image uri
|
|
287
472
|
const choices = vars.map(v => v[0]);
|
|
288
473
|
const enterOwnMsg = 'enter my own / use default';
|
|
289
|
-
|
|
290
|
-
'ghcr.io/${{ github.repository }}/${{ github.ref_name }}/app:build-${{ github.run_number }}';
|
|
474
|
+
|
|
291
475
|
choices.push(new inquirer.Separator() as any);
|
|
292
476
|
choices.push(enterOwnMsg);
|
|
293
477
|
choices.push(defaultUri);
|
|
@@ -312,7 +496,7 @@ const determineAppImageUri = async (
|
|
|
312
496
|
return [enterOwnMsg, defaultUri].includes(answers.appImageVar);
|
|
313
497
|
},
|
|
314
498
|
prefix: `\n \nš`,
|
|
315
|
-
message: cli.messages.devinit.ciEnterOwnAppImagePrompt(),
|
|
499
|
+
message: cli.messages.devinit.ciEnterOwnAppImagePrompt(cli.git),
|
|
316
500
|
name: 'appImageUri',
|
|
317
501
|
default: defaultUri,
|
|
318
502
|
},
|
|
@@ -326,7 +510,13 @@ const determineAppImageUri = async (
|
|
|
326
510
|
appImageVar = undefined;
|
|
327
511
|
}
|
|
328
512
|
|
|
329
|
-
if (appImageVar)
|
|
513
|
+
if (appImageVar)
|
|
514
|
+
appImageUri =
|
|
515
|
+
cli.git.type === 'github'
|
|
516
|
+
? `\${{ env.${appImageVar} }}`
|
|
517
|
+
: `\$${appImageVar}`;
|
|
518
|
+
|
|
519
|
+
// TODO: prompt further for image tag
|
|
330
520
|
|
|
331
521
|
return {
|
|
332
522
|
addVar: !appImageVar,
|