@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.
- package/README.md +136 -0
- package/bin/azuread_update_callbacks_for_app.sh +58 -0
- package/bin/xray.sh +273 -0
- package/bitbucket-pipelines.yml +142 -0
- package/eslint.config.js +25 -2
- package/jest.config.js +15 -0
- package/next/execute.sh +8 -2
- package/package.json +23 -7
- package/src/auth0/alias.js +79 -0
- package/src/auth0/alias.test.js +172 -0
- package/src/auth0/cli.js +82 -0
- package/src/auth0/configure.js +902 -0
- package/src/auth0/configure.test.js +167 -0
- package/src/auth0/export.js +48 -0
- package/src/auth0/export.test.js +103 -0
- package/src/auth0/metadata.js +141 -0
- package/src/auth0/metadata.test.js +186 -0
- package/src/auth0/vault.js +60 -0
- package/src/auth0/vault.test.js +134 -0
- package/src/changeset/changelog-wiki.js +328 -0
- package/src/changeset/changeset-config.js +34 -0
- package/src/changeset/cli.js +151 -195
- package/src/changeset/create.js +215 -0
- package/src/changeset/package-util.js +87 -0
- package/src/changeset/util.js +43 -0
- package/src/changeset/validate.js +45 -1
- package/src/changeset/version.js +102 -0
- package/src/jenkins/setParams.groovy +1 -2
- package/src/jfrog/xray-scan.yaml +29 -0
- package/src/lib/merge-driver.js +204 -0
- package/src/merge/cli-merge-all.js +521 -0
- package/src/merge/cli-merge.js +572 -0
- package/src/merge/cli-resolve.js +399 -0
- package/src/merge/cli.js +38 -0
- package/src/merge/common.js +7 -0
- package/src/merge/merge-changelog.js +87 -0
- package/src/merge/merge-package.js +106 -0
- package/src/merge/merge-types.js +82 -0
- package/src/merge/pr.js +231 -0
- package/src/micro/cli.js +51 -0
- package/src/micro/ms-deploy.js +451 -0
- package/src/micro/ms-publish.js +186 -0
- package/src/next/alias.js +53 -0
- package/src/next/apache.js +81 -0
- package/src/next/cli.js +111 -15
- package/src/next/config.js +72 -7
- package/src/next/connect.js +226 -0
- package/src/next/copy.js +32 -1
- package/src/next/deploy.js +514 -75
- package/src/next/git-to-semver.js +79 -0
- package/src/next/haproxy.js +76 -0
- package/src/next/jenkins.js +3 -2
- package/src/next/jira.js +84 -0
- package/src/next/logger.js +17 -0
- package/src/next/pipline.js +0 -1
- package/src/next/publish.js +53 -30
- package/src/next/test.js +5 -0
- package/src/next/util.js +95 -0
- package/src/next/vault.js +45 -0
- package/terraform/apply.sh +41 -0
- package/terraform/import.sh +111 -0
- package/terraform/init.sh +78 -0
- package/terraform/plan.sh +95 -0
- package/terraform/validate.sh +17 -0
package/src/next/deploy.js
CHANGED
|
@@ -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 {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
|
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
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
157
|
-
const
|
|
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',
|
|
353
|
+
await cloneAndReadDeployConfig('graphql-server', env, site);
|
|
162
354
|
|
|
163
|
-
deployConfig.graphqlServer
|
|
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
|
|
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
|
|
181
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
application
|
|
276
|
-
|
|
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
|
-
|
|
728
|
+
toEnv,
|
|
729
|
+
toSite,
|
|
730
|
+
'app',
|
|
279
731
|
);
|
|
280
732
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
292
|
-
for (const attribute of appDetails.deployAttributes) {
|
|
293
|
-
const sourceValue = lodash.get(fromDeployConfig, attribute);
|
|
739
|
+
console.log('appinfoLocal', appInfoLocal);
|
|
294
740
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|