@andrebuzeli/git-mcp 15.8.3 → 15.8.5
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 +39 -125
- package/package.json +27 -44
- package/src/index.js +146 -139
- package/src/providers/providerManager.js +203 -203
- package/src/tools/git-diff.js +137 -126
- package/src/tools/git-help.js +285 -285
- package/src/tools/git-remote.js +472 -472
- package/src/tools/git-workflow.js +403 -403
- package/src/utils/env.js +104 -104
- package/src/utils/errors.js +431 -431
- package/src/utils/gitAdapter.js +932 -932
- package/src/utils/hooks.js +255 -255
- package/src/utils/metrics.js +198 -198
- package/src/utils/providerExec.js +58 -58
- package/src/utils/repoHelpers.js +160 -160
- package/src/utils/retry.js +123 -123
- package/install.sh +0 -68
package/src/utils/metrics.js
CHANGED
|
@@ -1,198 +1,198 @@
|
|
|
1
|
-
// Sistema de Métricas/Telemetria para git-mcp
|
|
2
|
-
// Opt-in via variável de ambiente ENABLE_METRICS=true
|
|
3
|
-
|
|
4
|
-
const metricsEnabled = process.env.ENABLE_METRICS === "true";
|
|
5
|
-
|
|
6
|
-
// Armazena métricas em memória
|
|
7
|
-
const metricsStore = {
|
|
8
|
-
operations: [],
|
|
9
|
-
errors: [],
|
|
10
|
-
startTime: Date.now(),
|
|
11
|
-
summary: {
|
|
12
|
-
totalOperations: 0,
|
|
13
|
-
successfulOperations: 0,
|
|
14
|
-
failedOperations: 0,
|
|
15
|
-
totalDurationMs: 0,
|
|
16
|
-
operationsByType: {}
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Registra início de uma operação
|
|
22
|
-
* @param {string} operation - Nome da operação (ex: "git-workflow:push")
|
|
23
|
-
* @param {Object} metadata - Metadados adicionais
|
|
24
|
-
* @returns {string} - ID da operação para tracking
|
|
25
|
-
*/
|
|
26
|
-
export function startOperation(operation, metadata = {}) {
|
|
27
|
-
if (!metricsEnabled) return null;
|
|
28
|
-
|
|
29
|
-
const id = `${operation}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
30
|
-
|
|
31
|
-
metricsStore.operations.push({
|
|
32
|
-
id,
|
|
33
|
-
operation,
|
|
34
|
-
startTime: Date.now(),
|
|
35
|
-
endTime: null,
|
|
36
|
-
duration: null,
|
|
37
|
-
status: "running",
|
|
38
|
-
metadata,
|
|
39
|
-
error: null
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return id;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Finaliza uma operação
|
|
47
|
-
* @param {string} id - ID da operação
|
|
48
|
-
* @param {string} status - "success" ou "error"
|
|
49
|
-
* @param {Error|null} error - Erro se houver
|
|
50
|
-
*/
|
|
51
|
-
export function endOperation(id, status = "success", error = null) {
|
|
52
|
-
if (!metricsEnabled || !id) return;
|
|
53
|
-
|
|
54
|
-
const op = metricsStore.operations.find(o => o.id === id);
|
|
55
|
-
if (!op) return;
|
|
56
|
-
|
|
57
|
-
op.endTime = Date.now();
|
|
58
|
-
op.duration = op.endTime - op.startTime;
|
|
59
|
-
op.status = status;
|
|
60
|
-
|
|
61
|
-
if (error) {
|
|
62
|
-
op.error = {
|
|
63
|
-
message: error.message || String(error),
|
|
64
|
-
code: error.code || "UNKNOWN"
|
|
65
|
-
};
|
|
66
|
-
metricsStore.errors.push({
|
|
67
|
-
timestamp: Date.now(),
|
|
68
|
-
operation: op.operation,
|
|
69
|
-
error: op.error
|
|
70
|
-
});
|
|
71
|
-
metricsStore.summary.failedOperations++;
|
|
72
|
-
} else {
|
|
73
|
-
metricsStore.summary.successfulOperations++;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
metricsStore.summary.totalOperations++;
|
|
77
|
-
metricsStore.summary.totalDurationMs += op.duration;
|
|
78
|
-
|
|
79
|
-
// Atualiza contagem por tipo
|
|
80
|
-
const opType = op.operation.split(":")[0];
|
|
81
|
-
metricsStore.summary.operationsByType[opType] =
|
|
82
|
-
(metricsStore.summary.operationsByType[opType] || 0) + 1;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Wrapper para medir tempo de execução de uma função
|
|
87
|
-
* @param {string} operation - Nome da operação
|
|
88
|
-
* @param {Function} fn - Função async para executar
|
|
89
|
-
* @param {Object} metadata - Metadados adicionais
|
|
90
|
-
*/
|
|
91
|
-
export async function withMetrics(operation, fn, metadata = {}) {
|
|
92
|
-
const id = startOperation(operation, metadata);
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const result = await fn();
|
|
96
|
-
endOperation(id, "success");
|
|
97
|
-
return result;
|
|
98
|
-
} catch (error) {
|
|
99
|
-
endOperation(id, "error", error);
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Retorna resumo das métricas
|
|
106
|
-
*/
|
|
107
|
-
export function getMetricsSummary() {
|
|
108
|
-
if (!metricsEnabled) {
|
|
109
|
-
return { enabled: false, message: "Métricas desabilitadas. Use ENABLE_METRICS=true para habilitar." };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const uptimeMs = Date.now() - metricsStore.startTime;
|
|
113
|
-
const avgDuration = metricsStore.summary.totalOperations > 0
|
|
114
|
-
? metricsStore.summary.totalDurationMs / metricsStore.summary.totalOperations
|
|
115
|
-
: 0;
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
enabled: true,
|
|
119
|
-
uptime: {
|
|
120
|
-
ms: uptimeMs,
|
|
121
|
-
formatted: formatDuration(uptimeMs)
|
|
122
|
-
},
|
|
123
|
-
operations: {
|
|
124
|
-
total: metricsStore.summary.totalOperations,
|
|
125
|
-
successful: metricsStore.summary.successfulOperations,
|
|
126
|
-
failed: metricsStore.summary.failedOperations,
|
|
127
|
-
successRate: metricsStore.summary.totalOperations > 0
|
|
128
|
-
? ((metricsStore.summary.successfulOperations / metricsStore.summary.totalOperations) * 100).toFixed(2) + "%"
|
|
129
|
-
: "N/A"
|
|
130
|
-
},
|
|
131
|
-
performance: {
|
|
132
|
-
totalDurationMs: metricsStore.summary.totalDurationMs,
|
|
133
|
-
avgDurationMs: avgDuration.toFixed(2),
|
|
134
|
-
operationsPerMinute: uptimeMs > 0
|
|
135
|
-
? ((metricsStore.summary.totalOperations / uptimeMs) * 60000).toFixed(2)
|
|
136
|
-
: 0
|
|
137
|
-
},
|
|
138
|
-
byType: metricsStore.summary.operationsByType,
|
|
139
|
-
recentErrors: metricsStore.errors.slice(-10)
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Retorna operações recentes
|
|
145
|
-
* @param {number} limit - Número máximo de operações
|
|
146
|
-
*/
|
|
147
|
-
export function getRecentOperations(limit = 20) {
|
|
148
|
-
if (!metricsEnabled) return [];
|
|
149
|
-
return metricsStore.operations.slice(-limit);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Limpa métricas antigas
|
|
154
|
-
* @param {number} maxAgeMs - Idade máxima em ms (default: 1 hora)
|
|
155
|
-
*/
|
|
156
|
-
export function pruneMetrics(maxAgeMs = 3600000) {
|
|
157
|
-
if (!metricsEnabled) return;
|
|
158
|
-
|
|
159
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
160
|
-
metricsStore.operations = metricsStore.operations.filter(o => o.startTime > cutoff);
|
|
161
|
-
metricsStore.errors = metricsStore.errors.filter(e => e.timestamp > cutoff);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Reseta todas as métricas
|
|
166
|
-
*/
|
|
167
|
-
export function resetMetrics() {
|
|
168
|
-
metricsStore.operations = [];
|
|
169
|
-
metricsStore.errors = [];
|
|
170
|
-
metricsStore.startTime = Date.now();
|
|
171
|
-
metricsStore.summary = {
|
|
172
|
-
totalOperations: 0,
|
|
173
|
-
successfulOperations: 0,
|
|
174
|
-
failedOperations: 0,
|
|
175
|
-
totalDurationMs: 0,
|
|
176
|
-
operationsByType: {}
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Formata duração em formato legível
|
|
182
|
-
*/
|
|
183
|
-
function formatDuration(ms) {
|
|
184
|
-
const seconds = Math.floor(ms / 1000);
|
|
185
|
-
const minutes = Math.floor(seconds / 60);
|
|
186
|
-
const hours = Math.floor(minutes / 60);
|
|
187
|
-
|
|
188
|
-
if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
189
|
-
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
|
190
|
-
return `${seconds}s`;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Verifica se métricas estão habilitadas
|
|
195
|
-
*/
|
|
196
|
-
export function isMetricsEnabled() {
|
|
197
|
-
return metricsEnabled;
|
|
198
|
-
}
|
|
1
|
+
// Sistema de Métricas/Telemetria para git-mcp
|
|
2
|
+
// Opt-in via variável de ambiente ENABLE_METRICS=true
|
|
3
|
+
|
|
4
|
+
const metricsEnabled = process.env.ENABLE_METRICS === "true";
|
|
5
|
+
|
|
6
|
+
// Armazena métricas em memória
|
|
7
|
+
const metricsStore = {
|
|
8
|
+
operations: [],
|
|
9
|
+
errors: [],
|
|
10
|
+
startTime: Date.now(),
|
|
11
|
+
summary: {
|
|
12
|
+
totalOperations: 0,
|
|
13
|
+
successfulOperations: 0,
|
|
14
|
+
failedOperations: 0,
|
|
15
|
+
totalDurationMs: 0,
|
|
16
|
+
operationsByType: {}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Registra início de uma operação
|
|
22
|
+
* @param {string} operation - Nome da operação (ex: "git-workflow:push")
|
|
23
|
+
* @param {Object} metadata - Metadados adicionais
|
|
24
|
+
* @returns {string} - ID da operação para tracking
|
|
25
|
+
*/
|
|
26
|
+
export function startOperation(operation, metadata = {}) {
|
|
27
|
+
if (!metricsEnabled) return null;
|
|
28
|
+
|
|
29
|
+
const id = `${operation}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
30
|
+
|
|
31
|
+
metricsStore.operations.push({
|
|
32
|
+
id,
|
|
33
|
+
operation,
|
|
34
|
+
startTime: Date.now(),
|
|
35
|
+
endTime: null,
|
|
36
|
+
duration: null,
|
|
37
|
+
status: "running",
|
|
38
|
+
metadata,
|
|
39
|
+
error: null
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Finaliza uma operação
|
|
47
|
+
* @param {string} id - ID da operação
|
|
48
|
+
* @param {string} status - "success" ou "error"
|
|
49
|
+
* @param {Error|null} error - Erro se houver
|
|
50
|
+
*/
|
|
51
|
+
export function endOperation(id, status = "success", error = null) {
|
|
52
|
+
if (!metricsEnabled || !id) return;
|
|
53
|
+
|
|
54
|
+
const op = metricsStore.operations.find(o => o.id === id);
|
|
55
|
+
if (!op) return;
|
|
56
|
+
|
|
57
|
+
op.endTime = Date.now();
|
|
58
|
+
op.duration = op.endTime - op.startTime;
|
|
59
|
+
op.status = status;
|
|
60
|
+
|
|
61
|
+
if (error) {
|
|
62
|
+
op.error = {
|
|
63
|
+
message: error.message || String(error),
|
|
64
|
+
code: error.code || "UNKNOWN"
|
|
65
|
+
};
|
|
66
|
+
metricsStore.errors.push({
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
operation: op.operation,
|
|
69
|
+
error: op.error
|
|
70
|
+
});
|
|
71
|
+
metricsStore.summary.failedOperations++;
|
|
72
|
+
} else {
|
|
73
|
+
metricsStore.summary.successfulOperations++;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
metricsStore.summary.totalOperations++;
|
|
77
|
+
metricsStore.summary.totalDurationMs += op.duration;
|
|
78
|
+
|
|
79
|
+
// Atualiza contagem por tipo
|
|
80
|
+
const opType = op.operation.split(":")[0];
|
|
81
|
+
metricsStore.summary.operationsByType[opType] =
|
|
82
|
+
(metricsStore.summary.operationsByType[opType] || 0) + 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Wrapper para medir tempo de execução de uma função
|
|
87
|
+
* @param {string} operation - Nome da operação
|
|
88
|
+
* @param {Function} fn - Função async para executar
|
|
89
|
+
* @param {Object} metadata - Metadados adicionais
|
|
90
|
+
*/
|
|
91
|
+
export async function withMetrics(operation, fn, metadata = {}) {
|
|
92
|
+
const id = startOperation(operation, metadata);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const result = await fn();
|
|
96
|
+
endOperation(id, "success");
|
|
97
|
+
return result;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
endOperation(id, "error", error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Retorna resumo das métricas
|
|
106
|
+
*/
|
|
107
|
+
export function getMetricsSummary() {
|
|
108
|
+
if (!metricsEnabled) {
|
|
109
|
+
return { enabled: false, message: "Métricas desabilitadas. Use ENABLE_METRICS=true para habilitar." };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const uptimeMs = Date.now() - metricsStore.startTime;
|
|
113
|
+
const avgDuration = metricsStore.summary.totalOperations > 0
|
|
114
|
+
? metricsStore.summary.totalDurationMs / metricsStore.summary.totalOperations
|
|
115
|
+
: 0;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
enabled: true,
|
|
119
|
+
uptime: {
|
|
120
|
+
ms: uptimeMs,
|
|
121
|
+
formatted: formatDuration(uptimeMs)
|
|
122
|
+
},
|
|
123
|
+
operations: {
|
|
124
|
+
total: metricsStore.summary.totalOperations,
|
|
125
|
+
successful: metricsStore.summary.successfulOperations,
|
|
126
|
+
failed: metricsStore.summary.failedOperations,
|
|
127
|
+
successRate: metricsStore.summary.totalOperations > 0
|
|
128
|
+
? ((metricsStore.summary.successfulOperations / metricsStore.summary.totalOperations) * 100).toFixed(2) + "%"
|
|
129
|
+
: "N/A"
|
|
130
|
+
},
|
|
131
|
+
performance: {
|
|
132
|
+
totalDurationMs: metricsStore.summary.totalDurationMs,
|
|
133
|
+
avgDurationMs: avgDuration.toFixed(2),
|
|
134
|
+
operationsPerMinute: uptimeMs > 0
|
|
135
|
+
? ((metricsStore.summary.totalOperations / uptimeMs) * 60000).toFixed(2)
|
|
136
|
+
: 0
|
|
137
|
+
},
|
|
138
|
+
byType: metricsStore.summary.operationsByType,
|
|
139
|
+
recentErrors: metricsStore.errors.slice(-10)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Retorna operações recentes
|
|
145
|
+
* @param {number} limit - Número máximo de operações
|
|
146
|
+
*/
|
|
147
|
+
export function getRecentOperations(limit = 20) {
|
|
148
|
+
if (!metricsEnabled) return [];
|
|
149
|
+
return metricsStore.operations.slice(-limit);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Limpa métricas antigas
|
|
154
|
+
* @param {number} maxAgeMs - Idade máxima em ms (default: 1 hora)
|
|
155
|
+
*/
|
|
156
|
+
export function pruneMetrics(maxAgeMs = 3600000) {
|
|
157
|
+
if (!metricsEnabled) return;
|
|
158
|
+
|
|
159
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
160
|
+
metricsStore.operations = metricsStore.operations.filter(o => o.startTime > cutoff);
|
|
161
|
+
metricsStore.errors = metricsStore.errors.filter(e => e.timestamp > cutoff);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Reseta todas as métricas
|
|
166
|
+
*/
|
|
167
|
+
export function resetMetrics() {
|
|
168
|
+
metricsStore.operations = [];
|
|
169
|
+
metricsStore.errors = [];
|
|
170
|
+
metricsStore.startTime = Date.now();
|
|
171
|
+
metricsStore.summary = {
|
|
172
|
+
totalOperations: 0,
|
|
173
|
+
successfulOperations: 0,
|
|
174
|
+
failedOperations: 0,
|
|
175
|
+
totalDurationMs: 0,
|
|
176
|
+
operationsByType: {}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Formata duração em formato legível
|
|
182
|
+
*/
|
|
183
|
+
function formatDuration(ms) {
|
|
184
|
+
const seconds = Math.floor(ms / 1000);
|
|
185
|
+
const minutes = Math.floor(seconds / 60);
|
|
186
|
+
const hours = Math.floor(minutes / 60);
|
|
187
|
+
|
|
188
|
+
if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
189
|
+
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
|
190
|
+
return `${seconds}s`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Verifica se métricas estão habilitadas
|
|
195
|
+
*/
|
|
196
|
+
export function isMetricsEnabled() {
|
|
197
|
+
return metricsEnabled;
|
|
198
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { withRetry } from "./retry.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Executa operações em GitHub e Gitea em PARALELO com retry automático
|
|
5
|
-
* @param {ProviderManager} pm - Provider manager
|
|
6
|
-
* @param {Object} handlers - { github: async (owner) => {}, gitea: async (owner) => {} }
|
|
7
|
-
* @param {Object} options - { retry: boolean, maxRetries: number }
|
|
8
|
-
*/
|
|
9
|
-
export async function runBoth(pm, handlers, options = {}) {
|
|
10
|
-
const { retry = true, maxRetries = 3 } = options;
|
|
11
|
-
const out = { github: null, gitea: null };
|
|
12
|
-
|
|
13
|
-
// Buscar owners em paralelo
|
|
14
|
-
const [ghOwner, geOwner] = await Promise.all([
|
|
15
|
-
pm.getGitHubOwner().catch(() => ""),
|
|
16
|
-
pm.getGiteaOwner().catch(() => "")
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
// Preparar promises para execução paralela
|
|
20
|
-
const promises = [];
|
|
21
|
-
|
|
22
|
-
if (pm.github && ghOwner && handlers.github) {
|
|
23
|
-
const githubPromise = async () => {
|
|
24
|
-
const executor = () => handlers.github(ghOwner);
|
|
25
|
-
try {
|
|
26
|
-
const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
|
|
27
|
-
return { provider: "github", result };
|
|
28
|
-
} catch (e) {
|
|
29
|
-
return { provider: "github", result: { ok: false, error: String(e?.message || e) } };
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
promises.push(githubPromise());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (pm.giteaUrl && pm.giteaToken && geOwner && handlers.gitea) {
|
|
36
|
-
const giteaPromise = async () => {
|
|
37
|
-
const executor = () => handlers.gitea(geOwner);
|
|
38
|
-
try {
|
|
39
|
-
const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
|
|
40
|
-
return { provider: "gitea", result };
|
|
41
|
-
} catch (e) {
|
|
42
|
-
return { provider: "gitea", result: { ok: false, error: String(e?.message || e) } };
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
promises.push(giteaPromise());
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Executar em paralelo
|
|
49
|
-
const results = await Promise.all(promises);
|
|
50
|
-
|
|
51
|
-
// Mapear resultados
|
|
52
|
-
for (const { provider, result } of results) {
|
|
53
|
-
out[provider] = result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return out;
|
|
57
|
-
}
|
|
58
|
-
|
|
1
|
+
import { withRetry } from "./retry.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Executa operações em GitHub e Gitea em PARALELO com retry automático
|
|
5
|
+
* @param {ProviderManager} pm - Provider manager
|
|
6
|
+
* @param {Object} handlers - { github: async (owner) => {}, gitea: async (owner) => {} }
|
|
7
|
+
* @param {Object} options - { retry: boolean, maxRetries: number }
|
|
8
|
+
*/
|
|
9
|
+
export async function runBoth(pm, handlers, options = {}) {
|
|
10
|
+
const { retry = true, maxRetries = 3 } = options;
|
|
11
|
+
const out = { github: null, gitea: null };
|
|
12
|
+
|
|
13
|
+
// Buscar owners em paralelo
|
|
14
|
+
const [ghOwner, geOwner] = await Promise.all([
|
|
15
|
+
pm.getGitHubOwner().catch(() => ""),
|
|
16
|
+
pm.getGiteaOwner().catch(() => "")
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
// Preparar promises para execução paralela
|
|
20
|
+
const promises = [];
|
|
21
|
+
|
|
22
|
+
if (pm.github && ghOwner && handlers.github) {
|
|
23
|
+
const githubPromise = async () => {
|
|
24
|
+
const executor = () => handlers.github(ghOwner);
|
|
25
|
+
try {
|
|
26
|
+
const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
|
|
27
|
+
return { provider: "github", result };
|
|
28
|
+
} catch (e) {
|
|
29
|
+
return { provider: "github", result: { ok: false, error: String(e?.message || e) } };
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
promises.push(githubPromise());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (pm.giteaUrl && pm.giteaToken && geOwner && handlers.gitea) {
|
|
36
|
+
const giteaPromise = async () => {
|
|
37
|
+
const executor = () => handlers.gitea(geOwner);
|
|
38
|
+
try {
|
|
39
|
+
const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
|
|
40
|
+
return { provider: "gitea", result };
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return { provider: "gitea", result: { ok: false, error: String(e?.message || e) } };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
promises.push(giteaPromise());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Executar em paralelo
|
|
49
|
+
const results = await Promise.all(promises);
|
|
50
|
+
|
|
51
|
+
// Mapear resultados
|
|
52
|
+
for (const { provider, result } of results) {
|
|
53
|
+
out[provider] = result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|