blob: 0b0b3986ce42cf504bf69d26290d36773ddd3374 [file] [log] [blame]
// Copyright 2015 The Go 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 (
"bytes"
"crypto/sha1"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/cloud"
"google.golang.org/cloud/storage"
)
const (
uploadURL = "https://golang.org/dl/upload"
projectID = "999119582588"
storageBucket = "golang"
)
// File represents a file on the golang.org downloads page.
// It should be kept in sync with the (currently Google-interal) download code.
type File struct {
Filename string
OS string
Arch string
Version string
Checksum string
Size int64
Kind string // "archive", "installer", "source"
}
// fileRe matches the files created by the release tool, such as:
// go1.5beta2.src.tar.gz
// go1.5.1.linux-386.tar.gz
// go1.5.windows-amd64.msi
var fileRe = regexp.MustCompile(`^(go[a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+)(?:-([a-z0-9.]+))?)\.(tar\.gz|zip|pkg|msi)$`)
func upload(files []string) error {
ctx, err := serviceContext()
if err != nil {
return err
}
for _, name := range files {
base := filepath.Base(name)
log.Printf("Uploading %v", base)
m := fileRe.FindStringSubmatch(base)
if m == nil {
return fmt.Errorf("unrecognized file: %q", base)
}
var b Build
version := m[1]
if m[2] == "src" {
b.Source = true
} else {
b.OS = m[3]
b.Arch = m[4]
}
if err := uploadFile(ctx, &b, version, name); err != nil {
return err
}
}
return nil
}
func uploadFile(ctx context.Context, b *Build, version, filename string) error {
file, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
base := filepath.Base(filename)
// Upload the file to Google Cloud Storage.
wr := storage.NewWriter(ctx, storageBucket, base)
wr.ACL = []storage.ACLRule{
{Entity: storage.AllUsers, Role: storage.RoleReader},
}
wr.Write(file)
if err := wr.Close(); err != nil {
return fmt.Errorf("uploading file: %v", err)
}
// Post file details to golang.org.
var kind string
switch {
case b.Source:
kind = "source"
case strings.HasSuffix(base, ".tar.gz"), strings.HasSuffix(base, ".zip"):
kind = "archive"
case strings.HasSuffix(base, ".msi"), strings.HasSuffix(base, ".pkg"):
kind = "installer"
}
req, err := json.Marshal(File{
Filename: base,
Version: version,
OS: b.OS,
Arch: b.Arch,
Checksum: fmt.Sprintf("%x", sha1.Sum(file)),
Size: int64(len(file)),
Kind: kind,
})
if err != nil {
return err
}
v := url.Values{"user": {*user}, "key": []string{userToken()}}
u := fmt.Sprintf("%s?%s", uploadURL, v.Encode())
resp, err := http.Post(u, "application/json", bytes.NewReader(req))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("upload failed: %v\n%s", resp.Status, b)
}
return nil
}
func serviceContext() (context.Context, error) {
file := filepath.Join(os.Getenv("HOME"), "keys", "golang-org.service.json")
blob, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
config, err := google.JWTConfigFromJSON(blob, storage.ScopeReadWrite)
if err != nil {
return nil, err
}
return cloud.NewContext(projectID, config.Client(context.Background())), nil
}