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.
Files changed (2) hide show
  1. package/index.js +87 -9
  2. 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 files = Array.from(show.matchAll(/ ([^\s]+)\s+\|\s+\d+/g)).map(m => m[1]);
155
- for (const file of files) {
156
- fileChangeCounts[file] = (fileChangeCounts[file] || 0) + 1;
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:/i.test(c.message);
166
- const isFix = /^fix:/i.test(c.message);
167
- const isFeat = /^feat:/i.test(c.message);
168
- const isRefactor = /^refactor:/i.test(c.message);
169
- const isHotfix = /hotfix|urgent/i.test(c.message);
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildsight-collector",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "CLI do BuildSight para coleta automatizada de commits e métricas de repositórios.",
5
5
  "main": "index.js",
6
6
  "bin": {