From 2835650ef838c020972b68ed55cd74e112b02fdc Mon Sep 17 00:00:00 2001 From: Kuba Martin Date: Tue, 16 Apr 2024 13:53:21 +0200 Subject: [PATCH] Add top issues ranking as a scheduled GitHub Action. (#1497) Signed-off-by: Jakub Martin --- .../scripts/update_top_issues_ranking/go.mod | 9 ++ .../scripts/update_top_issues_ranking/main.go | 117 ++++++++++++++++++ .../update_top_issues_ranking/ranking.tmpl | 13 ++ .../workflows/update-top-issues-ranking.yml | 27 ++++ 4 files changed, 166 insertions(+) create mode 100644 .github/scripts/update_top_issues_ranking/go.mod create mode 100644 .github/scripts/update_top_issues_ranking/main.go create mode 100644 .github/scripts/update_top_issues_ranking/ranking.tmpl create mode 100644 .github/workflows/update-top-issues-ranking.yml diff --git a/.github/scripts/update_top_issues_ranking/go.mod b/.github/scripts/update_top_issues_ranking/go.mod new file mode 100644 index 0000000000..5f65fcb405 --- /dev/null +++ b/.github/scripts/update_top_issues_ranking/go.mod @@ -0,0 +1,9 @@ +module github.com/opentofu/opentofu/.github/scripts/update_top_issues_ranking + +go 1.22.1 + +require ( + github.com/google/go-github/v61 v61.0.0 +) + +require github.com/google/go-querystring v1.1.0 // indirect diff --git a/.github/scripts/update_top_issues_ranking/main.go b/.github/scripts/update_top_issues_ranking/main.go new file mode 100644 index 0000000000..c1fb87cd4f --- /dev/null +++ b/.github/scripts/update_top_issues_ranking/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "bytes" + "context" + _ "embed" + "log" + "os" + "slices" + "strconv" + "text/template" + + "github.com/google/go-github/v61/github" +) + +//go:embed ranking.tmpl +var rankingTemplateContent []byte + +func main() { + owner := os.Args[1] + repo := os.Args[2] + issueNumberToUpdate, err := strconv.Atoi(os.Args[3]) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + + // Initialize client. + client := github.NewClient(nil).WithAuthToken(os.Getenv("GITHUB_TOKEN")) + + // List all open issues. + listOpts := &github.IssueListByRepoOptions{ + State: "open", + ListOptions: github.ListOptions{ + PerPage: 100, + }, + } + var issues []*github.Issue + for { + curIssues, resp, err := client.Issues.ListByRepo(ctx, owner, repo, listOpts) + if err != nil { + log.Fatal(err) + } + issues = append(issues, curIssues...) + if resp.NextPage == 0 { + break + } + listOpts.Page = resp.NextPage + } + + // Filter to those that have at least 2 thumbs-up. + allIssues := issues + issues = nil + for _, issue := range allIssues { + if issue.GetReactions().GetPlusOne() < 2 { + continue + } + issues = append(issues, issue) + } + + // Sort by thumbs-up descending. + slices.SortFunc(issues, func(a, b *github.Issue) int { + return b.GetReactions().GetPlusOne() - a.GetReactions().GetPlusOne() + }) + + templateParams := struct { + EnhancementIssues []*github.Issue + BugIssues []*github.Issue + RFCIssues []*github.Issue + }{ + getTopIssuesByLabel("enhancement", issues), + getTopIssuesByLabel("bug", issues), + getTopIssuesByLabel("rfc", issues), + } + + // Render template for issue body. + var rankingBodyBuffer bytes.Buffer + rankingTemplate := template.Must(template.New("ranking").Parse(string(rankingTemplateContent))) + if err := rankingTemplate.Execute(&rankingBodyBuffer, templateParams); err != nil { + log.Fatal(err) + } + rankingBodyString := rankingBodyBuffer.String() + + // Update the issue. + _, _, err = client.Issues.Edit(ctx, owner, repo, issueNumberToUpdate, &github.IssueRequest{ + Body: &rankingBodyString, + }) + if err != nil { + log.Fatal(err) + } +} + +func getTopIssuesByLabel(label string, issues []*github.Issue) []*github.Issue { + var labelledIssues []*github.Issue + for _, issue := range issues { + if hasLabel(label, issue) { + labelledIssues = append(labelledIssues, issue) + } + } + + // Just the top 20. + if len(labelledIssues) > 20 { + labelledIssues = labelledIssues[:20] + } + + return labelledIssues +} + +func hasLabel(label string, issue *github.Issue) bool { + for _, issueLabel := range issue.Labels { + if issueLabel.GetName() == label { + return true + } + } + return false +} diff --git a/.github/scripts/update_top_issues_ranking/ranking.tmpl b/.github/scripts/update_top_issues_ranking/ranking.tmpl new file mode 100644 index 0000000000..1dcfe6ba77 --- /dev/null +++ b/.github/scripts/update_top_issues_ranking/ranking.tmpl @@ -0,0 +1,13 @@ +## Top Enhancements +{{range .EnhancementIssues}}1. {{.HTMLURL}} - {{.Reactions.PlusOne}} :+1: +{{end}} + +## Top Bugs + +{{range .BugIssues}}1. {{.HTMLURL}} - {{.Reactions.PlusOne}} :+1: +{{end}} + +## Top RFCs + +{{range .RFCIssues}}1. {{.HTMLURL}} - {{.Reactions.PlusOne}} :+1: +{{end}} diff --git a/.github/workflows/update-top-issues-ranking.yml b/.github/workflows/update-top-issues-ranking.yml new file mode 100644 index 0000000000..82762e428d --- /dev/null +++ b/.github/workflows/update-top-issues-ranking.yml @@ -0,0 +1,27 @@ +name: update-top-issues-ranking + +on: + workflow_dispatch: + schedule: + - cron: '0 10 * * *' + +jobs: + update: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: 1.22 + - name: Update top issues ranking + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd .github/scripts/update_top_issues_ranking + go mod download + go run main.go opentofu opentofu 1496