// Brett Morrison, June 2015
// A simple program to display a list of open pull requests from BitBucket ✔
// Setup the Authentication info
// Given a BB API, return JSON as a map interface
func getJSON(url string) (map[string]interface{}, error) {
- client := &http.Client{}
- req, err := http.NewRequest("GET", url, nil)
- return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
- req.SetBasicAuth(bitbucketUserName, bitbucketPassword)
- res, err := client.Do(req)
- return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
- if (res.StatusCode != 200) {
- return nil, errors.New(fmt.Sprintf("Response Code: %d", res.StatusCode))
- decoder := json.NewDecoder(res.Body)
- if err := decoder.Decode(&dat); err == io.EOF {
- return nil, errors.New(fmt.Sprintf("Decode Error: %s", err))
- jsonResponse := dat.(map[string]interface{})
- return jsonResponse, nil
+ client := &http.Client{}
+ req, err := http.NewRequest("GET", url, nil)
+ return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
+ req.SetBasicAuth(bitbucketUserName, bitbucketPassword)
+ res, err := client.Do(req)
+ return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
+ if res.StatusCode != 200 {
+ return nil, errors.New(fmt.Sprintf("Response Code: %d", res.StatusCode))
+ decoder := json.NewDecoder(res.Body)
+ if err := decoder.Decode(&dat); err == io.EOF {
+ return nil, errors.New(fmt.Sprintf("Decode Error: %s", err))
+ jsonResponse := dat.(map[string]interface{})
+ return jsonResponse, nil
-func displayParticipantInfo(id int, selfHrefLink string) (error) {
- // Get more details about the PR
- jsonResponseDet, err := getJSON(selfHrefLink)
- // Get details about the participants
- prsDet := jsonResponseDet["participants"]
- prsDetI := prsDet.([]interface{})
- // For determining if the PR is ready to merge
- numApprovedReviewers := 0
- // For each participant in the PR, display state depending on role
- for _, value := range prsDetI {
- valueMap := value.(map[string]interface{})
- role := valueMap["role"]
- approved := valueMap["approved"] == true
- displayName := valueMap["user"].(map[string]interface{})["display_name"]
- // TODO Rewrite with one line RegEx?
- fmt.Printf(" %s %s\n", approvedS, displayName)
- fmt.Printf(" %s (%s)\n", approvedS, displayName)
- fmt.Printf(" %s %s (%s)\n", approvedS, displayName, role)
- if numReviewers > 0 && numReviewers == numApprovedReviewers {
- fmt.Printf(" #%d %s READY TO MERGE, %d of %d REVIEWERS APPROVED\n\n", id, isOrNot, numApprovedReviewers, numReviewers)
+func displayParticipantInfo(id int, selfHrefLink string) error {
+ // Get more details about the PR
+ jsonResponseDet, err := getJSON(selfHrefLink)
+ // Get details about the participants
+ prsDet := jsonResponseDet["participants"]
+ prsDetI := prsDet.([]interface{})
+ // For determining if the PR is ready to merge
+ numApprovedReviewers := 0
+ // For each participant in the PR, display state depending on role
+ for _, value := range prsDetI {
+ valueMap := value.(map[string]interface{})
+ role := valueMap["role"]
+ approved := valueMap["approved"] == true
+ displayName := valueMap["user"].(map[string]interface{})["display_name"]
+ // TODO Rewrite with one line RegEx?
+ fmt.Printf(" %s %s\n", approvedS, displayName)
+ fmt.Printf(" %s (%s)\n", approvedS, displayName)
+ fmt.Printf(" %s %s (%s)\n", approvedS, displayName, role)
+ if numReviewers > 0 && numReviewers == numApprovedReviewers {
+ fmt.Printf(" #%d %s READY TO MERGE, %d of %d REVIEWERS APPROVED\n\n", id, isOrNot, numApprovedReviewers, numReviewers)
// Given a PR url, iterate through state and print info
-func listPR(pullRequestsLink string) (error) {
- var prApi = pullRequestsLink
- // PR API has pagination, code for > 1 page
- jsonResponse, err := getJSON(prApi)
- prs := jsonResponse["values"]
- prsI := prs.([]interface{})
- // For each PR in the repo
- for _, value := range prsI {
- valueMap := value.(map[string]interface{})
- id := int(valueMap["id"].(float64))
- // Display base info about the PR
- fmt.Printf(" #%d %s (%s -> %s) by %s\n",
- valueMap["source"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
- valueMap["destination"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
- valueMap["author"].(map[string]interface{})["display_name"])
- // Prep the URL for more details about the PR
- links := valueMap["links"]
- self := links.(map[string]interface{})["self"]
- selfHref := self.(map[string]interface{})["href"]
- selfHrefLink := fmt.Sprint(selfHref)
- // Display participant details about the PR
- err := displayParticipantInfo(id, selfHrefLink)
- // Determine if there's more results - if so, loop control back
- next := jsonResponse["next"]
- prApi = fmt.Sprint(next)
+func listPR(pullRequestsLink string) error {
+ var prApi = pullRequestsLink
+ // PR API has pagination, code for > 1 page
+ jsonResponse, err := getJSON(prApi)
+ prs := jsonResponse["values"]
+ prsI := prs.([]interface{})
+ // For each PR in the repo
+ for _, value := range prsI {
+ valueMap := value.(map[string]interface{})
+ id := int(valueMap["id"].(float64))
+ // Display base info about the PR
+ fmt.Printf(" #%d %s (%s -> %s) by %s\n",
+ valueMap["source"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
+ valueMap["destination"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
+ valueMap["author"].(map[string]interface{})["display_name"])
+ // Prep the URL for more details about the PR
+ links := valueMap["links"]
+ self := links.(map[string]interface{})["self"]
+ selfHref := self.(map[string]interface{})["href"]
+ selfHrefLink := fmt.Sprint(selfHref)
+ // Display participant details about the PR
+ err := displayParticipantInfo(id, selfHrefLink)
+ // Determine if there's more results - if so, loop control back
+ next := jsonResponse["next"]
+ prApi = fmt.Sprint(next)
- flag.StringVar(&bitbucketOwnerName, "ownername", "", "Bitbucket repository owner account")
- flag.StringVar(&bitbucketUserName, "username", "", "Bitbucket account username")
- flag.StringVar(&bitbucketPassword, "password", "", "Bitbucket account password")
+ flag.StringVar(&bitbucketOwnerName, "ownername", "", "Bitbucket repository owner account")
+ flag.StringVar(&bitbucketUserName, "username", "", "Bitbucket account username")
+ flag.StringVar(&bitbucketPassword, "password", "", "Bitbucket account password")
- var reposApi = bbBaseUrl + "/repositories/" + bitbucketOwnerName
- // Repo API has pagination, code for > 1 page
- for len(reposApi) > 0 {
- // Get the list of repos for this user / group
- jsonResponse, err := getJSON(reposApi)
- repos := jsonResponse["values"]
- reposI := repos.([]interface{})
- // For each repo, get the PR URL and process
- for _, value := range reposI {
- links := value.(map[string]interface{})["links"]
- pullRequests := links.(map[string]interface{})["pullrequests"]
- pullRequestsHref := pullRequests.(map[string]interface{})["href"]
- pullRequestsLink := fmt.Sprint(pullRequestsHref)
- fmt.Println("Repo:", pullRequestsLink)
- err := listPR(pullRequestsLink)
- fmt.Println(err) // OK to continue here if error, no need to exit the program
- // Determine if there's more results - if so, loop control back
- next := jsonResponse["next"]
- reposApi = fmt.Sprint(next)
+ var reposApi = bbBaseUrl + "/repositories/" + bitbucketOwnerName
+ // Repo API has pagination, code for > 1 page
+ for len(reposApi) > 0 {
+ // Get the list of repos for this user / group
+ jsonResponse, err := getJSON(reposApi)
+ repos := jsonResponse["values"]
+ reposI := repos.([]interface{})
+ // For each repo, get the PR URL and process
+ for _, value := range reposI {
+ links := value.(map[string]interface{})["links"]
+ pullRequests := links.(map[string]interface{})["pullrequests"]
+ pullRequestsHref := pullRequests.(map[string]interface{})["href"]
+ pullRequestsLink := fmt.Sprint(pullRequestsHref)
+ fmt.Println("Repo:", pullRequestsLink)
+ err := listPR(pullRequestsLink)
+ fmt.Println(err) // OK to continue here if error, no need to exit the program
+ // Determine if there's more results - if so, loop control back
+ next := jsonResponse["next"]
+ reposApi = fmt.Sprint(next)