blob: ec92b6d35a8a6a1743f8e59bc8ffa7820972da79 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
)
const fuchsiaURL = "https://fuchsia-review.googlesource.com"
// fuchsiaRE is a regexp for matching CL review URLs and extracting the CL numbers.
// Supports various forms. E.g.,
// - https://fuchsia-review.googlesource.com/c/fuchsia/+/123456789
// - fuchsia-review.googlesource.com/c/fuchsia/+/123456789/some/file
// - http://fxr/123456789
// - fxr/123456789/some/file
var fuchsiaRE = regexp.MustCompile(`^(?:https?://)?(?:fxr|fuchsia-review.googlesource.com/c/.+/\+)/(\d+).*`)
type queryInfo struct{ apiEndpoint, cl string }
func parseReviewURL(str string) (queryInfo, error) {
match := fuchsiaRE.FindStringSubmatch(str)
if match != nil {
return queryInfo{
apiEndpoint: fuchsiaURL,
cl: match[1],
}, nil
}
return queryInfo{}, errors.New("not a valid review URL")
}
// changeInfo is a JSON struct for ChangeInfo responses from Gerrit.
// https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-info.
// Only fields of interest are listed here.
type changeInfo struct {
Status string `json:"status"`
}
func getCLStatus(qi queryInfo) (string, error) {
query := fmt.Sprintf("%s/changes/?q=%s", qi.apiEndpoint, qi.cl)
resp, err := http.Get(query)
if err != nil {
return "", err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var cis []changeInfo
// Responses start with )]}' to prevent XSSI attacks. Discard them.
// See https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
if err := json.Unmarshal(b[4:], &cis); err != nil {
return "", err
}
switch len(cis) {
case 0:
return "", errors.New("CL not found")
case 1:
return cis[0].Status, nil
default:
return "", fmt.Errorf("Got %d CLs while expecting only one", len(cis))
}
}
func main() {
if len(os.Args) < 2 {
// TODO: Consider alternatives. E.g., show all outstanding CLs
// of the current user, or show all CLs that are pending in
// Global Integration.
log.Fatal("Review URL must be provided")
}
queryInfo, err := parseReviewURL(os.Args[1])
if err != nil {
log.Fatalf("Error parsing the review URL: %v", err)
}
status, err := getCLStatus(queryInfo)
if err != nil {
log.Fatalf("Error getting change info: %v", err)
}
fmt.Printf("CL status: %v\n", status)
}