@docupace/pipeline 1.0.5-alpha.8 → 1.0.5

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.5-alpha.8",
3
+ "version": "1.0.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -32,5 +32,9 @@
32
32
  "eslint-plugin-prettier": "^5.2.1",
33
33
  "globals": "^15.14.0",
34
34
  "prettier": "^3.4.2"
35
+ },
36
+ "scripts": {
37
+ "test-deploy-core": "dp-next-cli deploy-core-pipeline dev template",
38
+ "test-deploy-from-site": "dp-next-cli deploy-from-site --from-env dev --to-env dev --from-site financialdev --to-site template --deploy-core true"
35
39
  }
36
40
  }
@@ -9,12 +9,36 @@ import { zodResponseFormat } from 'openai/helpers/zod';
9
9
  import z from 'zod';
10
10
  import dotenv from 'dotenv';
11
11
  dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
12
+ import { exec } from 'child_process';
12
13
 
13
14
  const program = new Command();
14
15
 
15
16
  const args = process.argv;
16
17
 
17
- const DIR = path.resolve(process.cwd());
18
+ async function createChangesetDirectory() {
19
+ try {
20
+ // Use path.join to build a safe path to `.changeset`
21
+ const targetDir = path.join(process.cwd(), '.changeset');
22
+
23
+ // Create the directory recursively if it doesn't exist
24
+ await fs.promises.mkdir(targetDir, { recursive: true });
25
+ } catch (error) {
26
+ console.error('Error creating .changeset directory:', error);
27
+ }
28
+ }
29
+
30
+
31
+ function run(command) {
32
+ return new Promise((resolve, reject) => {
33
+ exec(command, { shell: true }, (error, stdout, stderr) => {
34
+ if (error) {
35
+ reject(error);
36
+ } else {
37
+ resolve({ stdout, stderr });
38
+ }
39
+ });
40
+ });
41
+ }
18
42
 
19
43
  async function fileExists(filePath) {
20
44
  try {
@@ -155,7 +179,8 @@ program
155
179
 
156
180
  try {
157
181
  // Step 1: Get the current branch name
158
- const branchName = (await $`git branch --show-current`).stdout.trim();
182
+ const { stdout: branchStdout } = await run(`git branch --show-current`);
183
+ const branchName = branchStdout.trim();
159
184
  console.log(`Current branch: ${branchName}`);
160
185
 
161
186
  // Step 2: Extract Jira ticket number (e.g., matching JIRA-1234 pattern)
@@ -195,6 +220,7 @@ program
195
220
  `Generating release notes for ${jiraTicketId} - ${jiraSummary}`,
196
221
  );
197
222
 
223
+ // Step 4: Use OpenAI to generate release notes
198
224
  const openaiClient = new OpenAI({
199
225
  apiKey: process.env['OPENAI_API_KEY'],
200
226
  });
@@ -231,22 +257,23 @@ program
231
257
  console.log(`Change type: ${change_type}`);
232
258
  console.log(`Release note: ${summary}`);
233
259
 
234
- // Step 5: Create a changeset using pnpm
260
+ // Step 5: Create a changeset .md file
235
261
  const changesetContent = `---
236
262
  "${componentName}": ${change_type}
237
263
  ---
238
264
 
239
265
  ${summary} [${jiraTicketId}]
240
- `;
266
+ `;
241
267
  const changesetFile = `./.changeset/${jiraTicketId}.md`;
242
268
 
243
- await $`mkdir -p ./.changeset`;
269
+ await createChangesetDirectory();
244
270
  await fs.promises.writeFile(changesetFile, changesetContent);
245
271
  console.log(`Created changeset file at ${changesetFile}`);
246
272
 
247
273
  console.log('Done!');
248
274
  } catch (err) {
249
275
  console.error(`Error: ${err.message}`);
276
+ process.exit(1);
250
277
  }
251
278
  });
252
279
 
@@ -0,0 +1,59 @@
1
+ import groovy.json.JsonSlurper
2
+
3
+ import hudson.model.*;
4
+
5
+ def build = Thread.currentThread().executable
6
+ def params = build.getEnvironment(listener)
7
+
8
+ def site = params.get('site')
9
+ def server = "jboss7"
10
+ def environment = params.get('environment')
11
+
12
+ def buildFile=new File("D:\\Docupace\\CustomFolders\\" + environment + "\\" + site + "\\" + server + "\\buildApp.properties")
13
+
14
+ List<String> jboss7controllerResult = []
15
+
16
+ def controllersReport = new File("D:\\Docupace\\controllers_report.json")
17
+ def inputJSON = new JsonSlurper().parseText(controllersReport.text)
18
+ def props = new Properties()
19
+ buildFile.withInputStream { stream -> props.load(stream) }
20
+ def webRootContext = props["web.root.context"]
21
+ if (webRootContext == null || webRootContext =="core") webRootContext = props["docupace.config.client.id"].toLowerCase()
22
+
23
+ webRootContext = webRootContext.replaceFirst('_sso','')
24
+ def map = inputJSON.'content'
25
+
26
+ def Boolean enabled
27
+
28
+ map.each {k,v ->
29
+ v.each{vk, vv -> if (vk == webRootContext ) {
30
+ enabled = false
31
+ if(vv.deploymentsProperty.size()>0) enabled = vv.deploymentsProperty[0].isEnabled
32
+ if (enabled) jboss7controllerResult.push(k)
33
+ else jboss7controllerResult << k
34
+ }
35
+ }
36
+ }
37
+
38
+ if (jboss7controllerResult.size()>0) {
39
+ if (environment == 'preprod') {
40
+ jboss7controllerResult.remove("dev-corjbdc10.corp.docupace.com")
41
+ jboss7controllerResult.remove("usdoc-s-jbdc1.corp.docupace.com")
42
+ }
43
+ if (environment == 'dev') {
44
+ jboss7controllerResult.remove("qa-corjbdc10.corp.docupace.com")
45
+ jboss7controllerResult.remove("qa-corjbdc20.corp.docupace.com")
46
+ jboss7controllerResult.remove("usdoc-s-jbdc2.corp.docupace.com")
47
+ }
48
+ }
49
+
50
+ println jboss7controllerResult[0]
51
+
52
+ env.jboss7controller = jboss7controllerResult[0]
53
+
54
+ def serverGroup = inputJSON.'content'."${jboss7controller}"."${webRootContext}".'serverGroup'
55
+
56
+ println serverGroup
57
+
58
+ params.put("serverGroup", serverGroup)
59
+
package/src/next/cli.js CHANGED
@@ -4,7 +4,7 @@ import { Command } from 'commander';
4
4
  import { pull } from './pull.js';
5
5
  import { sonar } from './sonar.js';
6
6
  import { publish } from './publish.js';
7
- import { deploy, deployFromSite } from './deploy.js';
7
+ import { deployFromSite } from './deploy.js';
8
8
  import { deployTemplate } from './deploy.js';
9
9
  import { copy } from './copy.js';
10
10
  import dotenv from 'dotenv';
@@ -15,7 +15,8 @@ const program = new Command();
15
15
 
16
16
  // GIT SHA computation
17
17
  import { $ } from 'zx';
18
- import {triggerJenkinsJob} from "./jenkins.js";
18
+ import { deployCore } from './jenkins.js';
19
+ import { runPipeline } from './pipline.js';
19
20
  const GIT_SHA = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
20
21
 
21
22
  program
@@ -46,9 +47,10 @@ program
46
47
  .requiredOption('-t, --to-env <toEnv>', 'Target environment')
47
48
  .requiredOption('-F, --from-site <fromSite>', 'Source site')
48
49
  .requiredOption('-T, --to-site <toSite>', 'Target site')
50
+ .option('-c, --deploy-core <deployCore>', 'Deploy Core', 'false')
49
51
  .action(async (options) => {
50
- const { fromEnv, fromSite, toEnv, toSite } = options;
51
- await deployFromSite(fromEnv, fromSite, toEnv, toSite);
52
+ const { fromEnv, fromSite, toEnv, toSite, deployCore } = options;
53
+ await deployFromSite(fromEnv, fromSite, toEnv, toSite, deployCore);
52
54
  });
53
55
 
54
56
  program
@@ -61,8 +63,24 @@ program
61
63
  program
62
64
  .command('deploy-core <env> <site>')
63
65
  .description('Deploy Core')
64
- .action(async (env, site) => {
65
- await triggerJenkinsJob();
66
+ .option('-l, --liquibase <liquibase>', 'Application name', 'true')
67
+ .option('-b, --branch <branch>', 'Branch', 'master')
68
+ .action(async (env, site, options) => {
69
+ await deployCore(env, site, options.liquibase, options.branch);
70
+ });
71
+
72
+ program
73
+ .command('deploy-core-pipeline <env> <site>')
74
+ .description('Deploy Core')
75
+ .option('-l, --liquibase <liquibase>', 'Application name', 'true')
76
+ .option('-b, --branch <branch>', 'Branch', 'master')
77
+ .action(async (env, site, options) => {
78
+ await runPipeline('site-deploy', 'deploy-core', 'main', {
79
+ ENV: env,
80
+ SITE: site,
81
+ LIQUIBASE: options.liquibase,
82
+ BRANCH: options.branch,
83
+ });
66
84
  });
67
85
 
68
86
  program
@@ -1,5 +1,29 @@
1
1
  export const environments = ['dev', 'qa', 'uat', 'stage', 'prod'];
2
2
 
3
+ export const environmentConfig = {
4
+ dev: {
5
+ newUrl: (site) => `${site}.dev.dpai.tech`,
6
+ oldUrl: (site) => `https://www.dev.docupaceinc.com/${site}`,
7
+ },
8
+ qa: {
9
+ newUrl: (site) => `${site}.qa.dpai.tech`,
10
+ oldUrl: (site) => `https://www.preprod.docupaceinc.com/${site}`,
11
+ },
12
+ uat: {
13
+ newUrl: (site) => `${site}.qa.dpai.tech`,
14
+ oldUrl: (site) => `https://www2.paperout.com/${site}_uat`,
15
+ },
16
+ stage: {
17
+ newUrl: (site) => `${site}.stage.dpai.tech`,
18
+ oldUrl: (site) => `https://www2.paperout.com/${site}_staging`,
19
+ },
20
+
21
+ prod: {
22
+ newUrl: (site) => `${site}.dpai.tech`,
23
+ oldUrl: (site) => `https://www2.paperout.com/${site}`,
24
+ },
25
+ };
26
+
3
27
  export const appInfo = {
4
28
  'graphql-server': {
5
29
  repositoryName: 'docupace-graphql-server',
@@ -35,7 +59,14 @@ export const getBitbucketInfo = () => {
35
59
  return {
36
60
  username: process.env.BITBUCKET_USERNAME,
37
61
  password: process.env.BITBUCKET_APP_PASSWORD,
62
+ workspace: 'dp-cloud',
38
63
  };
39
64
  };
40
65
 
66
+ export const getRepUrl = (repoName) => {
67
+ const { username, password, workspace } = getBitbucketInfo();
68
+ const repoUrl = `https://${username}:${password}@bitbucket.org/${workspace}/${repoName}.git`;
69
+ return repoUrl;
70
+ };
71
+
41
72
  export const jfRegistryUrl = 'registry.paperout.com';
@@ -3,8 +3,11 @@ import path from 'path';
3
3
  import yaml from 'js-yaml';
4
4
  import { $ } from 'zx';
5
5
  import lodash from 'lodash';
6
- import { appInfo, getBitbucketInfo } from './config.js';
6
+ import { appInfo, environmentConfig, getRepUrl } from './config.js';
7
7
  import { copy } from './copy.js';
8
+ import axios from 'axios';
9
+ import { runPipeline } from './pipline.js';
10
+ import { deployCore } from './jenkins.js';
8
11
 
9
12
  /**
10
13
  * Clone the repository, read the YAML file, and return the parsed data and file paths.
@@ -21,8 +24,7 @@ function getRepoLocalPath(env) {
21
24
 
22
25
  async function cloneEnvRepository(env) {
23
26
  const { repoPath, repoName } = getRepoLocalPath(env);
24
- const { username, password } = getBitbucketInfo();
25
- const repoUrl = `https://${username}:${password}@bitbucket.org/dp-cloud/${repoName}.git`;
27
+ const repoUrl = getRepUrl(repoName);
26
28
  console.log(`Cloning repository: ${repoName}`);
27
29
  await $`git clone ${repoUrl}`;
28
30
  console.log(`Repository cloned to: ${repoPath}`);
@@ -57,7 +59,7 @@ async function readDeployConfig(application, env, site) {
57
59
  export async function cloneAndReadDeployConfig(application, env, site) {
58
60
  const repoPath = await cloneEnvRepository(env);
59
61
 
60
- const { deployConfig, deployConfigFilePath } = readDeployConfig(
62
+ const { deployConfig, deployConfigFilePath } = await readDeployConfig(
61
63
  application,
62
64
  env,
63
65
  site,
@@ -107,7 +109,7 @@ export async function pushDeployConfig(repoPath, commitMessage) {
107
109
  console.log('📌 Committing changes to the repository...');
108
110
  await $`cd ${repoPath} && git config user.email "gitops@docupace.com"`;
109
111
  await $`cd ${repoPath} && git add .`;
110
- await $`cd ${repoPath} && git commit -m "${commitMessage}"`;
112
+ await $`cd ${repoPath} && git commit -m ${commitMessage}`;
111
113
  await $`cd ${repoPath} && git push`;
112
114
  console.log('✅ Changes pushed to the remote repository.');
113
115
 
@@ -187,7 +189,72 @@ export async function deploy(application, site) {
187
189
  console.log('Deployment completed successfully.');
188
190
  }
189
191
 
190
- export async function deployFromSite(fromEnv, fromSite, toEnv, toSite) {
192
+ async function getAppInfo(coreUrl) {
193
+ const url = `${coreUrl}/b2b/rest/v1/appinfo`;
194
+ return await axios.get(url);
195
+ }
196
+
197
+ function getBranchNameFromVersion(version) {
198
+ const match = version.match(/(\d+\.\d+\.[^\-]+)(?:-(\d+)-g([a-f0-9]+))?/);
199
+
200
+ if (!match) {
201
+ throw new Error(`Invalid version format: ${version}`);
202
+ }
203
+
204
+ const fullVersion = match[1]; // e.g., 24.19.19, 24.20.alpha3, etc.
205
+ const customBranch = match[2]; // e.g., 4, 58, or 0
206
+ const commitHash = match[3]; // e.g., 668edef58e, 634cf6c904, etc.
207
+
208
+ // If "alpha" is in the version or customBranch is non-zero, use the commit hash
209
+ if (fullVersion.includes('alpha') || (customBranch && customBranch !== '0')) {
210
+ return commitHash;
211
+ }
212
+
213
+ // Otherwise, use the full version
214
+ return fullVersion;
215
+ }
216
+
217
+ async function getCoreBranchName(fromEnv, fromSite) {
218
+ const envConfig = environmentConfig[fromEnv];
219
+ const oldUrl = envConfig.oldUrl(fromSite);
220
+ const newUrl = envConfig.newUrl(fromSite) + '/core';
221
+
222
+ let appinfo;
223
+ try {
224
+ appinfo = await getAppInfo(oldUrl);
225
+ } catch {
226
+ try {
227
+ appinfo = await getAppInfo(newUrl);
228
+ } catch {
229
+ throw new Error('Cannot get app info');
230
+ }
231
+ }
232
+
233
+ const coreVersion = appinfo.data.core.version;
234
+ const branchName = getBranchNameFromVersion(coreVersion);
235
+ return branchName;
236
+ }
237
+
238
+ export async function deployFromSite(
239
+ fromEnv,
240
+ fromSite,
241
+ toEnv,
242
+ toSite,
243
+ isDeployCore,
244
+ ) {
245
+ if (isDeployCore === 'true') {
246
+ const branchName = await getCoreBranchName(fromEnv, fromSite);
247
+ await deployCore(toEnv, toSite, 'true', branchName);
248
+
249
+ // To be added back when splitting between on prem and in cloud
250
+ /*await runPipeline('site-deploy', 'deploy-core', 'main', {
251
+ ENV: toEnv,
252
+ SITE: toSite,
253
+ LIQUIBASE: 'true',
254
+ BRANCH: branchName,
255
+ });*/
256
+ }
257
+
191
258
  // Clone repositories for source and target environments
192
259
  const fromRepoPath = await cloneEnvRepository(fromEnv);
193
260
  const toRepoPath =
@@ -4,19 +4,21 @@ const JENKINS_URL = 'http://devops.corp.docupace.com';
4
4
  const JOB_NAME = 'auto-deploy-site';
5
5
  const USERNAME = process.env.CORE_JENKINS_USERNAME;
6
6
  const API_TOKEN = process.env.CORE_JENKINS_API_TOKEN;
7
- const PARAMETERS = {
8
- environment: 'dev',
9
- site: 'TEMPLATE',
10
- runLiquibase: 'true',
11
- };
12
7
 
13
- export async function triggerJenkinsJob() {
8
+ export async function deployCore(env, site, liquibase, gitBranch) {
9
+ const parameters = {
10
+ environment: env,
11
+ site: site.toUpperCase(),
12
+ runLiquibase: liquibase,
13
+ gitBranch,
14
+ };
15
+
14
16
  try {
15
17
  // Trigger the job
16
18
  console.log('Triggering Jenkins job...');
17
19
  const buildResponse = await axios.post(
18
20
  `${JENKINS_URL}/job/${JOB_NAME}/buildWithParameters`,
19
- new URLSearchParams(PARAMETERS),
21
+ new URLSearchParams(parameters),
20
22
  {
21
23
  auth: { username: USERNAME, password: API_TOKEN },
22
24
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
@@ -34,7 +36,6 @@ export async function triggerJenkinsJob() {
34
36
  auth: { username: USERNAME, password: API_TOKEN },
35
37
  });
36
38
  jobUrl = queueResponse.data.executable?.url || null;
37
- jobUrl = jobUrl.replaceAll('https', 'http');
38
39
 
39
40
  if (!jobUrl) {
40
41
  console.log('Waiting for job to start...');
@@ -42,6 +43,8 @@ export async function triggerJenkinsJob() {
42
43
  }
43
44
  }
44
45
 
46
+ jobUrl = jobUrl.replaceAll('https', 'http');
47
+
45
48
  console.log(`Job started at: ${jobUrl}`);
46
49
 
47
50
  // Stream console output
@@ -0,0 +1,94 @@
1
+ import axios from 'axios';
2
+ import { getBitbucketInfo } from './config.js';
3
+
4
+ const BITBUCKET_BASE_URL = 'https://api.bitbucket.org/2.0';
5
+
6
+ async function triggerPipelineByName(
7
+ repository,
8
+ pipelineName,
9
+ branch,
10
+ variables,
11
+ ) {
12
+ const { username, password, workspace } = getBitbucketInfo();
13
+ const url = `${BITBUCKET_BASE_URL}/repositories/${workspace}/${repository}/pipelines/`;
14
+ const auth = {
15
+ username: username,
16
+ password: password,
17
+ };
18
+
19
+ const payload = {
20
+ target: {
21
+ type: 'pipeline_ref_target',
22
+ ref_type: 'branch',
23
+ ref_name: branch,
24
+ selector: {
25
+ type: 'custom',
26
+ pattern: pipelineName,
27
+ },
28
+ },
29
+ variables: Object.entries(variables).map(([key, value]) => ({
30
+ key,
31
+ value,
32
+ secured: false, // Set to true if the variable should be secure
33
+ })),
34
+ };
35
+
36
+ try {
37
+ const response = await axios.post(url, payload, { auth });
38
+ return response.data;
39
+ } catch (error) {
40
+ console.error(
41
+ 'Error triggering custom pipeline:',
42
+ error.response?.data || error.message,
43
+ );
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ async function getPipelineStatus(repository, pipelineUUID) {
49
+ const { username, password, workspace } = getBitbucketInfo();
50
+ const url = `${BITBUCKET_BASE_URL}/repositories/${workspace}/${repository}/pipelines/${pipelineUUID}`;
51
+ const auth = {
52
+ username: username,
53
+ password: password,
54
+ };
55
+
56
+ try {
57
+ const response = await axios.get(url, { auth });
58
+ return response.data.state.name;
59
+ } catch (error) {
60
+ console.error(
61
+ 'Error fetching pipeline status:',
62
+ error.response?.data || error.message,
63
+ );
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ export async function runPipeline(repository, pipelineName, branch, variables) {
69
+ try {
70
+ console.log(
71
+ `Triggering custom pipeline: ${pipelineName} for branch: ${branch}...`,
72
+ );
73
+ const pipeline = await triggerPipelineByName(
74
+ repository,
75
+ pipelineName,
76
+ branch,
77
+ variables,
78
+ );
79
+ const pipelineUUID = pipeline.uuid;
80
+
81
+ console.log(`Pipeline triggered with UUID: ${pipelineUUID}`);
82
+ let status = '';
83
+ do {
84
+ console.log('Checking pipeline status...');
85
+ await new Promise((resolve) => setTimeout(resolve, 10000)); // Wait 10 seconds
86
+ status = await getPipelineStatus(repository, pipelineUUID);
87
+ console.log(`Pipeline status: ${status}`);
88
+ } while (['IN_PROGRESS', 'PENDING', 'RUNNING'].includes(status));
89
+
90
+ console.log(`Pipeline completed with status: ${status}`);
91
+ } catch (error) {
92
+ console.error('Error monitoring pipeline:', error.message);
93
+ }
94
+ }