Snippets
Brett Morrison A Go program to iterate through a specified owner's repositories on BitBucket and display the status of open Pull Requests
Created by
Brett Morrison
last modified
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 208 209 210 211 212 213 214 215 216 | //
// Brett Morrison, June 2015
//
// A simple program to display a list of open pull requests from BitBucket ✔
//
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
)
// Setup the Authentication info
const bbBaseURL = "https://bitbucket.org/api/2.0"
var bitbucketOwnerName string
var bitbucketUserName string
var bitbucketPassword 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, fmt.Errorf("request error: %s", err)
}
req.SetBasicAuth(bitbucketUserName, bitbucketPassword)
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request error: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("response code: %d", res.StatusCode)
}
var dat interface{}
decoder := json.NewDecoder(res.Body)
if err := decoder.Decode(&dat); err == io.EOF {
return nil, fmt.Errorf("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)
if err != nil {
return err
}
// Get details about the participants
prsDet := jsonResponseDet["participants"]
prsDetI := prsDet.([]interface{})
// For determining if the PR is ready to merge
numApprovedReviewers := 0
numReviewers := 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?
var approvedS = " "
if approved {
approvedS = "X"
}
switch role {
case "REVIEWER":
fmt.Printf(" %s %s\n", approvedS, displayName)
numReviewers++
if approved {
numApprovedReviewers++
}
case "PARTICIPANT":
fmt.Printf(" %s (%s)\n", approvedS, displayName)
default:
fmt.Printf(" %s %s (%s)\n", approvedS, displayName, role)
}
}
var isOrNot = "IS NOT"
if numReviewers > 0 && numReviewers == numApprovedReviewers {
isOrNot = "IS"
}
fmt.Printf(" #%d %s READY TO MERGE, %d of %d REVIEWERS APPROVED\n\n", *ID, isOrNot, numApprovedReviewers, numReviewers)
return nil
}
//
// 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
for len(prAPI) > 0 {
jsonResponse, err := getJSON(&prAPI)
if err != nil {
return err
}
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",
ID,
valueMap["title"],
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)
if err != nil {
return err
}
}
// Determine if there's more results - if so, loop control back
next := jsonResponse["next"]
if next != nil {
prAPI = fmt.Sprint(next)
} else {
prAPI = ""
}
}
return nil
}
func init() {
flag.StringVar(&bitbucketOwnerName, "ownername", "", "Bitbucket repository owner account")
flag.StringVar(&bitbucketUserName, "username", "", "Bitbucket account username")
flag.StringVar(&bitbucketPassword, "password", "", "Bitbucket account password")
}
func main() {
// Command line args
flag.Parse()
if len(os.Args) != 4 {
flag.Usage()
os.Exit(1)
}
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)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
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)
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 := jsonResponse["next"]
if next != nil {
reposAPI = fmt.Sprint(next)
} else {
reposAPI = ""
}
}
}
|
Comments (2)
You can clone a snippet to your computer for local editing. Learn more.
Blogged: http://www.brettmorrison.com/a-golang-programming-exercise-bitbucket-pull-requests
The gameplay of Geometry Dash Scratch is similar to the original game. Players control a customizable character and navigate through a series of challenging obstacles and hazards. The objective is to reach the end of each level while avoiding spikes, saws, and other dangerous objects. The unique twist is that the movement and jumps must be synchronized with the rhythm of the background music, adding an extra layer of challenge and excitement.