blob: 779b9e1f32a41884b3892d3c13985bb8dc22f1b2 [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 whereiscl
import (
"encoding/base64"
"encoding/xml"
"fmt"
"net/url"
"go.fuchsia.dev/fuchsia/tools/whereiscl/netutil"
)
const fuchsiaURL = "https://fuchsia.googlesource.com"
// GIStatus represents the status of a CL in Global Integration.
type GIStatus string
const (
giStatusUnknown GIStatus = "UNKNOWN"
giStatusPassed = "PASSED"
giStatusPending = "PENDING"
)
func downloadGIManifest(name string) ([]byte, error) {
u, err := url.Parse(fuchsiaURL)
if err != nil {
return nil, err
}
u.Path = "/integration/+/HEAD/" + name
q := u.Query()
q.Set("format", "TEXT")
u.RawQuery = q.Encode()
b, err := netutil.HTTPGet(u.String())
if err != nil {
return nil, err
}
return base64.StdEncoding.DecodeString(string(b))
}
// Structs for unmarshalling the jiri manifest XML data.
type manifest struct {
XMLName xml.Name `xml:"manifest"`
Projects []project `xml:"projects>project"`
}
type project struct {
XMLName xml.Name `xml:"project"`
Name string `xml:"name,attr"`
Revision string `xml:"revision,attr"`
}
func getGIRevision(content []byte, project string) (string, error) {
m := manifest{}
if err := xml.Unmarshal(content, &m); err != nil {
return "", err
}
for _, p := range m.Projects {
if p.Name == project {
return p.Revision, nil
}
}
return "", fmt.Errorf("project %q is not found in the jiri manifest", project)
}
type gitLogs struct {
Log []gitLog `json:"log"`
}
type gitLog struct {
Commit string `json:"commit"`
}
func isAfterGI(project, clRevision, giRevision string) (bool, error) {
u, err := url.Parse(fuchsiaURL)
if err != nil {
return false, err
}
u.Path = fmt.Sprintf("/%s/+log/%s..HEAD", project, giRevision)
q := u.Query()
q.Set("format", "JSON")
u.RawQuery = q.Encode()
logs := gitLogs{}
if err := netutil.HTTPGetJSON(u.String(), &logs); err != nil {
return false, err
}
for _, log := range logs.Log {
if log.Commit == clRevision {
return true, nil
}
}
return false, nil
}
// GetGIStatus returns whether a given ChangeInfo passed Global Integration.
func GetGIStatus(ci *ChangeInfo) (GIStatus, error) {
var name string
switch ci.Project {
case "fuchsia":
name = "stem"
case "topaz":
name = "topaz/minimal"
case "experiences":
name = "flower"
default:
return giStatusUnknown, nil
}
manifest, err := downloadGIManifest(name)
if err != nil {
return giStatusUnknown, err
}
rev, err := getGIRevision(manifest, ci.Project)
if err != nil {
return giStatusUnknown, err
}
after, err := isAfterGI(ci.Project, ci.CurrentRevision, rev)
if err != nil {
return giStatusUnknown, err
}
if after {
return giStatusPending, nil
}
return giStatusPassed, nil
}