Skip to content

Commit

Permalink
add go utility on generating git issues at provider repos
Browse files Browse the repository at this point in the history
  • Loading branch information
nawazkh committed Aug 9, 2023
1 parent 59b8c79 commit 89cd79b
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,15 @@ $(GINKGO): # Build ginkgo from tools folder.
$(GOLANGCI_LINT): # Build golangci-lint from tools folder.
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOLANGCI_LINT_PKG) $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)

.PHONY: provider-issues
provider-issues: # Creates issues for all CAPI providers
@if [ -n "${GITHUB_ISSUE_OPENER_TOKEN}" ]; then \
go run ./hack/tools/update_providers/open_issues.go --dryRun=false --token=${GITHUB_ISSUE_OPENER_TOKEN}; \
else \
echo "Github token GITHUB_ISSUE_OPENER_TOKEN" not set; \
fi


## --------------------------------------
## Helpers
## --------------------------------------
Expand Down
28 changes: 28 additions & 0 deletions hack/tools/update_providers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# open_issues

`open_issues` is a go utility intended to open git issues on provider repos.

## Pre-requisites

- Create a github token which has a access to the following scopes. And save it in your environment as `GITHUB_ISSUE_OPENER_TOKEN`
- `repo:status` - Grants access to commit status on public and private repositories.
- `repo_deployment` - Grants access to deployment statuses on public and private repositories.
- `public_repo` - Grants access to public repositories
- Decide upon the title of the issue to be opened. Update it in the utility.
- Update the issue body

## How to run the tool

- From the root of the project Cluster API, run below to dry run the utility.

```go
make provider-issues-dry-run
```

OR

run below to create the issues at the provider repositories.

```sh
make provider-issues
```
210 changes: 210 additions & 0 deletions hack/tools/update_providers/open_issues.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//go:build tools
// +build tools

/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// main is the main package for the open issues utility.
package main

import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
)

const (
baseURL = "https://api.github.com"
issueTitle = "CAPI v1.x.0-beta.0 has been released and is ready for testing"
)

var (
repoList = []string{
"kubernetes-sigs/cluster-api-addon-provider-helm",
"kubernetes-sigs/cluster-api-provider-aws",
"kubernetes-sigs/cluster-api-provider-azure",
"kubernetes-sigs/cluster-api-provider-cloudstack",
"kubernetes-sigs/cluster-api-provider-digitalocean",
"kubernetes-sigs/cluster-api-provider-gcp",
"kubernetes-sigs/cluster-api-provider-kubemark",
"kubernetes-sigs/cluster-api-provider-kubevirt",
"kubernetes-sigs/cluster-api-provider-ibmcloud",
"kubernetes-sigs/cluster-api-provider-nested",
"oracle/cluster-api-provider-oci",
"kubernetes-sigs/cluster-api-provider-openstack",
"kubernetes-sigs/cluster-api-operator",
"kubernetes-sigs/cluster-api-provider-packet",
"kubernetes-sigs/cluster-api-provider-vsphere",
}
)

// Issue is the struct for the issue.
type Issue struct {
// Title is the title of the issue.
Title string `json:"title"`

// Body is the body of the issue.
Body string `json:"body"`
}

// IssueResponse is the struct for the issue response.
type IssueResponse struct {
// HTMLURL is the URL of the issue.
HTMLURL string `json:"html_url"`
}

func main() {
token := flag.String("token", "", "GitHub personal access token")
dryRun := flag.Bool("dryRun", true, "Dry run")
flag.Parse()

if *token == "" {
fmt.Println("GitHub personal access token is required.")
os.Exit(1)
}

issueBody := `
<!-- body -->
<!-- TODO: remove all TODOs before running this utility -->
<!-- TODO: update CAPI release semver -->
CAPI v1.x.0-beta.0 has been released and is ready for testing.
Looking forward to your feedback before CAPI 1.x.0 release!
## For quick reference
<!-- body -->
<!-- TODO: CAPI release notes -->
- [CAPI v1.x.0-beta.0 release notes](https://github.com/kubernetes-sigs/cluster-api/releases/tag/v1.x.0-beta.0)
- [Shortcut to CAPI git issues](https://github.com/kubernetes-sigs/cluster-api/issues)
## Following are the planned dates for the upcoming releases
<!-- TODO: update CAPI release timeline -->
|Release|Expected Date|
|-----|-----|
|v1.5.0-beta.x | Tuesday 5th July 2023|
|release-1.5 branch created (Begin [Code Freeze])|Tuesday 11th July 2023|
|v1.5.0-rc.0 released|Tuesday 11th July 2023|
| release-1.5 jobs created | Tuesday 11th July 2023 |
| v1.5.0-rc.x released | Tuesday 18th July 2023 |
| v1.5.0 released | Tuesday 25th July 2023 |
<!-- body -->
<!-- [List of CAPI providers](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/release/release-tasks.md#communicate-beta-to-providers) -->
`

if *dryRun {
fmt.Printf("\n")
fmt.Println("###############################################")
fmt.Println("Dry run is enabled. No git issues will be created.")
fmt.Println("###############################################")
fmt.Printf("\n")
}

fmt.Println("\n\nCreating issues for the following repositories:")
fmt.Println(strings.Join(repoList, "\n"))
fmt.Printf("\n")

if !*dryRun {
fmt.Println("Continue to create issues? (y/n)")
var response string
_, err := fmt.Scanln(&response)
if err != nil {
fmt.Printf("Failed to read response: %s\n", err)
os.Exit(1)
}
if response != "y" {
fmt.Println("Aborting...")
os.Exit(0)
}
}
fmt.Printf("\n")

for _, repo := range repoList {
url := fmt.Sprintf("%s/repos/%s/issues", baseURL, repo)
if *dryRun {
fmt.Printf("Dry run: %s\n", url)
continue
}

issue := Issue{
Title: issueTitle,
Body: issueBody,
}

issueJSON, err := json.Marshal(issue)
if err != nil {
fmt.Printf("Failed to marshal issue: %s\n", err)
os.Exit(1)
}

req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, bytes.NewBuffer(issueJSON))
if err != nil {
fmt.Printf("Failed to create request: %s\n", err)
os.Exit(1)
}

req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *token))
req.Header.Set("X-Github-Api-Version", "2022-11-28")
req.Header.Set("User-Agent", "open_git_issues")
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Failed to send request: %s\n", err)
os.Exit(1)
}

if resp.StatusCode != http.StatusCreated {
fmt.Printf("Failed to create issue for repository '%s'\nStatus code: %d\nStatus:%s\n\n\n", repo, resp.StatusCode, resp.Status)
} else {
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Failed to read response body: %s\n", err)
err := resp.Body.Close()
if err != nil {
fmt.Printf("Failed to close response body: %s\n", err)
}
os.Exit(1)
}

var issueResponse IssueResponse
err = json.Unmarshal(responseBody, &issueResponse)
if err != nil {
fmt.Printf("Failed to unmarshal issue response: %s\n", err)
err := resp.Body.Close()
if err != nil {
fmt.Printf("Failed to close response body: %s\n", err)
}
os.Exit(1)
}

fmt.Printf("\nIssue created for repository '%s'\nURL: %s\n", repo, issueResponse.HTMLURL)
}
err = resp.Body.Close()
if err != nil {
fmt.Printf("Failed to close response body: %s\n", err)
}
}
}

0 comments on commit 89cd79b

Please sign in to comment.