@docupace/pipeline 1.0.26-alpha-3 โ†’ 1.0.26

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.
@@ -4,10 +4,9 @@ import yaml from 'js-yaml';
4
4
  import { $ } from 'zx';
5
5
  import lodash from 'lodash';
6
6
  import { appInfo, environmentConfig, getRepUrl } from './config.js';
7
- import { copy } from './copy.js';
7
+ import { copy2 } from './copy.js';
8
8
  import axios from 'axios';
9
- import { runPipeline } from './pipline.js';
10
- import { deployCore } from './jenkins.js';
9
+ import { deployCore, deployConnect } from './jenkins.js';
11
10
 
12
11
  /**
13
12
  * Clone the repository, read the YAML file, and return the parsed data and file paths.
@@ -22,6 +21,80 @@ function getRepoLocalPath(env) {
22
21
  return { repoPath, repoName };
23
22
  }
24
23
 
24
+ async function updateAndCommitConnectVersion(
25
+ toEnv,
26
+ toSite,
27
+ getComparedVersion,
28
+ connectDeployPath,
29
+ ) {
30
+ const filePath = path.join(
31
+ connectDeployPath,
32
+ 'envs',
33
+ toEnv,
34
+ toSite,
35
+ 'dock.sh',
36
+ );
37
+
38
+ try {
39
+ let content = fs.readFileSync(filePath, 'utf8');
40
+ content = content.replace(
41
+ /IMAGE_VERSION=.*/,
42
+ `IMAGE_VERSION=git-commit-${getComparedVersion}`,
43
+ );
44
+ fs.writeFileSync(filePath, content, 'utf8');
45
+
46
+ // Git commands to stage, commit and push changes
47
+ await $`cd ${connectDeployPath} && git config user.email "gitops@docupace.com"`;
48
+ await $`cd ${connectDeployPath} && git add .`;
49
+ await $`cd ${connectDeployPath} && git commit -m "Update Connect version for ${toSite} in ${toEnv} to ${getComparedVersion}"`;
50
+ await $`cd ${connectDeployPath} && git push`;
51
+
52
+ console.log('๐Ÿงน Cleaning up...');
53
+ await $`rm -rf ${connectDeployPath}`;
54
+ console.log('โœ… Cleanup complete.');
55
+ } catch (error) {
56
+ console.error('Error updating Connect version:', error);
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ async function fetchLoginFooter(env, site, isNewUrl) {
62
+ const url = isNewUrl
63
+ ? environmentConfig[env].newUrl(site)
64
+ : environmentConfig[env].oldUrl(site);
65
+ const endpoint = isNewUrl ? '/connect/loginFooter' : '_ui/loginFooter';
66
+ return await axios.get(`${url}${endpoint}`);
67
+ }
68
+
69
+ async function compareGetConnectSiteVersions(fromEnv, fromSite, toEnv, toSite) {
70
+ try {
71
+ let fromResponse, toResponse;
72
+ try {
73
+ fromResponse = await fetchLoginFooter(fromEnv, fromSite, true);
74
+ } catch {
75
+ fromResponse = await fetchLoginFooter(fromEnv, fromSite, false);
76
+ }
77
+
78
+ try {
79
+ toResponse = await fetchLoginFooter(toEnv, toSite, true);
80
+ } catch {
81
+ toResponse = await fetchLoginFooter(toEnv, toSite, false);
82
+ }
83
+
84
+ const fromMatch = fromResponse.data.match(/UI Build: ([a-f0-9]*)/);
85
+ const toMatch = toResponse.data.match(/UI Build: ([a-f0-9]*)/);
86
+
87
+ if (fromMatch && fromMatch[1] && toMatch && toMatch[1]) {
88
+ if (fromMatch[1] !== toMatch[1]) {
89
+ return fromMatch[1];
90
+ }
91
+ }
92
+ return false;
93
+ } catch (error) {
94
+ console.error('Error fetching Connect versions:', error);
95
+ throw error;
96
+ }
97
+ }
25
98
  function getRepoLocalPathByRepoSlug(repoSlug) {
26
99
  const repoName = `${repoSlug}`;
27
100
  const repoPath = path.join(process.cwd(), repoName);
@@ -34,7 +107,7 @@ function cleanUpRepoPath(repoPath) {
34
107
  console.log(`${repoPath} directory exists. Removing it...`);
35
108
  fs.rmSync(repoPath, {
36
109
  recursive: true,
37
- force: true
110
+ force: true,
38
111
  });
39
112
  }
40
113
  }
@@ -53,7 +126,7 @@ export async function cloneRepository(repoSlug) {
53
126
  const { repoPath, repoName } = getRepoLocalPathByRepoSlug(repoSlug);
54
127
  const repoUrl = getRepUrl(repoName);
55
128
 
56
- await cleanUpRepoPath(repoPath)
129
+ await cleanUpRepoPath(repoPath);
57
130
 
58
131
  console.log(`Cloning repository: ${repoName}`);
59
132
  await $`git clone --depth 1 ${repoUrl}`;
@@ -71,6 +144,23 @@ function getConfigPath(application, env, site, name) {
71
144
  return { repoPath, deployConfigFilePath };
72
145
  }
73
146
 
147
+ function getAppTypesFromRepo(env, site) {
148
+ const { repoPath } = getRepoLocalPath(env);
149
+ const deployConfigFilePath = path.join(repoPath, `dp/${site}`);
150
+ const applications = [];
151
+ if (fs.existsSync(deployConfigFilePath)) {
152
+ const files = fs.readdirSync(deployConfigFilePath);
153
+ files.forEach((file) => {
154
+ if (file.includes('.values.yaml')) {
155
+ const appName = file.split('.')[0];
156
+ applications.push(appName);
157
+ }
158
+ });
159
+ }
160
+
161
+ return { applications };
162
+ }
163
+
74
164
  export async function readDeployConfig(application, env, site, name) {
75
165
  const { deployConfigFilePath } = getConfigPath(application, env, site, name);
76
166
 
@@ -266,12 +356,72 @@ async function getCoreBranchName(fromEnv, fromSite) {
266
356
  return branchName;
267
357
  }
268
358
 
359
+ export async function deployApp(
360
+ application,
361
+ appDetails,
362
+ fromEnv,
363
+ fromSite,
364
+ toEnv,
365
+ toSite,
366
+ ) {
367
+ let changesMade = false;
368
+ console.log(`Processing application: ${application}`);
369
+
370
+ // Read the deployment configuration for the source environment and site
371
+ const { deployConfig: fromDeployConfig } = await readDeployConfig(
372
+ application,
373
+ fromEnv,
374
+ fromSite,
375
+ 'values',
376
+ );
377
+
378
+ if (fromEnv !== toEnv) {
379
+ await copy2(
380
+ appDetails.repositoryName,
381
+ fromEnv,
382
+ toEnv,
383
+ fromDeployConfig.image.tag,
384
+ );
385
+ }
386
+
387
+ // Read the deployment configuration for the target environment and site
388
+ const {
389
+ deployConfig: toDeployConfig,
390
+ deployConfigFilePath: toDeployConfigFilePath,
391
+ } = await readDeployConfig(application, toEnv, toSite, 'values');
392
+
393
+ // Update target configuration using attributes specified in appInfo
394
+ for (const attribute of appDetails.deployAttributes) {
395
+ const sourceValue = lodash.get(fromDeployConfig, attribute);
396
+
397
+ if (sourceValue !== undefined) {
398
+ // Update the target configuration with the source value
399
+ lodash.set(toDeployConfig, attribute, sourceValue);
400
+ changesMade = true;
401
+ }
402
+ }
403
+
404
+ const {
405
+ deployConfig: toDeployAppConfig,
406
+ deployConfigFilePath: toDeployAppConfigFilePath,
407
+ } = await readDeployConfig(application, toEnv, toSite, 'app');
408
+
409
+ toDeployAppConfig.revision = fromDeployConfig.image.tag;
410
+
411
+ // Write updated configuration to the target YAML file
412
+ writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
413
+
414
+ writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
415
+ return changesMade;
416
+ }
417
+
269
418
  export async function deployFromSite(
270
419
  fromEnv,
271
420
  fromSite,
272
421
  toEnv,
273
422
  toSite,
274
423
  isDeployCore,
424
+ isDeployConnect,
275
425
  ) {
276
426
  if (isDeployCore === 'true') {
277
427
  const branchName = await getCoreBranchName(fromEnv, fromSite);
@@ -288,6 +438,30 @@ export async function deployFromSite(
288
438
  });*/
289
439
  }
290
440
 
441
+ if (isDeployConnect === 'true') {
442
+ const branch = '';
443
+ const getComparedVersion = await compareGetConnectSiteVersions(
444
+ fromEnv,
445
+ fromSite,
446
+ toEnv,
447
+ toSite,
448
+ );
449
+ if (getComparedVersion) {
450
+ console.log('Version to deploy is: ', getComparedVersion);
451
+ const connectDeployPath = await cloneRepository('connect-deploy');
452
+ // update target version set to git-commit-${getCommparedVersion}}
453
+ await updateAndCommitConnectVersion(
454
+ toEnv,
455
+ toSite,
456
+ getComparedVersion,
457
+ connectDeployPath,
458
+ );
459
+ // save and deploy
460
+ const runLiquibase = true;
461
+ await deployConnect(toEnv, toSite, runLiquibase, branch);
462
+ }
463
+ }
464
+
291
465
  // Clone repositories for source and target environments
292
466
  const fromRepoPath = await cloneEnvRepository(fromEnv);
293
467
  const toRepoPath =
@@ -295,52 +469,53 @@ export async function deployFromSite(
295
469
 
296
470
  // Initialize a flag to track changes
297
471
  let changesMade = false;
298
-
472
+ const applications = await getAppTypesFromRepo(toEnv, toSite);
473
+ console.log(applications);
299
474
  // Iterate over applications in appInfo
300
- for (const [application, appDetails] of Object.entries(appInfo)) {
301
- console.log(`Processing application: ${application}`);
302
475
 
303
- // Read the deployment configuration for the source environment and site
304
- const { deployConfig: fromDeployConfig } = await readDeployConfig(
305
- application,
306
- fromEnv,
476
+ let appInfoLocal = Object.fromEntries(
477
+ applications.applications
478
+ .filter((application) =>
479
+ Object.prototype.hasOwnProperty.call(appInfo, application),
480
+ )
481
+ .map((application) => [application, appInfo[application]]),
482
+ );
483
+
484
+ // let appInfoLocal = {};
485
+ // for (const application of applications.applications) {
486
+ // if (Object.prototype.hasOwnProperty.call(appInfo, application)) {
487
+ // appInfoLocal[application] = appInfo[application];
488
+ // }
489
+ // }
490
+
491
+ if (Object.keys(appInfoLocal).length === 0) {
492
+ const { deployConfig: toDeployAppConfig } = await readDeployConfig(
307
493
  fromSite,
308
- 'values',
494
+ toEnv,
495
+ toSite,
496
+ 'app',
309
497
  );
310
498
 
311
- if (fromEnv !== toEnv) {
312
- await copy(application, fromEnv, toEnv, fromDeployConfig.image.tag);
313
- }
314
-
315
- // Read the deployment configuration for the target environment and site
316
- const {
317
- deployConfig: toDeployConfig,
318
- deployConfigFilePath: toDeployConfigFilePath,
319
- } = await readDeployConfig(application, toEnv, toSite, 'values');
499
+ appInfoLocal[fromSite] = {
500
+ repositoryName: toDeployAppConfig.repo,
501
+ deployAttributes: ['image.tag'],
502
+ };
503
+ }
320
504
 
321
- // Update target configuration using attributes specified in appInfo
322
- for (const attribute of appDetails.deployAttributes) {
323
- const sourceValue = lodash.get(fromDeployConfig, attribute);
505
+ console.log('appinfoLocal', appInfoLocal);
324
506
 
325
- if (sourceValue !== undefined) {
326
- // Update the target configuration with the source value
327
- lodash.set(toDeployConfig, attribute, sourceValue);
328
- changesMade = true;
329
- }
507
+ for (const [application, appDetails] of Object.entries(appInfoLocal)) {
508
+ let cm = await deployApp(
509
+ application,
510
+ appDetails,
511
+ fromEnv,
512
+ fromSite,
513
+ toEnv,
514
+ toSite,
515
+ );
516
+ if (cm) {
517
+ changesMade = true;
330
518
  }
331
-
332
- const {
333
- deployConfig: toDeployAppConfig,
334
- deployConfigFilePath: toDeployAppConfigFilePath,
335
- } = await readDeployConfig(application, toEnv, toSite, 'app');
336
-
337
- toDeployAppConfig.revision = fromDeployConfig.image.tag;
338
-
339
- // Write updated configuration to the target YAML file
340
- writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
341
-
342
- writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
343
-
344
519
  }
345
520
 
346
521
  // Push all changes if any updates were made
@@ -1,122 +1,123 @@
1
- import axios from 'axios';
2
-
3
- const CORE_JENKINS_URL = 'http://devops.corp.docupace.com';
4
- const CORE_JOB_NAME = 'auto-deploy-site';
5
- const CORE_USERNAME = process.env.CORE_JENKINS_USERNAME;
6
- const CORE_API_TOKEN = process.env.CORE_JENKINS_API_TOKEN;
7
-
8
- const CONNECT_JENKINS_URL = 'http://usdoc-s-buildui.corp.docupace.com/jenkins';
9
- const CONNECT_JOB_NAME = 'docupace_build_and_run_site_v2';
10
- const CONNECT_USERNAME = process.env.CONNECT_JENKINS_USERNAME;
11
- const CONNECT_API_TOKEN = process.env.CONNECT_JENKINS_API_TOKEN;
12
-
13
- export async function deployCore(env, site, liquibase, gitBranch) {
14
- const parameters = {
15
- environment: env,
16
- site: site.toUpperCase(),
17
- runLiquibase: liquibase,
18
- gitBranch,
19
- };
20
-
21
- return callJenkinsJob({
22
- jenkinsUrl: CORE_JENKINS_URL,
23
- jobName: CORE_JOB_NAME,
24
- auth: { username: CORE_USERNAME, password: CORE_API_TOKEN },
25
- parameters,
26
- });
27
- }
28
-
29
- export async function deployConnect(env, site, liquibase, branch) {
30
- const parameters = {
31
- BUILD_ENVIRONMENT: env,
32
- BUILD_SITE: site,
33
- RUN_LIQUIBASE: liquibase,
34
- };
35
-
36
- return callJenkinsJob({
37
- jenkinsUrl: CONNECT_JENKINS_URL,
38
- jobName: CONNECT_JOB_NAME,
39
- auth: { username: CONNECT_USERNAME, password: CONNECT_API_TOKEN },
40
- parameters,
41
- });
42
- }
43
-
44
- export async function callJenkinsJob({
45
- jenkinsUrl,
46
- jobName,
47
- auth,
48
- parameters,
49
- }) {
50
- try {
51
- // Trigger the job
52
- console.log('Triggering Jenkins job...');
53
- const buildResponse = await axios.post(
54
- `${jenkinsUrl}/job/${jobName}/buildWithParameters`,
55
- new URLSearchParams(parameters),
56
- {
57
- auth,
58
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
59
- validateStatus: (status) => status === 201 || status === 302, // Accept 201 and 302 as valid statuses
60
- },
61
- );
62
-
63
- const queueUrl = buildResponse.headers.location;
64
- console.log(`Job queued at: ${queueUrl}`);
65
-
66
- // Poll queue for executable URL
67
- let jobUrl = null;
68
- while (!jobUrl) {
69
- const queueResponse = await axios.get(`${queueUrl}api/json`, {
70
- auth,
71
- });
72
- jobUrl = queueResponse.data.executable?.url || null;
73
-
74
- if (!jobUrl) {
75
- console.log('Waiting for job to start...');
76
- await new Promise((resolve) => setTimeout(resolve, 5000));
77
- }
78
- }
79
-
80
- jobUrl = jobUrl.replaceAll('https', 'http');
81
-
82
- console.log(`Job started at: ${jobUrl}`);
83
-
84
- // Stream console output
85
- const consoleUrl = `${jobUrl}consoleText`;
86
- let lastSize = 0;
87
-
88
- console.log('Streaming job output...');
89
- while (true) {
90
- const consoleResponse = await axios.get(consoleUrl, {
91
- auth,
92
- responseType: 'text',
93
- });
94
-
95
- const consoleText = consoleResponse.data;
96
- const newOutput = consoleText.slice(lastSize);
97
- process.stdout.write(newOutput); // Write new output to console
98
- lastSize = consoleText.length;
99
-
100
- // Check job status
101
- const jobResponse = await axios.get(`${jobUrl}api/json`, {
102
- auth,
103
- });
104
- const result = jobResponse.data.result;
105
-
106
- if (result) {
107
- console.log(`\nJob completed with result: ${result}`);
108
- if (result === 'SUCCESS') {
109
- return;
110
- } else {
111
- throw new Error('Jenkins job failed');
112
- }
113
- }
114
-
115
- // Wait before polling again
116
- await new Promise((resolve) => setTimeout(resolve, 5000));
117
- }
118
- } catch (error) {
119
- console.error('Error:', error.message);
120
- throw error;
121
- }
122
- }
1
+ import axios from 'axios';
2
+
3
+ const CORE_JENKINS_URL = 'http://devops.corp.docupace.com';
4
+ const CORE_JOB_NAME = 'auto-deploy-site';
5
+ const CORE_USERNAME = process.env.CORE_JENKINS_USERNAME;
6
+ const CORE_API_TOKEN = process.env.CORE_JENKINS_API_TOKEN;
7
+
8
+ const CONNECT_JENKINS_URL = 'http://usdoc-s-buildui.corp.docupace.com/jenkins';
9
+ const CONNECT_JOB_NAME = 'docupace_build_and_run_site_v2';
10
+ const CONNECT_USERNAME = process.env.CONNECT_JENKINS_USERNAME;
11
+ const CONNECT_API_TOKEN = process.env.CONNECT_JENKINS_API_TOKEN;
12
+
13
+ export async function deployCore(env, site, liquibase, gitBranch) {
14
+ const parameters = {
15
+ environment: env,
16
+ site: site.toUpperCase(),
17
+ runLiquibase: liquibase,
18
+ gitBranch,
19
+ };
20
+
21
+ return callJenkinsJob({
22
+ jenkinsUrl: CORE_JENKINS_URL,
23
+ jobName: CORE_JOB_NAME,
24
+ auth: { username: CORE_USERNAME, password: CORE_API_TOKEN },
25
+ parameters,
26
+ });
27
+ }
28
+
29
+ // eslint-disable-next-line no-unused-vars
30
+ export async function deployConnect(env, site, liquibase, branch) {
31
+ const parameters = {
32
+ BUILD_ENVIRONMENT: env,
33
+ BUILD_SITE: site,
34
+ RUN_LIQUIBASE: liquibase,
35
+ };
36
+
37
+ return callJenkinsJob({
38
+ jenkinsUrl: CONNECT_JENKINS_URL,
39
+ jobName: CONNECT_JOB_NAME,
40
+ auth: { username: CONNECT_USERNAME, password: CONNECT_API_TOKEN },
41
+ parameters,
42
+ });
43
+ }
44
+
45
+ export async function callJenkinsJob({
46
+ jenkinsUrl,
47
+ jobName,
48
+ auth,
49
+ parameters,
50
+ }) {
51
+ try {
52
+ // Trigger the job
53
+ console.log('Triggering Jenkins job...');
54
+ const buildResponse = await axios.post(
55
+ `${jenkinsUrl}/job/${jobName}/buildWithParameters`,
56
+ new URLSearchParams(parameters),
57
+ {
58
+ auth,
59
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
60
+ validateStatus: (status) => status === 201 || status === 302, // Accept 201 and 302 as valid statuses
61
+ },
62
+ );
63
+
64
+ const queueUrl = buildResponse.headers.location;
65
+ console.log(`Job queued at: ${queueUrl}`);
66
+
67
+ // Poll queue for executable URL
68
+ let jobUrl = null;
69
+ while (!jobUrl) {
70
+ const queueResponse = await axios.get(`${queueUrl}api/json`, {
71
+ auth,
72
+ });
73
+ jobUrl = queueResponse.data.executable?.url || null;
74
+
75
+ if (!jobUrl) {
76
+ console.log('Waiting for job to start...');
77
+ await new Promise((resolve) => setTimeout(resolve, 5000));
78
+ }
79
+ }
80
+
81
+ jobUrl = jobUrl.replaceAll('https', 'http');
82
+
83
+ console.log(`Job started at: ${jobUrl}`);
84
+
85
+ // Stream console output
86
+ const consoleUrl = `${jobUrl}consoleText`;
87
+ let lastSize = 0;
88
+
89
+ console.log('Streaming job output...');
90
+ while (true) {
91
+ const consoleResponse = await axios.get(consoleUrl, {
92
+ auth,
93
+ responseType: 'text',
94
+ });
95
+
96
+ const consoleText = consoleResponse.data;
97
+ const newOutput = consoleText.slice(lastSize);
98
+ process.stdout.write(newOutput); // Write new output to console
99
+ lastSize = consoleText.length;
100
+
101
+ // Check job status
102
+ const jobResponse = await axios.get(`${jobUrl}api/json`, {
103
+ auth,
104
+ });
105
+ const result = jobResponse.data.result;
106
+
107
+ if (result) {
108
+ console.log(`\nJob completed with result: ${result}`);
109
+ if (result === 'SUCCESS') {
110
+ return;
111
+ } else {
112
+ throw new Error('Jenkins job failed');
113
+ }
114
+ }
115
+
116
+ // Wait before polling again
117
+ await new Promise((resolve) => setTimeout(resolve, 5000));
118
+ }
119
+ } catch (error) {
120
+ console.error('Error:', error.message);
121
+ throw error;
122
+ }
123
+ }