@docupace/pipeline 1.0.6 → 1.0.7-7.micro-cli

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 (68) hide show
  1. package/.prettierrc.cjs +12 -12
  2. package/README.md +136 -0
  3. package/bin/azuread_update_callbacks_for_app.sh +58 -0
  4. package/bin/next-changeset-validate.sh +8 -8
  5. package/bitbucket-pipelines.yml +60 -0
  6. package/eslint.config.js +49 -26
  7. package/jest.config.js +15 -0
  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 -117
  12. package/next/sonar.sh +23 -23
  13. package/package.json +22 -7
  14. package/src/auth0/alias.js +79 -0
  15. package/src/auth0/alias.test.js +172 -0
  16. package/src/auth0/cli.js +82 -0
  17. package/src/auth0/configure.js +902 -0
  18. package/src/auth0/configure.test.js +167 -0
  19. package/src/auth0/export.js +48 -0
  20. package/src/auth0/export.test.js +103 -0
  21. package/src/auth0/metadata.js +141 -0
  22. package/src/auth0/metadata.test.js +186 -0
  23. package/src/auth0/vault.js +60 -0
  24. package/src/auth0/vault.test.js +134 -0
  25. package/src/changeset/changelog-wiki.js +328 -0
  26. package/src/changeset/changeset-config.js +34 -0
  27. package/src/changeset/cli.js +237 -281
  28. package/src/changeset/create.js +215 -0
  29. package/src/changeset/package-util.js +87 -0
  30. package/src/changeset/util.js +43 -0
  31. package/src/changeset/validate.js +91 -47
  32. package/src/changeset/version.js +102 -0
  33. package/src/jenkins/setParams.groovy +58 -59
  34. package/src/lib/merge-driver.js +204 -0
  35. package/src/merge/cli-merge-all.js +507 -0
  36. package/src/merge/cli-merge.js +492 -0
  37. package/src/merge/cli-resolve.js +399 -0
  38. package/src/merge/cli.js +38 -0
  39. package/src/merge/merge-changelog.js +87 -0
  40. package/src/merge/merge-package.js +106 -0
  41. package/src/merge/merge-types.js +82 -0
  42. package/src/merge/pr.js +231 -0
  43. package/src/micro/cli.js +71 -0
  44. package/src/micro/ms-deploy.js +273 -0
  45. package/src/micro/ms-publish.js +186 -0
  46. package/src/next/alias.js +53 -0
  47. package/src/next/apache.js +81 -0
  48. package/src/next/cli.js +111 -15
  49. package/src/next/config.js +137 -72
  50. package/src/next/connect.js +226 -0
  51. package/src/next/copy.js +61 -30
  52. package/src/next/deploy.js +633 -312
  53. package/src/next/git-to-semver.js +79 -0
  54. package/src/next/haproxy.js +76 -0
  55. package/src/next/jenkins.js +123 -88
  56. package/src/next/jira.js +84 -0
  57. package/src/next/logger.js +17 -0
  58. package/src/next/pipline.js +114 -115
  59. package/src/next/publish.js +71 -48
  60. package/src/next/pull.js +7 -7
  61. package/src/next/sonar.js +23 -23
  62. package/src/next/test.js +5 -0
  63. package/src/next/util.js +93 -0
  64. package/src/next/vault.js +45 -0
  65. package/terraform/apply.sh +37 -0
  66. package/terraform/init.sh +72 -0
  67. package/terraform/plan.sh +76 -0
  68. package/terraform/validate.sh +17 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Create a Bitbucket pull request
3
+ * @param {string} sourceBranch - Source branch name
4
+ * @param {string} targetBranch - Target branch name
5
+ * @param {string} title - PR title
6
+ * @param {string} description - PR description
7
+ * @returns {Promise<Object>} - PR creation result
8
+ */
9
+ import { execSync } from 'child_process';
10
+
11
+ /**
12
+ * Check for existing pull requests with specific description pattern
13
+ * @param {string} descriptionPattern - Pattern to match in PR descriptions
14
+ * @returns {Promise<Object[]>} - Array of matching PRs
15
+ */
16
+ export async function getExistingPRsWithDescription(
17
+ descriptionPattern,
18
+ repositories,
19
+ ) {
20
+ // If no repositories provided, fall back to current repo behavior for backward compatibility
21
+ if (!repositories || !Array.isArray(repositories)) {
22
+ return getExistingPRsFromCurrentRepo(descriptionPattern);
23
+ }
24
+
25
+ try {
26
+ // Check for Bitbucket credentials
27
+ const username = process.env.BITBUCKET_USERNAME;
28
+ const password = process.env.BITBUCKET_APP_PASSWORD;
29
+
30
+ if (!username || !password) {
31
+ console.log(
32
+ `\n⚠️ Bitbucket credentials not found. Cannot check existing PRs.`,
33
+ );
34
+ return [];
35
+ }
36
+
37
+ const auth = Buffer.from(`${username}:${password}`).toString('base64');
38
+ const allMatchingPRs = [];
39
+
40
+ // Check each repository for matching PRs
41
+ for (const repo of repositories) {
42
+ try {
43
+ const match = repo.url.match(/bitbucket\.org[\/:]([^\/]+)\/([^\/\.]+)/);
44
+
45
+ if (!match) {
46
+ console.warn(`Could not parse repository URL: ${repo.url}`);
47
+ continue;
48
+ }
49
+
50
+ const [, workspace, repoSlug] = match;
51
+
52
+ const response = await fetch(
53
+ `https://api.bitbucket.org/2.0/repositories/${workspace}/${repoSlug}/pullrequests?state=OPEN`,
54
+ {
55
+ method: 'GET',
56
+ headers: {
57
+ Authorization: `Basic ${auth}`,
58
+ 'Content-Type': 'application/json',
59
+ },
60
+ },
61
+ );
62
+
63
+ if (!response.ok) {
64
+ const error = await response.text();
65
+ console.warn(
66
+ `Bitbucket API error for ${repo.name}: ${response.status} - ${error}`,
67
+ );
68
+ continue;
69
+ }
70
+
71
+ const data = await response.json();
72
+ const matchingPRs = data.values.filter(
73
+ (pr) =>
74
+ pr.description && pr.description.startsWith(descriptionPattern),
75
+ );
76
+
77
+ // Add repository info to each PR for context
78
+ matchingPRs.forEach((pr) => {
79
+ pr.repositoryName = repo.name;
80
+ pr.repositoryUrl = repo.url;
81
+ });
82
+
83
+ allMatchingPRs.push(...matchingPRs);
84
+ } catch (error) {
85
+ console.warn(
86
+ `Failed to check PRs for repository ${repo.name}: ${error.message}`,
87
+ );
88
+ }
89
+ }
90
+
91
+ return allMatchingPRs;
92
+ } catch (error) {
93
+ console.error(`Failed to get existing PRs: ${error.message}`);
94
+ return [];
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Legacy function for backward compatibility - checks PRs in current repo only
100
+ */
101
+ export async function getExistingPRsFromCurrentRepo(descriptionPattern) {
102
+ try {
103
+ // Get Bitbucket workspace and repo from git remote
104
+ const remoteUrl = execSync('git config --get remote.origin.url', {
105
+ encoding: 'utf-8',
106
+ }).trim();
107
+ const match = remoteUrl.match(/bitbucket\.org[\/:]([^\/]+)\/([^\/\.]+)/);
108
+
109
+ if (!match) {
110
+ throw new Error(
111
+ 'Could not parse Bitbucket workspace and repository from remote URL',
112
+ );
113
+ }
114
+
115
+ const [, workspace, repoSlug] = match;
116
+
117
+ // Check for Bitbucket credentials
118
+ const username = process.env.BITBUCKET_USERNAME;
119
+ const password = process.env.BITBUCKET_APP_PASSWORD;
120
+
121
+ if (!username || !password) {
122
+ console.log(
123
+ `\n⚠️ Bitbucket credentials not found. Cannot check existing PRs.`,
124
+ );
125
+ return [];
126
+ }
127
+
128
+ const auth = Buffer.from(`${username}:${password}`).toString('base64');
129
+ const response = await fetch(
130
+ `https://api.bitbucket.org/2.0/repositories/${workspace}/${repoSlug}/pullrequests?state=OPEN`,
131
+ {
132
+ method: 'GET',
133
+ headers: {
134
+ Authorization: `Basic ${auth}`,
135
+ 'Content-Type': 'application/json',
136
+ },
137
+ },
138
+ );
139
+
140
+ if (!response.ok) {
141
+ const error = await response.text();
142
+ throw new Error(`Bitbucket API error: ${response.status} - ${error}`);
143
+ }
144
+
145
+ const data = await response.json();
146
+ const matchingPRs = data.values.filter(
147
+ (pr) => pr.description && pr.description.startsWith(descriptionPattern),
148
+ );
149
+
150
+ return matchingPRs;
151
+ } catch (error) {
152
+ console.error(`Failed to get existing PRs: ${error.message}`);
153
+ return [];
154
+ }
155
+ }
156
+
157
+ export async function createBitbucketPR(
158
+ sourceBranch,
159
+ targetBranch,
160
+ title,
161
+ description,
162
+ ) {
163
+ try {
164
+ // Get Bitbucket workspace and repo from git remote
165
+ const remoteUrl = execSync('git config --get remote.origin.url', {
166
+ encoding: 'utf-8',
167
+ }).trim();
168
+ const match = remoteUrl.match(/bitbucket\.org[\/:]([^\/]+)\/([^\/\.]+)/);
169
+
170
+ if (!match) {
171
+ throw new Error(
172
+ 'Could not parse Bitbucket workspace and repository from remote URL',
173
+ );
174
+ }
175
+
176
+ const [, workspace, repoSlug] = match;
177
+
178
+ // Check for Bitbucket credentials
179
+ const username = process.env.BITBUCKET_USERNAME;
180
+ const password = process.env.BITBUCKET_APP_PASSWORD;
181
+
182
+ if (!username || !password) {
183
+ console.log(
184
+ `\n⚠️ Bitbucket credentials not found in environment variables.`,
185
+ );
186
+ console.log(
187
+ 'Please set BITBUCKET_USERNAME and BITBUCKET_APP_PASSWORD to auto-create PR.',
188
+ );
189
+ console.log(
190
+ `Manual PR creation URL: https://bitbucket.org/${workspace}/${repoSlug}/pull-requests/new?source=${sourceBranch}&dest=${targetBranch}`,
191
+ );
192
+ return {
193
+ success: false,
194
+ manual: true,
195
+ url: `https://bitbucket.org/${workspace}/${repoSlug}/pull-requests/new?source=${sourceBranch}&dest=${targetBranch}`,
196
+ };
197
+ }
198
+
199
+ // Create PR via Bitbucket API
200
+ const prData = {
201
+ title,
202
+ description,
203
+ source: { branch: { name: sourceBranch } },
204
+ destination: { branch: { name: targetBranch } },
205
+ };
206
+
207
+ const auth = Buffer.from(`${username}:${password}`).toString('base64');
208
+ const response = await fetch(
209
+ `https://api.bitbucket.org/2.0/repositories/${workspace}/${repoSlug}/pullrequests`,
210
+ {
211
+ method: 'POST',
212
+ headers: {
213
+ Authorization: `Basic ${auth}`,
214
+ 'Content-Type': 'application/json',
215
+ },
216
+ body: JSON.stringify(prData),
217
+ },
218
+ );
219
+
220
+ if (!response.ok) {
221
+ const error = await response.text();
222
+ throw new Error(`Bitbucket API error: ${response.status} - ${error}`);
223
+ }
224
+
225
+ const pr = await response.json();
226
+ return { success: true, pr, url: pr.links.html.href };
227
+ } catch (error) {
228
+ console.error(`Failed to create Bitbucket PR: ${error.message}`);
229
+ return { success: false, error: error.message };
230
+ }
231
+ }
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { publishMicroservice } from './ms-publish.js';
5
+
6
+ import dotenv from 'dotenv';
7
+ import {
8
+ deployMicroserviceEndToEnd,
9
+ promoteMicroserviceImage,
10
+ testMicroserviceDeploy,
11
+ testMicroservicePublish,
12
+ } from './ms-deploy.js';
13
+
14
+ dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
15
+
16
+ const program = new Command();
17
+
18
+ //const GIT_SHA = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
19
+
20
+ // Add the new publish-microservice command
21
+ program
22
+ .command('publish-microservice <serviceName>')
23
+ .description('Publish a microservice Docker image with version tags')
24
+ .option('-t, --tag <tag>', 'Version tag (defaults to BITBUCKET_TAG)')
25
+ .action(async (serviceName, options) => {
26
+ await publishMicroservice(serviceName, options);
27
+ });
28
+
29
+ program
30
+ .command('deploy-microservice <repoName>')
31
+ .description('Deploy microservice end-to-end (build + publish + deploy)')
32
+ .option('-e, --env <environment>', 'Target environment', 'dev')
33
+ .option('-t, --tag <tag>', 'Version tag (defaults to BITBUCKET_TAG)')
34
+ .action(async (repoName, options) => {
35
+ await deployMicroserviceEndToEnd(repoName, options.env, options.tag);
36
+ });
37
+
38
+ program
39
+ .command('promote-microservice <repoName>')
40
+ .description('Promote microservice image between environments')
41
+ .requiredOption('-f, --from <fromEnv>', 'Source environment')
42
+ .requiredOption('-t, --to <toEnv>', 'Target environment')
43
+ .requiredOption('-g, --tag <tag>', 'Image tag to promote')
44
+ .action(async (repoName, options) => {
45
+ await promoteMicroserviceImage(
46
+ repoName,
47
+ options.from,
48
+ options.to,
49
+ options.tag,
50
+ );
51
+ });
52
+
53
+ program
54
+ .command('test-deploy-microservice <serviceName>')
55
+ .description('Test microservice deployment only (no image building)')
56
+ .option('-e, --env <environment>', 'Target environment', 'dev')
57
+ .option('-t, --tag <tag>', 'Version tag', 'v0.1.0')
58
+ .action(async (serviceName, options) => {
59
+ await testMicroserviceDeploy(serviceName, options.env, options.tag);
60
+ });
61
+
62
+ program
63
+ .command('test-publish-microservice <serviceName>')
64
+ .description(
65
+ 'Test microservice Docker build and publish only (no deployment)',
66
+ )
67
+ .action(async (serviceName) => {
68
+ await testMicroservicePublish(serviceName);
69
+ });
70
+
71
+ program.parse(process.argv);
@@ -0,0 +1,273 @@
1
+ import { $ } from '../next/logger.js';
2
+ import { publishMicroservice } from './ms-publish.js';
3
+ import { copy2 } from '../next/copy.js';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ /**
7
+ * Deploy microservice end-to-end: build, publish, and deploy
8
+ * @param {string} repoName - Name of microservice (e.g., 'swp', 'fidelity', 'esp')
9
+ * @param {string} environment - Target environment (dev, qa, uat, stage, prod)
10
+ * @param {string} tag - Version tag (optional, defaults to BITBUCKET_TAG)
11
+ */
12
+ export async function deployMicroserviceEndToEnd(
13
+ repoName,
14
+ environment = '',
15
+ tag = null,
16
+ ) {
17
+ const serviceName = String(repoName || '')
18
+ .split('-')
19
+ .pop()
20
+ .trim();
21
+ const registryUrl = process.env.JF_REGISTRY_URL || '';
22
+ const deployTag = tag || process.env.BITBUCKET_TAG || process.env.VERSION_TAG;
23
+ if (!deployTag) {
24
+ throw new Error(
25
+ 'No tag provided. Set tag parameter or BITBUCKET_TAG/VERSION_TAG environment variable.',
26
+ );
27
+ }
28
+ console.log(`Starting ${serviceName} microservice deployment...`);
29
+ console.log(`Environment: ${environment}`);
30
+ console.log(`Tag: ${deployTag}`);
31
+ console.log('==========================================');
32
+ try {
33
+ // Step 1: Get Git SHA for image building
34
+ const gitSha = (
35
+ await $`git log ${deployTag} --pretty=format:'%h' -n 1`
36
+ ).stdout.trim();
37
+ console.log(`Git SHA: ${gitSha}`);
38
+ // Step 2: Publish Docker image with version tags
39
+ console.log('\nPublishing Docker image...');
40
+ await publishMicroservice(serviceName, {
41
+ tag: deployTag,
42
+ });
43
+ console.log('Docker image published successfully!');
44
+ // Step 3: Deploy to GitOps repository
45
+ console.log(`\nDeploying to ${environment} environment...`);
46
+ await deployMicroservice(serviceName, serviceName, environment, deployTag);
47
+ console.log(`Deployment to ${environment} completed successfully!`);
48
+ console.log(
49
+ `\n${serviceName} microservice deployment completed successfully!`,
50
+ );
51
+ console.log(
52
+ `Image: ${registryUrl}/${repoName}-${environment}-docker-local/${serviceName}:${deployTag}`,
53
+ );
54
+ } catch (error) {
55
+ console.error('\nDeployment failed:', error.message);
56
+ throw error;
57
+ }
58
+ }
59
+ /**
60
+ * Promote microservice image between environments
61
+ * @param {string} repoName - Name of microservice
62
+ * @param {string} fromEnv - Source environment
63
+ * @param {string} toEnv - Target environment
64
+ * @param {string} tag - Image tag to promote
65
+ */
66
+ export async function promoteMicroserviceImage(repoName, fromEnv, toEnv, tag) {
67
+ const serviceName = String(repoName || '')
68
+ .split('-')
69
+ .pop()
70
+ .trim();
71
+ console.log(`Starting ${serviceName} image promotion...`);
72
+ console.log(`From: ${fromEnv} → To: ${toEnv}`);
73
+ console.log(`Tag: ${tag}`);
74
+ console.log('==========================================');
75
+ try {
76
+ // Step 1: Copy Docker image between registries
77
+ console.log('\nCopying Docker image...');
78
+ const repositoryName = `${repoName}`;
79
+ await copy2(repositoryName, fromEnv, toEnv, tag, serviceName);
80
+ console.log('Docker image copied successfully!');
81
+ // Step 2: Deploy to target environment
82
+ console.log(`\nDeploying to ${toEnv} environment...`);
83
+ await deployMicroservice(serviceName, serviceName, toEnv, tag);
84
+ console.log(`Deployment to ${toEnv} completed successfully!`);
85
+ console.log(`\n${serviceName} image promotion completed successfully!`);
86
+ } catch (error) {
87
+ console.error('\nImage promotion failed:', error.message);
88
+ throw error;
89
+ }
90
+ }
91
+ /**
92
+ * Test deployment only (no image building)
93
+ * @param {string} serviceName - Name of microservice
94
+ * @param {string} environment - Target environment
95
+ * @param {string} tag - Version tag
96
+ */
97
+ export async function testMicroserviceDeploy(
98
+ serviceName,
99
+ environment = 'dev',
100
+ tag = 'v0.1.0',
101
+ ) {
102
+ console.log(`Starting ${serviceName} test deployment...`);
103
+ console.log(`Environment: ${environment}`);
104
+ console.log(`Tag: ${tag}`);
105
+ console.log('==========================================');
106
+ try {
107
+ console.log(`\nTesting deployment to ${environment}...`);
108
+ await deployMicroservice(serviceName, serviceName, environment, tag);
109
+ console.log(`Test deployment to ${environment} completed successfully!`);
110
+ } catch (error) {
111
+ console.error('\nTest deployment failed:', error.message);
112
+ throw error;
113
+ }
114
+ }
115
+ /**
116
+ * Test Docker build and publish only (no deployment)
117
+ * @param {string} serviceName - Name of microservice
118
+ */
119
+ export async function testMicroservicePublish(serviceName) {
120
+ console.log(`Starting ${serviceName} test publish...`);
121
+ console.log('==========================================');
122
+ try {
123
+ const gitSha = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
124
+ console.log(`Git SHA: ${gitSha}`);
125
+ console.log('\nTesting Docker build and publish...');
126
+ await publishMicroservice(gitSha, {
127
+ imageName: serviceName,
128
+ });
129
+ console.log('Test publish completed successfully!');
130
+ } catch (error) {
131
+ console.error('\nTest publish failed:', error.message);
132
+ throw error;
133
+ }
134
+ }
135
+
136
+ /////
137
+
138
+ export async function deployMicroservice(
139
+ appName,
140
+ helmPath,
141
+ environment = '',
142
+ tag = null,
143
+ ) {
144
+ if (!appName || !helmPath) {
145
+ throw new Error(
146
+ 'Missing required parameters. Usage: deployMicroservice(appName, helmPath, [environment], [tag])',
147
+ );
148
+ }
149
+
150
+ let deployTag = tag;
151
+ if (!deployTag) {
152
+ deployTag =
153
+ process.env.VERSION_TAG ||
154
+ process.env.BITBUCKET_TAG ||
155
+ (process.env.BITBUCKET_BUILD_NUMBER
156
+ ? `build-${process.env.BITBUCKET_BUILD_NUMBER}`
157
+ : null);
158
+ }
159
+
160
+ if (!deployTag) {
161
+ throw new Error(
162
+ 'No tag specified and no default tag available from environment variables',
163
+ );
164
+ }
165
+
166
+ const repoName = `gitops-${environment}`;
167
+ const filePath = `dp/${helmPath}/${appName}.values.yaml`;
168
+
169
+ const gitopsBranch =
170
+ environment === 'dev' || environment === 'uat'
171
+ ? process.env.GITOPS_BRANCH || 'master'
172
+ : process.env.GITOPS_BRANCH || 'main';
173
+
174
+ let repoUrl;
175
+ if (environment === 'dev' && process.env.GITOPS_REP_TOKEN) {
176
+ repoUrl = `https://x-token-auth:${process.env.GITOPS_REP_TOKEN}@bitbucket.org/dp-cloud/${repoName}.git`;
177
+ } else if (
178
+ process.env.BITBUCKET_USERNAME &&
179
+ process.env.BITBUCKET_APP_PASSWORD
180
+ ) {
181
+ repoUrl = `https://${process.env.BITBUCKET_USERNAME}:${process.env.BITBUCKET_APP_PASSWORD}@bitbucket.org/dp-cloud/${repoName}.git`;
182
+ } else {
183
+ throw new Error(
184
+ 'No authentication credentials found for GitOps repository',
185
+ );
186
+ }
187
+
188
+ console.log('=========================================');
189
+ console.log('Microservice Deployment Configuration');
190
+ console.log('=========================================');
191
+ console.log(`Application: ${appName}`);
192
+ console.log(`Environment: ${environment}`);
193
+ console.log(`Tag: ${deployTag}`);
194
+ console.log(`GitOps Repo: ${repoName}`);
195
+ console.log(`Values File: ${filePath}`);
196
+ console.log(`Branch: ${gitopsBranch}`);
197
+ console.log('=========================================');
198
+
199
+ try {
200
+ console.log('Cloning GitOps repository...');
201
+ await $`git clone --depth 1 ${repoUrl} ${repoName}`;
202
+
203
+ console.log('Checking if directory exists...');
204
+ await $`ls -la ${repoName}`;
205
+
206
+ console.log(`Changing to directory: ${repoName}`);
207
+ const originalDir = process.cwd();
208
+ process.chdir(repoName);
209
+
210
+ console.log('Verifying directory change...');
211
+ const newDir = process.cwd();
212
+ console.log(`Original: ${originalDir}`);
213
+ console.log(`New: ${newDir}`);
214
+
215
+ await $`cd ${repoName} && git config user.email "pipeline@bitbucket.org"`;
216
+ await $`cd ${repoName} && git config user.name "Bitbucket Pipeline"`;
217
+
218
+ console.log(`Attempting to checkout branch: ${gitopsBranch}`);
219
+ await $`cd ${repoName} && git checkout ${gitopsBranch}`;
220
+
221
+ const fullFilePath = path.resolve(filePath);
222
+ console.log(`Looking for file at: ${fullFilePath}`);
223
+ if (!fs.existsSync(fullFilePath)) {
224
+ throw new Error(
225
+ `Values file ${filePath} does not exist in ${repoName} on branch ${gitopsBranch}`,
226
+ );
227
+ }
228
+
229
+ console.log(`Updating image tag to ${deployTag}...`);
230
+
231
+ let fileContent = fs.readFileSync(fullFilePath, 'utf8');
232
+
233
+ const tagRegex = /^ {2}tag: .*/gm;
234
+ const newContent = fileContent.replace(tagRegex, ` tag: ${deployTag}`);
235
+
236
+ if (fileContent === newContent) {
237
+ console.log(`No changes detected - tag is already ${deployTag}`);
238
+ return;
239
+ }
240
+
241
+ fs.writeFileSync(fullFilePath, newContent, 'utf8');
242
+
243
+ console.log('Changes to be committed:');
244
+ await $`cd ${repoName} && git diff ${filePath}`;
245
+
246
+ await $`cd ${repoName} && git add ${filePath}`;
247
+ await $`cd ${repoName} && git commit -m "Update ${appName} image tag to ${deployTag} in ${environment} environment"`;
248
+
249
+ console.log('Pushing changes to GitOps repository...');
250
+ await $`cd ${repoName} && git push origin ${gitopsBranch}`;
251
+
252
+ console.log('=========================================');
253
+ console.log(
254
+ `✅ Successfully deployed ${appName}:${deployTag} to ${environment}`,
255
+ );
256
+ console.log('=========================================');
257
+ } catch (error) {
258
+ console.error('Error during microservice deployment:', error.message);
259
+ throw error;
260
+ } finally {
261
+ try {
262
+ process.chdir('..');
263
+ await $`rm -rf ${repoName}`;
264
+ } catch (cleanupError) {
265
+ console.warn(
266
+ 'Warning: Failed to clean up temporary directory:',
267
+ cleanupError.message,
268
+ );
269
+ }
270
+ }
271
+ }
272
+
273
+ export default deployMicroservice;