blob: 6d4547230f45ff847a3f2e1b42468da955bece20 [file] [log] [blame]
// Copyright 2017 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.
// Update repository revisions in the manifest.
package main
import (
"bufio"
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
type stringsValue []string
func (i *stringsValue) String() string {
return strings.Join(*i, ",")
}
func (i *stringsValue) Set(value string) error {
*i = strings.Split(value, ",")
return nil
}
var (
manifestVar string
projectsVar = stringsValue{}
)
func init() {
flag.StringVar(&manifestVar, "manifest", "", "Name of the manifest file")
flag.Var(&projectsVar, "projects", "List of projects to update")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: update-manifest\n")
flag.PrintDefaults()
}
}
type Manifest struct {
Projects []Project `xml:"projects>project"`
XMLName struct{} `xml:"manifest"`
}
type Project struct {
Name string `xml:"name,attr,omitempty"`
Remote string `xml:"remote,attr,omitempty"`
RemoteBranch string `xml:"remotebranch,attr,omitempty"`
Revision string `xml:"revision,attr,omitempty"`
XMLName struct{} `xml:"project"`
}
func manifestFromBytes(data []byte) (*Manifest, error) {
m := new(Manifest)
if err := xml.Unmarshal(data, m); err != nil {
return nil, err
}
return m, nil
}
func getLatestRevision(manifest, remote, branch string) (string, error) {
cmd := exec.Command("git", "ls-remote", remote, fmt.Sprintf("refs/heads/%s", branch))
cmd.Dir = filepath.Dir(manifest)
stdout, err := cmd.StdoutPipe()
if err != nil {
return "", err
}
if err := cmd.Start(); err != nil {
return "", err
}
r := bufio.NewReader(stdout)
out, _, err := r.ReadLine()
if err != nil {
return "", err
}
if err := cmd.Wait(); err != nil {
return "", err
}
return strings.Fields(string(out))[0], nil
}
func updateManifest(manifest string, projects map[string]bool) error {
content, err := ioutil.ReadFile(filepath.Join(manifest))
if err != nil {
return fmt.Errorf("Could not read from %s: %s", manifest, err)
}
m, err := manifestFromBytes(content)
if err != nil {
return fmt.Errorf("Cannot parse manifest %s: %s", manifest, err)
}
str := string(content)
for _, p := range m.Projects {
if len(projects) > 0 {
if _, ok := projects[p.Name]; !ok {
continue
}
}
if p.Revision != "" {
branch := "master"
if p.RemoteBranch != "" {
branch = p.RemoteBranch
}
revision, err := getLatestRevision(manifest, p.Remote, branch)
if err != nil {
return err
}
str = strings.Replace(str, p.Revision, string(revision), 1)
}
}
if err := ioutil.WriteFile(manifest, []byte(str), os.ModePerm); err != nil {
return fmt.Errorf("Could not write to %s: %s", manifest, err)
}
return nil
}
func main() {
flag.Parse()
if _, err := os.Stat(manifestVar); os.IsNotExist(err) {
log.Fatalf("Manifest %s does not exist", manifestVar)
}
projects := map[string]bool{}
for _, p := range projectsVar {
projects[p] = true
}
if err := updateManifest(manifestVar, projects); err != nil {
log.Fatal(err)
}
}