@docupace/pipeline 1.0.77 → 1.0.79

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 (69) hide show
  1. package/.prettierrc.cjs +12 -12
  2. package/README.md +135 -135
  3. package/bin/azuread_update_callbacks_for_app.sh +58 -58
  4. package/bin/next-changeset-validate.sh +8 -8
  5. package/bitbucket-pipelines.yml +59 -59
  6. package/eslint.config.js +49 -49
  7. package/jest.config.js +15 -15
  8. package/next/changeset.sh +75 -75
  9. package/next/copy.sh +34 -34
  10. package/next/deploy.sh +40 -40
  11. package/next/execute.sh +123 -123
  12. package/next/sonar.sh +23 -23
  13. package/package.json +1 -1
  14. package/src/auth0/alias.js +79 -79
  15. package/src/auth0/alias.test.js +172 -172
  16. package/src/auth0/cli.js +82 -82
  17. package/src/auth0/configure.js +902 -902
  18. package/src/auth0/configure.test.js +167 -167
  19. package/src/auth0/export.js +48 -48
  20. package/src/auth0/export.test.js +103 -103
  21. package/src/auth0/metadata.js +141 -141
  22. package/src/auth0/metadata.test.js +186 -186
  23. package/src/auth0/vault.js +60 -60
  24. package/src/auth0/vault.test.js +134 -134
  25. package/src/changeset/changelog-wiki.js +328 -328
  26. package/src/changeset/changeset-config.js +34 -34
  27. package/src/changeset/cli.js +237 -237
  28. package/src/changeset/create.js +215 -215
  29. package/src/changeset/package-util.js +87 -87
  30. package/src/changeset/util.js +43 -43
  31. package/src/changeset/validate.js +91 -91
  32. package/src/changeset/version.js +102 -102
  33. package/src/jenkins/setParams.groovy +58 -58
  34. package/src/lib/merge-driver.js +204 -204
  35. package/src/merge/cli-merge-all.js +507 -507
  36. package/src/merge/cli-merge.js +492 -492
  37. package/src/merge/cli-resolve.js +399 -399
  38. package/src/merge/cli.js +38 -38
  39. package/src/merge/merge-changelog.js +87 -87
  40. package/src/merge/merge-package.js +106 -106
  41. package/src/merge/merge-types.js +82 -82
  42. package/src/merge/pr.js +231 -231
  43. package/src/micro/deploy-microservice.js +153 -0
  44. package/src/micro/{ms-publish.js → publish-ms.js} +8 -6
  45. package/src/next/alias.js +53 -53
  46. package/src/next/apache.js +81 -81
  47. package/src/next/cli.js +10 -1
  48. package/src/next/config.js +137 -137
  49. package/src/next/connect.js +226 -226
  50. package/src/next/copy.js +61 -61
  51. package/src/next/deploy.js +666 -633
  52. package/src/next/git-to-semver.js +79 -79
  53. package/src/next/haproxy.js +76 -76
  54. package/src/next/jenkins.js +123 -123
  55. package/src/next/jira.js +84 -84
  56. package/src/next/logger.js +17 -17
  57. package/src/next/pipline.js +114 -114
  58. package/src/next/publish.js +71 -71
  59. package/src/next/pull.js +7 -7
  60. package/src/next/sonar.js +23 -23
  61. package/src/next/test.js +5 -5
  62. package/src/next/util.js +95 -93
  63. package/src/next/vault.js +45 -45
  64. package/terraform/apply.sh +37 -37
  65. package/terraform/init.sh +72 -72
  66. package/terraform/plan.sh +76 -76
  67. package/terraform/validate.sh +17 -17
  68. package/src/micro/cli.js +0 -71
  69. package/src/micro/ms-deploy.js +0 -271
@@ -1,633 +1,666 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import yaml from 'js-yaml';
4
- import { $ } from 'zx';
5
- import lodash from 'lodash';
6
- import { appInfo, environmentConfig, getRepUrl } from './config.js';
7
- import { copy2 } from './copy.js';
8
- import axios from 'axios';
9
- import { deployConnect, deployCore } from './jenkins.js';
10
- import { gitDescribeToSemver } from './git-to-semver.js';
11
- import semver from 'semver';
12
-
13
- /**
14
- * Clone the repository, read the YAML file, and return the parsed data and file paths.
15
- * @param {string} application - The application name.
16
- * @param {string} site - The site name.
17
- * @returns {object} - The repository path, file path, and YAML data.
18
- */
19
-
20
- function getRepoLocalPath(env) {
21
- const repoName = `gitops-${env}`;
22
- const repoPath = path.join(process.cwd(), repoName);
23
- return { repoPath, repoName };
24
- }
25
-
26
- async function updateAndCommitConnectVersion(
27
- toEnv,
28
- toSite,
29
- getComparedVersion,
30
- connectDeployPath,
31
- ) {
32
- const connectToEnv = toEnv == 'qa' ? 'preprod' : toEnv;
33
- const filePath = path.join(
34
- connectDeployPath,
35
- 'envs',
36
- connectToEnv,
37
- toSite,
38
- 'dock.sh',
39
- );
40
-
41
- try {
42
- let content = fs.readFileSync(filePath, 'utf8');
43
- content = content.replace(
44
- /IMAGE_VERSION=.*/,
45
- `IMAGE_VERSION=git-commit-${getComparedVersion}`,
46
- );
47
- fs.writeFileSync(filePath, content, 'utf8');
48
-
49
- // Git commands to stage, commit and push changes
50
- await $`cd ${connectDeployPath} && git config user.email "gitops@docupace.com"`;
51
-
52
- if (process.env.BITBUCKET_STEP_TRIGGERER_UUID) {
53
- try {
54
- const response = await axios.get(
55
- `https://api.bitbucket.org/2.0/users/${process.env.BITBUCKET_STEP_TRIGGERER_UUID}`,
56
- );
57
- const triggererUsername = response.data.display_name;
58
- console.log(`📌 Run triggered by: ${triggererUsername}`);
59
- await $`cd ${connectDeployPath} && git config user.name ${triggererUsername}`;
60
- } catch (error) {
61
- console.warn('Failed to fetch Bitbucket user info:', error.message);
62
- }
63
- }
64
-
65
- await $`cd ${connectDeployPath} && git add .`;
66
- await $`cd ${connectDeployPath} && git commit -m "Update Connect version for ${toSite} in ${toEnv} to ${getComparedVersion}" || echo "No changes to commit"`;
67
- await $`cd ${connectDeployPath} && git push`;
68
-
69
- console.log('🧹 Cleaning up...');
70
- await $`rm -rf ${connectDeployPath}`;
71
- console.log('✅ Cleanup complete.');
72
- } catch (error) {
73
- console.error('Error updating Connect version:', error);
74
- throw error;
75
- }
76
- }
77
-
78
- async function fetchLoginFooter(env, site, isNewUrl) {
79
- const url = isNewUrl
80
- ? environmentConfig[env].newUrl(site)
81
- : environmentConfig[env].oldUrl(site);
82
- const endpoint = isNewUrl ? '/connect/loginFooter' : '_ui/loginFooter';
83
- return await axios.get(`${url}${endpoint}`);
84
- }
85
-
86
- async function compareGetConnectSiteVersions(fromEnv, fromSite, toEnv, toSite) {
87
- try {
88
- let fromResponse, toResponse;
89
- try {
90
- fromResponse = await fetchLoginFooter(fromEnv, fromSite, true);
91
- } catch {
92
- fromResponse = await fetchLoginFooter(fromEnv, fromSite, false);
93
- }
94
-
95
- try {
96
- toResponse = await fetchLoginFooter(toEnv, toSite, true);
97
- } catch {
98
- toResponse = await fetchLoginFooter(toEnv, toSite, false);
99
- }
100
-
101
- const fromMatch = fromResponse.data.match(/UI Build: ([a-f0-9]*)/);
102
- const toMatch = toResponse.data.match(/UI Build: ([a-f0-9]*)/);
103
-
104
- if (fromMatch && fromMatch[1] && toMatch && toMatch[1]) {
105
- if (fromMatch[1] !== toMatch[1]) {
106
- return fromMatch[1];
107
- }
108
- }
109
- return false;
110
- } catch (error) {
111
- console.error('Error fetching Connect versions:', error);
112
- throw error;
113
- }
114
- }
115
- function getRepoLocalPathByRepoSlug(repoSlug) {
116
- const repoName = `${repoSlug}`;
117
- const repoPath = path.join(process.cwd(), repoName);
118
- return { repoPath, repoName };
119
- }
120
-
121
- function cleanUpRepoPath(repoPath) {
122
- // Check and handle repo directory
123
- if (fs.existsSync(repoPath)) {
124
- console.log(`${repoPath} directory exists. Removing it...`);
125
- fs.rmSync(repoPath, {
126
- recursive: true,
127
- force: true,
128
- });
129
- }
130
- }
131
-
132
- export async function cloneEnvRepository(env) {
133
- const { repoPath, repoName } = getRepoLocalPath(env);
134
- const repoUrl = getRepUrl(repoName);
135
- console.log(`Cloning repository: ${repoName}`);
136
- await cleanUpRepoPath(repoPath);
137
- await $`git clone --depth 1 ${repoUrl}`;
138
- console.log(`Repository cloned to: ${repoPath}`);
139
- return repoPath;
140
- }
141
-
142
- export async function cloneRepository(repoSlug) {
143
- const { repoPath, repoName } = getRepoLocalPathByRepoSlug(repoSlug);
144
- const repoUrl = getRepUrl(repoName);
145
-
146
- await cleanUpRepoPath(repoPath);
147
-
148
- console.log(`Cloning repository: ${repoName}`);
149
- await $`git clone --depth 1 ${repoUrl}`;
150
- console.log(`Repository cloned to: ${repoPath}`);
151
- return repoPath;
152
- }
153
-
154
- function getConfigPath(application, env, site, name) {
155
- const { repoPath } = getRepoLocalPath(env);
156
- const deployConfigFilePath = path.join(
157
- repoPath,
158
- `dp/${site}/${application}.${name}.yaml`,
159
- );
160
-
161
- return { repoPath, deployConfigFilePath };
162
- }
163
-
164
- function getAppTypesFromRepo(env, site) {
165
- const { repoPath } = getRepoLocalPath(env);
166
- const deployConfigFilePath = path.join(repoPath, `dp/${site}`);
167
- const applications = [];
168
- if (fs.existsSync(deployConfigFilePath)) {
169
- const files = fs.readdirSync(deployConfigFilePath);
170
- files.forEach((file) => {
171
- if (file.includes('.values.yaml')) {
172
- const appName = file.split('.')[0];
173
- applications.push(appName);
174
- }
175
- });
176
- }
177
-
178
- return { applications };
179
- }
180
-
181
- export async function readDeployConfig(application, env, site, name) {
182
- const { deployConfigFilePath } = getConfigPath(application, env, site, name);
183
-
184
- let deployConfig;
185
- try {
186
- deployConfig = yaml.load(fs.readFileSync(deployConfigFilePath, 'utf8'));
187
- console.log(`YAML file loaded from: ${deployConfigFilePath}`);
188
- } catch (error) {
189
- console.error(`Error reading YAML file: ${error.message}`);
190
- process.exit(1);
191
- }
192
-
193
- return { deployConfig, deployConfigFilePath };
194
- }
195
-
196
- export async function cloneAndReadDeployConfig(application, env, site) {
197
- const repoPath = await cloneEnvRepository(env);
198
-
199
- const { deployConfig, deployConfigFilePath } = await readDeployConfig(
200
- application,
201
- env,
202
- site,
203
- 'values',
204
- );
205
-
206
- return { repoPath, deployConfigFilePath, deployConfig };
207
- }
208
-
209
- /**
210
- * Update the `tag` field in the YAML data.
211
- * @param {object} deployConfig - The parsed YAML data.
212
- * @param {string} tag - The new tag to set.
213
- * @returns {object} - The updated YAML data.
214
- */
215
- export function setTag(deployConfig, tag) {
216
- if (deployConfig.image && deployConfig.image.tag) {
217
- console.log(`Current image tag: ${deployConfig.image.tag}`);
218
- deployConfig.image.tag = tag;
219
- console.log(`Updated image tag to: ${deployConfig.image.tag}`);
220
- } else {
221
- console.error('No "image.tag" field found in the YAML file.');
222
- process.exit(1);
223
- }
224
- return deployConfig;
225
- }
226
-
227
- /**
228
- * Write the updated YAML data back to the file, commit the changes, and push to the remote branch.
229
- * @param {string} repoPath - The path to the cloned repository.
230
- * @param {string} deployConfigFilePath - The path to the YAML file.
231
- * @param {object} deployConfig - The updated YAML data.
232
- */
233
-
234
- export async function pushDeployConfig(repoPath, commitMessage) {
235
- console.log('📌 Checking for updates to commit...');
236
- // Check if there are any changes to commit
237
- const status = (
238
- await $`cd ${repoPath} && git status --porcelain`
239
- ).stdout.trim();
240
- if (!status) {
241
- console.log('✅ No changes to commit. Exiting.');
242
- await $`rm -rf ${repoPath}`;
243
- console.log('🧹 Cleanup complete.');
244
- return;
245
- }
246
-
247
- console.log('📌 Committing changes to the repository...');
248
- await $`cd ${repoPath} && git config user.email "gitops@docupace.com"`;
249
-
250
- if (process.env.BITBUCKET_STEP_TRIGGERER_UUID) {
251
- try {
252
- const response = await axios.get(
253
- `https://api.bitbucket.org/2.0/users/${process.env.BITBUCKET_STEP_TRIGGERER_UUID}`,
254
- );
255
- const triggererUsername = response.data.display_name;
256
- console.log(`📌 Run triggered by: ${triggererUsername}`);
257
- await $`cd ${repoPath} && git config user.name ${triggererUsername}`;
258
- } catch (error) {
259
- console.warn('Failed to fetch Bitbucket user info:', error.message);
260
- }
261
- }
262
-
263
- await $`cd ${repoPath} && git add .`;
264
- await $`cd ${repoPath} && git commit -m ${commitMessage}`;
265
- await $`cd ${repoPath} && git push`;
266
- console.log('✅ Changes pushed to the remote repository.');
267
-
268
- console.log('🧹 Cleaning up...');
269
- await $`rm -rf ${repoPath}`;
270
- console.log('✅ Cleanup complete.');
271
- }
272
-
273
- function writeDeployConfig(deployConfigFilePath, deployConfig) {
274
- try {
275
- fs.writeFileSync(deployConfigFilePath, yaml.dump(deployConfig), 'utf8');
276
- console.log(`✅ Updated YAML file saved at: ${deployConfigFilePath}`);
277
- } catch (error) {
278
- console.error(`❌ Error writing YAML file: ${error.message}`);
279
- process.exit(1);
280
- }
281
- }
282
-
283
- export async function writeAndPushDeployConfig(
284
- repoPath,
285
- deployConfigFilePath,
286
- deployConfig,
287
- commitMessage,
288
- ) {
289
- writeDeployConfig(deployConfigFilePath, deployConfig);
290
- await pushDeployConfig(repoPath, commitMessage);
291
- }
292
-
293
- /**
294
- * Main deploy function that orchestrates reading, updating, and writing the deployment config.
295
- * @param {string} application - The application name.
296
- * @param {string} site - The site name.
297
- */
298
-
299
- export async function getDeployTag(prefix = 'v') {
300
- const gitTag = (
301
- await $`git tag --points-at HEAD | grep '^${prefix}' | head -n 1`
302
- ).stdout.trim();
303
- const deployTag = gitTag || `build-${process.env.BITBUCKET_BUILD_NUMBER}`;
304
- return deployTag;
305
- }
306
-
307
- function getDeploymentTargets(tag, config) {
308
- const semverRegex = /^v?(\d+)\.(\d+)\.(\d+)(?:-([\w.\-]+))?$/;
309
- const match = tag.match(semverRegex);
310
-
311
- if (!match) {
312
- throw new Error(`Invalid tag format: ${tag}`);
313
- }
314
-
315
- const [_, major, minor, patch, preRelease] = match;
316
- const isAlpha = preRelease && preRelease.toLowerCase().includes('alpha');
317
-
318
- return config
319
- .filter((entry) => {
320
- if (entry.version.toLowerCase() === 'alpha' && isAlpha) {
321
- return true;
322
- }
323
-
324
- const [entryMajor, entryMinor] = entry.version.split('.');
325
- return (
326
- !isAlpha &&
327
- parseInt(entryMajor) === parseInt(major) &&
328
- parseInt(entryMinor) === parseInt(minor)
329
- );
330
- })
331
- .map((entry) => ({ env: entry.env, site: entry.site }));
332
- }
333
-
334
- export async function getDefaultDeploySite(deployTag) {
335
- const repPath = await cloneEnvRepository('dev');
336
- const autoDeployConfigPath = path.resolve(repPath, 'auto-deploy.yaml');
337
- const autoDeployConfig = yaml.load(
338
- fs.readFileSync(autoDeployConfigPath, 'utf8'),
339
- );
340
- return getDeploymentTargets(deployTag, autoDeployConfig);
341
- }
342
-
343
- export async function deployTemplate(
344
- env,
345
- site,
346
- deployTag,
347
- templateType = 'standardConfigRemoteBranch',
348
- ) {
349
- console.log(`Deploying standard template for site: ${site}`);
350
- console.log(`Using deployment tag: ${deployTag}`);
351
- const { repoPath, deployConfigFilePath, deployConfig } =
352
- await cloneAndReadDeployConfig('graphql-server', env, site);
353
-
354
- deployConfig.graphqlServer[templateType] = deployTag;
355
-
356
- await writeAndPushDeployConfig(
357
- repoPath,
358
- deployConfigFilePath,
359
- deployConfig,
360
- `Update template to ${deployConfig.graphqlServer.standardConfigRemoteBranch}`,
361
- );
362
- console.log('Deployment completed successfully.');
363
- }
364
-
365
- export async function deployAppWithTag(application, env, site, deployTag) {
366
- console.log(`Deploying application: ${application} for site: ${site}`);
367
- console.log(`Using deployment tag: ${deployTag}`);
368
- const appDetails = appInfo[application];
369
-
370
- if (env !== 'dev') {
371
- await copy2(
372
- appDetails.repositoryName,
373
- 'dev',
374
- env,
375
- deployTag,
376
- appDetails.imageName,
377
- );
378
- }
379
-
380
- const { repoPath, deployConfigFilePath, deployConfig } =
381
- await cloneAndReadDeployConfig(application, env, site);
382
- const updatedConfigData = setTag(deployConfig, deployTag);
383
-
384
- const {
385
- deployConfig: toDeployAppConfig,
386
- deployConfigFilePath: toDeployAppConfigFilePath,
387
- } = await readDeployConfig(application, env, site, 'app');
388
-
389
- toDeployAppConfig.revision = appDetails.getAppRevision
390
- ? appDetails.getAppRevision(updatedConfigData)
391
- : updatedConfigData.image.tag;
392
-
393
- writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
394
-
395
- await writeAndPushDeployConfig(
396
- repoPath,
397
- deployConfigFilePath,
398
- updatedConfigData,
399
- `Update image tag to ${deployConfig.image.tag}`,
400
- );
401
-
402
- console.log('Deployment completed successfully.');
403
- }
404
-
405
- async function getAppInfo(coreUrl) {
406
- const url = `${coreUrl}/b2b/rest/v1/appinfo`;
407
- return await axios.get(url);
408
- }
409
-
410
- export function getBranchNameFromVersion(version) {
411
- let parsed;
412
- try {
413
- const semverVersion = gitDescribeToSemver(version);
414
- parsed = semver.parse(semverVersion);
415
- let newVersion;
416
- if (parsed.prerelease.length > 0) {
417
- newVersion = parsed.build[1].slice(1);
418
- } else if (parsed.build[0] != '0') {
419
- console.error(`❌ This version is a custom branch. Deployment canceled.`);
420
- process.exit(1);
421
- } else {
422
- newVersion = 'v' + parsed.version;
423
- }
424
- console.log(`Extracted version ${newVersion} from ${version}`);
425
- return newVersion;
426
- } catch {
427
- console.log(`Error parsing version : ${version}`);
428
- console.log('Parsed Object:');
429
- console.log(parsed);
430
- }
431
- }
432
-
433
- async function getCoreBranchName(fromEnv, fromSite) {
434
- const envConfig = environmentConfig[fromEnv];
435
- const oldUrl = envConfig.oldUrl(fromSite);
436
- const newUrl = envConfig.newUrl(fromSite) + '/core';
437
-
438
- let appinfo;
439
- try {
440
- appinfo = await getAppInfo(oldUrl);
441
- } catch {
442
- try {
443
- appinfo = await getAppInfo(newUrl);
444
- } catch {
445
- throw new Error('Cannot get app info');
446
- }
447
- }
448
-
449
- const coreVersion = appinfo.data.core.version;
450
- const branchName = getBranchNameFromVersion(coreVersion);
451
- return branchName;
452
- }
453
-
454
- export async function deployApp(
455
- application,
456
- appDetails,
457
- fromEnv,
458
- fromSite,
459
- toEnv,
460
- toSite,
461
- ) {
462
- let changesMade = false;
463
- console.log(`Processing application: ${application}`);
464
-
465
- // Read the deployment configuration for the source environment and site
466
- const { deployConfig: fromDeployConfig } = await readDeployConfig(
467
- application,
468
- fromEnv,
469
- fromSite,
470
- 'values',
471
- );
472
-
473
- if (fromEnv !== toEnv) {
474
- await copy2(
475
- appDetails.repositoryName,
476
- fromEnv,
477
- toEnv,
478
- fromDeployConfig.image.tag,
479
- appDetails.imageName,
480
- );
481
- }
482
-
483
- // Read the deployment configuration for the target environment and site
484
- const {
485
- deployConfig: toDeployConfig,
486
- deployConfigFilePath: toDeployConfigFilePath,
487
- } = await readDeployConfig(application, toEnv, toSite, 'values');
488
-
489
- // Update target configuration using attributes specified in appInfo
490
- for (const attribute of appDetails.deployAttributes) {
491
- const sourceValue = lodash.get(fromDeployConfig, attribute);
492
-
493
- if (sourceValue !== undefined) {
494
- // Update the target configuration with the source value
495
- lodash.set(toDeployConfig, attribute, sourceValue);
496
- changesMade = true;
497
- }
498
- }
499
-
500
- if (appDetails.updateConfigFunction) {
501
- appDetails.updateConfigFunction(toDeployConfig);
502
- }
503
-
504
- const {
505
- deployConfig: toDeployAppConfig,
506
- deployConfigFilePath: toDeployAppConfigFilePath,
507
- } = await readDeployConfig(application, toEnv, toSite, 'app');
508
-
509
- toDeployAppConfig.revision = appDetails.getAppRevision
510
- ? appDetails.getAppRevision(fromDeployConfig)
511
- : fromDeployConfig.image.tag;
512
-
513
- // Write updated configuration to the target YAML file
514
- writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
515
-
516
- writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
517
- return changesMade;
518
- }
519
-
520
- export async function deployFromSite(
521
- fromEnv,
522
- fromSite,
523
- toEnv,
524
- toSite,
525
- isDeployCore,
526
- isDeployConnect,
527
- ) {
528
- if (isDeployCore === 'true') {
529
- const branchName = await getCoreBranchName(fromEnv, fromSite);
530
-
531
- const coreEnv = toEnv == 'qa' ? 'preprod' : toEnv;
532
- await deployCore(coreEnv, toSite, 'true', branchName);
533
-
534
- // To be added back when splitting between on prem and in cloud
535
- /*await runPipeline('site-deploy', 'deploy-core', 'main', {
536
- ENV: toEnv,
537
- SITE: toSite,
538
- LIQUIBASE: 'true',
539
- BRANCH: branchName,
540
- });*/
541
- }
542
-
543
- if (isDeployConnect === 'true') {
544
- const branch = '';
545
- const getComparedVersion = await compareGetConnectSiteVersions(
546
- fromEnv,
547
- fromSite,
548
- toEnv,
549
- toSite,
550
- );
551
- if (getComparedVersion) {
552
- console.log('Version to deploy is: ', getComparedVersion);
553
- const connectDeployPath = await cloneRepository('connect-deploy');
554
- // update target version set to git-commit-${getCommparedVersion}}
555
- await updateAndCommitConnectVersion(
556
- toEnv,
557
- toSite,
558
- getComparedVersion,
559
- connectDeployPath,
560
- );
561
- // save and deploy
562
- const runLiquibase = true;
563
- await deployConnect(toEnv, toSite, runLiquibase, branch);
564
- }
565
- }
566
-
567
- // Clone repositories for source and target environments
568
- const fromRepoPath = await cloneEnvRepository(fromEnv);
569
- const toRepoPath =
570
- toEnv === fromEnv ? fromRepoPath : await cloneEnvRepository(toEnv);
571
-
572
- // Initialize a flag to track changes
573
- let changesMade = false;
574
- const applications = await getAppTypesFromRepo(toEnv, toSite);
575
- console.log(applications);
576
- // Iterate over applications in appInfo
577
-
578
- let appInfoLocal = Object.fromEntries(
579
- applications.applications
580
- .filter((application) =>
581
- Object.prototype.hasOwnProperty.call(appInfo, application),
582
- )
583
- .map((application) => [application, appInfo[application]]),
584
- );
585
-
586
- // let appInfoLocal = {};
587
- // for (const application of applications.applications) {
588
- // if (Object.prototype.hasOwnProperty.call(appInfo, application)) {
589
- // appInfoLocal[application] = appInfo[application];
590
- // }
591
- // }
592
-
593
- if (Object.keys(appInfoLocal).length === 0) {
594
- const { deployConfig: toDeployAppConfig } = await readDeployConfig(
595
- fromSite,
596
- toEnv,
597
- toSite,
598
- 'app',
599
- );
600
-
601
- appInfoLocal[fromSite] = {
602
- repositoryName: toDeployAppConfig.repo,
603
- deployAttributes: ['image.tag'],
604
- };
605
- }
606
-
607
- console.log('appinfoLocal', appInfoLocal);
608
-
609
- for (const [application, appDetails] of Object.entries(appInfoLocal)) {
610
- let cm = await deployApp(
611
- application,
612
- appDetails,
613
- fromEnv,
614
- fromSite,
615
- toEnv,
616
- toSite,
617
- );
618
- if (cm) {
619
- changesMade = true;
620
- }
621
- }
622
-
623
- // Push all changes if any updates were made
624
- if (changesMade) {
625
- await pushDeployConfig(
626
- toRepoPath,
627
- `Updated deployment configurations from ${fromEnv}/${fromSite} to ${toEnv}/${toSite}`,
628
- );
629
- console.log('✅ All configurations updated and pushed successfully.');
630
- } else {
631
- console.log('✅ No changes detected. Nothing to push.');
632
- }
633
- }
1
+ import axios from 'axios';
2
+ import fs from 'fs';
3
+ import yaml from 'js-yaml';
4
+ import lodash from 'lodash';
5
+ import path from 'path';
6
+ import semver from 'semver';
7
+ import { $ } from 'zx';
8
+ import { appInfo, environmentConfig, getRepUrl } from './config.js';
9
+ import { copy2 } from './copy.js';
10
+ import { gitDescribeToSemver } from './git-to-semver.js';
11
+ import { deployConnect, deployCore } from './jenkins.js';
12
+
13
+ /**
14
+ * Clone the repository, read the YAML file, and return the parsed data and file paths.
15
+ * @param {string} application - The application name.
16
+ * @param {string} site - The site name.
17
+ * @returns {object} - The repository path, file path, and YAML data.
18
+ */
19
+
20
+ function getRepoLocalPath(env) {
21
+ const repoName = `gitops-${env}`;
22
+ const repoPath = path.join(process.cwd(), repoName);
23
+ return { repoPath, repoName };
24
+ }
25
+
26
+ async function updateAndCommitConnectVersion(
27
+ toEnv,
28
+ toSite,
29
+ getComparedVersion,
30
+ connectDeployPath,
31
+ ) {
32
+ const connectToEnv = toEnv == 'qa' ? 'preprod' : toEnv;
33
+ const filePath = path.join(
34
+ connectDeployPath,
35
+ 'envs',
36
+ connectToEnv,
37
+ toSite,
38
+ 'dock.sh',
39
+ );
40
+
41
+ try {
42
+ let content = fs.readFileSync(filePath, 'utf8');
43
+ content = content.replace(
44
+ /IMAGE_VERSION=.*/,
45
+ `IMAGE_VERSION=git-commit-${getComparedVersion}`,
46
+ );
47
+ fs.writeFileSync(filePath, content, 'utf8');
48
+
49
+ // Git commands to stage, commit and push changes
50
+ await $`cd ${connectDeployPath} && git config user.email "gitops@docupace.com"`;
51
+
52
+ if (process.env.BITBUCKET_STEP_TRIGGERER_UUID) {
53
+ try {
54
+ const response = await axios.get(
55
+ `https://api.bitbucket.org/2.0/users/${process.env.BITBUCKET_STEP_TRIGGERER_UUID}`,
56
+ );
57
+ const triggererUsername = response.data.display_name;
58
+ console.log(`📌 Run triggered by: ${triggererUsername}`);
59
+ await $`cd ${connectDeployPath} && git config user.name ${triggererUsername}`;
60
+ } catch (error) {
61
+ console.warn('Failed to fetch Bitbucket user info:', error.message);
62
+ }
63
+ }
64
+
65
+ await $`cd ${connectDeployPath} && git add .`;
66
+ await $`cd ${connectDeployPath} && git commit -m "Update Connect version for ${toSite} in ${toEnv} to ${getComparedVersion}" || echo "No changes to commit"`;
67
+ await $`cd ${connectDeployPath} && git push`;
68
+
69
+ console.log('🧹 Cleaning up...');
70
+ await $`rm -rf ${connectDeployPath}`;
71
+ console.log('✅ Cleanup complete.');
72
+ } catch (error) {
73
+ console.error('Error updating Connect version:', error);
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ async function fetchLoginFooter(env, site, isNewUrl) {
79
+ const url = isNewUrl
80
+ ? environmentConfig[env].newUrl(site)
81
+ : environmentConfig[env].oldUrl(site);
82
+ const endpoint = isNewUrl ? '/connect/loginFooter' : '_ui/loginFooter';
83
+ return await axios.get(`${url}${endpoint}`);
84
+ }
85
+
86
+ async function compareGetConnectSiteVersions(fromEnv, fromSite, toEnv, toSite) {
87
+ try {
88
+ let fromResponse, toResponse;
89
+ try {
90
+ fromResponse = await fetchLoginFooter(fromEnv, fromSite, true);
91
+ } catch {
92
+ fromResponse = await fetchLoginFooter(fromEnv, fromSite, false);
93
+ }
94
+
95
+ try {
96
+ toResponse = await fetchLoginFooter(toEnv, toSite, true);
97
+ } catch {
98
+ toResponse = await fetchLoginFooter(toEnv, toSite, false);
99
+ }
100
+
101
+ const fromMatch = fromResponse.data.match(/UI Build: ([a-f0-9]*)/);
102
+ const toMatch = toResponse.data.match(/UI Build: ([a-f0-9]*)/);
103
+
104
+ if (fromMatch && fromMatch[1] && toMatch && toMatch[1]) {
105
+ if (fromMatch[1] !== toMatch[1]) {
106
+ return fromMatch[1];
107
+ }
108
+ }
109
+ return false;
110
+ } catch (error) {
111
+ console.error('Error fetching Connect versions:', error);
112
+ throw error;
113
+ }
114
+ }
115
+ function getRepoLocalPathByRepoSlug(repoSlug) {
116
+ const repoName = `${repoSlug}`;
117
+ const repoPath = path.join(process.cwd(), repoName);
118
+ return { repoPath, repoName };
119
+ }
120
+
121
+ function cleanUpRepoPath(repoPath) {
122
+ // Check and handle repo directory
123
+ if (fs.existsSync(repoPath)) {
124
+ console.log(`${repoPath} directory exists. Removing it...`);
125
+ fs.rmSync(repoPath, {
126
+ recursive: true,
127
+ force: true,
128
+ });
129
+ }
130
+ }
131
+
132
+ export async function cloneEnvRepository(env) {
133
+ const { repoPath, repoName } = getRepoLocalPath(env);
134
+ const repoUrl = getRepUrl(repoName);
135
+ console.log(`Cloning repository: ${repoName}`);
136
+ await cleanUpRepoPath(repoPath);
137
+ await $`git clone --depth 1 ${repoUrl}`;
138
+ console.log(`Repository cloned to: ${repoPath}`);
139
+ return repoPath;
140
+ }
141
+
142
+ export async function cloneRepository(repoSlug) {
143
+ const { repoPath, repoName } = getRepoLocalPathByRepoSlug(repoSlug);
144
+ const repoUrl = getRepUrl(repoName);
145
+
146
+ await cleanUpRepoPath(repoPath);
147
+
148
+ console.log(`Cloning repository: ${repoName}`);
149
+ await $`git clone --depth 1 ${repoUrl}`;
150
+ console.log(`Repository cloned to: ${repoPath}`);
151
+ return repoPath;
152
+ }
153
+
154
+ function getConfigPath(application, env, site, name) {
155
+ const { repoPath } = getRepoLocalPath(env);
156
+ const deployConfigFilePath = path.join(
157
+ repoPath,
158
+ `dp/${site}/${application}.${name}.yaml`,
159
+ );
160
+
161
+ return { repoPath, deployConfigFilePath };
162
+ }
163
+
164
+ function getAppTypesFromRepo(env, site) {
165
+ const { repoPath } = getRepoLocalPath(env);
166
+ const deployConfigFilePath = path.join(repoPath, `dp/${site}`);
167
+ const applications = [];
168
+ if (fs.existsSync(deployConfigFilePath)) {
169
+ const files = fs.readdirSync(deployConfigFilePath);
170
+ files.forEach((file) => {
171
+ if (file.includes('.values.yaml')) {
172
+ const appName = file.split('.')[0];
173
+ applications.push(appName);
174
+ }
175
+ });
176
+ }
177
+
178
+ return { applications };
179
+ }
180
+
181
+ export async function readDeployConfig(application, env, site, name) {
182
+ const { deployConfigFilePath } = getConfigPath(application, env, site, name);
183
+
184
+ let deployConfig;
185
+ try {
186
+ deployConfig = yaml.load(fs.readFileSync(deployConfigFilePath, 'utf8'));
187
+ console.log(`YAML file loaded from: ${deployConfigFilePath}`);
188
+ } catch (error) {
189
+ console.error(`Error reading YAML file: ${error.message}`);
190
+ process.exit(1);
191
+ }
192
+
193
+ return { deployConfig, deployConfigFilePath };
194
+ }
195
+
196
+ export async function cloneAndReadDeployConfig(application, env, site) {
197
+ const repoPath = await cloneEnvRepository(env);
198
+
199
+ const { deployConfig, deployConfigFilePath } = await readDeployConfig(
200
+ application,
201
+ env,
202
+ site,
203
+ 'values',
204
+ );
205
+
206
+ return { repoPath, deployConfigFilePath, deployConfig };
207
+ }
208
+
209
+ /**
210
+ * Update the `tag` field in the YAML data.
211
+ * @param {object} deployConfig - The parsed YAML data.
212
+ * @param {string} tag - The new tag to set.
213
+ * @returns {object} - The updated YAML data.
214
+ */
215
+ export function setTag(deployConfig, tag) {
216
+ if (deployConfig.image && deployConfig.image.tag) {
217
+ console.log(`Current image tag: ${deployConfig.image.tag}`);
218
+ deployConfig.image.tag = tag;
219
+ console.log(`Updated image tag to: ${deployConfig.image.tag}`);
220
+ } else {
221
+ console.error('No "image.tag" field found in the YAML file.');
222
+ process.exit(1);
223
+ }
224
+ return deployConfig;
225
+ }
226
+
227
+ /**
228
+ * Write the updated YAML data back to the file, commit the changes, and push to the remote branch.
229
+ * @param {string} repoPath - The path to the cloned repository.
230
+ * @param {string} deployConfigFilePath - The path to the YAML file.
231
+ * @param {object} deployConfig - The updated YAML data.
232
+ */
233
+
234
+ export async function pushDeployConfig(repoPath, commitMessage) {
235
+ console.log('📌 Checking for updates to commit...');
236
+ // Check if there are any changes to commit
237
+ const status = (
238
+ await $`cd ${repoPath} && git status --porcelain`
239
+ ).stdout.trim();
240
+ if (!status) {
241
+ console.log('✅ No changes to commit. Exiting.');
242
+ await $`rm -rf ${repoPath}`;
243
+ console.log('🧹 Cleanup complete.');
244
+ return;
245
+ }
246
+
247
+ console.log('📌 Committing changes to the repository...');
248
+ await $`cd ${repoPath} && git config user.email "gitops@docupace.com"`;
249
+
250
+ if (process.env.BITBUCKET_STEP_TRIGGERER_UUID) {
251
+ try {
252
+ const response = await axios.get(
253
+ `https://api.bitbucket.org/2.0/users/${process.env.BITBUCKET_STEP_TRIGGERER_UUID}`,
254
+ );
255
+ const triggererUsername = response.data.display_name;
256
+ console.log(`📌 Run triggered by: ${triggererUsername}`);
257
+ await $`cd ${repoPath} && git config user.name ${triggererUsername}`;
258
+ } catch (error) {
259
+ console.warn('Failed to fetch Bitbucket user info:', error.message);
260
+ }
261
+ }
262
+
263
+ await $`cd ${repoPath} && git add .`;
264
+ await $`cd ${repoPath} && git commit -m ${commitMessage}`;
265
+ await $`cd ${repoPath} && git push`;
266
+ console.log('✅ Changes pushed to the remote repository.');
267
+
268
+ console.log('🧹 Cleaning up...');
269
+ await $`rm -rf ${repoPath}`;
270
+ console.log('✅ Cleanup complete.');
271
+ }
272
+
273
+ function writeDeployConfig(deployConfigFilePath, deployConfig) {
274
+ try {
275
+ fs.writeFileSync(deployConfigFilePath, yaml.dump(deployConfig), 'utf8');
276
+ console.log(`✅ Updated YAML file saved at: ${deployConfigFilePath}`);
277
+ } catch (error) {
278
+ console.error(`❌ Error writing YAML file: ${error.message}`);
279
+ process.exit(1);
280
+ }
281
+ }
282
+
283
+ export async function writeAndPushDeployConfig(
284
+ repoPath,
285
+ deployConfigFilePath,
286
+ deployConfig,
287
+ commitMessage,
288
+ ) {
289
+ writeDeployConfig(deployConfigFilePath, deployConfig);
290
+ await pushDeployConfig(repoPath, commitMessage);
291
+ }
292
+
293
+ /**
294
+ * Main deploy function that orchestrates reading, updating, and writing the deployment config.
295
+ * @param {string} application - The application name.
296
+ * @param {string} site - The site name.
297
+ */
298
+
299
+ export async function getDeployTag(prefix = 'v') {
300
+ const gitTag = (
301
+ await $`git tag --points-at HEAD | grep '^${prefix}' | head -n 1`
302
+ ).stdout.trim();
303
+ const deployTag = gitTag || `build-${process.env.BITBUCKET_BUILD_NUMBER}`;
304
+ return deployTag;
305
+ }
306
+
307
+ function getDeploymentTargets(tag, config) {
308
+ const semverRegex = /^v?(\d+)\.(\d+)\.(\d+)(?:-([\w.\-]+))?$/;
309
+ const match = tag.match(semverRegex);
310
+
311
+ if (!match) {
312
+ throw new Error(`Invalid tag format: ${tag}`);
313
+ }
314
+
315
+ const [_, major, minor, patch, preRelease] = match;
316
+ const isAlpha = preRelease && preRelease.toLowerCase().includes('alpha');
317
+
318
+ return config
319
+ .filter((entry) => {
320
+ if (entry.version.toLowerCase() === 'alpha' && isAlpha) {
321
+ return true;
322
+ }
323
+
324
+ const [entryMajor, entryMinor] = entry.version.split('.');
325
+ return (
326
+ !isAlpha &&
327
+ parseInt(entryMajor) === parseInt(major) &&
328
+ parseInt(entryMinor) === parseInt(minor)
329
+ );
330
+ })
331
+ .map((entry) => ({ env: entry.env, site: entry.site }));
332
+ }
333
+
334
+ export async function getDefaultDeploySite(deployTag) {
335
+ const repPath = await cloneEnvRepository('dev');
336
+ const autoDeployConfigPath = path.resolve(repPath, 'auto-deploy.yaml');
337
+ const autoDeployConfig = yaml.load(
338
+ fs.readFileSync(autoDeployConfigPath, 'utf8'),
339
+ );
340
+ return getDeploymentTargets(deployTag, autoDeployConfig);
341
+ }
342
+
343
+ export async function deployTemplate(
344
+ env,
345
+ site,
346
+ deployTag,
347
+ templateType = 'standardConfigRemoteBranch',
348
+ ) {
349
+ console.log(`Deploying standard template for site: ${site}`);
350
+ console.log(`Using deployment tag: ${deployTag}`);
351
+ const { repoPath, deployConfigFilePath, deployConfig } =
352
+ await cloneAndReadDeployConfig('graphql-server', env, site);
353
+
354
+ deployConfig.graphqlServer[templateType] = deployTag;
355
+
356
+ await writeAndPushDeployConfig(
357
+ repoPath,
358
+ deployConfigFilePath,
359
+ deployConfig,
360
+ `Update template to ${deployConfig.graphqlServer.standardConfigRemoteBranch}`,
361
+ );
362
+ console.log('Deployment completed successfully.');
363
+ }
364
+
365
+ export async function deployAppWithTag(application, env, site, deployTag) {
366
+ console.log(`Deploying application: ${application} for site: ${site}`);
367
+ console.log(`Using deployment tag: ${deployTag}`);
368
+ const appDetails = appInfo[application];
369
+
370
+ const repoPath = await cloneEnvRepository(env);
371
+ const { deployConfigFilePath: valuesFilePath } = getConfigPath(
372
+ application,
373
+ env,
374
+ site,
375
+ 'values',
376
+ );
377
+ const { deployConfigFilePath: appFilePath } = getConfigPath(
378
+ application,
379
+ env,
380
+ site,
381
+ 'app',
382
+ );
383
+
384
+ if (!fs.existsSync(valuesFilePath) || !fs.existsSync(appFilePath)) {
385
+ const missingFiles = [];
386
+ if (!fs.existsSync(valuesFilePath))
387
+ missingFiles.push(`${application}.values.yaml`);
388
+ if (!fs.existsSync(appFilePath))
389
+ missingFiles.push(`${application}.app.yaml`);
390
+ console.log(
391
+ `⚠️ Skipping deployment to ${site}: config file(s) missing: ${missingFiles.join(', ')}`,
392
+ );
393
+ console.log(
394
+ ` This site may not be configured for ${application} application`,
395
+ );
396
+ return;
397
+ }
398
+
399
+ if (env !== 'dev') {
400
+ await copy2(
401
+ appDetails.repositoryName,
402
+ 'dev',
403
+ env,
404
+ deployTag,
405
+ appDetails.imageName,
406
+ );
407
+ }
408
+
409
+ const { deployConfig, deployConfigFilePath } = await readDeployConfig(
410
+ application,
411
+ env,
412
+ site,
413
+ 'values',
414
+ );
415
+ const updatedConfigData = setTag(deployConfig, deployTag);
416
+
417
+ const {
418
+ deployConfig: toDeployAppConfig,
419
+ deployConfigFilePath: toDeployAppConfigFilePath,
420
+ } = await readDeployConfig(application, env, site, 'app');
421
+
422
+ toDeployAppConfig.revision = appDetails.getAppRevision
423
+ ? appDetails.getAppRevision(updatedConfigData)
424
+ : updatedConfigData.image.tag;
425
+
426
+ writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
427
+
428
+ await writeAndPushDeployConfig(
429
+ repoPath,
430
+ deployConfigFilePath,
431
+ updatedConfigData,
432
+ `Update image tag to ${deployConfig.image.tag}`,
433
+ );
434
+
435
+ console.log('Deployment completed successfully.');
436
+ }
437
+
438
+ async function getAppInfo(coreUrl) {
439
+ const url = `${coreUrl}/b2b/rest/v1/appinfo`;
440
+ return await axios.get(url);
441
+ }
442
+
443
+ export function getBranchNameFromVersion(version) {
444
+ let parsed;
445
+ try {
446
+ const semverVersion = gitDescribeToSemver(version);
447
+ parsed = semver.parse(semverVersion);
448
+ let newVersion;
449
+ if (parsed.prerelease.length > 0) {
450
+ newVersion = parsed.build[1].slice(1);
451
+ } else if (parsed.build[0] != '0') {
452
+ console.error(`❌ This version is a custom branch. Deployment canceled.`);
453
+ process.exit(1);
454
+ } else {
455
+ newVersion = 'v' + parsed.version;
456
+ }
457
+ console.log(`Extracted version ${newVersion} from ${version}`);
458
+ return newVersion;
459
+ } catch {
460
+ console.log(`Error parsing version : ${version}`);
461
+ console.log('Parsed Object:');
462
+ console.log(parsed);
463
+ }
464
+ }
465
+
466
+ async function getCoreBranchName(fromEnv, fromSite) {
467
+ const envConfig = environmentConfig[fromEnv];
468
+ const oldUrl = envConfig.oldUrl(fromSite);
469
+ const newUrl = envConfig.newUrl(fromSite) + '/core';
470
+
471
+ let appinfo;
472
+ try {
473
+ appinfo = await getAppInfo(oldUrl);
474
+ } catch {
475
+ try {
476
+ appinfo = await getAppInfo(newUrl);
477
+ } catch {
478
+ throw new Error('Cannot get app info');
479
+ }
480
+ }
481
+
482
+ const coreVersion = appinfo.data.core.version;
483
+ const branchName = getBranchNameFromVersion(coreVersion);
484
+ return branchName;
485
+ }
486
+
487
+ export async function deployApp(
488
+ application,
489
+ appDetails,
490
+ fromEnv,
491
+ fromSite,
492
+ toEnv,
493
+ toSite,
494
+ ) {
495
+ let changesMade = false;
496
+ console.log(`Processing application: ${application}`);
497
+
498
+ // Read the deployment configuration for the source environment and site
499
+ const { deployConfig: fromDeployConfig } = await readDeployConfig(
500
+ application,
501
+ fromEnv,
502
+ fromSite,
503
+ 'values',
504
+ );
505
+
506
+ if (fromEnv !== toEnv) {
507
+ await copy2(
508
+ appDetails.repositoryName,
509
+ fromEnv,
510
+ toEnv,
511
+ fromDeployConfig.image.tag,
512
+ appDetails.imageName,
513
+ );
514
+ }
515
+
516
+ // Read the deployment configuration for the target environment and site
517
+ const {
518
+ deployConfig: toDeployConfig,
519
+ deployConfigFilePath: toDeployConfigFilePath,
520
+ } = await readDeployConfig(application, toEnv, toSite, 'values');
521
+
522
+ // Update target configuration using attributes specified in appInfo
523
+ for (const attribute of appDetails.deployAttributes) {
524
+ const sourceValue = lodash.get(fromDeployConfig, attribute);
525
+
526
+ if (sourceValue !== undefined) {
527
+ // Update the target configuration with the source value
528
+ lodash.set(toDeployConfig, attribute, sourceValue);
529
+ changesMade = true;
530
+ }
531
+ }
532
+
533
+ if (appDetails.updateConfigFunction) {
534
+ appDetails.updateConfigFunction(toDeployConfig);
535
+ }
536
+
537
+ const {
538
+ deployConfig: toDeployAppConfig,
539
+ deployConfigFilePath: toDeployAppConfigFilePath,
540
+ } = await readDeployConfig(application, toEnv, toSite, 'app');
541
+
542
+ toDeployAppConfig.revision = appDetails.getAppRevision
543
+ ? appDetails.getAppRevision(fromDeployConfig)
544
+ : fromDeployConfig.image.tag;
545
+
546
+ // Write updated configuration to the target YAML file
547
+ writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
548
+
549
+ writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
550
+ return changesMade;
551
+ }
552
+
553
+ export async function deployFromSite(
554
+ fromEnv,
555
+ fromSite,
556
+ toEnv,
557
+ toSite,
558
+ isDeployCore,
559
+ isDeployConnect,
560
+ ) {
561
+ if (isDeployCore === 'true') {
562
+ const branchName = await getCoreBranchName(fromEnv, fromSite);
563
+
564
+ const coreEnv = toEnv == 'qa' ? 'preprod' : toEnv;
565
+ await deployCore(coreEnv, toSite, 'true', branchName);
566
+
567
+ // To be added back when splitting between on prem and in cloud
568
+ /*await runPipeline('site-deploy', 'deploy-core', 'main', {
569
+ ENV: toEnv,
570
+ SITE: toSite,
571
+ LIQUIBASE: 'true',
572
+ BRANCH: branchName,
573
+ });*/
574
+ }
575
+
576
+ if (isDeployConnect === 'true') {
577
+ const branch = '';
578
+ const getComparedVersion = await compareGetConnectSiteVersions(
579
+ fromEnv,
580
+ fromSite,
581
+ toEnv,
582
+ toSite,
583
+ );
584
+ if (getComparedVersion) {
585
+ console.log('Version to deploy is: ', getComparedVersion);
586
+ const connectDeployPath = await cloneRepository('connect-deploy');
587
+ // update target version set to git-commit-${getCommparedVersion}}
588
+ await updateAndCommitConnectVersion(
589
+ toEnv,
590
+ toSite,
591
+ getComparedVersion,
592
+ connectDeployPath,
593
+ );
594
+ // save and deploy
595
+ const runLiquibase = true;
596
+ await deployConnect(toEnv, toSite, runLiquibase, branch);
597
+ }
598
+ }
599
+
600
+ // Clone repositories for source and target environments
601
+ const fromRepoPath = await cloneEnvRepository(fromEnv);
602
+ const toRepoPath =
603
+ toEnv === fromEnv ? fromRepoPath : await cloneEnvRepository(toEnv);
604
+
605
+ // Initialize a flag to track changes
606
+ let changesMade = false;
607
+ const applications = await getAppTypesFromRepo(toEnv, toSite);
608
+ console.log(applications);
609
+ // Iterate over applications in appInfo
610
+
611
+ let appInfoLocal = Object.fromEntries(
612
+ applications.applications
613
+ .filter((application) =>
614
+ Object.prototype.hasOwnProperty.call(appInfo, application),
615
+ )
616
+ .map((application) => [application, appInfo[application]]),
617
+ );
618
+
619
+ // let appInfoLocal = {};
620
+ // for (const application of applications.applications) {
621
+ // if (Object.prototype.hasOwnProperty.call(appInfo, application)) {
622
+ // appInfoLocal[application] = appInfo[application];
623
+ // }
624
+ // }
625
+
626
+ if (Object.keys(appInfoLocal).length === 0) {
627
+ const { deployConfig: toDeployAppConfig } = await readDeployConfig(
628
+ fromSite,
629
+ toEnv,
630
+ toSite,
631
+ 'app',
632
+ );
633
+
634
+ appInfoLocal[fromSite] = {
635
+ repositoryName: toDeployAppConfig.repo,
636
+ deployAttributes: ['image.tag'],
637
+ };
638
+ }
639
+
640
+ console.log('appinfoLocal', appInfoLocal);
641
+
642
+ for (const [application, appDetails] of Object.entries(appInfoLocal)) {
643
+ let cm = await deployApp(
644
+ application,
645
+ appDetails,
646
+ fromEnv,
647
+ fromSite,
648
+ toEnv,
649
+ toSite,
650
+ );
651
+ if (cm) {
652
+ changesMade = true;
653
+ }
654
+ }
655
+
656
+ // Push all changes if any updates were made
657
+ if (changesMade) {
658
+ await pushDeployConfig(
659
+ toRepoPath,
660
+ `Updated deployment configurations from ${fromEnv}/${fromSite} to ${toEnv}/${toSite}`,
661
+ );
662
+ console.log('✅ All configurations updated and pushed successfully.');
663
+ } else {
664
+ console.log('✅ No changes detected. Nothing to push.');
665
+ }
666
+ }