249 lines
4.7 KiB
Go
249 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/bzip2"
|
|
"compress/gzip"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"flag"
|
|
"hash"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
ignore = "*\n!.gitignore\n"
|
|
dirPermissions = 0755
|
|
)
|
|
|
|
type V struct {
|
|
Upstream string
|
|
Digest map[string]string
|
|
Compress string
|
|
RemovePrefix bool
|
|
Exclude []string
|
|
}
|
|
|
|
func main() {
|
|
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
|
|
|
justCheck := flag.Bool("check", false, "verify the code in upstream/")
|
|
|
|
flag.Parse()
|
|
|
|
if(*justCheck && !repositoryIsClean()){
|
|
log.Fatal("cannot verify upstream/ files: working directory not clean")
|
|
}
|
|
|
|
f, err := ioutil.ReadFile("vendor.json")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
vendor := &V{}
|
|
if err := json.Unmarshal(f, vendor); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if _, err := os.Stat("upstream"); err == nil {
|
|
log.Println("recreating upstream")
|
|
if(*justCheck){
|
|
run("rm", "-r", "-f", "upstream")
|
|
} else {
|
|
run("git", "rm", "-r", "-f", "upstream")
|
|
}
|
|
} else {
|
|
if(*justCheck){
|
|
log.Fatalf("Cannot verify upstream/ as it does not exists.")
|
|
}
|
|
os.MkdirAll("patch", dirPermissions)
|
|
os.MkdirAll("jehanne", dirPermissions)
|
|
ig, err := os.Create(path.Join("jehanne", ".gitignore"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer ig.Close()
|
|
if _, err := ig.WriteString(ignore); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
run("git", "add", ig.Name())
|
|
}
|
|
|
|
if err := do(vendor, *justCheck); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if(*justCheck){
|
|
if(repositoryIsClean()){
|
|
log.Printf("the files in upstream/ matches those in "+path.Base(vendor.Upstream))
|
|
} else {
|
|
log.Fatalf("the files in upstream/ does not match those in "+path.Base(vendor.Upstream))
|
|
}
|
|
} else {
|
|
run("git", "add", "vendor.json")
|
|
run("git", "commit", "-s", "-m", "vendor: pull in "+path.Base(vendor.Upstream))
|
|
}
|
|
}
|
|
|
|
func repositoryIsClean() bool {
|
|
out, err := exec.Command("git", "status", "--porcelain").Output()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if(len(out) > 0){
|
|
log.Println("git status --porcelain");
|
|
log.Println(string(out))
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func do(v *V, justCheck bool) error {
|
|
name := fetch(v)
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.Remove(name)
|
|
|
|
var unZ io.Reader
|
|
switch v.Compress {
|
|
case "gzip":
|
|
unZ, err = gzip.NewReader(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case "bzip2":
|
|
unZ = bzip2.NewReader(f)
|
|
default:
|
|
unZ = f
|
|
}
|
|
|
|
ar := tar.NewReader(unZ)
|
|
h, err := ar.Next()
|
|
untar:
|
|
for ; err == nil; h, err = ar.Next() {
|
|
n := h.Name
|
|
if v.RemovePrefix {
|
|
n = strings.SplitN(n, "/", 2)[1]
|
|
}
|
|
for _, ex := range v.Exclude {
|
|
if strings.HasPrefix(n, ex) {
|
|
continue untar
|
|
}
|
|
}
|
|
n = path.Join("upstream", n)
|
|
if h.FileInfo().IsDir() {
|
|
os.MkdirAll(n, dirPermissions)
|
|
continue
|
|
}
|
|
os.MkdirAll(path.Dir(n), dirPermissions)
|
|
out, err := os.Create(n)
|
|
if err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
|
|
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
|
|
return err
|
|
}
|
|
out.Close()
|
|
}
|
|
if err != io.EOF {
|
|
return err
|
|
}
|
|
|
|
if(justCheck){
|
|
return nil;
|
|
}
|
|
return run("git", "add", "upstream")
|
|
}
|
|
|
|
type match struct {
|
|
hash.Hash
|
|
Good []byte
|
|
Name string
|
|
}
|
|
|
|
func (m match) OK() bool {
|
|
return bytes.Equal(m.Good, m.Hash.Sum(nil))
|
|
}
|
|
|
|
func fetch(v *V) string {
|
|
if len(v.Digest) == 0 {
|
|
log.Fatal("no checksums specifed")
|
|
}
|
|
|
|
f, err := ioutil.TempFile("", "cmdVendor")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
req, err := http.NewRequest("GET", v.Upstream, nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
client := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DisableCompression: true,
|
|
},
|
|
}
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
var digests []match
|
|
for k, v := range v.Digest {
|
|
g, err := hex.DecodeString(v)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
switch k {
|
|
case "sha224":
|
|
digests = append(digests, match{sha256.New224(), g, k})
|
|
case "sha256":
|
|
digests = append(digests, match{sha256.New(), g, k})
|
|
case "sha384":
|
|
digests = append(digests, match{sha512.New384(), g, k})
|
|
case "sha512":
|
|
digests = append(digests, match{sha512.New(), g, k})
|
|
}
|
|
}
|
|
ws := make([]io.Writer, len(digests))
|
|
for i := range digests {
|
|
ws[i] = digests[i]
|
|
}
|
|
w := io.MultiWriter(ws...)
|
|
|
|
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for _, h := range digests {
|
|
if !h.OK() {
|
|
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
|
|
}
|
|
}
|
|
return f.Name()
|
|
}
|
|
|
|
func run(exe string, arg ...string) error {
|
|
cmd := exec.Command(exe, arg...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|