@docupace/pipeline 1.0.7-beta.2 → 1.0.8-6.xray

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