buildsight-collector 1.0.3 → 1.0.4
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/index.js +87 -9
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -146,27 +146,103 @@ async function main() {
|
|
|
146
146
|
// --- 🔹 3. Merges locais ---
|
|
147
147
|
const merges = await git.log({ '--merges': null });
|
|
148
148
|
|
|
149
|
-
// --- 🔹 4. Arquivos mais modificados ---
|
|
149
|
+
// --- 🔹 4. Arquivos mais modificados + Code Churn + Hot Spots ---
|
|
150
150
|
const fileChangeCounts = {};
|
|
151
|
+
const codeChurn = {}; // { file: { additions, deletions, commits, authors: Set } }
|
|
152
|
+
const hotSpots = {}; // { file: { totalChanges, bugFixCount, authors: Set, lastModified } }
|
|
153
|
+
|
|
151
154
|
for (const commit of log.all) {
|
|
152
155
|
try {
|
|
156
|
+
// Obter estatísticas detalhadas do commit (linhas +/-)
|
|
153
157
|
const show = await git.raw(['show', '--stat', '--oneline', commit.hash]);
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
const fileMatches = Array.from(show.matchAll(/ ([^\s]+)\s+\|\s+(\d+)/g));
|
|
159
|
+
|
|
160
|
+
// Obter detalhes de adições/remoções por arquivo
|
|
161
|
+
const numstat = await git.raw(['show', '--numstat', '--oneline', commit.hash]);
|
|
162
|
+
const numstatLines = numstat.split('\n').slice(1).filter(line => line.trim());
|
|
163
|
+
|
|
164
|
+
const isBugFix = /^fix|bug|hotfix|urgent|patch/i.test(commit.message);
|
|
165
|
+
|
|
166
|
+
for (const line of numstatLines) {
|
|
167
|
+
const match = line.match(/^(\d+|-)\t(\d+|-)\t(.+)$/);
|
|
168
|
+
if (match) {
|
|
169
|
+
const additions = match[1] === '-' ? 0 : parseInt(match[1], 10);
|
|
170
|
+
const deletions = match[2] === '-' ? 0 : parseInt(match[2], 10);
|
|
171
|
+
const file = match[3].trim();
|
|
172
|
+
|
|
173
|
+
// Atualizar fileChangeCounts
|
|
174
|
+
fileChangeCounts[file] = (fileChangeCounts[file] || 0) + 1;
|
|
175
|
+
|
|
176
|
+
// Atualizar Code Churn
|
|
177
|
+
if (!codeChurn[file]) {
|
|
178
|
+
codeChurn[file] = {
|
|
179
|
+
additions: 0,
|
|
180
|
+
deletions: 0,
|
|
181
|
+
commits: 0,
|
|
182
|
+
authors: new Set()
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
codeChurn[file].additions += additions;
|
|
186
|
+
codeChurn[file].deletions += deletions;
|
|
187
|
+
codeChurn[file].commits += 1;
|
|
188
|
+
codeChurn[file].authors.add(commit.author_email);
|
|
189
|
+
|
|
190
|
+
// Atualizar Hot Spots
|
|
191
|
+
if (!hotSpots[file]) {
|
|
192
|
+
hotSpots[file] = {
|
|
193
|
+
totalChanges: 0,
|
|
194
|
+
bugFixCount: 0,
|
|
195
|
+
authors: new Set(),
|
|
196
|
+
lastModified: commit.date
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
hotSpots[file].totalChanges += 1;
|
|
200
|
+
hotSpots[file].authors.add(commit.author_email);
|
|
201
|
+
if (isBugFix) {
|
|
202
|
+
hotSpots[file].bugFixCount += 1;
|
|
203
|
+
}
|
|
204
|
+
// Atualizar última modificação se mais recente
|
|
205
|
+
if (new Date(commit.date) > new Date(hotSpots[file].lastModified)) {
|
|
206
|
+
hotSpots[file].lastModified = commit.date;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
157
209
|
}
|
|
158
210
|
} catch { }
|
|
159
211
|
}
|
|
160
212
|
|
|
213
|
+
// Converter Sets para arrays e calcular métricas finais
|
|
214
|
+
const codeChurnData = Object.entries(codeChurn).map(([file, data]) => ({
|
|
215
|
+
file,
|
|
216
|
+
additions: data.additions,
|
|
217
|
+
deletions: data.deletions,
|
|
218
|
+
totalChurn: data.additions + data.deletions,
|
|
219
|
+
commits: data.commits,
|
|
220
|
+
churnRate: data.commits > 0 ? (data.additions + data.deletions) / data.commits : 0,
|
|
221
|
+
authors: Array.from(data.authors),
|
|
222
|
+
authorCount: data.authors.size
|
|
223
|
+
})).sort((a, b) => b.totalChurn - a.totalChurn);
|
|
224
|
+
|
|
225
|
+
const hotSpotsData = Object.entries(hotSpots).map(([file, data]) => ({
|
|
226
|
+
file,
|
|
227
|
+
totalChanges: data.totalChanges,
|
|
228
|
+
bugFixCount: data.bugFixCount,
|
|
229
|
+
bugFixRatio: data.totalChanges > 0 ? data.bugFixCount / data.totalChanges : 0,
|
|
230
|
+
authors: Array.from(data.authors),
|
|
231
|
+
authorCount: data.authors.size,
|
|
232
|
+
lastModified: data.lastModified,
|
|
233
|
+
// Score composto: frequência de mudanças + proporção de bug fixes + número de autores
|
|
234
|
+
hotSpotScore: (data.totalChanges * 0.4) + (data.bugFixCount * 0.4) + (data.authors.size * 0.2)
|
|
235
|
+
})).sort((a, b) => b.hotSpotScore - a.hotSpotScore);
|
|
236
|
+
|
|
161
237
|
// --- 🔹 5. Montar dados crus por commit ---
|
|
162
238
|
const commits = await Promise.all(
|
|
163
239
|
log.all.map(async (c) => {
|
|
164
240
|
const isMerge = c.message.startsWith("Merge");
|
|
165
|
-
const isRevert = /revert
|
|
166
|
-
const isFix = /^fix
|
|
167
|
-
const isFeat = /^feat
|
|
168
|
-
const isRefactor = /^refactor
|
|
169
|
-
const isHotfix =
|
|
241
|
+
const isRevert = /revert/i.test(c.message);
|
|
242
|
+
const isFix = /^fix/i.test(c.message);
|
|
243
|
+
const isFeat = /^(feat|feature)/i.test(c.message);
|
|
244
|
+
const isRefactor = /^refactor/i.test(c.message);
|
|
245
|
+
const isHotfix = /^(hotfix|urgent)/i.test(c.message);
|
|
170
246
|
|
|
171
247
|
let filesChanged = [];
|
|
172
248
|
try {
|
|
@@ -230,6 +306,8 @@ async function main() {
|
|
|
230
306
|
message: m.message,
|
|
231
307
|
})),
|
|
232
308
|
fileChangeCounts, // 🔥 arquivos mais alterados
|
|
309
|
+
codeChurn: codeChurnData, // 🔄 detecção de code churn
|
|
310
|
+
hotSpots: hotSpotsData, // 🔥 identificação de hot spots
|
|
233
311
|
},
|
|
234
312
|
commits: batch,
|
|
235
313
|
};
|