@docupace/pipeline 1.0.39-matttest.23 → 1.0.39-matttest.25

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.39-matttest.23",
3
+ "version": "1.0.39-matttest.25",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,6 +23,7 @@
23
23
  "commander": "^12.1.0",
24
24
  "dotenv": "^16.4.7",
25
25
  "fs-extra": "^11.3.0",
26
+ "inquirer": "^12.6.1",
26
27
  "js-yaml": "^4.1.0",
27
28
  "lodash": "^4.17.21",
28
29
  "openai": "^4.76.3",
@@ -42,6 +43,7 @@
42
43
  "test-deploy-core": "dp-next-cli deploy-core-pipeline dev template",
43
44
  "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",
44
45
  "test-run-pipline": "dp-next-cli run-pipeline -r docupace-next-e2e -b master -p run-tests-qa",
45
- "test-validate": "dp-changset-cli validate"
46
+ "test-validate": "dp-changset-cli validate",
47
+ "test-create": "dp-changset-cli create-changeset"
46
48
  }
47
49
  }
@@ -5,16 +5,16 @@ import crypto from 'crypto';
5
5
 
6
6
  import {
7
7
  cloneEnvRepository,
8
- readDeployConfig,
9
- pushDeployConfig,
10
8
  cloneRepository,
9
+ pushDeployConfig,
10
+ readDeployConfig,
11
11
  } from '../next/deploy.js';
12
12
 
13
13
  import { getVaultSecrets, updateVaultSecrets } from './vault.js';
14
14
  import {
15
- getVaultSecretsConfigs,
16
- environmentConfig,
17
15
  coreSiteEnvMap,
16
+ environmentConfig,
17
+ getVaultSecretsConfigs,
18
18
  } from '../next/config.js';
19
19
 
20
20
  async function ensureDirectoryExists(dirPath) {
@@ -1,11 +1,10 @@
1
1
  import fs from 'fs-extra';
2
2
  import axios from 'axios';
3
3
  import path from 'path';
4
- import { cloneRepository } from '../next/deploy.js';
4
+ import { cloneRepository, pushDeployConfig } from '../next/deploy.js';
5
5
  import { getAuth0AuthToken } from './configure.js';
6
6
  import { getVaultSecrets } from './vault.js';
7
7
  import { getVaultSecretsConfigs } from '../next/config.js';
8
- import { pushDeployConfig } from '../next/deploy.js';
9
8
 
10
9
  export async function getAuth0Metadata(site, env) {
11
10
  try {
@@ -1,146 +1,71 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import { $ } from 'zx';
4
- import path from 'path';
5
4
  import { validateChangesets } from './validate.js';
6
- import fs from 'fs';
7
- import OpenAI from 'openai';
8
- import { zodResponseFormat } from 'openai/helpers/zod';
9
- import z from 'zod';
10
5
  import dotenv from 'dotenv';
11
- import { makeJiraRequest, updateComponentVersion } from '../next/jira.js';
6
+ import { isMonoRepo, setGitInfo } from '../next/util.js';
7
+ import { updateJiraWithVersion, versionCommand } from './version.js';
8
+ import { createChangeset } from './create.js';
9
+
12
10
  dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
13
- import { setGitInfo } from '../next/util.js';
14
11
 
15
12
  const program = new Command();
16
13
 
17
14
  const args = process.argv;
18
15
 
19
- async function createChangesetDirectory() {
20
- try {
21
- // Use path.join to build a safe path to `.changeset`
22
- const targetDir = path.join(process.cwd(), '.changeset');
16
+ async function commitTagCommand(releases) {
17
+ console.log('📌 Checking for updates to commit...');
23
18
 
24
- // Create the directory recursively if it doesn't exist
25
- await fs.promises.mkdir(targetDir, { recursive: true });
26
- } catch (error) {
27
- console.error('Error creating .changeset directory:', error);
19
+ // Check if there are changes to commit
20
+ const status = (await $`git status --porcelain`).stdout.trim();
21
+ if (!status) {
22
+ console.log('✅ No changes to commit. Exiting.');
23
+ return;
28
24
  }
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
- }
42
25
 
43
- async function fileExists(filePath) {
44
- try {
45
- await fs.promises.access(filePath);
46
- return true;
47
- } catch {
48
- return false;
49
- }
50
- }
26
+ console.log('📌 Committing and tagging the new version...');
27
+ await setGitInfo();
28
+ await $`git add .`;
51
29
 
52
- async function getMainPackage() {
53
- const mainModulePath = process.cwd();
54
- if (mainModulePath) {
55
- const packageJsonPath = path.join(mainModulePath, 'package.json');
56
- if (await fileExists(packageJsonPath)) {
57
- const fileContent = await fs.promises.readFile(packageJsonPath, 'utf-8');
58
- const packageJson = JSON.parse(fileContent);
59
- return packageJson;
60
- }
30
+ if (isMonoRepo()) {
31
+ await commitTagMono(releases);
32
+ } else {
33
+ await commitTag();
61
34
  }
62
- throw new Error('Main package.json not found');
63
35
  }
64
36
 
65
- const changeSetPackage = `{
66
- "name": "changeset-package",
67
- "version": "1.0.0",
68
- "description": "",
69
- "scripts": {
70
- },
71
- "type": "module",
72
- "keywords": [],
73
- "author": "",
74
- "license": "ISC",
75
- "dependencies": {},
76
- "devDependencies": {
77
- "@changesets/cli": "^2.27.10"
78
- }
37
+ export function getAppReleases(releases) {
38
+ const appReleases = releases.filter((release) =>
39
+ release.name.endsWith('-app'),
40
+ );
41
+ return appReleases;
79
42
  }
80
- `;
81
-
82
- async function versionCommand(closeVersion = false) {
83
- console.log('📦 Running Changeset versioning...');
84
- await setGitInfo();
85
- await $`mv package.json package.json.bak`;
86
- await $`mv pnpm-lock.yaml pnpm-lock.yaml.bak`;
87
- await fs.promises.writeFile('package.json', changeSetPackage);
88
- await $`pnpm install`;
89
- await $`rm package.json pnpm-lock.yaml`;
90
- await $`mv package.json.bak package.json`;
91
- await $`mv pnpm-lock.yaml.bak pnpm-lock.yaml`;
92
- if (closeVersion) {
93
- await $`pnpm changeset pre exit`;
94
- }
95
43
 
96
- await $`pnpm changeset version`;
44
+ function getAppNameFromPackageName(packageName) {
45
+ const appName = packageName.includes('/')
46
+ ? packageName.split('/')[1]
47
+ : packageName;
97
48
 
98
- // Read the updated package.json to get the new version and package name
99
- const packageJson = JSON.parse(
100
- await fs.promises.readFile('package.json', 'utf-8'),
101
- );
102
- const version = packageJson.version;
103
- const packageName = packageJson.name;
49
+ return appName;
50
+ }
104
51
 
105
- if (closeVersion) {
106
- return { version, packageName };
52
+ async function commitTagMono(releases) {
53
+ let versions = [];
54
+ for (const release of getAppReleases(releases)) {
55
+ const newVersion = release.newVersion;
56
+ const appName = getAppNameFromPackageName(release.name);
57
+ versions.push(`${appName}@${newVersion}`);
107
58
  }
108
-
109
- // Get the last commit message and extract Jira ticket
110
- const lastCommit = (await $`git log -1 --pretty=%B`).stdout.trim();
111
- console.log(`Last commit '${lastCommit}' being processed to get Jira ticket`);
112
-
113
- const jiraTicketMatch = lastCommit.match(/([A-Z]+-\d+)/);
114
- const jiraTicketId = jiraTicketMatch ? jiraTicketMatch[0] : null;
115
-
116
- // If we have all the necessary data, update the component version in Jira
117
- if (jiraTicketId && version && packageName) {
118
- try {
119
- await updateComponentVersion(jiraTicketId, packageName, version);
120
- } catch (error) {
121
- console.error(
122
- `Failed to update Jira component version: ${error.message}`,
123
- );
124
- }
59
+ await $`git commit -m "Version bump to ${versions.join(',')} and changelog"`;
60
+ for (const newVersion of versions) {
61
+ await $`git tag v${newVersion}`;
62
+ console.log(`✅ Version ${newVersion} tagged.`);
125
63
  }
126
-
127
- console.log('✅ Changeset versioning complete.');
128
- return { version, packageName };
64
+ await $`git push origin --tags`;
65
+ console.log(`✅ Version pushed.`);
129
66
  }
130
67
 
131
- async function commitTagCommand() {
132
- console.log('📌 Checking for updates to commit...');
133
-
134
- // Check if there are changes to commit
135
- const status = (await $`git status --porcelain`).stdout.trim();
136
- if (!status) {
137
- console.log('✅ No changes to commit. Exiting.');
138
- return;
139
- }
140
-
141
- console.log('📌 Committing and tagging the new version...');
142
- await setGitInfo();
143
- await $`git add .`;
68
+ async function commitTag() {
144
69
  const newVersion = (
145
70
  await $`node -p "require('./package.json').version"`
146
71
  ).stdout.trim();
@@ -163,7 +88,7 @@ program
163
88
  try {
164
89
  await validateChangesets();
165
90
  console.log('✅ Changeset validation passed.');
166
- } catch (error) {
91
+ } catch {
167
92
  console.error(
168
93
  '❌ No changeset found. Please add a changeset to this PR. Please see more information at https://docupacellc.atlassian.net/wiki/spaces/PM/pages/1350893571/Creating+changesets+for+Docupace+Next',
169
94
  );
@@ -192,8 +117,9 @@ program
192
117
  .description('Run versioning and commit-and-tag in sequence')
193
118
  .action(async () => {
194
119
  console.log('📦 Running all steps...');
195
- await versionCommand();
196
- await commitTagCommand();
120
+ const releases = await versionCommand();
121
+ await updateJiraWithVersion(releases);
122
+ await commitTagCommand(releases);
197
123
  });
198
124
 
199
125
  program
@@ -201,15 +127,21 @@ program
201
127
  .description('Closes a version')
202
128
  .action(async () => {
203
129
  console.log('📦 Running all steps...');
204
- const { version } = await versionCommand(true);
205
- await commitTagCommand();
130
+ const releases = await versionCommand(true);
131
+ await commitTagCommand(releases);
206
132
 
207
- // Get current branch
208
133
  const { stdout: currentBranch } = await $`git rev-parse --abbrev-ref HEAD`;
209
134
 
210
- // Create and push release branch
211
- await $`git checkout -b release/${version}`;
212
- await $`git push origin release/${version}`;
135
+ if (isMonoRepo()) {
136
+ for (const release of getAppReleases(releases)) {
137
+ const appName = getAppNameFromPackageName(release.name);
138
+ await $`git checkout -b release/${appName}/${release.newVersion}`;
139
+ await $`git push origin release/${appName}/${release.newVersion}`;
140
+ }
141
+ } else {
142
+ await $`git checkout -b release/${releases[0].newVersion}`;
143
+ await $`git push origin release/${releases[0].newVersion}`;
144
+ }
213
145
 
214
146
  // Return to original branch
215
147
  await $`git checkout ${currentBranch.trim()}`;
@@ -227,100 +159,7 @@ program
227
159
  'Creates a changeset based on the current branch and Jira ticket information',
228
160
  )
229
161
  .action(async () => {
230
- const mainPackage = await getMainPackage();
231
- const componentName = mainPackage.name;
232
-
233
- try {
234
- // Step 1: Get the current branch name
235
- const branchName = (await $`git branch --show-current`).stdout.trim();
236
- console.log(`Current branch: ${branchName}`);
237
-
238
- // Step 2: Extract Jira ticket number (e.g., matching JIRA-1234 pattern)
239
- const jiraTicketMatch = branchName.match(/([A-Z]+-\d+)/);
240
- if (!jiraTicketMatch) {
241
- throw new Error('No Jira ticket found in the branch name.');
242
- }
243
- const jiraTicketId = jiraTicketMatch[0];
244
-
245
- const jiraBaseUrl = 'https://docupacellc.atlassian.net';
246
- const jiraEmail = process.env.JIRA_API_EMAIL;
247
- const jiraApiKey = process.env.JIRA_API_KEY;
248
- const jiraFullApiKey = `${jiraEmail}:${jiraApiKey}`;
249
- const jiraUrl = `${jiraBaseUrl}/rest/api/2/issue/${jiraTicketId}`;
250
- const jiraResponse = await fetch(jiraUrl, {
251
- headers: {
252
- Authorization: `Basic ${Buffer.from(jiraFullApiKey).toString('base64')}`,
253
- 'Content-Type': 'application/json',
254
- },
255
- });
256
-
257
- if (!jiraResponse.ok) {
258
- throw new Error(
259
- `Failed to fetch Jira ticket. Status: ${jiraResponse.status}`,
260
- );
261
- }
262
- const jiraTicketData = await jiraResponse.json();
263
-
264
- const jiraSummary = jiraTicketData.fields.summary;
265
-
266
- console.log(
267
- `Generating release notes for ${jiraTicketId} - ${jiraSummary}`,
268
- );
269
-
270
- const openaiClient = new OpenAI({
271
- apiKey: process.env['OPENAI_API_KEY'],
272
- });
273
-
274
- const ChangeRequestSchema = z.object({
275
- change_type: z.enum(['patch', 'minor']),
276
- summary: z.string(),
277
- jira_ticket_id: z.string(),
278
- });
279
-
280
- const openAiResponse = await openaiClient.chat.completions.create({
281
- model: 'gpt-4o-mini',
282
- messages: [
283
- {
284
- role: 'system',
285
- content: `Process JSON from a Jira ticket to generate simple maximum 2 sentence release note for the ticket and determine the change type for bug it should be a patch for new feature it should be a minor change`,
286
- },
287
- {
288
- role: 'user',
289
- content: JSON.stringify(jiraTicketData),
290
- },
291
- ],
292
- response_format: zodResponseFormat(
293
- ChangeRequestSchema,
294
- 'changeset_schema',
295
- ),
296
- temperature: 0,
297
- });
298
-
299
- const assistantResponse = openAiResponse.choices[0].message.content;
300
- const parsedResponse = JSON.parse(assistantResponse);
301
-
302
- const { change_type, summary } = parsedResponse;
303
- console.log(`Change type: ${change_type}`);
304
- console.log(`Release note: ${summary}`);
305
-
306
- // Step 5: Create a changeset .md file
307
- const changesetContent = `---
308
- "${componentName}": ${change_type}
309
- ---
310
-
311
- ${summary} [${jiraTicketId}]
312
- `;
313
- const changesetFile = `./.changeset/${jiraTicketId}.md`;
314
-
315
- await createChangesetDirectory();
316
- await fs.promises.writeFile(changesetFile, changesetContent);
317
- console.log(`Created changeset file at ${changesetFile}`);
318
-
319
- console.log('Done!');
320
- } catch (err) {
321
- console.error(`Error: ${err.message}`);
322
- process.exit(1);
323
- }
162
+ await createChangeset();
324
163
  });
325
164
 
326
165
  // Parse arguments
@@ -0,0 +1,182 @@
1
+ import { $ } from 'zx';
2
+ import OpenAI from 'openai';
3
+ import z from 'zod';
4
+ import { zodResponseFormat } from 'openai/helpers/zod';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { getAllAppPackages, isMonoRepo } from '../next/util.js';
8
+ import inquirer from 'inquirer';
9
+ import { getMainPackage, getPackage } from './util.js';
10
+
11
+ async function createChangesetDirectory() {
12
+ try {
13
+ // Use path.join to build a safe path to `.changeset`
14
+ const targetDir = path.join(process.cwd(), '.changeset');
15
+
16
+ // Create the directory recursively if it doesn't exist
17
+ await fs.promises.mkdir(targetDir, { recursive: true });
18
+ } catch (error) {
19
+ console.error('Error creating .changeset directory:', error);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Gets the current git branch name
25
+ */
26
+ async function getCurrentBranch() {
27
+ const branchName = (await $`git branch --show-current`).stdout.trim();
28
+ console.log(`Current branch: ${branchName}`);
29
+ return branchName;
30
+ }
31
+
32
+ /**
33
+ * Extracts a Jira ticket ID from a branch name
34
+ */
35
+ function extractJiraTicketId(branchName) {
36
+ const jiraTicketMatch = branchName.match(/([A-Z]+-\d+)/);
37
+ if (!jiraTicketMatch) {
38
+ throw new Error('No Jira ticket found in the branch name.');
39
+ }
40
+ return jiraTicketMatch[0];
41
+ }
42
+
43
+ /**
44
+ * Fetches Jira ticket data from Jira API
45
+ */
46
+ async function fetchJiraTicketData(jiraTicketId) {
47
+ const jiraBaseUrl = 'https://docupacellc.atlassian.net';
48
+ const jiraEmail = process.env.JIRA_API_EMAIL;
49
+ const jiraApiKey = process.env.JIRA_API_KEY;
50
+ const jiraFullApiKey = `${jiraEmail}:${jiraApiKey}`;
51
+ const jiraUrl = `${jiraBaseUrl}/rest/api/2/issue/${jiraTicketId}`;
52
+
53
+ const jiraResponse = await fetch(jiraUrl, {
54
+ headers: {
55
+ Authorization: `Basic ${Buffer.from(jiraFullApiKey).toString('base64')}`,
56
+ 'Content-Type': 'application/json',
57
+ },
58
+ });
59
+
60
+ if (!jiraResponse.ok) {
61
+ throw new Error(
62
+ `Failed to fetch Jira ticket. Status: ${jiraResponse.status}`,
63
+ );
64
+ }
65
+
66
+ return await jiraResponse.json();
67
+ }
68
+
69
+ /**
70
+ * Generates release notes using OpenAI
71
+ */
72
+ async function generateReleaseNotes(jiraTicketData) {
73
+ const openaiClient = new OpenAI({
74
+ apiKey: process.env['OPENAI_API_KEY'],
75
+ });
76
+
77
+ const ChangeRequestSchema = z.object({
78
+ change_type: z.enum(['patch', 'minor']),
79
+ summary: z.string(),
80
+ jira_ticket_id: z.string(),
81
+ });
82
+
83
+ const openAiResponse = await openaiClient.chat.completions.create({
84
+ model: 'gpt-4o-mini',
85
+ messages: [
86
+ {
87
+ role: 'system',
88
+ content: `Process JSON from a Jira ticket to generate simple maximum 2 sentence release note for the ticket and determine the change type for bug it should be a patch for new feature it should be a minor change`,
89
+ },
90
+ {
91
+ role: 'user',
92
+ content: JSON.stringify(jiraTicketData),
93
+ },
94
+ ],
95
+ response_format: zodResponseFormat(ChangeRequestSchema, 'changeset_schema'),
96
+ temperature: 0,
97
+ });
98
+
99
+ const assistantResponse = openAiResponse.choices[0].message.content;
100
+ return JSON.parse(assistantResponse);
101
+ }
102
+
103
+ /**
104
+ * Writes the changeset file
105
+ */
106
+ async function writeChangesetFile(
107
+ componentName,
108
+ jiraTicketId,
109
+ changeType,
110
+ summary,
111
+ ) {
112
+ const changesetContent = `---
113
+ "${componentName}": ${changeType}
114
+ ---
115
+
116
+ ${summary} [${jiraTicketId}]
117
+ `;
118
+ const changesetFile = `./.changeset/${jiraTicketId}.md`;
119
+
120
+ await createChangesetDirectory();
121
+ await fs.promises.writeFile(changesetFile, changesetContent);
122
+ console.log(`Created changeset file at ${changesetFile}`);
123
+ }
124
+
125
+ /**
126
+ * Main function to create a changeset
127
+ */
128
+
129
+ export async function createChangeset() {
130
+ if (isMonoRepo()) {
131
+ const appPackages = await getAllAppPackages();
132
+ const response = await inquirer.prompt([
133
+ {
134
+ type: 'list',
135
+ name: 'package',
136
+ message: 'Which package do you want to generate changesetfor?',
137
+ choices: appPackages.map((appPackage) => appPackage.name),
138
+ },
139
+ ]);
140
+
141
+ const selectedPackage = appPackages.find(
142
+ (appPackage) => appPackage.name == response.package,
143
+ );
144
+
145
+ const selectedPackageContent = await getPackage(selectedPackage.path);
146
+ await createChangesetForPackage(selectedPackageContent);
147
+ } else {
148
+ const mainPackage = await getMainPackage();
149
+ await createChangesetForPackage(mainPackage);
150
+ }
151
+ }
152
+
153
+ export async function createChangesetForPackage(mainPackage) {
154
+ const componentName = mainPackage.name;
155
+ try {
156
+ // Step 1: Get the current branch name
157
+ const branchName = await getCurrentBranch();
158
+
159
+ // Step 2: Extract Jira ticket number
160
+ const jiraTicketId = extractJiraTicketId(branchName);
161
+
162
+ // Step 3: Fetch Jira ticket data
163
+ const jiraTicketData = await fetchJiraTicketData(jiraTicketId);
164
+ const jiraSummary = jiraTicketData.fields.summary;
165
+ console.log(
166
+ `Generating release notes for ${jiraTicketId} - ${jiraSummary}`,
167
+ );
168
+
169
+ // Step 4: Generate release notes using OpenAI
170
+ const { change_type, summary } = await generateReleaseNotes(jiraTicketData);
171
+ console.log(`Change type: ${change_type}`);
172
+ console.log(`Release note: ${summary}`);
173
+
174
+ // Step 5: Create a changeset .md file
175
+ await writeChangesetFile(componentName, jiraTicketId, change_type, summary);
176
+
177
+ console.log('Done!');
178
+ } catch (err) {
179
+ console.error(`Error: ${err.message}`);
180
+ process.exit(1);
181
+ }
182
+ }
@@ -0,0 +1,27 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+
4
+ async function fileExists(filePath) {
5
+ try {
6
+ await fs.promises.access(filePath);
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function getMainPackage() {
13
+ const mainModulePath = process.cwd();
14
+ return getPackage(mainModulePath);
15
+ }
16
+
17
+ export async function getPackage(mainModulePath) {
18
+ if (mainModulePath) {
19
+ const packageJsonPath = path.join(mainModulePath, 'package.json');
20
+ if (await fileExists(packageJsonPath)) {
21
+ const fileContent = await fs.promises.readFile(packageJsonPath, 'utf-8');
22
+ const packageJson = JSON.parse(fileContent);
23
+ return packageJson;
24
+ }
25
+ }
26
+ throw new Error('Main package.json not found');
27
+ }
@@ -2,39 +2,40 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { $ } from 'zx';
4
4
 
5
- import { getChangedPackages, isMonoRepo, setGitInfo } from '../next/util.js';
5
+ import { isMonoRepo, setGitInfo } from '../next/util.js';
6
+ import { versionStatus } from './version.js';
6
7
 
7
8
  const CHANGESET_DIR = '.changeset';
8
9
 
9
- export async function validateChangesets() {
10
- /*if (isMonoRepo()) {
11
- const branch = process.env.BITBUCKET_BRANCH;
12
- const destBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
10
+ function areArraysEqualIgnoringOrder(arr1, arr2) {
11
+ if (arr1.length !== arr2.length) return false;
13
12
 
14
- const changePackages = await getChangedPackages(destBranch, branch);
13
+ const sortedA = [...arr1].sort();
14
+ const sortedB = [...arr2].sort();
15
15
 
16
- const appPackages = changePackages.filter((appPackage) =>
17
- appPackage.name.endsWith('-app'),
16
+ return sortedA.every((element, index) => element === sortedB[index]);
17
+ }
18
+
19
+ export async function validateChangesets() {
20
+ if (isMonoRepo()) {
21
+ await setGitInfo();
22
+ const destBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
23
+ await $`git fetch origin ${destBranch}:${destBranch}`;
24
+ await versionStatus(destBranch);
25
+ /*const changedPackages = await getChangedPackages();
26
+ const releaseNames = releases.map((release) => release.name);
27
+ const changedPackageNames = changedPackages.map(
28
+ (changedPackage) => changedPackage.name,
18
29
  );
19
30
 
20
- let appPaths;
21
- if (appPackages.length > 0) {
22
- appPaths = appPackages.map((appPackage) => appPackage.path);
23
- for (const appPath of appPaths) {
24
- const changesetPath = path.resolve(appPath, CHANGESET_DIR);
25
- await validateChangesetPath(changesetPath);
26
- }
27
- } else {
28
- throw new Error('No changeset for at least one app');
29
- }
31
+ if (!areArraysEqualIgnoringOrder(releaseNames, changedPackageNames)) {
32
+ throw new Error(
33
+ 'Changesets must be created for all packages created and for those that have those changed as dependencies',
34
+ );
35
+ }*/
30
36
  } else {
31
37
  await validateChangesetPath(path.resolve(process.cwd(), CHANGESET_DIR));
32
- }*/
33
-
34
- await setGitInfo();
35
- const destBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
36
- await $`git fetch origin ${destBranch}:${destBranch}`;
37
- await $`changeset status --since=${destBranch}`;
38
+ }
38
39
  }
39
40
 
40
41
  export async function validateChangesetPath(directoryPath) {
@@ -0,0 +1,89 @@
1
+ import { setGitInfo } from '../next/util.js';
2
+ import { $ } from 'zx';
3
+ import fs from 'fs';
4
+ import { updateComponentVersion } from '../next/jira.js';
5
+
6
+ const changeSetPackage = `{
7
+ "name": "changeset-package",
8
+ "version": "1.0.0",
9
+ "description": "",
10
+ "scripts": {
11
+ },
12
+ "type": "module",
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC",
16
+ "dependencies": {},
17
+ "devDependencies": {
18
+ "@changesets/cli": "^2.27.10"
19
+ }
20
+ }
21
+ `;
22
+
23
+ export async function versionStatus(sinceBranch) {
24
+ const outputPath = './changeset-output.json';
25
+
26
+ if (sinceBranch) {
27
+ await $`pnpm changeset status --output=${outputPath} --since=${sinceBranch}`;
28
+ } else {
29
+ await $`pnpm changeset status --output=${outputPath}`;
30
+ }
31
+
32
+ // Read the file and parse JSON
33
+ const jsonOutput = JSON.parse(await fs.promises.readFile(outputPath, 'utf8'));
34
+
35
+ await fs.promises.rm(outputPath);
36
+ return jsonOutput.releases;
37
+ }
38
+
39
+ export async function versionCommand(closeVersion = false) {
40
+ console.log('📦 Running Changeset versioning...');
41
+ await setGitInfo();
42
+ await $`mv package.json package.json.bak`;
43
+ await $`mv pnpm-lock.yaml pnpm-lock.yaml.bak`;
44
+ await fs.promises.writeFile('package.json', changeSetPackage);
45
+ await $`pnpm install`;
46
+ await $`rm package.json pnpm-lock.yaml`;
47
+ await $`mv package.json.bak package.json`;
48
+ await $`mv pnpm-lock.yaml.bak pnpm-lock.yaml`;
49
+ if (closeVersion) {
50
+ await $`pnpm changeset pre exit`;
51
+ }
52
+
53
+ try {
54
+ const releases = await versionStatus();
55
+
56
+ await $`pnpm changeset version`;
57
+
58
+ return releases;
59
+ } catch {
60
+ return [];
61
+ }
62
+ }
63
+
64
+ export async function updateJiraWithVersion(releases) {
65
+ const lastCommit = (await $`git log -1 --pretty=%B`).stdout.trim();
66
+ console.log(`Last commit '${lastCommit}' being processed to get Jira ticket`);
67
+
68
+ const jiraTicketMatch = lastCommit.match(/([A-Z]+-\d+)/);
69
+ const jiraTicketId = jiraTicketMatch ? jiraTicketMatch[0] : null;
70
+
71
+ // If we have all the necessary data, update the component version in Jira
72
+ if (jiraTicketId) {
73
+ for (const release of releases) {
74
+ try {
75
+ await updateComponentVersion(
76
+ jiraTicketId,
77
+ release.name,
78
+ release.newVersion,
79
+ );
80
+ } catch (error) {
81
+ console.error(
82
+ `Failed to update Jira component version: ${error.message}`,
83
+ );
84
+ }
85
+ }
86
+ }
87
+
88
+ console.log('✅ Changeset versioning complete.');
89
+ }
@@ -1,6 +1,5 @@
1
1
  import groovy.json.JsonSlurper
2
-
3
- import hudson.model.*;
2
+ import hudson.model.*
4
3
 
5
4
  def build = Thread.currentThread().executable
6
5
  def params = build.getEnvironment(listener)
@@ -1,40 +1,43 @@
1
1
  import { $ } from 'zx';
2
2
 
3
3
  export async function addApacheSite() {
4
- if (
5
- !process.env.ENVIRONMENT ||
6
- (process.env.ENVIRONMENT !== 'dev' && process.env.ENVIRONMENT !== 'qa')
7
- ) {
8
- log("Error: ENVIRONMENT must be either 'dev' or 'qa'.");
9
- process.exit(1);
10
- }
11
- if (!process.env.SITE) {
12
- log('Error: SITE cannot be empty.');
13
- process.exit(1);
14
- }
15
-
16
- // Save private key from base64
17
- const haproxyPrivateKey = process.env.HAPROXY_PRIVATE_KEY;
18
- if (!haproxyPrivateKey) {
19
- log('Error: HAPROXY_PRIVATE_KEY not set.');
20
- process.exit(1);
21
- }
22
-
23
- await fs.writeFile('private.key', Buffer.from(haproxyPrivateKey, 'base64'));
24
- await fs.chmod('private.key', 0o600);
25
-
26
-
27
- console.log("Running command");
28
- const sshCmd = (server,script) =>
29
- $`ssh -i private.key -o StrictHostKeyChecking=no root@${server} ${script}`;
30
-
31
- let server = `${process.env.ENVIRONMENT}-apalb01a.corp.docupace.com`;
32
- // The script is run twice in the Bash version
33
- await sshCmd(server, `/root/repo/iis-preprod/scripts/addsite/add_core_site.sh ${process.env.ENVIRONMENT} ${process.env.SITE}`);
34
-
35
- server = `${process.env.ENVIRONMENT}-apalb01b.corp.docupace.com`;
4
+ if (
5
+ !process.env.ENVIRONMENT ||
6
+ (process.env.ENVIRONMENT !== 'dev' && process.env.ENVIRONMENT !== 'qa')
7
+ ) {
8
+ log("Error: ENVIRONMENT must be either 'dev' or 'qa'.");
9
+ process.exit(1);
10
+ }
11
+ if (!process.env.SITE) {
12
+ log('Error: SITE cannot be empty.');
13
+ process.exit(1);
14
+ }
36
15
 
37
- await sshCmd(server,`/root/repo/iis-preprod/scripts/addsite/add_core_site.sh ${process.env.ENVIRONMENT} ${process.env.SITE}`);
16
+ // Save private key from base64
17
+ const haproxyPrivateKey = process.env.HAPROXY_PRIVATE_KEY;
18
+ if (!haproxyPrivateKey) {
19
+ log('Error: HAPROXY_PRIVATE_KEY not set.');
20
+ process.exit(1);
38
21
  }
39
-
40
-
22
+
23
+ await fs.writeFile('private.key', Buffer.from(haproxyPrivateKey, 'base64'));
24
+ await fs.chmod('private.key', 0o600);
25
+
26
+ console.log('Running command');
27
+ const sshCmd = (server, script) =>
28
+ $`ssh -i private.key -o StrictHostKeyChecking=no root@${server} ${script}`;
29
+
30
+ let server = `${process.env.ENVIRONMENT}-apalb01a.corp.docupace.com`;
31
+ // The script is run twice in the Bash version
32
+ await sshCmd(
33
+ server,
34
+ `/root/repo/iis-preprod/scripts/addsite/add_core_site.sh ${process.env.ENVIRONMENT} ${process.env.SITE}`,
35
+ );
36
+
37
+ server = `${process.env.ENVIRONMENT}-apalb01b.corp.docupace.com`;
38
+
39
+ await sshCmd(
40
+ server,
41
+ `/root/repo/iis-preprod/scripts/addsite/add_core_site.sh ${process.env.ENVIRONMENT} ${process.env.SITE}`,
42
+ );
43
+ }
package/src/next/cli.js CHANGED
@@ -7,26 +7,24 @@ import { publish } from './publish.js';
7
7
  import {
8
8
  deployAppWithTag,
9
9
  deployFromSite,
10
+ deployTemplate,
10
11
  getDefaultDeploySite,
11
12
  getDeployTag,
12
13
  } from './deploy.js';
13
- import { deployTemplate } from './deploy.js';
14
14
  import { copy } from './copy.js';
15
15
  import { vaultAddSiteData } from './vault.js';
16
16
  import { addApacheSite } from './apache.js';
17
17
  import { haproxySync } from './haproxy.js';
18
18
  import { connectClone } from './connect.js';
19
19
  import dotenv from 'dotenv';
20
+ import { $ } from 'zx';
21
+ import { deployConnect, deployCore } from './jenkins.js';
22
+ import { runPipeline } from './pipline.js';
20
23
 
21
24
  dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
22
25
 
23
26
  const program = new Command();
24
27
 
25
- import { $ } from 'zx';
26
- import { deployConnect, deployCore } from './jenkins.js';
27
- import { runPipeline } from './pipline.js';
28
- import { buildChangePackages } from './util.js';
29
-
30
28
  const GIT_SHA = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
31
29
 
32
30
  program
@@ -6,7 +6,7 @@ import lodash from 'lodash';
6
6
  import { appInfo, environmentConfig, getRepUrl } from './config.js';
7
7
  import { copy2 } from './copy.js';
8
8
  import axios from 'axios';
9
- import { deployCore, deployConnect } from './jenkins.js';
9
+ import { deployConnect, deployCore } from './jenkins.js';
10
10
 
11
11
  /**
12
12
  * Clone the repository, read the YAML file, and return the parsed data and file paths.
@@ -1,12 +1,11 @@
1
1
  import { $ } from 'zx';
2
2
 
3
-
4
3
  export async function haproxySync() {
5
- const repoPath = '/root/repo/docupace-ansible';
6
- const inventory = path.join(repoPath, 'inventories/preprod/inventory');
7
-
8
- // Create haproxy.sh
9
- const scriptContent = `
4
+ const repoPath = '/root/repo/docupace-ansible';
5
+ const inventory = path.join(repoPath, 'inventories/preprod/inventory');
6
+
7
+ // Create haproxy.sh
8
+ const scriptContent = `
10
9
  #!/bin/bash
11
10
 
12
11
  [[ ! -f ${inventory} ]] && ( echo "Alert: no inventory file: ${inventory}" ; exit 1 )
@@ -27,24 +26,24 @@ export async function haproxySync() {
27
26
  git commit -m "Sync haproxy"
28
27
  git push origin master
29
28
  `;
30
-
31
- await fs.writeFile('haproxy.sh', scriptContent);
32
-
33
- // Private key
34
- const haproxyPrivateKey = process.env.HAPROXY_PRIVATE_KEY;
35
- if (!haproxyPrivateKey) {
36
- log('Error: HAPROXY_PRIVATE_KEY not set.');
37
- process.exit(1);
38
- }
39
-
40
- await fs.writeFile('private.key', Buffer.from(haproxyPrivateKey, 'base64'));
41
- await fs.chmod('private.key', 0o600);
42
-
43
- await $`cat private.key`; // Just to mimic the script's behavior
44
-
45
- // Copy script to remote
46
- await $`scp -i private.key -o StrictHostKeyChecking=no haproxy.sh root@10.10.28.198:/tmp/haproxy.sh`;
47
-
48
- // Execute script on remote
49
- await $`ssh -i private.key -o StrictHostKeyChecking=no root@10.10.28.198 "bash /tmp/haproxy.sh"`;
29
+
30
+ await fs.writeFile('haproxy.sh', scriptContent);
31
+
32
+ // Private key
33
+ const haproxyPrivateKey = process.env.HAPROXY_PRIVATE_KEY;
34
+ if (!haproxyPrivateKey) {
35
+ log('Error: HAPROXY_PRIVATE_KEY not set.');
36
+ process.exit(1);
50
37
  }
38
+
39
+ await fs.writeFile('private.key', Buffer.from(haproxyPrivateKey, 'base64'));
40
+ await fs.chmod('private.key', 0o600);
41
+
42
+ await $`cat private.key`; // Just to mimic the script's behavior
43
+
44
+ // Copy script to remote
45
+ await $`scp -i private.key -o StrictHostKeyChecking=no haproxy.sh root@10.10.28.198:/tmp/haproxy.sh`;
46
+
47
+ // Execute script on remote
48
+ await $`ssh -i private.key -o StrictHostKeyChecking=no root@10.10.28.198 "bash /tmp/haproxy.sh"`;
49
+ }
package/src/next/jira.js CHANGED
@@ -12,11 +12,6 @@ const jiraClient = axios.create({
12
12
  },
13
13
  });
14
14
 
15
- const packageComponents = {
16
- 'docupace-ui': 'next-ui',
17
- 'docupace-graphql-server': 'graphql-server',
18
- '@docupace/template': 'template',
19
- };
20
15
  /**
21
16
  * Updates the component version custom field in Jira
22
17
  * @param {string} issueKey - The Jira issue key
@@ -24,7 +19,7 @@ const packageComponents = {
24
19
  * @param {string} version - The version to set
25
20
  */
26
21
  export async function updateComponentVersion(issueKey, packageName, version) {
27
- const component = packageComponents[packageName] ?? packageName;
22
+ const component = packageName;
28
23
  console.log(
29
24
  `Updating ${issueKey} for package ${packageName} translated to component ${component} with version ${version}`,
30
25
  );
@@ -56,12 +56,13 @@ export async function publish(gitSha) {
56
56
  await $`docker tag ${IMAGE_NAME} ${JF_REGISTRY_FULL_URL}/${BUILD_IMAGE_NAME}`;
57
57
  await $`docker push ${JF_REGISTRY_FULL_URL}/${BUILD_IMAGE_NAME}`;
58
58
 
59
- const GIT_TAGS = (await $`git tag --points-at HEAD`).stdout
59
+ const gitTags = (await $`git tag --points-at HEAD`).stdout
60
60
  .trim()
61
61
  .split('\n')
62
62
  .filter(Boolean);
63
- for (const GIT_TAG of GIT_TAGS) {
64
- const tagName = `${BITBUCKET_REPO_SLUG}:${GIT_TAG}`;
63
+ for (const gitTag of gitTags) {
64
+ const version = gitTag.includes('@') ? 'v' + gitTag.split('@')[1] : gitTag;
65
+ const tagName = `${IMAGE_PREFIX}:${version}`;
65
66
  await $`docker tag ${IMAGE_NAME} ${JF_REGISTRY_FULL_URL}/${tagName}`;
66
67
  await $`docker push ${JF_REGISTRY_FULL_URL}/${tagName}`;
67
68
  }
package/src/next/util.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import { existsSync } from 'fs';
2
- import { join } from 'path';
3
- import { $, nothrow } from 'zx';
4
- import path from 'path';
5
-
6
- const GIT_SHA = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
2
+ import path, { join } from 'path';
3
+ import { $ } from 'zx';
7
4
 
8
5
  export function isMonoRepo() {
9
6
  // Check for the presence of turbo.json which indicates a Turborepo monorepo
@@ -41,6 +38,33 @@ export async function getChangedPackagesInternal(fromRef, toRef) {
41
38
  }
42
39
  }
43
40
 
41
+ export async function getAllPackages() {
42
+ try {
43
+ const turboDryOutput = await $`turbo run build --dry=json`;
44
+ const dryRunJson = turboDryOutput.stdout.trim();
45
+ const dryRun = JSON.parse(dryRunJson);
46
+
47
+ const packages = dryRun.tasks.map((task) => ({
48
+ name: task.package,
49
+ directory: task.directory,
50
+ path: path.resolve(process.cwd(), task.directory),
51
+ }));
52
+
53
+ return packages;
54
+ } catch (error) {
55
+ console.error('Error getting changed packages:', error);
56
+ return [];
57
+ }
58
+ }
59
+
60
+ export async function getAllAppPackages() {
61
+ const changedPackages = await getAllPackages();
62
+ const appPackages = changedPackages.filter((appPackage) =>
63
+ appPackage.directory.startsWith('apps'),
64
+ );
65
+ return appPackages;
66
+ }
67
+
44
68
  export async function getChangedPackages() {
45
69
  const branch = process.env.BITBUCKET_BRANCH;
46
70
  const destBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
@@ -60,26 +84,10 @@ export async function getChangedAppPackages() {
60
84
  return appPackages;
61
85
  }
62
86
 
63
- export async function buildChangePackages() {
64
- const appPackages = await getChangedAppPackages();
65
- if (appPackages.length == 0) {
66
- throw new Error('Nothing to build');
67
- }
68
- for (const appPackage of appPackages) {
69
- await dockerBuild(appPackage);
70
- }
71
- }
72
-
73
- export async function dockerBuild(appPackage) {
74
- process.env.DOCKER_HOST = '';
75
- const shortName = appPackage.name.replace('@docupace/', '');
76
- const imageName = `${process.env.BITBUCKET_REPO_SLUG}-${shortName}:sha-${GIT_SHA}-withMaps`;
77
- console.log(`Building Docker image ${imageName}...`);
78
- await $`docker build --secret id=npm_token,env=DOCUPACE_NPM_TOKEN . --file ${path.resolve(appPackage.path, 'Dockerfile')} --target builder --tag ${imageName}`;
79
- }
80
-
81
87
  export async function setGitInfo() {
82
- await $`git config --global user.email "gitops@docupace.com"`;
83
- await $`git config --global user.name "Gitops"`;
84
- await $`git remote set-url origin https://${process.env.THIS_REP_TOKEN}@bitbucket.org/${process.env.BITBUCKET_REPO_FULL_NAME}.git`;
88
+ if (!process.env.IS_DEV) {
89
+ await $`git config --global user.email "gitops@docupace.com"`;
90
+ await $`git config --global user.name "Gitops"`;
91
+ await $`git remote set-url origin https://${process.env.THIS_REP_TOKEN}@bitbucket.org/${process.env.BITBUCKET_REPO_FULL_NAME}.git`;
92
+ }
85
93
  }
package/src/next/vault.js CHANGED
@@ -3,8 +3,8 @@ import axios from 'axios';
3
3
  export async function vaultAddSiteData(SITE, SITE_CONFIG_REMOTE_PATH) {
4
4
  const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;
5
5
  const AUTH0_CLIENT_SECRET = process.env.AUTH0_CLIENT_SECRET;
6
- const VAULT_ADDR = process.env.VAULT_ADDR; // Vault host URL from environment variables
7
- const VAULT_TOKEN = process.env.VAULT_TOKEN; // Vault token from environment variables
6
+ const VAULT_ADDR = process.env.VAULT_ADDR; // Vault host URL from environment variables
7
+ const VAULT_TOKEN = process.env.VAULT_TOKEN; // Vault token from environment variables
8
8
 
9
9
  if (!VAULT_ADDR || !VAULT_TOKEN) {
10
10
  throw new Error('Missing VAULT_ADDR or VAULT_TOKEN environment variable.');
@@ -13,7 +13,6 @@ export async function vaultAddSiteData(SITE, SITE_CONFIG_REMOTE_PATH) {
13
13
  const vaultApiUrl = `${VAULT_ADDR}/v1/secret/data/${SITE}/common/config`;
14
14
 
15
15
  try {
16
-
17
16
  if (process.env.VAULT_SKIP_VERIFY === 'true') {
18
17
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
19
18
  }
@@ -32,12 +31,15 @@ export async function vaultAddSiteData(SITE, SITE_CONFIG_REMOTE_PATH) {
32
31
  'X-Vault-Token': VAULT_TOKEN,
33
32
  'Content-Type': 'application/json',
34
33
  },
35
- }
34
+ },
36
35
  );
37
36
 
38
37
  console.log('Data added to Vault successfully:', response.data);
39
38
  } catch (error) {
40
- console.error('Error adding data to Vault:', error.response?.data || error.message);
39
+ console.error(
40
+ 'Error adding data to Vault:',
41
+ error.response?.data || error.message,
42
+ );
41
43
  throw error;
42
44
  }
43
45
  }