From 7bd84a6751e76a969d947e643ff79fc56e759756 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:39:11 +0200
Subject: [PATCH 001/125] test
---
.github/workflows/statistics_bot.yml | 289 +++++++++++++++++++++++++++
1 file changed, 289 insertions(+)
create mode 100644 .github/workflows/statistics_bot.yml
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
new file mode 100644
index 0000000000..2954772cbd
--- /dev/null
+++ b/.github/workflows/statistics_bot.yml
@@ -0,0 +1,289 @@
+name: Monthly Repository Statistics
+
+on:
+ schedule:
+ # Runs at 9 AM UTC on the 1st day of every month
+ - cron: '0 9 1 * *'
+
+ pull_request:
+
+
+jobs:
+ generate-monthly-report:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ issues: write
+ pull-requests: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Generate Monthly Statistics Report
+ uses: actions/github-script@v7
+ env:
+ MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
+ // Get date range for previous month
+ function getLastMonthRange() {
+ const now = new Date();
+ const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
+ const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0);
+
+ return {
+ start: lastMonth.toISOString(),
+ end: endOfLastMonth.toISOString(),
+ monthName: lastMonth.toLocaleString('default', { month: 'long', year: 'numeric' })
+ };
+ }
+
+ // Get commit statistics
+ async function getCommitStats(since, until) {
+ const commits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ const contributors = new Set();
+ const filesChanged = new Set();
+ let totalAdditions = 0;
+ let totalDeletions = 0;
+
+ for (const commit of commits) {
+ if (commit.author) {
+ contributors.add(commit.author.login);
+ }
+
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ if (commitDetail.data.files) {
+ commitDetail.data.files.forEach(file => {
+ filesChanged.add(file.filename);
+ });
+ }
+
+ totalAdditions += commitDetail.data.stats?.additions || 0;
+ totalDeletions += commitDetail.data.stats?.deletions || 0;
+ } catch (error) {
+ console.log(`Error getting commit details for ${commit.sha}:`, error.message);
+ }
+ }
+
+ return {
+ totalCommits: commits.length,
+ uniqueContributors: contributors.size,
+ contributorsList: Array.from(contributors),
+ filesChanged: filesChanged.size,
+ linesAdded: totalAdditions,
+ linesRemoved: totalDeletions,
+ netLines: totalAdditions - totalDeletions,
+ };
+ }
+
+ // Get pull request statistics
+ async function getPRStats(since, until) {
+ const allPRs = await github.paginate(github.rest.pulls.list, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'all',
+ sort: 'updated',
+ direction: 'desc',
+ per_page: 100,
+ });
+
+ const mergedPRs = allPRs.filter(pr =>
+ pr.merged_at &&
+ new Date(pr.merged_at) >= new Date(since) &&
+ new Date(pr.merged_at) <= new Date(until)
+ );
+
+ const openedPRs = allPRs.filter(pr =>
+ new Date(pr.created_at) >= new Date(since) &&
+ new Date(pr.created_at) <= new Date(until)
+ );
+
+ let totalReviewTime = 0;
+ let reviewTimeCount = 0;
+
+ for (const pr of mergedPRs) {
+ if (pr.created_at && pr.merged_at) {
+ const reviewTime = new Date(pr.merged_at) - new Date(pr.created_at);
+ totalReviewTime += reviewTime;
+ reviewTimeCount++;
+ }
+ }
+
+ const avgReviewTimeHours = reviewTimeCount > 0
+ ? Math.round(totalReviewTime / reviewTimeCount / (1000 * 60 * 60))
+ : 0;
+
+ return {
+ merged: mergedPRs.length,
+ opened: openedPRs.length,
+ avgReviewTimeHours,
+ mergedPRTitles: mergedPRs.slice(0, 10).map(pr => `#${pr.number}: ${pr.title}`),
+ };
+ }
+
+ // Get issue statistics
+ async function getIssueStats(since, until) {
+ const allIssues = await github.paginate(github.rest.issues.listForRepo, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'all',
+ sort: 'updated',
+ direction: 'desc',
+ per_page: 100,
+ });
+
+ const issues = allIssues.filter(issue => !issue.pull_request);
+
+ const openedIssues = issues.filter(issue =>
+ new Date(issue.created_at) >= new Date(since) &&
+ new Date(issue.created_at) <= new Date(until)
+ );
+
+ const closedIssues = issues.filter(issue =>
+ issue.closed_at &&
+ new Date(issue.closed_at) >= new Date(since) &&
+ new Date(issue.closed_at) <= new Date(until)
+ );
+
+ return {
+ opened: openedIssues.length,
+ closed: closedIssues.length,
+ };
+ }
+
+ // Get language statistics
+ async function getLanguageStats() {
+ try {
+ const languages = await github.rest.repos.listLanguages({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
+ return Object.entries(languages.data)
+ .map(([lang, bytes]) => ({
+ language: lang,
+ percentage: ((bytes / total) * 100).toFixed(1)
+ }))
+ .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
+ .slice(0, 5);
+ } catch (error) {
+ console.log('Error getting language stats:', error.message);
+ return [];
+ }
+ }
+
+ // Generate markdown report
+ function generateReport(stats, dateRange) {
+ const { monthName } = dateRange;
+
+ return `# 📊 Monthly Repository Report - ${monthName}
+
+ ## 🎯 Key Metrics
+
+ | Metric | Count |
+ |--------|--------|
+ | 📝 Total Commits | ${stats.commits.totalCommits} |
+ | 📁 Files Changed | ${stats.commits.filesChanged} |
+ | 🔀 PRs Merged | ${stats.prs.merged} |
+ | 🆕 PRs Opened | ${stats.prs.opened} |
+ | 🐛 Issues Opened | ${stats.issues.opened} |
+ | ✅ Issues Closed | ${stats.issues.closed} |
+ | 👥 Active Contributors | ${stats.commits.uniqueContributors} |
+
+ ## 📈 Code Changes
+
+ - **Lines Added:** +${stats.commits.linesAdded.toLocaleString()}
+ - **Lines Removed:** -${stats.commits.linesRemoved.toLocaleString()}
+ - **Net Change:** ${stats.commits.netLines >= 0 ? '+' : ''}${stats.commits.netLines.toLocaleString()} lines
+
+ ${stats.prs.avgReviewTimeHours > 0 ? `## ⏱️ Review Metrics\n\n- **Average PR Review Time:** ${stats.prs.avgReviewTimeHours} hours` : ''}
+
+ ## 👨💻 Top Contributors
+
+ ${stats.commits.contributorsList.slice(0, 5).map((contributor, i) => `${i + 1}. @${contributor}`).join('\n')}
+
+ ${stats.languages.length > 0 ? `## 💻 Language Breakdown\n\n${stats.languages.map(lang => `- **${lang.language}:** ${lang.percentage}%`).join('\n')}` : ''}
+
+ ${stats.prs.mergedPRTitles.length > 0 ? `## 🎉 Recent Merged PRs\n\n${stats.prs.mergedPRTitles.slice(0, 5).map(title => `- ${title}`).join('\n')}` : ''}
+
+ ---
+ *Generated automatically by Monthly Stats Bot on ${new Date().toLocaleDateString()}*`;
+ }
+
+ // Main execution
+ try {
+ const dateRange = getLastMonthRange();
+ console.log(`Generating report for ${dateRange.monthName}...`);
+
+ // Gather all statistics
+ const [commitStats, prStats, issueStats, languages] = await Promise.all([
+ getCommitStats(dateRange.start, dateRange.end),
+ getPRStats(dateRange.start, dateRange.end),
+ getIssueStats(dateRange.start, dateRange.end),
+ getLanguageStats(),
+ ]);
+
+ const stats = {
+ commits: commitStats,
+ prs: prStats,
+ issues: issueStats,
+ languages,
+ };
+
+ // Generate the report
+ const reportBody = generateReport(stats, dateRange);
+
+ // Post to Mattermost
+ const mattermostPayload = {
+ text: `## 📊 Monthly Repository Report - ${dateRange.monthName}`,
+ attachments: [{
+ color: '#36a64f',
+ fields: [
+ { title: '📝 Total Commits', value: stats.commits.totalCommits.toString(), short: true },
+ { title: '📁 Files Changed', value: stats.commits.filesChanged.toString(), short: true },
+ { title: '🔀 PRs Merged', value: stats.prs.merged.toString(), short: true },
+ { title: '🆕 PRs Opened', value: stats.prs.opened.toString(), short: true },
+ { title: '🐛 Issues Opened', value: stats.issues.opened.toString(), short: true },
+ { title: '✅ Issues Closed', value: stats.issues.closed.toString(), short: true },
+ { title: '👥 Active Contributors', value: stats.commits.uniqueContributors.toString(), short: true },
+ { title: '📈 Net Lines Changed', value: `${stats.commits.netLines >= 0 ? '+' : ''}${stats.commits.netLines.toLocaleString()}`, short: true }
+ ],
+ text: `**Code Changes:** +${stats.commits.linesAdded.toLocaleString()} / -${stats.commits.linesRemoved.toLocaleString()} lines\n` +
+ `**Top Contributors:** ${stats.commits.contributorsList.slice(0, 3).join(', ')}\n` +
+ `${stats.languages.length > 0 ? `**Languages:** ${stats.languages.slice(0, 3).map(l => `${l.language} (${l.percentage}%)`).join(', ')}\n` : ''}` +
+ `${stats.prs.avgReviewTimeHours > 0 ? `**Avg PR Review Time:** ${stats.prs.avgReviewTimeHours} hours\n` : ''}` +
+ `\n[View Repository](https://github.com/${context.repo.owner}/${context.repo.repo})`
+ }]
+ };
+
+ const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(mattermostPayload)
+ });
+
+ if (!response.ok) {
+ throw new Error(`Mattermost webhook failed: ${response.status} ${response.statusText}`);
+ }
+
+ console.log(`Monthly report posted to Mattermost successfully`);
+
+ } catch (error) {
+ console.error('Error generating monthly report:', error);
+ core.setFailed(`Monthly report generation failed: ${error.message}`);
+ }
\ No newline at end of file
From da7fecd1100d2aa81434a88a38f9be18047e96ee Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:40:27 +0200
Subject: [PATCH 002/125] test2
---
.github/workflows/statistics_bot.yml | 47 ++--------------------------
1 file changed, 3 insertions(+), 44 deletions(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 2954772cbd..ee97cb8387 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -4,9 +4,7 @@ on:
schedule:
# Runs at 9 AM UTC on the 1st day of every month
- cron: '0 9 1 * *'
-
- pull_request:
-
+ workflow_dispatch: # Allows manual triggering
jobs:
generate-monthly-report:
@@ -24,6 +22,8 @@ jobs:
uses: actions/github-script@v7
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
+ with:
+ script: |
// Get date range for previous month
function getLastMonthRange() {
const now = new Date();
@@ -185,44 +185,6 @@ jobs:
}
}
- // Generate markdown report
- function generateReport(stats, dateRange) {
- const { monthName } = dateRange;
-
- return `# 📊 Monthly Repository Report - ${monthName}
-
- ## 🎯 Key Metrics
-
- | Metric | Count |
- |--------|--------|
- | 📝 Total Commits | ${stats.commits.totalCommits} |
- | 📁 Files Changed | ${stats.commits.filesChanged} |
- | 🔀 PRs Merged | ${stats.prs.merged} |
- | 🆕 PRs Opened | ${stats.prs.opened} |
- | 🐛 Issues Opened | ${stats.issues.opened} |
- | ✅ Issues Closed | ${stats.issues.closed} |
- | 👥 Active Contributors | ${stats.commits.uniqueContributors} |
-
- ## 📈 Code Changes
-
- - **Lines Added:** +${stats.commits.linesAdded.toLocaleString()}
- - **Lines Removed:** -${stats.commits.linesRemoved.toLocaleString()}
- - **Net Change:** ${stats.commits.netLines >= 0 ? '+' : ''}${stats.commits.netLines.toLocaleString()} lines
-
- ${stats.prs.avgReviewTimeHours > 0 ? `## ⏱️ Review Metrics\n\n- **Average PR Review Time:** ${stats.prs.avgReviewTimeHours} hours` : ''}
-
- ## 👨💻 Top Contributors
-
- ${stats.commits.contributorsList.slice(0, 5).map((contributor, i) => `${i + 1}. @${contributor}`).join('\n')}
-
- ${stats.languages.length > 0 ? `## 💻 Language Breakdown\n\n${stats.languages.map(lang => `- **${lang.language}:** ${lang.percentage}%`).join('\n')}` : ''}
-
- ${stats.prs.mergedPRTitles.length > 0 ? `## 🎉 Recent Merged PRs\n\n${stats.prs.mergedPRTitles.slice(0, 5).map(title => `- ${title}`).join('\n')}` : ''}
-
- ---
- *Generated automatically by Monthly Stats Bot on ${new Date().toLocaleDateString()}*`;
- }
-
// Main execution
try {
const dateRange = getLastMonthRange();
@@ -243,9 +205,6 @@ jobs:
languages,
};
- // Generate the report
- const reportBody = generateReport(stats, dateRange);
-
// Post to Mattermost
const mattermostPayload = {
text: `## 📊 Monthly Repository Report - ${dateRange.monthName}`,
From 29512e001805e0687f8e06dec2bf71d869dd2b3b Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:41:53 +0200
Subject: [PATCH 003/125] pr activation
---
.github/workflows/statistics_bot.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index ee97cb8387..c37b1b481e 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -4,7 +4,8 @@ on:
schedule:
# Runs at 9 AM UTC on the 1st day of every month
- cron: '0 9 1 * *'
- workflow_dispatch: # Allows manual triggering
+ pull_request: # Allows manual triggering
+
jobs:
generate-monthly-report:
From c019d21bbb0fdd7553ef6b2970988ddc131ec5d4 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:43:24 +0200
Subject: [PATCH 004/125] ?
---
.github/workflows/statistics_bot.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index c37b1b481e..99957f8e1f 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -4,6 +4,7 @@ on:
schedule:
# Runs at 9 AM UTC on the 1st day of every month
- cron: '0 9 1 * *'
+
pull_request: # Allows manual triggering
From e084a274ba56deb519298210b2ab057c702406a7 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:44:05 +0200
Subject: [PATCH 005/125] ?
---
.github/workflows/statistics_bot.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 99957f8e1f..6213358e8f 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -4,9 +4,8 @@ on:
schedule:
# Runs at 9 AM UTC on the 1st day of every month
- cron: '0 9 1 * *'
-
- pull_request: # Allows manual triggering
+ pull_request:
jobs:
generate-monthly-report:
From fe8ac322dd84fa015292ff1788540fde50a23e27 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:44:57 +0200
Subject: [PATCH 006/125] ?
---
.github/workflows/statistics_bot.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 6213358e8f..2b934ad506 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -1,10 +1,6 @@
name: Monthly Repository Statistics
on:
- schedule:
- # Runs at 9 AM UTC on the 1st day of every month
- - cron: '0 9 1 * *'
-
pull_request:
jobs:
From b96fd95fb06229a6869247a4af9ec0180eff9b52 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:45:22 +0200
Subject: [PATCH 007/125] ?
---
.github/workflows/statistics_bot.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 2b934ad506..4401ea1106 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -2,6 +2,9 @@ name: Monthly Repository Statistics
on:
pull_request:
+ schedule:
+ # Runs at 9 AM UTC on the 1st day of every month
+ - cron: '0 9 1 * *'
jobs:
generate-monthly-report:
From a1f0e86f03a83791f4b4df75379620f4f5a050fe Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:46:42 +0200
Subject: [PATCH 008/125] ?=
---
.github/workflows/statistics_bot.yml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 4401ea1106..2b934ad506 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -2,9 +2,6 @@ name: Monthly Repository Statistics
on:
pull_request:
- schedule:
- # Runs at 9 AM UTC on the 1st day of every month
- - cron: '0 9 1 * *'
jobs:
generate-monthly-report:
From 34eaf6b405a3dcd294942301316c3b56971468ea Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:47:27 +0200
Subject: [PATCH 009/125] now?
---
.github/workflows/statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index 2b934ad506..f12d5d2747 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -1,7 +1,7 @@
name: Monthly Repository Statistics
on:
- pull_request:
+ pull_request
jobs:
generate-monthly-report:
From 0c09f3d2c26688a90d222d3267263c30228460e8 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:48:00 +0200
Subject: [PATCH 010/125] =
---
.github/workflows/statistics_bot.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/statistics_bot.yml
index f12d5d2747..eba2c11431 100644
--- a/.github/workflows/statistics_bot.yml
+++ b/.github/workflows/statistics_bot.yml
@@ -1,7 +1,8 @@
name: Monthly Repository Statistics
on:
- pull_request
+ pull_request:
+ types: [opened, synchronize]
jobs:
generate-monthly-report:
From e8388913acc20405f520facec15779f987ee373f Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:48:30 +0200
Subject: [PATCH 011/125] ??
---
.github/workflows/{statistics_bot.yml => gh_statistics_bot.yml} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename .github/workflows/{statistics_bot.yml => gh_statistics_bot.yml} (100%)
diff --git a/.github/workflows/statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
similarity index 100%
rename from .github/workflows/statistics_bot.yml
rename to .github/workflows/gh_statistics_bot.yml
From 718f2cf08ab7ebaf975c4315c2436e2511602d81 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:19:38 +0200
Subject: [PATCH 012/125] test
---
.github/workflows/gh_statistics_bot.yml | 333 +++++++++++++++---------
1 file changed, 214 insertions(+), 119 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index eba2c11431..c15fa3837b 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -1,14 +1,17 @@
-name: Monthly Repository Statistics
+name: Monthly Repository Statistics with Charts
on:
pull_request:
types: [opened, synchronize]
+ schedule:
+ - cron: '0 9 1 * *'
+ workflow_dispatch:
jobs:
generate-monthly-report:
runs-on: ubuntu-latest
permissions:
- contents: read
+ contents: write # Changed to write for committing charts
issues: write
pull-requests: read
@@ -16,27 +19,47 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Generate Monthly Statistics Report
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+
+ - name: Install dependencies
+ run: |
+ npm init -y
+ npm install asciichart
+
+ - name: Generate Monthly Statistics Report with Charts
uses: actions/github-script@v7
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- // Get date range for previous month
- function getLastMonthRange() {
+ const asciichart = require('asciichart');
+
+ // Get date ranges for last 12 months
+ function getLast12MonthsRanges() {
+ const ranges = [];
const now = new Date();
- const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
- const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0);
- return {
- start: lastMonth.toISOString(),
- end: endOfLastMonth.toISOString(),
- monthName: lastMonth.toLocaleString('default', { month: 'long', year: 'numeric' })
- };
+ for (let i = 11; i >= 0; i--) {
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+
+ ranges.push({
+ start: monthStart.toISOString(),
+ end: monthEnd.toISOString(),
+ monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' }),
+ month: monthStart.getMonth(),
+ year: monthStart.getFullYear()
+ });
+ }
+
+ return ranges;
}
- // Get commit statistics
- async function getCommitStats(since, until) {
+ // Get commit statistics for a specific month
+ async function getMonthCommitStats(since, until) {
const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
repo: context.repo.repo,
@@ -50,7 +73,7 @@ jobs:
let totalAdditions = 0;
let totalDeletions = 0;
- for (const commit of commits) {
+ for (const commit of commits.slice(0, 50)) { // Limit to avoid rate limits
if (commit.author) {
contributors.add(commit.author.login);
}
@@ -71,14 +94,13 @@ jobs:
totalAdditions += commitDetail.data.stats?.additions || 0;
totalDeletions += commitDetail.data.stats?.deletions || 0;
} catch (error) {
- console.log(`Error getting commit details for ${commit.sha}:`, error.message);
+ console.log(`Error getting commit details: ${error.message}`);
}
}
return {
totalCommits: commits.length,
uniqueContributors: contributors.size,
- contributorsList: Array.from(contributors),
filesChanged: filesChanged.size,
linesAdded: totalAdditions,
linesRemoved: totalDeletions,
@@ -86,8 +108,8 @@ jobs:
};
}
- // Get pull request statistics
- async function getPRStats(since, until) {
+ // Get PR statistics for a specific month
+ async function getMonthPRStats(since, until) {
const allPRs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
@@ -108,122 +130,195 @@ jobs:
new Date(pr.created_at) <= new Date(until)
);
- let totalReviewTime = 0;
- let reviewTimeCount = 0;
-
- for (const pr of mergedPRs) {
- if (pr.created_at && pr.merged_at) {
- const reviewTime = new Date(pr.merged_at) - new Date(pr.created_at);
- totalReviewTime += reviewTime;
- reviewTimeCount++;
- }
- }
-
- const avgReviewTimeHours = reviewTimeCount > 0
- ? Math.round(totalReviewTime / reviewTimeCount / (1000 * 60 * 60))
- : 0;
-
return {
merged: mergedPRs.length,
opened: openedPRs.length,
- avgReviewTimeHours,
- mergedPRTitles: mergedPRs.slice(0, 10).map(pr => `#${pr.number}: ${pr.title}`),
};
}
- // Get issue statistics
- async function getIssueStats(since, until) {
- const allIssues = await github.paginate(github.rest.issues.listForRepo, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'all',
- sort: 'updated',
- direction: 'desc',
- per_page: 100,
- });
-
- const issues = allIssues.filter(issue => !issue.pull_request);
-
- const openedIssues = issues.filter(issue =>
- new Date(issue.created_at) >= new Date(since) &&
- new Date(issue.created_at) <= new Date(until)
- );
-
- const closedIssues = issues.filter(issue =>
- issue.closed_at &&
- new Date(issue.closed_at) >= new Date(since) &&
- new Date(issue.closed_at) <= new Date(until)
- );
+ // Create professional SVG charts
+ function createSVGChart(data, title, color = '#22c55e') {
+ const width = 600;
+ const height = 200;
+ const padding = 40;
+ const chartWidth = width - 2 * padding;
+ const chartHeight = height - 2 * padding;
+
+ const maxValue = Math.max(...data);
+ const minValue = Math.min(...data);
+ const range = maxValue - minValue || 1;
+
+ // Create SVG path for the line
+ const points = data.map((value, index) => {
+ const x = padding + (index / (data.length - 1)) * chartWidth;
+ const y = padding + chartHeight - ((value - minValue) / range) * chartHeight;
+ return `${x},${y}`;
+ }).join(' L');
+
+ // Create area fill path
+ const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
+
+ return ``;
+ }
+ // Commit charts to repository
+ async function commitChartsToRepo(monthlyData, currentBranch) {
+ const commits = monthlyData.map(d => d.stats.totalCommits);
+ const linesAdded = monthlyData.map(d => d.stats.linesAdded);
+ const prsOpened = monthlyData.map(d => d.prStats.opened);
+ const prsMerged = monthlyData.map(d => d.prStats.merged);
+
+ // Create SVG charts
+ const commitsSvg = createSVGChart(commits, 'Monthly Commits', '#3b82f6');
+ const linesSvg = createSVGChart(linesAdded, 'Lines Added per Month', '#22c55e');
+ const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
+
+ const files = [
+ { path: 'docs/assets/commits-chart.svg', content: commitsSvg },
+ { path: 'docs/assets/lines-chart.svg', content: linesSvg },
+ { path: 'docs/assets/prs-chart.svg', content: prsSvg }
+ ];
+
+ // Create or update each chart file
+ for (const file of files) {
+ try {
+ const base64Content = Buffer.from(file.content).toString('base64');
+
+ await github.rest.repos.createOrUpdateFileContents({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: file.path,
+ message: `Update repository activity chart: ${file.path}`,
+ content: base64Content,
+ branch: currentBranch
+ });
+
+ console.log(`Updated chart: ${file.path}`);
+ } catch (error) {
+ console.log(`Error updating ${file.path}:`, error.message);
+ }
+ }
+
return {
- opened: openedIssues.length,
- closed: closedIssues.length,
+ commitsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/commits-chart.svg`,
+ linesUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/lines-chart.svg`,
+ prsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/prs-chart.svg`
};
}
- // Get language statistics
- async function getLanguageStats() {
- try {
- const languages = await github.rest.repos.listLanguages({
- owner: context.repo.owner,
- repo: context.repo.repo,
+ // Main execution
+ try {
+ console.log('Gathering 12 months of repository data...');
+
+ const monthRanges = getLast12MonthsRanges();
+ const monthlyData = [];
+
+ // Gather data for each month (with rate limiting)
+ for (const range of monthRanges) {
+ console.log(`Processing ${range.monthName}...`);
+
+ const [stats, prStats] = await Promise.all([
+ getMonthCommitStats(range.start, range.end),
+ getMonthPRStats(range.start, range.end)
+ ]);
+
+ monthlyData.push({
+ month: range.monthName,
+ stats,
+ prStats
});
- const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
- return Object.entries(languages.data)
- .map(([lang, bytes]) => ({
- language: lang,
- percentage: ((bytes / total) * 100).toFixed(1)
- }))
- .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
- .slice(0, 5);
- } catch (error) {
- console.log('Error getting language stats:', error.message);
- return [];
+ // Small delay to avoid rate limiting
+ await new Promise(resolve => setTimeout(resolve, 1000));
}
- }
- // Main execution
- try {
- const dateRange = getLastMonthRange();
- console.log(`Generating report for ${dateRange.monthName}...`);
-
- // Gather all statistics
- const [commitStats, prStats, issueStats, languages] = await Promise.all([
- getCommitStats(dateRange.start, dateRange.end),
- getPRStats(dateRange.start, dateRange.end),
- getIssueStats(dateRange.start, dateRange.end),
- getLanguageStats(),
- ]);
-
- const stats = {
- commits: commitStats,
- prs: prStats,
- issues: issueStats,
- languages,
- };
+ // Get current branch for testing
+ const currentBranch = context.ref.replace('refs/heads/', '') || 'main';
+ console.log(`Using branch: ${currentBranch}`);
+
+ // Create and commit SVG charts
+ console.log('Generating and committing SVG charts...');
+ const chartUrls = await commitChartsToRepo(monthlyData, currentBranch);
- // Post to Mattermost
+ // Create ASCII charts for Mattermost
+ const commits = monthlyData.map(d => d.stats.totalCommits);
+ const linesAdded = monthlyData.map(d => d.stats.linesAdded);
+
+ const commitsChart = asciichart.plot(commits, {
+ height: 8,
+ colors: [asciichart.blue]
+ });
+
+ const linesChart = asciichart.plot(linesAdded, {
+ height: 8,
+ colors: [asciichart.green]
+ });
+
+ // Get current month stats for summary
+ const currentMonth = monthlyData[monthlyData.length - 1];
+ const stats = currentMonth.stats;
+ const prStats = currentMonth.prStats;
+
+ // Post to Mattermost with ASCII charts and SVG links
const mattermostPayload = {
- text: `## 📊 Monthly Repository Report - ${dateRange.monthName}`,
- attachments: [{
- color: '#36a64f',
- fields: [
- { title: '📝 Total Commits', value: stats.commits.totalCommits.toString(), short: true },
- { title: '📁 Files Changed', value: stats.commits.filesChanged.toString(), short: true },
- { title: '🔀 PRs Merged', value: stats.prs.merged.toString(), short: true },
- { title: '🆕 PRs Opened', value: stats.prs.opened.toString(), short: true },
- { title: '🐛 Issues Opened', value: stats.issues.opened.toString(), short: true },
- { title: '✅ Issues Closed', value: stats.issues.closed.toString(), short: true },
- { title: '👥 Active Contributors', value: stats.commits.uniqueContributors.toString(), short: true },
- { title: '📈 Net Lines Changed', value: `${stats.commits.netLines >= 0 ? '+' : ''}${stats.commits.netLines.toLocaleString()}`, short: true }
- ],
- text: `**Code Changes:** +${stats.commits.linesAdded.toLocaleString()} / -${stats.commits.linesRemoved.toLocaleString()} lines\n` +
- `**Top Contributors:** ${stats.commits.contributorsList.slice(0, 3).join(', ')}\n` +
- `${stats.languages.length > 0 ? `**Languages:** ${stats.languages.slice(0, 3).map(l => `${l.language} (${l.percentage}%)`).join(', ')}\n` : ''}` +
- `${stats.prs.avgReviewTimeHours > 0 ? `**Avg PR Review Time:** ${stats.prs.avgReviewTimeHours} hours\n` : ''}` +
- `\n[View Repository](https://github.com/${context.repo.owner}/${context.repo.repo})`
- }]
+ text: `## 📊 Monthly Repository Report - ${currentMonth.month}`,
+ attachments: [
+ {
+ color: '#36a64f',
+ fields: [
+ { title: '📝 Total Commits', value: stats.totalCommits.toString(), short: true },
+ { title: '📁 Files Changed', value: stats.filesChanged.toString(), short: true },
+ { title: '🔀 PRs Merged', value: prStats.merged.toString(), short: true },
+ { title: '🆕 PRs Opened', value: prStats.opened.toString(), short: true },
+ { title: '👥 Active Contributors', value: stats.uniqueContributors.toString(), short: true },
+ { title: '📈 Net Lines Changed', value: `${stats.netLines >= 0 ? '+' : ''}${stats.netLines.toLocaleString()}`, short: true }
+ ],
+ text: `**Code Changes:** +${stats.linesAdded.toLocaleString()} / -${stats.linesRemoved.toLocaleString()} lines\n` +
+ `**Repository:** [${context.repo.owner}/${context.repo.repo}](https://github.com/${context.repo.owner}/${context.repo.repo})\n\n` +
+ `### 📈 12-Month Activity Trends\n\`\`\`\nCommits:\n${commitsChart}\n\nLines Added:\n${linesChart}\n\`\`\`\n\n` +
+ `### 📊 Professional Charts Generated\n` +
+ `📈 [Commits Chart](${chartUrls.commitsUrl})\n` +
+ `📊 [Lines Chart](${chartUrls.linesUrl})\n` +
+ `🔀 [PRs Chart](${chartUrls.prsUrl})\n\n` +
+ `*Add these to your documentation:*\n` +
+ `\`\``
+ }
+ ]
};
const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
@@ -238,7 +333,7 @@ jobs:
throw new Error(`Mattermost webhook failed: ${response.status} ${response.statusText}`);
}
- console.log(`Monthly report posted to Mattermost successfully`);
+ console.log('Monthly report with charts posted to Mattermost successfully');
} catch (error) {
console.error('Error generating monthly report:', error);
From 489ce93205b06ef1d2970c2e65d34fa949c7b24c Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:24:13 +0200
Subject: [PATCH 013/125] fix: update chart file paths in statistics report
---
.github/workflows/gh_statistics_bot.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index c15fa3837b..9bb6909981 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -209,9 +209,9 @@ jobs:
const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
const files = [
- { path: 'docs/assets/commits-chart.svg', content: commitsSvg },
- { path: 'docs/assets/lines-chart.svg', content: linesSvg },
- { path: 'docs/assets/prs-chart.svg', content: prsSvg }
+ { path: 'docs/commits-chart.svg', content: commitsSvg },
+ { path: 'docs/lines-chart.svg', content: linesSvg },
+ { path: 'docs/prs-chart.svg', content: prsSvg }
];
// Create or update each chart file
From fa093483387f9ec600b74ec0e947a945b3b4734d Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:26:18 +0200
Subject: [PATCH 014/125] use branch
---
.github/workflows/gh_statistics_bot.yml | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 9bb6909981..31cc711727 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -209,9 +209,9 @@ jobs:
const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
const files = [
- { path: 'docs/commits-chart.svg', content: commitsSvg },
- { path: 'docs/lines-chart.svg', content: linesSvg },
- { path: 'docs/prs-chart.svg', content: prsSvg }
+ { path: 'docs/assets/commits-chart.svg', content: commitsSvg },
+ { path: 'docs/assets/lines-chart.svg', content: linesSvg },
+ { path: 'docs/assets/prs-chart.svg', content: prsSvg }
];
// Create or update each chart file
@@ -268,8 +268,14 @@ jobs:
}
// Get current branch for testing
- const currentBranch = context.ref.replace('refs/heads/', '') || 'main';
- console.log(`Using branch: ${currentBranch}`);
+ let currentBranch;
+ if (context.eventName === 'pull_request') {
+ currentBranch = context.payload.pull_request.head.ref;
+ console.log(`Using PR branch: ${currentBranch}`);
+ } else {
+ currentBranch = context.ref.replace('refs/heads/', '') || 'main';
+ console.log(`Using branch: ${currentBranch}`);
+ }
// Create and commit SVG charts
console.log('Generating and committing SVG charts...');
From a82f48668b6692ae7e062bfd25f9fa4337ee6340 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 10:27:42 +0000
Subject: [PATCH 015/125] Update repository activity chart:
docs/assets/commits-chart.svg
---
docs/assets/commits-chart.svg | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 docs/assets/commits-chart.svg
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
new file mode 100644
index 0000000000..074dad56a7
--- /dev/null
+++ b/docs/assets/commits-chart.svg
@@ -0,0 +1,30 @@
+
\ No newline at end of file
From 4e5984398a60e7bd8923f7df5f4fbf89f983c2a5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 10:27:43 +0000
Subject: [PATCH 016/125] Update repository activity chart:
docs/assets/lines-chart.svg
---
docs/assets/lines-chart.svg | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 docs/assets/lines-chart.svg
diff --git a/docs/assets/lines-chart.svg b/docs/assets/lines-chart.svg
new file mode 100644
index 0000000000..ea41534a7e
--- /dev/null
+++ b/docs/assets/lines-chart.svg
@@ -0,0 +1,30 @@
+
\ No newline at end of file
From 26d48d089ac199e67c142f1fabff13347497c73f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 10:27:44 +0000
Subject: [PATCH 017/125] Update repository activity chart:
docs/assets/prs-chart.svg
---
docs/assets/prs-chart.svg | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 docs/assets/prs-chart.svg
diff --git a/docs/assets/prs-chart.svg b/docs/assets/prs-chart.svg
new file mode 100644
index 0000000000..218b6e1d4c
--- /dev/null
+++ b/docs/assets/prs-chart.svg
@@ -0,0 +1,30 @@
+
\ No newline at end of file
From 52809a87159fe7146ac1c1f91d5e2e8b5b8b6fc2 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:33:03 +0200
Subject: [PATCH 018/125] test
---
.github/workflows/gh_statistics_bot.yml | 140 +++++++++++-------------
1 file changed, 64 insertions(+), 76 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 31cc711727..004b991a5c 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -37,25 +37,26 @@ jobs:
script: |
const asciichart = require('asciichart');
- // Get date ranges for last 12 months
- function getLast12MonthsRanges() {
- const ranges = [];
+ // Get commit statistics for current month only
+ async function getCurrentMonthStats() {
const now = new Date();
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
- for (let i = 11; i >= 0; i--) {
- const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
- const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
-
- ranges.push({
- start: monthStart.toISOString(),
- end: monthEnd.toISOString(),
- monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' }),
- month: monthStart.getMonth(),
- year: monthStart.getFullYear()
- });
- }
-
- return ranges;
+ const since = monthStart.toISOString();
+ const until = monthEnd.toISOString();
+ const monthName = monthStart.toLocaleString('default', { month: 'long', year: 'numeric' });
+
+ const [commitStats, prStats] = await Promise.all([
+ getMonthCommitStats(since, until),
+ getMonthPRStats(since, until)
+ ]);
+
+ return {
+ monthName,
+ stats: commitStats,
+ prStats
+ };
}
// Get commit statistics for a specific month
@@ -243,14 +244,38 @@ jobs:
// Main execution
try {
- console.log('Gathering 12 months of repository data...');
+ console.log('Gathering current month repository data...');
- const monthRanges = getLast12MonthsRanges();
- const monthlyData = [];
+ // Get current branch for testing
+ let currentBranch;
+ if (context.eventName === 'pull_request') {
+ currentBranch = context.payload.pull_request.head.ref;
+ console.log(`Using PR branch: ${currentBranch}`);
+ } else {
+ currentBranch = context.ref.replace('refs/heads/', '') || 'main';
+ console.log(`Using branch: ${currentBranch}`);
+ }
+
+ // Gather data for last 12 months (for charts only)
+ const monthRanges = [];
+ const now = new Date();
+
+ for (let i = 11; i >= 0; i--) {
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+
+ monthRanges.push({
+ start: monthStart.toISOString(),
+ end: monthEnd.toISOString(),
+ monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
+ });
+ }
- // Gather data for each month (with rate limiting)
+ const monthlyData = [];
+
+ // Gather data for charts (simplified)
for (const range of monthRanges) {
- console.log(`Processing ${range.monthName}...`);
+ console.log(`Processing ${range.monthName} for charts...`);
const [stats, prStats] = await Promise.all([
getMonthCommitStats(range.start, range.end),
@@ -263,68 +288,31 @@ jobs:
prStats
});
- // Small delay to avoid rate limiting
- await new Promise(resolve => setTimeout(resolve, 1000));
- }
-
- // Get current branch for testing
- let currentBranch;
- if (context.eventName === 'pull_request') {
- currentBranch = context.payload.pull_request.head.ref;
- console.log(`Using PR branch: ${currentBranch}`);
- } else {
- currentBranch = context.ref.replace('refs/heads/', '') || 'main';
- console.log(`Using branch: ${currentBranch}`);
+ await new Promise(resolve => setTimeout(resolve, 500));
}
// Create and commit SVG charts
console.log('Generating and committing SVG charts...');
const chartUrls = await commitChartsToRepo(monthlyData, currentBranch);
- // Create ASCII charts for Mattermost
- const commits = monthlyData.map(d => d.stats.totalCommits);
- const linesAdded = monthlyData.map(d => d.stats.linesAdded);
-
- const commitsChart = asciichart.plot(commits, {
- height: 8,
- colors: [asciichart.blue]
- });
-
- const linesChart = asciichart.plot(linesAdded, {
- height: 8,
- colors: [asciichart.green]
- });
-
- // Get current month stats for summary
- const currentMonth = monthlyData[monthlyData.length - 1];
- const stats = currentMonth.stats;
- const prStats = currentMonth.prStats;
+ // Get current month stats for Mattermost post
+ const currentMonthData = await getCurrentMonthStats();
- // Post to Mattermost with ASCII charts and SVG links
+ // Post clean summary to Mattermost
const mattermostPayload = {
- text: `## 📊 Monthly Repository Report - ${currentMonth.month}`,
- attachments: [
- {
- color: '#36a64f',
- fields: [
- { title: '📝 Total Commits', value: stats.totalCommits.toString(), short: true },
- { title: '📁 Files Changed', value: stats.filesChanged.toString(), short: true },
- { title: '🔀 PRs Merged', value: prStats.merged.toString(), short: true },
- { title: '🆕 PRs Opened', value: prStats.opened.toString(), short: true },
- { title: '👥 Active Contributors', value: stats.uniqueContributors.toString(), short: true },
- { title: '📈 Net Lines Changed', value: `${stats.netLines >= 0 ? '+' : ''}${stats.netLines.toLocaleString()}`, short: true }
- ],
- text: `**Code Changes:** +${stats.linesAdded.toLocaleString()} / -${stats.linesRemoved.toLocaleString()} lines\n` +
- `**Repository:** [${context.repo.owner}/${context.repo.repo}](https://github.com/${context.repo.owner}/${context.repo.repo})\n\n` +
- `### 📈 12-Month Activity Trends\n\`\`\`\nCommits:\n${commitsChart}\n\nLines Added:\n${linesChart}\n\`\`\`\n\n` +
- `### 📊 Professional Charts Generated\n` +
- `📈 [Commits Chart](${chartUrls.commitsUrl})\n` +
- `📊 [Lines Chart](${chartUrls.linesUrl})\n` +
- `🔀 [PRs Chart](${chartUrls.prsUrl})\n\n` +
- `*Add these to your documentation:*\n` +
- `\`\``
- }
- ]
+ text: `## 📊 ${currentMonthData.monthName} Repository Activity`,
+ attachments: [{
+ color: '#22c55e',
+ fields: [
+ { title: '📝 Commits', value: currentMonthData.stats.totalCommits.toString(), short: true },
+ { title: '📁 Files Changed', value: currentMonthData.stats.filesChanged.toString(), short: true },
+ { title: '🔀 PRs Merged', value: currentMonthData.prStats.merged.toString(), short: true },
+ { title: '🆕 PRs Opened', value: currentMonthData.prStats.opened.toString(), short: true },
+ { title: '👥 Contributors', value: currentMonthData.stats.uniqueContributors.toString(), short: true },
+ { title: '📈 Lines Added', value: `+${currentMonthData.stats.linesAdded.toLocaleString()}`, short: true }
+ ],
+ footer: `Repository: ${context.repo.owner}/${context.repo.repo} • Charts updated in docs/assets/`
+ }]
};
const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
From 67e96b566220c8c6c37f0d40e34a22efdecd3c24 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:42:04 +0200
Subject: [PATCH 019/125] update post
---
.github/workflows/gh_statistics_bot.yml | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 004b991a5c..ca5a9b2e95 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -298,9 +298,12 @@ jobs:
// Get current month stats for Mattermost post
const currentMonthData = await getCurrentMonthStats();
+ // Get current month stats for Mattermost post
+ const currentMonthData = await getCurrentMonthStats();
+
// Post clean summary to Mattermost
const mattermostPayload = {
- text: `## 📊 ${currentMonthData.monthName} Repository Activity`,
+ text: `📊 **${currentMonthData.monthName} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
@@ -309,9 +312,10 @@ jobs:
{ title: '🔀 PRs Merged', value: currentMonthData.prStats.merged.toString(), short: true },
{ title: '🆕 PRs Opened', value: currentMonthData.prStats.opened.toString(), short: true },
{ title: '👥 Contributors', value: currentMonthData.stats.uniqueContributors.toString(), short: true },
- { title: '📈 Lines Added', value: `+${currentMonthData.stats.linesAdded.toLocaleString()}`, short: true }
+ { title: '📈 Lines Added', value: currentMonthData.stats.linesAdded.toLocaleString(), short: true }
],
- footer: `Repository: ${context.repo.owner}/${context.repo.repo} • Charts updated in docs/assets/`
+ text: `Repository: ${context.repo.owner}/${context.repo.repo}`,
+ footer: 'Charts updated in docs/assets/ for documentation'
}]
};
From be220a9f5773e8378c7b12731a08bdcf8f453ad8 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:55:19 +0200
Subject: [PATCH 020/125] update
---
.github/workflows/gh_statistics_bot.yml | 20 --------------------
1 file changed, 20 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index ca5a9b2e95..e15f528327 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -37,27 +37,7 @@ jobs:
script: |
const asciichart = require('asciichart');
- // Get commit statistics for current month only
- async function getCurrentMonthStats() {
- const now = new Date();
- const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
- const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
-
- const since = monthStart.toISOString();
- const until = monthEnd.toISOString();
- const monthName = monthStart.toLocaleString('default', { month: 'long', year: 'numeric' });
-
- const [commitStats, prStats] = await Promise.all([
- getMonthCommitStats(since, until),
- getMonthPRStats(since, until)
- ]);
- return {
- monthName,
- stats: commitStats,
- prStats
- };
- }
// Get commit statistics for a specific month
async function getMonthCommitStats(since, until) {
From 775ae259de16c17b0a26ffaecfc10f5ead7f53f0 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:59:32 +0200
Subject: [PATCH 021/125] test
---
.github/workflows/gh_statistics_bot.yml | 96 ++++++++++---------------
1 file changed, 36 insertions(+), 60 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index e15f528327..70c303262b 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -11,7 +11,7 @@ jobs:
generate-monthly-report:
runs-on: ubuntu-latest
permissions:
- contents: write # Changed to write for committing charts
+ contents: write
issues: write
pull-requests: read
@@ -37,8 +37,6 @@ jobs:
script: |
const asciichart = require('asciichart');
-
-
// Get commit statistics for a specific month
async function getMonthCommitStats(since, until) {
const commits = await github.paginate(github.rest.repos.listCommits, {
@@ -54,7 +52,7 @@ jobs:
let totalAdditions = 0;
let totalDeletions = 0;
- for (const commit of commits.slice(0, 50)) { // Limit to avoid rate limits
+ for (const commit of commits.slice(0, 50)) {
if (commit.author) {
contributors.add(commit.author.login);
}
@@ -129,14 +127,12 @@ jobs:
const minValue = Math.min(...data);
const range = maxValue - minValue || 1;
- // Create SVG path for the line
const points = data.map((value, index) => {
const x = padding + (index / (data.length - 1)) * chartWidth;
const y = padding + chartHeight - ((value - minValue) / range) * chartHeight;
return `${x},${y}`;
}).join(' L');
- // Create area fill path
const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
return ``;
}
// Commit charts to repository
- async function commitChartsToRepo(monthlyData, currentBranch) {
+ async function commitChartsToRepo(monthlyData, targetBranch) {
const commits = monthlyData.map(d => d.stats.totalCommits);
const linesAdded = monthlyData.map(d => d.stats.linesAdded);
const prsOpened = monthlyData.map(d => d.prStats.opened);
- const prsMerged = monthlyData.map(d => d.prStats.merged);
- // Create SVG charts
const commitsSvg = createSVGChart(commits, 'Monthly Commits', '#3b82f6');
const linesSvg = createSVGChart(linesAdded, 'Lines Added per Month', '#22c55e');
const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
- const files = [
+ const chartFiles = [
{ path: 'docs/assets/commits-chart.svg', content: commitsSvg },
{ path: 'docs/assets/lines-chart.svg', content: linesSvg },
{ path: 'docs/assets/prs-chart.svg', content: prsSvg }
];
- // Create or update each chart file
- for (const file of files) {
+ for (const file of chartFiles) {
try {
const base64Content = Buffer.from(file.content).toString('base64');
@@ -206,7 +190,7 @@ jobs:
path: file.path,
message: `Update repository activity chart: ${file.path}`,
content: base64Content,
- branch: currentBranch
+ branch: targetBranch
});
console.log(`Updated chart: ${file.path}`);
@@ -216,54 +200,49 @@ jobs:
}
return {
- commitsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/commits-chart.svg`,
- linesUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/lines-chart.svg`,
- prsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${currentBranch}/docs/assets/prs-chart.svg`
+ commitsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/commits-chart.svg`,
+ linesUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/lines-chart.svg`,
+ prsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/prs-chart.svg`
};
}
// Main execution
try {
- console.log('Gathering current month repository data...');
+ console.log('Gathering repository data for last 12 months...');
- // Get current branch for testing
- let currentBranch;
+ // Get current branch
+ let targetBranch;
if (context.eventName === 'pull_request') {
- currentBranch = context.payload.pull_request.head.ref;
- console.log(`Using PR branch: ${currentBranch}`);
+ targetBranch = context.payload.pull_request.head.ref;
+ console.log(`Using PR branch: ${targetBranch}`);
} else {
- currentBranch = context.ref.replace('refs/heads/', '') || 'main';
- console.log(`Using branch: ${currentBranch}`);
+ targetBranch = context.ref.replace('refs/heads/', '') || 'main';
+ console.log(`Using branch: ${targetBranch}`);
}
- // Gather data for last 12 months (for charts only)
- const monthRanges = [];
+ // Gather data for last 12 months
+ const monthlyDataArray = [];
const now = new Date();
for (let i = 11; i >= 0; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
- monthRanges.push({
+ const monthRange = {
start: monthStart.toISOString(),
end: monthEnd.toISOString(),
monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
- });
- }
+ };
- const monthlyData = [];
-
- // Gather data for charts (simplified)
- for (const range of monthRanges) {
- console.log(`Processing ${range.monthName} for charts...`);
+ console.log(`Processing ${monthRange.monthName}...`);
const [stats, prStats] = await Promise.all([
- getMonthCommitStats(range.start, range.end),
- getMonthPRStats(range.start, range.end)
+ getMonthCommitStats(monthRange.start, monthRange.end),
+ getMonthPRStats(monthRange.start, monthRange.end)
]);
- monthlyData.push({
- month: range.monthName,
+ monthlyDataArray.push({
+ month: monthRange.monthName,
stats,
prStats
});
@@ -273,26 +252,23 @@ jobs:
// Create and commit SVG charts
console.log('Generating and committing SVG charts...');
- const chartUrls = await commitChartsToRepo(monthlyData, currentBranch);
-
- // Get current month stats for Mattermost post
- const currentMonthData = await getCurrentMonthStats();
+ await commitChartsToRepo(monthlyDataArray, targetBranch);
- // Get current month stats for Mattermost post
- const currentMonthData = await getCurrentMonthStats();
+ // Use latest month for Mattermost
+ const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
- // Post clean summary to Mattermost
+ // Post to Mattermost
const mattermostPayload = {
- text: `📊 **${currentMonthData.monthName} Repository Activity**`,
+ text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits', value: currentMonthData.stats.totalCommits.toString(), short: true },
- { title: '📁 Files Changed', value: currentMonthData.stats.filesChanged.toString(), short: true },
- { title: '🔀 PRs Merged', value: currentMonthData.prStats.merged.toString(), short: true },
- { title: '🆕 PRs Opened', value: currentMonthData.prStats.opened.toString(), short: true },
- { title: '👥 Contributors', value: currentMonthData.stats.uniqueContributors.toString(), short: true },
- { title: '📈 Lines Added', value: currentMonthData.stats.linesAdded.toLocaleString(), short: true }
+ { title: '📝 Commits', value: latestMonthData.stats.totalCommits.toString(), short: true },
+ { title: '📁 Files Changed', value: latestMonthData.stats.filesChanged.toString(), short: true },
+ { title: '🔀 PRs Merged', value: latestMonthData.prStats.merged.toString(), short: true },
+ { title: '🆕 PRs Opened', value: latestMonthData.prStats.opened.toString(), short: true },
+ { title: '👥 Contributors', value: latestMonthData.stats.uniqueContributors.toString(), short: true },
+ { title: '📈 Lines Added', value: latestMonthData.stats.linesAdded.toLocaleString(), short: true }
],
text: `Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Charts updated in docs/assets/ for documentation'
From 1373c9f4c083a55ae71d2e8620a800c8690520c4 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:03:46 +0200
Subject: [PATCH 022/125] bug fix
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 70c303262b..7997a53ea2 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -225,8 +225,8 @@ jobs:
const now = new Date();
for (let i = 11; i >= 0; i--) {
- const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
- const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i-1, 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() - i, 0);
const monthRange = {
start: monthStart.toISOString(),
From e7c2b48aa8efe5928800349486ad91dde1b0fbda Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:20:33 +0200
Subject: [PATCH 023/125] fix formatting in date calculation for monthly
statistics
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 7997a53ea2..0d54c233d2 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -225,7 +225,7 @@ jobs:
const now = new Date();
for (let i = 11; i >= 0; i--) {
- const monthStart = new Date(now.getFullYear(), now.getMonth() - i-1, 1);
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i - 1, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i, 0);
const monthRange = {
From addd35dd005bf9707ea987cdbe0e8f51069d6dca Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:28:08 +0200
Subject: [PATCH 024/125] is this good?
---
.github/workflows/gh_statistics_bot.yml | 40 +++++++++++++++++++++----
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 0d54c233d2..beef3899c2 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -37,7 +37,27 @@ jobs:
script: |
const asciichart = require('asciichart');
- // Get commit statistics for a specific month
+ // Get language statistics
+ async function getLanguageStats() {
+ try {
+ const languages = await github.rest.repos.listLanguages({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
+ return Object.entries(languages.data)
+ .map(([lang, bytes]) => ({
+ language: lang,
+ percentage: ((bytes / total) * 100).toFixed(1)
+ }))
+ .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
+ .slice(0, 3);
+ } catch (error) {
+ console.log('Error getting language stats:', error.message);
+ return [];
+ }
+ }
async function getMonthCommitStats(since, until) {
const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
@@ -80,6 +100,7 @@ jobs:
return {
totalCommits: commits.length,
uniqueContributors: contributors.size,
+ contributorsList: Array.from(contributors),
filesChanged: filesChanged.size,
linesAdded: totalAdditions,
linesRemoved: totalDeletions,
@@ -254,6 +275,9 @@ jobs:
console.log('Generating and committing SVG charts...');
await commitChartsToRepo(monthlyDataArray, targetBranch);
+ // Get language stats for current month
+ const languageStats = await getLanguageStats();
+
// Use latest month for Mattermost
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
@@ -263,14 +287,18 @@ jobs:
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits', value: latestMonthData.stats.totalCommits.toString(), short: true },
- { title: '📁 Files Changed', value: latestMonthData.stats.filesChanged.toString(), short: true },
+ { title: '📝 Commits to Main', value: latestMonthData.stats.totalCommitsMain.toString(), short: true },
+ { title: '🌿 All Branch Commits', value: latestMonthData.stats.totalCommitsAllBranches.toString(), short: true },
+ { title: '📁 Files Changed', value: latestMonthData.stats.filesChangedMain.toString(), short: true },
{ title: '🔀 PRs Merged', value: latestMonthData.prStats.merged.toString(), short: true },
{ title: '🆕 PRs Opened', value: latestMonthData.prStats.opened.toString(), short: true },
- { title: '👥 Contributors', value: latestMonthData.stats.uniqueContributors.toString(), short: true },
- { title: '📈 Lines Added', value: latestMonthData.stats.linesAdded.toLocaleString(), short: true }
+ { title: '👥 Authors', value: latestMonthData.stats.totalAuthors.toString(), short: true }
],
- text: `Repository: ${context.repo.owner}/${context.repo.repo}`,
+ text: `**Code Changes:** +${latestMonthData.stats.linesAdded.toLocaleString()} / -${latestMonthData.stats.linesRemoved.toLocaleString()} lines\n` +
+ `**Top Contributors:** ${latestMonthData.stats.authorsList.slice(0, 3).join(', ')}\n` +
+ `**Languages:** ${languageStats.map(l => `${l.language} (${l.percentage}%)`).join(', ')}\n\n` +
+ `*Excluding merges, ${latestMonthData.stats.totalAuthors} authors pushed ${latestMonthData.stats.totalCommitsMain} commits to main and ${latestMonthData.stats.totalCommitsAllBranches} commits to all branches*\n\n` +
+ `Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Charts updated in docs/assets/ for documentation'
}]
};
From cb79d30ca07688723bffff0c7db2e434c78c6680 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:33:35 +0200
Subject: [PATCH 025/125] update
---
.github/workflows/gh_statistics_bot.yml | 59 ++++++++++++++++---------
1 file changed, 37 insertions(+), 22 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index beef3899c2..a6efeba3a2 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -187,9 +187,9 @@ jobs:
// Commit charts to repository
async function commitChartsToRepo(monthlyData, targetBranch) {
- const commits = monthlyData.map(d => d.stats.totalCommits);
- const linesAdded = monthlyData.map(d => d.stats.linesAdded);
- const prsOpened = monthlyData.map(d => d.prStats.opened);
+ const commits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || 0);
+ const linesAdded = monthlyData.map(d => d.stats?.linesAdded || 0);
+ const prsOpened = monthlyData.map(d => d.prStats?.opened || 0);
const commitsSvg = createSVGChart(commits, 'Monthly Commits', '#3b82f6');
const linesSvg = createSVGChart(linesAdded, 'Lines Added per Month', '#22c55e');
@@ -205,26 +205,41 @@ jobs:
try {
const base64Content = Buffer.from(file.content).toString('base64');
- await github.rest.repos.createOrUpdateFileContents({
+ // Try to get existing file SHA
+ let existingFileSha = null;
+ try {
+ const existingFile = await github.rest.repos.getContent({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: file.path,
+ ref: targetBranch
+ });
+ existingFileSha = existingFile.data.sha;
+ } catch (error) {
+ // File doesn't exist, that's okay
+ console.log(`File ${file.path} doesn't exist yet, creating new file`);
+ }
+
+ const params = {
owner: context.repo.owner,
repo: context.repo.repo,
path: file.path,
message: `Update repository activity chart: ${file.path}`,
content: base64Content,
branch: targetBranch
- });
+ };
+ // Add SHA if file exists
+ if (existingFileSha) {
+ params.sha = existingFileSha;
+ }
+
+ await github.rest.repos.createOrUpdateFileContents(params);
console.log(`Updated chart: ${file.path}`);
} catch (error) {
console.log(`Error updating ${file.path}:`, error.message);
}
}
-
- return {
- commitsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/commits-chart.svg`,
- linesUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/lines-chart.svg`,
- prsUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${targetBranch}/docs/assets/prs-chart.svg`
- };
}
// Main execution
@@ -281,23 +296,23 @@ jobs:
// Use latest month for Mattermost
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
- // Post to Mattermost
+ // Post to Mattermost with safe property access
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits to Main', value: latestMonthData.stats.totalCommitsMain.toString(), short: true },
- { title: '🌿 All Branch Commits', value: latestMonthData.stats.totalCommitsAllBranches.toString(), short: true },
- { title: '📁 Files Changed', value: latestMonthData.stats.filesChangedMain.toString(), short: true },
- { title: '🔀 PRs Merged', value: latestMonthData.prStats.merged.toString(), short: true },
- { title: '🆕 PRs Opened', value: latestMonthData.prStats.opened.toString(), short: true },
- { title: '👥 Authors', value: latestMonthData.stats.totalAuthors.toString(), short: true }
+ { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
+ { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
+ { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
+ { title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
+ { title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
+ { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
],
- text: `**Code Changes:** +${latestMonthData.stats.linesAdded.toLocaleString()} / -${latestMonthData.stats.linesRemoved.toLocaleString()} lines\n` +
- `**Top Contributors:** ${latestMonthData.stats.authorsList.slice(0, 3).join(', ')}\n` +
- `**Languages:** ${languageStats.map(l => `${l.language} (${l.percentage}%)`).join(', ')}\n\n` +
- `*Excluding merges, ${latestMonthData.stats.totalAuthors} authors pushed ${latestMonthData.stats.totalCommitsMain} commits to main and ${latestMonthData.stats.totalCommitsAllBranches} commits to all branches*\n\n` +
+ text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
+ `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
+ `**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
+ `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Charts updated in docs/assets/ for documentation'
}]
From a549f09e66e4f8afa1737adf0fea5bc3742e9efc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:34:48 +0000
Subject: [PATCH 026/125] Update repository activity chart:
docs/assets/commits-chart.svg
---
docs/assets/commits-chart.svg | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
index 074dad56a7..49f515fa0c 100644
--- a/docs/assets/commits-chart.svg
+++ b/docs/assets/commits-chart.svg
@@ -6,25 +6,16 @@
-
-
-
-
+
+
-
-
+
-
-
-
-
Monthly Commits
-
-
- 12
+ 0
0
\ No newline at end of file
From 0ab37079aa2440318c7f7bdf590401570397d240 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:34:49 +0000
Subject: [PATCH 027/125] Update repository activity chart:
docs/assets/lines-chart.svg
---
docs/assets/lines-chart.svg | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/docs/assets/lines-chart.svg b/docs/assets/lines-chart.svg
index ea41534a7e..04394c8046 100644
--- a/docs/assets/lines-chart.svg
+++ b/docs/assets/lines-chart.svg
@@ -6,25 +6,16 @@
-
-
-
-
+
+
-
-
+
-
-
-
-
Lines Added per Month
-
-
26179
- 0
+ 210
\ No newline at end of file
From 2f58be40783c0c0ae23c6df094e228258b8fb2ac Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:34:50 +0000
Subject: [PATCH 028/125] Update repository activity chart:
docs/assets/prs-chart.svg
---
docs/assets/prs-chart.svg | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/docs/assets/prs-chart.svg b/docs/assets/prs-chart.svg
index 218b6e1d4c..7c59bf153f 100644
--- a/docs/assets/prs-chart.svg
+++ b/docs/assets/prs-chart.svg
@@ -6,25 +6,16 @@
-
-
-
-
+
+
-
-
+
-
-
-
-
Pull Requests Opened
-
-
15
- 1
+ 2
\ No newline at end of file
From 2c90bef473262176dc11a4f61306da74dda50778 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:36:57 +0200
Subject: [PATCH 029/125] new month
---
.github/workflows/gh_statistics_bot.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index a6efeba3a2..af0bf0e5dc 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -260,9 +260,9 @@ jobs:
const monthlyDataArray = [];
const now = new Date();
- for (let i = 11; i >= 0; i--) {
- const monthStart = new Date(now.getFullYear(), now.getMonth() - i - 1, 1);
- const monthEnd = new Date(now.getFullYear(), now.getMonth() - i, 0);
+ for (let i = 12; i >= 1; i--) {
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
const monthRange = {
start: monthStart.toISOString(),
From 0df157165a17eb5cd39405d2d8a00e4add9680ae Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:38:00 +0000
Subject: [PATCH 030/125] Update repository activity chart:
docs/assets/commits-chart.svg
From 02e6c28ed2834d198dfe52478b18e971bf61d0f0 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:38:01 +0000
Subject: [PATCH 031/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 56e403354fdde1e112665d6971f4bc1f0ebfb31d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:38:02 +0000
Subject: [PATCH 032/125] Update repository activity chart:
docs/assets/prs-chart.svg
From e70f4f3872b51333451386c8c3b13db2d62e3ef7 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:47:46 +0200
Subject: [PATCH 033/125] check logs
---
.github/workflows/gh_statistics_bot.yml | 337 ++++++++++++++++++++++++
1 file changed, 337 insertions(+)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index af0bf0e5dc..1f1d78b68a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -242,6 +242,343 @@ jobs:
}
}
+ // Main execution
+ try {
+ console.log('Gathering repository data for last 12 months...');
+
+ // Get current branch
+ let targetBranch;
+ if (context.eventName === 'pull_request') {
+ targetBranch = context.payload.pull_request.head.ref;
+ console.log(`Using PR branch: ${targetBranch}`);
+ } else {
+ targetBranch = context.ref.replace('refs/heads/', '') || 'main';
+ console.log(`Using branch: ${targetBranch}`);
+ }
+
+ // Gather data for last 12 months
+ const monthlyDataArray = [];
+ const now = new Date();
+
+ for (let i = 12; i >= 1; i--) {
+ const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+
+ const monthRange = {
+ start: monthStart.toISOString(),
+ end: monthEnd.toISOString(),
+ monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
+ };
+
+ console.log(`Processing ${monthRange.monthName}...`);
+
+ const [stats, prStats] = await Promise.all([
+ getMonthCommitStats(monthRange.start, monthRange.end),
+ getMonthPRStats(monthRange.start, monthRange.end)
+ ]);
+
+ monthlyDataArray.push({
+ month: monthRange.monthName,
+ stats,
+ prStats
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+
+ // Create and commit SVG charts
+ console.log('Generating and committing SVG charts...');
+ await commitChartsToRepo(monthlyDataArray, targetBranch);
+
+ // Get language stats for current month
+ const languageStats = await getLanguageStats();
+
+ // Use latest month for Mattermost
+ const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
+
+ // Post to Mattermost with safe property access
+ const mattermostPayload = {
+ text: `📊 **${latestMonthData.month} Repository Activity**`,
+ attachments: [{
+ color: '#22c55e',
+ fields: [
+ { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
+ { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
+ { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
+ { title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
+ { title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
+ { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
+ ],
+ text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
+ `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
+ `**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
+ `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
+ `Repository: ${context.repo.owner}/${context.repo.repo}`,
+ footer: 'Charts updated in docs/assets/ for documentation'
+ }]
+ };
+
+ const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(mattermostPayload)
+ });
+
+ if (!response.ok) {
+ throw new Error(`Mattermost webhook failed: ${response.status} ${response.statusText}`);
+ }
+
+ console.log('Monthly report with charts posted to Mattermost successfully');
+
+ } catch (error) {
+ console.error('Error generating monthly report:', error);
+ core.setFailed(`Monthly report generation failed: ${error.message}`);
+ }aname: Monthly Repository Statistics with Charts
+
+on:
+ pull_request:
+ types: [opened, synchronize]
+ schedule:
+ - cron: '0 9 1 * *'
+ workflow_dispatch:
+
+jobs:
+ generate-monthly-report:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+
+ - name: Install dependencies
+ run: |
+ npm init -y
+ npm install asciichart
+
+ - name: Generate Monthly Statistics Report with Charts
+ uses: actions/github-script@v7
+ env:
+ MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
+ with:
+ script: |
+ const asciichart = require('asciichart');
+
+ // Get language statistics
+ async function getLanguageStats() {
+ try {
+ const languages = await github.rest.repos.listLanguages({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
+ return Object.entries(languages.data)
+ .map(([lang, bytes]) => ({
+ language: lang,
+ percentage: ((bytes / total) * 100).toFixed(1)
+ }))
+ .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
+ .slice(0, 3);
+ } catch (error) {
+ console.log('Error getting language stats:', error.message);
+ return [];
+ }
+ }
+ async function getMonthCommitStats(since, until) {
+ const commits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ const contributors = new Set();
+ const filesChanged = new Set();
+ let totalAdditions = 0;
+ let totalDeletions = 0;
+
+ for (const commit of commits.slice(0, 50)) {
+ if (commit.author) {
+ contributors.add(commit.author.login);
+ }
+
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ if (commitDetail.data.files) {
+ commitDetail.data.files.forEach(file => {
+ filesChanged.add(file.filename);
+ });
+ }
+
+ totalAdditions += commitDetail.data.stats?.additions || 0;
+ totalDeletions += commitDetail.data.stats?.deletions || 0;
+ } catch (error) {
+ console.log(`Error getting commit details: ${error.message}`);
+ }
+ }
+
+ return {
+ totalCommits: commits.length,
+ uniqueContributors: contributors.size,
+ contributorsList: Array.from(contributors),
+ filesChanged: filesChanged.size,
+ linesAdded: totalAdditions,
+ linesRemoved: totalDeletions,
+ netLines: totalAdditions - totalDeletions,
+ };
+ }
+
+ // Get PR statistics for a specific month
+ async function getMonthPRStats(since, until) {
+ const allPRs = await github.paginate(github.rest.pulls.list, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'all',
+ sort: 'updated',
+ direction: 'desc',
+ per_page: 100,
+ });
+
+ const mergedPRs = allPRs.filter(pr =>
+ pr.merged_at &&
+ new Date(pr.merged_at) >= new Date(since) &&
+ new Date(pr.merged_at) <= new Date(until)
+ );
+
+ const openedPRs = allPRs.filter(pr =>
+ new Date(pr.created_at) >= new Date(since) &&
+ new Date(pr.created_at) <= new Date(until)
+ );
+
+ return {
+ merged: mergedPRs.length,
+ opened: openedPRs.length,
+ };
+ }
+
+ // Create professional SVG charts
+ function createSVGChart(data, title, color = '#22c55e') {
+ const width = 600;
+ const height = 200;
+ const padding = 40;
+ const chartWidth = width - 2 * padding;
+ const chartHeight = height - 2 * padding;
+
+ const maxValue = Math.max(...data);
+ const minValue = Math.min(...data);
+ const range = maxValue - minValue || 1;
+
+ const points = data.map((value, index) => {
+ const x = padding + (index / (data.length - 1)) * chartWidth;
+ const y = padding + chartHeight - ((value - minValue) / range) * chartHeight;
+ return `${x},${y}`;
+ }).join(' L');
+
+ const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
+
+ return ``;
+ }
+
+ // Commit charts to repository
+ async function commitChartsToRepo(monthlyData, targetBranch) {
+ const commits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || 0);
+ const linesAdded = monthlyData.map(d => d.stats?.linesAdded || 0);
+ const prsOpened = monthlyData.map(d => d.prStats?.opened || 0);
+
+ const commitsSvg = createSVGChart(commits, 'Monthly Commits', '#3b82f6');
+ const linesSvg = createSVGChart(linesAdded, 'Lines Added per Month', '#22c55e');
+ const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
+
+ const chartFiles = [
+ { path: 'docs/assets/commits-chart.svg', content: commitsSvg },
+ { path: 'docs/assets/lines-chart.svg', content: linesSvg },
+ { path: 'docs/assets/prs-chart.svg', content: prsSvg }
+ ];
+
+ for (const file of chartFiles) {
+ try {
+ const base64Content = Buffer.from(file.content).toString('base64');
+
+ // Try to get existing file SHA
+ let existingFileSha = null;
+ try {
+ const existingFile = await github.rest.repos.getContent({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: file.path,
+ ref: targetBranch
+ });
+ existingFileSha = existingFile.data.sha;
+ } catch (error) {
+ // File doesn't exist, that's okay
+ console.log(`File ${file.path} doesn't exist yet, creating new file`);
+ }
+
+ const params = {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: file.path,
+ message: `Update repository activity chart: ${file.path}`,
+ content: base64Content,
+ branch: targetBranch
+ };
+
+ // Add SHA if file exists
+ if (existingFileSha) {
+ params.sha = existingFileSha;
+ }
+
+ await github.rest.repos.createOrUpdateFileContents(params);
+ console.log(`Updated chart: ${file.path}`);
+ } catch (error) {
+ console.log(`Error updating ${file.path}:`, error.message);
+ }
+ }
+ }
+
// Main execution
try {
console.log('Gathering repository data for last 12 months...');
From a68f7dc3fb8c11a870ee5be6d00d9ecd0b7b2560 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:48:29 +0200
Subject: [PATCH 034/125] ??
---
.github/workflows/gh_statistics_bot.yml | 337 ------------------------
1 file changed, 337 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 1f1d78b68a..af0bf0e5dc 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -242,343 +242,6 @@ jobs:
}
}
- // Main execution
- try {
- console.log('Gathering repository data for last 12 months...');
-
- // Get current branch
- let targetBranch;
- if (context.eventName === 'pull_request') {
- targetBranch = context.payload.pull_request.head.ref;
- console.log(`Using PR branch: ${targetBranch}`);
- } else {
- targetBranch = context.ref.replace('refs/heads/', '') || 'main';
- console.log(`Using branch: ${targetBranch}`);
- }
-
- // Gather data for last 12 months
- const monthlyDataArray = [];
- const now = new Date();
-
- for (let i = 12; i >= 1; i--) {
- const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
- const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
-
- const monthRange = {
- start: monthStart.toISOString(),
- end: monthEnd.toISOString(),
- monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
- };
-
- console.log(`Processing ${monthRange.monthName}...`);
-
- const [stats, prStats] = await Promise.all([
- getMonthCommitStats(monthRange.start, monthRange.end),
- getMonthPRStats(monthRange.start, monthRange.end)
- ]);
-
- monthlyDataArray.push({
- month: monthRange.monthName,
- stats,
- prStats
- });
-
- await new Promise(resolve => setTimeout(resolve, 500));
- }
-
- // Create and commit SVG charts
- console.log('Generating and committing SVG charts...');
- await commitChartsToRepo(monthlyDataArray, targetBranch);
-
- // Get language stats for current month
- const languageStats = await getLanguageStats();
-
- // Use latest month for Mattermost
- const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
-
- // Post to Mattermost with safe property access
- const mattermostPayload = {
- text: `📊 **${latestMonthData.month} Repository Activity**`,
- attachments: [{
- color: '#22c55e',
- fields: [
- { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
- { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
- { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
- { title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
- { title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
- { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
- ],
- text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
- `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
- `**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
- `Repository: ${context.repo.owner}/${context.repo.repo}`,
- footer: 'Charts updated in docs/assets/ for documentation'
- }]
- };
-
- const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(mattermostPayload)
- });
-
- if (!response.ok) {
- throw new Error(`Mattermost webhook failed: ${response.status} ${response.statusText}`);
- }
-
- console.log('Monthly report with charts posted to Mattermost successfully');
-
- } catch (error) {
- console.error('Error generating monthly report:', error);
- core.setFailed(`Monthly report generation failed: ${error.message}`);
- }aname: Monthly Repository Statistics with Charts
-
-on:
- pull_request:
- types: [opened, synchronize]
- schedule:
- - cron: '0 9 1 * *'
- workflow_dispatch:
-
-jobs:
- generate-monthly-report:
- runs-on: ubuntu-latest
- permissions:
- contents: write
- issues: write
- pull-requests: read
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '18'
-
- - name: Install dependencies
- run: |
- npm init -y
- npm install asciichart
-
- - name: Generate Monthly Statistics Report with Charts
- uses: actions/github-script@v7
- env:
- MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
- with:
- script: |
- const asciichart = require('asciichart');
-
- // Get language statistics
- async function getLanguageStats() {
- try {
- const languages = await github.rest.repos.listLanguages({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
- return Object.entries(languages.data)
- .map(([lang, bytes]) => ({
- language: lang,
- percentage: ((bytes / total) * 100).toFixed(1)
- }))
- .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
- .slice(0, 3);
- } catch (error) {
- console.log('Error getting language stats:', error.message);
- return [];
- }
- }
- async function getMonthCommitStats(since, until) {
- const commits = await github.paginate(github.rest.repos.listCommits, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- since,
- until,
- per_page: 100,
- });
-
- const contributors = new Set();
- const filesChanged = new Set();
- let totalAdditions = 0;
- let totalDeletions = 0;
-
- for (const commit of commits.slice(0, 50)) {
- if (commit.author) {
- contributors.add(commit.author.login);
- }
-
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- if (commitDetail.data.files) {
- commitDetail.data.files.forEach(file => {
- filesChanged.add(file.filename);
- });
- }
-
- totalAdditions += commitDetail.data.stats?.additions || 0;
- totalDeletions += commitDetail.data.stats?.deletions || 0;
- } catch (error) {
- console.log(`Error getting commit details: ${error.message}`);
- }
- }
-
- return {
- totalCommits: commits.length,
- uniqueContributors: contributors.size,
- contributorsList: Array.from(contributors),
- filesChanged: filesChanged.size,
- linesAdded: totalAdditions,
- linesRemoved: totalDeletions,
- netLines: totalAdditions - totalDeletions,
- };
- }
-
- // Get PR statistics for a specific month
- async function getMonthPRStats(since, until) {
- const allPRs = await github.paginate(github.rest.pulls.list, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'all',
- sort: 'updated',
- direction: 'desc',
- per_page: 100,
- });
-
- const mergedPRs = allPRs.filter(pr =>
- pr.merged_at &&
- new Date(pr.merged_at) >= new Date(since) &&
- new Date(pr.merged_at) <= new Date(until)
- );
-
- const openedPRs = allPRs.filter(pr =>
- new Date(pr.created_at) >= new Date(since) &&
- new Date(pr.created_at) <= new Date(until)
- );
-
- return {
- merged: mergedPRs.length,
- opened: openedPRs.length,
- };
- }
-
- // Create professional SVG charts
- function createSVGChart(data, title, color = '#22c55e') {
- const width = 600;
- const height = 200;
- const padding = 40;
- const chartWidth = width - 2 * padding;
- const chartHeight = height - 2 * padding;
-
- const maxValue = Math.max(...data);
- const minValue = Math.min(...data);
- const range = maxValue - minValue || 1;
-
- const points = data.map((value, index) => {
- const x = padding + (index / (data.length - 1)) * chartWidth;
- const y = padding + chartHeight - ((value - minValue) / range) * chartHeight;
- return `${x},${y}`;
- }).join(' L');
-
- const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
-
- return ``;
- }
-
- // Commit charts to repository
- async function commitChartsToRepo(monthlyData, targetBranch) {
- const commits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || 0);
- const linesAdded = monthlyData.map(d => d.stats?.linesAdded || 0);
- const prsOpened = monthlyData.map(d => d.prStats?.opened || 0);
-
- const commitsSvg = createSVGChart(commits, 'Monthly Commits', '#3b82f6');
- const linesSvg = createSVGChart(linesAdded, 'Lines Added per Month', '#22c55e');
- const prsSvg = createSVGChart(prsOpened, 'Pull Requests Opened', '#8b5cf6');
-
- const chartFiles = [
- { path: 'docs/assets/commits-chart.svg', content: commitsSvg },
- { path: 'docs/assets/lines-chart.svg', content: linesSvg },
- { path: 'docs/assets/prs-chart.svg', content: prsSvg }
- ];
-
- for (const file of chartFiles) {
- try {
- const base64Content = Buffer.from(file.content).toString('base64');
-
- // Try to get existing file SHA
- let existingFileSha = null;
- try {
- const existingFile = await github.rest.repos.getContent({
- owner: context.repo.owner,
- repo: context.repo.repo,
- path: file.path,
- ref: targetBranch
- });
- existingFileSha = existingFile.data.sha;
- } catch (error) {
- // File doesn't exist, that's okay
- console.log(`File ${file.path} doesn't exist yet, creating new file`);
- }
-
- const params = {
- owner: context.repo.owner,
- repo: context.repo.repo,
- path: file.path,
- message: `Update repository activity chart: ${file.path}`,
- content: base64Content,
- branch: targetBranch
- };
-
- // Add SHA if file exists
- if (existingFileSha) {
- params.sha = existingFileSha;
- }
-
- await github.rest.repos.createOrUpdateFileContents(params);
- console.log(`Updated chart: ${file.path}`);
- } catch (error) {
- console.log(`Error updating ${file.path}:`, error.message);
- }
- }
- }
-
// Main execution
try {
console.log('Gathering repository data for last 12 months...');
From 33da17e16061929649fe8cf0f8a0dd7df0089954 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:49:43 +0000
Subject: [PATCH 035/125] Update repository activity chart:
docs/assets/commits-chart.svg
From 8097c0b36277d8683c5a7d77dc63f2c0ed8881e2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:49:43 +0000
Subject: [PATCH 036/125] Update repository activity chart:
docs/assets/lines-chart.svg
From f99195e8153229a9592d1e22be206ffba415555a Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:49:44 +0000
Subject: [PATCH 037/125] Update repository activity chart:
docs/assets/prs-chart.svg
From c673653f2b551c49c295173e31bbe4849b80f789 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:56:30 +0200
Subject: [PATCH 038/125] lol
---
.github/workflows/gh_statistics_bot.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index af0bf0e5dc..f307429896 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -4,8 +4,7 @@ on:
pull_request:
types: [opened, synchronize]
schedule:
- - cron: '0 9 1 * *'
- workflow_dispatch:
+ - cron: '0 9 1 * *''
jobs:
generate-monthly-report:
From 5d101d22fffb05bdd7c84d1aff46cc620a5f7dce Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 13:56:54 +0200
Subject: [PATCH 039/125] ups
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index f307429896..348e627384 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -4,8 +4,8 @@ on:
pull_request:
types: [opened, synchronize]
schedule:
- - cron: '0 9 1 * *''
-
+ - cron: '0 9 1 * *'
+
jobs:
generate-monthly-report:
runs-on: ubuntu-latest
From c514c0c8c4d0894333f2fe30f1c8fd07c3b92b0b Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:58:42 +0000
Subject: [PATCH 040/125] Update repository activity chart:
docs/assets/commits-chart.svg
From d119cb44cd80849260d557382ab8a1e6b878c174 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:58:43 +0000
Subject: [PATCH 041/125] Update repository activity chart:
docs/assets/lines-chart.svg
From c7a1b004bff032d3b57ae81416b86747b7edaf3a Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 11:58:44 +0000
Subject: [PATCH 042/125] Update repository activity chart:
docs/assets/prs-chart.svg
From e1089ee5474ae0bec057277a8555abd1a52b8a63 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:00:28 +0200
Subject: [PATCH 043/125] debug
---
.github/workflows/gh_statistics_bot.yml | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 348e627384..0ebbd5b6a8 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -5,7 +5,8 @@ on:
types: [opened, synchronize]
schedule:
- cron: '0 9 1 * *'
-
+ workflow_dispatch:
+
jobs:
generate-monthly-report:
runs-on: ubuntu-latest
@@ -255,7 +256,7 @@ jobs:
console.log(`Using branch: ${targetBranch}`);
}
- // Gather data for last 12 months
+ // Gather data for last 12 months - use previous month as latest
const monthlyDataArray = [];
const now = new Date();
@@ -263,13 +264,16 @@ jobs:
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+ // Debug the date calculation
+ console.log(`Month ${i}: ${monthStart.toLocaleDateString()} to ${monthEnd.toLocaleDateString()}`);
+
const monthRange = {
start: monthStart.toISOString(),
end: monthEnd.toISOString(),
monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
};
- console.log(`Processing ${monthRange.monthName}...`);
+ console.log(`Processing ${monthRange.monthName} (${monthRange.start} to ${monthRange.end})...`);
const [stats, prStats] = await Promise.all([
getMonthCommitStats(monthRange.start, monthRange.end),
@@ -292,8 +296,9 @@ jobs:
// Get language stats for current month
const languageStats = await getLanguageStats();
- // Use latest month for Mattermost
+ // Use latest month for Mattermost (should be July 2025)
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
+ console.log(`Latest month data:`, JSON.stringify(latestMonthData, null, 2));
// Post to Mattermost with safe property access
const mattermostPayload = {
From f393a4ea5acbba4f58e29bea4624d764c171025d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:01:48 +0000
Subject: [PATCH 044/125] Update repository activity chart:
docs/assets/commits-chart.svg
From e547269ee1814428f495ed4c84f0cf5b675ac03b Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:01:49 +0000
Subject: [PATCH 045/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 7d9d3742eae2c5b25dd9c2c577d1f95d73c2c8b8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:01:49 +0000
Subject: [PATCH 046/125] Update repository activity chart:
docs/assets/prs-chart.svg
From c70598dec3c11dc41b3079848a1d5c95d81e6940 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:05:11 +0200
Subject: [PATCH 047/125] test all branches
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 0ebbd5b6a8..858c2644cc 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -300,7 +300,7 @@ jobs:
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
console.log(`Latest month data:`, JSON.stringify(latestMonthData, null, 2));
- // Post to Mattermost with safe property access
+ // Post to Mattermost with correct property names
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
From 5071379b46d4950507a12fed69c52c22dc02dbe0 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:06:19 +0000
Subject: [PATCH 048/125] Update repository activity chart:
docs/assets/commits-chart.svg
From b7a572e94bd0e2a75c6ec701c06dd43f46345376 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:06:19 +0000
Subject: [PATCH 049/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 4f69f491487c761e10a44c5e69746d0b9887673e Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:06:20 +0000
Subject: [PATCH 050/125] Update repository activity chart:
docs/assets/prs-chart.svg
From a703d9bd42565c04560582d7b9d2f714a7b41a75 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:09:10 +0200
Subject: [PATCH 051/125] debug
---
.github/workflows/gh_statistics_bot.yml | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 858c2644cc..48494d0b10 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -298,25 +298,30 @@ jobs:
// Use latest month for Mattermost (should be July 2025)
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
- console.log(`Latest month data:`, JSON.stringify(latestMonthData, null, 2));
+ console.log(`Latest month data for Mattermost:`, JSON.stringify(latestMonthData, null, 2));
+ console.log(`Stats object:`, JSON.stringify(latestMonthData.stats, null, 2));
+ console.log(`PR Stats object:`, JSON.stringify(latestMonthData.prStats, null, 2));
- // Post to Mattermost with correct property names
+ // Post to Mattermost - debug version to see what we actually have
+ console.log('Available properties in stats:', Object.keys(latestMonthData.stats || {}));
+ console.log('Available properties in prStats:', Object.keys(latestMonthData.prStats || {}));
+
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
- { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
- { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
+ { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || latestMonthData.stats?.totalCommits || 0).toString(), short: true },
+ { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || latestMonthData.stats?.totalCommits || 0).toString(), short: true },
+ { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || latestMonthData.stats?.filesChanged || 0).toString(), short: true },
{ title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
{ title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
- { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
+ { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || latestMonthData.stats?.uniqueContributors || 0).toString(), short: true }
],
text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
- `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
+ `**Top Contributors:** ${(latestMonthData.stats?.authorsList || latestMonthData.stats?.contributorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
+ `*Excluding merges, ${latestMonthData.stats?.totalAuthors || latestMonthData.stats?.uniqueContributors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || latestMonthData.stats?.totalCommits || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || latestMonthData.stats?.totalCommits || 0} commits to all branches*\n\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Charts updated in docs/assets/ for documentation'
}]
From 369933cd153927dde7b0fbb229c59fe6e33ee0ba Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:10:15 +0000
Subject: [PATCH 052/125] Update repository activity chart:
docs/assets/commits-chart.svg
From a54d69592317fd3afb8386fb41b0d61403089b3b Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:10:16 +0000
Subject: [PATCH 053/125] Update repository activity chart:
docs/assets/lines-chart.svg
From c530e0095a702b343fbdf38d0435773d53dc61d7 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:10:17 +0000
Subject: [PATCH 054/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 47f5e959664a7e565054e894479716fd943d8ac7 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:16:47 +0200
Subject: [PATCH 055/125] ??
---
.github/workflows/gh_statistics_bot.yml | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 48494d0b10..b5e07a05a5 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -302,26 +302,23 @@ jobs:
console.log(`Stats object:`, JSON.stringify(latestMonthData.stats, null, 2));
console.log(`PR Stats object:`, JSON.stringify(latestMonthData.prStats, null, 2));
- // Post to Mattermost - debug version to see what we actually have
- console.log('Available properties in stats:', Object.keys(latestMonthData.stats || {}));
- console.log('Available properties in prStats:', Object.keys(latestMonthData.prStats || {}));
-
+ // Post to Mattermost using correct property names
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || latestMonthData.stats?.totalCommits || 0).toString(), short: true },
- { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || latestMonthData.stats?.totalCommits || 0).toString(), short: true },
- { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || latestMonthData.stats?.filesChanged || 0).toString(), short: true },
+ { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
+ { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
+ { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
{ title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
{ title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
- { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || latestMonthData.stats?.uniqueContributors || 0).toString(), short: true }
+ { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
],
text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
- `**Top Contributors:** ${(latestMonthData.stats?.authorsList || latestMonthData.stats?.contributorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
+ `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `*Excluding merges, ${latestMonthData.stats?.totalAuthors || latestMonthData.stats?.uniqueContributors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || latestMonthData.stats?.totalCommits || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || latestMonthData.stats?.totalCommits || 0} commits to all branches*\n\n` +
+ `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Charts updated in docs/assets/ for documentation'
}]
From b20df7b2b583ad940aab805a266bb7b6c6ecc736 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:18:04 +0000
Subject: [PATCH 056/125] Update repository activity chart:
docs/assets/commits-chart.svg
From dc8ab6dcac5d5d158a7a94392116c8d79d6f9a66 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:18:05 +0000
Subject: [PATCH 057/125] Update repository activity chart:
docs/assets/lines-chart.svg
From ae2cf43227e344bc74cf98b28cbedb2ba26befe9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:18:06 +0000
Subject: [PATCH 058/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 7344b0edc29ccd2b89a42fc4917dc5803de389dd Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:20:16 +0200
Subject: [PATCH 059/125] maybemaybe
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index b5e07a05a5..4e66e5bbf6 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -187,7 +187,7 @@ jobs:
// Commit charts to repository
async function commitChartsToRepo(monthlyData, targetBranch) {
- const commits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || 0);
+ const commits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || d.stats?.totalCommits || 0);
const linesAdded = monthlyData.map(d => d.stats?.linesAdded || 0);
const prsOpened = monthlyData.map(d => d.prStats?.opened || 0);
From ab40893f373f3343ecdfc06931d0d2208bbc98f3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:21:36 +0000
Subject: [PATCH 060/125] Update repository activity chart:
docs/assets/commits-chart.svg
---
docs/assets/commits-chart.svg | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
index 49f515fa0c..b63562788f 100644
--- a/docs/assets/commits-chart.svg
+++ b/docs/assets/commits-chart.svg
@@ -10,12 +10,12 @@
-
-
+
+
-
+
Monthly Commits
- 0
- 0
+ 12
+ 2
\ No newline at end of file
From de59af50e4c6e8288ce4d6beb8131bfd9a4eea63 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:21:36 +0000
Subject: [PATCH 061/125] Update repository activity chart:
docs/assets/lines-chart.svg
From f670257859de6809af56dace66401f7a2327626d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:21:37 +0000
Subject: [PATCH 062/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 3b6e05b2b2d30912f7d28c147e2bc1ab4c88a785 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:27:18 +0200
Subject: [PATCH 063/125] now
---
.github/workflows/gh_statistics_bot.yml | 169 ++++++++++++++++--------
1 file changed, 115 insertions(+), 54 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 4e66e5bbf6..d5bf6c90fd 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -58,57 +58,123 @@ jobs:
return [];
}
}
- async function getMonthCommitStats(since, until) {
- const commits = await github.paginate(github.rest.repos.listCommits, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- since,
- until,
- per_page: 100,
- });
-
- const contributors = new Set();
- const filesChanged = new Set();
- let totalAdditions = 0;
- let totalDeletions = 0;
- for (const commit of commits.slice(0, 50)) {
- if (commit.author) {
- contributors.add(commit.author.login);
+ // Get commit statistics - properly separated like GitHub Pulse
+ async function getMonthCommitStats(since, until) {
+ console.log(`=== GETTING COMMITS BETWEEN ${since} AND ${until} ===`);
+
+ try {
+ // Get the default branch name first
+ const repo = await github.rest.repos.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+ const defaultBranch = repo.data.default_branch;
+ console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
+
+ // Get ALL commits from ALL branches (no sha filter)
+ console.log('*** FETCHING ALL BRANCH COMMITS ***');
+ const allBranchCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`*** FOUND ${allBranchCommits.length} COMMITS ACROSS ALL BRANCHES ***`);
+
+ // Get commits from MAIN branch only
+ console.log('*** FETCHING MAIN BRANCH COMMITS ***');
+ const mainBranchCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: defaultBranch,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`*** FOUND ${mainBranchCommits.length} COMMITS ON ${defaultBranch} ***`);
+
+ // Process ALL branch commits for total authors and line changes
+ const allAuthors = new Set();
+ let totalAdditions = 0;
+ let totalDeletions = 0;
+
+ console.log('*** PROCESSING ALL BRANCH COMMITS FOR AUTHORS ***');
+ for (const commit of allBranchCommits) {
+ if (commit.author && commit.author.login) {
+ allAuthors.add(commit.author.login);
+ }
+
+ // Get detailed stats for first 50 commits to avoid rate limits
+ if (allBranchCommits.indexOf(commit) < 50) {
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ totalAdditions += commitDetail.data.stats?.additions || 0;
+ totalDeletions += commitDetail.data.stats?.deletions || 0;
+ } catch (error) {
+ console.log(`Error getting commit detail: ${error.message}`);
+ }
+ }
}
-
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- if (commitDetail.data.files) {
- commitDetail.data.files.forEach(file => {
- filesChanged.add(file.filename);
+
+ console.log(`*** FOUND ${allAuthors.size} UNIQUE AUTHORS ***`);
+ console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
+
+ // Process MAIN branch commits for main-specific file changes
+ let mainFileChanges = 0;
+
+ for (const commit of mainBranchCommits.slice(0, 30)) {
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
});
+
+ mainFileChanges += commitDetail.data.files?.length || 0;
+ } catch (error) {
+ console.log(`Error getting main commit detail: ${error.message}`);
}
-
- totalAdditions += commitDetail.data.stats?.additions || 0;
- totalDeletions += commitDetail.data.stats?.deletions || 0;
- } catch (error) {
- console.log(`Error getting commit details: ${error.message}`);
}
+
+ const result = {
+ totalCommitsAllBranches: allBranchCommits.length,
+ totalCommitsMain: mainBranchCommits.length,
+ totalAuthors: allAuthors.size,
+ authorsList: Array.from(allAuthors),
+ filesChangedMain: mainFileChanges,
+ linesAdded: totalAdditions,
+ linesRemoved: totalDeletions,
+ netLines: totalAdditions - totalDeletions,
+ };
+
+ console.log('*** FINAL RESULT ***', JSON.stringify(result, null, 2));
+ return result;
+
+ } catch (error) {
+ console.log('*** ERROR GETTING COMMIT STATS ***', error.message);
+ return {
+ totalCommitsAllBranches: 0,
+ totalCommitsMain: 0,
+ totalAuthors: 0,
+ authorsList: [],
+ filesChangedMain: 0,
+ linesAdded: 0,
+ linesRemoved: 0,
+ netLines: 0,
+ };
}
-
- return {
- totalCommits: commits.length,
- uniqueContributors: contributors.size,
- contributorsList: Array.from(contributors),
- filesChanged: filesChanged.size,
- linesAdded: totalAdditions,
- linesRemoved: totalDeletions,
- netLines: totalAdditions - totalDeletions,
- };
}
- // Get PR statistics for a specific month
+ // Get pull request statistics
async function getMonthPRStats(since, until) {
const allPRs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
@@ -216,7 +282,6 @@ jobs:
});
existingFileSha = existingFile.data.sha;
} catch (error) {
- // File doesn't exist, that's okay
console.log(`File ${file.path} doesn't exist yet, creating new file`);
}
@@ -229,7 +294,6 @@ jobs:
branch: targetBranch
};
- // Add SHA if file exists
if (existingFileSha) {
params.sha = existingFileSha;
}
@@ -244,7 +308,7 @@ jobs:
// Main execution
try {
- console.log('Gathering repository data for last 12 months...');
+ console.log('*** STARTING MONTHLY REPORT GENERATION ***');
// Get current branch
let targetBranch;
@@ -256,7 +320,7 @@ jobs:
console.log(`Using branch: ${targetBranch}`);
}
- // Gather data for last 12 months - use previous month as latest
+ // Gather data for last 12 months
const monthlyDataArray = [];
const now = new Date();
@@ -264,7 +328,6 @@ jobs:
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
- // Debug the date calculation
console.log(`Month ${i}: ${monthStart.toLocaleDateString()} to ${monthEnd.toLocaleDateString()}`);
const monthRange = {
@@ -293,16 +356,14 @@ jobs:
console.log('Generating and committing SVG charts...');
await commitChartsToRepo(monthlyDataArray, targetBranch);
- // Get language stats for current month
+ // Get language stats
const languageStats = await getLanguageStats();
- // Use latest month for Mattermost (should be July 2025)
+ // Use latest month for Mattermost
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
- console.log(`Latest month data for Mattermost:`, JSON.stringify(latestMonthData, null, 2));
- console.log(`Stats object:`, JSON.stringify(latestMonthData.stats, null, 2));
- console.log(`PR Stats object:`, JSON.stringify(latestMonthData.prStats, null, 2));
+ console.log(`*** LATEST MONTH FOR MATTERMOST ***`, JSON.stringify(latestMonthData, null, 2));
- // Post to Mattermost using correct property names
+ // Post to Mattermost
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
From 79c386a1484c26a633e8c88b0a429c71d962c626 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:28:29 +0000
Subject: [PATCH 064/125] Update repository activity chart:
docs/assets/commits-chart.svg
From e7fa9df25665185f33c2802cb61919e419d2ff9f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:28:29 +0000
Subject: [PATCH 065/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 0f2764e0c8092bd044e46b543a8f6a11884886f5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:28:30 +0000
Subject: [PATCH 066/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 1aae0c375bcbde9a3a59ea71e0b06a0aeccb5024 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:33:19 +0200
Subject: [PATCH 067/125] maybemaybe
---
.github/workflows/gh_statistics_bot.yml | 69 +++++++++++++------------
1 file changed, 37 insertions(+), 32 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index d5bf6c90fd..99736224d8 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -72,17 +72,14 @@ jobs:
const defaultBranch = repo.data.default_branch;
console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- // Get ALL commits from ALL branches (no sha filter)
- console.log('*** FETCHING ALL BRANCH COMMITS ***');
- const allBranchCommits = await github.paginate(github.rest.repos.listCommits, {
+ // Get all branches first
+ const branches = await github.paginate(github.rest.repos.listBranches, {
owner: context.repo.owner,
repo: context.repo.repo,
- since,
- until,
per_page: 100,
});
- console.log(`*** FOUND ${allBranchCommits.length} COMMITS ACROSS ALL BRANCHES ***`);
+ console.log(`*** FOUND ${branches.length} BRANCHES ***`);
// Get commits from MAIN branch only
console.log('*** FETCHING MAIN BRANCH COMMITS ***');
@@ -97,38 +94,44 @@ jobs:
console.log(`*** FOUND ${mainBranchCommits.length} COMMITS ON ${defaultBranch} ***`);
- // Process ALL branch commits for total authors and line changes
+ // Get commits from ALL branches
+ console.log('*** FETCHING COMMITS FROM ALL BRANCHES ***');
+ const allCommitsSet = new Set();
const allAuthors = new Set();
- let totalAdditions = 0;
- let totalDeletions = 0;
- console.log('*** PROCESSING ALL BRANCH COMMITS FOR AUTHORS ***');
- for (const commit of allBranchCommits) {
- if (commit.author && commit.author.login) {
- allAuthors.add(commit.author.login);
- }
-
- // Get detailed stats for first 50 commits to avoid rate limits
- if (allBranchCommits.indexOf(commit) < 50) {
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- totalAdditions += commitDetail.data.stats?.additions || 0;
- totalDeletions += commitDetail.data.stats?.deletions || 0;
- } catch (error) {
- console.log(`Error getting commit detail: ${error.message}`);
- }
+ for (const branch of branches.slice(0, 20)) { // Limit to first 20 branches to avoid rate limits
+ try {
+ console.log(`Checking branch: ${branch.name}`);
+ const branchCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: branch.name,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`Found ${branchCommits.length} commits on ${branch.name}`);
+
+ branchCommits.forEach(commit => {
+ allCommitsSet.add(commit.sha);
+ if (commit.author && commit.author.login) {
+ allAuthors.add(commit.author.login);
+ }
+ });
+
+ } catch (error) {
+ console.log(`Error getting commits for branch ${branch.name}:`, error.message);
}
}
- console.log(`*** FOUND ${allAuthors.size} UNIQUE AUTHORS ***`);
+ console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitsSet.size} ***`);
+ console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
- // Process MAIN branch commits for main-specific file changes
+ // Get detailed stats from main branch commits for line changes and file counts
+ let totalAdditions = 0;
+ let totalDeletions = 0;
let mainFileChanges = 0;
for (const commit of mainBranchCommits.slice(0, 30)) {
@@ -140,13 +143,15 @@ jobs:
});
mainFileChanges += commitDetail.data.files?.length || 0;
+ totalAdditions += commitDetail.data.stats?.additions || 0;
+ totalDeletions += commitDetail.data.stats?.deletions || 0;
} catch (error) {
console.log(`Error getting main commit detail: ${error.message}`);
}
}
const result = {
- totalCommitsAllBranches: allBranchCommits.length,
+ totalCommitsAllBranches: allCommitsSet.size,
totalCommitsMain: mainBranchCommits.length,
totalAuthors: allAuthors.size,
authorsList: Array.from(allAuthors),
From 3ba5b43f2c0a65f8ced1e92beec71c8b01ccdd7a Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:34:31 +0000
Subject: [PATCH 068/125] Update repository activity chart:
docs/assets/commits-chart.svg
---
docs/assets/commits-chart.svg | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
index b63562788f..49f515fa0c 100644
--- a/docs/assets/commits-chart.svg
+++ b/docs/assets/commits-chart.svg
@@ -10,12 +10,12 @@
-
-
+
+
-
+
Monthly Commits
- 12
- 2
+ 0
+ 0
\ No newline at end of file
From fe74e20851696ea26159e5a26d2ef6cc8c219ff7 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:34:32 +0000
Subject: [PATCH 069/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 77a863ddbdb9157dca725eff7c527eacf56bada5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:34:32 +0000
Subject: [PATCH 070/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 39327b39ee4bd238b71e854a0e13f84429fef2c1 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:36:07 +0200
Subject: [PATCH 071/125] lol
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 99736224d8..f3bbadaf81 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -99,7 +99,7 @@ jobs:
const allCommitsSet = new Set();
const allAuthors = new Set();
- for (const branch of branches.slice(0, 20)) { // Limit to first 20 branches to avoid rate limits
+ for (const branch of branches.slice(0, 200)) { // Limit to first 20 branches to avoid rate limits
try {
console.log(`Checking branch: ${branch.name}`);
const branchCommits = await github.paginate(github.rest.repos.listCommits, {
@@ -134,7 +134,7 @@ jobs:
let totalDeletions = 0;
let mainFileChanges = 0;
- for (const commit of mainBranchCommits.slice(0, 30)) {
+ for (const commit of mainBranchCommits.slice(0, 100)) {
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
From 5cb187c101fa7d47fbca0c6d1d4217a0b1f74658 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:38:29 +0200
Subject: [PATCH 072/125] maybe
---
.github/workflows/gh_statistics_bot.yml | 138 +++++++++++++-----------
1 file changed, 78 insertions(+), 60 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index f3bbadaf81..470fd0e9f4 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -59,82 +59,91 @@ jobs:
}
}
- // Get commit statistics - properly separated like GitHub Pulse
+ // Get commit statistics using GitHub's actual Pulse API
async function getMonthCommitStats(since, until) {
- console.log(`=== GETTING COMMITS BETWEEN ${since} AND ${until} ===`);
+ console.log(`=== USING GITHUB PULSE API FOR ${since} TO ${until} ===`);
try {
- // Get the default branch name first
- const repo = await github.rest.repos.get({
+ // Use GitHub's Code Frequency API (shows additions/deletions over time)
+ const codeFrequency = await github.rest.repos.getCodeFrequencyStats({
owner: context.repo.owner,
repo: context.repo.repo,
});
- const defaultBranch = repo.data.default_branch;
- console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- // Get all branches first
- const branches = await github.paginate(github.rest.repos.listBranches, {
+ console.log('*** CODE FREQUENCY DATA LENGTH ***', codeFrequency.data?.length);
+
+ // Use GitHub's Commit Activity API (shows commits per week)
+ const commitActivity = await github.rest.repos.getCommitActivityStats({
owner: context.repo.owner,
repo: context.repo.repo,
- per_page: 100,
});
- console.log(`*** FOUND ${branches.length} BRANCHES ***`);
+ console.log('*** COMMIT ACTIVITY DATA LENGTH ***', commitActivity.data?.length);
- // Get commits from MAIN branch only
- console.log('*** FETCHING MAIN BRANCH COMMITS ***');
- const mainBranchCommits = await github.paginate(github.rest.repos.listCommits, {
+ // Use GitHub's Participation API (shows commit participation)
+ const participation = await github.rest.repos.getParticipationStats({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ console.log('*** PARTICIPATION DATA ***', participation.data);
+
+ // Use GitHub's Contributors API to get author info
+ const contributors = await github.paginate(github.rest.repos.listContributors, {
owner: context.repo.owner,
repo: context.repo.repo,
- sha: defaultBranch,
- since,
- until,
per_page: 100,
});
- console.log(`*** FOUND ${mainBranchCommits.length} COMMITS ON ${defaultBranch} ***`);
+ console.log('*** FOUND CONTRIBUTORS ***', contributors.length);
- // Get commits from ALL branches
- console.log('*** FETCHING COMMITS FROM ALL BRANCHES ***');
- const allCommitsSet = new Set();
- const allAuthors = new Set();
+ // For the specific month, we still need to use listCommits but with better logic
+ // Get all commits using GraphQL-style approach via REST
- for (const branch of branches.slice(0, 200)) { // Limit to first 20 branches to avoid rate limits
- try {
- console.log(`Checking branch: ${branch.name}`);
- const branchCommits = await github.paginate(github.rest.repos.listCommits, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- sha: branch.name,
- since,
- until,
- per_page: 100,
- });
-
- console.log(`Found ${branchCommits.length} commits on ${branch.name}`);
-
- branchCommits.forEach(commit => {
- allCommitsSet.add(commit.sha);
- if (commit.author && commit.author.login) {
- allAuthors.add(commit.author.login);
- }
- });
-
- } catch (error) {
- console.log(`Error getting commits for branch ${branch.name}:`, error.message);
- }
+ // Method 1: Try to use search API to find commits in date range
+ const searchQuery = `repo:${context.repo.owner}/${context.repo.repo} author-date:${since.split('T')[0]}..${until.split('T')[0]}`;
+ console.log('*** SEARCHING WITH QUERY ***', searchQuery);
+
+ let searchResults = null;
+ try {
+ searchResults = await github.rest.search.commits({
+ q: searchQuery,
+ sort: 'author-date',
+ order: 'desc',
+ per_page: 100,
+ });
+ console.log('*** SEARCH FOUND COMMITS ***', searchResults.data.total_count);
+ } catch (searchError) {
+ console.log('*** SEARCH API ERROR ***', searchError.message);
}
- console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitsSet.size} ***`);
- console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
- console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
+ // Method 2: Fallback to regular commits API
+ const mainCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: repo.data.default_branch,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`*** MAIN BRANCH COMMITS: ${mainCommits.length} ***`);
+
+ // Use search results if available, otherwise use estimates
+ const totalCommitsAllBranches = searchResults?.data.total_count || mainCommits.length;
+ const totalCommitsMain = mainCommits.length;
- // Get detailed stats from main branch commits for line changes and file counts
+ // Process main commits for detailed stats
+ const authors = new Set();
let totalAdditions = 0;
let totalDeletions = 0;
- let mainFileChanges = 0;
+ let fileChanges = 0;
- for (const commit of mainBranchCommits.slice(0, 100)) {
+ for (const commit of mainCommits.slice(0, 50)) {
+ if (commit.author && commit.author.login) {
+ authors.add(commit.author.login);
+ }
+
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
@@ -142,30 +151,39 @@ jobs:
ref: commit.sha,
});
- mainFileChanges += commitDetail.data.files?.length || 0;
+ fileChanges += commitDetail.data.files?.length || 0;
totalAdditions += commitDetail.data.stats?.additions || 0;
totalDeletions += commitDetail.data.stats?.deletions || 0;
} catch (error) {
- console.log(`Error getting main commit detail: ${error.message}`);
+ console.log(`Error getting commit detail: ${error.message}`);
}
}
+ // If search worked, try to get all authors from search results
+ if (searchResults && searchResults.data.items) {
+ searchResults.data.items.forEach(commit => {
+ if (commit.author && commit.author.login) {
+ authors.add(commit.author.login);
+ }
+ });
+ }
+
const result = {
- totalCommitsAllBranches: allCommitsSet.size,
- totalCommitsMain: mainBranchCommits.length,
- totalAuthors: allAuthors.size,
- authorsList: Array.from(allAuthors),
- filesChangedMain: mainFileChanges,
+ totalCommitsAllBranches: totalCommitsAllBranches,
+ totalCommitsMain: totalCommitsMain,
+ totalAuthors: authors.size,
+ authorsList: Array.from(authors),
+ filesChangedMain: fileChanges,
linesAdded: totalAdditions,
linesRemoved: totalDeletions,
netLines: totalAdditions - totalDeletions,
};
- console.log('*** FINAL RESULT ***', JSON.stringify(result, null, 2));
+ console.log('*** FINAL PULSE-BASED RESULT ***', JSON.stringify(result, null, 2));
return result;
} catch (error) {
- console.log('*** ERROR GETTING COMMIT STATS ***', error.message);
+ console.log('*** ERROR WITH PULSE APIs ***', error.message);
return {
totalCommitsAllBranches: 0,
totalCommitsMain: 0,
From 6eacae3843d42c4ac5574c21ccd5cedf36ac5322 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:39:42 +0000
Subject: [PATCH 073/125] Update repository activity chart:
docs/assets/commits-chart.svg
From 9180bfb72512754eda94feec3cdc7eeedb155643 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:39:43 +0000
Subject: [PATCH 074/125] Update repository activity chart:
docs/assets/lines-chart.svg
---
docs/assets/lines-chart.svg | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/assets/lines-chart.svg b/docs/assets/lines-chart.svg
index 04394c8046..a684ae8e71 100644
--- a/docs/assets/lines-chart.svg
+++ b/docs/assets/lines-chart.svg
@@ -10,12 +10,12 @@
-
-
+
+
-
+
Lines Added per Month
- 26179
- 210
+ 0
+ 0
\ No newline at end of file
From 4465aaece8f7bb9f0ff4a85f7fa8dab0a7bf07a9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:39:44 +0000
Subject: [PATCH 075/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 715b0b2a52ad8a9304f3314f546891e286736661 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:40:30 +0000
Subject: [PATCH 076/125] Update repository activity chart:
docs/assets/commits-chart.svg
---
docs/assets/commits-chart.svg | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
index 49f515fa0c..92eee660f7 100644
--- a/docs/assets/commits-chart.svg
+++ b/docs/assets/commits-chart.svg
@@ -10,12 +10,12 @@
-
-
+
+
-
+
Monthly Commits
- 0
- 0
+ 297
+ 56
\ No newline at end of file
From 1599880289ef8999fbdc7d222f856fc4570cabc5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:40:30 +0000
Subject: [PATCH 077/125] Update repository activity chart:
docs/assets/lines-chart.svg
---
docs/assets/lines-chart.svg | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/assets/lines-chart.svg b/docs/assets/lines-chart.svg
index a684ae8e71..04394c8046 100644
--- a/docs/assets/lines-chart.svg
+++ b/docs/assets/lines-chart.svg
@@ -10,12 +10,12 @@
-
-
+
+
-
+
Lines Added per Month
- 0
- 0
+ 26179
+ 210
\ No newline at end of file
From 3ec485ef41f1bebece61e3c9d0180dca6c94aca6 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:40:31 +0000
Subject: [PATCH 078/125] Update repository activity chart:
docs/assets/prs-chart.svg
From 5ac8a7f232ac3e07e4094b28aa3d920c02b548b2 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:00:06 +0200
Subject: [PATCH 079/125] now?
---
.github/workflows/gh_statistics_bot.yml | 149 +++++++++++-------------
1 file changed, 66 insertions(+), 83 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 470fd0e9f4..c5b0e58d2a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -59,91 +59,83 @@ jobs:
}
}
- // Get commit statistics using GitHub's actual Pulse API
+ // Get commit statistics using GitHub's recommended approach
async function getMonthCommitStats(since, until) {
- console.log(`=== USING GITHUB PULSE API FOR ${since} TO ${until} ===`);
+ console.log(`=== GETTING ALL COMMITS FROM ALL BRANCHES ${since} TO ${until} ===`);
try {
- // Use GitHub's Code Frequency API (shows additions/deletions over time)
- const codeFrequency = await github.rest.repos.getCodeFrequencyStats({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- console.log('*** CODE FREQUENCY DATA LENGTH ***', codeFrequency.data?.length);
-
- // Use GitHub's Commit Activity API (shows commits per week)
- const commitActivity = await github.rest.repos.getCommitActivityStats({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- console.log('*** COMMIT ACTIVITY DATA LENGTH ***', commitActivity.data?.length);
-
- // Use GitHub's Participation API (shows commit participation)
- const participation = await github.rest.repos.getParticipationStats({
+ // Step 1: Get all branches in the repository
+ console.log('*** STEP 1: LISTING ALL BRANCHES ***');
+ const branches = await github.paginate(github.rest.repos.listBranches, {
owner: context.repo.owner,
repo: context.repo.repo,
+ per_page: 100,
});
- console.log('*** PARTICIPATION DATA ***', participation.data);
+ console.log(`*** FOUND ${branches.length} BRANCHES: ${branches.map(b => b.name).join(', ')} ***`);
- // Use GitHub's Contributors API to get author info
- const contributors = await github.paginate(github.rest.repos.listContributors, {
+ // Get default branch for main commit counting
+ const repo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
- per_page: 100,
});
+ const defaultBranch = repo.data.default_branch;
+ console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- console.log('*** FOUND CONTRIBUTORS ***', contributors.length);
-
- // For the specific month, we still need to use listCommits but with better logic
- // Get all commits using GraphQL-style approach via REST
+ // Step 2: Fetch commits for each branch
+ console.log('*** STEP 2: FETCHING COMMITS FROM EACH BRANCH ***');
+ const allCommitShas = new Set();
+ const allAuthors = new Set();
+ let mainBranchCommits = [];
- // Method 1: Try to use search API to find commits in date range
- const searchQuery = `repo:${context.repo.owner}/${context.repo.repo} author-date:${since.split('T')[0]}..${until.split('T')[0]}`;
- console.log('*** SEARCHING WITH QUERY ***', searchQuery);
-
- let searchResults = null;
- try {
- searchResults = await github.rest.search.commits({
- q: searchQuery,
- sort: 'author-date',
- order: 'desc',
- per_page: 100,
- });
- console.log('*** SEARCH FOUND COMMITS ***', searchResults.data.total_count);
- } catch (searchError) {
- console.log('*** SEARCH API ERROR ***', searchError.message);
+ for (const branch of branches) {
+ try {
+ console.log(`*** Fetching commits from branch: ${branch.name} ***`);
+
+ const branchCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: branch.name,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`*** Found ${branchCommits.length} commits on ${branch.name} ***`);
+
+ // Add to our sets (automatically handles duplicates)
+ branchCommits.forEach(commit => {
+ allCommitShas.add(commit.sha);
+ if (commit.author && commit.author.login) {
+ allAuthors.add(commit.author.login);
+ }
+ });
+
+ // Keep main branch commits separate
+ if (branch.name === defaultBranch) {
+ mainBranchCommits = branchCommits;
+ }
+
+ } catch (error) {
+ console.log(`*** Error fetching commits from ${branch.name}: ${error.message} ***`);
+ }
+
+ // Add delay to avoid rate limiting
+ await new Promise(resolve => setTimeout(resolve, 100));
}
- // Method 2: Fallback to regular commits API
- const mainCommits = await github.paginate(github.rest.repos.listCommits, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- sha: repo.data.default_branch,
- since,
- until,
- per_page: 100,
- });
-
- console.log(`*** MAIN BRANCH COMMITS: ${mainCommits.length} ***`);
-
- // Use search results if available, otherwise use estimates
- const totalCommitsAllBranches = searchResults?.data.total_count || mainCommits.length;
- const totalCommitsMain = mainCommits.length;
+ console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitShas.size} ***`);
+ console.log(`*** COMMITS ON MAIN BRANCH: ${mainBranchCommits.length} ***`);
+ console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
+ console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
- // Process main commits for detailed stats
- const authors = new Set();
+ // Step 3: Get detailed stats from main branch commits
+ console.log('*** STEP 3: GETTING DETAILED STATS FROM MAIN BRANCH ***');
let totalAdditions = 0;
let totalDeletions = 0;
- let fileChanges = 0;
+ let mainFileChanges = 0;
- for (const commit of mainCommits.slice(0, 50)) {
- if (commit.author && commit.author.login) {
- authors.add(commit.author.login);
- }
-
+ for (const commit of mainBranchCommits.slice(0, 50)) { // Limit to avoid rate limits
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
@@ -151,7 +143,7 @@ jobs:
ref: commit.sha,
});
- fileChanges += commitDetail.data.files?.length || 0;
+ mainFileChanges += commitDetail.data.files?.length || 0;
totalAdditions += commitDetail.data.stats?.additions || 0;
totalDeletions += commitDetail.data.stats?.deletions || 0;
} catch (error) {
@@ -159,31 +151,22 @@ jobs:
}
}
- // If search worked, try to get all authors from search results
- if (searchResults && searchResults.data.items) {
- searchResults.data.items.forEach(commit => {
- if (commit.author && commit.author.login) {
- authors.add(commit.author.login);
- }
- });
- }
-
const result = {
- totalCommitsAllBranches: totalCommitsAllBranches,
- totalCommitsMain: totalCommitsMain,
- totalAuthors: authors.size,
- authorsList: Array.from(authors),
- filesChangedMain: fileChanges,
+ totalCommitsAllBranches: allCommitShas.size,
+ totalCommitsMain: mainBranchCommits.length,
+ totalAuthors: allAuthors.size,
+ authorsList: Array.from(allAuthors),
+ filesChangedMain: mainFileChanges,
linesAdded: totalAdditions,
linesRemoved: totalDeletions,
netLines: totalAdditions - totalDeletions,
};
- console.log('*** FINAL PULSE-BASED RESULT ***', JSON.stringify(result, null, 2));
+ console.log('*** FINAL RESULT (FOLLOWING GITHUB DOCS) ***', JSON.stringify(result, null, 2));
return result;
} catch (error) {
- console.log('*** ERROR WITH PULSE APIs ***', error.message);
+ console.log('*** ERROR FOLLOWING GITHUB DOCS APPROACH ***', error.message);
return {
totalCommitsAllBranches: 0,
totalCommitsMain: 0,
From 693b02167b9bcffd4c4177bf5adec8ecc12a2bdc Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:01:39 +0200
Subject: [PATCH 080/125] remove branch limits
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index c5b0e58d2a..ddde858078 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -134,8 +134,8 @@ jobs:
let totalAdditions = 0;
let totalDeletions = 0;
let mainFileChanges = 0;
-
- for (const commit of mainBranchCommits.slice(0, 50)) { // Limit to avoid rate limits
+
+ for (const commit of mainBranchCommits) { // Limit to avoid rate limits
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
From f2ef4fe5f3f8c7e5cd67117ec433543554c11917 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:08:44 +0000
Subject: [PATCH 081/125] Update repository activity chart:
docs/assets/commits-chart.svg
From fefe1b28d2c82653203892bf7723d656310a0936 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:08:45 +0000
Subject: [PATCH 082/125] Update repository activity chart:
docs/assets/lines-chart.svg
From 0aecdda8e1ed0290ba9ec3160ed0b532caf83d9c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 1 Aug 2025 14:08:46 +0000
Subject: [PATCH 083/125] Update repository activity chart:
docs/assets/prs-chart.svg
From f55d9e5b6d0dfd412edb163b53ff3dbef4149336 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:10:57 +0200
Subject: [PATCH 084/125] Limit commit retrieval to the first 50 entries to
avoid rate limits
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index ddde858078..c5b0e58d2a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -134,8 +134,8 @@ jobs:
let totalAdditions = 0;
let totalDeletions = 0;
let mainFileChanges = 0;
-
- for (const commit of mainBranchCommits) { // Limit to avoid rate limits
+
+ for (const commit of mainBranchCommits.slice(0, 50)) { // Limit to avoid rate limits
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
From bfa7adf9a2c2845a173f7ef0c1f72b5ce00192c8 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:17:44 +0200
Subject: [PATCH 085/125] test
---
.github/workflows/gh_statistics_bot.yml | 60 +++++++++++++++++++++----
1 file changed, 52 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index c5b0e58d2a..8c66e3238a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -72,7 +72,51 @@ jobs:
per_page: 100,
});
- console.log(`*** FOUND ${branches.length} BRANCHES: ${branches.map(b => b.name).join(', ')} ***`);
+ console.log(`*** FOUND ${branches.length} BRANCHES ***`);
+
+ // Step 1.5: Sort branches by most recent commit date
+ console.log('*** STEP 1.5: SORTING BRANCHES BY RECENT ACTIVITY ***');
+ const branchesWithDates = [];
+
+ for (const branch of branches.slice(0, 50)) { // Get recent activity for first 50 to avoid rate limits
+ try {
+ const branchInfo = await github.rest.repos.getBranch({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ branch: branch.name,
+ });
+
+ branchesWithDates.push({
+ name: branch.name,
+ lastCommitDate: branchInfo.data.commit.commit.author.date,
+ branch: branch
+ });
+ } catch (error) {
+ console.log(`Error getting branch info for ${branch.name}: ${error.message}`);
+ // Add branch without date info as fallback
+ branchesWithDates.push({
+ name: branch.name,
+ lastCommitDate: '1970-01-01T00:00:00Z', // Very old date as fallback
+ branch: branch
+ });
+ }
+ }
+
+ // Add remaining branches without date checking (to avoid rate limits)
+ for (const branch of branches.slice(50)) {
+ branchesWithDates.push({
+ name: branch.name,
+ lastCommitDate: '1970-01-01T00:00:00Z',
+ branch: branch
+ });
+ }
+
+ // Sort by most recent first
+ branchesWithDates.sort((a, b) => new Date(b.lastCommitDate) - new Date(a.lastCommitDate));
+
+ // Take the most recent 30 branches (or all if fewer)
+ const sortedBranches = branchesWithDates.slice(0, 30).map(b => b.branch);
+ console.log(`*** USING TOP ${sortedBranches.length} MOST RECENT BRANCHES: ${sortedBranches.slice(0, 10).map(b => b.name).join(', ')}${sortedBranches.length > 10 ? '...' : ''} ***`);
// Get default branch for main commit counting
const repo = await github.rest.repos.get({
@@ -82,13 +126,13 @@ jobs:
const defaultBranch = repo.data.default_branch;
console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- // Step 2: Fetch commits for each branch
- console.log('*** STEP 2: FETCHING COMMITS FROM EACH BRANCH ***');
+ // Step 2: Fetch commits for each selected branch
+ console.log('*** STEP 2: FETCHING COMMITS FROM SELECTED BRANCHES ***');
const allCommitShas = new Set();
const allAuthors = new Set();
let mainBranchCommits = [];
- for (const branch of branches) {
+ for (const branch of sortedBranches) {
try {
console.log(`*** Fetching commits from branch: ${branch.name} ***`);
@@ -121,10 +165,10 @@ jobs:
}
// Add delay to avoid rate limiting
- await new Promise(resolve => setTimeout(resolve, 100));
+ await new Promise(resolve => setTimeout(resolve, 200));
}
- console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitShas.size} ***`);
+ console.log(`*** TOTAL UNIQUE COMMITS ACROSS SELECTED BRANCHES: ${allCommitShas.size} ***`);
console.log(`*** COMMITS ON MAIN BRANCH: ${mainBranchCommits.length} ***`);
console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
@@ -162,11 +206,11 @@ jobs:
netLines: totalAdditions - totalDeletions,
};
- console.log('*** FINAL RESULT (FOLLOWING GITHUB DOCS) ***', JSON.stringify(result, null, 2));
+ console.log('*** FINAL RESULT (SORTED BY RECENCY) ***', JSON.stringify(result, null, 2));
return result;
} catch (error) {
- console.log('*** ERROR FOLLOWING GITHUB DOCS APPROACH ***', error.message);
+ console.log('*** ERROR WITH SORTED BRANCH APPROACH ***', error.message);
return {
totalCommitsAllBranches: 0,
totalCommitsMain: 0,
From 32e5646de1b8a6bcf79a8ac97b0da3230874003b Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:23:41 +0200
Subject: [PATCH 086/125] all
---
.github/workflows/gh_statistics_bot.yml | 71 ++++++-------------------
1 file changed, 17 insertions(+), 54 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 8c66e3238a..9127ba27ad 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -74,50 +74,6 @@ jobs:
console.log(`*** FOUND ${branches.length} BRANCHES ***`);
- // Step 1.5: Sort branches by most recent commit date
- console.log('*** STEP 1.5: SORTING BRANCHES BY RECENT ACTIVITY ***');
- const branchesWithDates = [];
-
- for (const branch of branches.slice(0, 50)) { // Get recent activity for first 50 to avoid rate limits
- try {
- const branchInfo = await github.rest.repos.getBranch({
- owner: context.repo.owner,
- repo: context.repo.repo,
- branch: branch.name,
- });
-
- branchesWithDates.push({
- name: branch.name,
- lastCommitDate: branchInfo.data.commit.commit.author.date,
- branch: branch
- });
- } catch (error) {
- console.log(`Error getting branch info for ${branch.name}: ${error.message}`);
- // Add branch without date info as fallback
- branchesWithDates.push({
- name: branch.name,
- lastCommitDate: '1970-01-01T00:00:00Z', // Very old date as fallback
- branch: branch
- });
- }
- }
-
- // Add remaining branches without date checking (to avoid rate limits)
- for (const branch of branches.slice(50)) {
- branchesWithDates.push({
- name: branch.name,
- lastCommitDate: '1970-01-01T00:00:00Z',
- branch: branch
- });
- }
-
- // Sort by most recent first
- branchesWithDates.sort((a, b) => new Date(b.lastCommitDate) - new Date(a.lastCommitDate));
-
- // Take the most recent 30 branches (or all if fewer)
- const sortedBranches = branchesWithDates.slice(0, 30).map(b => b.branch);
- console.log(`*** USING TOP ${sortedBranches.length} MOST RECENT BRANCHES: ${sortedBranches.slice(0, 10).map(b => b.name).join(', ')}${sortedBranches.length > 10 ? '...' : ''} ***`);
-
// Get default branch for main commit counting
const repo = await github.rest.repos.get({
owner: context.repo.owner,
@@ -126,15 +82,19 @@ jobs:
const defaultBranch = repo.data.default_branch;
console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- // Step 2: Fetch commits for each selected branch
- console.log('*** STEP 2: FETCHING COMMITS FROM SELECTED BRANCHES ***');
+ // Step 2: Fetch commits for ALL branches
+ console.log('*** STEP 2: FETCHING COMMITS FROM ALL BRANCHES ***');
const allCommitShas = new Set();
const allAuthors = new Set();
let mainBranchCommits = [];
+ let branchesProcessed = 0;
- for (const branch of sortedBranches) {
+ for (const branch of branches) {
try {
- console.log(`*** Fetching commits from branch: ${branch.name} ***`);
+ branchesProcessed++;
+ if (branchesProcessed % 10 === 0) {
+ console.log(`*** Processing branch ${branchesProcessed}/${branches.length}: ${branch.name} ***`);
+ }
const branchCommits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
@@ -145,7 +105,9 @@ jobs:
per_page: 100,
});
- console.log(`*** Found ${branchCommits.length} commits on ${branch.name} ***`);
+ if (branchCommits.length > 0) {
+ console.log(`*** Found ${branchCommits.length} commits on ${branch.name} ***`);
+ }
// Add to our sets (automatically handles duplicates)
branchCommits.forEach(commit => {
@@ -164,11 +126,12 @@ jobs:
console.log(`*** Error fetching commits from ${branch.name}: ${error.message} ***`);
}
- // Add delay to avoid rate limiting
- await new Promise(resolve => setTimeout(resolve, 200));
+ // Small delay to be respectful of rate limits
+ await new Promise(resolve => setTimeout(resolve, 50));
}
- console.log(`*** TOTAL UNIQUE COMMITS ACROSS SELECTED BRANCHES: ${allCommitShas.size} ***`);
+ console.log(`*** PROCESSED ${branchesProcessed} BRANCHES ***`);
+ console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitShas.size} ***`);
console.log(`*** COMMITS ON MAIN BRANCH: ${mainBranchCommits.length} ***`);
console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
@@ -206,11 +169,11 @@ jobs:
netLines: totalAdditions - totalDeletions,
};
- console.log('*** FINAL RESULT (SORTED BY RECENCY) ***', JSON.stringify(result, null, 2));
+ console.log('*** FINAL RESULT (ALL BRANCHES PROCESSED) ***', JSON.stringify(result, null, 2));
return result;
} catch (error) {
- console.log('*** ERROR WITH SORTED BRANCH APPROACH ***', error.message);
+ console.log('*** ERROR PROCESSING ALL BRANCHES ***', error.message);
return {
totalCommitsAllBranches: 0,
totalCommitsMain: 0,
From d689e8282c68df29aac5a3b08009cc291722052b Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:28:02 +0200
Subject: [PATCH 087/125] just2 months
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 9127ba27ad..54a5571b0c 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -337,7 +337,7 @@ jobs:
const monthlyDataArray = [];
const now = new Date();
- for (let i = 12; i >= 1; i--) {
+ for (let i = 2; i >= 1; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
From d26789a472be9bda5fd0e75d91d3594e5b75a78f Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 16:36:02 +0200
Subject: [PATCH 088/125] Optimize commit statistics retrieval and enhance SVG
chart generation with month labels
---
.github/workflows/gh_statistics_bot.yml | 125 +++++++++++++++++-------
1 file changed, 89 insertions(+), 36 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 54a5571b0c..da8d7c4fa1 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -59,9 +59,9 @@ jobs:
}
}
- // Get commit statistics using GitHub's recommended approach
+ // Get commit statistics using GitHub's recommended approach - optimized
async function getMonthCommitStats(since, until) {
- console.log(`=== GETTING ALL COMMITS FROM ALL BRANCHES ${since} TO ${until} ===`);
+ console.log(`=== GETTING COMMITS FROM ACTIVE BRANCHES ${since} TO ${until} ===`);
try {
// Step 1: Get all branches in the repository
@@ -82,32 +82,66 @@ jobs:
const defaultBranch = repo.data.default_branch;
console.log(`*** DEFAULT BRANCH: ${defaultBranch} ***`);
- // Step 2: Fetch commits for ALL branches
- console.log('*** STEP 2: FETCHING COMMITS FROM ALL BRANCHES ***');
+ // Step 2: Check each branch for commits in the date range (with early exit)
+ console.log('*** STEP 2: FINDING BRANCHES WITH COMMITS IN DATE RANGE ***');
+ const activeBranches = [];
+ let branchesChecked = 0;
+
+ for (const branch of branches) {
+ try {
+ branchesChecked++;
+ if (branchesChecked % 20 === 0) {
+ console.log(`*** Checked ${branchesChecked}/${branches.length} branches ***`);
+ }
+
+ // Get just the first page to check if branch has commits in date range
+ const branchCommits = await github.rest.repos.listCommits({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: branch.name,
+ since,
+ until,
+ per_page: 1, // Just check if any commits exist
+ });
+
+ if (branchCommits.data.length > 0) {
+ activeBranches.push({
+ branch: branch,
+ hasCommits: true
+ });
+ console.log(`*** Branch ${branch.name} has commits in date range ***`);
+ }
+
+ } catch (error) {
+ console.log(`*** Error checking ${branch.name}: ${error.message} ***`);
+ }
+
+ // Small delay to avoid rate limiting
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+
+ console.log(`*** FOUND ${activeBranches.length} ACTIVE BRANCHES WITH COMMITS ***`);
+
+ // Step 3: Get full commit data only from active branches
+ console.log('*** STEP 3: FETCHING ALL COMMITS FROM ACTIVE BRANCHES ***');
const allCommitShas = new Set();
const allAuthors = new Set();
let mainBranchCommits = [];
- let branchesProcessed = 0;
- for (const branch of branches) {
+ for (const activeBranch of activeBranches) {
try {
- branchesProcessed++;
- if (branchesProcessed % 10 === 0) {
- console.log(`*** Processing branch ${branchesProcessed}/${branches.length}: ${branch.name} ***`);
- }
+ console.log(`*** Fetching all commits from active branch: ${activeBranch.branch.name} ***`);
const branchCommits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
repo: context.repo.repo,
- sha: branch.name,
+ sha: activeBranch.branch.name,
since,
until,
per_page: 100,
});
- if (branchCommits.length > 0) {
- console.log(`*** Found ${branchCommits.length} commits on ${branch.name} ***`);
- }
+ console.log(`*** Found ${branchCommits.length} commits on ${activeBranch.branch.name} ***`);
// Add to our sets (automatically handles duplicates)
branchCommits.forEach(commit => {
@@ -118,26 +152,26 @@ jobs:
});
// Keep main branch commits separate
- if (branch.name === defaultBranch) {
+ if (activeBranch.branch.name === defaultBranch) {
mainBranchCommits = branchCommits;
}
} catch (error) {
- console.log(`*** Error fetching commits from ${branch.name}: ${error.message} ***`);
+ console.log(`*** Error fetching commits from ${activeBranch.branch.name}: ${error.message} ***`);
}
- // Small delay to be respectful of rate limits
- await new Promise(resolve => setTimeout(resolve, 50));
+ // Small delay to avoid rate limiting
+ await new Promise(resolve => setTimeout(resolve, 100));
}
- console.log(`*** PROCESSED ${branchesProcessed} BRANCHES ***`);
- console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL BRANCHES: ${allCommitShas.size} ***`);
+ console.log(`*** PROCESSED ${activeBranches.length} ACTIVE BRANCHES ***`);
+ console.log(`*** TOTAL UNIQUE COMMITS ACROSS ALL ACTIVE BRANCHES: ${allCommitShas.size} ***`);
console.log(`*** COMMITS ON MAIN BRANCH: ${mainBranchCommits.length} ***`);
console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
- // Step 3: Get detailed stats from main branch commits
- console.log('*** STEP 3: GETTING DETAILED STATS FROM MAIN BRANCH ***');
+ // Step 4: Get detailed stats from main branch commits
+ console.log('*** STEP 4: GETTING DETAILED STATS FROM MAIN BRANCH ***');
let totalAdditions = 0;
let totalDeletions = 0;
let mainFileChanges = 0;
@@ -169,11 +203,11 @@ jobs:
netLines: totalAdditions - totalDeletions,
};
- console.log('*** FINAL RESULT (ALL BRANCHES PROCESSED) ***', JSON.stringify(result, null, 2));
+ console.log('*** FINAL OPTIMIZED RESULT ***', JSON.stringify(result, null, 2));
return result;
} catch (error) {
- console.log('*** ERROR PROCESSING ALL BRANCHES ***', error.message);
+ console.log('*** ERROR WITH OPTIMIZED APPROACH ***', error.message);
return {
totalCommitsAllBranches: 0,
totalCommitsMain: 0,
@@ -215,13 +249,13 @@ jobs:
};
}
- // Create professional SVG charts
- function createSVGChart(data, title, color = '#22c55e') {
+ // Create professional SVG charts with month labels
+ function createSVGChart(data, title, color = '#22c55e', monthLabels = []) {
const width = 600;
const height = 200;
const padding = 40;
const chartWidth = width - 2 * padding;
- const chartHeight = height - 2 * padding;
+ const chartHeight = height - 2 * padding - 20; // Extra space for labels
const maxValue = Math.max(...data);
const minValue = Math.min(...data);
@@ -235,6 +269,16 @@ jobs:
const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
+ // Create month labels (every 3rd month)
+ const monthLabelElements = monthLabels.map((month, index) => {
+ if (index % 3 === 0) { // Show every 3rd month
+ const x = padding + (index / (data.length - 1)) * chartWidth;
+ const y = padding + chartHeight + 15;
+ return `${month}`;
+ }
+ return '';
+ }).join('');
+
return ``;
}
- // Commit charts to repository
+ // Commit charts to repository and create PR
async function commitChartsToRepo(monthlyData, targetBranch) {
const totalCommits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || d.stats?.totalCommits || 0);
const mergedPRs = monthlyData.map(d => d.prStats?.merged || 0);
@@ -326,13 +341,51 @@ jobs:
const linesAddedSvg = createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels);
const activeContributorsSvg = createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels);
+ // Create a new branch for the PR
+ const now = new Date();
+ const prBranchName = `update-stats-charts-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
+
+ console.log(`*** CREATING NEW BRANCH: ${prBranchName} ***`);
+
+ try {
+ // Get the default branch reference
+ const defaultBranch = await github.rest.repos.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const mainBranchRef = await github.rest.git.getRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: `heads/${defaultBranch.data.default_branch}`,
+ });
+
+ // Create new branch
+ await github.rest.git.createRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: `refs/heads/${prBranchName}`,
+ sha: mainBranchRef.data.object.sha,
+ });
+
+ console.log(`*** CREATED BRANCH: ${prBranchName} ***`);
+
+ } catch (error) {
+ if (error.message.includes('Reference already exists')) {
+ console.log(`*** BRANCH ${prBranchName} ALREADY EXISTS, USING EXISTING BRANCH ***`);
+ } else {
+ throw error;
+ }
+ }
+
const chartFiles = [
- { path: 'docs/assets/total-commits-chart.svg', content: totalCommitsSvg },
- { path: 'docs/assets/merged-prs-chart.svg', content: mergedPRsSvg },
- { path: 'docs/assets/lines-added-chart.svg', content: linesAddedSvg },
- { path: 'docs/assets/active-contributors-chart.svg', content: activeContributorsSvg }
+ { path: 'docs/stats/total-commits-chart.svg', content: totalCommitsSvg },
+ { path: 'docs/stats/merged-prs-chart.svg', content: mergedPRsSvg },
+ { path: 'docs/stats/lines-added-chart.svg', content: linesAddedSvg },
+ { path: 'docs/stats/active-contributors-chart.svg', content: activeContributorsSvg }
];
+ // Commit files to the new branch
for (const file of chartFiles) {
try {
const base64Content = Buffer.from(file.content).toString('base64');
@@ -344,7 +397,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
path: file.path,
- ref: targetBranch
+ ref: prBranchName
});
existingFileSha = existingFile.data.sha;
} catch (error) {
@@ -357,7 +410,7 @@ jobs:
path: file.path,
message: `Update repository activity chart: ${file.path}`,
content: base64Content,
- branch: targetBranch
+ branch: prBranchName
};
if (existingFileSha) {
@@ -370,6 +423,53 @@ jobs:
console.log(`Error updating ${file.path}:`, error.message);
}
}
+
+ // Create the PR
+ const latestMonth = monthlyData[monthlyData.length - 1];
+ const prTitle = `📊 Update Repository Statistics Charts - ${latestMonth.month}`;
+ const prBody = `## 📊 Monthly Repository Statistics Update
+
+ This PR automatically updates our repository activity charts with the latest data for **${latestMonth.month}**.
+
+ ### 📈 Updated Charts
+ - 🔵 **Total Commits (All Branches):** ${latestMonth.stats?.totalCommitsAllBranches || latestMonth.stats?.totalCommits || 0} commits
+ - 🟢 **Merged Pull Requests:** ${latestMonth.prStats?.merged || 0} PRs
+ - 🟣 **Total Lines Added:** ${(latestMonth.stats?.linesAdded || 0).toLocaleString()} lines
+ - 🟠 **Active Contributors:** ${latestMonth.stats?.totalAuthors || latestMonth.stats?.uniqueContributors || 0} developers
+
+ ### 📁 Files Updated
+ - \`docs/stats/total-commits-chart.svg\`
+ - \`docs/stats/merged-prs-chart.svg\`
+ - \`docs/stats/lines-added-chart.svg\`
+ - \`docs/stats/active-contributors-chart.svg\`
+
+ ### 🤖 About
+ This PR was automatically generated by our monthly statistics bot. The charts show 12 months of repository activity trends and can be embedded in documentation to showcase project health and development velocity.
+
+ ---
+ *Generated automatically on ${new Date().toLocaleDateString()}*`;
+
+ try {
+ const pullRequest = await github.rest.pulls.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title: prTitle,
+ head: prBranchName,
+ base: defaultBranch.data.default_branch,
+ body: prBody,
+ });
+
+ console.log(`*** CREATED PR: ${pullRequest.data.html_url} ***`);
+ return pullRequest.data.html_url;
+
+ } catch (error) {
+ if (error.message.includes('A pull request already exists')) {
+ console.log(`*** PR ALREADY EXISTS FOR BRANCH ${prBranchName} ***`);
+ return `PR already exists for ${prBranchName}`;
+ } else {
+ throw error;
+ }
+ }
}
// Main execution
@@ -418,9 +518,9 @@ jobs:
await new Promise(resolve => setTimeout(resolve, 500));
}
- // Create and commit SVG charts
+ // Create and commit SVG charts, then create PR
console.log('Generating and committing SVG charts...');
- await commitChartsToRepo(monthlyDataArray, targetBranch);
+ const prUrl = await commitChartsToRepo(monthlyDataArray, targetBranch);
// Get language stats
const languageStats = await getLanguageStats();
@@ -429,7 +529,7 @@ jobs:
const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
console.log(`*** LATEST MONTH FOR MATTERMOST ***`, JSON.stringify(latestMonthData, null, 2));
- // Post to Mattermost
+ // Post to Mattermost with PR link
const mattermostPayload = {
text: `📊 **${latestMonthData.month} Repository Activity**`,
attachments: [{
@@ -446,8 +546,9 @@ jobs:
`**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
+ `**📊 Charts Updated:** ${typeof prUrl === 'string' && prUrl.startsWith('http') ? `[View PR](${prUrl})` : 'Charts updated in docs/stats/'}\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
- footer: 'Charts updated in docs/assets/ for documentation'
+ footer: 'Monthly statistics generated automatically'
}]
};
diff --git a/docs/assets/commits-chart.svg b/docs/assets/commits-chart.svg
deleted file mode 100644
index 92eee660f7..0000000000
--- a/docs/assets/commits-chart.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Monthly Commits
- 297
- 56
-
\ No newline at end of file
diff --git a/docs/assets/lines-chart.svg b/docs/assets/lines-chart.svg
deleted file mode 100644
index 04394c8046..0000000000
--- a/docs/assets/lines-chart.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Lines Added per Month
- 26179
- 210
-
\ No newline at end of file
diff --git a/docs/assets/prs-chart.svg b/docs/assets/prs-chart.svg
deleted file mode 100644
index 7c59bf153f..0000000000
--- a/docs/assets/prs-chart.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Pull Requests Opened
- 15
- 2
-
\ No newline at end of file
From 755b58725c9eee71e9b3b69ee82f65c5c7541dd0 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 17:40:37 +0200
Subject: [PATCH 094/125] Enhance author extraction in monthly statistics
report to include co-authors from commit messages and GitHub API
---
.github/workflows/gh_statistics_bot.yml | 80 ++++++++++++++++++++++---
1 file changed, 73 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 5eeec70ba3..aecf3ca4f9 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -59,7 +59,47 @@ jobs:
}
}
- // Cache for branch activity to avoid repeated API calls
+ // Extract all authors including co-authors from commit
+ function extractAllAuthors(commit, commitDetail = null) {
+ const authors = new Set();
+
+ // Add main commit author
+ if (commit.author && commit.author.login) {
+ authors.add(commit.author.login);
+ }
+
+ // Extract co-authors from commit message
+ if (commitDetail && commitDetail.commit && commitDetail.commit.message) {
+ const message = commitDetail.commit.message;
+ const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
+ let match;
+
+ while ((match = coAuthorRegex.exec(message)) !== null) {
+ const coAuthorName = match[1].trim();
+ const coAuthorEmail = match[2].trim();
+
+ // Try to match email to GitHub username (this is best effort)
+ // GitHub usually shows co-authors in the commit.author field, but let's also parse manually
+ authors.add(coAuthorName);
+ console.log(`Found co-author: ${coAuthorName} <${coAuthorEmail}>`);
+ }
+ }
+
+ // Also check GitHub's co-author API data if available
+ if (commitDetail && commitDetail.author && commitDetail.author.login) {
+ authors.add(commitDetail.author.login);
+ }
+
+ // Check for multiple authors in the commit data
+ if (commitDetail && commitDetail.commit && commitDetail.commit.author) {
+ // Sometimes GitHub includes co-authors in different fields
+ if (commitDetail.commit.author.name && commitDetail.commit.author.email) {
+ // This is best effort - we can't always map email to username without additional API calls
+ }
+ }
+
+ return Array.from(authors);
+ }
let branchActivityCache = null;
// Get active branches for the entire year (cache this)
@@ -161,13 +201,37 @@ jobs:
console.log(`*** Found ${branchCommits.length} commits on ${branch.name} for this month ***`);
}
- // Add to our sets (automatically handles duplicates)
- branchCommits.forEach(commit => {
+ // Process each commit to extract all authors including co-authors
+ for (const commit of branchCommits) {
allCommitShas.add(commit.sha);
+
+ // Add basic author first
if (commit.author && commit.author.login) {
allAuthors.add(commit.author.login);
}
- });
+
+ // For a subset of commits, get detailed info to extract co-authors
+ if (branchCommits.indexOf(commit) < 10) { // Limit to avoid rate limits
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ // Extract all authors including co-authors
+ const allCommitAuthors = extractAllAuthors(commit, commitDetail.data);
+ allCommitAuthors.forEach(author => {
+ if (author && author.trim()) {
+ allAuthors.add(author.trim());
+ }
+ });
+
+ } catch (error) {
+ console.log(`Error getting detailed commit info: ${error.message}`);
+ }
+ }
+ }
// Keep main branch commits separate
if (branch.name === defaultBranch) {
@@ -349,15 +413,17 @@ jobs:
try {
// Get the default branch reference
- const defaultBranch = await github.rest.repos.get({
+ const repoInfo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
+ const defaultBranchName = repoInfo.data.default_branch;
+
const mainBranchRef = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
- ref: `heads/${defaultBranch.data.default_branch}`,
+ ref: `heads/${defaultBranchName}`,
});
// Create new branch
@@ -455,7 +521,7 @@ jobs:
repo: context.repo.repo,
title: prTitle,
head: prBranchName,
- base: defaultBranch.data.default_branch,
+ base: defaultBranchName,
body: prBody,
});
From 2867253f48c8fac4f5c31d745238f556efbd0b61 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 19:42:14 +0200
Subject: [PATCH 095/125] remove ascii
---
.github/workflows/gh_statistics_bot.yml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index aecf3ca4f9..6894c4cb2e 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -27,7 +27,6 @@ jobs:
- name: Install dependencies
run: |
npm init -y
- npm install asciichart
- name: Generate Monthly Statistics Report with Charts
uses: actions/github-script@v7
@@ -35,8 +34,6 @@ jobs:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- const asciichart = require('asciichart');
-
// Get language statistics
async function getLanguageStats() {
try {
From e40b40fcdaeb35a266ef6928dbcc5598fedc3ecd Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 19:49:08 +0200
Subject: [PATCH 096/125] test
---
.github/workflows/gh_statistics_bot.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 6894c4cb2e..aecf3ca4f9 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -27,6 +27,7 @@ jobs:
- name: Install dependencies
run: |
npm init -y
+ npm install asciichart
- name: Generate Monthly Statistics Report with Charts
uses: actions/github-script@v7
@@ -34,6 +35,8 @@ jobs:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
+ const asciichart = require('asciichart');
+
// Get language statistics
async function getLanguageStats() {
try {
From e2790ad3d9c81ffd4c9ae215dd3c76dd4c9789dc Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 20:18:25 +0200
Subject: [PATCH 097/125] simplify
---
.github/workflows/gh_statistics_bot.yml | 623 ++++++++----------------
1 file changed, 200 insertions(+), 423 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index aecf3ca4f9..eb708961ab 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -1,4 +1,4 @@
-name: Monthly Repository Statistics with Charts
+name: Monthly Repository Statistics
on:
pull_request:
@@ -19,290 +19,127 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '18'
-
- - name: Install dependencies
- run: |
- npm init -y
- npm install asciichart
-
- - name: Generate Monthly Statistics Report with Charts
+ - name: Generate Simple Monthly Statistics
uses: actions/github-script@v7
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- const asciichart = require('asciichart');
-
- // Get language statistics
- async function getLanguageStats() {
- try {
- const languages = await github.rest.repos.listLanguages({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
- return Object.entries(languages.data)
- .map(([lang, bytes]) => ({
- language: lang,
- percentage: ((bytes / total) * 100).toFixed(1)
- }))
- .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
- .slice(0, 3);
- } catch (error) {
- console.log('Error getting language stats:', error.message);
- return [];
- }
- }
-
- // Extract all authors including co-authors from commit
- function extractAllAuthors(commit, commitDetail = null) {
- const authors = new Set();
-
- // Add main commit author
- if (commit.author && commit.author.login) {
- authors.add(commit.author.login);
- }
-
- // Extract co-authors from commit message
- if (commitDetail && commitDetail.commit && commitDetail.commit.message) {
- const message = commitDetail.commit.message;
- const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
- let match;
-
- while ((match = coAuthorRegex.exec(message)) !== null) {
- const coAuthorName = match[1].trim();
- const coAuthorEmail = match[2].trim();
-
- // Try to match email to GitHub username (this is best effort)
- // GitHub usually shows co-authors in the commit.author field, but let's also parse manually
- authors.add(coAuthorName);
- console.log(`Found co-author: ${coAuthorName} <${coAuthorEmail}>`);
- }
- }
-
- // Also check GitHub's co-author API data if available
- if (commitDetail && commitDetail.author && commitDetail.author.login) {
- authors.add(commitDetail.author.login);
- }
-
- // Check for multiple authors in the commit data
- if (commitDetail && commitDetail.commit && commitDetail.commit.author) {
- // Sometimes GitHub includes co-authors in different fields
- if (commitDetail.commit.author.name && commitDetail.commit.author.email) {
- // This is best effort - we can't always map email to username without additional API calls
- }
- }
-
- return Array.from(authors);
- }
- let branchActivityCache = null;
-
- // Get active branches for the entire year (cache this)
- async function getActiveBranchesForYear() {
- if (branchActivityCache) {
- console.log('*** USING CACHED BRANCH ACTIVITY ***');
- return branchActivityCache;
- }
-
- console.log('*** BUILDING BRANCH ACTIVITY CACHE FOR FULL YEAR ***');
+ // Simple stats with minimal API calls - main branch only
+ async function getSimpleMonthStats(since, until) {
+ console.log(`Getting stats for ${since} to ${until}`);
- // Get all branches
- const branches = await github.paginate(github.rest.repos.listBranches, {
+ // Get commits from main branch only (1 API call per month)
+ const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
repo: context.repo.repo,
+ since,
+ until,
per_page: 100,
});
- console.log(`*** FOUND ${branches.length} BRANCHES ***`);
+ console.log(`Found ${commits.length} commits on main branch`);
- // Get the date range for the entire year we're analyzing
- const now = new Date();
- const yearStart = new Date(now.getFullYear(), now.getMonth() - 12, 1);
- const yearEnd = new Date();
+ // Get unique contributors including co-authors
+ const contributors = new Set();
- console.log(`*** CHECKING ACTIVITY FROM ${yearStart.toISOString()} TO ${yearEnd.toISOString()} ***`);
+ // First pass: add main commit authors
+ commits.forEach(commit => {
+ if (commit.author && commit.author.login) {
+ contributors.add(commit.author.login);
+ }
+ if (commit.committer && commit.committer.login && commit.committer.login !== commit.author?.login) {
+ contributors.add(commit.committer.login);
+ }
+ });
- const activeBranches = [];
- let branchesChecked = 0;
+ // Get detailed stats from commits and extract co-authors
+ let totalLinesAdded = 0;
+ let filesChanged = 0;
- for (const branch of branches) {
+ for (const commit of commits.slice(0, 30)) {
try {
- branchesChecked++;
- if (branchesChecked % 20 === 0) {
- console.log(`*** Checked ${branchesChecked}/${branches.length} branches for yearly activity ***`);
- }
-
- // Check if branch has ANY commits in the entire year
- const branchCommits = await github.rest.repos.listCommits({
+ const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
- sha: branch.name,
- since: yearStart.toISOString(),
- until: yearEnd.toISOString(),
- per_page: 1,
+ ref: commit.sha,
});
- if (branchCommits.data.length > 0) {
- activeBranches.push(branch);
- console.log(`*** Branch ${branch.name} has commits in the year ***`);
- }
+ totalLinesAdded += commitDetail.data.stats?.additions || 0;
+ filesChanged += commitDetail.data.files?.length || 0;
- } catch (error) {
- console.log(`*** Error checking ${branch.name}: ${error.message} ***`);
- }
-
- await new Promise(resolve => setTimeout(resolve, 50));
- }
-
- console.log(`*** FOUND ${activeBranches.length} ACTIVE BRANCHES FOR THE YEAR ***`);
- branchActivityCache = activeBranches;
- return activeBranches;
- }
-
- // Get commit statistics using cached active branches
- async function getMonthCommitStats(since, until) {
- console.log(`=== GETTING COMMITS FROM CACHED ACTIVE BRANCHES ${since} TO ${until} ===`);
-
- try {
- // Get the list of active branches for the year (cached after first call)
- const activeBranches = await getActiveBranchesForYear();
-
- // Get default branch info
- const repo = await github.rest.repos.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
- const defaultBranch = repo.data.default_branch;
-
- console.log(`*** PROCESSING ${activeBranches.length} ACTIVE BRANCHES FOR THIS MONTH ***`);
-
- const allCommitShas = new Set();
- const allAuthors = new Set();
- let mainBranchCommits = [];
-
- // Now only check the active branches for this specific month
- for (const branch of activeBranches) {
- try {
- const branchCommits = await github.paginate(github.rest.repos.listCommits, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- sha: branch.name,
- since,
- until,
- per_page: 100,
- });
+ // Extract co-authors from commit message
+ if (commitDetail.data.commit && commitDetail.data.commit.message) {
+ const message = commitDetail.data.commit.message;
- if (branchCommits.length > 0) {
- console.log(`*** Found ${branchCommits.length} commits on ${branch.name} for this month ***`);
- }
+ // Look for Co-authored-by: patterns
+ const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
+ let match;
- // Process each commit to extract all authors including co-authors
- for (const commit of branchCommits) {
- allCommitShas.add(commit.sha);
+ while ((match = coAuthorRegex.exec(message)) !== null) {
+ const coAuthorName = match[1].trim();
+ const coAuthorEmail = match[2].trim();
- // Add basic author first
- if (commit.author && commit.author.login) {
- allAuthors.add(commit.author.login);
+ // Add co-author name to contributors
+ if (coAuthorName && coAuthorName.length > 0) {
+ contributors.add(coAuthorName);
+ console.log(`Found co-author: ${coAuthorName} <${coAuthorEmail}>`);
}
-
- // For a subset of commits, get detailed info to extract co-authors
- if (branchCommits.indexOf(commit) < 10) { // Limit to avoid rate limits
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- // Extract all authors including co-authors
- const allCommitAuthors = extractAllAuthors(commit, commitDetail.data);
- allCommitAuthors.forEach(author => {
- if (author && author.trim()) {
- allAuthors.add(author.trim());
- }
- });
-
- } catch (error) {
- console.log(`Error getting detailed commit info: ${error.message}`);
- }
- }
- }
-
- // Keep main branch commits separate
- if (branch.name === defaultBranch) {
- mainBranchCommits = branchCommits;
}
-
- } catch (error) {
- console.log(`*** Error fetching commits from ${branch.name}: ${error.message} ***`);
}
- await new Promise(resolve => setTimeout(resolve, 100));
+ // Also check if GitHub detected additional authors
+ if (commitDetail.data.author && commitDetail.data.author.login) {
+ contributors.add(commitDetail.data.author.login);
+ }
+
+ } catch (error) {
+ console.log(`Error getting commit detail: ${error.message}`);
}
-
- console.log(`*** TOTAL UNIQUE COMMITS ACROSS ACTIVE BRANCHES: ${allCommitShas.size} ***`);
- console.log(`*** COMMITS ON MAIN BRANCH: ${mainBranchCommits.length} ***`);
- console.log(`*** TOTAL UNIQUE AUTHORS: ${allAuthors.size} ***`);
- console.log(`*** AUTHORS: ${Array.from(allAuthors).join(', ')} ***`);
-
- // Get detailed stats from main branch commits
- let totalAdditions = 0;
- let totalDeletions = 0;
- let mainFileChanges = 0;
-
- for (const commit of mainBranchCommits.slice(0, 50)) {
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
+ }
+
+ // For remaining commits (beyond first 30), do a lighter check for co-authors
+ for (const commit of commits.slice(30, 60)) {
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ // Only extract co-authors, skip file/line stats to save API quota
+ if (commitDetail.data.commit && commitDetail.data.commit.message) {
+ const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
+ let match;
- mainFileChanges += commitDetail.data.files?.length || 0;
- totalAdditions += commitDetail.data.stats?.additions || 0;
- totalDeletions += commitDetail.data.stats?.deletions || 0;
- } catch (error) {
- console.log(`Error getting commit detail: ${error.message}`);
+ while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
+ const coAuthorName = match[1].trim();
+ if (coAuthorName && coAuthorName.length > 0) {
+ contributors.add(coAuthorName);
+ console.log(`Found co-author in commit ${commit.sha.substring(0, 7)}: ${coAuthorName}`);
+ }
+ }
}
+ } catch (error) {
+ console.log(`Error checking co-authors for commit ${commit.sha}: ${error.message}`);
}
-
- const result = {
- totalCommitsAllBranches: allCommitShas.size,
- totalCommitsMain: mainBranchCommits.length,
- totalAuthors: allAuthors.size,
- authorsList: Array.from(allAuthors),
- filesChangedMain: mainFileChanges,
- linesAdded: totalAdditions,
- linesRemoved: totalDeletions,
- netLines: totalAdditions - totalDeletions,
- };
-
- console.log('*** CACHED OPTIMIZED RESULT ***', JSON.stringify(result, null, 2));
- return result;
-
- } catch (error) {
- console.log('*** ERROR WITH CACHED APPROACH ***', error.message);
- return {
- totalCommitsAllBranches: 0,
- totalCommitsMain: 0,
- totalAuthors: 0,
- authorsList: [],
- filesChangedMain: 0,
- linesAdded: 0,
- linesRemoved: 0,
- netLines: 0,
- };
}
+
+ console.log(`Total unique contributors (including co-authors): ${contributors.size}`);
+ console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
+
+ return {
+ totalCommits: commits.length,
+ activeContributors: contributors.size,
+ linesAdded: totalLinesAdded,
+ filesChanged: filesChanged,
+ contributorsList: Array.from(contributors)
+ };
}
- // Get pull request statistics
- async function getMonthPRStats(since, until) {
- const allPRs = await github.paginate(github.rest.pulls.list, {
+ // Get PR stats (1 API call per month)
+ async function getPRStats(since, until) {
+ const prs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'all',
@@ -311,30 +148,43 @@ jobs:
per_page: 100,
});
- const mergedPRs = allPRs.filter(pr =>
+ const mergedPRs = prs.filter(pr =>
pr.merged_at &&
new Date(pr.merged_at) >= new Date(since) &&
new Date(pr.merged_at) <= new Date(until)
);
- const openedPRs = allPRs.filter(pr =>
- new Date(pr.created_at) >= new Date(since) &&
- new Date(pr.created_at) <= new Date(until)
- );
+ return { merged: mergedPRs.length };
+ }
- return {
- merged: mergedPRs.length,
- opened: openedPRs.length,
- };
+ // Get languages (1 API call total)
+ async function getLanguageStats() {
+ try {
+ const languages = await github.rest.repos.listLanguages({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const total = Object.values(languages.data).reduce((sum, bytes) => sum + bytes, 0);
+ return Object.entries(languages.data)
+ .map(([lang, bytes]) => ({
+ language: lang,
+ percentage: ((bytes / total) * 100).toFixed(1)
+ }))
+ .sort((a, b) => parseFloat(b.percentage) - parseFloat(a.percentage))
+ .slice(0, 3);
+ } catch (error) {
+ return [];
+ }
}
- // Create professional SVG charts with month labels
- function createSVGChart(data, title, color = '#22c55e', monthLabels = []) {
+ // Create SVG chart
+ function createSVGChart(data, title, color, monthLabels) {
const width = 600;
const height = 200;
const padding = 40;
const chartWidth = width - 2 * padding;
- const chartHeight = height - 2 * padding - 20; // Extra space for labels
+ const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
const minValue = Math.min(...data);
@@ -348,9 +198,8 @@ jobs:
const areaPoints = `M${padding},${padding + chartHeight} L${points} L${padding + chartWidth},${padding + chartHeight} Z`;
- // Create month labels (every 3rd month)
const monthLabelElements = monthLabels.map((month, index) => {
- if (index % 3 === 0) { // Show every 3rd month
+ if (index % 3 === 0) {
const x = padding + (index / (data.length - 1)) * chartWidth;
const y = padding + chartHeight + 15;
return `${month}`;
@@ -381,7 +230,6 @@ jobs:
return ``;
}).join('')}
-
${monthLabelElements}
${title}
@@ -390,249 +238,178 @@ jobs:
`;
}
- // Commit charts to repository and create PR
- async function commitChartsToRepo(monthlyData, targetBranch) {
- const totalCommits = monthlyData.map(d => d.stats?.totalCommitsAllBranches || d.stats?.totalCommits || 0);
- const mergedPRs = monthlyData.map(d => d.prStats?.merged || 0);
- const linesAdded = monthlyData.map(d => d.stats?.linesAdded || 0);
- const activeContributors = monthlyData.map(d => d.stats?.totalAuthors || d.stats?.uniqueContributors || 0);
-
- // Extract month labels from the data
+ // Create PR and commit charts
+ async function createChartsAndPR(monthlyData) {
+ const totalCommits = monthlyData.map(d => d.stats.totalCommits);
+ const mergedPRs = monthlyData.map(d => d.prStats.merged);
+ const linesAdded = monthlyData.map(d => d.stats.linesAdded);
+ const activeContributors = monthlyData.map(d => d.stats.activeContributors);
const monthLabels = monthlyData.map(d => d.month);
- const totalCommitsSvg = createSVGChart(totalCommits, 'Total Commits (All Branches)', '#3b82f6', monthLabels);
- const mergedPRsSvg = createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels);
- const linesAddedSvg = createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels);
- const activeContributorsSvg = createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels);
+ // Create the 4 required charts
+ const charts = [
+ { path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
+ { path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
+ { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
+ { path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
+ ];
- // Create a new branch for the PR
+ // Create branch for PR
const now = new Date();
- const prBranchName = `update-stats-charts-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
-
- console.log(`*** CREATING NEW BRANCH: ${prBranchName} ***`);
+ const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
try {
- // Get the default branch reference
- const repoInfo = await github.rest.repos.get({
+ const repo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
- const defaultBranchName = repoInfo.data.default_branch;
-
- const mainBranchRef = await github.rest.git.getRef({
+ const mainRef = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
- ref: `heads/${defaultBranchName}`,
+ ref: `heads/${repo.data.default_branch}`,
});
- // Create new branch
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
- ref: `refs/heads/${prBranchName}`,
- sha: mainBranchRef.data.object.sha,
+ ref: `refs/heads/${branchName}`,
+ sha: mainRef.data.object.sha,
});
- console.log(`*** CREATED BRANCH: ${prBranchName} ***`);
-
+ console.log(`Created branch: ${branchName}`);
} catch (error) {
- if (error.message.includes('Reference already exists')) {
- console.log(`*** BRANCH ${prBranchName} ALREADY EXISTS, USING EXISTING BRANCH ***`);
- } else {
+ if (!error.message.includes('Reference already exists')) {
throw error;
}
}
- const chartFiles = [
- { path: 'docs/stats/total-commits-chart.svg', content: totalCommitsSvg },
- { path: 'docs/stats/merged-prs-chart.svg', content: mergedPRsSvg },
- { path: 'docs/stats/lines-added-chart.svg', content: linesAddedSvg },
- { path: 'docs/stats/active-contributors-chart.svg', content: activeContributorsSvg }
- ];
-
- // Commit files to the new branch
- for (const file of chartFiles) {
+ // Commit charts
+ for (const chart of charts) {
try {
- const base64Content = Buffer.from(file.content).toString('base64');
-
- // Try to get existing file SHA
let existingFileSha = null;
try {
- const existingFile = await github.rest.repos.getContent({
+ const existing = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
- path: file.path,
- ref: prBranchName
+ path: chart.path,
+ ref: branchName
});
- existingFileSha = existingFile.data.sha;
- } catch (error) {
- console.log(`File ${file.path} doesn't exist yet, creating new file`);
- }
+ existingFileSha = existing.data.sha;
+ } catch (e) {}
const params = {
owner: context.repo.owner,
repo: context.repo.repo,
- path: file.path,
- message: `Update repository activity chart: ${file.path}`,
- content: base64Content,
- branch: prBranchName
+ path: chart.path,
+ message: `Update ${chart.path}`,
+ content: Buffer.from(chart.content).toString('base64'),
+ branch: branchName
};
- if (existingFileSha) {
- params.sha = existingFileSha;
- }
+ if (existingFileSha) params.sha = existingFileSha;
await github.rest.repos.createOrUpdateFileContents(params);
- console.log(`Updated chart: ${file.path}`);
+ console.log(`Updated: ${chart.path}`);
} catch (error) {
- console.log(`Error updating ${file.path}:`, error.message);
+ console.log(`Error updating ${chart.path}: ${error.message}`);
}
}
- // Create the PR
+ // Create PR
const latestMonth = monthlyData[monthlyData.length - 1];
- const prTitle = `📊 Update Repository Statistics Charts - ${latestMonth.month}`;
- const prBody = `## 📊 Monthly Repository Statistics Update
-
- This PR automatically updates our repository activity charts with the latest data for **${latestMonth.month}**.
-
- ### 📈 Updated Charts
- - 🔵 **Total Commits (All Branches):** ${latestMonth.stats?.totalCommitsAllBranches || latestMonth.stats?.totalCommits || 0} commits
- - 🟢 **Merged Pull Requests:** ${latestMonth.prStats?.merged || 0} PRs
- - 🟣 **Total Lines Added:** ${(latestMonth.stats?.linesAdded || 0).toLocaleString()} lines
- - 🟠 **Active Contributors:** ${latestMonth.stats?.totalAuthors || latestMonth.stats?.uniqueContributors || 0} developers
-
- ### 📁 Files Updated
- - \`docs/stats/total-commits-chart.svg\`
- - \`docs/stats/merged-prs-chart.svg\`
- - \`docs/stats/lines-added-chart.svg\`
- - \`docs/stats/active-contributors-chart.svg\`
-
- ### 🤖 About
- This PR was automatically generated by our monthly statistics bot. The charts show 12 months of repository activity trends and can be embedded in documentation to showcase project health and development velocity.
-
- ---
- *Generated automatically on ${new Date().toLocaleDateString()}*`;
-
try {
- const pullRequest = await github.rest.pulls.create({
+ const pr = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
- title: prTitle,
- head: prBranchName,
- base: defaultBranchName,
- body: prBody,
+ title: `📊 Update Repository Statistics - ${latestMonth.month}`,
+ head: branchName,
+ base: repo.data.default_branch,
+ body: `## Repository Activity Charts Updated
+
+ **${latestMonth.month} Summary:**
+ - 📝 Total Commits: ${latestMonth.stats.totalCommits}
+ - 🔀 Merged PRs: ${latestMonth.prStats.merged}
+ - 📈 Lines Added: ${latestMonth.stats.linesAdded.toLocaleString()}
+ - 👥 Active Contributors: ${latestMonth.stats.activeContributors}
+
+ Updated charts in \`docs/stats/\` showing 12 months of repository activity.`
});
- console.log(`*** CREATED PR: ${pullRequest.data.html_url} ***`);
- return pullRequest.data.html_url;
-
+ return pr.data.html_url;
} catch (error) {
if (error.message.includes('A pull request already exists')) {
- console.log(`*** PR ALREADY EXISTS FOR BRANCH ${prBranchName} ***`);
- return `PR already exists for ${prBranchName}`;
- } else {
- throw error;
+ return 'PR already exists';
}
+ throw error;
}
}
// Main execution
try {
- console.log('*** STARTING MONTHLY REPORT GENERATION ***');
+ console.log('Starting simple monthly report generation...');
- // Get current branch
- let targetBranch;
- if (context.eventName === 'pull_request') {
- targetBranch = context.payload.pull_request.head.ref;
- console.log(`Using PR branch: ${targetBranch}`);
- } else {
- targetBranch = context.ref.replace('refs/heads/', '') || 'main';
- console.log(`Using branch: ${targetBranch}`);
- }
-
- // Gather data for last 12 months
- const monthlyDataArray = [];
+ const monthlyData = [];
const now = new Date();
+ // Get last 12 months of data (simple approach)
for (let i = 12; i >= 1; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
- console.log(`Month ${i}: ${monthStart.toLocaleDateString()} to ${monthEnd.toLocaleDateString()}`);
+ const since = monthStart.toISOString();
+ const until = monthEnd.toISOString();
+ const monthName = monthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
- const monthRange = {
- start: monthStart.toISOString(),
- end: monthEnd.toISOString(),
- monthName: monthStart.toLocaleString('default', { month: 'short', year: 'numeric' })
- };
-
- console.log(`Processing ${monthRange.monthName} (${monthRange.start} to ${monthRange.end})...`);
+ console.log(`Processing ${monthName}...`);
const [stats, prStats] = await Promise.all([
- getMonthCommitStats(monthRange.start, monthRange.end),
- getMonthPRStats(monthRange.start, monthRange.end)
+ getSimpleMonthStats(since, until),
+ getPRStats(since, until)
]);
-
- monthlyDataArray.push({
- month: monthRange.monthName,
- stats,
- prStats
- });
-
- await new Promise(resolve => setTimeout(resolve, 500));
+
+ monthlyData.push({ month: monthName, stats, prStats });
}
-
- // Create and commit SVG charts, then create PR
- console.log('Generating and committing SVG charts...');
- const prUrl = await commitChartsToRepo(monthlyDataArray, targetBranch);
-
- // Get language stats
- const languageStats = await getLanguageStats();
-
- // Use latest month for Mattermost
- const latestMonthData = monthlyDataArray[monthlyDataArray.length - 1];
- console.log(`*** LATEST MONTH FOR MATTERMOST ***`, JSON.stringify(latestMonthData, null, 2));
-
- // Post to Mattermost with PR link
+
+ // Create charts and PR
+ const prUrl = await createChartsAndPR(monthlyData);
+
+ // Get languages
+ const languages = await getLanguageStats();
+
+ // Post to Mattermost
+ const latest = monthlyData[monthlyData.length - 1];
const mattermostPayload = {
- text: `📊 **${latestMonthData.month} Repository Activity**`,
+ text: `📊 **${latest.month} Repository Activity**`,
attachments: [{
color: '#22c55e',
fields: [
- { title: '📝 Commits to Main', value: (latestMonthData.stats?.totalCommitsMain || 0).toString(), short: true },
- { title: '🌿 All Branch Commits', value: (latestMonthData.stats?.totalCommitsAllBranches || 0).toString(), short: true },
- { title: '📁 Files Changed', value: (latestMonthData.stats?.filesChangedMain || 0).toString(), short: true },
- { title: '🔀 PRs Merged', value: (latestMonthData.prStats?.merged || 0).toString(), short: true },
- { title: '🆕 PRs Opened', value: (latestMonthData.prStats?.opened || 0).toString(), short: true },
- { title: '👥 Authors', value: (latestMonthData.stats?.totalAuthors || 0).toString(), short: true }
+ { title: '📝 Total Commits', value: latest.stats.totalCommits.toString(), short: true },
+ { title: '🔀 Merged PRs', value: latest.prStats.merged.toString(), short: true },
+ { title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
+ { title: '👥 Contributors', value: latest.stats.activeContributors.toString(), short: true }
],
- text: `**Code Changes:** +${(latestMonthData.stats?.linesAdded || 0).toLocaleString()} / -${(latestMonthData.stats?.linesRemoved || 0).toLocaleString()} lines\n` +
- `**Top Contributors:** ${(latestMonthData.stats?.authorsList || []).slice(0, 3).join(', ') || 'None'}\n` +
- `**Languages:** ${(languageStats || []).map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `*Excluding merges, ${latestMonthData.stats?.totalAuthors || 0} authors pushed ${latestMonthData.stats?.totalCommitsMain || 0} commits to main and ${latestMonthData.stats?.totalCommitsAllBranches || 0} commits to all branches*\n\n` +
- `**📊 Charts Updated:** ${typeof prUrl === 'string' && prUrl.startsWith('http') ? `[View PR](${prUrl})` : 'Charts updated in docs/stats/'}\n` +
+ text: `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
+ `**Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
+ `**Charts:** ${typeof prUrl === 'string' && prUrl.startsWith('http') ? `[View PR](${prUrl})` : 'Updated in docs/stats/'}\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
- footer: 'Monthly statistics generated automatically'
+ footer: 'Simple repository statistics'
}]
};
const response = await fetch(process.env.MATTERMOST_WEBHOOK_URL, {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
+ headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(mattermostPayload)
});
if (!response.ok) {
- throw new Error(`Mattermost webhook failed: ${response.status} ${response.statusText}`);
+ throw new Error(`Mattermost webhook failed: ${response.status}`);
}
- console.log('Monthly report with charts posted to Mattermost successfully');
+ console.log('Simple monthly report completed successfully');
} catch (error) {
- console.error('Error generating monthly report:', error);
- core.setFailed(`Monthly report generation failed: ${error.message}`);
+ console.error('Error:', error);
+ core.setFailed(`Report generation failed: ${error.message}`);
}
\ No newline at end of file
From 3066a6a0c7c5e0e41b91474e85e0619dedf8d51a Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 20:49:40 +0200
Subject: [PATCH 098/125] push the button
---
.github/workflows/gh_statistics_bot.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index eb708961ab..3d10d18733 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -5,7 +5,6 @@ on:
types: [opened, synchronize]
schedule:
- cron: '0 9 1 * *'
- workflow_dispatch:
jobs:
generate-monthly-report:
From c4b46deb62d6d12cc5160e57218b874735ed1a3f Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 20:54:45 +0200
Subject: [PATCH 099/125] test
---
.github/workflows/gh_statistics_bot.yml | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 3d10d18733..a5c03b0c6e 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -5,6 +5,7 @@ on:
types: [opened, synchronize]
schedule:
- cron: '0 9 1 * *'
+ workflow_dispatch:
jobs:
generate-monthly-report:
@@ -258,7 +259,7 @@ jobs:
const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
try {
- const repo = await github.rest.repos.get({
+ const repoInfo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
@@ -266,7 +267,7 @@ jobs:
const mainRef = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
- ref: `heads/${repo.data.default_branch}`,
+ ref: `heads/${repoInfo.data.default_branch}`,
});
await github.rest.git.createRef({
@@ -323,7 +324,7 @@ jobs:
repo: context.repo.repo,
title: `📊 Update Repository Statistics - ${latestMonth.month}`,
head: branchName,
- base: repo.data.default_branch,
+ base: repoInfo.data.default_branch,
body: `## Repository Activity Charts Updated
**${latestMonth.month} Summary:**
@@ -351,16 +352,18 @@ jobs:
const monthlyData = [];
const now = new Date();
- // Get last 12 months of data (simple approach)
+ // Get last 12 months of data (simple approach) - fixed date logic
for (let i = 12; i >= 1; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
+ console.log(`Month ${i}: ${monthStart.toLocaleDateString()} to ${monthEnd.toLocaleDateString()}`);
+
const since = monthStart.toISOString();
const until = monthEnd.toISOString();
const monthName = monthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
- console.log(`Processing ${monthName}...`);
+ console.log(`Processing ${monthName} (${since} to ${until})...`);
const [stats, prStats] = await Promise.all([
getSimpleMonthStats(since, until),
From 92dc179c32d11fb275d972edc03bae57350f69a9 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:00:35 +0200
Subject: [PATCH 100/125] test
---
.github/workflows/gh_statistics_bot.yml | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index a5c03b0c6e..22a8ee7fd6 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -254,16 +254,17 @@ jobs:
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
];
+ // Get repo info first
+ const repoInfo = await github.rest.repos.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
// Create branch for PR
const now = new Date();
const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
try {
- const repoInfo = await github.rest.repos.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
const mainRef = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
From 0b285ae8f0aa1998671231390e90a9d53d92f7b2 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:03:30 +0200
Subject: [PATCH 101/125] UPDATE
---
.github/workflows/gh_statistics_bot.yml | 98 ++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 22a8ee7fd6..2e1dde7421 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -13,7 +13,8 @@ jobs:
permissions:
contents: write
issues: write
- pull-requests: read
+ pull-requests: write # Added this permission
+ actions: read
steps:
- name: Checkout repository
@@ -317,6 +318,101 @@ jobs:
}
}
+ // Create PR
+ const latestMonth = monthlyData[monthlyData.length - 1];
+ try {
+ const pr = await github.rest.pulls.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title: `📊 Update Repository Statistics - ${latestMonth.month}`,
+ head: branchName,
+ base: repoInfo.data.default_branch,
+ body: `## Repository Activity Charts Updated
+
+ **${latestMonth.month} Summary:**
+ - 📝 Total Commits: ${latestMonth.stats.totalCommits}
+ - 🔀 Merged PRs: ${latestMonth.prStats.merged}
+ - 📈 Lines Added: ${latestMonth.stats.linesAdded.toLocaleString()}
+ - 👥 Active Contributors: ${latestMonth.stats.activeContributors}
+
+ Updated charts in \`docs/stats/\` showing 12 months of repository activity.`
+ });
+
+ return pr.data.html_url;
+ } catch (error) {
+ if (error.message.includes('A pull request already exists')) {
+ return 'PR already exists';
+ }
+ throw error;
+ }
+ }'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
+ { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
+ { path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
+ ];
+
+ // Get repo info first
+ const repoInfo = await github.rest.repos.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ // Create branch for PR
+ const now = new Date();
+ const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
+
+ try {
+ const mainRef = await github.rest.git.getRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: `heads/${repoInfo.data.default_branch}`,
+ });
+
+ await github.rest.git.createRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: `refs/heads/${branchName}`,
+ sha: mainRef.data.object.sha,
+ });
+
+ console.log(`Created branch: ${branchName}`);
+ } catch (error) {
+ if (!error.message.includes('Reference already exists')) {
+ throw error;
+ }
+ }
+
+ // Commit charts
+ for (const chart of charts) {
+ try {
+ let existingFileSha = null;
+ try {
+ const existing = await github.rest.repos.getContent({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: chart.path,
+ ref: branchName
+ });
+ existingFileSha = existing.data.sha;
+ } catch (e) {}
+
+ const params = {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: chart.path,
+ message: `Update ${chart.path}`,
+ content: Buffer.from(chart.content).toString('base64'),
+ branch: branchName
+ };
+
+ if (existingFileSha) params.sha = existingFileSha;
+
+ await github.rest.repos.createOrUpdateFileContents(params);
+ console.log(`Updated: ${chart.path}`);
+ } catch (error) {
+ console.log(`Error updating ${chart.path}: ${error.message}`);
+ }
+ }
+
// Create PR
const latestMonth = monthlyData[monthlyData.length - 1];
try {
From 2350a1aed9455e30c32dc767276c4d49fb16e640 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:06:06 +0200
Subject: [PATCH 102/125] now?
---
.github/workflows/gh_statistics_bot.yml | 133 +-----------------------
1 file changed, 4 insertions(+), 129 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 2e1dde7421..442a4d16a9 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -13,7 +13,7 @@ jobs:
permissions:
contents: write
issues: write
- pull-requests: write # Added this permission
+ pull-requests: write
actions: read
steps:
@@ -30,7 +30,6 @@ jobs:
async function getSimpleMonthStats(since, until) {
console.log(`Getting stats for ${since} to ${until}`);
- // Get commits from main branch only (1 API call per month)
const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
repo: context.repo.repo,
@@ -41,10 +40,7 @@ jobs:
console.log(`Found ${commits.length} commits on main branch`);
- // Get unique contributors including co-authors
const contributors = new Set();
-
- // First pass: add main commit authors
commits.forEach(commit => {
if (commit.author && commit.author.login) {
contributors.add(commit.author.login);
@@ -54,7 +50,6 @@ jobs:
}
});
- // Get detailed stats from commits and extract co-authors
let totalLinesAdded = 0;
let filesChanged = 0;
@@ -69,27 +64,20 @@ jobs:
totalLinesAdded += commitDetail.data.stats?.additions || 0;
filesChanged += commitDetail.data.files?.length || 0;
- // Extract co-authors from commit message
if (commitDetail.data.commit && commitDetail.data.commit.message) {
const message = commitDetail.data.commit.message;
-
- // Look for Co-authored-by: patterns
const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
let match;
while ((match = coAuthorRegex.exec(message)) !== null) {
const coAuthorName = match[1].trim();
- const coAuthorEmail = match[2].trim();
-
- // Add co-author name to contributors
if (coAuthorName && coAuthorName.length > 0) {
contributors.add(coAuthorName);
- console.log(`Found co-author: ${coAuthorName} <${coAuthorEmail}>`);
+ console.log(`Found co-author: ${coAuthorName}`);
}
}
}
- // Also check if GitHub detected additional authors
if (commitDetail.data.author && commitDetail.data.author.login) {
contributors.add(commitDetail.data.author.login);
}
@@ -99,7 +87,6 @@ jobs:
}
}
- // For remaining commits (beyond first 30), do a lighter check for co-authors
for (const commit of commits.slice(30, 60)) {
try {
const commitDetail = await github.rest.repos.getCommit({
@@ -108,7 +95,6 @@ jobs:
ref: commit.sha,
});
- // Only extract co-authors, skip file/line stats to save API quota
if (commitDetail.data.commit && commitDetail.data.commit.message) {
const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
let match;
@@ -117,16 +103,15 @@ jobs:
const coAuthorName = match[1].trim();
if (coAuthorName && coAuthorName.length > 0) {
contributors.add(coAuthorName);
- console.log(`Found co-author in commit ${commit.sha.substring(0, 7)}: ${coAuthorName}`);
}
}
}
} catch (error) {
- console.log(`Error checking co-authors for commit ${commit.sha}: ${error.message}`);
+ console.log(`Error checking co-authors: ${error.message}`);
}
}
- console.log(`Total unique contributors (including co-authors): ${contributors.size}`);
+ console.log(`Total contributors: ${contributors.size}`);
console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
return {
@@ -138,7 +123,6 @@ jobs:
};
}
- // Get PR stats (1 API call per month)
async function getPRStats(since, until) {
const prs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
@@ -158,7 +142,6 @@ jobs:
return { merged: mergedPRs.length };
}
- // Get languages (1 API call total)
async function getLanguageStats() {
try {
const languages = await github.rest.repos.listLanguages({
@@ -179,7 +162,6 @@ jobs:
}
}
- // Create SVG chart
function createSVGChart(data, title, color, monthLabels) {
const width = 600;
const height = 200;
@@ -239,7 +221,6 @@ jobs:
`;
}
- // Create PR and commit charts
async function createChartsAndPR(monthlyData) {
const totalCommits = monthlyData.map(d => d.stats.totalCommits);
const mergedPRs = monthlyData.map(d => d.prStats.merged);
@@ -247,7 +228,6 @@ jobs:
const activeContributors = monthlyData.map(d => d.stats.activeContributors);
const monthLabels = monthlyData.map(d => d.month);
- // Create the 4 required charts
const charts = [
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
@@ -255,13 +235,11 @@ jobs:
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
];
- // Get repo info first
const repoInfo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
- // Create branch for PR
const now = new Date();
const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
@@ -286,7 +264,6 @@ jobs:
}
}
- // Commit charts
for (const chart of charts) {
try {
let existingFileSha = null;
@@ -318,102 +295,6 @@ jobs:
}
}
- // Create PR
- const latestMonth = monthlyData[monthlyData.length - 1];
- try {
- const pr = await github.rest.pulls.create({
- owner: context.repo.owner,
- repo: context.repo.repo,
- title: `📊 Update Repository Statistics - ${latestMonth.month}`,
- head: branchName,
- base: repoInfo.data.default_branch,
- body: `## Repository Activity Charts Updated
-
- **${latestMonth.month} Summary:**
- - 📝 Total Commits: ${latestMonth.stats.totalCommits}
- - 🔀 Merged PRs: ${latestMonth.prStats.merged}
- - 📈 Lines Added: ${latestMonth.stats.linesAdded.toLocaleString()}
- - 👥 Active Contributors: ${latestMonth.stats.activeContributors}
-
- Updated charts in \`docs/stats/\` showing 12 months of repository activity.`
- });
-
- return pr.data.html_url;
- } catch (error) {
- if (error.message.includes('A pull request already exists')) {
- return 'PR already exists';
- }
- throw error;
- }
- }'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
- { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
- { path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
- ];
-
- // Get repo info first
- const repoInfo = await github.rest.repos.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- // Create branch for PR
- const now = new Date();
- const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
-
- try {
- const mainRef = await github.rest.git.getRef({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: `heads/${repoInfo.data.default_branch}`,
- });
-
- await github.rest.git.createRef({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: `refs/heads/${branchName}`,
- sha: mainRef.data.object.sha,
- });
-
- console.log(`Created branch: ${branchName}`);
- } catch (error) {
- if (!error.message.includes('Reference already exists')) {
- throw error;
- }
- }
-
- // Commit charts
- for (const chart of charts) {
- try {
- let existingFileSha = null;
- try {
- const existing = await github.rest.repos.getContent({
- owner: context.repo.owner,
- repo: context.repo.repo,
- path: chart.path,
- ref: branchName
- });
- existingFileSha = existing.data.sha;
- } catch (e) {}
-
- const params = {
- owner: context.repo.owner,
- repo: context.repo.repo,
- path: chart.path,
- message: `Update ${chart.path}`,
- content: Buffer.from(chart.content).toString('base64'),
- branch: branchName
- };
-
- if (existingFileSha) params.sha = existingFileSha;
-
- await github.rest.repos.createOrUpdateFileContents(params);
- console.log(`Updated: ${chart.path}`);
- } catch (error) {
- console.log(`Error updating ${chart.path}: ${error.message}`);
- }
- }
-
- // Create PR
const latestMonth = monthlyData[monthlyData.length - 1];
try {
const pr = await github.rest.pulls.create({
@@ -442,14 +323,12 @@ jobs:
}
}
- // Main execution
try {
console.log('Starting simple monthly report generation...');
const monthlyData = [];
const now = new Date();
- // Get last 12 months of data (simple approach) - fixed date logic
for (let i = 12; i >= 1; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
@@ -470,13 +349,9 @@ jobs:
monthlyData.push({ month: monthName, stats, prStats });
}
- // Create charts and PR
const prUrl = await createChartsAndPR(monthlyData);
-
- // Get languages
const languages = await getLanguageStats();
- // Post to Mattermost
const latest = monthlyData[monthlyData.length - 1];
const mattermostPayload = {
text: `📊 **${latest.month} Repository Activity**`,
From aacec1938322325e4bae1f79598cb9f3ec39965a Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:10:08 +0200
Subject: [PATCH 103/125] final?
---
.github/workflows/gh_statistics_bot.yml | 51 +++++++++----------------
1 file changed, 19 insertions(+), 32 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 442a4d16a9..262c610603 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -221,7 +221,7 @@ jobs:
`;
}
- async function createChartsAndPR(monthlyData) {
+ async function createChartsOnBranch(monthlyData) {
const totalCommits = monthlyData.map(d => d.stats.totalCommits);
const mergedPRs = monthlyData.map(d => d.prStats.merged);
const linesAdded = monthlyData.map(d => d.stats.linesAdded);
@@ -235,11 +235,13 @@ jobs:
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
];
+ // Get repo info
const repoInfo = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
+ // Create new branch
const now = new Date();
const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
@@ -259,11 +261,14 @@ jobs:
console.log(`Created branch: ${branchName}`);
} catch (error) {
- if (!error.message.includes('Reference already exists')) {
+ if (error.message.includes('Reference already exists')) {
+ console.log(`Branch ${branchName} already exists, using existing branch`);
+ } else {
throw error;
}
}
+ // Commit charts to the new branch only
for (const chart of charts) {
try {
let existingFileSha = null;
@@ -275,7 +280,9 @@ jobs:
ref: branchName
});
existingFileSha = existing.data.sha;
- } catch (e) {}
+ } catch (e) {
+ console.log(`File ${chart.path} doesn't exist on branch, creating new file`);
+ }
const params = {
owner: context.repo.owner,
@@ -289,38 +296,18 @@ jobs:
if (existingFileSha) params.sha = existingFileSha;
await github.rest.repos.createOrUpdateFileContents(params);
- console.log(`Updated: ${chart.path}`);
+ console.log(`Updated on branch: ${chart.path}`);
} catch (error) {
- console.log(`Error updating ${chart.path}: ${error.message}`);
+ console.log(`Error updating ${chart.path} on branch: ${error.message}`);
}
}
const latestMonth = monthlyData[monthlyData.length - 1];
- try {
- const pr = await github.rest.pulls.create({
- owner: context.repo.owner,
- repo: context.repo.repo,
- title: `📊 Update Repository Statistics - ${latestMonth.month}`,
- head: branchName,
- base: repoInfo.data.default_branch,
- body: `## Repository Activity Charts Updated
-
- **${latestMonth.month} Summary:**
- - 📝 Total Commits: ${latestMonth.stats.totalCommits}
- - 🔀 Merged PRs: ${latestMonth.prStats.merged}
- - 📈 Lines Added: ${latestMonth.stats.linesAdded.toLocaleString()}
- - 👥 Active Contributors: ${latestMonth.stats.activeContributors}
-
- Updated charts in \`docs/stats/\` showing 12 months of repository activity.`
- });
-
- return pr.data.html_url;
- } catch (error) {
- if (error.message.includes('A pull request already exists')) {
- return 'PR already exists';
- }
- throw error;
- }
+ return {
+ branchName: branchName,
+ message: `Charts updated on branch ${branchName} for ${latestMonth.month}`,
+ branchUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}`
+ };
}
try {
@@ -349,7 +336,7 @@ jobs:
monthlyData.push({ month: monthName, stats, prStats });
}
- const prUrl = await createChartsAndPR(monthlyData);
+ const branchResult = await createChartsOnBranch(monthlyData);
const languages = await getLanguageStats();
const latest = monthlyData[monthlyData.length - 1];
@@ -365,7 +352,7 @@ jobs:
],
text: `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `**Charts:** ${typeof prUrl === 'string' && prUrl.startsWith('http') ? `[View PR](${prUrl})` : 'Updated in docs/stats/'}\n` +
+ `**Charts:** [View on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
footer: 'Simple repository statistics'
}]
From 73d15ad005f2cbc146a30deb9e4176a1cb71d149 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:17:00 +0200
Subject: [PATCH 104/125] update bot
---
.github/workflows/gh_statistics_bot.yml | 174 +++++++++++++++++++-----
1 file changed, 138 insertions(+), 36 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 262c610603..3f43bbb437 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -26,9 +26,9 @@ jobs:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- // Simple stats with minimal API calls - main branch only
+ // Simple stats for historical data (main branch only)
async function getSimpleMonthStats(since, until) {
- console.log(`Getting stats for ${since} to ${until}`);
+ console.log(`Getting simple stats for ${since} to ${until}`);
const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
@@ -38,8 +38,6 @@ jobs:
per_page: 100,
});
- console.log(`Found ${commits.length} commits on main branch`);
-
const contributors = new Set();
commits.forEach(commit => {
if (commit.author && commit.author.login) {
@@ -51,6 +49,7 @@ jobs:
});
let totalLinesAdded = 0;
+ let totalLinesRemoved = 0;
let filesChanged = 0;
for (const commit of commits.slice(0, 30)) {
@@ -62,64 +61,151 @@ jobs:
});
totalLinesAdded += commitDetail.data.stats?.additions || 0;
+ totalLinesRemoved += commitDetail.data.stats?.deletions || 0;
filesChanged += commitDetail.data.files?.length || 0;
if (commitDetail.data.commit && commitDetail.data.commit.message) {
- const message = commitDetail.data.commit.message;
const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
let match;
-
- while ((match = coAuthorRegex.exec(message)) !== null) {
+ while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
const coAuthorName = match[1].trim();
if (coAuthorName && coAuthorName.length > 0) {
contributors.add(coAuthorName);
- console.log(`Found co-author: ${coAuthorName}`);
}
}
}
- if (commitDetail.data.author && commitDetail.data.author.login) {
- contributors.add(commitDetail.data.author.login);
- }
-
} catch (error) {
console.log(`Error getting commit detail: ${error.message}`);
}
}
- for (const commit of commits.slice(30, 60)) {
+ return {
+ totalCommits: commits.length,
+ activeContributors: contributors.size,
+ linesAdded: totalLinesAdded,
+ linesRemoved: totalLinesRemoved,
+ linesChanged: totalLinesAdded + totalLinesRemoved,
+ filesChanged: filesChanged,
+ contributorsList: Array.from(contributors)
+ };
+ }
+
+ // Comprehensive stats for current month (all branches)
+ async function getComprehensiveMonthStats(since, until) {
+ console.log(`Getting comprehensive stats for ${since} to ${until}`);
+
+ // Get all branches
+ const branches = await github.paginate(github.rest.repos.listBranches, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ per_page: 100,
+ });
+
+ console.log(`Found ${branches.length} branches`);
+
+ // Find active branches (have commits in date range)
+ const activeBranches = [];
+ for (const branch of branches.slice(0, 50)) { // Limit to avoid rate limits
try {
- const commitDetail = await github.rest.repos.getCommit({
+ const branchCommits = await github.rest.repos.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
- ref: commit.sha,
+ sha: branch.name,
+ since,
+ until,
+ per_page: 1,
});
- if (commitDetail.data.commit && commitDetail.data.commit.message) {
- const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
- let match;
-
- while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
- const coAuthorName = match[1].trim();
- if (coAuthorName && coAuthorName.length > 0) {
- contributors.add(coAuthorName);
+ if (branchCommits.data.length > 0) {
+ activeBranches.push(branch);
+ console.log(`Branch ${branch.name} has commits in date range`);
+ }
+ } catch (error) {
+ console.log(`Error checking branch ${branch.name}: ${error.message}`);
+ }
+ }
+
+ console.log(`Found ${activeBranches.length} active branches`);
+
+ // Get all commits from active branches
+ const allCommitShas = new Set();
+ const allAuthors = new Set();
+ let totalLinesAdded = 0;
+ let totalLinesRemoved = 0;
+ let totalFilesChanged = 0;
+
+ for (const branch of activeBranches) {
+ try {
+ const branchCommits = await github.paginate(github.rest.repos.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ sha: branch.name,
+ since,
+ until,
+ per_page: 100,
+ });
+
+ console.log(`Found ${branchCommits.length} commits on ${branch.name}`);
+
+ for (const commit of branchCommits) {
+ if (!allCommitShas.has(commit.sha)) {
+ allCommitShas.add(commit.sha);
+
+ if (commit.author && commit.author.login) {
+ allAuthors.add(commit.author.login);
+ }
+ if (commit.committer && commit.committer.login) {
+ allAuthors.add(commit.committer.login);
+ }
+
+ // Get detailed stats for first 20 unique commits
+ if (allCommitShas.size <= 20) {
+ try {
+ const commitDetail = await github.rest.repos.getCommit({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: commit.sha,
+ });
+
+ totalLinesAdded += commitDetail.data.stats?.additions || 0;
+ totalLinesRemoved += commitDetail.data.stats?.deletions || 0;
+ totalFilesChanged += commitDetail.data.files?.length || 0;
+
+ // Extract co-authors
+ if (commitDetail.data.commit && commitDetail.data.commit.message) {
+ const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
+ let match;
+ while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
+ const coAuthorName = match[1].trim();
+ if (coAuthorName && coAuthorName.length > 0) {
+ allAuthors.add(coAuthorName);
+ }
+ }
+ }
+ } catch (error) {
+ console.log(`Error getting detailed stats: ${error.message}`);
+ }
}
}
}
} catch (error) {
- console.log(`Error checking co-authors: ${error.message}`);
+ console.log(`Error getting commits from ${branch.name}: ${error.message}`);
}
}
- console.log(`Total contributors: ${contributors.size}`);
- console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
+ console.log(`Total unique commits: ${allCommitShas.size}`);
+ console.log(`Total unique authors: ${allAuthors.size}`);
+ console.log(`Authors: ${Array.from(allAuthors).join(', ')}`);
return {
- totalCommits: commits.length,
- activeContributors: contributors.size,
+ totalCommits: allCommitShas.size,
+ activeContributors: allAuthors.size,
linesAdded: totalLinesAdded,
- filesChanged: filesChanged,
- contributorsList: Array.from(contributors)
+ linesRemoved: totalLinesRemoved,
+ linesChanged: totalLinesAdded + totalLinesRemoved,
+ filesChanged: totalFilesChanged,
+ contributorsList: Array.from(allAuthors)
};
}
@@ -224,14 +310,14 @@ jobs:
async function createChartsOnBranch(monthlyData) {
const totalCommits = monthlyData.map(d => d.stats.totalCommits);
const mergedPRs = monthlyData.map(d => d.prStats.merged);
- const linesAdded = monthlyData.map(d => d.stats.linesAdded);
+ const linesChanged = monthlyData.map(d => d.stats.linesChanged);
const activeContributors = monthlyData.map(d => d.stats.activeContributors);
const monthLabels = monthlyData.map(d => d.month);
const charts = [
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
- { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
+ { path: 'docs/stats/lines-changed-chart.svg', content: createSVGChart(linesChanged, 'Total Lines Changed', '#22c55e', monthLabels) },
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
];
@@ -316,7 +402,8 @@ jobs:
const monthlyData = [];
const now = new Date();
- for (let i = 12; i >= 1; i--) {
+ // Get historical data (main branch only for speed)
+ for (let i = 12; i >= 2; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
@@ -326,7 +413,7 @@ jobs:
const until = monthEnd.toISOString();
const monthName = monthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
- console.log(`Processing ${monthName} (${since} to ${until})...`);
+ console.log(`Processing ${monthName} (simple stats)...`);
const [stats, prStats] = await Promise.all([
getSimpleMonthStats(since, until),
@@ -336,6 +423,20 @@ jobs:
monthlyData.push({ month: monthName, stats, prStats });
}
+ // Get current month with comprehensive stats (all branches)
+ const currentMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
+ const currentMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0);
+ const currentMonthName = currentMonthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
+
+ console.log(`Processing current month ${currentMonthName} with comprehensive stats...`);
+
+ const [currentStats, currentPRStats] = await Promise.all([
+ getComprehensiveMonthStats(currentMonthStart.toISOString(), currentMonthEnd.toISOString()),
+ getPRStats(currentMonthStart.toISOString(), currentMonthEnd.toISOString())
+ ]);
+
+ monthlyData.push({ month: currentMonthName, stats: currentStats, prStats: currentPRStats });
+
const branchResult = await createChartsOnBranch(monthlyData);
const languages = await getLanguageStats();
@@ -347,10 +448,11 @@ jobs:
fields: [
{ title: '📝 Total Commits', value: latest.stats.totalCommits.toString(), short: true },
{ title: '🔀 Merged PRs', value: latest.prStats.merged.toString(), short: true },
- { title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
+ { title: '📊 Lines Changed', value: latest.stats.linesChanged.toLocaleString(), short: true },
{ title: '👥 Contributors', value: latest.stats.activeContributors.toString(), short: true }
],
- text: `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
+ text: `**Code Changes:** +${latest.stats.linesAdded.toLocaleString()} / -${latest.stats.linesRemoved.toLocaleString()} lines\n` +
+ `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**Charts:** [View on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
From 2d66174b6dc9c2d59500622f146df37004316768 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:20:13 +0200
Subject: [PATCH 105/125] Revert "update bot"
This reverts commit 73d15ad005f2cbc146a30deb9e4176a1cb71d149.
---
.github/workflows/gh_statistics_bot.yml | 174 +++++-------------------
1 file changed, 36 insertions(+), 138 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 3f43bbb437..262c610603 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -26,9 +26,9 @@ jobs:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- // Simple stats for historical data (main branch only)
+ // Simple stats with minimal API calls - main branch only
async function getSimpleMonthStats(since, until) {
- console.log(`Getting simple stats for ${since} to ${until}`);
+ console.log(`Getting stats for ${since} to ${until}`);
const commits = await github.paginate(github.rest.repos.listCommits, {
owner: context.repo.owner,
@@ -38,6 +38,8 @@ jobs:
per_page: 100,
});
+ console.log(`Found ${commits.length} commits on main branch`);
+
const contributors = new Set();
commits.forEach(commit => {
if (commit.author && commit.author.login) {
@@ -49,7 +51,6 @@ jobs:
});
let totalLinesAdded = 0;
- let totalLinesRemoved = 0;
let filesChanged = 0;
for (const commit of commits.slice(0, 30)) {
@@ -61,151 +62,64 @@ jobs:
});
totalLinesAdded += commitDetail.data.stats?.additions || 0;
- totalLinesRemoved += commitDetail.data.stats?.deletions || 0;
filesChanged += commitDetail.data.files?.length || 0;
if (commitDetail.data.commit && commitDetail.data.commit.message) {
+ const message = commitDetail.data.commit.message;
const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
let match;
- while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
+
+ while ((match = coAuthorRegex.exec(message)) !== null) {
const coAuthorName = match[1].trim();
if (coAuthorName && coAuthorName.length > 0) {
contributors.add(coAuthorName);
+ console.log(`Found co-author: ${coAuthorName}`);
}
}
}
- } catch (error) {
- console.log(`Error getting commit detail: ${error.message}`);
- }
- }
-
- return {
- totalCommits: commits.length,
- activeContributors: contributors.size,
- linesAdded: totalLinesAdded,
- linesRemoved: totalLinesRemoved,
- linesChanged: totalLinesAdded + totalLinesRemoved,
- filesChanged: filesChanged,
- contributorsList: Array.from(contributors)
- };
- }
-
- // Comprehensive stats for current month (all branches)
- async function getComprehensiveMonthStats(since, until) {
- console.log(`Getting comprehensive stats for ${since} to ${until}`);
-
- // Get all branches
- const branches = await github.paginate(github.rest.repos.listBranches, {
- owner: context.repo.owner,
- repo: context.repo.repo,
- per_page: 100,
- });
-
- console.log(`Found ${branches.length} branches`);
-
- // Find active branches (have commits in date range)
- const activeBranches = [];
- for (const branch of branches.slice(0, 50)) { // Limit to avoid rate limits
- try {
- const branchCommits = await github.rest.repos.listCommits({
- owner: context.repo.owner,
- repo: context.repo.repo,
- sha: branch.name,
- since,
- until,
- per_page: 1,
- });
-
- if (branchCommits.data.length > 0) {
- activeBranches.push(branch);
- console.log(`Branch ${branch.name} has commits in date range`);
+ if (commitDetail.data.author && commitDetail.data.author.login) {
+ contributors.add(commitDetail.data.author.login);
}
+
} catch (error) {
- console.log(`Error checking branch ${branch.name}: ${error.message}`);
+ console.log(`Error getting commit detail: ${error.message}`);
}
}
- console.log(`Found ${activeBranches.length} active branches`);
-
- // Get all commits from active branches
- const allCommitShas = new Set();
- const allAuthors = new Set();
- let totalLinesAdded = 0;
- let totalLinesRemoved = 0;
- let totalFilesChanged = 0;
-
- for (const branch of activeBranches) {
+ for (const commit of commits.slice(30, 60)) {
try {
- const branchCommits = await github.paginate(github.rest.repos.listCommits, {
+ const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
- sha: branch.name,
- since,
- until,
- per_page: 100,
+ ref: commit.sha,
});
- console.log(`Found ${branchCommits.length} commits on ${branch.name}`);
-
- for (const commit of branchCommits) {
- if (!allCommitShas.has(commit.sha)) {
- allCommitShas.add(commit.sha);
-
- if (commit.author && commit.author.login) {
- allAuthors.add(commit.author.login);
- }
- if (commit.committer && commit.committer.login) {
- allAuthors.add(commit.committer.login);
- }
-
- // Get detailed stats for first 20 unique commits
- if (allCommitShas.size <= 20) {
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- totalLinesAdded += commitDetail.data.stats?.additions || 0;
- totalLinesRemoved += commitDetail.data.stats?.deletions || 0;
- totalFilesChanged += commitDetail.data.files?.length || 0;
-
- // Extract co-authors
- if (commitDetail.data.commit && commitDetail.data.commit.message) {
- const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
- let match;
- while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
- const coAuthorName = match[1].trim();
- if (coAuthorName && coAuthorName.length > 0) {
- allAuthors.add(coAuthorName);
- }
- }
- }
- } catch (error) {
- console.log(`Error getting detailed stats: ${error.message}`);
- }
+ if (commitDetail.data.commit && commitDetail.data.commit.message) {
+ const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
+ let match;
+
+ while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
+ const coAuthorName = match[1].trim();
+ if (coAuthorName && coAuthorName.length > 0) {
+ contributors.add(coAuthorName);
}
}
}
} catch (error) {
- console.log(`Error getting commits from ${branch.name}: ${error.message}`);
+ console.log(`Error checking co-authors: ${error.message}`);
}
}
- console.log(`Total unique commits: ${allCommitShas.size}`);
- console.log(`Total unique authors: ${allAuthors.size}`);
- console.log(`Authors: ${Array.from(allAuthors).join(', ')}`);
+ console.log(`Total contributors: ${contributors.size}`);
+ console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
return {
- totalCommits: allCommitShas.size,
- activeContributors: allAuthors.size,
+ totalCommits: commits.length,
+ activeContributors: contributors.size,
linesAdded: totalLinesAdded,
- linesRemoved: totalLinesRemoved,
- linesChanged: totalLinesAdded + totalLinesRemoved,
- filesChanged: totalFilesChanged,
- contributorsList: Array.from(allAuthors)
+ filesChanged: filesChanged,
+ contributorsList: Array.from(contributors)
};
}
@@ -310,14 +224,14 @@ jobs:
async function createChartsOnBranch(monthlyData) {
const totalCommits = monthlyData.map(d => d.stats.totalCommits);
const mergedPRs = monthlyData.map(d => d.prStats.merged);
- const linesChanged = monthlyData.map(d => d.stats.linesChanged);
+ const linesAdded = monthlyData.map(d => d.stats.linesAdded);
const activeContributors = monthlyData.map(d => d.stats.activeContributors);
const monthLabels = monthlyData.map(d => d.month);
const charts = [
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
- { path: 'docs/stats/lines-changed-chart.svg', content: createSVGChart(linesChanged, 'Total Lines Changed', '#22c55e', monthLabels) },
+ { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
];
@@ -402,8 +316,7 @@ jobs:
const monthlyData = [];
const now = new Date();
- // Get historical data (main branch only for speed)
- for (let i = 12; i >= 2; i--) {
+ for (let i = 12; i >= 1; i--) {
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() - i + 1, 0);
@@ -413,7 +326,7 @@ jobs:
const until = monthEnd.toISOString();
const monthName = monthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
- console.log(`Processing ${monthName} (simple stats)...`);
+ console.log(`Processing ${monthName} (${since} to ${until})...`);
const [stats, prStats] = await Promise.all([
getSimpleMonthStats(since, until),
@@ -423,20 +336,6 @@ jobs:
monthlyData.push({ month: monthName, stats, prStats });
}
- // Get current month with comprehensive stats (all branches)
- const currentMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
- const currentMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0);
- const currentMonthName = currentMonthStart.toLocaleString('default', { month: 'short', year: 'numeric' });
-
- console.log(`Processing current month ${currentMonthName} with comprehensive stats...`);
-
- const [currentStats, currentPRStats] = await Promise.all([
- getComprehensiveMonthStats(currentMonthStart.toISOString(), currentMonthEnd.toISOString()),
- getPRStats(currentMonthStart.toISOString(), currentMonthEnd.toISOString())
- ]);
-
- monthlyData.push({ month: currentMonthName, stats: currentStats, prStats: currentPRStats });
-
const branchResult = await createChartsOnBranch(monthlyData);
const languages = await getLanguageStats();
@@ -448,11 +347,10 @@ jobs:
fields: [
{ title: '📝 Total Commits', value: latest.stats.totalCommits.toString(), short: true },
{ title: '🔀 Merged PRs', value: latest.prStats.merged.toString(), short: true },
- { title: '📊 Lines Changed', value: latest.stats.linesChanged.toLocaleString(), short: true },
+ { title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
{ title: '👥 Contributors', value: latest.stats.activeContributors.toString(), short: true }
],
- text: `**Code Changes:** +${latest.stats.linesAdded.toLocaleString()} / -${latest.stats.linesRemoved.toLocaleString()} lines\n` +
- `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
+ text: `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
`**Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**Charts:** [View on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`Repository: ${context.repo.owner}/${context.repo.repo}`,
From 6f17e777e0dca87f0c7b0b8b3ed069330a9865dc Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:23:24 +0200
Subject: [PATCH 106/125] check
---
.github/workflows/gh_statistics_bot.yml | 23 -----------------------
1 file changed, 23 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 262c610603..fed26efd47 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -87,29 +87,6 @@ jobs:
}
}
- for (const commit of commits.slice(30, 60)) {
- try {
- const commitDetail = await github.rest.repos.getCommit({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: commit.sha,
- });
-
- if (commitDetail.data.commit && commitDetail.data.commit.message) {
- const coAuthorRegex = /Co-authored-by:\s*([^<]+)\s*<([^>]+)>/gi;
- let match;
-
- while ((match = coAuthorRegex.exec(commitDetail.data.commit.message)) !== null) {
- const coAuthorName = match[1].trim();
- if (coAuthorName && coAuthorName.length > 0) {
- contributors.add(coAuthorName);
- }
- }
- }
- } catch (error) {
- console.log(`Error checking co-authors: ${error.message}`);
- }
- }
console.log(`Total contributors: ${contributors.size}`);
console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
From dad73fe5ce3b6aa1b6fbb9bd005fc60d06e2422d Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:25:53 +0200
Subject: [PATCH 107/125] webflow
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index fed26efd47..742e84ed50 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -45,7 +45,7 @@ jobs:
if (commit.author && commit.author.login) {
contributors.add(commit.author.login);
}
- if (commit.committer && commit.committer.login && commit.committer.login !== commit.author?.login) {
+ if (commit.committer && commit.committer.login && commit.committer.login !== commit.author?.login && commit.committer.login !== 'github-actions[bot]' && commit.committer.login !== 'dependabot[bot]' && commit.committer.login !== 'web-flow[bot]') {
contributors.add(commit.committer.login);
}
});
From 645dd467306f8e614e40bb98c61496bc4bf703b5 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:27:22 +0200
Subject: [PATCH 108/125] now?
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 742e84ed50..cdee3b9b5d 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -45,7 +45,7 @@ jobs:
if (commit.author && commit.author.login) {
contributors.add(commit.author.login);
}
- if (commit.committer && commit.committer.login && commit.committer.login !== commit.author?.login && commit.committer.login !== 'github-actions[bot]' && commit.committer.login !== 'dependabot[bot]' && commit.committer.login !== 'web-flow[bot]') {
+ if (commit.committer && commit.committer.login && commit.committer.login !== commit.author?.login && commit.committer.login !== 'github-actions[bot]' && commit.committer.login !== 'dependabot[bot]' && commit.committer.login !== 'web-flow') {
contributors.add(commit.committer.login);
}
});
From 764970736fce996387e4f64063a49a783716509b Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:37:16 +0200
Subject: [PATCH 109/125] now?
---
.github/workflows/gh_statistics_bot.yml | 218 +++++++++++++++++++++---
1 file changed, 196 insertions(+), 22 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index cdee3b9b5d..90f2c1d01c 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -20,13 +20,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Generate Simple Monthly Statistics
+ - name: Generate Enhanced Monthly Statistics
uses: actions/github-script@v7
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
with:
script: |
- // Simple stats with minimal API calls - main branch only
+ // Enhanced stats with additional metrics - main branch focused
async function getSimpleMonthStats(since, until) {
console.log(`Getting stats for ${since} to ${until}`);
@@ -41,6 +41,10 @@ jobs:
console.log(`Found ${commits.length} commits on main branch`);
const contributors = new Set();
+ let totalLinesAdded = 0;
+ let totalLinesDeleted = 0;
+ let filesChanged = 0;
+
commits.forEach(commit => {
if (commit.author && commit.author.login) {
contributors.add(commit.author.login);
@@ -50,9 +54,6 @@ jobs:
}
});
- let totalLinesAdded = 0;
- let filesChanged = 0;
-
for (const commit of commits.slice(0, 30)) {
try {
const commitDetail = await github.rest.repos.getCommit({
@@ -62,6 +63,7 @@ jobs:
});
totalLinesAdded += commitDetail.data.stats?.additions || 0;
+ totalLinesDeleted += commitDetail.data.stats?.deletions || 0;
filesChanged += commitDetail.data.files?.length || 0;
if (commitDetail.data.commit && commitDetail.data.commit.message) {
@@ -87,7 +89,6 @@ jobs:
}
}
-
console.log(`Total contributors: ${contributors.size}`);
console.log(`Contributors: ${Array.from(contributors).join(', ')}`);
@@ -95,6 +96,8 @@ jobs:
totalCommits: commits.length,
activeContributors: contributors.size,
linesAdded: totalLinesAdded,
+ linesDeleted: totalLinesDeleted,
+ netLinesChanged: totalLinesAdded - totalLinesDeleted,
filesChanged: filesChanged,
contributorsList: Array.from(contributors)
};
@@ -116,7 +119,120 @@ jobs:
new Date(pr.merged_at) <= new Date(until)
);
- return { merged: mergedPRs.length };
+ const openedPRs = prs.filter(pr =>
+ new Date(pr.created_at) >= new Date(since) &&
+ new Date(pr.created_at) <= new Date(until)
+ );
+
+ const closedPRs = prs.filter(pr =>
+ pr.closed_at && !pr.merged_at &&
+ new Date(pr.closed_at) >= new Date(since) &&
+ new Date(pr.closed_at) <= new Date(until)
+ );
+
+ // Calculate average time to merge for merged PRs
+ let totalMergeTime = 0;
+ let mergeTimeCount = 0;
+
+ mergedPRs.forEach(pr => {
+ if (pr.created_at && pr.merged_at) {
+ const created = new Date(pr.created_at);
+ const merged = new Date(pr.merged_at);
+ totalMergeTime += (merged - created) / (1000 * 60 * 60 * 24); // days
+ mergeTimeCount++;
+ }
+ });
+
+ const avgMergeTime = mergeTimeCount > 0 ? (totalMergeTime / mergeTimeCount).toFixed(1) : 0;
+
+ return {
+ merged: mergedPRs.length,
+ opened: openedPRs.length,
+ closed: closedPRs.length,
+ avgMergeTimeDays: parseFloat(avgMergeTime)
+ };
+ }
+
+ async function getIssueStats(since, until) {
+ const issues = await github.paginate(github.rest.issues.listForRepo, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'all',
+ sort: 'updated',
+ direction: 'desc',
+ per_page: 100,
+ });
+
+ // Filter out pull requests (GitHub API includes PRs in issues)
+ const actualIssues = issues.filter(issue => !issue.pull_request);
+
+ const openedIssues = actualIssues.filter(issue =>
+ new Date(issue.created_at) >= new Date(since) &&
+ new Date(issue.created_at) <= new Date(until)
+ );
+
+ const closedIssues = actualIssues.filter(issue =>
+ issue.closed_at &&
+ new Date(issue.closed_at) >= new Date(since) &&
+ new Date(issue.closed_at) <= new Date(until)
+ );
+
+ // Calculate average time to close for closed issues
+ let totalCloseTime = 0;
+ let closeTimeCount = 0;
+
+ closedIssues.forEach(issue => {
+ if (issue.created_at && issue.closed_at) {
+ const created = new Date(issue.created_at);
+ const closed = new Date(issue.closed_at);
+ totalCloseTime += (closed - created) / (1000 * 60 * 60 * 24); // days
+ closeTimeCount++;
+ }
+ });
+
+ const avgCloseTime = closeTimeCount > 0 ? (totalCloseTime / closeTimeCount).toFixed(1) : 0;
+
+ // Count issues by labels
+ const labelCounts = {};
+ openedIssues.forEach(issue => {
+ issue.labels.forEach(label => {
+ labelCounts[label.name] = (labelCounts[label.name] || 0) + 1;
+ });
+ });
+
+ return {
+ opened: openedIssues.length,
+ closed: closedIssues.length,
+ avgCloseTimeDays: parseFloat(avgCloseTime),
+ topLabels: Object.entries(labelCounts)
+ .sort(([,a], [,b]) => b - a)
+ .slice(0, 3)
+ .map(([label, count]) => ({ label, count }))
+ };
+ }
+
+ async function getReleaseStats(since, until) {
+ try {
+ const releases = await github.paginate(github.rest.repos.listReleases, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ per_page: 100,
+ });
+
+ const monthlyReleases = releases.filter(release =>
+ new Date(release.created_at) >= new Date(since) &&
+ new Date(release.created_at) <= new Date(until)
+ );
+
+ return {
+ total: monthlyReleases.length,
+ preReleases: monthlyReleases.filter(r => r.prerelease).length,
+ latestRelease: monthlyReleases.length > 0 ? monthlyReleases[0].tag_name : null
+ };
+ } catch (error) {
+ console.log(`Error getting releases: ${error.message}`);
+ return { total: 0, preReleases: 0, latestRelease: null };
+ }
}
async function getLanguageStats() {
@@ -139,6 +255,25 @@ jobs:
}
}
+ async function getStarAndForkStats() {
+ try {
+ const repo = await github.rest.repos.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ return {
+ stars: repo.data.stargazers_count,
+ forks: repo.data.forks_count,
+ watchers: repo.data.subscribers_count,
+ openIssues: repo.data.open_issues_count
+ };
+ } catch (error) {
+ console.log(`Error getting repo stats: ${error.message}`);
+ return { stars: 0, forks: 0, watchers: 0, openIssues: 0 };
+ }
+ }
+
function createSVGChart(data, title, color, monthLabels) {
const width = 600;
const height = 200;
@@ -203,13 +338,17 @@ jobs:
const mergedPRs = monthlyData.map(d => d.prStats.merged);
const linesAdded = monthlyData.map(d => d.stats.linesAdded);
const activeContributors = monthlyData.map(d => d.stats.activeContributors);
+ const issuesClosed = monthlyData.map(d => d.issueStats.closed);
+ const netLinesChanged = monthlyData.map(d => d.stats.netLinesChanged);
const monthLabels = monthlyData.map(d => d.month);
const charts = [
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
- { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Total Lines Added', '#22c55e', monthLabels) },
- { path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) }
+ { path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Lines Added', '#22c55e', monthLabels) },
+ { path: 'docs/stats/net-lines-changed-chart.svg', content: createSVGChart(netLinesChanged, 'Net Lines Changed', '#f59e0b', monthLabels) },
+ { path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) },
+ { path: 'docs/stats/issues-closed-chart.svg', content: createSVGChart(issuesClosed, 'Issues Closed', '#ef4444', monthLabels) }
];
// Get repo info
@@ -288,7 +427,7 @@ jobs:
}
try {
- console.log('Starting simple monthly report generation...');
+ console.log('Starting enhanced monthly report generation...');
const monthlyData = [];
const now = new Date();
@@ -305,33 +444,62 @@ jobs:
console.log(`Processing ${monthName} (${since} to ${until})...`);
- const [stats, prStats] = await Promise.all([
+ const [stats, prStats, issueStats, releaseStats] = await Promise.all([
getSimpleMonthStats(since, until),
- getPRStats(since, until)
+ getPRStats(since, until),
+ getIssueStats(since, until),
+ getReleaseStats(since, until)
]);
- monthlyData.push({ month: monthName, stats, prStats });
+ monthlyData.push({ month: monthName, stats, prStats, issueStats, releaseStats });
}
const branchResult = await createChartsOnBranch(monthlyData);
const languages = await getLanguageStats();
+ const repoStats = await getStarAndForkStats();
const latest = monthlyData[monthlyData.length - 1];
+
+ // Create summary statistics
+ const topCommitType = Object.entries(latest.stats.commitTypes)
+ .sort(([,a], [,b]) => b - a)[0];
+
const mattermostPayload = {
- text: `📊 **${latest.month} Repository Activity**`,
+ text: `📊 **${latest.month} Repository Activity Report**`,
attachments: [{
color: '#22c55e',
fields: [
+ // Core Development Activity
{ title: '📝 Total Commits', value: latest.stats.totalCommits.toString(), short: true },
- { title: '🔀 Merged PRs', value: latest.prStats.merged.toString(), short: true },
+ { title: '👥 Active Contributors', value: latest.stats.activeContributors.toString(), short: true },
{ title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
- { title: '👥 Contributors', value: latest.stats.activeContributors.toString(), short: true }
+ { title: '📉 Lines Deleted', value: latest.stats.linesDeleted.toLocaleString(), short: true },
+ { title: '⚖️ Net Lines Changed', value: latest.stats.netLinesChanged.toLocaleString(), short: true },
+ { title: '📁 Files Changed', value: latest.stats.filesChanged.toString(), short: true },
+
+ // Pull Request Activity
+ { title: '🔀 PRs Merged', value: latest.prStats.merged.toString(), short: true },
+ { title: '🆕 PRs Opened', value: latest.prStats.opened.toString(), short: true },
+ { title: '❌ PRs Closed', value: latest.prStats.closed.toString(), short: true },
+ { title: '⏱️ Avg Merge Time', value: `${latest.prStats.avgMergeTimeDays} days`, short: true },
+
+ // Issue Management
+ { title: '🐛 Issues Opened', value: latest.issueStats.opened.toString(), short: true },
+ { title: '✅ Issues Closed', value: latest.issueStats.closed.toString(), short: true },
+ { title: '🕐 Avg Close Time', value: `${latest.issueStats.avgCloseTimeDays} days`, short: true },
+ { title: '🏷️ Top Issue Label', value: latest.issueStats.topLabels[0]?.label || 'None', short: true },
+
+ // Release Activity
+ { title: '🚀 Releases', value: latest.releaseStats.total.toString(), short: true },
+ { title: '📦 Latest Release', value: latest.releaseStats.latestRelease || 'None', short: true },
],
- text: `**Top Contributors:** ${latest.stats.contributorsList.slice(0, 3).join(', ') || 'None'}\n` +
- `**Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
- `**Charts:** [View on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
- `Repository: ${context.repo.owner}/${context.repo.repo}`,
- footer: 'Simple repository statistics'
+ text: `**🏆 Top Contributors:** ${latest.stats.contributorsList.slice(0, 5).join(', ') || 'None'}\n` +
+ `**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n` +
+ `**📊 Most Common Commit Type:** ${topCommitType ? `${topCommitType[0]} (${topCommitType[1]} commits)` : 'N/A'}\n` +
+ `**⭐ Repository Stats:** ${repoStats.stars} stars • ${repoStats.forks} forks • ${repoStats.openIssues} open issues\n\n` +
+ `**📈 Charts:** [View detailed charts on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
+ `**📂 Repository:** ${context.repo.owner}/${context.repo.repo}`,
+ footer: 'Enhanced repository statistics • Generated by GitHub Actions'
}]
};
@@ -345,7 +513,13 @@ jobs:
throw new Error(`Mattermost webhook failed: ${response.status}`);
}
- console.log('Simple monthly report completed successfully');
+ console.log('Enhanced monthly report completed successfully');
+ console.log(`Generated stats for ${latest.month}:`);
+ console.log(`- Commits: ${latest.stats.totalCommits}`);
+ console.log(`- Contributors: ${latest.stats.activeContributors}`);
+ console.log(`- Issues closed: ${latest.issueStats.closed}`);
+ console.log(`- PRs merged: ${latest.prStats.merged}`);
+ console.log(`- Releases: ${latest.releaseStats.total}`);
} catch (error) {
console.error('Error:', error);
From 827a6c4d22f17f60f8f755352281e5e20a3dd397 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:40:34 +0200
Subject: [PATCH 110/125] now?
---
.github/workflows/gh_statistics_bot.yml | 85 ++-----------------------
1 file changed, 6 insertions(+), 79 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 90f2c1d01c..ea783c4130 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -130,26 +130,10 @@ jobs:
new Date(pr.closed_at) <= new Date(until)
);
- // Calculate average time to merge for merged PRs
- let totalMergeTime = 0;
- let mergeTimeCount = 0;
-
- mergedPRs.forEach(pr => {
- if (pr.created_at && pr.merged_at) {
- const created = new Date(pr.created_at);
- const merged = new Date(pr.merged_at);
- totalMergeTime += (merged - created) / (1000 * 60 * 60 * 24); // days
- mergeTimeCount++;
- }
- });
-
- const avgMergeTime = mergeTimeCount > 0 ? (totalMergeTime / mergeTimeCount).toFixed(1) : 0;
-
return {
merged: mergedPRs.length,
opened: openedPRs.length,
- closed: closedPRs.length,
- avgMergeTimeDays: parseFloat(avgMergeTime)
+ closed: closedPRs.length
};
}
@@ -177,37 +161,9 @@ jobs:
new Date(issue.closed_at) <= new Date(until)
);
- // Calculate average time to close for closed issues
- let totalCloseTime = 0;
- let closeTimeCount = 0;
-
- closedIssues.forEach(issue => {
- if (issue.created_at && issue.closed_at) {
- const created = new Date(issue.created_at);
- const closed = new Date(issue.closed_at);
- totalCloseTime += (closed - created) / (1000 * 60 * 60 * 24); // days
- closeTimeCount++;
- }
- });
-
- const avgCloseTime = closeTimeCount > 0 ? (totalCloseTime / closeTimeCount).toFixed(1) : 0;
-
- // Count issues by labels
- const labelCounts = {};
- openedIssues.forEach(issue => {
- issue.labels.forEach(label => {
- labelCounts[label.name] = (labelCounts[label.name] || 0) + 1;
- });
- });
-
return {
opened: openedIssues.length,
- closed: closedIssues.length,
- avgCloseTimeDays: parseFloat(avgCloseTime),
- topLabels: Object.entries(labelCounts)
- .sort(([,a], [,b]) => b - a)
- .slice(0, 3)
- .map(([label, count]) => ({ label, count }))
+ closed: closedIssues.length
};
}
@@ -255,25 +211,6 @@ jobs:
}
}
- async function getStarAndForkStats() {
- try {
- const repo = await github.rest.repos.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- return {
- stars: repo.data.stargazers_count,
- forks: repo.data.forks_count,
- watchers: repo.data.subscribers_count,
- openIssues: repo.data.open_issues_count
- };
- } catch (error) {
- console.log(`Error getting repo stats: ${error.message}`);
- return { stars: 0, forks: 0, watchers: 0, openIssues: 0 };
- }
- }
-
function createSVGChart(data, title, color, monthLabels) {
const width = 600;
const height = 200;
@@ -427,7 +364,7 @@ jobs:
}
try {
- console.log('Starting enhanced monthly report generation...');
+ console.log('Starting monthly report generation...');
const monthlyData = [];
const now = new Date();
@@ -456,14 +393,9 @@ jobs:
const branchResult = await createChartsOnBranch(monthlyData);
const languages = await getLanguageStats();
- const repoStats = await getStarAndForkStats();
const latest = monthlyData[monthlyData.length - 1];
- // Create summary statistics
- const topCommitType = Object.entries(latest.stats.commitTypes)
- .sort(([,a], [,b]) => b - a)[0];
-
const mattermostPayload = {
text: `📊 **${latest.month} Repository Activity Report**`,
attachments: [{
@@ -481,25 +413,20 @@ jobs:
{ title: '🔀 PRs Merged', value: latest.prStats.merged.toString(), short: true },
{ title: '🆕 PRs Opened', value: latest.prStats.opened.toString(), short: true },
{ title: '❌ PRs Closed', value: latest.prStats.closed.toString(), short: true },
- { title: '⏱️ Avg Merge Time', value: `${latest.prStats.avgMergeTimeDays} days`, short: true },
// Issue Management
{ title: '🐛 Issues Opened', value: latest.issueStats.opened.toString(), short: true },
{ title: '✅ Issues Closed', value: latest.issueStats.closed.toString(), short: true },
- { title: '🕐 Avg Close Time', value: `${latest.issueStats.avgCloseTimeDays} days`, short: true },
- { title: '🏷️ Top Issue Label', value: latest.issueStats.topLabels[0]?.label || 'None', short: true },
// Release Activity
{ title: '🚀 Releases', value: latest.releaseStats.total.toString(), short: true },
{ title: '📦 Latest Release', value: latest.releaseStats.latestRelease || 'None', short: true },
],
text: `**🏆 Top Contributors:** ${latest.stats.contributorsList.slice(0, 5).join(', ') || 'None'}\n` +
- `**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n` +
- `**📊 Most Common Commit Type:** ${topCommitType ? `${topCommitType[0]} (${topCommitType[1]} commits)` : 'N/A'}\n` +
- `**⭐ Repository Stats:** ${repoStats.stars} stars • ${repoStats.forks} forks • ${repoStats.openIssues} open issues\n\n` +
+ `**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**📈 Charts:** [View detailed charts on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`**📂 Repository:** ${context.repo.owner}/${context.repo.repo}`,
- footer: 'Enhanced repository statistics • Generated by GitHub Actions'
+ footer: 'Repository statistics • Generated by GitHub Actions'
}]
};
@@ -513,7 +440,7 @@ jobs:
throw new Error(`Mattermost webhook failed: ${response.status}`);
}
- console.log('Enhanced monthly report completed successfully');
+ console.log('Monthly report completed successfully');
console.log(`Generated stats for ${latest.month}:`);
console.log(`- Commits: ${latest.stats.totalCommits}`);
console.log(`- Contributors: ${latest.stats.activeContributors}`);
From 2d1030e9e5be119f1d08c5cc451d49fe6cca9e5f Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 1 Aug 2025 21:45:20 +0200
Subject: [PATCH 111/125] final
---
.github/workflows/gh_statistics_bot.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index ea783c4130..fbb21888c8 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -417,10 +417,6 @@ jobs:
// Issue Management
{ title: '🐛 Issues Opened', value: latest.issueStats.opened.toString(), short: true },
{ title: '✅ Issues Closed', value: latest.issueStats.closed.toString(), short: true },
-
- // Release Activity
- { title: '🚀 Releases', value: latest.releaseStats.total.toString(), short: true },
- { title: '📦 Latest Release', value: latest.releaseStats.latestRelease || 'None', short: true },
],
text: `**🏆 Top Contributors:** ${latest.stats.contributorsList.slice(0, 5).join(', ') || 'None'}\n` +
`**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
From 77284e4d466c7aaaecc0f93199624c25b72f7c8b Mon Sep 17 00:00:00 2001
From: Sascha Korf <51127093+xsaschako@users.noreply.github.com>
Date: Sun, 10 Aug 2025 22:41:55 +0200
Subject: [PATCH 112/125] update graph
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index fbb21888c8..562c977aba 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -219,7 +219,7 @@ jobs:
const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
- const minValue = Math.min(...data);
+ const minValue = Math.max(0, Math.min(...data) - Math.max(...data) * 0.1);
const range = maxValue - minValue || 1;
const points = data.map((value, index) => {
From 340d50c5b710f4fc7fce54f0f71f348c10aa6cdd Mon Sep 17 00:00:00 2001
From: Sascha Korf <51127093+xsaschako@users.noreply.github.com>
Date: Sun, 10 Aug 2025 22:47:01 +0200
Subject: [PATCH 113/125] ?
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 562c977aba..3a50079db1 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -219,7 +219,7 @@ jobs:
const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
- const minValue = Math.max(0, Math.min(...data) - Math.max(...data) * 0.1);
+ const minValue = Math.max(Math.max(0, Math.min(...data) - Math.max(...data) * 0.1), 0);
const range = maxValue - minValue || 1;
const points = data.map((value, index) => {
From 1f5f6379668308497e5ea5a943922ffa8921e00a Mon Sep 17 00:00:00 2001
From: Sascha Korf <51127093+xsaschako@users.noreply.github.com>
Date: Sun, 10 Aug 2025 22:52:22 +0200
Subject: [PATCH 114/125] ?
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 3a50079db1..05914ede3d 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -219,7 +219,7 @@ jobs:
const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
- const minValue = Math.max(Math.max(0, Math.min(...data) - Math.max(...data) * 0.1), 0);
+ const minValue = Math.max(1, Math.min(...data) - Math.max(...data) * 0.1);
const range = maxValue - minValue || 1;
const points = data.map((value, index) => {
From 1f28ab603457dfdbe3cb6395a8a793455ed5a459 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:32:58 +0200
Subject: [PATCH 115/125] does this work?
---
.github/workflows/gh_statistics_bot.yml | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 05914ede3d..2ffb60a3be 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -4,7 +4,7 @@ on:
pull_request:
types: [opened, synchronize]
schedule:
- - cron: '0 9 1 * *'
+ - cron: "0 9 1 * *"
workflow_dispatch:
jobs:
@@ -12,14 +12,12 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
- issues: write
- pull-requests: write
actions: read
-
+
steps:
- name: Checkout repository
uses: actions/checkout@v4
-
+
- name: Generate Enhanced Monthly Statistics
uses: actions/github-script@v7
env:
@@ -54,7 +52,7 @@ jobs:
}
});
- for (const commit of commits.slice(0, 30)) {
+ for (const commit of commits.slice(0, 50)) {
try {
const commitDetail = await github.rest.repos.getCommit({
owner: context.repo.owner,
@@ -418,7 +416,7 @@ jobs:
{ title: '🐛 Issues Opened', value: latest.issueStats.opened.toString(), short: true },
{ title: '✅ Issues Closed', value: latest.issueStats.closed.toString(), short: true },
],
- text: `**🏆 Top Contributors:** ${latest.stats.contributorsList.slice(0, 5).join(', ') || 'None'}\n` +
+ text: `**👥 Active Contributors:** ${latest.stats.contributorsList.slice(0, 15).join(', ') || 'None'}\n` +
`**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**📈 Charts:** [View detailed charts on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`**📂 Repository:** ${context.repo.owner}/${context.repo.repo}`,
@@ -447,4 +445,4 @@ jobs:
} catch (error) {
console.error('Error:', error);
core.setFailed(`Report generation failed: ${error.message}`);
- }
\ No newline at end of file
+ }
From 8cac08d8dc0af6abc5dc9815753be4c1e3a0c91f Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:44:22 +0200
Subject: [PATCH 116/125] fix bug
---
.github/workflows/gh_statistics_bot.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 2ffb60a3be..5aa5f9299a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -95,7 +95,7 @@ jobs:
activeContributors: contributors.size,
linesAdded: totalLinesAdded,
linesDeleted: totalLinesDeleted,
- netLinesChanged: totalLinesAdded - totalLinesDeleted,
+ totalLinesChanged: totalLinesAdded + totalLinesDeleted,
filesChanged: filesChanged,
contributorsList: Array.from(contributors)
};
@@ -217,7 +217,7 @@ jobs:
const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
- const minValue = Math.max(1, Math.min(...data) - Math.max(...data) * 0.1);
+ const minValue = Math.max(0, Math.min(...data) - Math.max(...data) * 0.1);
const range = maxValue - minValue || 1;
const points = data.map((value, index) => {
@@ -274,14 +274,14 @@ jobs:
const linesAdded = monthlyData.map(d => d.stats.linesAdded);
const activeContributors = monthlyData.map(d => d.stats.activeContributors);
const issuesClosed = monthlyData.map(d => d.issueStats.closed);
- const netLinesChanged = monthlyData.map(d => d.stats.netLinesChanged);
+ const totalLinesChanged = monthlyData.map(d => d.stats.totalLinesChanged);
const monthLabels = monthlyData.map(d => d.month);
const charts = [
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
{ path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Lines Added', '#22c55e', monthLabels) },
- { path: 'docs/stats/net-lines-changed-chart.svg', content: createSVGChart(netLinesChanged, 'Net Lines Changed', '#f59e0b', monthLabels) },
+ { path: 'docs/stats/net-lines-changed-chart.svg', content: createSVGChart(totalLinesChanged, 'Total Lines Changed', '#f59e0b', monthLabels) },
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) },
{ path: 'docs/stats/issues-closed-chart.svg', content: createSVGChart(issuesClosed, 'Issues Closed', '#ef4444', monthLabels) }
];
@@ -404,7 +404,7 @@ jobs:
{ title: '👥 Active Contributors', value: latest.stats.activeContributors.toString(), short: true },
{ title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
{ title: '📉 Lines Deleted', value: latest.stats.linesDeleted.toLocaleString(), short: true },
- { title: '⚖️ Net Lines Changed', value: latest.stats.netLinesChanged.toLocaleString(), short: true },
+ { title: '⚖️ Net Lines Changed', value: latest.stats.totalLinesChanged.toLocaleString(), short: true },
{ title: '📁 Files Changed', value: latest.stats.filesChanged.toString(), short: true },
// Pull Request Activity
From 5a2c9914be5852eab085af92ac9f917e141165a2 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:46:34 +0200
Subject: [PATCH 117/125] fix name of branch
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 5aa5f9299a..c013eb2738 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -294,7 +294,7 @@ jobs:
// Create new branch
const now = new Date();
- const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
+ const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() ).toString().padStart(2, '0')}`;
try {
const mainRef = await github.rest.git.getRef({
From 2fd202296c17612615b8dad006d3619f56a22053 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:49:16 +0200
Subject: [PATCH 118/125] fix bugs
---
.github/workflows/gh_statistics_bot.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index c013eb2738..2f5547b277 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -281,7 +281,7 @@ jobs:
{ path: 'docs/stats/total-commits-chart.svg', content: createSVGChart(totalCommits, 'Total Commits', '#3b82f6', monthLabels) },
{ path: 'docs/stats/merged-prs-chart.svg', content: createSVGChart(mergedPRs, 'Merged Pull Requests', '#10b981', monthLabels) },
{ path: 'docs/stats/lines-added-chart.svg', content: createSVGChart(linesAdded, 'Lines Added', '#22c55e', monthLabels) },
- { path: 'docs/stats/net-lines-changed-chart.svg', content: createSVGChart(totalLinesChanged, 'Total Lines Changed', '#f59e0b', monthLabels) },
+ { path: 'docs/stats/total-lines-changed-chart.svg', content: createSVGChart(totalLinesChanged, 'Total Lines Changed', '#f59e0b', monthLabels) },
{ path: 'docs/stats/active-contributors-chart.svg', content: createSVGChart(activeContributors, 'Active Contributors', '#8b5cf6', monthLabels) },
{ path: 'docs/stats/issues-closed-chart.svg', content: createSVGChart(issuesClosed, 'Issues Closed', '#ef4444', monthLabels) }
];
@@ -294,7 +294,7 @@ jobs:
// Create new branch
const now = new Date();
- const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth() ).toString().padStart(2, '0')}`;
+ const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth()-1 ).toString().padStart(2, '0')}`;
try {
const mainRef = await github.rest.git.getRef({
@@ -404,7 +404,7 @@ jobs:
{ title: '👥 Active Contributors', value: latest.stats.activeContributors.toString(), short: true },
{ title: '📈 Lines Added', value: latest.stats.linesAdded.toLocaleString(), short: true },
{ title: '📉 Lines Deleted', value: latest.stats.linesDeleted.toLocaleString(), short: true },
- { title: '⚖️ Net Lines Changed', value: latest.stats.totalLinesChanged.toLocaleString(), short: true },
+ { title: '⚖️ Total Lines Changed', value: latest.stats.totalLinesChanged.toLocaleString(), short: true },
{ title: '📁 Files Changed', value: latest.stats.filesChanged.toString(), short: true },
// Pull Request Activity
From f7f68d04ee2889124ead25675bb5b5511fb11edb Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:52:12 +0200
Subject: [PATCH 119/125] unfix fix
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 2f5547b277..75520fc42a 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -294,7 +294,7 @@ jobs:
// Create new branch
const now = new Date();
- const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth()-1 ).toString().padStart(2, '0')}`;
+ const branchName = `update-stats-${now.getFullYear()}-${(now.getMonth()).toString().padStart(2, '0')}`;
try {
const mainRef = await github.rest.git.getRef({
From e0ec3a28c9f43a4a3cf57ecff2158d9cf32fe640 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Wed, 8 Oct 2025 17:57:46 +0200
Subject: [PATCH 120/125] fx
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 75520fc42a..0a2487ba80 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -217,7 +217,7 @@ jobs:
const chartHeight = height - 2 * padding - 20;
const maxValue = Math.max(...data);
- const minValue = Math.max(0, Math.min(...data) - Math.max(...data) * 0.1);
+ const minValue = Math.max(1, Math.min(...data) - Math.max(...data) * 0.1);
const range = maxValue - minValue || 1;
const points = data.map((value, index) => {
From 334f3b618b2b633dc0a1c4b523fb0b7738712082 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:01:36 +0100
Subject: [PATCH 121/125] test
---
.github/workflows/gh_statistics_bot.yml | 79 ++++++++++++++++++++++++-
1 file changed, 76 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 0a2487ba80..6d151661b9 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -268,6 +268,63 @@ jobs:
`;
}
+ function createCSVFile(monthlyData) {
+ // CSV headers
+ const headers = [
+ 'Month',
+ 'Total Commits',
+ 'Active Contributors',
+ 'Lines Added',
+ 'Lines Deleted',
+ 'Total Lines Changed',
+ 'Files Changed',
+ 'PRs Merged',
+ 'PRs Opened',
+ 'PRs Closed',
+ 'Issues Opened',
+ 'Issues Closed',
+ 'Releases',
+ 'Pre-Releases',
+ 'Latest Release',
+ 'Contributors'
+ ];
+
+ // Escape CSV value (handle commas and quotes)
+ const escapeCSV = (value) => {
+ if (value === null || value === undefined) return '';
+ const str = String(value);
+ if (str.includes(',') || str.includes('"') || str.includes('\n')) {
+ return `"${str.replace(/"/g, '""')}"`;
+ }
+ return str;
+ };
+
+ // Build CSV rows
+ const rows = monthlyData.map(d => [
+ d.month,
+ d.stats.totalCommits,
+ d.stats.activeContributors,
+ d.stats.linesAdded,
+ d.stats.linesDeleted,
+ d.stats.totalLinesChanged,
+ d.stats.filesChanged,
+ d.prStats.merged,
+ d.prStats.opened,
+ d.prStats.closed,
+ d.issueStats.opened,
+ d.issueStats.closed,
+ d.releaseStats.total,
+ d.releaseStats.preReleases,
+ d.releaseStats.latestRelease || 'N/A',
+ d.stats.contributorsList.join('; ')
+ ].map(escapeCSV).join(','));
+
+ // Combine headers and rows
+ const csvContent = [headers.join(','), ...rows].join('\n');
+
+ return csvContent;
+ }
+
async function createChartsOnBranch(monthlyData) {
const totalCommits = monthlyData.map(d => d.stats.totalCommits);
const mergedPRs = monthlyData.map(d => d.prStats.merged);
@@ -286,6 +343,17 @@ jobs:
{ path: 'docs/stats/issues-closed-chart.svg', content: createSVGChart(issuesClosed, 'Issues Closed', '#ef4444', monthLabels) }
];
+ // Create CSV file
+ const csvContent = createCSVFile(monthlyData);
+ const now = new Date();
+ const csvFileName = `monthly-statistics-${now.getFullYear()}-${(now.getMonth()).toString().padStart(2, '0')}.csv`;
+
+ charts.push({
+ path: `docs/stats/${csvFileName}`,
+ content: csvContent,
+ isBase64: false
+ });
+
// Get repo info
const repoInfo = await github.rest.repos.get({
owner: context.repo.owner,
@@ -340,7 +408,7 @@ jobs:
repo: context.repo.repo,
path: chart.path,
message: `Update ${chart.path}`,
- content: Buffer.from(chart.content).toString('base64'),
+ content: chart.isBase64 ? chart.content : Buffer.from(chart.content).toString('base64'),
branch: branchName
};
@@ -354,10 +422,14 @@ jobs:
}
const latestMonth = monthlyData[monthlyData.length - 1];
+ const now = new Date();
+ const csvFileName = `monthly-statistics-${now.getFullYear()}-${(now.getMonth()).toString().padStart(2, '0')}.csv`;
+
return {
branchName: branchName,
message: `Charts updated on branch ${branchName} for ${latestMonth.month}`,
- branchUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}`
+ branchUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}`,
+ csvUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${branchName}/docs/stats/${csvFileName}`
};
}
@@ -419,7 +491,8 @@ jobs:
text: `**👥 Active Contributors:** ${latest.stats.contributorsList.slice(0, 15).join(', ') || 'None'}\n` +
`**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**📈 Charts:** [View detailed charts on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
- `**📂 Repository:** ${context.repo.owner}/${context.repo.repo}`,
+ `**📊 CSV Report:** [Download CSV file](${branchResult.csvUrl})\n` +
+ `**� Repository:** ${context.repo.owner}/${context.repo.repo}`,
footer: 'Repository statistics • Generated by GitHub Actions'
}]
};
From b5dd7b4cb523322d16cff10f590875a595c8458c Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:02:54 +0100
Subject: [PATCH 122/125] uopdate
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 6d151661b9..3c768958af 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -345,8 +345,8 @@ jobs:
// Create CSV file
const csvContent = createCSVFile(monthlyData);
- const now = new Date();
- const csvFileName = `monthly-statistics-${now.getFullYear()}-${(now.getMonth()).toString().padStart(2, '0')}.csv`;
+ const csvNow = new Date();
+ const csvFileName = `monthly-statistics-${csvNow.getFullYear()}-${(csvNow.getMonth()).toString().padStart(2, '0')}.csv`;
charts.push({
path: `docs/stats/${csvFileName}`,
From ab496c0bbf9a9993956dd90b3a29740d50679159 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:03:11 +0100
Subject: [PATCH 123/125] update
---
.github/workflows/gh_statistics_bot.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 3c768958af..22bee3d13f 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -422,8 +422,7 @@ jobs:
}
const latestMonth = monthlyData[monthlyData.length - 1];
- const now = new Date();
- const csvFileName = `monthly-statistics-${now.getFullYear()}-${(now.getMonth()).toString().padStart(2, '0')}.csv`;
+ const csvFileName = `monthly-statistics-${csvNow.getFullYear()}-${(csvNow.getMonth()).toString().padStart(2, '0')}.csv`;
return {
branchName: branchName,
From daf93baa3d265e22b84695c4a564cf5d59e5ed48 Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:04:27 +0100
Subject: [PATCH 124/125] test
---
.github/workflows/gh_statistics_bot.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index 22bee3d13f..ed84d0bec8 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -422,13 +422,13 @@ jobs:
}
const latestMonth = monthlyData[monthlyData.length - 1];
- const csvFileName = `monthly-statistics-${csvNow.getFullYear()}-${(csvNow.getMonth()).toString().padStart(2, '0')}.csv`;
return {
branchName: branchName,
message: `Charts updated on branch ${branchName} for ${latestMonth.month}`,
branchUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}`,
- csvUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${branchName}/docs/stats/${csvFileName}`
+ csvUrl: `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${branchName}/docs/stats/${csvFileName}`,
+ csvFileName: csvFileName
};
}
From c95561c8bb1a415c87ceb1dcfa30b4ac12c6d2ee Mon Sep 17 00:00:00 2001
From: Sascha <51127093+xsaschako@users.noreply.github.com>
Date: Sat, 8 Nov 2025 17:25:26 +0100
Subject: [PATCH 125/125] bot update
---
.github/workflows/gh_statistics_bot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gh_statistics_bot.yml b/.github/workflows/gh_statistics_bot.yml
index ed84d0bec8..a752eb0bfe 100644
--- a/.github/workflows/gh_statistics_bot.yml
+++ b/.github/workflows/gh_statistics_bot.yml
@@ -491,7 +491,7 @@ jobs:
`**💻 Languages:** ${languages.map(l => `${l.language} (${l.percentage}%)`).join(', ') || 'N/A'}\n\n` +
`**📈 Charts:** [View detailed charts on branch ${branchResult.branchName}](${branchResult.branchUrl})\n` +
`**📊 CSV Report:** [Download CSV file](${branchResult.csvUrl})\n` +
- `**� Repository:** ${context.repo.owner}/${context.repo.repo}`,
+ `**🦠 Repository:** ${context.repo.owner}/${context.repo.repo}`,
footer: 'Repository statistics • Generated by GitHub Actions'
}]
};