@manojkmfsi/monodog 1.0.1
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/.eslintrc.cjs +15 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +79 -0
- package/LICENCE +21 -0
- package/README.md +55 -0
- package/dist/config-loader.js +116 -0
- package/dist/get-db-url.js +10 -0
- package/dist/gitService.js +242 -0
- package/dist/index.js +1370 -0
- package/dist/serve.js +103 -0
- package/dist/setup.js +155 -0
- package/dist/utils/ci-status.js +446 -0
- package/dist/utils/helpers.js +237 -0
- package/dist/utils/monorepo-scanner.js +486 -0
- package/dist/utils/utilities.js +414 -0
- package/monodog-conf.example.json +16 -0
- package/monodog-conf.json +16 -0
- package/monodog-dashboard/README.md +58 -0
- package/monodog-dashboard/dist/assets/index-2d967652.js +71 -0
- package/monodog-dashboard/dist/assets/index-504dc418.css +1 -0
- package/monodog-dashboard/dist/index.html +15 -0
- package/package.json +50 -0
- package/prisma/migrations/20251017041048_init/migration.sql +19 -0
- package/prisma/migrations/20251017083007_add_package/migration.sql +21 -0
- package/prisma/migrations/20251021083705_alter_package/migration.sql +37 -0
- package/prisma/migrations/20251022085155_test/migration.sql +2 -0
- package/prisma/migrations/20251022160841_/migration.sql +35 -0
- package/prisma/migrations/20251023130158_rename_column_name/migration.sql +34 -0
- package/prisma/migrations/20251023174837_/migration.sql +34 -0
- package/prisma/migrations/20251023175830_uodate_schema/migration.sql +32 -0
- package/prisma/migrations/20251024103700_add_dependency_info/migration.sql +13 -0
- package/prisma/migrations/20251025192150_add_dependency_info/migration.sql +19 -0
- package/prisma/migrations/20251025192342_add_dependency_info/migration.sql +40 -0
- package/prisma/migrations/20251025204613_add_dependency_info/migration.sql +8 -0
- package/prisma/migrations/20251026071336_add_dependency_info/migration.sql +25 -0
- package/prisma/migrations/20251027062626_add_commit/migration.sql +10 -0
- package/prisma/migrations/20251027062748_add_commit/migration.sql +23 -0
- package/prisma/migrations/20251027092741_add_commit/migration.sql +17 -0
- package/prisma/migrations/20251027112736_add_health_status/migration.sql +16 -0
- package/prisma/migrations/20251027140546_init_packages/migration.sql +16 -0
- package/prisma/migrations/20251029073436_added_package_heath_key/migration.sql +34 -0
- package/prisma/migrations/20251029073830_added_package_health_key/migration.sql +49 -0
- package/prisma/migrations/20251111091920_/migration.sql +16 -0
- package/prisma/migrations/20251211155036_cascade_on_auto_delete/migration.sql +48 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +114 -0
- package/release.config.js +41 -0
- package/src/config-loader.ts +119 -0
- package/src/get-db-url.ts +11 -0
- package/src/gitService.ts +277 -0
- package/src/index.ts +1554 -0
- package/src/serve.ts +87 -0
- package/src/setup.ts +164 -0
- package/src/types/monorepo-scanner.d.ts +32 -0
- package/src/utils/ci-status.ts +639 -0
- package/src/utils/helpers.js +203 -0
- package/src/utils/helpers.js.map +1 -0
- package/src/utils/helpers.ts +238 -0
- package/src/utils/monorepo-scanner.ts +599 -0
- package/src/utils/utilities.ts +483 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ciStatusManager = exports.CIStatusManager = void 0;
|
|
4
|
+
exports.getPackageCIStatus = getPackageCIStatus;
|
|
5
|
+
exports.getMonorepoCIStatus = getMonorepoCIStatus;
|
|
6
|
+
exports.triggerPackageBuild = triggerPackageBuild;
|
|
7
|
+
class CIStatusManager {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.providers = new Map();
|
|
10
|
+
this.cache = new Map();
|
|
11
|
+
this.cacheExpiry = 2 * 60 * 1000; // 2 minutes
|
|
12
|
+
this.initializeDefaultProviders();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Initialize default CI providers
|
|
16
|
+
*/
|
|
17
|
+
initializeDefaultProviders() {
|
|
18
|
+
// GitHub Actions
|
|
19
|
+
this.addProvider({
|
|
20
|
+
name: 'GitHub Actions',
|
|
21
|
+
type: 'github',
|
|
22
|
+
baseUrl: 'https://api.github.com',
|
|
23
|
+
});
|
|
24
|
+
// GitLab CI
|
|
25
|
+
this.addProvider({
|
|
26
|
+
name: 'GitLab CI',
|
|
27
|
+
type: 'gitlab',
|
|
28
|
+
baseUrl: 'https://gitlab.com/api/v4',
|
|
29
|
+
});
|
|
30
|
+
// CircleCI
|
|
31
|
+
this.addProvider({
|
|
32
|
+
name: 'CircleCI',
|
|
33
|
+
type: 'circleci',
|
|
34
|
+
baseUrl: 'https://circleci.com/api/v2',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Add a CI provider
|
|
39
|
+
*/
|
|
40
|
+
addProvider(provider) {
|
|
41
|
+
this.providers.set(provider.name, provider);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Remove a CI provider
|
|
45
|
+
*/
|
|
46
|
+
removeProvider(name) {
|
|
47
|
+
this.providers.delete(name);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get all registered providers
|
|
51
|
+
*/
|
|
52
|
+
getProviders() {
|
|
53
|
+
return Array.from(this.providers.values());
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Fetch CI status for a specific package
|
|
57
|
+
*/
|
|
58
|
+
async getPackageStatus(packageName, providerName) {
|
|
59
|
+
const cacheKey = `package-status-${packageName}-${providerName || 'all'}`;
|
|
60
|
+
const cached = this.getFromCache(cacheKey);
|
|
61
|
+
if (cached) {
|
|
62
|
+
return cached;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
let builds = [];
|
|
66
|
+
if (providerName) {
|
|
67
|
+
const provider = this.providers.get(providerName);
|
|
68
|
+
if (provider) {
|
|
69
|
+
builds = await this.fetchBuildsFromProvider(provider, packageName);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Fetch from all providers
|
|
74
|
+
for (const provider of this.providers.values()) {
|
|
75
|
+
const providerBuilds = await this.fetchBuildsFromProvider(provider, packageName);
|
|
76
|
+
builds.push(...providerBuilds);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (builds.length === 0) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// Sort builds by start time (newest first)
|
|
83
|
+
builds.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());
|
|
84
|
+
const lastBuild = builds[0];
|
|
85
|
+
const buildHistory = builds.slice(0, 10); // Last 10 builds
|
|
86
|
+
const successRate = this.calculateSuccessRate(builds);
|
|
87
|
+
const averageDuration = this.calculateAverageDuration(builds);
|
|
88
|
+
const isHealthy = this.determinePackageHealth(builds);
|
|
89
|
+
const issues = this.identifyIssues(builds);
|
|
90
|
+
const status = {
|
|
91
|
+
packageName,
|
|
92
|
+
lastBuild,
|
|
93
|
+
buildHistory,
|
|
94
|
+
successRate,
|
|
95
|
+
averageDuration,
|
|
96
|
+
lastCommit: lastBuild.commit,
|
|
97
|
+
lastCommitDate: lastBuild.startTime,
|
|
98
|
+
branch: lastBuild.branch,
|
|
99
|
+
isHealthy,
|
|
100
|
+
issues,
|
|
101
|
+
};
|
|
102
|
+
this.setCache(cacheKey, status);
|
|
103
|
+
return status;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error(`Error fetching CI status for ${packageName}:`, error);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get overall monorepo CI status
|
|
112
|
+
*/
|
|
113
|
+
async getMonorepoStatus(packages) {
|
|
114
|
+
const cacheKey = 'monorepo-ci-status';
|
|
115
|
+
const cached = this.getFromCache(cacheKey);
|
|
116
|
+
if (cached) {
|
|
117
|
+
return cached;
|
|
118
|
+
}
|
|
119
|
+
const packageStatuses = [];
|
|
120
|
+
const allBuilds = [];
|
|
121
|
+
let totalTests = 0;
|
|
122
|
+
let passedTests = 0;
|
|
123
|
+
let failedTests = 0;
|
|
124
|
+
const packageCoverage = {};
|
|
125
|
+
// Get status for each package
|
|
126
|
+
for (const pkg of packages) {
|
|
127
|
+
const status = await this.getPackageStatus(pkg.name);
|
|
128
|
+
if (status) {
|
|
129
|
+
packageStatuses.push(status);
|
|
130
|
+
allBuilds.push(...status.buildHistory);
|
|
131
|
+
// Aggregate test results
|
|
132
|
+
if (status.lastBuild?.tests) {
|
|
133
|
+
totalTests += status.lastBuild.tests.total;
|
|
134
|
+
passedTests += status.lastBuild.tests.passed;
|
|
135
|
+
failedTests += status.lastBuild.tests.failed;
|
|
136
|
+
}
|
|
137
|
+
// Aggregate coverage
|
|
138
|
+
if (status.lastBuild?.coverage) {
|
|
139
|
+
packageCoverage[pkg.name] = status.lastBuild.coverage.percentage;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Calculate overall metrics
|
|
144
|
+
const totalPackages = packages.length;
|
|
145
|
+
const healthyPackages = packageStatuses.filter(s => s.isHealthy).length;
|
|
146
|
+
const warningPackages = packageStatuses.filter(s => !s.isHealthy && s.issues.length < 3).length;
|
|
147
|
+
const errorPackages = packageStatuses.filter(s => !s.isHealthy && s.issues.length >= 3).length;
|
|
148
|
+
const overallHealth = (healthyPackages / totalPackages) * 100;
|
|
149
|
+
// Sort builds by time
|
|
150
|
+
allBuilds.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());
|
|
151
|
+
const recentBuilds = allBuilds.slice(0, 20);
|
|
152
|
+
const failedBuilds = allBuilds.filter(b => b.status === 'failed');
|
|
153
|
+
// Calculate overall coverage
|
|
154
|
+
const coverageValues = Object.values(packageCoverage);
|
|
155
|
+
const overallCoverage = coverageValues.length > 0
|
|
156
|
+
? coverageValues.reduce((sum, val) => sum + val, 0) /
|
|
157
|
+
coverageValues.length
|
|
158
|
+
: 0;
|
|
159
|
+
const status = {
|
|
160
|
+
totalPackages,
|
|
161
|
+
healthyPackages,
|
|
162
|
+
warningPackages,
|
|
163
|
+
errorPackages,
|
|
164
|
+
overallHealth,
|
|
165
|
+
packages: packageStatuses,
|
|
166
|
+
recentBuilds,
|
|
167
|
+
failedBuilds,
|
|
168
|
+
coverage: {
|
|
169
|
+
overall: overallCoverage,
|
|
170
|
+
packages: packageCoverage,
|
|
171
|
+
},
|
|
172
|
+
tests: {
|
|
173
|
+
total: totalTests,
|
|
174
|
+
passed: passedTests,
|
|
175
|
+
failed: failedTests,
|
|
176
|
+
successRate: totalTests > 0 ? (passedTests / totalTests) * 100 : 0,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
this.setCache(cacheKey, status);
|
|
180
|
+
return status;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Fetch builds from a specific CI provider
|
|
184
|
+
*/
|
|
185
|
+
async fetchBuildsFromProvider(_provider, packageName) {
|
|
186
|
+
// This is a mock implementation
|
|
187
|
+
// In a real implementation, you would make API calls to the CI provider
|
|
188
|
+
const mockBuilds = [
|
|
189
|
+
{
|
|
190
|
+
id: `build-${Date.now()}-1`,
|
|
191
|
+
status: 'success',
|
|
192
|
+
branch: 'main',
|
|
193
|
+
commit: 'abc1234',
|
|
194
|
+
commitMessage: `feat: update ${packageName}`,
|
|
195
|
+
author: 'developer@example.com',
|
|
196
|
+
startTime: new Date(Date.now() - 1000 * 60 * 30), // 30 minutes ago
|
|
197
|
+
endTime: new Date(Date.now() - 1000 * 60 * 25), // 25 minutes ago
|
|
198
|
+
duration: 5 * 60 * 1000, // 5 minutes
|
|
199
|
+
url: `https://ci.example.com/builds/build-${Date.now()}-1`,
|
|
200
|
+
packageName,
|
|
201
|
+
workflowName: 'Build and Test',
|
|
202
|
+
jobName: 'test',
|
|
203
|
+
steps: [
|
|
204
|
+
{
|
|
205
|
+
name: 'Install dependencies',
|
|
206
|
+
status: 'success',
|
|
207
|
+
duration: 30000,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'Run tests',
|
|
211
|
+
status: 'success',
|
|
212
|
+
duration: 120000,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'Build package',
|
|
216
|
+
status: 'success',
|
|
217
|
+
duration: 60000,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
coverage: {
|
|
221
|
+
percentage: 85,
|
|
222
|
+
lines: 1000,
|
|
223
|
+
functions: 50,
|
|
224
|
+
branches: 200,
|
|
225
|
+
statements: 1200,
|
|
226
|
+
},
|
|
227
|
+
tests: {
|
|
228
|
+
total: 150,
|
|
229
|
+
passed: 145,
|
|
230
|
+
failed: 0,
|
|
231
|
+
skipped: 5,
|
|
232
|
+
duration: 120000,
|
|
233
|
+
suites: [
|
|
234
|
+
{
|
|
235
|
+
name: 'Unit Tests',
|
|
236
|
+
status: 'pass',
|
|
237
|
+
tests: [],
|
|
238
|
+
duration: 80000,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'Integration Tests',
|
|
242
|
+
status: 'pass',
|
|
243
|
+
tests: [],
|
|
244
|
+
duration: 40000,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: `build-${Date.now()}-2`,
|
|
251
|
+
status: 'failed',
|
|
252
|
+
branch: 'feature/new-feature',
|
|
253
|
+
commit: 'def5678',
|
|
254
|
+
commitMessage: `fix: resolve issue in ${packageName}`,
|
|
255
|
+
author: 'developer@example.com',
|
|
256
|
+
startTime: new Date(Date.now() - 1000 * 60 * 60 * 2), // 2 hours ago
|
|
257
|
+
endTime: new Date(Date.now() - 1000 * 60 * 60 * 1.5), // 1.5 hours ago
|
|
258
|
+
duration: 30 * 60 * 1000, // 30 minutes
|
|
259
|
+
url: `https://ci.example.com/builds/build-${Date.now()}-2`,
|
|
260
|
+
packageName,
|
|
261
|
+
workflowName: 'Build and Test',
|
|
262
|
+
jobName: 'test',
|
|
263
|
+
steps: [
|
|
264
|
+
{
|
|
265
|
+
name: 'Install dependencies',
|
|
266
|
+
status: 'success',
|
|
267
|
+
duration: 30000,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'Run tests',
|
|
271
|
+
status: 'failed',
|
|
272
|
+
duration: 120000,
|
|
273
|
+
error: 'Test suite failed with 3 failing tests',
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
tests: {
|
|
277
|
+
total: 150,
|
|
278
|
+
passed: 147,
|
|
279
|
+
failed: 3,
|
|
280
|
+
skipped: 0,
|
|
281
|
+
duration: 120000,
|
|
282
|
+
suites: [
|
|
283
|
+
{
|
|
284
|
+
name: 'Unit Tests',
|
|
285
|
+
status: 'pass',
|
|
286
|
+
tests: [],
|
|
287
|
+
duration: 80000,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'Integration Tests',
|
|
291
|
+
status: 'fail',
|
|
292
|
+
tests: [],
|
|
293
|
+
duration: 40000,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
];
|
|
299
|
+
return mockBuilds;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Calculate success rate from builds
|
|
303
|
+
*/
|
|
304
|
+
calculateSuccessRate(builds) {
|
|
305
|
+
if (builds.length === 0)
|
|
306
|
+
return 0;
|
|
307
|
+
const successfulBuilds = builds.filter(b => b.status === 'success').length;
|
|
308
|
+
return (successfulBuilds / builds.length) * 100;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Calculate average build duration
|
|
312
|
+
*/
|
|
313
|
+
calculateAverageDuration(builds) {
|
|
314
|
+
if (builds.length === 0)
|
|
315
|
+
return 0;
|
|
316
|
+
const completedBuilds = builds.filter(b => b.duration !== undefined);
|
|
317
|
+
if (completedBuilds.length === 0)
|
|
318
|
+
return 0;
|
|
319
|
+
const totalDuration = completedBuilds.reduce((sum, b) => sum + (b.duration || 0), 0);
|
|
320
|
+
return totalDuration / completedBuilds.length;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Determine if a package is healthy based on CI results
|
|
324
|
+
*/
|
|
325
|
+
determinePackageHealth(builds) {
|
|
326
|
+
if (builds.length === 0)
|
|
327
|
+
return true;
|
|
328
|
+
const recentBuilds = builds.slice(0, 5); // Last 5 builds
|
|
329
|
+
const successRate = this.calculateSuccessRate(recentBuilds);
|
|
330
|
+
return successRate >= 80; // 80% success rate threshold
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Identify issues from build results
|
|
334
|
+
*/
|
|
335
|
+
identifyIssues(builds) {
|
|
336
|
+
const issues = [];
|
|
337
|
+
if (builds.length === 0)
|
|
338
|
+
return issues;
|
|
339
|
+
const recentBuilds = builds.slice(0, 3); // Last 3 builds
|
|
340
|
+
const successRate = this.calculateSuccessRate(recentBuilds);
|
|
341
|
+
if (successRate < 50) {
|
|
342
|
+
issues.push('High failure rate in recent builds');
|
|
343
|
+
}
|
|
344
|
+
const failedBuilds = recentBuilds.filter(b => b.status === 'failed');
|
|
345
|
+
for (const build of failedBuilds) {
|
|
346
|
+
const failedSteps = build.steps.filter(s => s.status === 'failed');
|
|
347
|
+
for (const step of failedSteps) {
|
|
348
|
+
if (step.error) {
|
|
349
|
+
issues.push(`Build step '${step.name}' failed: ${step.error}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// Check for long build times
|
|
354
|
+
const avgDuration = this.calculateAverageDuration(recentBuilds);
|
|
355
|
+
if (avgDuration > 10 * 60 * 1000) {
|
|
356
|
+
// 10 minutes
|
|
357
|
+
issues.push('Builds are taking longer than expected');
|
|
358
|
+
}
|
|
359
|
+
return issues;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get cache value if not expired
|
|
363
|
+
*/
|
|
364
|
+
getFromCache(key) {
|
|
365
|
+
const cached = this.cache.get(key);
|
|
366
|
+
if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
|
|
367
|
+
return cached.data;
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Set cache value with timestamp
|
|
373
|
+
*/
|
|
374
|
+
setCache(key, data) {
|
|
375
|
+
this.cache.set(key, {
|
|
376
|
+
data,
|
|
377
|
+
timestamp: Date.now(),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Clear the cache
|
|
382
|
+
*/
|
|
383
|
+
clearCache() {
|
|
384
|
+
this.cache.clear();
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get build logs for a specific build
|
|
388
|
+
*/
|
|
389
|
+
async getBuildLogs(buildId, providerName) {
|
|
390
|
+
// Mock implementation
|
|
391
|
+
return `Build logs for ${buildId} from ${providerName}\n\nStep 1: Install dependencies\n✓ Dependencies installed successfully\n\nStep 2: Run tests\n✓ All tests passed\n\nStep 3: Build package\n✓ Package built successfully`;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Trigger a new build for a package
|
|
395
|
+
*/
|
|
396
|
+
async triggerBuild(packageName, providerName, branch = 'main') {
|
|
397
|
+
try {
|
|
398
|
+
// Mock implementation
|
|
399
|
+
const buildId = `build-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
400
|
+
console.log(`Triggering build for ${packageName} on ${branch} via ${providerName}`);
|
|
401
|
+
return {
|
|
402
|
+
success: true,
|
|
403
|
+
buildId,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get build artifacts
|
|
415
|
+
*/
|
|
416
|
+
async getBuildArtifacts(buildId, _providerName) {
|
|
417
|
+
// Mock implementation
|
|
418
|
+
return [
|
|
419
|
+
{
|
|
420
|
+
name: 'coverage-report.html',
|
|
421
|
+
type: 'coverage',
|
|
422
|
+
size: 1024 * 50, // 50KB
|
|
423
|
+
url: `https://ci.example.com/artifacts/${buildId}/coverage-report.html`,
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: 'test-results.xml',
|
|
427
|
+
type: 'test',
|
|
428
|
+
size: 1024 * 10, // 10KB
|
|
429
|
+
url: `https://ci.example.com/artifacts/${buildId}/test-results.xml`,
|
|
430
|
+
},
|
|
431
|
+
];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
exports.CIStatusManager = CIStatusManager;
|
|
435
|
+
// Export default instance
|
|
436
|
+
exports.ciStatusManager = new CIStatusManager();
|
|
437
|
+
// Export convenience functions
|
|
438
|
+
async function getPackageCIStatus(packageName) {
|
|
439
|
+
return exports.ciStatusManager.getPackageStatus(packageName);
|
|
440
|
+
}
|
|
441
|
+
async function getMonorepoCIStatus(packages) {
|
|
442
|
+
return exports.ciStatusManager.getMonorepoStatus(packages);
|
|
443
|
+
}
|
|
444
|
+
async function triggerPackageBuild(packageName, providerName, branch) {
|
|
445
|
+
return exports.ciStatusManager.triggerBuild(packageName, providerName, branch);
|
|
446
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getCommits = getCommits;
|
|
37
|
+
exports.storePackage = storePackage;
|
|
38
|
+
exports.storeCommits = storeCommits;
|
|
39
|
+
exports.getPackageDependenciesInfo = getPackageDependenciesInfo;
|
|
40
|
+
exports.storeDependencies = storeDependencies;
|
|
41
|
+
// dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
|
42
|
+
// Fallback import to handle environments where PrismaClient isn't a named export
|
|
43
|
+
const PrismaPkg = __importStar(require("@prisma/client"));
|
|
44
|
+
const PrismaClient = PrismaPkg.PrismaClient || PrismaPkg.default || PrismaPkg;
|
|
45
|
+
// Provide a fallback reference to the Prisma namespace so errors like
|
|
46
|
+
// Prisma.PrismaClientKnownRequestError can be referenced safely.
|
|
47
|
+
const Prisma = PrismaPkg.Prisma || PrismaPkg.PrismaClient?.Prisma || PrismaPkg.default?.Prisma || PrismaPkg;
|
|
48
|
+
const config_loader_1 = require("../config-loader");
|
|
49
|
+
// Default settings
|
|
50
|
+
const DEFAULT_PORT = 4000;
|
|
51
|
+
const port = config_loader_1.appConfig.server.port ?? DEFAULT_PORT; //Default port
|
|
52
|
+
const host = config_loader_1.appConfig.server.host ?? 'localhost'; //Default host
|
|
53
|
+
const prisma = new PrismaClient();
|
|
54
|
+
const API_BASE = `http://${host}:${port}/api`;
|
|
55
|
+
async function getCommits(path) {
|
|
56
|
+
const res = await fetch(API_BASE + `/commits/` + encodeURIComponent(path));
|
|
57
|
+
if (!res.ok)
|
|
58
|
+
throw new Error('Failed to fetch commits');
|
|
59
|
+
return (await res.json());
|
|
60
|
+
}
|
|
61
|
+
async function storeCommits(packageName, commits) {
|
|
62
|
+
console.log('💾 Storing commits for:' + packageName);
|
|
63
|
+
// Create or update dependencies
|
|
64
|
+
for (const commit of commits) {
|
|
65
|
+
try {
|
|
66
|
+
await prisma.commit.upsert({
|
|
67
|
+
where: {
|
|
68
|
+
hash: commit.hash,
|
|
69
|
+
},
|
|
70
|
+
update: {
|
|
71
|
+
message: commit.message,
|
|
72
|
+
author: commit.author,
|
|
73
|
+
date: commit.date,
|
|
74
|
+
type: commit.type,
|
|
75
|
+
},
|
|
76
|
+
create: {
|
|
77
|
+
hash: commit.hash,
|
|
78
|
+
message: commit.message,
|
|
79
|
+
author: commit.author,
|
|
80
|
+
date: commit.date,
|
|
81
|
+
type: commit.type,
|
|
82
|
+
packageName: packageName,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
const err = e;
|
|
88
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError &&
|
|
89
|
+
err.code === 'P2002') {
|
|
90
|
+
// Handle unique constraint violation (e.g., commit already exists)
|
|
91
|
+
console.warn(`Skipping commit: ${commit.hash} (Depenndency already exists)`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Handle any other unexpected errors
|
|
95
|
+
console.error(`Failed to store commit: ${commit.hash}`, err);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Store packages in database
|
|
102
|
+
*/
|
|
103
|
+
async function storePackage(pkg) {
|
|
104
|
+
try {
|
|
105
|
+
// Create or update package
|
|
106
|
+
await prisma.package.upsert({
|
|
107
|
+
where: { name: pkg.name },
|
|
108
|
+
update: {
|
|
109
|
+
version: pkg.version,
|
|
110
|
+
type: pkg.type,
|
|
111
|
+
path: pkg.path,
|
|
112
|
+
description: pkg.description,
|
|
113
|
+
license: pkg.license,
|
|
114
|
+
repository: JSON.stringify(pkg.repository),
|
|
115
|
+
scripts: JSON.stringify(pkg.scripts),
|
|
116
|
+
lastUpdated: new Date(),
|
|
117
|
+
},
|
|
118
|
+
create: {
|
|
119
|
+
// Timestamps
|
|
120
|
+
createdAt: new Date(),
|
|
121
|
+
lastUpdated: new Date(),
|
|
122
|
+
// Key Metrics and Relationships
|
|
123
|
+
dependencies: JSON.stringify(pkg.dependencies), // The total number of direct dependencies (12 in your example)
|
|
124
|
+
// Manual Serialization Required: Stores a JSON array string of maintainers, e.g., '["team-frontend"]'
|
|
125
|
+
maintainers: pkg.maintainers.join(','),
|
|
126
|
+
// Manual Serialization Required: Stores a JSON array string of tags, e.g., '["core", "ui"]'
|
|
127
|
+
// Manual Serialization Required: Stores the scripts object as a JSON string
|
|
128
|
+
// Example: '{"dev": "vite", "build": "tsc && vite build"}'
|
|
129
|
+
scripts: JSON.stringify(pkg.scripts),
|
|
130
|
+
// Dependency Lists (Manual Serialization Required)
|
|
131
|
+
// Stores a JSON array string of dependencies.
|
|
132
|
+
// dependenciesList: JSON.stringify(pkg.dependenciesList),
|
|
133
|
+
devDependencies: JSON.stringify(pkg.devDependencies),
|
|
134
|
+
peerDependencies: JSON.stringify(pkg.peerDependencies),
|
|
135
|
+
name: pkg.name,
|
|
136
|
+
version: pkg.version,
|
|
137
|
+
type: pkg.type,
|
|
138
|
+
path: pkg.path,
|
|
139
|
+
description: pkg.description ?? '',
|
|
140
|
+
license: pkg.license ?? '',
|
|
141
|
+
repository: JSON.stringify(pkg.repository),
|
|
142
|
+
status: '',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
const commits = await getCommits(pkg.path ?? '');
|
|
146
|
+
if (commits.length) {
|
|
147
|
+
await storeCommits(pkg.name, commits);
|
|
148
|
+
}
|
|
149
|
+
const dependenciesInfo = getPackageDependenciesInfo(pkg);
|
|
150
|
+
if (dependenciesInfo.length) {
|
|
151
|
+
await storeDependencies(pkg.name, dependenciesInfo);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.warn(`⚠️ Failed to store report for ${pkg.name}:`, error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get package dependencies
|
|
160
|
+
*/
|
|
161
|
+
function getPackageDependenciesInfo(pkg) {
|
|
162
|
+
const allDeps = [];
|
|
163
|
+
Object.keys(pkg.dependencies || {}).forEach(depName => {
|
|
164
|
+
allDeps.push({
|
|
165
|
+
name: depName,
|
|
166
|
+
version: pkg.dependencies[depName],
|
|
167
|
+
type: 'dependency',
|
|
168
|
+
latest: '',
|
|
169
|
+
outdated: false,
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
Object.keys(pkg.devDependencies || {}).forEach(depName => {
|
|
173
|
+
allDeps.push({
|
|
174
|
+
name: depName,
|
|
175
|
+
version: pkg.devDependencies[depName],
|
|
176
|
+
type: 'devDependency',
|
|
177
|
+
latest: '',
|
|
178
|
+
outdated: false,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
Object.keys(pkg.peerDependencies || {}).forEach(depName => {
|
|
182
|
+
allDeps.push({
|
|
183
|
+
name: depName,
|
|
184
|
+
version: pkg.peerDependencies[depName],
|
|
185
|
+
type: 'peerDependency',
|
|
186
|
+
latest: '',
|
|
187
|
+
outdated: false,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
return allDeps;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Store dependencies in database
|
|
194
|
+
*/
|
|
195
|
+
async function storeDependencies(packageName, dependencies) {
|
|
196
|
+
console.log('💾 Storing Dependencies for:' + packageName);
|
|
197
|
+
// Create or update dependencies
|
|
198
|
+
for (const dep of dependencies) {
|
|
199
|
+
try {
|
|
200
|
+
await prisma.dependencyInfo.upsert({
|
|
201
|
+
where: {
|
|
202
|
+
name_packageName: {
|
|
203
|
+
// This refers to the composite unique constraint
|
|
204
|
+
name: dep.name,
|
|
205
|
+
packageName,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
update: {
|
|
209
|
+
version: dep.version,
|
|
210
|
+
type: dep.type,
|
|
211
|
+
latest: dep.latest,
|
|
212
|
+
outdated: dep.outdated,
|
|
213
|
+
},
|
|
214
|
+
create: {
|
|
215
|
+
name: dep.name,
|
|
216
|
+
version: dep.version,
|
|
217
|
+
type: dep.type,
|
|
218
|
+
latest: dep.latest,
|
|
219
|
+
outdated: dep.outdated,
|
|
220
|
+
packageName: packageName,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
const err = e;
|
|
226
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError &&
|
|
227
|
+
err.code === 'P2002') {
|
|
228
|
+
// Handle unique constraint violation (e.g., depedency already exists)
|
|
229
|
+
console.warn(`Skipping dependency: ${dep.name} (Depenndency already exists)`);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Handle any other unexpected errors
|
|
233
|
+
console.error(`Failed to store dependency: ${dep.name}`, err);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|