gitlab-auto-reviewers 2.0.1 → 2.1.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/LICENSE +1 -1
- package/dist/bin/cli.js +17 -29
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/index.d.ts +1 -8
- package/dist/bin/index.d.ts.map +1 -1
- package/dist/bin/index.js +26 -9
- package/dist/bin/index.js.map +1 -1
- package/dist/bin/mcp.js +0 -0
- package/dist/cli/cicd-integration.d.ts.map +1 -1
- package/dist/cli/cicd-integration.js +12 -8
- package/dist/cli/cicd-integration.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +113 -27
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/config.d.ts +7 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +23 -12
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/data-source-selector.d.ts +6 -0
- package/dist/cli/data-source-selector.d.ts.map +1 -1
- package/dist/cli/data-source-selector.js +58 -19
- package/dist/cli/data-source-selector.js.map +1 -1
- package/dist/cli/enhanced-cli.d.ts +104 -0
- package/dist/cli/enhanced-cli.d.ts.map +1 -0
- package/dist/cli/enhanced-cli.js +321 -0
- package/dist/cli/enhanced-cli.js.map +1 -0
- package/dist/cli/enhanced-data-source-resolver.d.ts +67 -0
- package/dist/cli/enhanced-data-source-resolver.d.ts.map +1 -0
- package/dist/cli/enhanced-data-source-resolver.js +249 -0
- package/dist/cli/enhanced-data-source-resolver.js.map +1 -0
- package/dist/cli/enhanced-parameter-resolver.d.ts +111 -0
- package/dist/cli/enhanced-parameter-resolver.d.ts.map +1 -0
- package/dist/cli/enhanced-parameter-resolver.js +233 -0
- package/dist/cli/enhanced-parameter-resolver.js.map +1 -0
- package/dist/cli/errors.d.ts.map +1 -1
- package/dist/cli/errors.js +4 -0
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/parameter-source-tracker.d.ts +169 -0
- package/dist/cli/parameter-source-tracker.d.ts.map +1 -0
- package/dist/cli/parameter-source-tracker.js +252 -0
- package/dist/cli/parameter-source-tracker.js.map +1 -0
- package/dist/config/config.service.d.ts +24 -2
- package/dist/config/config.service.d.ts.map +1 -1
- package/dist/config/config.service.js +39 -6
- package/dist/config/config.service.js.map +1 -1
- package/dist/datasources/local-git-data-source.d.ts +26 -0
- package/dist/datasources/local-git-data-source.d.ts.map +1 -1
- package/dist/datasources/local-git-data-source.js +97 -1
- package/dist/datasources/local-git-data-source.js.map +1 -1
- package/dist/enhanced-config.js +26 -0
- package/dist/index.js +0 -0
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/services/blacklist.service.d.ts +11 -5
- package/dist/services/blacklist.service.d.ts.map +1 -1
- package/dist/services/blacklist.service.js +28 -7
- package/dist/services/blacklist.service.js.map +1 -1
- package/dist/services/reviewer-service.d.ts +2 -0
- package/dist/services/reviewer-service.d.ts.map +1 -1
- package/dist/services/reviewer-service.js +99 -19
- package/dist/services/reviewer-service.js.map +1 -1
- package/dist/services/whitelist.service.js +1 -1
- package/dist/services/whitelist.service.js.map +1 -1
- package/dist/types/enhanced-config.d.ts +121 -0
- package/dist/types/enhanced-config.d.ts.map +1 -0
- package/dist/types/enhanced-config.js +27 -0
- package/dist/types/enhanced-config.js.map +1 -0
- package/package.json +9 -8
- package/dist/bin/deprecated-mcp.d.ts +0 -11
- package/dist/bin/deprecated-mcp.d.ts.map +0 -1
- package/dist/bin/deprecated-mcp.js +0 -44
- package/dist/bin/deprecated-mcp.js.map +0 -1
- package/dist/tools.d.ts +0 -22
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -176
- package/dist/tools.js.map +0 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter Source Tracker
|
|
3
|
+
*
|
|
4
|
+
* Tracks and logs the source of each configuration parameter for debugging.
|
|
5
|
+
* Implements Requirement 3.5: THE System SHALL log the source of each configuration value for debugging
|
|
6
|
+
*
|
|
7
|
+
* @module cli/parameter-source-tracker
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Parameter Source Tracker
|
|
11
|
+
*
|
|
12
|
+
* Tracks the source of each configuration parameter and provides
|
|
13
|
+
* comprehensive logging for debugging configuration issues.
|
|
14
|
+
*/
|
|
15
|
+
export class ParameterSourceTracker {
|
|
16
|
+
parameters = new Map();
|
|
17
|
+
logger;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new ParameterSourceTracker instance
|
|
20
|
+
*
|
|
21
|
+
* @param logger - Logger instance for outputting parameter source information
|
|
22
|
+
*/
|
|
23
|
+
constructor(logger) {
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Track a parameter with its value and source
|
|
28
|
+
*/
|
|
29
|
+
track(name, value, source, options) {
|
|
30
|
+
this.parameters.set(name, {
|
|
31
|
+
name,
|
|
32
|
+
value,
|
|
33
|
+
source,
|
|
34
|
+
sourceDetail: options?.sourceDetail,
|
|
35
|
+
masked: options?.masked,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a parameter value with source tracking
|
|
40
|
+
* Implements the precedence: CLI argument > environment variable > auto-detected > config file > default
|
|
41
|
+
*/
|
|
42
|
+
resolve(name, cliValue, envVarName, autoDetectFn, configValue, defaultValue, options) {
|
|
43
|
+
// Priority 1: CLI argument
|
|
44
|
+
if (cliValue !== undefined) {
|
|
45
|
+
this.track(name, cliValue, 'cli-argument', {
|
|
46
|
+
sourceDetail: `--${this.toKebabCase(name)}`,
|
|
47
|
+
masked: options?.masked,
|
|
48
|
+
});
|
|
49
|
+
return cliValue;
|
|
50
|
+
}
|
|
51
|
+
// Priority 2: Environment variable
|
|
52
|
+
if (envVarName) {
|
|
53
|
+
const envValue = process.env[envVarName];
|
|
54
|
+
if (envValue !== undefined && envValue !== '') {
|
|
55
|
+
const parsedValue = this.parseEnvValue(envValue);
|
|
56
|
+
this.track(name, parsedValue, 'environment', {
|
|
57
|
+
sourceDetail: envVarName,
|
|
58
|
+
masked: options?.masked,
|
|
59
|
+
});
|
|
60
|
+
return parsedValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Priority 3: Auto-detection
|
|
64
|
+
if (autoDetectFn) {
|
|
65
|
+
const autoValue = autoDetectFn();
|
|
66
|
+
if (autoValue !== undefined) {
|
|
67
|
+
this.track(name, autoValue, 'auto-detected', {
|
|
68
|
+
masked: options?.masked,
|
|
69
|
+
});
|
|
70
|
+
return autoValue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Priority 4: Config file
|
|
74
|
+
if (configValue !== undefined) {
|
|
75
|
+
this.track(name, configValue, 'config-file', {
|
|
76
|
+
masked: options?.masked,
|
|
77
|
+
});
|
|
78
|
+
return configValue;
|
|
79
|
+
}
|
|
80
|
+
// Priority 5: Default value
|
|
81
|
+
if (defaultValue !== undefined) {
|
|
82
|
+
this.track(name, defaultValue, 'default', {
|
|
83
|
+
masked: options?.masked,
|
|
84
|
+
});
|
|
85
|
+
return defaultValue;
|
|
86
|
+
}
|
|
87
|
+
// Not set
|
|
88
|
+
this.track(name, undefined, 'not-set', {
|
|
89
|
+
masked: options?.masked,
|
|
90
|
+
});
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get all tracked parameters
|
|
95
|
+
*/
|
|
96
|
+
getAll() {
|
|
97
|
+
return new Map(this.parameters);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get a specific tracked parameter
|
|
101
|
+
*
|
|
102
|
+
* @param name - The name of the parameter to retrieve
|
|
103
|
+
* @returns The tracked parameter or undefined if not found
|
|
104
|
+
*/
|
|
105
|
+
get(name) {
|
|
106
|
+
return this.parameters.get(name);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Log all parameter sources
|
|
110
|
+
* This implements Requirement 3.5
|
|
111
|
+
*/
|
|
112
|
+
logParameterSources() {
|
|
113
|
+
this.logger.info('Configuration resolved - parameter sources:');
|
|
114
|
+
const sortedParams = Array.from(this.parameters.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
115
|
+
for (const [name, param] of sortedParams) {
|
|
116
|
+
const displayValue = this.formatValueForLog(param);
|
|
117
|
+
const sourceInfo = this.formatSourceForLog(param);
|
|
118
|
+
this.logger.info(` ${name}: ${displayValue} (${sourceInfo})`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Log parameter sources in a structured format (for JSON output)
|
|
123
|
+
*/
|
|
124
|
+
toStructuredLog() {
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const [name, param] of this.parameters) {
|
|
127
|
+
result[name] = {
|
|
128
|
+
value: param.masked ? '***' : param.value,
|
|
129
|
+
source: param.source,
|
|
130
|
+
sourceDetail: param.sourceDetail,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Format value for logging (handles masking and undefined)
|
|
137
|
+
*/
|
|
138
|
+
formatValueForLog(param) {
|
|
139
|
+
if (param.value === undefined) {
|
|
140
|
+
return '<not set>';
|
|
141
|
+
}
|
|
142
|
+
if (param.masked) {
|
|
143
|
+
return '***';
|
|
144
|
+
}
|
|
145
|
+
if (typeof param.value === 'string') {
|
|
146
|
+
// Truncate long strings
|
|
147
|
+
if (param.value.length > 50) {
|
|
148
|
+
return `"${param.value.substring(0, 47)}..."`;
|
|
149
|
+
}
|
|
150
|
+
return `"${param.value}"`;
|
|
151
|
+
}
|
|
152
|
+
return String(param.value);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Format source information for logging
|
|
156
|
+
*/
|
|
157
|
+
formatSourceForLog(param) {
|
|
158
|
+
switch (param.source) {
|
|
159
|
+
case 'cli-argument':
|
|
160
|
+
return `CLI argument ${param.sourceDetail || ''}`.trim();
|
|
161
|
+
case 'environment':
|
|
162
|
+
return `environment variable ${param.sourceDetail || ''}`.trim();
|
|
163
|
+
case 'auto-detected':
|
|
164
|
+
return param.sourceDetail ? `auto-detected from ${param.sourceDetail}` : 'auto-detected';
|
|
165
|
+
case 'config-file':
|
|
166
|
+
return param.sourceDetail ? `config file ${param.sourceDetail}` : 'config file';
|
|
167
|
+
case 'default':
|
|
168
|
+
return 'default';
|
|
169
|
+
case 'not-set':
|
|
170
|
+
return 'not set';
|
|
171
|
+
default:
|
|
172
|
+
return param.source;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Convert camelCase to kebab-case
|
|
177
|
+
*/
|
|
178
|
+
toKebabCase(str) {
|
|
179
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Parse environment variable value to appropriate type
|
|
183
|
+
*/
|
|
184
|
+
parseEnvValue(value) {
|
|
185
|
+
// Try to parse as number
|
|
186
|
+
const num = Number(value);
|
|
187
|
+
if (!isNaN(num) && value.trim() !== '') {
|
|
188
|
+
return num;
|
|
189
|
+
}
|
|
190
|
+
// Try to parse as boolean
|
|
191
|
+
if (value.toLowerCase() === 'true') {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (value.toLowerCase() === 'false') {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// Return as string
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Create a parameter source tracker and resolve all CLI parameters
|
|
203
|
+
*
|
|
204
|
+
* @param options - CLI options from argument parsing
|
|
205
|
+
* @param cicdEnv - CI/CD environment detection result
|
|
206
|
+
* @param logger - Logger instance
|
|
207
|
+
* @returns Resolved parameters with source tracking
|
|
208
|
+
*/
|
|
209
|
+
export function resolveParametersWithTracking(options, cicdEnv, logger) {
|
|
210
|
+
const tracker = new ParameterSourceTracker(logger);
|
|
211
|
+
// Resolve each parameter with source tracking
|
|
212
|
+
// NOTE: We need to resolve project FIRST to determine if we should auto-detect repoPath
|
|
213
|
+
// When both project ID and project directory are available in CI, prefer API mode (project ID)
|
|
214
|
+
// because it provides more reliable data access in CI environments
|
|
215
|
+
const project = tracker.resolve('project', options.project, 'CI_PROJECT_ID', // Also check CI_PROJECT_ID environment variable
|
|
216
|
+
() => cicdEnv.isCI ? cicdEnv.projectId : undefined, undefined, undefined);
|
|
217
|
+
// Only auto-detect repoPath from CI_PROJECT_DIR if project is NOT already set
|
|
218
|
+
// This prevents the "Cannot specify both --repo-path and --project" conflict
|
|
219
|
+
const repoPath = tracker.resolve('repoPath', options.repoPath, project ? undefined : 'CI_PROJECT_DIR', // Only use CI_PROJECT_DIR if project is not set
|
|
220
|
+
undefined, undefined, undefined);
|
|
221
|
+
// Resolve mergeRequestIid FIRST to determine if we should auto-detect branch
|
|
222
|
+
// When both MR IID and branch are available in CI, prefer MR IID because
|
|
223
|
+
// it provides more specific context for reviewer suggestions
|
|
224
|
+
const mergeRequestIid = tracker.resolve('mergeRequestIid', options.mergeRequestIid, 'CI_MERGE_REQUEST_IID', () => cicdEnv.isCI ? cicdEnv.mergeRequestIid : undefined, undefined, undefined);
|
|
225
|
+
// Only auto-detect branch from CI_COMMIT_REF_NAME if mergeRequestIid is NOT already set
|
|
226
|
+
// This prevents the "Cannot specify both merge request IID and branch name" conflict
|
|
227
|
+
const branch = tracker.resolve('branch', options.branch, mergeRequestIid ? undefined : 'CI_COMMIT_REF_NAME', // Only use CI_COMMIT_REF_NAME if MR IID is not set
|
|
228
|
+
() => (cicdEnv.isCI && !mergeRequestIid) ? cicdEnv.branch : undefined, undefined, undefined);
|
|
229
|
+
const gitlabUrl = tracker.resolve('gitlabUrl', options.gitlabUrl, 'GITLAB_URL', () => process.env.CI_SERVER_URL, // Auto-detect from GitLab CI environment
|
|
230
|
+
undefined, 'https://gitlab.com') || 'https://gitlab.com';
|
|
231
|
+
const gitlabToken = tracker.resolve('gitlabToken', options.gitlabToken, 'GITLAB_TOKEN', () => process.env.CI_JOB_TOKEN, undefined, undefined, { masked: true });
|
|
232
|
+
const format = tracker.resolve('format', options.format, undefined, () => cicdEnv.isCI ? 'json' : undefined, undefined, 'text') || 'text';
|
|
233
|
+
const verbose = tracker.resolve('verbose', options.verbose, undefined, undefined, undefined, false) || false;
|
|
234
|
+
const dryRun = tracker.resolve('dryRun', options.dryRun, undefined, undefined, undefined, false) || false;
|
|
235
|
+
const quiet = tracker.resolve('quiet', options.quiet, undefined, () => cicdEnv.isCI ? true : undefined, undefined, false) || false;
|
|
236
|
+
return {
|
|
237
|
+
tracker,
|
|
238
|
+
resolved: {
|
|
239
|
+
repoPath,
|
|
240
|
+
project,
|
|
241
|
+
mergeRequestIid,
|
|
242
|
+
branch,
|
|
243
|
+
gitlabUrl,
|
|
244
|
+
gitlabToken,
|
|
245
|
+
format,
|
|
246
|
+
verbose,
|
|
247
|
+
dryRun,
|
|
248
|
+
quiet,
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=parameter-source-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parameter-source-tracker.js","sourceRoot":"","sources":["../../src/cli/parameter-source-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,17 +37,36 @@ export interface MCPServerConfig {
|
|
|
37
37
|
*
|
|
38
38
|
* Loads configuration from environment variables, applies defaults,
|
|
39
39
|
* validates settings, and provides type-safe access to configuration.
|
|
40
|
+
*
|
|
41
|
+
* Supports singleton pattern to avoid multiple instantiations and duplicate logging.
|
|
40
42
|
*/
|
|
41
43
|
export declare class ConfigService {
|
|
44
|
+
private static instance;
|
|
45
|
+
private static hasLoggedDefaults;
|
|
42
46
|
private config;
|
|
43
47
|
private appliedDefaults;
|
|
44
48
|
/**
|
|
45
49
|
* Create a new configuration service
|
|
46
50
|
*
|
|
47
51
|
* Automatically loads and validates configuration from environment variables.
|
|
48
|
-
* Logs any default values that were applied.
|
|
52
|
+
* Logs any default values that were applied (only once per process).
|
|
53
|
+
*
|
|
54
|
+
* @param skipLogging - If true, skip logging applied defaults (useful for testing)
|
|
49
55
|
*/
|
|
50
|
-
constructor();
|
|
56
|
+
constructor(skipLogging?: boolean);
|
|
57
|
+
/**
|
|
58
|
+
* Get the singleton instance of ConfigService
|
|
59
|
+
*
|
|
60
|
+
* This ensures configuration is only loaded and logged once per process,
|
|
61
|
+
* avoiding duplicate log messages when multiple components need config.
|
|
62
|
+
*
|
|
63
|
+
* @returns The shared ConfigService instance
|
|
64
|
+
*/
|
|
65
|
+
static getInstance(): ConfigService;
|
|
66
|
+
/**
|
|
67
|
+
* Reset the singleton instance (primarily for testing)
|
|
68
|
+
*/
|
|
69
|
+
static resetInstance(): void;
|
|
51
70
|
/**
|
|
52
71
|
* Get the current configuration (read-only)
|
|
53
72
|
*/
|
|
@@ -58,6 +77,8 @@ export declare class ConfigService {
|
|
|
58
77
|
getAppliedDefaults(): ReadonlyMap<string, unknown>;
|
|
59
78
|
/**
|
|
60
79
|
* Load configuration from environment variables and validate
|
|
80
|
+
*
|
|
81
|
+
* @param skipLogging - If true, skip logging applied defaults
|
|
61
82
|
*/
|
|
62
83
|
private loadAndValidate;
|
|
63
84
|
/**
|
|
@@ -83,6 +104,7 @@ export declare class ConfigService {
|
|
|
83
104
|
/**
|
|
84
105
|
* Log which default values were applied
|
|
85
106
|
* Uses console.error since MCP servers log to stderr
|
|
107
|
+
* Only logs once per process to avoid duplicate messages
|
|
86
108
|
*/
|
|
87
109
|
private logAppliedDefaults;
|
|
88
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.service.d.ts","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAE9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAG5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IAGrB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED
|
|
1
|
+
{"version":3,"file":"config.service.d.ts","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAE9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAG5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IAGrB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IAEzC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,eAAe,CAAmC;IAE1D;;;;;;;OAOG;gBACS,WAAW,UAAQ;IAI/B;;;;;;;OAOG;IACH,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAK5B;;OAEG;IACH,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC;IAIhC;;OAEG;IACH,kBAAkB,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IAKlD;;;;OAIG;IACH,OAAO,CAAC,eAAe;IA+BvB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2CtB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAepB;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;CAa3B"}
|
|
@@ -9,18 +9,45 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Loads configuration from environment variables, applies defaults,
|
|
11
11
|
* validates settings, and provides type-safe access to configuration.
|
|
12
|
+
*
|
|
13
|
+
* Supports singleton pattern to avoid multiple instantiations and duplicate logging.
|
|
12
14
|
*/
|
|
13
15
|
export class ConfigService {
|
|
16
|
+
static instance = null;
|
|
17
|
+
static hasLoggedDefaults = false;
|
|
14
18
|
config;
|
|
15
19
|
appliedDefaults = new Map();
|
|
16
20
|
/**
|
|
17
21
|
* Create a new configuration service
|
|
18
22
|
*
|
|
19
23
|
* Automatically loads and validates configuration from environment variables.
|
|
20
|
-
* Logs any default values that were applied.
|
|
24
|
+
* Logs any default values that were applied (only once per process).
|
|
25
|
+
*
|
|
26
|
+
* @param skipLogging - If true, skip logging applied defaults (useful for testing)
|
|
21
27
|
*/
|
|
22
|
-
constructor() {
|
|
23
|
-
this.config = this.loadAndValidate();
|
|
28
|
+
constructor(skipLogging = false) {
|
|
29
|
+
this.config = this.loadAndValidate(skipLogging);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the singleton instance of ConfigService
|
|
33
|
+
*
|
|
34
|
+
* This ensures configuration is only loaded and logged once per process,
|
|
35
|
+
* avoiding duplicate log messages when multiple components need config.
|
|
36
|
+
*
|
|
37
|
+
* @returns The shared ConfigService instance
|
|
38
|
+
*/
|
|
39
|
+
static getInstance() {
|
|
40
|
+
if (!ConfigService.instance) {
|
|
41
|
+
ConfigService.instance = new ConfigService();
|
|
42
|
+
}
|
|
43
|
+
return ConfigService.instance;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reset the singleton instance (primarily for testing)
|
|
47
|
+
*/
|
|
48
|
+
static resetInstance() {
|
|
49
|
+
ConfigService.instance = null;
|
|
50
|
+
ConfigService.hasLoggedDefaults = false;
|
|
24
51
|
}
|
|
25
52
|
/**
|
|
26
53
|
* Get the current configuration (read-only)
|
|
@@ -37,8 +64,10 @@ export class ConfigService {
|
|
|
37
64
|
}
|
|
38
65
|
/**
|
|
39
66
|
* Load configuration from environment variables and validate
|
|
67
|
+
*
|
|
68
|
+
* @param skipLogging - If true, skip logging applied defaults
|
|
40
69
|
*/
|
|
41
|
-
loadAndValidate() {
|
|
70
|
+
loadAndValidate(skipLogging = false) {
|
|
42
71
|
const config = {
|
|
43
72
|
// GitLab Configuration
|
|
44
73
|
gitlabUrl: this.getEnvOrDefault('GITLAB_URL', 'https://gitlab.com'),
|
|
@@ -58,7 +87,9 @@ export class ConfigService {
|
|
|
58
87
|
debug: this.getBooleanEnv('DEBUG', false),
|
|
59
88
|
};
|
|
60
89
|
this.validateConfig(config);
|
|
61
|
-
|
|
90
|
+
if (!skipLogging) {
|
|
91
|
+
this.logAppliedDefaults();
|
|
92
|
+
}
|
|
62
93
|
return config;
|
|
63
94
|
}
|
|
64
95
|
/**
|
|
@@ -154,11 +185,13 @@ export class ConfigService {
|
|
|
154
185
|
/**
|
|
155
186
|
* Log which default values were applied
|
|
156
187
|
* Uses console.error since MCP servers log to stderr
|
|
188
|
+
* Only logs once per process to avoid duplicate messages
|
|
157
189
|
*/
|
|
158
190
|
logAppliedDefaults() {
|
|
159
|
-
if (this.appliedDefaults.size === 0) {
|
|
191
|
+
if (this.appliedDefaults.size === 0 || ConfigService.hasLoggedDefaults) {
|
|
160
192
|
return;
|
|
161
193
|
}
|
|
194
|
+
ConfigService.hasLoggedDefaults = true;
|
|
162
195
|
const timestamp = new Date().toISOString();
|
|
163
196
|
console.error(`[${timestamp}] [INFO] [ConfigService] Applied default configuration values:`);
|
|
164
197
|
for (const [key, value] of this.appliedDefaults.entries()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.service.js","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwCH
|
|
1
|
+
{"version":3,"file":"config.service.js","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,12 @@ export declare class LocalGitDataSource extends GitLabAPIDataSource implements G
|
|
|
23
23
|
* @param config - Optional configuration service for settings
|
|
24
24
|
*/
|
|
25
25
|
constructor(gitlabApi: GitLabAPI, repoPath: string, logger?: Logger, config?: ConfigService);
|
|
26
|
+
/**
|
|
27
|
+
* Get the repository path
|
|
28
|
+
*
|
|
29
|
+
* @returns The local repository path
|
|
30
|
+
*/
|
|
31
|
+
get repositoryPath(): string;
|
|
26
32
|
/**
|
|
27
33
|
* Get the project path from the git remote URL
|
|
28
34
|
*
|
|
@@ -62,10 +68,30 @@ export declare class LocalGitDataSource extends GitLabAPIDataSource implements G
|
|
|
62
68
|
* @returns Promise resolving to array of contributor email addresses (up to 100)
|
|
63
69
|
*/
|
|
64
70
|
getProjectContributors(project: string, commitSha: string): Promise<string[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Check if a path is a git submodule (gitlink with mode 160000)
|
|
73
|
+
*
|
|
74
|
+
* @param path - The file path to check
|
|
75
|
+
* @param ref - The commit SHA to check at
|
|
76
|
+
* @returns Promise resolving to true if path is a submodule
|
|
77
|
+
*/
|
|
78
|
+
private isSubmodule;
|
|
79
|
+
/**
|
|
80
|
+
* Get blame-like data for a submodule by using commit history
|
|
81
|
+
*
|
|
82
|
+
* Since git blame doesn't work on submodule gitlinks, we get the
|
|
83
|
+
* commit history for who changed the submodule reference.
|
|
84
|
+
*
|
|
85
|
+
* @param path - The submodule path
|
|
86
|
+
* @param ref - The commit SHA
|
|
87
|
+
* @returns Promise resolving to array of blame lines from commit history
|
|
88
|
+
*/
|
|
89
|
+
private getSubmoduleBlame;
|
|
65
90
|
/**
|
|
66
91
|
* Get git blame information from local repository
|
|
67
92
|
*
|
|
68
93
|
* Validates commit existence before attempting blame.
|
|
94
|
+
* For submodules, uses commit history instead of blame.
|
|
69
95
|
* Falls back to API if local blame fails (graceful degradation).
|
|
70
96
|
*
|
|
71
97
|
* @param project - The project ID or path
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-git-data-source.d.ts","sourceRoot":"","sources":["../../src/datasources/local-git-data-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,kBACX,SAAQ,mBACR,YAAW,aAAa;IActB,OAAO,CAAC,QAAQ;IAZlB,OAAO,CAAC,MAAM,CAAgB;IAE9B;;;;;;;OAOG;gBAED,SAAS,EAAE,SAAS,EACZ,QAAQ,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,aAAa;IAMxB;;;;;;;;OAQG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyD7C;;;;;OAKG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmCzC;;;;;;;;;OASG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwExD;;;;;;;;;OASG;IACG,sBAAsB,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IA+EpB
|
|
1
|
+
{"version":3,"file":"local-git-data-source.d.ts","sourceRoot":"","sources":["../../src/datasources/local-git-data-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,kBACX,SAAQ,mBACR,YAAW,aAAa;IActB,OAAO,CAAC,QAAQ;IAZlB,OAAO,CAAC,MAAM,CAAgB;IAE9B;;;;;;;OAOG;gBAED,SAAS,EAAE,SAAS,EACZ,QAAQ,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,aAAa;IAMxB;;;;OAIG;IACH,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED;;;;;;;;OAQG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyD7C;;;;;OAKG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmCzC;;;;;;;;;OASG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwExD;;;;;;;;;OASG;IACG,sBAAsB,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IA+EpB;;;;;;OAMG;YACW,WAAW;IAezB;;;;;;;;;OASG;YACW,iBAAiB;IAiD/B;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAoGhF;;OAEG;YACW,oBAAoB;IAkBlC;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiHxE;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;YACW,cAAc;IA2D5B;;;;;;;;;;OAUG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA8CzB;;;;;;;;OAQG;IACG,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAsBpB;;;;;;;;;;;OAWG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,mBAAmB,EAAE,UAAU,CAAC;CAc7G"}
|
|
@@ -26,7 +26,15 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
|
|
|
26
26
|
constructor(gitlabApi, repoPath, logger, config) {
|
|
27
27
|
super(gitlabApi, logger || new Logger('LocalGitDataSource'));
|
|
28
28
|
this.repoPath = repoPath;
|
|
29
|
-
this.config = config ||
|
|
29
|
+
this.config = config || ConfigService.getInstance();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the repository path
|
|
33
|
+
*
|
|
34
|
+
* @returns The local repository path
|
|
35
|
+
*/
|
|
36
|
+
get repositoryPath() {
|
|
37
|
+
return this.repoPath;
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Get the project path from the git remote URL
|
|
@@ -240,10 +248,88 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
|
|
|
240
248
|
throw ErrorHandler.gitOperationError('Failed to get project contributors', context, error instanceof Error ? error : new Error(String(error)));
|
|
241
249
|
}
|
|
242
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Check if a path is a git submodule (gitlink with mode 160000)
|
|
253
|
+
*
|
|
254
|
+
* @param path - The file path to check
|
|
255
|
+
* @param ref - The commit SHA to check at
|
|
256
|
+
* @returns Promise resolving to true if path is a submodule
|
|
257
|
+
*/
|
|
258
|
+
async isSubmodule(path, ref) {
|
|
259
|
+
try {
|
|
260
|
+
const git = simpleGit(this.repoPath);
|
|
261
|
+
const result = await git.raw(['ls-tree', ref, '--', path]);
|
|
262
|
+
// Submodules have mode 160000 (gitlink)
|
|
263
|
+
return result.startsWith('160000');
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
// Re-throw MCPErrors, otherwise return false (graceful degradation)
|
|
267
|
+
if (error instanceof Error && error.message.includes('MCP')) {
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get blame-like data for a submodule by using commit history
|
|
275
|
+
*
|
|
276
|
+
* Since git blame doesn't work on submodule gitlinks, we get the
|
|
277
|
+
* commit history for who changed the submodule reference.
|
|
278
|
+
*
|
|
279
|
+
* @param path - The submodule path
|
|
280
|
+
* @param ref - The commit SHA
|
|
281
|
+
* @returns Promise resolving to array of blame lines from commit history
|
|
282
|
+
*/
|
|
283
|
+
async getSubmoduleBlame(path, ref) {
|
|
284
|
+
const git = simpleGit(this.repoPath);
|
|
285
|
+
try {
|
|
286
|
+
// Get the last commit that modified this submodule reference
|
|
287
|
+
const logResult = await git.raw([
|
|
288
|
+
'log',
|
|
289
|
+
'-1',
|
|
290
|
+
'--format=%H%n%ae%n%an%n%aI%n%s',
|
|
291
|
+
ref,
|
|
292
|
+
'--',
|
|
293
|
+
path,
|
|
294
|
+
]);
|
|
295
|
+
const lines = logResult.trim().split('\n');
|
|
296
|
+
if (lines.length < 5) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
const [commitId, authorEmail, authorName, authoredDate, message] = lines;
|
|
300
|
+
return [{
|
|
301
|
+
commit: {
|
|
302
|
+
id: commitId,
|
|
303
|
+
message,
|
|
304
|
+
parent_ids: [],
|
|
305
|
+
authored_date: authoredDate,
|
|
306
|
+
author_name: authorName,
|
|
307
|
+
author_email: authorEmail,
|
|
308
|
+
committed_date: authoredDate,
|
|
309
|
+
committer_name: authorName,
|
|
310
|
+
committer_email: authorEmail,
|
|
311
|
+
},
|
|
312
|
+
lines: ['1'],
|
|
313
|
+
}];
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
// Re-throw MCPErrors, otherwise log and return empty (graceful degradation)
|
|
317
|
+
if (error instanceof Error && error.message.includes('MCP')) {
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
this.logger.debug('Failed to get submodule commit history', {
|
|
321
|
+
path,
|
|
322
|
+
ref: ref.substring(0, 8),
|
|
323
|
+
error: error instanceof Error ? error.message : String(error),
|
|
324
|
+
});
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
243
328
|
/**
|
|
244
329
|
* Get git blame information from local repository
|
|
245
330
|
*
|
|
246
331
|
* Validates commit existence before attempting blame.
|
|
332
|
+
* For submodules, uses commit history instead of blame.
|
|
247
333
|
* Falls back to API if local blame fails (graceful degradation).
|
|
248
334
|
*
|
|
249
335
|
* @param project - The project ID or path
|
|
@@ -257,6 +343,16 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
|
|
|
257
343
|
try {
|
|
258
344
|
// Validate commit exists before attempting blame (Requirement 6.1)
|
|
259
345
|
await this.validateCommitExists(ref);
|
|
346
|
+
// Check if path is a submodule - use commit history instead of blame
|
|
347
|
+
if (await this.isSubmodule(path, ref)) {
|
|
348
|
+
this.logger.info('Path is a submodule, using commit history instead of blame', {
|
|
349
|
+
...context,
|
|
350
|
+
isSubmodule: true,
|
|
351
|
+
});
|
|
352
|
+
const submoduleBlame = await this.getSubmoduleBlame(path, ref);
|
|
353
|
+
endTimer();
|
|
354
|
+
return submoduleBlame;
|
|
355
|
+
}
|
|
260
356
|
const git = simpleGit(this.repoPath);
|
|
261
357
|
// Wrap git operation with retry logic (Requirement 3.2)
|
|
262
358
|
const result = await ErrorHandler.withRetry(async () => {
|