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