From d46235357738dce17b7600c83694c3c56b04ba74 Mon Sep 17 00:00:00 2001 From: Su <112690947+sushmangupta@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:59:29 +0100 Subject: [PATCH 1/2] Add repository stats workflow with Slack integration This workflow collects and reports repository statistics for pull requests and issues, with options for Slack notifications. --- .github/workflows/repo-stats.yml | 189 +++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 .github/workflows/repo-stats.yml diff --git a/.github/workflows/repo-stats.yml b/.github/workflows/repo-stats.yml new file mode 100644 index 0000000..3722b82 --- /dev/null +++ b/.github/workflows/repo-stats.yml @@ -0,0 +1,189 @@ +name: Repository stats (PRs & Issues) + +on: + workflow_dispatch: + inputs: + days: + description: "Number of days to look back" + required: false + default: 7 + type: number + post_to_slack: + description: "Post report to Slack" + required: false + default: false + type: boolean + schedule: + # Weekly on Monday at 09:00 UTC + - cron: "0 9 * * 1" + +jobs: + gather-stats: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + steps: + - name: Gather PR and issue stats + id: stats + env: + GH_REPO: ${{ github.repository }} + GH_SERVER: ${{ github.server_url }} + DAYS_RAW: ${{ github.event.inputs.days || 7 }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Clamp days to 1–365 + DAYS=$DAYS_RAW + if ! [[ "$DAYS" =~ ^[0-9]+$ ]] || [[ "$DAYS" -lt 1 ]]; then DAYS=7; fi + if [[ "$DAYS" -gt 365 ]]; then DAYS=365; fi + + START=$(date -u -d "${DAYS} days ago" +%Y-%m-%d) + END=$(date -u +%Y-%m-%d) + + echo "start_date=$START" >> $GITHUB_OUTPUT + echo "end_date=$END" >> $GITHUB_OUTPUT + + OWNER="${GH_REPO%%/*}" + REPO="${GH_REPO#*/}" + API="https://api.github.com/repos/${OWNER}/${REPO}" + + # Fetch all data + curl -sS -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" \ + "${API}/pulls?state=all&sort=created&direction=desc&per_page=100" > /tmp/pulls_all.json + curl -sS -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" \ + "${API}/pulls?state=closed&sort=updated&direction=desc&per_page=100" > /tmp/pulls_closed.json + curl -sS -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" \ + "${API}/issues?state=all&sort=created&direction=desc&per_page=100" > /tmp/issues_all.json + curl -sS -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" \ + "${API}/issues?state=closed&sort=updated&direction=desc&per_page=100" > /tmp/issues_closed.json + + # Counts for the period + PR_OPENED=$(jq -r --arg start "$START" '[.[] | select(.created_at[:10] >= $start)] | length' /tmp/pulls_all.json) + PR_MERGED=$(jq -r --arg start "$START" '[.[] | select(.merged_at != null and (.merged_at[:10] >= $start))] | length' /tmp/pulls_closed.json) + PR_CLOSED=$(jq -r --arg start "$START" '[.[] | select(.merged_at == null and .closed_at != null and (.closed_at[:10] >= $start))] | length' /tmp/pulls_closed.json) + ISSUES_OPENED=$(jq -r --arg start "$START" '[.[] | select(.pull_request == null and .created_at[:10] >= $start)] | length' /tmp/issues_all.json) + ISSUES_CLOSED=$(jq -r --arg start "$START" '[.[] | select(.pull_request == null and .closed_at != null and (.closed_at[:10] >= $start))] | length' /tmp/issues_closed.json) + + BASE="$GH_SERVER/$GH_REPO" + + # --- detail.md: full overview with counts in headings --- + echo "## Detailed repository activity: $START → $END" > detail.md + echo "" >> detail.md + + echo "### Pull requests created ($PR_OPENED)" >> detail.md + echo "" >> detail.md + jq -r --arg start "$START" --arg base "$BASE" '.[] | select(.created_at[:10] >= $start) | "- [\(.state | ascii_upcase)] [#\(.number) \(.title) (created: \(.created_at[:10]))](\($base)/pull/\(.number))"' /tmp/pulls_all.json > /tmp/pr_created.txt 2>/dev/null || true + [ -s /tmp/pr_created.txt ] && cat /tmp/pr_created.txt >> detail.md || echo "- No PRs in this period" >> detail.md + echo "" >> detail.md + + echo "### Pull requests merged ($PR_MERGED)" >> detail.md + echo "" >> detail.md + jq -r --arg start "$START" --arg base "$BASE" '.[] | select(.merged_at != null and (.merged_at[:10] >= $start)) | "- [#\(.number) \(.title) (merged: \(.merged_at[:10]))](\($base)/pull/\(.number))"' /tmp/pulls_closed.json > /tmp/pr_merged.txt 2>/dev/null || true + [ -s /tmp/pr_merged.txt ] && cat /tmp/pr_merged.txt >> detail.md || echo "- No merged PRs in this period" >> detail.md + echo "" >> detail.md + + echo "### Pull requests closed ($PR_CLOSED)" >> detail.md + echo "" >> detail.md + jq -r --arg start "$START" --arg base "$BASE" '.[] | select(.merged_at == null and .closed_at != null and (.closed_at[:10] >= $start)) | "- [#\(.number) \(.title) (closed: \(.closed_at[:10]))](\($base)/pull/\(.number))"' /tmp/pulls_closed.json > /tmp/pr_closed.txt 2>/dev/null || true + [ -s /tmp/pr_closed.txt ] && cat /tmp/pr_closed.txt >> detail.md || echo "- No closed (unmerged) PRs in this period" >> detail.md + echo "" >> detail.md + + echo "### Issues opened ($ISSUES_OPENED)" >> detail.md + echo "" >> detail.md + jq -r --arg start "$START" --arg base "$BASE" '.[] | select(.pull_request == null and .created_at[:10] >= $start) | "- [\(.state | ascii_upcase)] [#\(.number) \(.title) (opened: \(.created_at[:10]))](\($base)/issues/\(.number))"' /tmp/issues_all.json > /tmp/issue_opened.txt 2>/dev/null || true + [ -s /tmp/issue_opened.txt ] && cat /tmp/issue_opened.txt >> detail.md || echo "- No issues in this period" >> detail.md + echo "" >> detail.md + + echo "### Issues closed ($ISSUES_CLOSED)" >> detail.md + echo "" >> detail.md + jq -r --arg start "$START" --arg base "$BASE" '.[] | select(.pull_request == null and .closed_at != null and (.closed_at[:10] >= $start)) | "- [#\(.number) \(.title) (closed: \(.closed_at[:10]))](\($base)/issues/\(.number))"' /tmp/issues_closed.json > /tmp/issue_closed.txt 2>/dev/null || true + [ -s /tmp/issue_closed.txt ] && cat /tmp/issue_closed.txt >> detail.md || echo "- No closed issues in this period" >> detail.md + + # --- slack.md + slack_report.txt: Slack summary (PRs opened/closed/merged, Issues opened/closed) --- + { + echo ":books: *Weekly Docs Update (Git Repository)*" + echo "" + echo "*API Docs*" + echo "" + echo "*PRs*" + echo ":inbox_tray: $PR_OPENED PRs opened" + echo ":white_check_mark: $PR_MERGED PRs merged" + echo ":lock: $PR_CLOSED PRs closed" + echo "" + echo "*Issues*" + echo ":inbox_tray: $ISSUES_OPENED issues opened" + echo ":ladybug: $ISSUES_CLOSED issues closed" + echo "" + echo "View workflow run: $BASE/actions/runs/${{ github.run_id }}" + } > slack.md + echo "" >> slack.md + echo '```' >> slack.md + cat detail.md >> slack.md + echo '```' >> slack.md + cp slack.md slack_report.txt + + - name: Upload report + uses: actions/upload-artifact@v4 + with: + name: repo-stats-report + path: | + detail.md + slack.md + + - name: Print detail report + run: cat detail.md + + - name: Prepare Slack payload + id: slack_payload + if: (github.event_name == 'workflow_dispatch' && github.event.inputs.post_to_slack == 'true') || (github.event_name == 'schedule') + env: + START_DATE: ${{ steps.stats.outputs.start_date }} + END_DATE: ${{ steps.stats.outputs.end_date }} + GH_SERVER: ${{ github.server_url }} + GH_REPO: ${{ github.repository }} + GITHUB_RUN_ID: ${{ github.run_id }} + run: | + # Split slack_report.txt into ≤3000-char chunks at newline boundaries + awk ' + BEGIN { n=0; cur=""; len=0 } + { + l = length($0) + 1 + if (len + l > 3000 && len > 0) { + printf "%s", cur > ("/tmp/sw_chunk_" n ".txt") + n++; cur = $0 "\n"; len = l + } else { cur = cur $0 "\n"; len += l } + } + END { + if (len > 0) { printf "%s", cur > ("/tmp/sw_chunk_" n ".txt"); n++ } + print n > "/tmp/sw_chunk_count.txt" + } + ' slack_report.txt + + NUM_CHUNKS=$(cat /tmp/sw_chunk_count.txt) + if ! [[ "$NUM_CHUNKS" =~ ^[0-9]+$ ]] || [[ "$NUM_CHUNKS" -lt 1 ]]; then NUM_CHUNKS=1; fi + RAWFILE_ARGS=() + SECTION_EXPRS=() + for i in $(seq 0 $((NUM_CHUNKS - 1))); do + RAWFILE_ARGS+=(--rawfile "c${i}" "/tmp/sw_chunk_${i}.txt") + SECTION_EXPRS+=("{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\$c${i}}}") + done + SECTIONS_STR=$(IFS=','; echo "${SECTION_EXPRS[*]}") + + jq -n "${RAWFILE_ARGS[@]}" \ + --arg start "$START_DATE" --arg end "$END_DATE" \ + --arg link "$GH_SERVER/$GH_REPO/actions/runs/$GITHUB_RUN_ID" \ + '{blocks:([{type:"header",text:{type:"plain_text",text:"📚 Weekly API Docs Update (Git Repository)",emoji:true}},{type:"context",elements:[{type:"mrkdwn",text:($start+" → "+$end+" | <"+$link+"|View run>")}]},{type:"divider"}]+['"$SECTIONS_STR"'])}' \ + > payload.json + echo "payload<> $GITHUB_OUTPUT + cat payload.json >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Post to Slack + if: (github.event_name == 'workflow_dispatch' && github.event.inputs.post_to_slack == 'true') || (github.event_name == 'schedule') + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: ${{ steps.slack_payload.outputs.payload }} + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFICATIONS_WEBHOOK }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK From 627ff624a22a4576a9c3d9caa6bc12df3e5881a4 Mon Sep 17 00:00:00 2001 From: Su <112690947+sushmangupta@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:53:37 +0100 Subject: [PATCH 2/2] Change API Docs to Dev Docs and update artifact name Updated the workflow to reflect 'Dev Docs' instead of 'API Docs' and modified the artifact name to include the repository name. --- .github/workflows/repo-stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/repo-stats.yml b/.github/workflows/repo-stats.yml index 3722b82..af0336d 100644 --- a/.github/workflows/repo-stats.yml +++ b/.github/workflows/repo-stats.yml @@ -104,7 +104,7 @@ jobs: { echo ":books: *Weekly Docs Update (Git Repository)*" echo "" - echo "*API Docs*" + echo "*Dev Docs*" echo "" echo "*PRs*" echo ":inbox_tray: $PR_OPENED PRs opened" @@ -126,7 +126,7 @@ jobs: - name: Upload report uses: actions/upload-artifact@v4 with: - name: repo-stats-report + name: repo-stats-report-${{ matrix.repo }} path: | detail.md slack.md