140 lines
3.7 KiB
Go
140 lines
3.7 KiB
Go
// Copyright 2021 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 (
|
|
"archive/zip"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"golang.org/x/benchmarks/sweet/cli/bootstrap"
|
|
"golang.org/x/benchmarks/sweet/common"
|
|
"golang.org/x/benchmarks/sweet/common/log"
|
|
)
|
|
|
|
const (
|
|
putUsage = `Uploads a new version of the benchmark assets to GCS.
|
|
|
|
Usage: %s put [flags]
|
|
`
|
|
)
|
|
|
|
type putCmd struct {
|
|
auth bootstrap.AuthOption
|
|
force bool
|
|
bucket string
|
|
assetsDir string
|
|
assetsHashFile string
|
|
version string
|
|
}
|
|
|
|
func (*putCmd) Name() string { return "put" }
|
|
func (*putCmd) Synopsis() string {
|
|
return "Uploads a new version of the benchmark assets."
|
|
}
|
|
func (*putCmd) PrintUsage(w io.Writer, base string) {
|
|
fmt.Fprintf(w, putUsage, base)
|
|
}
|
|
|
|
func (c *putCmd) SetFlags(f *flag.FlagSet) {
|
|
c.auth = bootstrap.AuthAppDefault
|
|
f.Var(&c.auth, "auth", fmt.Sprintf("authentication method (options: %s)", authOpts(false)))
|
|
f.BoolVar(&c.force, "force", false, "force upload even if assets for this version exist")
|
|
f.StringVar(&c.version, "version", common.Version, "the version to upload assets for")
|
|
f.StringVar(&c.bucket, "bucket", "go-sweet-assets", "GCS bucket to upload assets to")
|
|
f.StringVar(&c.assetsDir, "assets-dir", "./assets", "assets directory to zip and upload")
|
|
f.StringVar(&c.assetsHashFile, "assets-hash-file", "./assets.hash", "file containing assets SHA256 hashes")
|
|
}
|
|
|
|
func (c *putCmd) Run(_ []string) error {
|
|
log.SetActivityLog(true)
|
|
|
|
if err := bootstrap.ValidateVersion(c.version); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("Archiving, compressing, and uploading: %s", c.assetsDir)
|
|
|
|
// Create storage writer for streaming.
|
|
wc, err := bootstrap.NewStorageWriter(c.bucket, c.version, c.auth, c.force)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer wc.Close()
|
|
|
|
// Pass everything we write through a hash.
|
|
hash := bootstrap.Hash()
|
|
w := io.MultiWriter(wc, hash)
|
|
|
|
// Write the archive.
|
|
if err := createAssetsArchive(w, c.assetsDir, c.version); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update hash file.
|
|
log.Printf("Updating hash file...")
|
|
return updateAssetsHash(bootstrap.CanonicalizeHash(hash), c.assetsHashFile, c.version, c.force)
|
|
}
|
|
|
|
func createAssetsArchive(w io.Writer, assetsDir, version string) (err error) {
|
|
zw := zip.NewWriter(w)
|
|
defer func() {
|
|
if cerr := zw.Close(); cerr != nil && err == nil {
|
|
err = fmt.Errorf("closing zip archive: %w", cerr)
|
|
}
|
|
}()
|
|
return filepath.Walk(assetsDir, func(fpath string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outPath, err := filepath.Rel(assetsDir, fpath)
|
|
if err != nil {
|
|
// By the guarantees of filepath.Walk, this shouldn't happen.
|
|
panic(err)
|
|
}
|
|
if info.IsDir() {
|
|
// Add a trailing slash to indicate we're creating a directory.
|
|
_, err := zw.Create(outPath + "/")
|
|
return err
|
|
}
|
|
if info.Mode()&os.ModeSymlink != 0 {
|
|
return fmt.Errorf("encountered symlink %s: symbolic links are not supported in assets", fpath)
|
|
}
|
|
// Create a file in our zip archive for writing.
|
|
fh := new(zip.FileHeader)
|
|
fh.Name = outPath
|
|
fh.Method = zip.Deflate
|
|
fh.SetMode(info.Mode())
|
|
zf, err := zw.CreateHeader(fh)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Open the original file for reading.
|
|
f, err := os.Open(fpath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Copy data into the archive.
|
|
_, err = io.Copy(zf, f)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func updateAssetsHash(hash, hashfile, version string, force bool) error {
|
|
vals, err := bootstrap.ReadHashesFile(hashfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok := vals.Put(version, hash, force); !ok {
|
|
return fmt.Errorf("hash for this version already exists")
|
|
}
|
|
return vals.WriteToFile(hashfile)
|
|
}
|