@docupace/pipeline 1.0.93 โ†’ 1.0.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docupace/pipeline",
3
- "version": "1.0.93",
3
+ "version": "1.0.95",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/next/cli.js CHANGED
@@ -135,9 +135,14 @@ program
135
135
  program
136
136
  .command('deploy-app <application>')
137
137
  .description('Deploy an application for a specific site')
138
- .action(async (application) => {
138
+ .option('-e, --env <env>', 'Target environment (overrides default sites)')
139
+ .option('-s, --site <site>', 'Target site (overrides default sites)')
140
+ .action(async (application, options) => {
139
141
  const deployTag = await getDeployTag();
140
- const sites = await getDefaultDeploySite(deployTag);
142
+ const sites =
143
+ options.env && options.site
144
+ ? [{ env: options.env, site: options.site }]
145
+ : await getDefaultDeploySite(deployTag);
141
146
  for (const { env, site } of sites) {
142
147
  await deployAppWithTag(application, env, site, deployTag);
143
148
  }
@@ -146,10 +151,15 @@ program
146
151
  program
147
152
  .command('deploy-mono-app <tag> <application>')
148
153
  .description('Deploy an mono application for a specific site')
149
- .action(async (tag, application) => {
154
+ .option('-e, --env <env>', 'Target environment (overrides default sites)')
155
+ .option('-s, --site <site>', 'Target site (overrides default sites)')
156
+ .action(async (tag, application, options) => {
150
157
  const deployTag = await getDeployTag(tag);
151
158
  const version = 'v' + deployTag.split('@')[1];
152
- const sites = await getDefaultDeploySite(version);
159
+ const sites =
160
+ options.env && options.site
161
+ ? [{ env: options.env, site: options.site }]
162
+ : await getDefaultDeploySite(version);
153
163
  for (const { env, site } of sites) {
154
164
  await deployAppWithTag(application, env, site, version);
155
165
  }
@@ -70,7 +70,7 @@ async function updateAndCommitConnectVersion(
70
70
 
71
71
  console.log('๐Ÿงน Cleaning up...');
72
72
  await $`rm -rf ${connectDeployPath}`;
73
- console.log('โœ… Cleanup complete.');
73
+ console.log('Cleanup complete.');
74
74
  } catch (error) {
75
75
  console.error('Error updating Connect version:', error);
76
76
  throw error;
@@ -183,13 +183,19 @@ function getAppTypesFromRepo(env, site) {
183
183
  export async function readDeployConfig(application, env, site, name) {
184
184
  const { deployConfigFilePath } = getConfigPath(application, env, site, name);
185
185
 
186
- let deployConfig;
186
+ let deployConfig = null;
187
187
  try {
188
188
  deployConfig = yaml.load(fs.readFileSync(deployConfigFilePath, 'utf8'));
189
189
  console.log(`YAML file loaded from: ${deployConfigFilePath}`);
190
190
  } catch (error) {
191
- console.error(`Error reading YAML file: ${error.message}`);
192
- process.exit(1);
191
+ if (error.code === 'ENOENT') {
192
+ console.log(
193
+ `โš ๏ธ No version found for ${application} in ${env}/${site} (${deployConfigFilePath} does not exist), skipping`,
194
+ );
195
+ } else {
196
+ console.error(`Error reading YAML file: ${error.message}`);
197
+ process.exit(1);
198
+ }
193
199
  }
194
200
 
195
201
  return { deployConfig, deployConfigFilePath };
@@ -240,7 +246,7 @@ export async function pushDeployConfig(repoPath, commitMessage) {
240
246
  await $`cd ${repoPath} && git status --porcelain`
241
247
  ).stdout.trim();
242
248
  if (!status) {
243
- console.log('โœ… No changes to commit. Exiting.');
249
+ console.log('No changes to commit. Exiting.');
244
250
  await $`rm -rf ${repoPath}`;
245
251
  console.log('๐Ÿงน Cleanup complete.');
246
252
  return;
@@ -265,17 +271,17 @@ export async function pushDeployConfig(repoPath, commitMessage) {
265
271
  await $`cd ${repoPath} && git add .`;
266
272
  await $`cd ${repoPath} && git commit -m ${commitMessage}`;
267
273
  await $`cd ${repoPath} && git push`;
268
- console.log('โœ… Changes pushed to the remote repository.');
274
+ console.log('Changes pushed to the remote repository.');
269
275
 
270
276
  console.log('๐Ÿงน Cleaning up...');
271
277
  await $`rm -rf ${repoPath}`;
272
- console.log('โœ… Cleanup complete.');
278
+ console.log('Cleanup complete.');
273
279
  }
274
280
 
275
281
  function writeDeployConfig(deployConfigFilePath, deployConfig) {
276
282
  try {
277
283
  fs.writeFileSync(deployConfigFilePath, yaml.dump(deployConfig), 'utf8');
278
- console.log(`โœ… Updated YAML file saved at: ${deployConfigFilePath}`);
284
+ console.log(`Updated YAML file saved at: ${deployConfigFilePath}`);
279
285
  } catch (error) {
280
286
  console.error(`โŒ Error writing YAML file: ${error.message}`);
281
287
  process.exit(1);
@@ -506,6 +512,10 @@ export async function deployApp(
506
512
  'values',
507
513
  );
508
514
 
515
+ if (!fromDeployConfig) {
516
+ return false;
517
+ }
518
+
509
519
  if (fromEnv !== toEnv) {
510
520
  await copy2(
511
521
  appDetails.repositoryName,
@@ -522,6 +532,10 @@ export async function deployApp(
522
532
  deployConfigFilePath: toDeployConfigFilePath,
523
533
  } = await readDeployConfig(application, toEnv, toSite, 'values');
524
534
 
535
+ if (!toDeployConfig) {
536
+ return false;
537
+ }
538
+
525
539
  // Update target configuration using attributes specified in appInfo
526
540
  for (const attribute of appDetails.deployAttributes) {
527
541
  const sourceValue = lodash.get(fromDeployConfig, attribute);
@@ -582,23 +596,40 @@ async function updateAutoDeployVersion(
582
596
  oldVersion,
583
597
  newVersion,
584
598
  ) {
599
+ console.log(
600
+ `Info: Checking auto-deploy version update for ${toEnv}/${toSite}: ${oldVersion} โ†’ ${newVersion}`,
601
+ );
602
+
585
603
  const oldMajorMinor = extractMajorMinor(oldVersion);
586
604
  const newMajorMinor = extractMajorMinor(newVersion);
587
605
 
588
- if (!oldMajorMinor || !newMajorMinor || oldMajorMinor === newMajorMinor) {
606
+ if (!oldMajorMinor || !newMajorMinor) {
607
+ console.log(
608
+ `Info: Could not parse major.minor from versions (old: ${oldVersion}, new: ${newVersion}), skipping auto-deploy update`,
609
+ );
610
+ return false;
611
+ }
612
+
613
+ if (oldMajorMinor === newMajorMinor) {
614
+ console.log(
615
+ `Info: Major.minor version unchanged (${newMajorMinor}), skipping auto-deploy update`,
616
+ );
589
617
  return false;
590
618
  }
591
619
 
592
620
  if (newVersion.toLowerCase().includes('alpha') || newVersion.includes('-')) {
621
+ console.log(
622
+ `Info: New version (${newVersion}) is a pre-release, skipping auto-deploy update`,
623
+ );
593
624
  return false;
594
625
  }
595
626
 
596
- const { repoPath } = getRepoLocalPath(fromEnv);
597
- const autoDeployConfigPath = path.join(repoPath, autoDeployConfigName);
627
+ const { repoPath: devRepoPath } = getRepoLocalPath('dev');
628
+ const autoDeployConfigPath = path.join(devRepoPath, autoDeployConfigName);
598
629
 
599
630
  if (!fs.existsSync(autoDeployConfigPath)) {
600
631
  console.log(
601
- `Info: ${autoDeployConfigName} not found in ${fromEnv}, skipping auto-deploy update`,
632
+ `Info: ${autoDeployConfigName} not found in gitops-dev, skipping auto-deploy update`,
602
633
  );
603
634
  return false;
604
635
  }
@@ -639,6 +670,112 @@ async function updateAutoDeployVersion(
639
670
  return true;
640
671
  }
641
672
 
673
+ /**
674
+ * Validate that all deployed components have internally consistent versions and
675
+ * share the same major.minor across the board before a git commit.
676
+ *
677
+ * Per-app checks:
678
+ * - image.tag major.minor == app.yaml revision major.minor
679
+ * (handles both plain "v1.21.3" and prefixed "enterprise-ui-app@1.21.6" revisions)
680
+ * - graphql-server additionally: image.tag major.minor == standardConfigRemoteBranch major.minor
681
+ *
682
+ * Cross-app check:
683
+ * - all deployed components must share the same major.minor
684
+ *
685
+ * @param {string} env
686
+ * @param {string} site
687
+ * @param {object} deployedApps - appInfoLocal map of { appName: appDetails }
688
+ * @throws {Error} if any version mismatch is detected
689
+ */
690
+ async function validateDeployVersionConsistency(env, site, deployedApps) {
691
+ console.log(
692
+ `Validating version consistency for all components in ${env}/${site}...`,
693
+ );
694
+
695
+ const mismatches = [];
696
+ const appVersions = [];
697
+
698
+ for (const [application] of Object.entries(deployedApps)) {
699
+ const { deployConfigFilePath: valuesPath } = getConfigPath(
700
+ application,
701
+ env,
702
+ site,
703
+ 'values',
704
+ );
705
+ const { deployConfigFilePath: appPath } = getConfigPath(
706
+ application,
707
+ env,
708
+ site,
709
+ 'app',
710
+ );
711
+
712
+ if (!fs.existsSync(valuesPath) || !fs.existsSync(appPath)) {
713
+ console.log(
714
+ `โš ๏ธ Skipping version consistency check for ${application}: config files missing in ${env}/${site}`,
715
+ );
716
+ continue;
717
+ }
718
+
719
+ const valuesConfig = yaml.load(fs.readFileSync(valuesPath, 'utf8'));
720
+ const appConfig = yaml.load(fs.readFileSync(appPath, 'utf8'));
721
+
722
+ const imageTag = valuesConfig?.image?.tag;
723
+ const revision = appConfig?.revision;
724
+
725
+ // Extract bare version from revision (e.g. "enterprise-ui-app@1.21.6" โ†’ "1.21.6")
726
+ const revisionVersion = revision?.includes('@')
727
+ ? revision.split('@')[1]
728
+ : revision;
729
+
730
+ const imageMajorMinor = extractMajorMinor(imageTag);
731
+ const revisionMajorMinor = extractMajorMinor(revisionVersion);
732
+
733
+ if (imageMajorMinor && revisionMajorMinor) {
734
+ if (imageMajorMinor !== revisionMajorMinor) {
735
+ mismatches.push(
736
+ `${application}: image.tag (${imageTag}) does not match app.yaml revision (${revision}) on major.minor`,
737
+ );
738
+ }
739
+ }
740
+
741
+ if (application === 'graphql-server') {
742
+ const standardConfigBranch =
743
+ valuesConfig?.graphqlServer?.standardConfigRemoteBranch;
744
+ const standardConfigMajorMinor = extractMajorMinor(standardConfigBranch);
745
+ if (imageMajorMinor && standardConfigMajorMinor) {
746
+ if (imageMajorMinor !== standardConfigMajorMinor) {
747
+ mismatches.push(
748
+ `graphql-server: image.tag (${imageTag}) does not match graphqlServer.standardConfigRemoteBranch (${standardConfigBranch}) on major.minor`,
749
+ );
750
+ }
751
+ }
752
+ }
753
+
754
+ if (imageMajorMinor) {
755
+ appVersions.push({ application, imageTag, majorMinor: imageMajorMinor });
756
+ }
757
+ }
758
+
759
+ const uniqueMajorMinors = [...new Set(appVersions.map((a) => a.majorMinor))];
760
+ if (uniqueMajorMinors.length > 1) {
761
+ const versionList = appVersions
762
+ .map((a) => `${a.application} (${a.imageTag})`)
763
+ .join(', ');
764
+ mismatches.push(
765
+ `cross-app: components are on different major.minor versions: ${versionList}`,
766
+ );
767
+ }
768
+
769
+ if (mismatches.length > 0) {
770
+ throw new Error(
771
+ `โŒ Version consistency validation failed for ${env}/${site}:\n${mismatches.map((m) => ` - ${m}`).join('\n')}`,
772
+ );
773
+ }
774
+
775
+ console.log(
776
+ `โœ… Version consistency validated for ${env}/${site}: all ${appVersions.length} component(s) aligned on ${uniqueMajorMinors[0] ?? 'n/a'}`,
777
+ );
778
+ }
642
779
  export async function deployFromSite(
643
780
  fromEnv,
644
781
  fromSite,
@@ -789,9 +926,14 @@ export async function deployFromSite(
789
926
  const toRepoPath =
790
927
  toEnv === fromEnv ? fromRepoPath : await cloneEnvRepository(toEnv);
791
928
 
929
+ // Ensure gitops-dev is cloned for auto-deploy version checking
930
+ if (fromEnv !== 'dev' && toEnv !== 'dev') {
931
+ await cloneEnvRepository('dev');
932
+ }
933
+
792
934
  // Initialize a flag to track changes
793
935
  let changesMade = false;
794
- const applications = await getAppTypesFromRepo(toEnv, toSite);
936
+ const applications = getAppTypesFromRepo(fromEnv, fromSite);
795
937
  console.log(applications);
796
938
  // Iterate over applications in appInfo
797
939
 
@@ -844,8 +986,10 @@ export async function deployFromSite(
844
986
  toSite,
845
987
  'values',
846
988
  );
847
- newVersion = fromConfig.image?.tag;
848
- oldVersion = toConfig.image?.tag;
989
+ if (fromConfig && toConfig) {
990
+ newVersion = fromConfig.image?.tag;
991
+ oldVersion = toConfig.image?.tag;
992
+ }
849
993
  }
850
994
 
851
995
  let cm = await deployApp(
@@ -861,7 +1005,15 @@ export async function deployFromSite(
861
1005
  }
862
1006
  }
863
1007
 
864
- if (oldVersion && newVersion && oldVersion !== newVersion) {
1008
+ if (!oldVersion || !newVersion) {
1009
+ console.log(
1010
+ `Info: Could not determine versions for auto-deploy check (old: ${oldVersion}, new: ${newVersion})`,
1011
+ );
1012
+ } else if (oldVersion === newVersion) {
1013
+ console.log(
1014
+ `Info: Version unchanged (${newVersion}), skipping auto-deploy update`,
1015
+ );
1016
+ } else {
865
1017
  const autoDeployUpdated = await updateAutoDeployVersion(
866
1018
  fromEnv,
867
1019
  toEnv,
@@ -871,7 +1023,7 @@ export async function deployFromSite(
871
1023
  );
872
1024
  if (autoDeployUpdated) {
873
1025
  console.log(
874
- `Info: auto-deploy.yaml updated for ${toEnv}/${toSite} due to version change ${oldVersion} โ†’ ${newVersion}`,
1026
+ `Info: auto-deploy.yaml updated for ${toEnv}/${toSite}: ${oldVersion} โ†’ ${newVersion}`,
875
1027
  );
876
1028
  changesMade = true;
877
1029
  }
@@ -879,12 +1031,13 @@ export async function deployFromSite(
879
1031
 
880
1032
  // Push all changes if any updates were made
881
1033
  if (changesMade) {
1034
+ await validateDeployVersionConsistency(toEnv, toSite, appInfoLocal);
882
1035
  await pushDeployConfig(
883
1036
  toRepoPath,
884
1037
  `Updated deployment configurations from ${fromEnv}/${fromSite} to ${toEnv}/${toSite}`,
885
1038
  );
886
- console.log('โœ… All configurations updated and pushed successfully.');
1039
+ console.log('All configurations updated and pushed successfully.');
887
1040
  } else {
888
- console.log('โœ… No changes detected. Nothing to push.');
1041
+ console.log('No changes detected. Nothing to push.');
889
1042
  }
890
1043
  }