@manojkmfsi/monodog 1.1.0 → 1.1.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/config-loader.js +9 -10
- package/dist/constants/security.js +1 -1
- package/dist/controllers/commit-controller.js +5 -4
- package/dist/controllers/config-controller.js +4 -3
- package/dist/controllers/health-controller.js +2 -1
- package/dist/controllers/package-controller.js +4 -3
- package/dist/middleware/logger.js +8 -2
- package/dist/middleware/server-startup.js +2 -2
- package/dist/middleware/swagger-middleware.js +3 -2
- package/dist/repositories/commit-repository.js +4 -3
- package/dist/repositories/dependency-repository.js +4 -3
- package/dist/services/commit-service.js +7 -7
- package/dist/services/config-service.js +12 -11
- package/dist/services/git-service.js +9 -8
- package/dist/services/health-service.js +5 -4
- package/dist/services/package-service.js +5 -4
- package/dist/utils/ci-status.js +3 -2
- package/dist/utils/db-utils.js +87 -0
- package/dist/utils/monorepo-scanner.js +8 -7
- package/dist/utils/utilities.js +21 -18
- package/monodog-config.example.json +2 -2
- package/monodog-config.json +5 -4
- package/package.json +1 -1
- package/src/config-loader.ts +11 -13
- package/src/constants/security.ts +1 -1
- package/src/controllers/commit-controller.ts +5 -4
- package/src/controllers/config-controller.ts +4 -3
- package/src/controllers/health-controller.ts +2 -1
- package/src/controllers/package-controller.ts +4 -3
- package/src/middleware/logger.ts +9 -2
- package/src/middleware/server-startup.ts +2 -2
- package/src/middleware/swagger-middleware.ts +3 -2
- package/src/repositories/commit-repository.ts +4 -3
- package/src/repositories/dependency-repository.ts +4 -3
- package/src/services/commit-service.ts +7 -7
- package/src/services/config-service.ts +12 -11
- package/src/services/git-service.ts +9 -8
- package/src/services/health-service.ts +5 -4
- package/src/services/package-service.ts +5 -4
- package/src/utils/ci-status.ts +3 -2
- package/src/utils/db-utils.ts +92 -0
- package/src/utils/monorepo-scanner.ts +8 -10
- package/src/utils/utilities.ts +22 -20
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { GitService } from './git-service';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import { AppLogger } from '../middleware/logger';
|
|
4
5
|
|
|
5
6
|
export const getCommitsByPathService = async (packagePath: string) => {
|
|
6
7
|
|
|
7
8
|
// Decode the package path
|
|
8
9
|
const decodedPath = decodeURIComponent(packagePath);
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
AppLogger.debug('Fetching commits for path: ' + decodedPath);
|
|
12
|
+
AppLogger.debug('Current working directory: ' + process.cwd());
|
|
12
13
|
|
|
13
14
|
const gitService = new GitService();
|
|
14
15
|
|
|
@@ -19,22 +20,21 @@ export const getCommitsByPathService = async (packagePath: string) => {
|
|
|
19
20
|
// If it's an absolute path, make it relative to project root
|
|
20
21
|
if (path.isAbsolute(decodedPath)) {
|
|
21
22
|
relativePath = path.relative(projectRoot, decodedPath);
|
|
22
|
-
|
|
23
|
+
AppLogger.debug('Converted absolute path to relative: ' + relativePath);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
// Check if the path exists
|
|
26
27
|
try {
|
|
27
28
|
await fs.promises.access(relativePath);
|
|
28
|
-
|
|
29
|
+
AppLogger.debug('Path exists: ' + relativePath);
|
|
29
30
|
} catch (fsError) {
|
|
30
|
-
console.log('Path does not exist:', relativePath);
|
|
31
31
|
// Try the original path as well
|
|
32
32
|
try {
|
|
33
33
|
await fs.promises.access(decodedPath);
|
|
34
|
-
|
|
34
|
+
AppLogger.debug('Original Commit path exists: ' + decodedPath);
|
|
35
35
|
relativePath = decodedPath; // Use original path if it exists
|
|
36
36
|
} catch (secondError) {
|
|
37
|
-
throw new Error(`Path does not exist: ${decodedPath}`);
|
|
37
|
+
throw new Error(`Commit Path does not exist: ${decodedPath}`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
+
import { AppLogger } from '../middleware/logger';
|
|
4
5
|
import { PackageRepository } from '../repositories';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -91,13 +92,13 @@ async function scanConfigFiles(rootDir: string): Promise<any[]> {
|
|
|
91
92
|
hasSecrets: containsSecrets(content, item.name),
|
|
92
93
|
});
|
|
93
94
|
} catch (error) {
|
|
94
|
-
|
|
95
|
+
AppLogger.warn(`Could not read file: ${fullPath}`);
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
} catch (error) {
|
|
100
|
-
|
|
101
|
+
AppLogger.warn(`Could not scan directory: ${dir}`);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -139,7 +140,7 @@ async function scanConfigFiles(rootDir: string): Promise<any[]> {
|
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
|
|
143
|
+
AppLogger.info(`Scanning for config files in: ${rootDir}`);
|
|
143
144
|
|
|
144
145
|
// Start scanning from root
|
|
145
146
|
scanDirectory(rootDir);
|
|
@@ -147,16 +148,16 @@ async function scanConfigFiles(rootDir: string): Promise<any[]> {
|
|
|
147
148
|
// Sort files by path for consistent ordering
|
|
148
149
|
configFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
149
150
|
|
|
150
|
-
|
|
151
|
+
AppLogger.info(`Found ${configFiles.length} configuration files`);
|
|
151
152
|
|
|
152
153
|
// Log some sample files for debugging
|
|
153
154
|
if (configFiles.length > 0) {
|
|
154
|
-
|
|
155
|
+
AppLogger.info('Sample config files found');
|
|
155
156
|
configFiles.slice(0, 5).forEach(file => {
|
|
156
|
-
|
|
157
|
+
AppLogger.debug(` - ${file.path} (${file.type})`);
|
|
157
158
|
});
|
|
158
159
|
if (configFiles.length > 5) {
|
|
159
|
-
|
|
160
|
+
AppLogger.debug(` ... and ${configFiles.length - 5} more`);
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
|
@@ -244,8 +245,8 @@ export const updateConfigFileService = async (id: string, rootDir: string, conte
|
|
|
244
245
|
id.startsWith('/') ? id.slice(1) : id
|
|
245
246
|
);
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
AppLogger.debug('Saving file: ' + filePath);
|
|
249
|
+
AppLogger.debug('Root directory: ' + rootDir);
|
|
249
250
|
|
|
250
251
|
// Security check: ensure the file is within the project directory
|
|
251
252
|
if (!filePath.startsWith(rootDir)) {
|
|
@@ -289,7 +290,7 @@ export const updatePackageConfigurationService = async (packagePath: string, pac
|
|
|
289
290
|
try {
|
|
290
291
|
newConfig = JSON.parse(config);
|
|
291
292
|
} catch (error) {
|
|
292
|
-
|
|
293
|
+
AppLogger.error('JSON parsing error', error as Error);
|
|
293
294
|
throw new Error('Invalid JSON format: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
294
295
|
}
|
|
295
296
|
|
|
@@ -362,7 +363,7 @@ export const updatePackageConfigurationService = async (packagePath: string, pac
|
|
|
362
363
|
if (newConfig.peerDependencies)
|
|
363
364
|
updateData.peerDependencies = JSON.stringify(newConfig.peerDependencies);
|
|
364
365
|
|
|
365
|
-
|
|
366
|
+
AppLogger.debug('Updating database with:', updateData);
|
|
366
367
|
|
|
367
368
|
const updatedPackage = await PackageRepository.updateConfig(packageName, updateData);
|
|
368
369
|
|
|
@@ -9,6 +9,7 @@ import { exec } from 'child_process';
|
|
|
9
9
|
import { promisify } from 'util';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
|
|
12
|
+
import { AppLogger } from '../middleware/logger';
|
|
12
13
|
import type { Commit } from '../types';
|
|
13
14
|
import { VALID_COMMIT_TYPES } from '../types';
|
|
14
15
|
|
|
@@ -40,10 +41,10 @@ export class GitService {
|
|
|
40
41
|
// First, validate we're in a git repo
|
|
41
42
|
await this.validateGitRepository(pathArgument);
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
AppLogger.debug(`Executing Git command in: ${this.repoPath}`);
|
|
44
45
|
// Use a simpler git log format
|
|
45
46
|
const command = `git log --pretty=format:"%H|%an|%ad|%s" --date=iso-strict ${pathArgument}`;
|
|
46
|
-
|
|
47
|
+
AppLogger.debug(`Git command: ${command}`);
|
|
47
48
|
|
|
48
49
|
const { stdout, stderr } = await execPromise(command, {
|
|
49
50
|
cwd: this.repoPath,
|
|
@@ -51,11 +52,11 @@ export class GitService {
|
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
if (stderr) {
|
|
54
|
-
|
|
55
|
+
AppLogger.warn('Git stderr: ' + stderr);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
if (!stdout.trim()) {
|
|
58
|
-
|
|
59
|
+
AppLogger.debug('No commits found for path: ' + pathFilter);
|
|
59
60
|
return [];
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -78,14 +79,14 @@ export class GitService {
|
|
|
78
79
|
|
|
79
80
|
commits.push(commit);
|
|
80
81
|
} catch (parseError) {
|
|
81
|
-
|
|
82
|
+
AppLogger.error('Failed to parse commit line: ' + line, parseError as Error);
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
AppLogger.debug(`Successfully parsed ${commits.length} commits`);
|
|
86
87
|
return commits;
|
|
87
88
|
} catch (error) {
|
|
88
|
-
|
|
89
|
+
AppLogger.error('Error in getAllCommits', error as Error);
|
|
89
90
|
throw error;
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -129,7 +130,7 @@ export class GitService {
|
|
|
129
130
|
await execPromise('git '+pathArgument+' rev-parse --is-inside-work-tree', {
|
|
130
131
|
cwd: this.repoPath,
|
|
131
132
|
});
|
|
132
|
-
|
|
133
|
+
AppLogger.debug('Valid git repository');
|
|
133
134
|
} catch (error) {
|
|
134
135
|
throw new Error(
|
|
135
136
|
'Not a git repository (or any of the parent directories)'
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
funCheckSecurityAudit,
|
|
10
10
|
} from '../utils/monorepo-scanner';
|
|
11
11
|
|
|
12
|
+
import { AppLogger } from '../middleware/logger';
|
|
12
13
|
import { PackageHealthRepository, PackageRepository } from '../repositories';
|
|
13
14
|
import type { TransformedPackageHealth, HealthResponse, PackageHealthModel } from '../types/database';
|
|
14
15
|
|
|
@@ -17,7 +18,7 @@ let inFlightHealthRefresh: Promise<HealthResponse> | null = null;
|
|
|
17
18
|
|
|
18
19
|
export const getHealthSummaryService = async (): Promise<HealthResponse> => {
|
|
19
20
|
const packageHealthData = await PackageHealthRepository.findAll() as PackageHealthModel[];
|
|
20
|
-
|
|
21
|
+
AppLogger.debug('packageHealthData count: ' + packageHealthData.length);
|
|
21
22
|
|
|
22
23
|
// Transform the data to match the expected frontend format
|
|
23
24
|
const packages = packageHealthData.map((pkg: PackageHealthModel) => {
|
|
@@ -60,7 +61,7 @@ export const getHealthSummaryService = async (): Promise<HealthResponse> => {
|
|
|
60
61
|
export const healthRefreshService = async (rootDir: string) => {
|
|
61
62
|
// If a health refresh is already in progress, return the in-flight promise
|
|
62
63
|
if (inFlightHealthRefresh) {
|
|
63
|
-
|
|
64
|
+
AppLogger.info('Health refresh already in progress, returning cached promise');
|
|
64
65
|
return inFlightHealthRefresh;
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -68,7 +69,7 @@ export const healthRefreshService = async (rootDir: string) => {
|
|
|
68
69
|
inFlightHealthRefresh = (async () => {
|
|
69
70
|
try {
|
|
70
71
|
const packages = scanMonorepo(rootDir);
|
|
71
|
-
|
|
72
|
+
AppLogger.debug('packages count: ' + packages.length);
|
|
72
73
|
const healthMetrics = await Promise.all(
|
|
73
74
|
packages.map(async pkg => {
|
|
74
75
|
try {
|
|
@@ -99,7 +100,7 @@ export const healthRefreshService = async (rootDir: string) => {
|
|
|
99
100
|
? 'warning'
|
|
100
101
|
: 'error';
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
AppLogger.debug(`${pkg.name}: ${packageStatus}`, health);
|
|
103
104
|
|
|
104
105
|
await PackageHealthRepository.upsert({
|
|
105
106
|
packageName: pkg.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { scanMonorepo } from '../utils/utilities';
|
|
2
2
|
import { generateReports } from '../utils/monorepo-scanner';
|
|
3
3
|
import { ciStatusManager } from '../utils/ci-status';
|
|
4
|
+
import { AppLogger } from '../middleware/logger';
|
|
4
5
|
import { PackageRepository, CommitRepository, DependencyRepository } from '../repositories';
|
|
5
6
|
import type { PackageInfo, DependencyInfo, PackageReport } from '../types';
|
|
6
7
|
import { getCommitsByPathService } from './commit-service';
|
|
@@ -98,7 +99,7 @@ async function storePackage(pkg: PackageInfo): Promise<void> {
|
|
|
98
99
|
await DependencyRepository.storeMany(pkg.name, dependenciesInfo);
|
|
99
100
|
}
|
|
100
101
|
} catch (error) {
|
|
101
|
-
|
|
102
|
+
AppLogger.warn(`Failed to store report for ${pkg.name}`);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -107,9 +108,9 @@ export const getPackagesService = async (rootPath: string) => {
|
|
|
107
108
|
if (!dbPackages.length) {
|
|
108
109
|
try {
|
|
109
110
|
const rootDir = rootPath;
|
|
110
|
-
|
|
111
|
+
AppLogger.debug('rootDir: ' + rootDir);
|
|
111
112
|
const packages = scanMonorepo(rootDir);
|
|
112
|
-
|
|
113
|
+
AppLogger.debug('packages scanned: ' + packages.length);
|
|
113
114
|
for (const pkg of packages) {
|
|
114
115
|
await storePackage(pkg);
|
|
115
116
|
}
|
|
@@ -155,7 +156,7 @@ export const refreshPackagesService = async (rootPath: string) => {
|
|
|
155
156
|
const rootDir = rootPath;
|
|
156
157
|
const packages = scanMonorepo(rootDir);
|
|
157
158
|
|
|
158
|
-
|
|
159
|
+
AppLogger.debug('packages count: ' + packages.length);
|
|
159
160
|
for (const pkg of packages) {
|
|
160
161
|
await storePackage(pkg);
|
|
161
162
|
}
|
package/src/utils/ci-status.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AppLogger } from '../middleware/logger';
|
|
1
2
|
import type { PackageInfo } from './utilities';
|
|
2
3
|
import type {
|
|
3
4
|
CIProvider,
|
|
@@ -130,7 +131,7 @@ export class CIStatusManager {
|
|
|
130
131
|
this.setCache(cacheKey, status);
|
|
131
132
|
return status;
|
|
132
133
|
} catch (error) {
|
|
133
|
-
|
|
134
|
+
AppLogger.error(`Error fetching CI status for ${packageName}`, error as Error);
|
|
134
135
|
return null;
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -468,7 +469,7 @@ export class CIStatusManager {
|
|
|
468
469
|
// Mock implementation
|
|
469
470
|
const buildId = `build-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
470
471
|
|
|
471
|
-
|
|
472
|
+
AppLogger.info(
|
|
472
473
|
`Triggering build for ${packageName} on ${branch} via ${providerName}`
|
|
473
474
|
);
|
|
474
475
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { PackageInfo, DependencyInfo, Commit } from '../types';
|
|
2
|
+
import { AppLogger } from '../middleware/logger';
|
|
3
|
+
import { CommitRepository, DependencyRepository, PackageRepository } from '../repositories';
|
|
4
|
+
import { appConfig } from '../config-loader';
|
|
5
|
+
|
|
6
|
+
// Default settings
|
|
7
|
+
const DEFAULT_PORT = 4000;
|
|
8
|
+
const port = appConfig.server.port ?? DEFAULT_PORT; //Default port
|
|
9
|
+
const host = appConfig.server.host ?? 'localhost'; //Default host
|
|
10
|
+
|
|
11
|
+
const API_BASE = `http://${host}:${port}/api`;
|
|
12
|
+
|
|
13
|
+
async function getCommits(path: string): Promise<Commit[]> {
|
|
14
|
+
const res = await fetch(API_BASE + `/commits/` + encodeURIComponent(path));
|
|
15
|
+
if (!res.ok) throw new Error('Failed to fetch commits');
|
|
16
|
+
return (await res.json()) as Commit[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get package dependencies
|
|
21
|
+
*/
|
|
22
|
+
function getPackageDependenciesInfo(pkg: PackageInfo): DependencyInfo[] {
|
|
23
|
+
const allDeps: DependencyInfo[] = [];
|
|
24
|
+
Object.keys(pkg.dependencies || {}).forEach(depName => {
|
|
25
|
+
allDeps.push({
|
|
26
|
+
name: depName,
|
|
27
|
+
version: pkg.dependencies[depName],
|
|
28
|
+
type: 'dependency',
|
|
29
|
+
latest: '',
|
|
30
|
+
outdated: false,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
Object.keys(pkg.devDependencies || {}).forEach(depName => {
|
|
34
|
+
allDeps.push({
|
|
35
|
+
name: depName,
|
|
36
|
+
version: pkg.devDependencies[depName],
|
|
37
|
+
type: 'devDependency',
|
|
38
|
+
latest: '',
|
|
39
|
+
outdated: false,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
Object.keys(pkg.peerDependencies || {}).forEach(depName => {
|
|
43
|
+
allDeps.push({
|
|
44
|
+
name: depName,
|
|
45
|
+
version: pkg.peerDependencies[depName]!,
|
|
46
|
+
type: 'peerDependency',
|
|
47
|
+
latest: '',
|
|
48
|
+
outdated: false,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return allDeps;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Store packages in database using repository pattern
|
|
56
|
+
*/
|
|
57
|
+
async function storePackage(pkg: PackageInfo): Promise<void> {
|
|
58
|
+
try {
|
|
59
|
+
// Create or update package using repository
|
|
60
|
+
await PackageRepository.upsert({
|
|
61
|
+
name: pkg.name,
|
|
62
|
+
version: pkg.version,
|
|
63
|
+
type: pkg.type,
|
|
64
|
+
path: pkg.path,
|
|
65
|
+
description: pkg.description,
|
|
66
|
+
license: pkg.license,
|
|
67
|
+
repository: pkg.repository,
|
|
68
|
+
scripts: pkg.scripts,
|
|
69
|
+
dependencies: pkg.dependencies,
|
|
70
|
+
devDependencies: pkg.devDependencies,
|
|
71
|
+
peerDependencies: pkg.peerDependencies,
|
|
72
|
+
maintainers: pkg.maintainers.join(','),
|
|
73
|
+
status: '',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Store commits using repository
|
|
77
|
+
const commits = await getCommits(pkg.path ?? '');
|
|
78
|
+
if (commits.length) {
|
|
79
|
+
await CommitRepository.storeMany(pkg.name, commits);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Store dependencies using repository
|
|
83
|
+
const dependenciesInfo = getPackageDependenciesInfo(pkg);
|
|
84
|
+
if (dependenciesInfo.length) {
|
|
85
|
+
await DependencyRepository.storeMany(pkg.name, dependenciesInfo);
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
AppLogger.warn(`Failed to store report for ${pkg.name}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { storePackage };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
|
+
import { AppLogger } from '../middleware/logger';
|
|
5
6
|
import type {
|
|
6
7
|
PackageInfo,
|
|
7
8
|
DependencyInfo,
|
|
@@ -43,11 +44,11 @@ export class MonorepoScanner {
|
|
|
43
44
|
return cached;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
AppLogger.debug('Starting monorepo scan...');
|
|
47
48
|
|
|
48
49
|
// Scan all packages
|
|
49
50
|
const packages = scanMonorepo(this.rootDir);
|
|
50
|
-
|
|
51
|
+
AppLogger.debug(`Found ${packages.length} packages`);
|
|
51
52
|
|
|
52
53
|
// Generate statistics
|
|
53
54
|
const stats = generateMonorepoStats(packages);
|
|
@@ -74,10 +75,10 @@ export class MonorepoScanner {
|
|
|
74
75
|
// Cache the result
|
|
75
76
|
this.setCache(cacheKey, result);
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
AppLogger.debug(`Scan completed in ${result.scanDuration}ms`);
|
|
78
79
|
return result;
|
|
79
80
|
} catch (error) {
|
|
80
|
-
|
|
81
|
+
AppLogger.error('Error during scan', error as Error);
|
|
81
82
|
throw error;
|
|
82
83
|
}
|
|
83
84
|
}
|
|
@@ -94,7 +95,7 @@ export class MonorepoScanner {
|
|
|
94
95
|
const report = await this.generatePackageReport(pkg);
|
|
95
96
|
reports.push(report);
|
|
96
97
|
} catch (error) {
|
|
97
|
-
|
|
98
|
+
AppLogger.error(`Error generating report for ${pkg.name}`, error as Error);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
@@ -206,10 +207,7 @@ export class MonorepoScanner {
|
|
|
206
207
|
0
|
|
207
208
|
);
|
|
208
209
|
} catch (error) {
|
|
209
|
-
|
|
210
|
-
`Error parsing coverage file for ${pkg.name}:`,
|
|
211
|
-
error
|
|
212
|
-
);
|
|
210
|
+
AppLogger.warn(`Error parsing coverage file for ${pkg.name}`);
|
|
213
211
|
}
|
|
214
212
|
}
|
|
215
213
|
// If we find any coverage file but can't parse it, assume coverage exists
|
|
@@ -232,7 +230,7 @@ export class MonorepoScanner {
|
|
|
232
230
|
|
|
233
231
|
return 0;
|
|
234
232
|
} catch (error) {
|
|
235
|
-
|
|
233
|
+
AppLogger.warn(`Error checking coverage for ${pkg.name}`);
|
|
236
234
|
return 0;
|
|
237
235
|
}
|
|
238
236
|
}
|
package/src/utils/utilities.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { appConfig } from '../config-loader';
|
|
5
|
+
import { AppLogger } from '../middleware/logger';
|
|
5
6
|
import {calculatePackageHealth} from './health-utils';
|
|
6
7
|
import * as yaml from 'js-yaml';
|
|
7
8
|
|
|
@@ -64,7 +65,7 @@ function getWorkspacesFromPnpmYaml(rootDir: string): string[] | undefined {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
} catch (e) {
|
|
67
|
-
|
|
68
|
+
AppLogger.error(`Error parsing pnpm-workspace.yaml at ${workspaceYamlPath}:`, e as Error);
|
|
68
69
|
}
|
|
69
70
|
return undefined;
|
|
70
71
|
}
|
|
@@ -83,27 +84,27 @@ export function getWorkspacesFromRoot(rootDir: string): string[] | undefined {
|
|
|
83
84
|
|
|
84
85
|
// Handle both standard array and object format (used by yarn/pnpm)
|
|
85
86
|
if (Array.isArray(packageJson.workspaces)) {
|
|
86
|
-
|
|
87
|
+
AppLogger.info('Workspace configuration found in package.json');
|
|
87
88
|
return packageJson.workspaces;
|
|
88
89
|
} else if (packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)) {
|
|
89
|
-
|
|
90
|
+
AppLogger.info('Workspace configuration found in package.json');
|
|
90
91
|
return packageJson.workspaces.packages;
|
|
91
92
|
}
|
|
92
93
|
} catch (e) {
|
|
93
|
-
|
|
94
|
+
AppLogger.error(`Error parsing package.json at ${packageJsonPath}. Attempting to read pnpm-workspace.yaml...`, e as Error);
|
|
94
95
|
}
|
|
95
96
|
} else {
|
|
96
|
-
|
|
97
|
+
AppLogger.warn(`No package.json found at root directory: ${rootDir}`);
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
// Fallback to pnpm-workspace.yaml
|
|
100
101
|
const pnpmWorkspaces = getWorkspacesFromPnpmYaml(rootDir);
|
|
101
102
|
if (pnpmWorkspaces && pnpmWorkspaces.length > 0) {
|
|
102
|
-
|
|
103
|
+
AppLogger.info('Workspace configuration found in pnpm-workspace.yaml');
|
|
103
104
|
return pnpmWorkspaces;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
AppLogger.warn('No workspace configuration found in package.json or pnpm-workspace.yaml');
|
|
107
108
|
return undefined;
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -112,22 +113,24 @@ export function getWorkspacesFromRoot(rootDir: string): string[] | undefined {
|
|
|
112
113
|
*/
|
|
113
114
|
function scanMonorepo(rootDir: string): PackageInfo[] {
|
|
114
115
|
const packages: PackageInfo[] = [];
|
|
115
|
-
|
|
116
|
+
AppLogger.debug('rootDir: ' + rootDir);
|
|
116
117
|
const workspacesGlobs = appConfig.workspaces;
|
|
117
118
|
// Use provided workspaces globs if given, otherwise attempt to detect from root package.json or pnpm-workspace.yaml
|
|
118
119
|
const detectedWorkspacesGlobs = workspacesGlobs.length > 0 ? workspacesGlobs : getWorkspacesFromRoot(rootDir);
|
|
119
120
|
if (detectedWorkspacesGlobs && detectedWorkspacesGlobs.length > 0) {
|
|
120
121
|
if (workspacesGlobs.length) {
|
|
121
|
-
|
|
122
|
+
AppLogger.info(`Using provided workspaces globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
122
123
|
} else {
|
|
123
|
-
|
|
124
|
+
AppLogger.info(`Detected Monorepo Workspaces Globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
// 1. Resolve the globs into concrete package directory paths
|
|
127
128
|
const resolvedPackagePaths = resolveWorkspaceGlobs(rootDir, detectedWorkspacesGlobs);
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
AppLogger.debug(`Resolved package directories (Total ${resolvedPackagePaths.length})`);
|
|
131
|
+
if (resolvedPackagePaths.length < workspacesGlobs.length) {
|
|
132
|
+
AppLogger.warn('Some workspaces globs provided are invalid.');
|
|
133
|
+
}
|
|
131
134
|
|
|
132
135
|
// 2. Integration of the requested loop structure for package scanning
|
|
133
136
|
for (const workspacePath of resolvedPackagePaths) {
|
|
@@ -135,7 +138,7 @@ function scanMonorepo(rootDir: string): PackageInfo[] {
|
|
|
135
138
|
// The package name would be read from the package.json inside this path
|
|
136
139
|
const packageName = path.basename(fullPackagePath);
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
AppLogger.debug(`- Scanning path: ${workspacePath} (Package: ${packageName})`);
|
|
139
142
|
|
|
140
143
|
const packageInfo = parsePackageInfo(fullPackagePath, packageName);
|
|
141
144
|
if (packageInfo) {
|
|
@@ -143,7 +146,7 @@ function scanMonorepo(rootDir: string): PackageInfo[] {
|
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
} else {
|
|
146
|
-
|
|
149
|
+
AppLogger.warn('No workspace globs provided or detected. Returning empty package list.');
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
return packages;
|
|
@@ -189,7 +192,7 @@ export function parsePackageInfo(
|
|
|
189
192
|
repository: packageJson.repository || {},
|
|
190
193
|
};
|
|
191
194
|
} catch (error) {
|
|
192
|
-
|
|
195
|
+
AppLogger.error(`Error parsing package.json for ${packageName}`, error as Error);
|
|
193
196
|
return null;
|
|
194
197
|
}
|
|
195
198
|
}
|
|
@@ -384,7 +387,7 @@ function findMonorepoRoot(): string {
|
|
|
384
387
|
);
|
|
385
388
|
// If it has workspaces or is the root monorepo package
|
|
386
389
|
if (packageJson.workspaces || fs.existsSync(pnpmWorkspacePath)) {
|
|
387
|
-
|
|
390
|
+
AppLogger.debug('Found monorepo root: ' + currentDir);
|
|
388
391
|
return currentDir;
|
|
389
392
|
}
|
|
390
393
|
} catch (error) {
|
|
@@ -395,7 +398,7 @@ function findMonorepoRoot(): string {
|
|
|
395
398
|
// Check if we're at the git root
|
|
396
399
|
const gitPath = path.join(currentDir, '.git');
|
|
397
400
|
if (fs.existsSync(gitPath)) {
|
|
398
|
-
|
|
401
|
+
AppLogger.debug('Found git root (likely monorepo root): ' + currentDir);
|
|
399
402
|
return currentDir;
|
|
400
403
|
}
|
|
401
404
|
|
|
@@ -406,9 +409,8 @@ function findMonorepoRoot(): string {
|
|
|
406
409
|
}
|
|
407
410
|
|
|
408
411
|
// Fallback to process.cwd() if we can't find the root
|
|
409
|
-
|
|
410
|
-
'Could not find monorepo root, using process.cwd():'
|
|
411
|
-
process.cwd()
|
|
412
|
+
AppLogger.warn(
|
|
413
|
+
'Could not find monorepo root, using process.cwd(): ' + process.cwd()
|
|
412
414
|
);
|
|
413
415
|
return process.cwd();
|
|
414
416
|
}
|