@docupace/pipeline 1.0.39-matttest.9 → 1.0.39-newrelease.10

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 ADDED
@@ -0,0 +1,136 @@
1
+ # Pipeline Shared Scripts
2
+
3
+ A collection of CLI tools for Docupace's CI/CD pipelines and deployment workflows.
4
+
5
+ ## Overview
6
+
7
+ This package provides a set of command-line interfaces (CLIs) to facilitate various DevOps operations in the Docupace ecosystem:
8
+
9
+ - `dp-next-cli`: Deployment and pipeline management for Next applications
10
+ - `dp-changeset-cli`: Versioning and changeset management
11
+ - `dp-auth0-cli`: Auth0 tenant configuration and management
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @docupace/pipeline
17
+ ```
18
+
19
+ Or with pnpm:
20
+
21
+ ```bash
22
+ pnpm add @docupace/pipeline
23
+ ```
24
+
25
+ ## Command Line Interfaces
26
+
27
+ ### dp-next-cli
28
+
29
+ Manages deployment, Docker image handling, and infrastructure configuration.
30
+
31
+ ```bash
32
+ dp-next-cli [command] [options]
33
+ ```
34
+
35
+ **Commands:**
36
+
37
+ - `pull <imageName>`: Pull a Docker image from the registry
38
+ - `sonar`: Run Sonar analysis
39
+ - `addApacheSite`: Add site to apache
40
+ - `connectClone`: Clone connect site
41
+ - `haproxySync`: Sync haproxy config
42
+ - `vaultAddSiteData`: Add m2m auth0 data to vault
43
+ - `-s, --site-name <siteName>`: Site name
44
+ - `-p, --site-config-remote-path <path>`: Site config remote path
45
+ - `publish`: Publish a Docker image
46
+ - `deploy-from-site`: Deploy an application for a specific site
47
+ - `-f, --from-env <fromEnv>`: Source environment
48
+ - `-t, --to-env <toEnv>`: Target environment
49
+ - `-F, --from-site <fromSite>`: Source site
50
+ - `-T, --to-site <toSite>`: Target site
51
+ - `-c, --deploy-core <deployCore>`: Deploy Core (default: "false")
52
+ - `-C, --deploy-connect <deployConnect>`: Deploy Connect (default: "false")
53
+ - `deploy-template`: Deploy an application for a specific site
54
+ - `deploy-app <application>`: Deploy an application for a specific site
55
+ - `deploy-core <env> <site>`: Deploy Core
56
+ - `-l, --liquibase <liquibase>`: Application name (default: "true")
57
+ - `-b, --branch <branch>`: Branch (default: "master")
58
+ - `deploy-connect <env> <site>`: Deploy Connect
59
+ - `-l, --liquibase <liquibase>`: Application name (default: "true")
60
+ - `-b, --branch <branch>`: Branch (default: "master")
61
+ - `deploy-core-pipeline <env> <site>`: Deploy Core via pipeline
62
+ - `-l, --liquibase <liquibase>`: Application name (default: "true")
63
+ - `-b, --branch <branch>`: Branch (default: "master")
64
+ - `copy`: Copy a Docker image between registries
65
+ - `-a, --application <application>`: Application name
66
+ - `-f, --from-env <fromEnv>`: Source environment
67
+ - `-t, --to-env <toEnv>`: Target environment
68
+ - `-g, --tag <tag>`: Image tag
69
+ - `run-pipeline`: Trigger a pipeline by name
70
+ - `-r, --repository <repository>`: Repository name
71
+ - `-p, --pipeline-name <pipelineName>`: Pipeline name
72
+ - `-b, --branch <branch>`: Branch name
73
+ - `-v, --variables <key=value>`: Pipeline variable as key=value pair
74
+ - `-w, --wait`: Wait for the pipeline to complete (default: false)
75
+
76
+ ### dp-changeset-cli
77
+
78
+ Manages versioning and changesets for mono-repos and single-package repos.
79
+
80
+ ```bash
81
+ dp-changeset-cli [command] [options]
82
+ ```
83
+
84
+ **Commands:**
85
+
86
+ - `validate`: Validate changesets for the provided PR
87
+ - `version`: Run Changeset versioning
88
+ - `commit-tag`: Commit and tag the new version
89
+ - `version-commit-tag`: Run versioning and commit-and-tag in sequence
90
+ - `close-version`: Close a version and create a release branch
91
+ - `create-changeset`: Create a changeset based on the current branch and Jira ticket information
92
+
93
+ ### dp-auth0-cli
94
+
95
+ Manages Auth0 tenant configuration across environments.
96
+
97
+ ```bash
98
+ dp-auth0-cli [command] [options]
99
+ ```
100
+
101
+ **Commands:**
102
+
103
+ - `configureFromSite <fromSite> <toSite> <fromEnv> <toEnv> <deployUI> <deployGraphQLServer> <useNewDomain> <useS3>`: Configure Auth0 tenants
104
+ - `getVaultSecrets <vaultPath>`: Get secrets from Vault
105
+ - `configureAlias <site> <env> <username> <password> <realm>`: Configure Core Alias for the given site
106
+ - `exportConf <site> <env>`: Export Auth0 configuration for the given site
107
+ - `getAuth0Metadata <site> <env>`: Get Auth0 metadata for the given site
108
+
109
+ ## Environment Configuration
110
+
111
+ The CLI tools look for environment configuration in the following locations:
112
+ - `.env.pipeline.local`
113
+ - `config/.env.pipeline.local`
114
+
115
+ ## Examples
116
+
117
+ ```bash
118
+ # Deploy from one site to another
119
+ dp-next-cli deploy-from-site --from-env dev --to-env dev --from-site financialdev --to-site template --deploy-core true
120
+
121
+ # Run a pipeline
122
+ dp-next-cli run-pipeline -r docupace-next-e2e -b master -p run-tests-qa
123
+
124
+ # Validate changesets
125
+ dp-changeset-cli validate
126
+
127
+ # Create a new changeset
128
+ dp-changeset-cli create-changeset
129
+
130
+ # Configure Auth0 for a site
131
+ dp-auth0-cli configureFromSite sourceSite targetSite dev prod true true true false
132
+ ```
133
+
134
+ ## License
135
+
136
+ ISC
package/next/execute.sh CHANGED
@@ -53,8 +53,14 @@ copy() {
53
53
  }
54
54
 
55
55
  publish() {
56
- BUILD_IMAGE_NAME="${BITBUCKET_REPO_SLUG}:build-${BITBUCKET_BUILD_NUMBER}"
57
- IMAGE_NAME="${BITBUCKET_REPO_SLUG}:sha-${GIT_SHA}"
56
+ SLUG_SUFFIX=""
57
+ if [ -n "$APP_NAME" ]; then
58
+ SLUG_SUFFIX="-${APP_NAME}"
59
+ fi
60
+
61
+ BUILD_IMAGE_NAME="${BITBUCKET_REPO_SLUG}${SLUG_SUFFIX}:build-${BITBUCKET_BUILD_NUMBER}"
62
+ IMAGE_NAME="${BITBUCKET_REPO_SLUG}${SLUG_SUFFIX}:sha-${GIT_SHA}"
63
+
58
64
  export DOCKER_HOST=""
59
65
 
60
66
  echo "Publishing Docker image ${IMAGE_NAME}..."
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@docupace/pipeline",
3
- "version": "1.0.39-matttest.9",
3
+ "version": "1.0.39-newrelease.10",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
+ "exports": {
8
+ "./changelogCanny": "./src/changeset/changelog-module.cjs"
9
+ },
7
10
  "bin": {
8
11
  "dp-changeset-cli": "src/changeset/cli.js",
9
12
  "dp-next-cli": "src/next/cli.js",
@@ -17,15 +20,17 @@
17
20
  "dependencies": {
18
21
  "@changesets/cli": "^2.27.10",
19
22
  "@docupace/core-client": "^0.1.59",
20
- "@docupace/pipeline": "^1.0.33-alpha-2",
21
23
  "auth0-deploy-cli": "^8.8.0",
22
24
  "axios": "^1.7.9",
23
25
  "commander": "^12.1.0",
24
26
  "dotenv": "^16.4.7",
25
27
  "fs-extra": "^11.3.0",
28
+ "inquirer": "^12.6.1",
26
29
  "js-yaml": "^4.1.0",
27
30
  "lodash": "^4.17.21",
31
+ "node-fetch": "^3.3.2",
28
32
  "openai": "^4.76.3",
33
+ "semver": "^7.7.2",
29
34
  "zod": "^3.24.1",
30
35
  "zx": "^8.3.0"
31
36
  },
@@ -42,6 +47,7 @@
42
47
  "test-deploy-core": "dp-next-cli deploy-core-pipeline dev template",
43
48
  "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
49
  "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"
50
+ "test-validate": "dp-changset-cli validate",
51
+ "test-create": "dp-changset-cli create-changeset"
46
52
  }
47
53
  }
@@ -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 {
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import fetch from 'node-fetch';
3
+
4
+ // Configuration
5
+ const CANNY_API_KEY = process.env.CANNY_API_KEY;
6
+
7
+ /**
8
+ * Upload release notes to Canny
9
+ * @param {Object} releaseNote - The release note data
10
+ * @returns {Promise<Object>} - The full Canny entry object
11
+ */
12
+ export async function uploadToCanny(releaseNote, labelIDs) {
13
+ if (!CANNY_API_KEY) {
14
+ console.warn('Canny API key not provided. Skipping upload.');
15
+ return null;
16
+ }
17
+
18
+ try {
19
+ // Prepare the content for Canny based on the release notes
20
+ const title = releaseNote.title;
21
+ let content = '';
22
+
23
+ // Include user-facing description if available
24
+ if (
25
+ releaseNote.userFacingDescription &&
26
+ releaseNote.userFacingDescription.__line.trim()
27
+ ) {
28
+ content += `${releaseNote.userFacingDescription.__line}\n\n`;
29
+ } else {
30
+ content += `${releaseNote.summary}\n\n`;
31
+ }
32
+
33
+ // Include technical details if available
34
+ if (
35
+ releaseNote.technicalDescription &&
36
+ releaseNote.technicalDescription.__line.trim()
37
+ ) {
38
+ content += `**Technical Details**\n\n${releaseNote.technicalDescription.__line}\n\n`;
39
+ }
40
+
41
+ // Add JIRA reference
42
+ content += `**Reference**: JIRA ticket [${releaseNote.ticketId}]`;
43
+
44
+ // Map our changeType to Canny's type parameter
45
+ let entryType;
46
+ if (releaseNote.changeType === 'New') {
47
+ entryType = 'new';
48
+ } else if (releaseNote.changeType === 'Improved') {
49
+ entryType = 'improved';
50
+ } else if (releaseNote.changeType === 'Fixed') {
51
+ entryType = 'fixed';
52
+ }
53
+
54
+ // Create a new changelog entry in Canny
55
+ const createResponse = await fetch(
56
+ 'https://canny.io/api/v1/entries/create',
57
+ {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ body: JSON.stringify({
63
+ apiKey: CANNY_API_KEY,
64
+ title: title,
65
+ details: content,
66
+ type: entryType,
67
+ published: false, // Keep as draft initially
68
+ labelIDs,
69
+ }),
70
+ },
71
+ );
72
+
73
+ if (!createResponse.ok) {
74
+ console.error(`Error from Canny API: ${createResponse.statusText}`);
75
+ return null;
76
+ }
77
+
78
+ const createData = await createResponse.json();
79
+ const entryId = createData.id;
80
+
81
+ // Fetch the complete entry details using the entries/list endpoint
82
+ const listResponse = await fetch('https://canny.io/api/v1/entries/list', {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ body: JSON.stringify({
88
+ apiKey: CANNY_API_KEY,
89
+ }),
90
+ });
91
+
92
+ if (!listResponse.ok) {
93
+ console.error(
94
+ `Error fetching entries list from Canny API: ${listResponse.statusText}`,
95
+ );
96
+ return createData; // Return just the create response as fallback
97
+ }
98
+
99
+ const listData = await listResponse.json();
100
+
101
+ // Find the entry we just created in the list
102
+ const fullEntry = listData.entries.find((entry) => entry.id === entryId);
103
+
104
+ if (!fullEntry) {
105
+ console.warn(
106
+ `Created entry with ID ${entryId} not found in entries list. Returning partial data.`,
107
+ );
108
+ return createData;
109
+ }
110
+
111
+ return fullEntry;
112
+ } catch (error) {
113
+ console.error(`Error uploading to Canny: ${error.message}`);
114
+ return null;
115
+ }
116
+ }
@@ -0,0 +1,26 @@
1
+ async function getReleaseLine(changeset, type, changelogOpts) {
2
+ return (async () => {
3
+ const { default: changelog } = await import('./changelog.js');
4
+ return await changelog.getReleaseLine(changeset, type, changelogOpts); // Now you can use `changelog` here
5
+ })();
6
+ }
7
+
8
+ async function getDependencyReleaseLine(
9
+ changesets,
10
+ dependenciesUpdated,
11
+ changelogOpts,
12
+ ) {
13
+ return (async () => {
14
+ const { default: changelog } = await import('./changelog.js');
15
+ return await changelog.getDependenciesReleaseLine(
16
+ changesets,
17
+ dependenciesUpdated,
18
+ changelogOpts,
19
+ ); // Now you can use `changelog` here
20
+ })();
21
+ }
22
+
23
+ module.exports = {
24
+ getReleaseLine,
25
+ getDependencyReleaseLine,
26
+ };
@@ -0,0 +1,139 @@
1
+ import defaultChangeLog from '@changesets/cli/changelog';
2
+ import { loadChangesetOutput } from './version.js';
3
+ import path from 'path';
4
+ import yaml from 'js-yaml';
5
+ import { getJiraReleaseNotes, updateJiraReleaseNotes } from '../next/jira.js';
6
+ import { uploadToCanny } from './canny.js';
7
+ import fs from 'fs';
8
+ import semver from 'semver';
9
+ import { componentConfig, labelIDMap } from './config.js';
10
+
11
+ async function updateDetailsLinkInText(text, processDetailsLink, params) {
12
+ const detailsLinkRegex = /\[\[details\]\]\((.*?)\)/;
13
+
14
+ const match = text.match(detailsLinkRegex);
15
+ if (!match) {
16
+ return text; // No details link found
17
+ }
18
+
19
+ const originalLink = match[1];
20
+ const newLink = await processDetailsLink(originalLink, params);
21
+
22
+ const updatedText = text.replace(detailsLinkRegex, `[[details]](${newLink})`);
23
+ return updatedText;
24
+ }
25
+
26
+ function getChanges(params) {
27
+ const changes = params.changeset.releases.map((release) => {
28
+ const matchingRelease = params.output.releases.find(
29
+ (r) => r.name === release.name,
30
+ );
31
+
32
+ const version = matchingRelease ? matchingRelease.newVersion : null;
33
+ const parsed = version ? semver.parse(version) : null;
34
+
35
+ return {
36
+ name: release.name,
37
+ type: release.type,
38
+ version,
39
+ majorMinor: parsed ? `${parsed.major}.${parsed.minor}` : null,
40
+ };
41
+ });
42
+ return changes;
43
+ }
44
+
45
+ function getLabels(changes) {
46
+ const labels = [];
47
+ for (const change of changes) {
48
+ const componentName = componentConfig[change.name];
49
+ if (componentName) {
50
+ const labelName = componentName;
51
+ if (!labels.includes(labelName)) {
52
+ labels.push(labelName);
53
+ }
54
+ }
55
+ }
56
+ return labels;
57
+ }
58
+
59
+ function getJiraVersions(changes) {
60
+ const versions = [];
61
+ for (const change of changes) {
62
+ const componentName = componentConfig[change.name];
63
+ if (componentName) {
64
+ const labelName = `${componentName} - ${change.majorMinor}`;
65
+ if (!versions.includes(labelName)) {
66
+ versions.push(labelName);
67
+ }
68
+ }
69
+ }
70
+ return versions;
71
+ }
72
+
73
+ function getLabelIDs(labels) {
74
+ return labels.map((label) => labelIDMap[label]);
75
+ }
76
+
77
+ async function fetchNewLink(oldLink, params) {
78
+ if (oldLink.includes('.yaml')) {
79
+ const normalizedOldLink = path.normalize(oldLink);
80
+ const yamlPath = path.resolve(
81
+ process.cwd(),
82
+ '.changeset',
83
+ normalizedOldLink,
84
+ );
85
+ const releaseNote = yaml.load(fs.readFileSync(yamlPath, 'utf-8'));
86
+ const jiraReleaseInfo = await getJiraReleaseNotes(releaseNote.ticketId);
87
+ if (jiraReleaseInfo?.url) {
88
+ return jiraReleaseInfo.url;
89
+ } else {
90
+ const changes = getChanges(params);
91
+ const labels = getLabels(changes);
92
+ const labelIDs = getLabelIDs(labels);
93
+ const jiraVersions = getJiraVersions(changes);
94
+ const data = await uploadToCanny(releaseNote, labelIDs);
95
+ await updateJiraReleaseNotes(
96
+ releaseNote.ticketId,
97
+ releaseNote.summary,
98
+ data.url,
99
+ jiraVersions,
100
+ );
101
+ return data.url;
102
+ }
103
+ } else {
104
+ return oldLink;
105
+ }
106
+ }
107
+
108
+ async function uploadChangeset(changeset) {
109
+ const output = await loadChangesetOutput();
110
+ const summary = await updateDetailsLinkInText(
111
+ changeset.summary,
112
+ fetchNewLink,
113
+ { changeset, output },
114
+ );
115
+ return { ...changeset, summary };
116
+ }
117
+
118
+ async function getReleaseLine(changeset, type, changelogOpts) {
119
+ const newChangeset = await uploadChangeset(changeset);
120
+ return await defaultChangeLog.getReleaseLine(
121
+ newChangeset,
122
+ type,
123
+ changelogOpts,
124
+ );
125
+ }
126
+
127
+ async function getDependenciesReleaseLine(
128
+ changesets,
129
+ dependenciesUpdated,
130
+ changelogOpts,
131
+ ) {
132
+ return await defaultChangeLog.getDependencyReleaseLine(
133
+ changesets,
134
+ dependenciesUpdated,
135
+ changelogOpts,
136
+ );
137
+ }
138
+
139
+ export default { getReleaseLine, getDependenciesReleaseLine };