@soft-artel/ci 2.3.87 → 2.3.89

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 (80) hide show
  1. package/Project.d.ts +57 -0
  2. package/Project.d.ts.map +1 -0
  3. package/Project.js +173 -0
  4. package/Project.js.map +1 -0
  5. package/commands/incrementBuild.d.ts +3 -0
  6. package/commands/incrementBuild.d.ts.map +1 -0
  7. package/commands/incrementBuild.js +34 -0
  8. package/commands/incrementBuild.js.map +1 -0
  9. package/commands/k8s-build.d.ts +30 -0
  10. package/commands/k8s-build.d.ts.map +1 -0
  11. package/commands/k8s-build.js +291 -0
  12. package/commands/k8s-build.js.map +1 -0
  13. package/commands/k8s-deploy.d.ts +11 -0
  14. package/commands/k8s-deploy.d.ts.map +1 -0
  15. package/commands/k8s-deploy.js +145 -0
  16. package/commands/k8s-deploy.js.map +1 -0
  17. package/commands/xcode.d.ts +3 -0
  18. package/commands/xcode.d.ts.map +1 -0
  19. package/commands/xcode.js +128 -0
  20. package/commands/xcode.js.map +1 -0
  21. package/package.json +3 -3
  22. package/services/Git.d.ts +46 -0
  23. package/services/Git.d.ts.map +1 -0
  24. package/services/Git.js +138 -0
  25. package/services/Git.js.map +1 -0
  26. package/services/Gitlab.d.ts +14 -0
  27. package/services/Gitlab.d.ts.map +1 -0
  28. package/services/Gitlab.js +101 -0
  29. package/services/Gitlab.js.map +1 -0
  30. package/services/Jira.d.ts +32 -0
  31. package/services/Jira.d.ts.map +1 -0
  32. package/services/Jira.js +136 -0
  33. package/services/Jira.js.map +1 -0
  34. package/services/Reporter.d.ts +43 -0
  35. package/services/Reporter.d.ts.map +1 -0
  36. package/services/Reporter.js +134 -0
  37. package/services/Reporter.js.map +1 -0
  38. package/utils/Exception.d.ts +5 -0
  39. package/utils/Exception.d.ts.map +1 -0
  40. package/utils/Exception.js +14 -0
  41. package/utils/Exception.js.map +1 -0
  42. package/utils/Logger.d.ts +11 -0
  43. package/utils/Logger.d.ts.map +1 -0
  44. package/utils/Logger.js +62 -0
  45. package/utils/Logger.js.map +1 -0
  46. package/utils/Shell.d.ts +39 -0
  47. package/utils/Shell.d.ts.map +1 -0
  48. package/utils/Shell.js +89 -0
  49. package/utils/Shell.js.map +1 -0
  50. package/utils/helpers.d.ts +16 -0
  51. package/utils/helpers.d.ts.map +1 -0
  52. package/utils/helpers.js +109 -0
  53. package/utils/helpers.js.map +1 -0
  54. package/utils/prototype.d.ts +9 -0
  55. package/utils/prototype.d.ts.map +1 -0
  56. package/utils/prototype.js +186 -0
  57. package/utils/prototype.js.map +1 -0
  58. package/.env +0 -21
  59. package/.eslintcache +0 -1
  60. package/.eslintignore +0 -4
  61. package/.eslintrc +0 -246
  62. package/.gitlab-ci.yml +0 -12
  63. package/README.md +0 -33
  64. package/_publish.sh +0 -11
  65. package/src/Project.ts +0 -286
  66. package/src/commands/incrementBuild.ts +0 -46
  67. package/src/commands/k8s-build.ts +0 -441
  68. package/src/commands/k8s-deploy.ts +0 -215
  69. package/src/commands/xcode.ts +0 -192
  70. package/src/services/Git.ts +0 -203
  71. package/src/services/Gitlab.ts +0 -129
  72. package/src/services/Jira.ts +0 -228
  73. package/src/services/Reporter.ts +0 -230
  74. package/src/utils/Exception.ts +0 -19
  75. package/src/utils/Logger.ts +0 -85
  76. package/src/utils/Shell.ts +0 -120
  77. package/src/utils/helpers.ts +0 -126
  78. package/src/utils/prototype.ts +0 -313
  79. package/test.ts +0 -0
  80. package/tsconfig.json +0 -25
@@ -1,441 +0,0 @@
1
- #!/usr/bin/env node
2
- import '../utils/prototype';
3
-
4
- import {program, OptionValues} from 'commander';
5
- import { promises as fsAsync } from 'fs';
6
- import Mustache from 'mustache';
7
- import yaml from 'js-yaml';
8
-
9
- import Shell from '../utils/Shell';
10
- import { Jira } from '../services/Jira';
11
- import Git from '../services/Git';
12
- import { Config, Project, RunCommandHandler } from '../Project';
13
- import { Reporter } from '../services/Reporter';
14
- import { log } from '../utils/Logger';
15
- import { md5, resolvePath } from '../utils/helpers';
16
- import { Exception } from '../utils/Exception';
17
-
18
- const ENV = process.env;
19
-
20
- // ============================================
21
- // Comand flow
22
-
23
- const runHandler: RunCommandHandler = async ( prj: Project, reporter: Reporter, opts: OptionValues, config: Config ) => {
24
-
25
- reporter.$message_footer_changes_from = prj.version;
26
- reporter.$message_footer_changes_to = ENV.CI_COMMIT_REF_NAME || 'dev';
27
-
28
- const release = opts.changelog ? await Jira.resolve(prj) : { tasks:[], bugs:[], changelog:''};
29
-
30
- // --build: Build and push docker image to registry
31
- Shell.cd( prj.rootPath );
32
-
33
- // -----------------------------
34
- // 1. Start
35
- prj.incrementBuild();
36
-
37
- const pkg = JSON.parse(await Shell.cat(prj.rootPath +'/package.json') || 'no package.json file!');
38
- pkg.version = prj.version;
39
- await fsAsync.writeFile(prj.rootPath +'/package.json', JSON.stringify(pkg, null, 3), { encoding: 'utf8', flag: 'w+'});
40
-
41
- await reporter.startBuild();
42
- log.info(`1. build ${ prj.stage }`);
43
-
44
- // -----------------------------
45
- // 2. Chek updated apps
46
- await reporter.append(` + Load apps and check updated`);
47
- log.info(`2. Load apps and check updated`);
48
-
49
- const updateFiles = await Git.getDiffFiles();
50
- log.info(`\n\nUPDATED FILES:\n$`, updateFiles);
51
-
52
- const { apps, updated, images } = await getAppsAndImages(prj, config, updateFiles);
53
-
54
- if(!updated){
55
- log.info('SKIP BUILD: NO Apps to update!');
56
- await reporter.skip(`SKIP - no apps to update\n ${ updateFiles.join('\n ')}`);
57
- process.exit(0);
58
- }
59
-
60
- // -----------------------------
61
- // 4. write k8s manifests!
62
- await reporter.append(` + Write K8s manifests`);
63
- log.info(`3. Write K8s manifests`);
64
-
65
- let imagesToBuild: Record<string, DockerImage> = {}
66
-
67
- for (const appName of updated) {
68
- const app = apps[ appName ]
69
- const image = images[ app.dockerImageKey ]
70
-
71
- if( !image ){
72
- throw new Exception('No dockerfile found for app: '+ app.name, { app, images })
73
- }
74
-
75
-
76
- for (const stage of Object.keys( prj.$stagesState )) {
77
-
78
- const k8s_data: Record<string, any> = {};
79
-
80
- k8s_data.stage = stage;
81
- k8s_data.app = app;
82
- k8s_data.image = image;
83
- k8s_data.prj = prj;
84
- k8s_data.spec = app.k8s_spec[ stage ] || {};
85
-
86
- if(!k8s_data.spec.replicas){
87
- k8s_data.spec.replicas = 1;
88
- }
89
-
90
- log.dbg(`${app.name} k8s data`, k8s_data);
91
- // k8s_data.spec.volumeMounts = k8s_data.spec.volumes;
92
- const manifest = Mustache.render(app.k8s_tpl, k8s_data);
93
-
94
- yaml.loadAll(manifest); // Test yaml
95
-
96
- await Shell.mkdir(`${ prj.rootPath}/_k8s/${stage}`, {ignoreError: false});
97
- await fsAsync.writeFile(`${ prj.rootPath}/_k8s/${stage}/${app.name}.yml`, manifest);
98
-
99
- log.dbg(`[${stage}] ${app.name} writed -> ${ prj.rootPath}/_k8s/${stage}/${app.name}.yml `);
100
-
101
- }
102
-
103
- imagesToBuild[ image.key ] = image
104
- }
105
-
106
- // -----------------------------
107
- // 5. Build images
108
- await Shell.execRepeat(`docker login -u ${ ENV.CI_REGISTRY_USER } -p ${ ENV.CI_REGISTRY_PASSWORD } ${ ENV.CI_REGISTRY }`);
109
-
110
- for (const image of Object.values( imagesToBuild )) {
111
-
112
- // 5.1. distrib
113
- log.info(`${ image.tag } distrib`);
114
- await reporter.append(` + ${ image.tag } run tests and distrib`);
115
- const distribCommand = image.distribRun || 'npm run distrib'
116
- log.info( distribCommand )
117
- await Shell.exec( distribCommand );
118
-
119
- // 5.2. build
120
- log.info(`${ image.tag } build docker image from root context dir: ${ prj.rootPath }`);
121
- await reporter.append(` + ${ image.tag } build docker image`);
122
- Shell.cd(image.path);
123
- await Shell.exec(`docker build --platform amd64 -t ${ image.fullTag } -f ${ image.path } ${ prj.rootPath }/.`);
124
-
125
- // 5.3 push
126
- log.dbg(`${ image.tag } Docker push`);
127
- await reporter.append(` + ${ image.tag } push image to registry`);
128
- await Shell.execRepeat(`docker login -u ${ ENV.CI_REGISTRY_USER } -p ${ ENV.CI_REGISTRY_PASSWORD } ${ ENV.CI_REGISTRY }`);
129
- await Shell.execRepeat(`docker push ${ image.fullTag }`);
130
- log.dbg(`Clear local docker image: ${ image.tag }`);
131
- await Shell.exec(`docker rmi -f ${ image.fullTag }`);
132
-
133
- }
134
-
135
- // -----------------------------
136
- // 5. Save new build to GitLab CI Variable
137
-
138
- await reporter.append(` + Save current build: ${ prj.build }`);
139
- log.dbg(`7. Put current build:${ prj.build } to GitLab`);
140
- await prj.saveGitLabBuild();
141
-
142
- // -----------------------
143
-
144
-
145
- if(opts.changelog){
146
- await prj.updateChangeLog(release.changelog);
147
- await reporter.append(` + Update <a href=\"${ prj.url }/-/blob/${ ENV.CI_COMMIT_REF_NAME || 'dev' }/CHANGELOG.md\">changelog</a>.`);
148
- }
149
-
150
- await Git.setOrigin();
151
- await Shell.exec(`git add -A`, { ignoreError: true, silent: false });
152
- await Shell.exec(`git commit -a -m "v${ prj.version }"`, { ignoreError: true, silent: false });
153
-
154
- try{
155
- await Git.push(ENV.CI_COMMIT_REF_NAME || 'dev');
156
- }catch(e){
157
- log.error(e);
158
- await Shell.exec(`git checkout --ours .`, { ignoreError: true, silent: false });
159
- await Shell.exec(`git add -u`, { ignoreError: true, silent: false });
160
- await Shell.exec(`git commit -a -m "v${ prj.version } resolved"`, { ignoreError: true, silent: false });
161
- await Git.push(ENV.CI_COMMIT_REF_NAME || 'dev');
162
- }
163
-
164
-
165
- if(opts.gittag){
166
- await reporter.append(` + Git tag new version`);
167
- await Git.makeTag(prj.version);
168
- }
169
-
170
- await reporter.succses();
171
- }
172
-
173
-
174
- // ========================================================
175
- // Command
176
-
177
- async function main() {
178
-
179
- program
180
- .option('--path <project dir>', 'Path to project directory [default - current]')
181
-
182
- .option('--no-prebuild', 'Skip prebuild phase')
183
-
184
- .option('--no-changelog', 'Skip create gitlog')
185
- .option('--no-gittag', 'Skip create and push new git tag')
186
- .action( async () => { await Project.run( program.opts() || {}, runHandler ) });
187
-
188
- await program.parseAsync(process.argv);
189
- }
190
- main();
191
-
192
-
193
- // ================================================================================================
194
- // Clasess
195
-
196
- class App{
197
-
198
- group = '';
199
- name = '';
200
- app = '';
201
- path = '';
202
-
203
- $pathComp: string[] = [ './' ];
204
-
205
- dockerImageKey = ''
206
-
207
- k8s_tpl = ''
208
- k8s_spec: Record<string, any> = {}
209
-
210
- // ----------
211
-
212
- constructor(path: string = '', prj: Project){
213
- let pathComp = path.split('/');
214
- log.dbg( 'App try load by path:'+ path, { rootPath: prj.rootPath, pathComp } )
215
-
216
- this.path = path;
217
-
218
- if( pathComp.length > 1 && pathComp[0] === 'src' ){
219
- // Remove src folder reference
220
- pathComp = pathComp.splice(1)
221
- }
222
-
223
- if( pathComp.length > 1 && pathComp[1] === 'src' ){
224
- // Remove src folder reference
225
- pathComp = pathComp.splice(0,-1)
226
- }
227
-
228
-
229
- if(path === prj.rootPath ){
230
- this.$pathComp = [ prj.rootPath ];
231
- }
232
- else if(pathComp.length > 1 && pathComp[1].length > 1 ){
233
-
234
- this.group = pathComp[0];
235
- this.app = pathComp[1];
236
- this.name = `${this.group}-${this.app}`;
237
- this.$pathComp.push(`${ this.group }/`);
238
- this.$pathComp.push(`${ this.path }/`);
239
-
240
- }else{
241
- this.app = pathComp.length == 1 ? pathComp[0] : path;
242
- this.group = prj.name !== this.app ? prj.name : ''
243
-
244
- this.name = this.group === '' ? this.app : `${this.group}-${this.app}`;
245
-
246
- this.$pathComp = [ this.path ]
247
- }
248
-
249
- if( this.app === ''){
250
- this.group = '';
251
- this.app = prj.name;
252
- this.name = this.app ;
253
- }
254
-
255
- log.dbg( this.name, this )
256
- }
257
-
258
- }
259
-
260
- // ------------------------------------------------------------------
261
-
262
-
263
- export class DockerImage{
264
-
265
- path = '';
266
- key = '';
267
-
268
- srcImage = '';
269
-
270
- tag = '';
271
- fullTag = '';
272
-
273
- distribRun?: string;
274
-
275
- // -----------
276
- constructor( path: string, appName:string, ver: string, build: number, dockerfile?: string ){
277
- if( !dockerfile ){
278
- throw new Error('Wrong dockerfile: Empty! by path:'+path)
279
- }
280
-
281
- this.path = path
282
- this.key = md5( dockerfile )
283
-
284
- const dockerImage = /FROM (.+)/.exec(dockerfile);
285
- if(!dockerImage || dockerImage.length < 2){
286
- throw new Error(`Wrong dockerfile format:\n${dockerfile}`);
287
- }
288
-
289
- this.srcImage = dockerImage[1];
290
-
291
- const dockerName = /#[ ]?NAME (.+)/.exec(dockerfile);
292
- const name = dockerName && dockerName.length > 1 ? dockerName[1] : appName || this.key;
293
-
294
- // api-v1.2:#321
295
- this.tag = `${name}-v${ver}:${build}`;
296
- this.fullTag = `${ ENV.CI_REGISTRY_IMAGE }/${ this.tag }`;
297
-
298
- const distribAction = /#[ ]?DISTRIB RUN (.+)/.exec(dockerfile);
299
- this.distribRun = distribAction && distribAction.length > 1 ? distribAction[1] : undefined;
300
-
301
- }
302
- }
303
-
304
-
305
- // ================================================================================================
306
- // Helpers:
307
-
308
- export async function getAppsAndImages(prj: Project, config: Config, updatedPaths: string[], maxdepth: number = 4, ignore: string[] = ['*/_*', '*/\.*', '*/node_modules*']): Promise<{ apps: Record<string, App>; updated: string[] | undefined; images: Record<string, DockerImage> }>{
309
-
310
- const appsPath = config.appsPath ? resolvePath( prj.rootPath + '/' + config.appsPath ) : prj.rootPath
311
-
312
- Shell.cd( appsPath );
313
-
314
- const appFile = config.appFile || 'app.k8s.yaml'
315
-
316
- const appsPaths = await Shell.find( appsPath, appFile, maxdepth, ignore);
317
-
318
- log.dbg( 'Apps paths by appFile:'+ appFile, appsPaths )
319
-
320
- const ignorePaths = [ ...config.ignorePaths || [], ...config.sharedPaths || [] ];
321
-
322
- const apps: Record<string, App> = {};
323
- const watchPaths: Record<string, string> = {};
324
-
325
- const dockerImages: Record<string, DockerImage> = {}
326
-
327
- // Load rootDocker Image
328
- let rootDockerFileKey = ''
329
- const dockerfileRootPath = resolvePath( prj.rootPath )+'/dockerfile'
330
- if( await Shell.test('-e', dockerfileRootPath ) ){
331
- const image = new DockerImage( dockerfileRootPath, prj.name, prj.shortVersion, prj.build, await Shell.cat( dockerfileRootPath ) );
332
- dockerImages[ image.key ] = image
333
- rootDockerFileKey= image.key
334
- log.dbg( 'Dokerfile at root path - found! '+ image.key )
335
- }
336
- else{
337
- log.info( 'Dokerfile at root path not found! path:'+ dockerfileRootPath )
338
- }
339
-
340
- const k8s_tpl_fileName = 'app.k8s.yaml';
341
- const k8s_spec_fileName = 'app.k8s.json';
342
-
343
- for(const file of appsPaths.sort()) {
344
- const appFullPath = resolvePath( file.replace( '/'+appFile, '') )
345
-
346
- const path = appFullPath.replace(appsPath+'/', '').replace('/src', '')
347
-
348
- let skip = false;
349
- for(const i_path of ignorePaths){
350
- if(path.includes(i_path)){
351
- skip = true;
352
- break;
353
- }
354
- }
355
-
356
- if(skip){
357
- continue;
358
- }
359
-
360
- const app = new App(path, prj);
361
-
362
- // ------------
363
- // get app manifest
364
-
365
-
366
- app.k8s_tpl = await Shell.cat( appFullPath + '/' + k8s_tpl_fileName, {silent: true, ignoreError: false }) || ''
367
-
368
- const k8s_specFile = await Shell.cat( appFullPath + '/' + k8s_spec_fileName, {silent: true, ignoreError:true });
369
-
370
- if(k8s_specFile && k8s_specFile !== ''){
371
- try {
372
- app.k8s_spec = JSON.parse( k8s_specFile ) as Record<string, any>;
373
- } catch(e){
374
- log.error(e);
375
- }
376
- }
377
-
378
- // ------------
379
- // get app dockerfile
380
-
381
- const dockerfileAppPath = appFullPath+'/dockerfile'
382
- if( app.path !== '' && await Shell.test('-e', dockerfileAppPath ) ){
383
- log.info( 'Dokerfile at app "'+ app.name +'" found! path:'+ dockerfileAppPath )
384
- const image = new DockerImage( dockerfileAppPath, app.name, prj.shortVersion, prj.build, await Shell.cat( dockerfileAppPath ) );
385
- dockerImages[ image.key ] = image
386
- app.dockerImageKey = image.key
387
- }
388
- else{
389
- log.dbg( 'App dokerfile not found at:"'+ dockerfileAppPath +'", and set as default:'+ rootDockerFileKey )
390
- app.dockerImageKey = rootDockerFileKey
391
- }
392
-
393
- apps[ app.name ] = app;
394
- watchPaths[ app.path ] = app.name;
395
- }
396
-
397
- log.dbg(`Find apps by ${ appFile }`, Object.keys( apps ).sort());
398
-
399
- if( Object.keys( dockerImages ).length === 0 ){
400
- throw new Exception('No any dockerfile found in project! Pleace collect it on root or app path')
401
- }
402
-
403
- const sharedPaths = config.sharedPaths || [];
404
-
405
- sharedPaths.push('package.json');
406
- sharedPaths.push( k8s_tpl_fileName );
407
- sharedPaths.push( k8s_spec_fileName );
408
- sharedPaths.push('dockerfile');
409
- sharedPaths.push('.gitlab-ci.yml')
410
- sharedPaths.push('config.ts')
411
-
412
- const watchPathsArr = Object.keys(watchPaths);
413
-
414
- let appsChanged: Record<string, App> = {};
415
-
416
- for (const file of updatedPaths) {
417
-
418
- let allApps = false;
419
- for(const i_path of sharedPaths){
420
- if(file.includes(i_path)){
421
- allApps = true;
422
- break;
423
- }
424
- }
425
- if(allApps){
426
- appsChanged = apps;
427
- break;
428
- }
429
-
430
- for(const testPath of watchPathsArr){
431
- const appName = watchPaths[ testPath ];
432
- if((RegExp(testPath).exec(file)) && apps[ appName ] && appsChanged[ appName ] === undefined){
433
- appsChanged[ appName ] = apps[ appName ];
434
- }
435
- }
436
- }
437
-
438
- const changed = Object.keys(appsChanged);
439
- return { apps, updated: changed.length > 0 ? changed : undefined, images: dockerImages };
440
-
441
- }
@@ -1,215 +0,0 @@
1
- #!/usr/bin/env node
2
- import '../utils/prototype';
3
-
4
- import {program, OptionValues} from 'commander';
5
-
6
- import Shell, { ExecOptions } from '../utils/Shell';
7
- import { Jira, ReleaseInfo } from '../services/Jira';
8
- import Git from '../services/Git';
9
- import { Config, Project } from '../Project';
10
- import { Reporter } from '../services/Reporter';
11
- import { log } from '../utils/Logger';
12
- import { RunCommandHandler } from '../Project';
13
- import { Exception } from '../utils/Exception';
14
- import { resolvePath } from '../utils/helpers';
15
-
16
- const ENV = process.env;
17
-
18
-
19
- // ============================================
20
- // Command flow
21
-
22
- const runHandler: RunCommandHandler = async ( prj: Project, reporter: Reporter, opts: OptionValues, config: Config ) => {
23
-
24
- // ------------------
25
- // Инициализируемся
26
-
27
- reporter.$message_footer_changes_from = prj.prevVersion || prj.version;
28
- reporter.$message_footer_changes_to = prj.version;
29
-
30
- const release: ReleaseInfo = opts.changelog ? await Jira.resolve(prj) : { commits:[], components:[], tasks:[], bugs:[], changelog:''};
31
-
32
- // 1. Меняем стейдж на новый, потому что деплоим в него!
33
- const stage = opts.stage;
34
-
35
- const configKey = `K8S_${ stage.toUpperCase() }_CONFIG`;
36
- let configFile = ENV[ configKey ];
37
-
38
- if(!configFile){
39
- throw new Exception(`CI VARIABLE K8s config file not found: ${ configKey }`);
40
- }
41
-
42
- configFile = resolvePath(configFile) ;
43
-
44
-
45
- prj.stage = stage;
46
- await reporter.startDeploy(stage);
47
-
48
- // предполагаем для для каждого стейджа у нас отдельныая k8s нода!
49
- // namespace = default для всех ресурсов
50
-
51
- // 2. Получаем путь
52
- const k8s_dir = `${ prj.rootPath }/_k8s/${stage}/`;
53
- Shell.cd(k8s_dir);
54
-
55
- // 3. Обходим ВСЕ файлы - те что не поменялись не применятся
56
- const deployedApps: Record<string, string> = {};
57
- const k8s_files = await Shell.ls(k8s_dir);
58
-
59
- try{
60
-
61
- // Деплоим все(!) аппы - те что не поменялись не применятся
62
- for (const k8s_file of k8s_files) {
63
-
64
- const appName = k8s_file.replace('.yml', '');
65
- const status = await k8sApply( configFile, `${ k8s_dir }/${k8s_file}`, appName, opts.namespace );
66
-
67
- let msg = '';
68
- if(status.valid){
69
- deployedApps[ appName ] = status.msg;
70
- msg = ` + <b>${appName}</b>`;
71
- await reporter.append(msg);
72
- } else {
73
- msg = ` ❌ <b>${appName}</b> yaml is invalid!\n${ status.msg }`;
74
- await reporter.append(msg);
75
- throw new Error(`FAIL: Deployment app: ${appName}`);
76
- }
77
-
78
- }
79
-
80
- } catch(error){
81
-
82
- // Деплой сфейлился - надо откатыватся
83
- if(Object.keys(deployedApps).length > 0){
84
-
85
- log.fail('FAIL: Deployments to rollout', Object.keys(deployedApps));
86
- await reporter.append(` 🚫 <b>APPLAY FAIL</b>: Rollout all deployed apps`);
87
-
88
- for (const appName of Object.keys(deployedApps)) {
89
- await k8sExec(`rollout undo deployment/${ appName }`, configFile, undefined, opts.namespace );
90
- }
91
-
92
- }
93
-
94
- throw error;
95
- }
96
-
97
- // deploy succsess, push changes to stage brunch
98
- if(opts.gitpush){
99
- await Git.setOrigin();
100
- await Git.push(stage);
101
- }
102
-
103
- // await reporter.append(` + Save ${ prj.stage} -> ${ prj.version} to GitLab`);
104
- await prj.saveGitLabStagesVersions();
105
-
106
- if(opts.changelog){
107
- await reporter.append(` ----------------\n+ Resolved tasks:${ release.tasks.length }, bugs: ${ release.bugs.length }`);
108
-
109
- const bugsDeployed = await Jira.moveBugsAsDeployed(release.bugs);
110
- if(bugsDeployed.length > 0){
111
- await reporter.append(` + Deployed ${ bugsDeployed.length } bugs.`);
112
- } else {
113
- // await reporter.append(` - Deploy bugs. [SKIPED]`);
114
- }
115
- }
116
-
117
- await reporter.succses(release);
118
-
119
- }
120
-
121
- // ========================================================
122
- // Command
123
-
124
- async function main() {
125
-
126
- program
127
- .option('--path <project dir>', 'Path to project directory [default - current]')
128
- .option('--stage <stage>', 'K8s node stage <stage>')
129
- .option('--namespace <namespace>', 'Deploy manifests to K8s specific namespace')
130
-
131
- .option('--no-changelog', 'Skip create gitlog')
132
- .option('--no-gitpush', 'Skip pushing to stage branch after deploy')
133
-
134
- .option('--debug', 'Output extra debugging info')
135
- .action( async () => { await Project.run( program.opts() || {}, runHandler ) });
136
-
137
- await program.parseAsync(process.argv);
138
- }
139
- main();
140
-
141
-
142
- // ================================================================================================
143
- // Helpers:
144
-
145
- export async function k8sApply( configFile: string, manifestFile: string, appName: string, namespace?: string): Promise<{valid: boolean; msg: string}>{
146
-
147
- let status: string;
148
-
149
- try{
150
- await k8sExec(`apply -f ${ manifestFile } --dry-run=server --validate`, configFile, undefined, namespace);
151
- status = (await Shell.exec(`cat ${ manifestFile } | grep 'image:'`)).replace('image: ', '').replace(ENV.CI_REGISTRY_IMAGE+'/', '').trim();
152
- }catch(e){
153
- log.error(e);
154
- log.fail('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
155
- log.fail(`VALIDATE FAIL: ${appName} at ${manifestFile}`);
156
- log.fail(e.message);
157
- log.fail(`${ await Shell.cat(manifestFile) }`);
158
- log.fail('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
159
-
160
- return { valid:false, msg: e.message };
161
- }
162
-
163
- try{
164
- status = await k8sExec(`apply -f ${ manifestFile }`, configFile, undefined, namespace);
165
- await k8sExec(`rollout status deploy/${ appName } --watch=true --timeout=300s`, configFile);
166
-
167
- log.info('+++++++++++++++++++++++++++++');
168
- log.info(`DEPLOYED: ${appName}`);
169
- log.info(await k8sExec(`get pods --no-headers -l app=${appName} -o wide`, configFile, { ignoreError:true, silent:true }), namespace);
170
- log.info('+++++++++++++++++++++++++++++');
171
-
172
- return { valid:true, msg: status };
173
-
174
- } catch(error){
175
- log.fail('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
176
- log.fail(`DEPLOY FAIL: ${appName}`);
177
- log.fail('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
178
- log.fail(await k8sExec(`get pods --no-headers -l app=${appName} -o wide`, configFile, { ignoreError:true, silent:true }), namespace);
179
- throw error;
180
- }
181
-
182
- }
183
-
184
- // ------------------------------
185
-
186
- export async function k8sStatus(configFile: string, stage: string, reporter: Reporter | undefined = undefined, appsOnly: boolean = true, namespace?: string): Promise<string[]>{
187
-
188
- const cmd = `get pods -o=jsonpath='{range .items[*]}{"\\n"}{"<b>"}{.metadata.labels.app}{"</b> "}{range .spec.containers[*]}{.image}{end}{" "}{.metadata.labels.pod-template-hash}{" "}{.status.phase}{end}'`;
189
- const statusArr = (await k8sExec(cmd, configFile, undefined, namespace)).split('\n');
190
-
191
- const result: string[] = [];
192
- for (const row of statusArr) {
193
- if(row.length < 4 || (appsOnly && (RegExp((ENV.CI_REGISTRY_IMAGE || '')).exec(row)) === null)){
194
- continue;
195
- }
196
- result.push(row.replace(ENV.CI_REGISTRY_IMAGE+'/', ''));
197
- }
198
-
199
- log.info(`K8S Node: ${ stage } [${ namespace || 'default' }]`, result);
200
-
201
- if(reporter){
202
- const msg = `\n<b>${ stage.toUpperCase() }</b> Pods:\n-----------------\n ` + result.join('\n-----------------\n ');
203
- await reporter.append(msg, '');
204
- }
205
-
206
- return result;
207
- }
208
-
209
-
210
- // ------------------------------
211
-
212
-
213
- export async function k8sExec( cmd: string, configFile: string, opt: ExecOptions | undefined = undefined, namespace?: string, cmdPrefix = ''): Promise<string>{
214
- return Shell.exec(`${cmdPrefix}kubectl --kubeconfig ${ configFile } ${ namespace ? `-n ${ namespace }` : '' } ${ cmd }`, opt);
215
- }