From 1fe835ee36870f4597e1b60c1a880b6724e77002 Mon Sep 17 00:00:00 2001 From: Enes Tekdemir Date: Sun, 2 Aug 2026 20:51:42 +0300 Subject: [PATCH] fix: include private contributor statistics safely --- .github/workflows/update-projects.yml | 31 +++++++++++++++++---------- scripts/update_projects.go | 22 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/.github/workflows/update-projects.yml b/.github/workflows/update-projects.yml index 97fa254..f6d87bf 100644 --- a/.github/workflows/update-projects.yml +++ b/.github/workflows/update-projects.yml @@ -5,30 +5,39 @@ on: - cron: '0 0 * * *' workflow_dispatch: +permissions: + contents: write + +concurrency: + group: update-projects + cancel-in-progress: false + jobs: update-projects: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: - go-version: '1.24' - - name: Prepare Go module and install dependencies + go-version: '1.26' + - name: Download and verify dependencies run: | - if [ ! -f go.mod ]; then - go mod init tempmod - fi - go mod tidy + go mod download + go mod verify + go test ./... - name: Run update_projects.go env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Falls back to the repository token until an organization owner adds + # the read-only ORG_STATS_TOKEN secret to the upstream repository. + GITHUB_TOKEN: ${{ secrets.ORG_STATS_TOKEN || github.token }} run: | go run scripts/update_projects.go - name: Commit and push if changed run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' git add profile/README.md git diff --cached --quiet || git commit -m 'chore: update projects section [auto]' git push diff --git a/scripts/update_projects.go b/scripts/update_projects.go index 5c56a3f..2374b51 100644 --- a/scripts/update_projects.go +++ b/scripts/update_projects.go @@ -2,6 +2,7 @@ package main import ( context "context" + "errors" "fmt" "os" "regexp" @@ -52,7 +53,7 @@ var ( func fetchOrgRepos(client *gh.Client, ctx context.Context, org string) ([]*gh.Repository, error) { var allRepos []*gh.Repository - opt := &gh.RepositoryListByOrgOptions{Type: "public", ListOptions: gh.ListOptions{PerPage: 100}} + opt := &gh.RepositoryListByOrgOptions{Type: "all", ListOptions: gh.ListOptions{PerPage: 100}} for { repos, resp, err := client.Repositories.ListByOrg(ctx, org, opt) if err != nil { @@ -215,7 +216,7 @@ func formatMarkdown(repos []GhProjects) string { return b.String() } -func fetchContributors(client *gh.Client, ctx context.Context, org string, repos []*gh.Repository) []ContributorStats { +func fetchContributors(client *gh.Client, ctx context.Context, org string, repos []*gh.Repository) ([]ContributorStats, error) { contribMap := map[string]*ContributorStats{} since := time.Now().AddDate(0, 0, -30) @@ -231,7 +232,7 @@ func fetchContributors(client *gh.Client, ctx context.Context, org string, repos for { commits, resp, err := client.Repositories.ListCommits(ctx, org, repo.GetName(), commitOpt) if err != nil { - break + return nil, errors.New("failed to fetch contributor commit statistics") } for _, c := range commits { if c.Author == nil { @@ -267,7 +268,7 @@ func fetchContributors(client *gh.Client, ctx context.Context, org string, repos for { issues, resp, err := client.Issues.ListByRepo(ctx, org, repo.GetName(), issueOpt) if err != nil { - break + return nil, errors.New("failed to fetch contributor issue statistics") } for _, issue := range issues { if issue.User == nil || issue.PullRequestLinks != nil { @@ -304,7 +305,7 @@ func fetchContributors(client *gh.Client, ctx context.Context, org string, repos for { prs, resp, err := client.PullRequests.List(ctx, org, repo.GetName(), prOpt) if err != nil { - break + return nil, errors.New("failed to fetch contributor pull request statistics") } shouldBreak := false for _, pr := range prs { @@ -362,7 +363,7 @@ func fetchContributors(client *gh.Client, ctx context.Context, org string, repos contribs[i].Badges = badges } sort.Slice(contribs, func(i, j int) bool { return contribs[i].XP > contribs[j].XP }) - return contribs + return contribs, nil } func formatContributorsMarkdown(contribs []ContributorStats) string { @@ -405,7 +406,9 @@ func main() { var statsList []RepoStats mostStars := 0 for _, repo := range repos { - if slices.Contains(excludedProjects, repo.GetName()) { + // Private repositories contribute to the contributor ranking, but their + // names and project statistics must never be published in the public README. + if repo.GetPrivate() || slices.Contains(excludedProjects, repo.GetName()) { continue } stats, err := fetchRepoStats(client, ctx, org, repo) @@ -432,7 +435,10 @@ func main() { md := formatMarkdown(projects[:displayCount]) md += "\n\n[...and more projects](https://github.com/HappyHackingSpace?tab=repositories)" - contributors := fetchContributors(client, ctx, org, repos) + contributors, err := fetchContributors(client, ctx, org, repos) + if err != nil { + panic(err) + } contribDisplayCount := min(len(contributors), 10) contribMd := formatContributorsMarkdown(contributors[:contribDisplayCount]) contribMd += "\n\n[...and more contributors](https://github.com/orgs/HappyHackingSpace/people)"