Snippets
Brett Morrison A Go program to iterate through a specified owner's repositories on BitBucket and display the status of open Pull Requests
Revised by
Brett Morrison
c7c4df2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | //
// Brett Morrison, June 2015
//
// A simple program to display a list of open pull requests from BitBucket ✔
//
package main
import (
"os"
"flag"
"fmt"
"io"
"encoding/json"
"net/http"
"errors"
)
// Setup the Authentication info
const bb_base_url = "https://bitbucket.org/api/2.0"
var bitbucket_owner_name string
var bitbucket_username string
var bitbucket_password string
//
// 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)
if err != nil {
return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
}
req.SetBasicAuth(bitbucket_username, bitbucket_password)
res, err := client.Do(req)
if err != nil {
return nil, errors.New(fmt.Sprintf("Request Error: %s", err))
}
defer res.Body.Close()
if (res.StatusCode != 200) {
return nil, errors.New(fmt.Sprintf("Response Code: %d", res.StatusCode))
}
var dat interface{}
decoder := json.NewDecoder(res.Body)
if err := decoder.Decode(&dat); err == io.EOF {
return nil, errors.New(fmt.Sprintf("Decode Error: %s", err))
}
json_response := dat.(map[string]interface{})
return json_response, nil
}
//
// Given a PR url, iterate through state and print info
//
func listPR(pull_requests_link string) (error) {
var pr_api = pull_requests_link
// PR API has pagination, code for > 1 page
for len(pr_api) > 0 {
json_response, err := getJSON(pr_api)
if err != nil {
return err
}
prs := json_response["values"]
prs_i := prs.([]interface{})
// For each PR in the repo
for _, value := range prs_i {
value_map := value.(map[string]interface{})
id := value_map["id"]
// Display base info about the PR
fmt.Printf(" #%.0f %s (%s -> %s) by %s\n",
id,
value_map["title"],
value_map["source"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
value_map["destination"].(map[string]interface{})["branch"].(map[string]interface{})["name"],
value_map["author"].(map[string]interface{})["display_name"])
// Prep the URL for more details about the PR
links := value_map["links"]
self := links.(map[string]interface{})["self"]
self_href := self.(map[string]interface{})["href"]
self_href_link := fmt.Sprint(self_href)
// Get more details about the PR
json_response_det, err := getJSON(self_href_link)
if err != nil {
return err
}
// Get details about the participants
prs_det := json_response_det["participants"]
prs_det_i := prs_det.([]interface{})
// For determining if the PR is ready to merge
num_approved_reviewers := 0
num_reviewers := 0
// For each participant in the PR, display state depending on role
for _, value := range prs_det_i {
value_map := value.(map[string]interface{})
role := value_map["role"]
approved := value_map["approved"] == true
display_name := value_map["user"].(map[string]interface{})["display_name"]
// TODO Rewrite with one line RegEx?
var approved_s = " "
if approved {
approved_s = "X"
}
switch (role) {
case "REVIEWER":
fmt.Printf(" %s %s\n", approved_s, display_name)
num_reviewers++
if (approved) {
num_approved_reviewers++
}
case "PARTICIPANT":
fmt.Printf(" %s (%s)\n", approved_s, display_name)
default:
fmt.Printf(" %s %s (%s)\n", approved_s, display_name, role)
}
}
var is_or_not = "IS NOT"
if num_reviewers > 0 && num_reviewers == num_approved_reviewers {
is_or_not = "IS"
}
fmt.Printf(" #%.0f %s READY TO MERGE, %d of %d REVIEWERS APPROVED\n\n", id, is_or_not, num_approved_reviewers, num_reviewers)
}
// Determine if there's more results - if so, loop control back
next := json_response["next"]
if next != nil {
pr_api = fmt.Sprint(next)
} else {
pr_api = ""
}
}
return nil
}
func init() {
flag.StringVar(&bitbucket_owner_name, "ownername", "", "Bitbucket repository owner account")
flag.StringVar(&bitbucket_username, "username", "", "Bitbucket account username")
flag.StringVar(&bitbucket_password, "password", "", "Bitbucket account password")
}
func main() {
// Command line args
flag.Parse()
if len(os.Args) != 4 {
flag.Usage()
os.Exit(1)
}
var repos_api = bb_base_url + "/repositories/" + bitbucket_owner_name
// Repo API has pagination, code for > 1 page
for len(repos_api) > 0 {
// Get the list of repos for this user / group
json_response, err := getJSON(repos_api)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
repos := json_response["values"]
repos_i := repos.([]interface{})
// For each repo, get the PR URL and process
for _, value := range repos_i {
links := value.(map[string]interface{})["links"]
pullrequests := links.(map[string]interface{})["pullrequests"]
pullrequests_href := pullrequests.(map[string]interface{})["href"]
pull_requests_link := fmt.Sprint(pullrequests_href)
fmt.Println("Repo:", pull_requests_link)
err := listPR(pull_requests_link)
if err != nil {
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 := json_response["next"]
if next != nil {
repos_api = fmt.Sprint(next)
} else {
repos_api = ""
}
}
}
|
You can clone a snippet to your computer for local editing. Learn more.