pcu 0.3.5 → 0.4.0
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 +45 -0
- package/README.zh-CN.md +64 -0
- package/dist/application/services/CatalogUpdateService.d.ts +15 -8
- package/dist/application/services/CatalogUpdateService.d.ts.map +1 -1
- package/dist/application/services/CatalogUpdateService.js +259 -47
- package/dist/application/services/CatalogUpdateService.js.map +1 -1
- package/dist/cli/commands/CheckCommand.d.ts +2 -3
- package/dist/cli/commands/CheckCommand.d.ts.map +1 -1
- package/dist/cli/commands/CheckCommand.js +16 -9
- package/dist/cli/commands/CheckCommand.js.map +1 -1
- package/dist/cli/commands/SecurityCommand.d.ts +113 -0
- package/dist/cli/commands/SecurityCommand.d.ts.map +1 -0
- package/dist/cli/commands/SecurityCommand.js +412 -0
- package/dist/cli/commands/SecurityCommand.js.map +1 -0
- package/dist/cli/commands/UpdateCommand.d.ts +2 -3
- package/dist/cli/commands/UpdateCommand.d.ts.map +1 -1
- package/dist/cli/commands/UpdateCommand.js +19 -12
- package/dist/cli/commands/UpdateCommand.js.map +1 -1
- package/dist/cli/formatters/OutputFormatter.d.ts +17 -0
- package/dist/cli/formatters/OutputFormatter.d.ts.map +1 -1
- package/dist/cli/formatters/OutputFormatter.js +113 -0
- package/dist/cli/formatters/OutputFormatter.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +66 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/common/config/Config.d.ts +2 -2
- package/dist/common/config/Config.d.ts.map +1 -1
- package/dist/common/config/Config.js +31 -4
- package/dist/common/config/Config.js.map +1 -1
- package/dist/common/config/ConfigLoader.d.ts +45 -0
- package/dist/common/config/ConfigLoader.d.ts.map +1 -0
- package/dist/common/config/ConfigLoader.js +157 -0
- package/dist/common/config/ConfigLoader.js.map +1 -0
- package/dist/common/config/PackageFilterConfig.d.ts +54 -0
- package/dist/common/config/PackageFilterConfig.d.ts.map +1 -0
- package/dist/common/config/PackageFilterConfig.js +67 -0
- package/dist/common/config/PackageFilterConfig.js.map +1 -0
- package/dist/common/config/index.d.ts +2 -0
- package/dist/common/config/index.d.ts.map +1 -1
- package/dist/common/config/index.js +2 -0
- package/dist/common/config/index.js.map +1 -1
- package/dist/common/utils/validation.d.ts.map +1 -1
- package/dist/common/utils/validation.js +29 -2
- package/dist/common/utils/validation.js.map +1 -1
- package/dist/infrastructure/external-services/NpmRegistryService.d.ts +10 -2
- package/dist/infrastructure/external-services/NpmRegistryService.d.ts.map +1 -1
- package/dist/infrastructure/external-services/NpmRegistryService.js +48 -18
- package/dist/infrastructure/external-services/NpmRegistryService.js.map +1 -1
- package/package.json +23 -23
package/README.md
CHANGED
|
@@ -323,6 +323,51 @@ Create a `.pcurc.json` file in your project root:
|
|
|
323
323
|
}
|
|
324
324
|
```
|
|
325
325
|
|
|
326
|
+
#### Package Filtering Configuration
|
|
327
|
+
|
|
328
|
+
You can also configure package-specific update rules by creating a `.pcurc.json`
|
|
329
|
+
with filtering options:
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
// Exclude packages you never want to update
|
|
334
|
+
"exclude": ["typescript", "@types/node", "react", "react-dom"],
|
|
335
|
+
|
|
336
|
+
// Only update specific packages (optional - if not specified, all packages are considered)
|
|
337
|
+
"include": ["lodash*", "chalk", "commander"],
|
|
338
|
+
|
|
339
|
+
// Package-specific update rules
|
|
340
|
+
"packageRules": [
|
|
341
|
+
{
|
|
342
|
+
"patterns": ["@types/*"],
|
|
343
|
+
"target": "latest", // Always update type definitions to latest
|
|
344
|
+
"autoUpdate": true
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"patterns": ["react", "react-dom"],
|
|
348
|
+
"target": "patch", // Only patch updates for React
|
|
349
|
+
"requireConfirmation": true // Always ask before updating
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"patterns": ["eslint*", "prettier"],
|
|
353
|
+
"target": "minor", // Minor updates for dev tools
|
|
354
|
+
"groupUpdate": true // Update related packages together
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
|
|
358
|
+
// Override defaults
|
|
359
|
+
"defaults": {
|
|
360
|
+
"target": "minor",
|
|
361
|
+
"createBackup": true
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Configuration priority**: Package rules > CLI options > Default configuration
|
|
367
|
+
|
|
368
|
+
**Pattern matching**: Supports glob patterns like `react*`, `@types/*`,
|
|
369
|
+
`eslint*`
|
|
370
|
+
|
|
326
371
|
## 📁 Project Structure
|
|
327
372
|
|
|
328
373
|
This project follows Domain-Driven Design (DDD) principles:
|
package/README.zh-CN.md
CHANGED
|
@@ -316,6 +316,70 @@ pcu -t --interactive # 交互式主题设置
|
|
|
316
316
|
}
|
|
317
317
|
```
|
|
318
318
|
|
|
319
|
+
#### 包过滤配置
|
|
320
|
+
|
|
321
|
+
您还可以通过创建包含过滤选项的 `.pcurc.json` 来配置特定包的更新规则:
|
|
322
|
+
|
|
323
|
+
```json
|
|
324
|
+
{
|
|
325
|
+
// 排除您永远不想更新的包
|
|
326
|
+
"exclude": ["typescript", "@types/node", "react", "react-dom"],
|
|
327
|
+
|
|
328
|
+
// 仅更新特定包(可选 - 如果不指定,将考虑所有包)
|
|
329
|
+
"include": ["lodash*", "chalk", "commander"],
|
|
330
|
+
|
|
331
|
+
// 特定包的更新规则
|
|
332
|
+
"packageRules": [
|
|
333
|
+
{
|
|
334
|
+
"patterns": ["@types/*"],
|
|
335
|
+
"target": "latest", // 类型定义总是更新到最新版本
|
|
336
|
+
"autoUpdate": true
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"patterns": ["react", "react-dom"],
|
|
340
|
+
"target": "patch", // React 只进行 patch 更新
|
|
341
|
+
"requireConfirmation": true // 更新前总是询问
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"patterns": ["eslint*", "prettier"],
|
|
345
|
+
"target": "minor", // 开发工具进行 minor 更新
|
|
346
|
+
"groupUpdate": true // 相关包一起更新
|
|
347
|
+
}
|
|
348
|
+
],
|
|
349
|
+
|
|
350
|
+
// 安全配置
|
|
351
|
+
"security": {
|
|
352
|
+
"autoFixVulnerabilities": true, // 自动检查并修复安全漏洞
|
|
353
|
+
"allowMajorForSecurity": true, // 为安全修复允许主版本升级
|
|
354
|
+
"notifyOnSecurityUpdate": true // 安全更新时显示通知
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
// 高级配置
|
|
358
|
+
"advanced": {
|
|
359
|
+
"concurrency": 5, // 并发网络请求数量(默认: 5)
|
|
360
|
+
"timeout": 30000, // 网络请求超时时间(毫秒,默认: 30000)
|
|
361
|
+
"retries": 3, // 失败重试次数(默认: 3)
|
|
362
|
+
"cacheValidityMinutes": 60 // 缓存有效期(分钟,默认: 60,设为0禁用缓存)
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
// Monorepo 配置
|
|
366
|
+
"monorepo": {
|
|
367
|
+
"syncVersions": ["react", "react-dom"], // 需要在多个 catalog 间同步版本的包
|
|
368
|
+
"catalogPriority": ["default", "latest", "react17"] // catalog 优先级顺序
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// 覆盖默认设置
|
|
372
|
+
"defaults": {
|
|
373
|
+
"target": "minor",
|
|
374
|
+
"createBackup": true
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**配置优先级**: 包规则 > CLI 选项 > 默认配置
|
|
380
|
+
|
|
381
|
+
**模式匹配**: 支持 glob 模式,如 `react*`、`@types/*`、`eslint*`
|
|
382
|
+
|
|
319
383
|
## 📁 项目结构
|
|
320
384
|
|
|
321
385
|
本项目遵循领域驱动设计 (DDD) 原则:
|
|
@@ -63,6 +63,9 @@ export interface PlannedUpdate {
|
|
|
63
63
|
updateType: 'major' | 'minor' | 'patch';
|
|
64
64
|
reason: string;
|
|
65
65
|
affectedPackages: string[];
|
|
66
|
+
requireConfirmation?: boolean;
|
|
67
|
+
autoUpdate?: boolean;
|
|
68
|
+
groupUpdate?: boolean;
|
|
66
69
|
}
|
|
67
70
|
export interface VersionConflict {
|
|
68
71
|
packageName: string;
|
|
@@ -134,6 +137,10 @@ export declare class CatalogUpdateService {
|
|
|
134
137
|
private readonly workspaceRepository;
|
|
135
138
|
private readonly registryService;
|
|
136
139
|
constructor(workspaceRepository: WorkspaceRepository, registryService: NpmRegistryService);
|
|
140
|
+
/**
|
|
141
|
+
* Create a new CatalogUpdateService with advanced configuration
|
|
142
|
+
*/
|
|
143
|
+
static createWithConfig(workspaceRepository: WorkspaceRepository, workspacePath?: string): CatalogUpdateService;
|
|
137
144
|
/**
|
|
138
145
|
* Check for outdated catalog dependencies
|
|
139
146
|
*/
|
|
@@ -150,14 +157,6 @@ export declare class CatalogUpdateService {
|
|
|
150
157
|
* Analyze the impact of updating a specific dependency
|
|
151
158
|
*/
|
|
152
159
|
analyzeImpact(catalogName: string, packageName: string, newVersion: string, workspacePath?: string): Promise<ImpactAnalysis>;
|
|
153
|
-
/**
|
|
154
|
-
* Check if a specific package should be updated based on filters
|
|
155
|
-
*/
|
|
156
|
-
private shouldCheckPackage;
|
|
157
|
-
/**
|
|
158
|
-
* Check if package matches a pattern (simple glob-like matching)
|
|
159
|
-
*/
|
|
160
|
-
private matchesPattern;
|
|
161
160
|
/**
|
|
162
161
|
* Check if a package needs updating
|
|
163
162
|
*/
|
|
@@ -186,5 +185,13 @@ export declare class CatalogUpdateService {
|
|
|
186
185
|
* Generate recommendations based on analysis
|
|
187
186
|
*/
|
|
188
187
|
private generateRecommendations;
|
|
188
|
+
/**
|
|
189
|
+
* Create sync version updates for packages that should be synchronized across catalogs
|
|
190
|
+
*/
|
|
191
|
+
private createSyncVersionUpdates;
|
|
192
|
+
/**
|
|
193
|
+
* Resolve version conflicts using catalog priority
|
|
194
|
+
*/
|
|
195
|
+
private resolveVersionConflict;
|
|
189
196
|
}
|
|
190
197
|
//# sourceMappingURL=CatalogUpdateService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogUpdateService.d.ts","sourceRoot":"","sources":["../../../src/application/services/CatalogUpdateService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAGvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,8DAA8D,CAAC;
|
|
1
|
+
{"version":3,"file":"CatalogUpdateService.d.ts","sourceRoot":"","sources":["../../../src/application/services/CatalogUpdateService.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { WorkspacePath } from '../../domain/value-objects/WorkspacePath.js';
|
|
9
9
|
import { Version } from '../../domain/value-objects/Version.js';
|
|
10
|
+
import { NpmRegistryService } from '../../infrastructure/external-services/NpmRegistryService.js';
|
|
11
|
+
import { ConfigLoader } from '../../common/config/ConfigLoader.js';
|
|
10
12
|
export class CatalogUpdateService {
|
|
11
13
|
workspaceRepository;
|
|
12
14
|
registryService;
|
|
@@ -14,11 +16,35 @@ export class CatalogUpdateService {
|
|
|
14
16
|
this.workspaceRepository = workspaceRepository;
|
|
15
17
|
this.registryService = registryService;
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a new CatalogUpdateService with advanced configuration
|
|
21
|
+
*/
|
|
22
|
+
static createWithConfig(workspaceRepository, workspacePath) {
|
|
23
|
+
const config = ConfigLoader.loadConfig(workspacePath || process.cwd());
|
|
24
|
+
// Create registry service with advanced configuration
|
|
25
|
+
const advancedConfig = {};
|
|
26
|
+
if (config.advanced?.concurrency !== undefined) {
|
|
27
|
+
advancedConfig.concurrency = config.advanced.concurrency;
|
|
28
|
+
}
|
|
29
|
+
if (config.advanced?.timeout !== undefined) {
|
|
30
|
+
advancedConfig.timeout = config.advanced.timeout;
|
|
31
|
+
}
|
|
32
|
+
if (config.advanced?.retries !== undefined) {
|
|
33
|
+
advancedConfig.retries = config.advanced.retries;
|
|
34
|
+
}
|
|
35
|
+
if (config.advanced?.cacheValidityMinutes !== undefined) {
|
|
36
|
+
advancedConfig.cacheValidityMinutes = config.advanced.cacheValidityMinutes;
|
|
37
|
+
}
|
|
38
|
+
const registryService = new NpmRegistryService('https://registry.npmjs.org/', advancedConfig);
|
|
39
|
+
return new CatalogUpdateService(workspaceRepository, registryService);
|
|
40
|
+
}
|
|
17
41
|
/**
|
|
18
42
|
* Check for outdated catalog dependencies
|
|
19
43
|
*/
|
|
20
44
|
async checkOutdatedDependencies(options = {}) {
|
|
21
45
|
const workspacePath = WorkspacePath.fromString(options.workspacePath || process.cwd());
|
|
46
|
+
// Load configuration
|
|
47
|
+
const config = ConfigLoader.loadConfig(workspacePath.toString());
|
|
22
48
|
// Load workspace
|
|
23
49
|
const workspace = await this.workspaceRepository.findByPath(workspacePath);
|
|
24
50
|
if (!workspace) {
|
|
@@ -44,21 +70,49 @@ export class CatalogUpdateService {
|
|
|
44
70
|
}
|
|
45
71
|
const dependencies = catalog.getDependencies();
|
|
46
72
|
for (const [packageName, currentRange] of dependencies) {
|
|
47
|
-
// Apply
|
|
48
|
-
|
|
73
|
+
// Apply configuration filters
|
|
74
|
+
const packageConfig = ConfigLoader.getPackageConfig(packageName, config);
|
|
75
|
+
if (!packageConfig.shouldUpdate) {
|
|
49
76
|
continue;
|
|
50
77
|
}
|
|
78
|
+
// Override target from package-specific configuration, considering security settings
|
|
79
|
+
let effectiveTarget = packageConfig.target;
|
|
80
|
+
// Check for security vulnerabilities if security config is enabled
|
|
81
|
+
let hasSecurityVulnerabilities = false;
|
|
82
|
+
if (config.security?.autoFixVulnerabilities) {
|
|
83
|
+
try {
|
|
84
|
+
const currentVersion = currentRange.getMinVersion()?.toString();
|
|
85
|
+
if (currentVersion) {
|
|
86
|
+
const securityReport = await this.registryService.checkSecurityVulnerabilities(packageName, currentVersion);
|
|
87
|
+
hasSecurityVulnerabilities = securityReport.hasVulnerabilities;
|
|
88
|
+
// Allow major updates for security fixes if configured
|
|
89
|
+
if (hasSecurityVulnerabilities && config.security.allowMajorForSecurity) {
|
|
90
|
+
effectiveTarget = 'latest';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.warn(`Failed to check security for ${packageName}:`, error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
51
98
|
try {
|
|
52
|
-
const outdatedInfo = await this.checkPackageUpdate(packageName, currentRange, options.
|
|
99
|
+
const outdatedInfo = await this.checkPackageUpdate(packageName, currentRange, effectiveTarget, options.includePrerelease || config.defaults?.includePrerelease || false);
|
|
53
100
|
if (outdatedInfo) {
|
|
54
101
|
// Get affected packages
|
|
55
102
|
const affectedPackages = workspace
|
|
56
103
|
.getPackagesUsingCatalogDependency(catalog.getName(), packageName)
|
|
57
104
|
.getPackageNames();
|
|
58
|
-
|
|
105
|
+
// Override security update flag based on security check
|
|
106
|
+
const finalOutdatedInfo = {
|
|
59
107
|
...outdatedInfo,
|
|
60
108
|
affectedPackages,
|
|
61
|
-
|
|
109
|
+
isSecurityUpdate: hasSecurityVulnerabilities || outdatedInfo.isSecurityUpdate,
|
|
110
|
+
};
|
|
111
|
+
outdatedDependencies.push(finalOutdatedInfo);
|
|
112
|
+
// Log security notifications if enabled
|
|
113
|
+
if (hasSecurityVulnerabilities && config.security?.notifyOnSecurityUpdate) {
|
|
114
|
+
console.warn(`🔒 Security vulnerability detected in ${packageName}@${outdatedInfo.currentVersion}`);
|
|
115
|
+
}
|
|
62
116
|
}
|
|
63
117
|
}
|
|
64
118
|
catch (error) {
|
|
@@ -91,9 +145,14 @@ export class CatalogUpdateService {
|
|
|
91
145
|
const outdatedReport = await this.checkOutdatedDependencies(options);
|
|
92
146
|
const updates = [];
|
|
93
147
|
const conflicts = [];
|
|
148
|
+
// Load configuration for package rules and monorepo settings
|
|
149
|
+
const workspacePath = options.workspacePath || process.cwd();
|
|
150
|
+
const config = ConfigLoader.loadConfig(workspacePath);
|
|
94
151
|
// Convert outdated dependencies to planned updates
|
|
95
152
|
for (const catalogInfo of outdatedReport.catalogs) {
|
|
96
153
|
for (const outdated of catalogInfo.outdatedDependencies) {
|
|
154
|
+
// Get package-specific configuration
|
|
155
|
+
const packageConfig = ConfigLoader.getPackageConfig(outdated.packageName, config);
|
|
97
156
|
const update = {
|
|
98
157
|
catalogName: catalogInfo.catalogName,
|
|
99
158
|
packageName: outdated.packageName,
|
|
@@ -102,10 +161,21 @@ export class CatalogUpdateService {
|
|
|
102
161
|
updateType: outdated.updateType,
|
|
103
162
|
reason: this.getUpdateReason(outdated),
|
|
104
163
|
affectedPackages: outdated.affectedPackages,
|
|
164
|
+
requireConfirmation: packageConfig.requireConfirmation,
|
|
165
|
+
autoUpdate: packageConfig.autoUpdate,
|
|
166
|
+
groupUpdate: packageConfig.groupUpdate,
|
|
105
167
|
};
|
|
106
168
|
updates.push(update);
|
|
107
169
|
}
|
|
108
170
|
}
|
|
171
|
+
// Handle syncVersions - ensure packages in syncVersions list are synchronized across catalogs
|
|
172
|
+
if (config.monorepo?.syncVersions && config.monorepo.syncVersions.length > 0) {
|
|
173
|
+
const workspacePathObj = WorkspacePath.fromString(workspacePath);
|
|
174
|
+
const workspace = await this.workspaceRepository.findByPath(workspacePathObj);
|
|
175
|
+
if (workspace) {
|
|
176
|
+
updates.push(...(await this.createSyncVersionUpdates(config.monorepo.syncVersions, workspace, updates)));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
109
179
|
// Detect conflicts (same package in multiple catalogs with different versions)
|
|
110
180
|
const packageCatalogMap = new Map();
|
|
111
181
|
for (const update of updates) {
|
|
@@ -114,20 +184,15 @@ export class CatalogUpdateService {
|
|
|
114
184
|
}
|
|
115
185
|
packageCatalogMap.get(update.packageName).push(update);
|
|
116
186
|
}
|
|
187
|
+
// Handle conflicts with catalogPriority
|
|
117
188
|
for (const [packageName, packageUpdates] of packageCatalogMap) {
|
|
118
189
|
if (packageUpdates.length > 1) {
|
|
119
190
|
const uniqueVersions = new Set(packageUpdates.map((u) => u.newVersion));
|
|
120
191
|
if (uniqueVersions.size > 1) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
currentVersion: u.currentVersion,
|
|
126
|
-
proposedVersion: u.newVersion,
|
|
127
|
-
})),
|
|
128
|
-
recommendation: `Consider using the same version across all catalogs`,
|
|
129
|
-
};
|
|
130
|
-
conflicts.push(conflict);
|
|
192
|
+
const resolvedConflict = this.resolveVersionConflict(packageName, packageUpdates, config.monorepo?.catalogPriority || ['default']);
|
|
193
|
+
if (resolvedConflict) {
|
|
194
|
+
conflicts.push(resolvedConflict);
|
|
195
|
+
}
|
|
131
196
|
}
|
|
132
197
|
}
|
|
133
198
|
}
|
|
@@ -144,6 +209,8 @@ export class CatalogUpdateService {
|
|
|
144
209
|
*/
|
|
145
210
|
async executeUpdates(plan, options) {
|
|
146
211
|
const workspacePath = WorkspacePath.fromString(plan.workspace.path);
|
|
212
|
+
// Load configuration for security settings
|
|
213
|
+
const config = ConfigLoader.loadConfig(workspacePath.toString());
|
|
147
214
|
// Load workspace
|
|
148
215
|
const workspace = await this.workspaceRepository.findByPath(workspacePath);
|
|
149
216
|
if (!workspace) {
|
|
@@ -152,6 +219,8 @@ export class CatalogUpdateService {
|
|
|
152
219
|
const updatedDependencies = [];
|
|
153
220
|
const skippedDependencies = [];
|
|
154
221
|
const errors = [];
|
|
222
|
+
// Track security updates for notification
|
|
223
|
+
const securityUpdates = [];
|
|
155
224
|
// Execute updates
|
|
156
225
|
for (const update of plan.updates) {
|
|
157
226
|
try {
|
|
@@ -168,6 +237,20 @@ export class CatalogUpdateService {
|
|
|
168
237
|
continue;
|
|
169
238
|
}
|
|
170
239
|
}
|
|
240
|
+
// Check if this is a security update and track it
|
|
241
|
+
let isSecurityUpdate = false;
|
|
242
|
+
if (config.security?.notifyOnSecurityUpdate) {
|
|
243
|
+
try {
|
|
244
|
+
const securityReport = await this.registryService.checkSecurityVulnerabilities(update.packageName, update.currentVersion);
|
|
245
|
+
if (securityReport.hasVulnerabilities) {
|
|
246
|
+
isSecurityUpdate = true;
|
|
247
|
+
securityUpdates.push(`${update.packageName}@${update.currentVersion} → ${update.newVersion}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.warn(`Failed to check security for ${update.packageName}:`, error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
171
254
|
// Perform the update
|
|
172
255
|
workspace.updateCatalogDependency(update.catalogName, update.packageName, update.newVersion);
|
|
173
256
|
updatedDependencies.push({
|
|
@@ -177,6 +260,10 @@ export class CatalogUpdateService {
|
|
|
177
260
|
toVersion: update.newVersion,
|
|
178
261
|
updateType: update.updateType,
|
|
179
262
|
});
|
|
263
|
+
// Log security update notification
|
|
264
|
+
if (isSecurityUpdate && config.security?.notifyOnSecurityUpdate) {
|
|
265
|
+
console.log(`✅ Security fix applied: ${update.packageName}@${update.currentVersion} → ${update.newVersion}`);
|
|
266
|
+
}
|
|
180
267
|
}
|
|
181
268
|
catch (error) {
|
|
182
269
|
errors.push({
|
|
@@ -191,6 +278,44 @@ export class CatalogUpdateService {
|
|
|
191
278
|
if (!options.dryRun && updatedDependencies.length > 0) {
|
|
192
279
|
try {
|
|
193
280
|
await this.workspaceRepository.save(workspace);
|
|
281
|
+
// Show summary of security updates if any
|
|
282
|
+
if (securityUpdates.length > 0 && config.security?.notifyOnSecurityUpdate) {
|
|
283
|
+
console.log(`\n🔒 Security Updates Summary:`);
|
|
284
|
+
console.log(` Applied ${securityUpdates.length} security fix(es):`);
|
|
285
|
+
securityUpdates.forEach((update) => console.log(` • ${update}`));
|
|
286
|
+
}
|
|
287
|
+
// Show summary of synced version updates
|
|
288
|
+
const syncUpdates = updatedDependencies.filter((u) => plan.updates.find((pu) => pu.packageName === u.packageName &&
|
|
289
|
+
pu.catalogName === u.catalogName &&
|
|
290
|
+
pu.reason.includes('Sync version')));
|
|
291
|
+
if (syncUpdates.length > 0) {
|
|
292
|
+
console.log(`\n🔄 Version Sync Summary:`);
|
|
293
|
+
const syncedPackages = new Set(syncUpdates.map((u) => u.packageName));
|
|
294
|
+
console.log(` Synchronized ${syncedPackages.size} package(s) across catalogs:`);
|
|
295
|
+
// Group by package name
|
|
296
|
+
const syncByPackage = new Map();
|
|
297
|
+
syncUpdates.forEach((update) => {
|
|
298
|
+
if (!syncByPackage.has(update.packageName)) {
|
|
299
|
+
syncByPackage.set(update.packageName, []);
|
|
300
|
+
}
|
|
301
|
+
syncByPackage.get(update.packageName).push(update);
|
|
302
|
+
});
|
|
303
|
+
syncByPackage.forEach((updates, packageName) => {
|
|
304
|
+
const catalogs = updates.map((u) => u.catalogName).join(', ');
|
|
305
|
+
const version = updates[0]?.toVersion;
|
|
306
|
+
console.log(` • ${packageName}@${version} in catalogs: ${catalogs}`);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// Show catalog priority resolution if any
|
|
310
|
+
if (plan.hasConflicts &&
|
|
311
|
+
plan.conflicts.some((c) => c.recommendation.includes('Priority catalog'))) {
|
|
312
|
+
console.log(`\n📋 Catalog Priority Resolutions:`);
|
|
313
|
+
plan.conflicts
|
|
314
|
+
.filter((c) => c.recommendation.includes('Priority catalog'))
|
|
315
|
+
.forEach((conflict) => {
|
|
316
|
+
console.log(` • ${conflict.packageName}: ${conflict.recommendation}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
194
319
|
}
|
|
195
320
|
catch (error) {
|
|
196
321
|
errors.push({
|
|
@@ -274,38 +399,6 @@ export class CatalogUpdateService {
|
|
|
274
399
|
recommendations,
|
|
275
400
|
};
|
|
276
401
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Check if a specific package should be updated based on filters
|
|
279
|
-
*/
|
|
280
|
-
shouldCheckPackage(packageName, include, exclude) {
|
|
281
|
-
// Check exclude patterns first
|
|
282
|
-
if (exclude && exclude.length > 0) {
|
|
283
|
-
for (const pattern of exclude) {
|
|
284
|
-
if (this.matchesPattern(packageName, pattern)) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Check include patterns
|
|
290
|
-
if (include && include.length > 0) {
|
|
291
|
-
for (const pattern of include) {
|
|
292
|
-
if (this.matchesPattern(packageName, pattern)) {
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return false; // If include patterns exist, package must match one
|
|
297
|
-
}
|
|
298
|
-
return true; // Include by default if no patterns
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Check if package matches a pattern (simple glob-like matching)
|
|
302
|
-
*/
|
|
303
|
-
matchesPattern(packageName, pattern) {
|
|
304
|
-
// Convert glob pattern to regex
|
|
305
|
-
const regexPattern = pattern.replace(/\*/g, '.*').replace(/\?/g, '.');
|
|
306
|
-
const regex = new RegExp(`^${regexPattern}$`, 'i');
|
|
307
|
-
return regex.test(packageName);
|
|
308
|
-
}
|
|
309
402
|
/**
|
|
310
403
|
* Check if a package needs updating
|
|
311
404
|
*/
|
|
@@ -517,5 +610,124 @@ export class CatalogUpdateService {
|
|
|
517
610
|
}
|
|
518
611
|
return recommendations;
|
|
519
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Create sync version updates for packages that should be synchronized across catalogs
|
|
615
|
+
*/
|
|
616
|
+
async createSyncVersionUpdates(syncVersions, workspace, existingUpdates) {
|
|
617
|
+
const syncUpdates = [];
|
|
618
|
+
const catalogs = workspace.getCatalogs();
|
|
619
|
+
const allCatalogs = catalogs.getAll();
|
|
620
|
+
for (const packageName of syncVersions) {
|
|
621
|
+
// Check if this package exists in multiple catalogs
|
|
622
|
+
const catalogsWithPackage = allCatalogs.filter((catalog) => catalog && catalog.getDependencyVersion(packageName));
|
|
623
|
+
if (catalogsWithPackage.length <= 1) {
|
|
624
|
+
continue; // No need to sync if package is only in one catalog
|
|
625
|
+
}
|
|
626
|
+
// Find the highest version from existing updates or determine target version
|
|
627
|
+
let targetVersion = null;
|
|
628
|
+
let targetUpdateType = 'patch';
|
|
629
|
+
// Check if this package has any existing updates
|
|
630
|
+
const existingUpdate = existingUpdates.find((u) => u.packageName === packageName);
|
|
631
|
+
if (existingUpdate) {
|
|
632
|
+
targetVersion = existingUpdate.newVersion;
|
|
633
|
+
targetUpdateType = existingUpdate.updateType;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
// Get latest version for this package
|
|
637
|
+
try {
|
|
638
|
+
const packageInfo = await this.registryService.getPackageInfo(packageName);
|
|
639
|
+
targetVersion = packageInfo.latestVersion;
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
console.warn(`Failed to get version info for sync package ${packageName}:`, error);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (!targetVersion)
|
|
647
|
+
continue;
|
|
648
|
+
// Create sync updates for all catalogs that need updating
|
|
649
|
+
for (const catalog of catalogsWithPackage) {
|
|
650
|
+
const currentRange = catalog.getDependencyVersion(packageName);
|
|
651
|
+
if (!currentRange)
|
|
652
|
+
continue;
|
|
653
|
+
const currentVersion = currentRange.getMinVersion();
|
|
654
|
+
if (!currentVersion)
|
|
655
|
+
continue;
|
|
656
|
+
const currentVersionString = currentVersion.toString();
|
|
657
|
+
// Skip if already at target version
|
|
658
|
+
if (currentVersionString === targetVersion)
|
|
659
|
+
continue;
|
|
660
|
+
// Check if this update already exists
|
|
661
|
+
const existingUpdateForCatalog = existingUpdates.find((u) => u.packageName === packageName && u.catalogName === catalog.getName());
|
|
662
|
+
if (existingUpdateForCatalog) {
|
|
663
|
+
// Update the existing update to use the sync version
|
|
664
|
+
existingUpdateForCatalog.newVersion = targetVersion;
|
|
665
|
+
existingUpdateForCatalog.reason = `Sync version across catalogs: ${existingUpdateForCatalog.reason}`;
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
// Create new sync update
|
|
669
|
+
const affectedPackages = workspace
|
|
670
|
+
.getPackagesUsingCatalogDependency(catalog.getName(), packageName)
|
|
671
|
+
.getPackageNames();
|
|
672
|
+
const syncUpdate = {
|
|
673
|
+
catalogName: catalog.getName(),
|
|
674
|
+
packageName: packageName,
|
|
675
|
+
currentVersion: currentVersionString,
|
|
676
|
+
newVersion: targetVersion,
|
|
677
|
+
updateType: targetUpdateType,
|
|
678
|
+
reason: `Sync version with other catalogs`,
|
|
679
|
+
affectedPackages,
|
|
680
|
+
requireConfirmation: true, // Sync updates should be confirmed
|
|
681
|
+
autoUpdate: false,
|
|
682
|
+
groupUpdate: true,
|
|
683
|
+
};
|
|
684
|
+
syncUpdates.push(syncUpdate);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return syncUpdates;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Resolve version conflicts using catalog priority
|
|
692
|
+
*/
|
|
693
|
+
resolveVersionConflict(packageName, packageUpdates, catalogPriority) {
|
|
694
|
+
// Find the highest priority catalog with an update for this package
|
|
695
|
+
let priorityCatalog = null;
|
|
696
|
+
for (const catalogName of catalogPriority) {
|
|
697
|
+
const catalogUpdate = packageUpdates.find((u) => u.catalogName === catalogName);
|
|
698
|
+
if (catalogUpdate) {
|
|
699
|
+
priorityCatalog = catalogUpdate;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// If no priority catalog found, use the first one
|
|
704
|
+
if (!priorityCatalog) {
|
|
705
|
+
priorityCatalog = packageUpdates[0] || null;
|
|
706
|
+
}
|
|
707
|
+
// Update all other catalogs to use the priority catalog's version
|
|
708
|
+
const priorityVersion = priorityCatalog?.newVersion;
|
|
709
|
+
if (priorityVersion && priorityCatalog) {
|
|
710
|
+
for (const update of packageUpdates) {
|
|
711
|
+
if (update.catalogName !== priorityCatalog.catalogName) {
|
|
712
|
+
update.newVersion = priorityVersion;
|
|
713
|
+
update.reason = `Using version from priority catalog (${priorityCatalog.catalogName}): ${update.reason}`;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// Create conflict record for reporting
|
|
718
|
+
const uniqueVersions = new Set(packageUpdates.map((u) => u.newVersion));
|
|
719
|
+
if (uniqueVersions.size > 1) {
|
|
720
|
+
return {
|
|
721
|
+
packageName,
|
|
722
|
+
catalogs: packageUpdates.map((u) => ({
|
|
723
|
+
catalogName: u.catalogName,
|
|
724
|
+
currentVersion: u.currentVersion,
|
|
725
|
+
proposedVersion: u.newVersion,
|
|
726
|
+
})),
|
|
727
|
+
recommendation: `Resolved using catalog priority. Priority catalog '${priorityCatalog?.catalogName}' version '${priorityVersion}' selected.`,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
520
732
|
}
|
|
521
733
|
//# sourceMappingURL=CatalogUpdateService.js.map
|