@elliemae/ds-monorepo-devops 3.51.0-next.10 → 3.51.0-next.12

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.
@@ -75,6 +75,25 @@ export async function promptForCommandOptions() {
75
75
  ...answers,
76
76
  };
77
77
  }
78
+ /*
79
+ * given a file name, gitRemote and gitBranch and a command, return the command to execute the command on each file with xargs
80
+ * @param options
81
+ * @param {string} [options.gitRemote] - the remote to take the file from
82
+ * @param {string} [options.gitBranch] - the branch to take the file from
83
+ * @param {string} [options.fileName] - the file to restore
84
+ * @param {boolean} [options.wantSubfolder] - whether to restore the file in all subfolders or current folder only
85
+ * @param {string} [options.cmd] - the command to execute on each file
86
+ *
87
+ * @returns {string} - the command to execute the command on each file with xargs
88
+ *
89
+ * @example
90
+ * // => git ls-files origin/develop '** /CHANGELOG.md' | xargs git restore --source=origin/develop'
91
+ * executeCmdOnEachFileWithXargs({gitRemote: 'origin', gitBranch: 'develop', fileName: 'CHANGELOG.md', cmd: 'git restore --source=origin/develop' })
92
+ */
93
+ const executeCmdOnEachFileWithXargs = ({ gitRemote, gitBranch, fileName, wantSubfolder = true, cmd }) =>
94
+ wantSubfolder
95
+ ? `git ls-files ${gitRemote}/${gitBranch} '**/${fileName}' | xargs ${cmd}`
96
+ : `git ls-files ${gitRemote}/${gitBranch} '${fileName}' | xargs ${cmd}`;
78
97
 
79
98
  /*
80
99
  * given a file name, gitRemote and gitBranch, return the command to restore the file in all subfolders, taking the file from the remote branch
@@ -90,9 +109,35 @@ export async function promptForCommandOptions() {
90
109
  * getRestoreFileFromBranchUnixCmd({gitRemote: 'origin', gitBranch: 'develop', fileName: 'CHANGELOG.md'})
91
110
  */
92
111
  const getRestoreFileFromBranchUnixCmd = ({ gitRemote, gitBranch, fileName, wantSubfolder = true }) =>
93
- wantSubfolder
94
- ? `git ls-files ${gitRemote}/${gitBranch} '**/${fileName}' | xargs git restore --source=${gitRemote}/${gitBranch} `
95
- : `git ls-files ${gitRemote}/${gitBranch} '${fileName}' | xargs git restore --source=${gitRemote}/${gitBranch} `;
112
+ executeCmdOnEachFileWithXargs({
113
+ gitRemote,
114
+ gitBranch,
115
+ fileName,
116
+ wantSubfolder,
117
+ cmd: `git restore --source=${gitRemote}/${gitBranch} `,
118
+ });
119
+
120
+ /*
121
+ * given a file name, gitRemote and gitBranch, return the command to add the files in all subfolders to the commit
122
+ * @param options
123
+ * @param {string} [options.gitRemote] - the remote to take the file from
124
+ * @param {string} [options.gitBranch] - the branch to take the file from
125
+ * @param {string} [options.fileName] - the file to restore
126
+ * @param {boolean} [options.wantSubfolder] - whether to restore the file in all subfolders or current folder only
127
+ * @returns {string} - the command to add the files in all subfolders to the commit
128
+ * @example
129
+ *
130
+ * // => git ls-files origin/develop '** /CHANGELOG.md' | xargs git add '
131
+ * getAddChangedFilesToCommitCmd({gitRemote: 'origin', gitBranch: 'develop', fileName: 'CHANGELOG.md'})
132
+ */
133
+ const getAddChangedFilesToCommitCmd = ({ gitRemote, gitBranch, fileName, wantSubfolder = true }) =>
134
+ executeCmdOnEachFileWithXargs({
135
+ gitRemote,
136
+ gitBranch,
137
+ fileName,
138
+ wantSubfolder,
139
+ cmd: `git add `,
140
+ });
96
141
 
97
142
  // 0 nothing to do, 1 can't auto-solve, -1 can auto-solve
98
143
  const canAutosolvePackageJsonConflict = async (packageJsonFilePath) => {
@@ -138,6 +183,8 @@ const gitReverseMerge = async (options) => {
138
183
  .replace(/\//g, '');
139
184
  const newBranchName = `${gitBranch}-${gitCurrent}-${todayMMDDYYYY}`;
140
185
 
186
+ // pull latest version of current branch
187
+ await execSyntaxSugar({ command: `git pull` });
141
188
  // create a new branch
142
189
  await execSyntaxSugar({ command: `git checkout -b ${newBranchName}` });
143
190
  // run the git merge command
@@ -156,19 +203,33 @@ const gitReverseMerge = async (options) => {
156
203
  // 3 - keep any branch CHANGELOG.md (they will be auto-generated anyway)
157
204
  const restoreChangelogCmd = getRestoreFileFromBranchUnixCmd({
158
205
  gitRemote,
159
- gitCurrent,
206
+ gitBranch: gitCurrent,
160
207
  fileName: 'CHANGELOG.md',
161
208
  wantSubfolder: false,
162
209
  });
163
210
  await execSyntaxSugar({ command: restoreChangelogCmd });
211
+ const addChangelogCmd = getAddChangedFilesToCommitCmd({
212
+ gitRemote,
213
+ gitBranch: gitCurrent,
214
+ fileName: 'CHANGELOG.md',
215
+ wantSubfolder: false,
216
+ });
217
+ await execSyntaxSugar({ command: addChangelogCmd });
164
218
  // 4 - keep any branch ** /CHANGELOG.md (they will be auto-generated anyway)
165
219
  const restoreChangelogsCmd = getRestoreFileFromBranchUnixCmd({
166
220
  gitRemote,
167
- gitCurrent,
221
+ gitBranch: gitCurrent,
168
222
  fileName: 'CHANGELOG.md',
169
223
  wantSubfolder: true,
170
224
  });
171
225
  await execSyntaxSugar({ command: restoreChangelogsCmd });
226
+ const addChangelogsCmd = getAddChangedFilesToCommitCmd({
227
+ gitRemote,
228
+ gitBranch: gitCurrent,
229
+ fileName: 'CHANGELOG.md',
230
+ wantSubfolder: true,
231
+ });
232
+ await execSyntaxSugar({ command: addChangelogsCmd });
172
233
 
173
234
  // we check all package.json files for conflicts and resolve those that can be auto-resolved, listin the ones that can't
174
235
  const cantAutoSolve = [];
@@ -192,15 +253,29 @@ const gitReverseMerge = async (options) => {
192
253
  wantSubfolder: false,
193
254
  });
194
255
  await execSyntaxSugar({ command: restorePackageJsonCmd });
256
+ const addPackageJsonCmd = getAddChangedFilesToCommitCmd({
257
+ gitRemote,
258
+ gitBranch,
259
+ fileName: packageJsonFilePath,
260
+ wantSubfolder: false,
261
+ });
262
+ await execSyntaxSugar({ command: addPackageJsonCmd });
195
263
  }
196
264
 
197
- // 6 - run 'pnpm run use-registry-version && pnpm run use-workspace-version' to update the package.json files with the correct version
198
- await execSyntaxSugar({ command: 'pnpm run use-registry-version && pnpm run use-workspace-version' });
199
-
200
265
  // if there are conflicts that can't be auto-solved, list them
266
+ // and because we can't resolve them, we can't continue with the automatic process
201
267
  if (cantAutoSolve.length > 0) {
202
268
  console.log("The following package.json files have conflicts that can't be auto-solved:");
203
269
  console.log(cantAutoSolve.join('\n'));
270
+ console.log('Please resolve these conflicts manually and finish the merge manually too');
271
+ console.log(
272
+ 'you probably will need to run \n\t`pnpm run use-registry-version && pnpm run use-workspace-version`\nafter you resolve the package.json conflicts',
273
+ );
274
+ }
275
+
276
+ // 6 - run 'pnpm run use-registry-version && pnpm run use-workspace-version' to update the package.json files with the correct version
277
+ if (cantAutoSolve.length === 0) {
278
+ await execSyntaxSugar({ command: 'pnpm run use-registry-version && pnpm run use-workspace-version' });
204
279
  }
205
280
  };
206
281
 
@@ -7,7 +7,7 @@ const { glob } = defGlob;
7
7
 
8
8
  export async function promptForCommandOptions(options) {
9
9
  const questions = [];
10
- if (!options.hello) {
10
+ if (!options.dryRun) {
11
11
  questions.push({
12
12
  type: 'list',
13
13
  name: 'dryRun',
@@ -1,10 +1,12 @@
1
1
  import inquirer from 'inquirer';
2
2
  import { execSyncNxTags } from './execSyncNxTags/index.mjs';
3
- import {execGitReverseMerge} from './execGitReverseMerge/index.mjs';
3
+ import { execGitReverseMerge } from './execGitReverseMerge/index.mjs';
4
+ import { execForcePnpmCatalogVersions } from './forcePnpmCatalogVersions/index.mjs';
4
5
 
5
6
  const COMMANDS = {
6
7
  SYNC_NX_TAGS: 'sync-nx-tags',
7
8
  REVERSE_MERGE_BRANCH: 'reverse-merge-branch',
9
+ FORCE_PNPM_CATALOG_VERSIONS: 'force-pnpm-catalog-versions',
8
10
  EXIT: 'exit',
9
11
  };
10
12
  export const checkCommandOrInquire = async (options) => {
@@ -32,6 +34,9 @@ export async function executeCommandsMap(args, options) {
32
34
  case COMMANDS.REVERSE_MERGE_BRANCH:
33
35
  execGitReverseMerge(options);
34
36
  break;
37
+ case COMMANDS.FORCE_PNPM_CATALOG_VERSIONS:
38
+ execForcePnpmCatalogVersions(options);
39
+ break;
35
40
  default:
36
41
  break;
37
42
  }
@@ -0,0 +1,140 @@
1
+ /* eslint-disable complexity, no-console, max-statements */
2
+ import inquirer from 'inquirer';
3
+ import defGlob from 'glob';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+ const { glob } = defGlob;
7
+
8
+ export async function promptForCommandOptions(options) {
9
+ const questions = [];
10
+ if (!options.dryRun) {
11
+ questions.push({
12
+ type: 'list',
13
+ name: 'dryRun',
14
+ message: 'dry-run?',
15
+ default: 'n',
16
+ choices: ['y', 'n'],
17
+ });
18
+ }
19
+ const answers = await inquirer.prompt(questions);
20
+
21
+ return {
22
+ ...answers,
23
+ };
24
+ }
25
+ /**
26
+ * read the file cwd + pnpm-workspace.yaml and based on the "catalog:" field,
27
+ *
28
+ * check all subfolders under cwd + packages/ for a package.json file
29
+ * any dependency/devDependency/peerDependency that matches something that is in the catalog: field
30
+ * will be pointed to the catalog version via the "catalog:" value
31
+ *
32
+ * @example
33
+ *
34
+ * `pnpm-workspace.yaml`
35
+ * ```yaml
36
+ * catalog:
37
+ * react: 18.2.0
38
+ * ```
39
+ * `packages/my-package/package.json`
40
+ * ```json
41
+ * {
42
+ * "dependencies": {
43
+ * "react": "no-matter-what",
44
+ * "some-other-dep": "^1.0.0",
45
+ * }
46
+ * }
47
+ * ```
48
+ * expected result:
49
+ * `packages/my-package/package.json`
50
+ * ```json
51
+ * {
52
+ * "dependencies": {
53
+ * "react": "catalog:",
54
+ * "some-other-dep": "^1.0.0",
55
+ * }
56
+ * }
57
+ * ```
58
+ *
59
+ * @param options
60
+ * @param {string} [options.dryRun='n'] - Indicates whether to perform a dry run ('y' or 'n').
61
+ * @returns {Promise<void>}
62
+ */
63
+ const convertDepsToCatalogIfExisting = async (options) => {
64
+ const { dryRun } = options;
65
+ const pnpmWorspaceFilePath = path.resolve(process.cwd(), 'pnpm-workspace.yaml');
66
+ const pnpmWorkspaceAsString = fs.existsSync(pnpmWorspaceFilePath)
67
+ ? fs.readFileSync(pnpmWorspaceFilePath, 'utf8')
68
+ : -1;
69
+ if (pnpmWorkspaceAsString === -1) {
70
+ console.log('No pnpm-workspace.yaml found');
71
+ return;
72
+ }
73
+ const catalogStrings = [...pnpmWorkspaceAsString.matchAll(/catalog:\n((?:\s{2}.*\n?)*)/gm)]?.[0]?.[1] ?? '';
74
+ const catalogPackagesKeys = [...catalogStrings.matchAll(/(?:[\ ]{2}["']?)([\s\S]*?)(?:["']?:)/gm)].map((m) => m?.[1]);
75
+ if (!catalogPackagesKeys.length === 0) {
76
+ console.log('No catalog packages found');
77
+ return;
78
+ }
79
+ const globPatternPackage = 'packages/**/*/package.json';
80
+ const packageJsonFilesPaths = glob(globPatternPackage, { sync: true, ignore: ['**/node_modules/**', '**/dist/**'] });
81
+ for (let i = 0; i < packageJsonFilesPaths.length; i += 1) {
82
+ const packageJsonFilePath = packageJsonFilesPaths[i];
83
+ const finalPath = path.resolve(process.cwd(), packageJsonFilePath);
84
+ const packageJson = JSON.parse(fs.readFileSync(finalPath, 'utf8'));
85
+
86
+ const { dependencies = {}, devDependencies = {}, peerDependencies = {} } = packageJson;
87
+
88
+ const log = {
89
+ changedDeps: [],
90
+ changedDevDeps: [],
91
+ changedPeerDeps: [],
92
+ };
93
+
94
+ Object.keys(dependencies).forEach((pkg) => {
95
+ const isInCatalog = catalogPackagesKeys.includes(pkg);
96
+ if (isInCatalog) {
97
+ packageJson.dependencies[pkg] = 'catalog:';
98
+ log.changedDeps.push(pkg);
99
+ }
100
+ });
101
+ Object.keys(devDependencies).forEach((pkg) => {
102
+ const isInCatalog = catalogPackagesKeys.includes(pkg);
103
+ if (isInCatalog) {
104
+ packageJson.devDependencies[pkg] = 'catalog:';
105
+ log.changedDevDeps.push(pkg);
106
+ }
107
+ });
108
+ Object.keys(peerDependencies).forEach((pkg) => {
109
+ const isInCatalog = catalogPackagesKeys.includes(pkg);
110
+ if (isInCatalog) {
111
+ packageJson.peerDependencies[pkg] = 'catalog:';
112
+ log.changedPeerDeps.push(pkg);
113
+ }
114
+ });
115
+
116
+ if (dryRun === 'n') {
117
+ fs.writeFileSync(packageJsonFilePath, JSON.stringify(packageJson, null, 2));
118
+ if (log.changedDeps.length > 0)
119
+ console.log(`Updated ${packageJsonFilePath} dependencies: ${log.changedDeps.join(', ')}`);
120
+ if (log.changedDevDeps.length > 0)
121
+ console.log(`Updated ${packageJsonFilePath} devDependencies: ${log.changedDevDeps.join(', ')}`);
122
+ if (log.changedPeerDeps.length > 0)
123
+ console.log(`Updated ${packageJsonFilePath} peerDependencies: ${log.changedPeerDeps.join(', ')}`);
124
+ continue;
125
+ }
126
+ if (dryRun === 'y') {
127
+ if (log.changedDeps.length > 0)
128
+ console.log(`Would update ${packageJsonFilePath} dependencies: ${log.changedDeps.join(', ')}`);
129
+ if (log.changedDevDeps.length > 0)
130
+ console.log(`Would update ${packageJsonFilePath} devDependencies: ${log.changedDevDeps.join(', ')}`);
131
+ if (log.changedPeerDeps.length > 0)
132
+ console.log(`Would update ${packageJsonFilePath} peerDependencies: ${log.changedPeerDeps.join(', ')}`);
133
+ }
134
+ }
135
+ };
136
+
137
+ export const execForcePnpmCatalogVersions = async (options) => {
138
+ const commandOptions = await promptForCommandOptions(options);
139
+ await convertDepsToCatalogIfExisting(commandOptions);
140
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-monorepo-devops",
3
- "version": "3.51.0-next.10",
3
+ "version": "3.51.0-next.12",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Monorepo Devops",
6
6
  "type": "module",