From 1a45ed6371814fd65189123656c976568ebcbf0b Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 11 Nov 2025 18:07:40 -0500 Subject: [PATCH 01/12] Add Discord as a decision-making platform Include Discord alongside pull requests and GitHub discussions as a platform where technical and process decisions are made through consensus. --- GOVERNANCE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index b55dc0f36bf8..e7ad1aec5c96 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -49,7 +49,7 @@ Core Maintainers have admin access across all repositories but use standard cont ### Day-to-Day Decisions -* Most technical and process decisions are made through consensus in pull requests or GitHub discussions. +* Most technical and process decisions are made through consensus in pull requests, GitHub discussions, or Discord. * Core Maintainers can approve and merge changes quickly when there's clear benefit. * Significant architectural changes should have discussion in a GitHub issue or discussion before implementation. * Core Maintainers may step in when disputes arise or when decisions have project-wide impact. From 9c9fdd9da8d6a2319b4fdd99e9f7abbaa398b8f2 Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Mon, 17 Nov 2025 17:21:58 -0500 Subject: [PATCH 02/12] Add Community Stars analysis script and team lists This adds tooling to generate monthly Community Stars rankings for the goose repository. Files added: - scripts/community_stars.py: Python script that analyzes GitHub contributor stats - scripts/community_stars_teams.txt: Team categorization lists (easy to update via PR) Features: - Analyzes contributions by time period (monthly or date range) - Categorizes contributors into: Goose maintainers, Block non-goose, External, Bots - Generates Top 5 Community All-Stars (external contributors) - Generates Top 5 Team Stars (Block employees, non-goose) - Outputs monthly leaderboard with all eligible contributors - Scoring: commits + total lines changed (additions + deletions) Usage: curl -s -H 'Accept: application/vnd.github.v3+json' \ 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json python3 scripts/community_stars.py 'November 2024' The script can load team lists from local file or GitHub URL, making it usable both locally and in automated workflows. --- GOVERNANCE.md | 2 +- scripts/community_stars.py | 271 ++++++++++++++++++++++++++++++ scripts/community_stars_teams.txt | 113 +++++++++++++ 3 files changed, 385 insertions(+), 1 deletion(-) create mode 100755 scripts/community_stars.py create mode 100644 scripts/community_stars_teams.txt diff --git a/GOVERNANCE.md b/GOVERNANCE.md index e7ad1aec5c96..b55dc0f36bf8 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -49,7 +49,7 @@ Core Maintainers have admin access across all repositories but use standard cont ### Day-to-Day Decisions -* Most technical and process decisions are made through consensus in pull requests, GitHub discussions, or Discord. +* Most technical and process decisions are made through consensus in pull requests or GitHub discussions. * Core Maintainers can approve and merge changes quickly when there's clear benefit. * Significant architectural changes should have discussion in a GitHub issue or discussion before implementation. * Core Maintainers may step in when disputes arise or when decisions have project-wide impact. diff --git a/scripts/community_stars.py b/scripts/community_stars.py new file mode 100755 index 000000000000..e52da1fa4066 --- /dev/null +++ b/scripts/community_stars.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 +""" +Community Stars Analysis Script for block/goose repository + +This script analyzes GitHub contributor statistics and generates rankings for: +- Top 5 Community All-Stars (External contributors) +- Top 5 Team Stars (Block employees, non-goose team) +- Monthly Leaderboard (all eligible contributors) + +Usage: + python3 community_stars.py "November 2025" + python3 community_stars.py "November 1, 2025 - November 17, 2025" + +Requirements: + - GitHub contributor data at /tmp/github_contributors.json + - Team list file (local or from GitHub) +""" + +import json +import re +import sys +import urllib.request +from datetime import datetime +import calendar +from pathlib import Path + +# GitHub URL for team list file +TEAMS_FILE_URL = "https://raw.githubusercontent.com/block/goose/main/scripts/community_stars_teams.txt" +LOCAL_TEAMS_FILE = Path(__file__).parent / "community_stars_teams.txt" + +def load_team_lists(): + """Load and parse team lists from file (local or GitHub).""" + content = None + + # Try local file first + if LOCAL_TEAMS_FILE.exists(): + with open(LOCAL_TEAMS_FILE, 'r') as f: + content = f.read() + else: + # Fall back to GitHub + try: + with urllib.request.urlopen(TEAMS_FILE_URL) as response: + content = response.read().decode('utf-8') + except Exception as e: + print(f"Error: Could not load team list file from {TEAMS_FILE_URL}") + print(f"Details: {e}") + sys.exit(1) + + # Parse the team lists + goose_maintainers = set() + block_non_goose = set() + external_goose = set() + external = set() + bots = set() + + current_section = None + for line in content.split('\n'): + line = line.strip() + + # Skip comments and empty lines + if not line or line.startswith('#'): + # Check for section headers in comments + if '# Goose Maintainers' in line: + current_section = 'goose_maintainers' + elif '# Block, non-goose' in line: + current_section = 'block_non_goose' + elif '# External, goose' in line: + current_section = 'external_goose' + elif '# External' in line and 'goose' not in line: + current_section = 'external' + elif '# Bots' in line: + current_section = 'bots' + continue + + # Add username to appropriate set + username = line.lower() + if current_section == 'goose_maintainers': + goose_maintainers.add(username) + elif current_section == 'block_non_goose': + block_non_goose.add(username) + elif current_section == 'external_goose': + external_goose.add(username) + elif current_section == 'external': + external.add(username) + elif current_section == 'bots': + bots.add(username) + + return goose_maintainers, block_non_goose, external_goose, external, bots + +def parse_date_range(date_input): + """Parse various date input formats and return start/end timestamps.""" + date_input = date_input.strip() + + # Format: "Month YYYY" (e.g., "November 2025") + month_year_pattern = r'^(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{4})$' + match = re.match(month_year_pattern, date_input, re.IGNORECASE) + if match: + month_name = match.group(1) + year = int(match.group(2)) + start_date = datetime.strptime(f"{month_name} 1 {year}", "%B %d %Y") + last_day = calendar.monthrange(year, start_date.month)[1] + end_date = datetime(year, start_date.month, last_day, 23, 59, 59) + return start_date.timestamp(), end_date.timestamp(), date_input + + # Format: "Date1 - Date2" (e.g., "November 1, 2025 - November 17, 2025") + if ' - ' in date_input or ' to ' in date_input: + separator = ' - ' if ' - ' in date_input else ' to ' + parts = date_input.split(separator) + if len(parts) == 2: + date_formats = ["%B %d, %Y", "%b %d, %Y", "%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y"] + start_date = None + end_date = None + + for fmt in date_formats: + try: + start_date = datetime.strptime(parts[0].strip(), fmt) + end_date = datetime.strptime(parts[1].strip(), fmt) + break + except ValueError: + continue + + if start_date and end_date: + end_date = datetime(end_date.year, end_date.month, end_date.day, 23, 59, 59) + return start_date.timestamp(), end_date.timestamp(), date_input + + raise ValueError(f"Could not parse date input: {date_input}\nSupported formats:\n - 'Month YYYY' (e.g., 'November 2025')\n - 'Month Day, YYYY - Month Day, YYYY' (e.g., 'November 1, 2025 - November 17, 2025')") + +def main(): + # Parse command line arguments + if len(sys.argv) < 2: + print("Usage: python3 community_stars.py 'date_range'") + print("Examples:") + print(" python3 community_stars.py 'November 2025'") + print(" python3 community_stars.py 'November 1, 2025 - November 17, 2025'") + sys.exit(1) + + date_input = sys.argv[1] + try: + start_timestamp, end_timestamp, display_period = parse_date_range(date_input) + start_date = datetime.fromtimestamp(start_timestamp) + end_date = datetime.fromtimestamp(end_timestamp) + except ValueError as e: + print(f"Error: {e}") + sys.exit(1) + + # Load team lists + goose_maintainers, block_non_goose, external_goose, external, bots = load_team_lists() + + # Load GitHub data + github_data_file = '/tmp/github_contributors.json' + try: + with open(github_data_file, 'r') as f: + contributors_data = json.load(f) + except FileNotFoundError: + print(f"Error: GitHub contributor data not found at {github_data_file}") + print("Please run: curl -s -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") + sys.exit(1) + + # Process contributors + contributor_stats = [] + + for contributor in contributors_data: + # Skip if author is None (deleted users) + if contributor.get('author') is None: + continue + + username = contributor['author']['login'] + username_lower = username.lower() + + # Skip excluded categories + if username_lower in bots or username_lower in goose_maintainers or username_lower in external_goose: + continue + + # Calculate stats for the specified period + period_commits = 0 + period_additions = 0 + period_deletions = 0 + + for week in contributor['weeks']: + week_timestamp = week['w'] + if start_timestamp <= week_timestamp <= end_timestamp: + period_commits += week['c'] + period_additions += week['a'] + period_deletions += week['d'] + + # Only include contributors with activity in the period + if period_commits > 0: + total_lines = period_additions + period_deletions + + # Categorize (only Block non-goose and External now) + if username_lower in block_non_goose: + category = 'block_non_goose' + elif username_lower in external: + category = 'external' + else: + category = 'unknown' + + contributor_stats.append({ + 'username': username, + 'category': category, + 'commits': period_commits, + 'additions': period_additions, + 'deletions': period_deletions, + 'total_lines': total_lines, + 'score': period_commits + total_lines + }) + + # Sort by score + contributor_stats.sort(key=lambda x: x['score'], reverse=True) + + # Separate by category + block_list = [c for c in contributor_stats if c['category'] == 'block_non_goose'] + external_list = [c for c in contributor_stats if c['category'] == 'external'] + unknown_list = [c for c in contributor_stats if c['category'] == 'unknown'] + + # Get top 5 from each + top_external = external_list[:5] + top_internal = block_list[:5] + + # Print results + print("=" * 70) + print(f"COMMUNITY STARS - {display_period.upper()}") + print(f"(Period: {start_date.strftime('%B %d, %Y')} - {end_date.strftime('%B %d, %Y')})") + print("=" * 70) + print() + + print("🏆 TOP 5 COMMUNITY ALL-STARS (External Contributors)") + print("-" * 70) + if top_external: + for i, contrib in enumerate(top_external, 1): + print(f"{i}. @{contrib['username']:20s} - {contrib['commits']:3d} commits, {contrib['total_lines']:6,d} lines") + else: + print("No external contributors found for this period.") + + print() + print("⭐ TOP 5 TEAM STARS (Block, non-goose)") + print("-" * 70) + if top_internal: + for i, contrib in enumerate(top_internal, 1): + print(f"{i}. @{contrib['username']:20s} - {contrib['commits']:3d} commits, {contrib['total_lines']:6,d} lines") + else: + print("No internal contributors found for this period.") + + print() + print("📊 MONTHLY LEADERBOARD (All Contributors)") + print("-" * 70) + if contributor_stats: + for i, contrib in enumerate(contributor_stats, 1): + cat_label = "External" if contrib['category'] == 'external' else "Block" if contrib['category'] == 'block_non_goose' else "Unknown" + print(f"{i:2d}. @{contrib['username']:20s} - {contrib['commits']:3d} commits, {contrib['total_lines']:6,d} lines [{cat_label}]") + else: + print("No contributors found for this period.") + + if unknown_list: + print() + print("⚠️ UNKNOWN CONTRIBUTORS (not in team lists):") + print("-" * 70) + for contrib in unknown_list: + print(f" @{contrib['username']:20s} - {contrib['commits']:3d} commits, {contrib['total_lines']:6,d} lines") + + print() + print("=" * 70) + print(f"Total contributors (excluding bots, goose maintainers, external goose): {len(contributor_stats)}") + print(f" External: {len(external_list)}") + print(f" Block (non-goose): {len(block_list)}") + if unknown_list: + print(f" Unknown: {len(unknown_list)}") + print("=" * 70) + +if __name__ == "__main__": + main() diff --git a/scripts/community_stars_teams.txt b/scripts/community_stars_teams.txt new file mode 100644 index 000000000000..32b6e19fbef8 --- /dev/null +++ b/scripts/community_stars_teams.txt @@ -0,0 +1,113 @@ +# Community Stars Team Lists +# This file categorizes contributors for the block/goose Community Stars program +# Format: One username per line under each category header + +# Goose Maintainers (excluded from rankings) +angiejones +zanesq +michaelneale +alexhancock +blackgirlbytes +jamadeo +lily-de +dianed-square +yingjiehe-xyz +EbonyLouis +DOsinga +Kvadratni +salman1993 +acekyd +katzdave +taniandjerry +zakiali +tlongwell-block +aha-square +amed-xyz +emma-squared +nahiyankhan +jsibbison-square +agiuliano-square +spencrmartin +alicehau +opdich +aharvard +dhanji +baxen + +# Block, non-goose (eligible for Team Stars) +wendytang +kalvinnchau +lifeizhou-ap +angelahning +wpfleger96 +matthewdiamant +tiensi +JohnMAustin78 +jackjackbits +finn-block +shellz-n-stuff +sheagcraig +cloud-on-prem +dorien-koelemeijer +elenazherdeva +maniksurtani +AaronGoldsmith +exitcode0 +alexrrouse +JJSwigut +damienrj +joahg +simonsickle +taylorkmho +chaitanyarahalkar + +# External, goose (excluded from rankings) +The-Best-Codes +Abhijay007 + +# External (eligible for Community All-Stars) +ARYPROGRAMMER +dbraduan +codefromthecrypt +Better-Boy +GaryZhous +iandouglas +lamchau +laanak08 +Lymah123 +the-matrixneo +arielherself +Developerayo +SalvatoreT +sheikhlimon +cgwalters +Anudhyan +johnlanda +alexyao2015 +aegntic +bwalding +ajgray-stripe +sfc-gh-twhite +adhintz +sana-db +toyamagu-2021 +Shreyanshsingh23 +Jay4242 +jalateras +sings-to-bees-on-wednesdays +myaple +necaris +par5ul1 +rockwotj +ki3ani +vlascik +eyelight +nick-w-nick +ayax79 + +# Bots (excluded from rankings) +dependabot[bot] +Copilot +claude +SquareGist +github-actions[bot] From d863712b6d745dd935d144fb0d545717e6976f46 Mon Sep 17 00:00:00 2001 From: taniandjerry <126204004+taniandjerry@users.noreply.github.com> Date: Mon, 17 Nov 2025 17:40:17 -0500 Subject: [PATCH 03/12] Update scripts/community_stars.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/community_stars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/community_stars.py b/scripts/community_stars.py index e52da1fa4066..540691d929fa 100755 --- a/scripts/community_stars.py +++ b/scripts/community_stars.py @@ -95,7 +95,7 @@ def parse_date_range(date_input): month_year_pattern = r'^(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{4})$' match = re.match(month_year_pattern, date_input, re.IGNORECASE) if match: - month_name = match.group(1) + month_name = match.group(1).capitalize() year = int(match.group(2)) start_date = datetime.strptime(f"{month_name} 1 {year}", "%B %d %Y") last_day = calendar.monthrange(year, start_date.month)[1] From 81814522d712905730421996bee6e858e0d68c5a Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Mon, 17 Nov 2025 17:40:32 -0500 Subject: [PATCH 04/12] Address Copilot review suggestions - Fix case-insensitive month parsing bug (capitalize month name) - Add JSON decode error handling for malformed GitHub API responses - Improve External section header matching to use startswith for safety --- scripts/community_stars.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/community_stars.py b/scripts/community_stars.py index 540691d929fa..4fb68343787f 100755 --- a/scripts/community_stars.py +++ b/scripts/community_stars.py @@ -66,7 +66,7 @@ def load_team_lists(): current_section = 'block_non_goose' elif '# External, goose' in line: current_section = 'external_goose' - elif '# External' in line and 'goose' not in line: + elif line.startswith('# External') and 'goose' not in line.lower(): current_section = 'external' elif '# Bots' in line: current_section = 'bots' @@ -155,6 +155,11 @@ def main(): print(f"Error: GitHub contributor data not found at {github_data_file}") print("Please run: curl -s -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON in {github_data_file}") + print(f"Details: {e}") + print("The GitHub API may have returned an error. Try fetching the data again.") + sys.exit(1) # Process contributors contributor_stats = [] From 28ee6ff194fa3ba11f192020f7229ef814bca68f Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 18 Nov 2025 09:24:22 -0500 Subject: [PATCH 05/12] Move community stars scripts to documentation/scripts directory - Move community_stars.py to documentation/scripts/ - Move community_stars_teams.txt to documentation/scripts/ - Better organization for documentation-related scripts --- {scripts => documentation/scripts}/community_stars.py | 0 {scripts => documentation/scripts}/community_stars_teams.txt | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {scripts => documentation/scripts}/community_stars.py (100%) rename {scripts => documentation/scripts}/community_stars_teams.txt (100%) diff --git a/scripts/community_stars.py b/documentation/scripts/community_stars.py similarity index 100% rename from scripts/community_stars.py rename to documentation/scripts/community_stars.py diff --git a/scripts/community_stars_teams.txt b/documentation/scripts/community_stars_teams.txt similarity index 100% rename from scripts/community_stars_teams.txt rename to documentation/scripts/community_stars_teams.txt From 8667a1ade81946c1c952586856d176dde23db783 Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 18 Nov 2025 10:03:10 -0500 Subject: [PATCH 06/12] Remove ambiguous date formats, keep only ISO format - Remove %m/%d/%Y (US) and %d/%m/%Y (international) formats - Keep only unambiguous formats: Month names and ISO 8601 (YYYY-MM-DD) - Update documentation and error messages - Addresses Copilot review comment about ambiguous date parsing --- documentation/scripts/community_stars.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index 4fb68343787f..ccebbc7a280c 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -10,6 +10,7 @@ Usage: python3 community_stars.py "November 2025" python3 community_stars.py "November 1, 2025 - November 17, 2025" + python3 community_stars.py "2025-11-01 - 2025-11-17" Requirements: - GitHub contributor data at /tmp/github_contributors.json @@ -102,12 +103,12 @@ def parse_date_range(date_input): end_date = datetime(year, start_date.month, last_day, 23, 59, 59) return start_date.timestamp(), end_date.timestamp(), date_input - # Format: "Date1 - Date2" (e.g., "November 1, 2025 - November 17, 2025") + # Format: "Date1 - Date2" (e.g., "November 1, 2025 - November 17, 2025" or "2025-11-01 - 2025-11-17") if ' - ' in date_input or ' to ' in date_input: separator = ' - ' if ' - ' in date_input else ' to ' parts = date_input.split(separator) if len(parts) == 2: - date_formats = ["%B %d, %Y", "%b %d, %Y", "%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y"] + date_formats = ["%B %d, %Y", "%b %d, %Y", "%Y-%m-%d"] start_date = None end_date = None @@ -123,7 +124,7 @@ def parse_date_range(date_input): end_date = datetime(end_date.year, end_date.month, end_date.day, 23, 59, 59) return start_date.timestamp(), end_date.timestamp(), date_input - raise ValueError(f"Could not parse date input: {date_input}\nSupported formats:\n - 'Month YYYY' (e.g., 'November 2025')\n - 'Month Day, YYYY - Month Day, YYYY' (e.g., 'November 1, 2025 - November 17, 2025')") + raise ValueError(f"Could not parse date input: {date_input}\nSupported formats:\n - 'Month YYYY' (e.g., 'November 2025')\n - 'Month Day, YYYY - Month Day, YYYY' (e.g., 'November 1, 2025 - November 17, 2025')\n - 'YYYY-MM-DD - YYYY-MM-DD' (e.g., '2025-11-01 - 2025-11-17')") def main(): # Parse command line arguments @@ -132,6 +133,7 @@ def main(): print("Examples:") print(" python3 community_stars.py 'November 2025'") print(" python3 community_stars.py 'November 1, 2025 - November 17, 2025'") + print(" python3 community_stars.py '2025-11-01 - 2025-11-17'") sys.exit(1) date_input = sys.argv[1] From 53178a40b371946b71cf2379af427fd8b712eda9 Mon Sep 17 00:00:00 2001 From: taniandjerry <126204004+taniandjerry@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:17:33 -0500 Subject: [PATCH 07/12] Update documentation/scripts/community_stars.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- documentation/scripts/community_stars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index ccebbc7a280c..7c746baaab53 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -26,7 +26,7 @@ from pathlib import Path # GitHub URL for team list file -TEAMS_FILE_URL = "https://raw.githubusercontent.com/block/goose/main/scripts/community_stars_teams.txt" +TEAMS_FILE_URL = "https://raw.githubusercontent.com/block/goose/main/documentation/scripts/community_stars_teams.txt" LOCAL_TEAMS_FILE = Path(__file__).parent / "community_stars_teams.txt" def load_team_lists(): From 79b42c408ba1348b7189782f8b4735a87e4020b0 Mon Sep 17 00:00:00 2001 From: taniandjerry <126204004+taniandjerry@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:18:21 -0500 Subject: [PATCH 08/12] Update documentation/scripts/community_stars.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- documentation/scripts/community_stars.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index 7c746baaab53..aa1dcdefa2cb 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -155,7 +155,9 @@ def main(): contributors_data = json.load(f) except FileNotFoundError: print(f"Error: GitHub contributor data not found at {github_data_file}") - print("Please run: curl -s -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") + print("Please run the following command to fetch contributor data (replace YOUR_TOKEN with a GitHub personal access token):") + print(" curl -s -H 'Authorization: token YOUR_TOKEN' -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") + print("Note: Using a token avoids hitting GitHub's low unauthenticated rate limits. You can create a token at https://github.com/settings/tokens (no scopes required).") sys.exit(1) except json.JSONDecodeError as e: print(f"Error: Invalid JSON in {github_data_file}") From 9e36cbdd753a00729b0b3e0851847acf812cbfbc Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 18 Nov 2025 10:20:18 -0500 Subject: [PATCH 09/12] Add comments clarifying case-insensitive username matching - Document that .lower() is applied to all usernames including bot names - Clarify that bot names with brackets (e.g., 'dependabot[bot]') are handled - Address Copilot review comment about consistent case handling --- documentation/scripts/community_stars.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index aa1dcdefa2cb..89cacffe8ec9 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -73,7 +73,8 @@ def load_team_lists(): current_section = 'bots' continue - # Add username to appropriate set + # Add username to appropriate set (lowercase for case-insensitive matching) + # This handles bot names like "dependabot[bot]" -> "dependabot[bot]" username = line.lower() if current_section == 'goose_maintainers': goose_maintainers.add(username) @@ -176,7 +177,7 @@ def main(): username = contributor['author']['login'] username_lower = username.lower() - # Skip excluded categories + # Skip excluded categories (case-insensitive matching) if username_lower in bots or username_lower in goose_maintainers or username_lower in external_goose: continue From ae54fd0163f882c326abdeaf0a35a51fb70d110d Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 18 Nov 2025 10:23:47 -0500 Subject: [PATCH 10/12] Revert GitHub token requirement in error message - Remove requirement for personal access token - Keep simple curl command without authentication - Makes the script easier to use for most cases --- documentation/scripts/community_stars.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index 89cacffe8ec9..f0fc83c84562 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -156,9 +156,7 @@ def main(): contributors_data = json.load(f) except FileNotFoundError: print(f"Error: GitHub contributor data not found at {github_data_file}") - print("Please run the following command to fetch contributor data (replace YOUR_TOKEN with a GitHub personal access token):") - print(" curl -s -H 'Authorization: token YOUR_TOKEN' -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") - print("Note: Using a token avoids hitting GitHub's low unauthenticated rate limits. You can create a token at https://github.com/settings/tokens (no scopes required).") + print("Please run: curl -s -H 'Accept: application/vnd.github.v3+json' 'https://api.github.com/repos/block/goose/stats/contributors' > /tmp/github_contributors.json") sys.exit(1) except json.JSONDecodeError as e: print(f"Error: Invalid JSON in {github_data_file}") From ffdf741a79e22774b71a42cf9911d61278f9a491 Mon Sep 17 00:00:00 2001 From: taniandjerry <126204004+taniandjerry@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:26:36 -0500 Subject: [PATCH 11/12] Update documentation/scripts/community_stars_teams.txt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- documentation/scripts/community_stars_teams.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/scripts/community_stars_teams.txt b/documentation/scripts/community_stars_teams.txt index 32b6e19fbef8..7305b9b7cb29 100644 --- a/documentation/scripts/community_stars_teams.txt +++ b/documentation/scripts/community_stars_teams.txt @@ -107,7 +107,5 @@ ayax79 # Bots (excluded from rankings) dependabot[bot] -Copilot -claude SquareGist github-actions[bot] From 907f453eedf3f7ddff9c6c7d164f4184f6999ba3 Mon Sep 17 00:00:00 2001 From: Tania Chakraborty Date: Tue, 18 Nov 2025 10:31:53 -0500 Subject: [PATCH 12/12] Clarify that .lower() applies to entire username including brackets - Reference the pattern used elsewhere: 'goose' not in line.lower() - Make it explicit that brackets are included in lowercasing - Addresses Copilot feedback about consistent case handling --- documentation/scripts/community_stars.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/scripts/community_stars.py b/documentation/scripts/community_stars.py index f0fc83c84562..6abf42fe6dca 100755 --- a/documentation/scripts/community_stars.py +++ b/documentation/scripts/community_stars.py @@ -74,7 +74,8 @@ def load_team_lists(): continue # Add username to appropriate set (lowercase for case-insensitive matching) - # This handles bot names like "dependabot[bot]" -> "dependabot[bot]" + # Apply .lower() to entire username including brackets (e.g., "dependabot[bot]") + # This matches the pattern used above: 'goose' not in line.lower() username = line.lower() if current_section == 'goose_maintainers': goose_maintainers.add(username)