build: move sign/upload out of ci.go

This removes ci.go dependencies on vendored packages and speeds
up the build.
This commit is contained in:
Felix Lange 2017-08-07 17:05:40 +02:00
parent 46cf0a616b
commit 42c86a09ed
5 changed files with 342 additions and 324 deletions

305
build/azure.go Normal file
View file

@ -0,0 +1,305 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build none
/*
The azure command signs and uploads release binaries to a Microsoft Azure bucket and is called from
Continuous Integration scripts.
Usage: go run build/azure.go <command> <command flags/arguments>
Available commands are:
upload [ -store s ] [ -signer key-var ] [ file... ] -- uploads build artefacts
purge [ -store s ] [ -days threshold ] -- purges old archives from the blobstore
list [ -store s ] -- lists contents of the blobstore
For all commands, -n prevents the actual upload/download (dry run mode).
*/
package main
import (
"bytes"
"encoding/base64"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
storage "github.com/Azure/azure-storage-go"
"github.com/ethereum/go-ethereum/internal/build"
"golang.org/x/crypto/openpgp"
)
const defaultStore = "gethstore/builds"
func main() {
log.SetFlags(log.Lshortfile)
if _, err := os.Stat(filepath.Join("build", "azure.go")); os.IsNotExist(err) {
log.Fatal("this script must be run from the root of the repository")
}
if len(os.Args) < 2 {
log.Fatal("need subcommand as first argument")
}
switch os.Args[1] {
case "upload":
doUpload(os.Args[2:])
case "purge":
doPurge(os.Args[2:])
case "list":
doList(os.Args[2:])
default:
log.Fatal("unknown command ", os.Args[1])
}
}
func doUpload(cmdline []string) {
var (
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
store = flag.String("store", defaultStore, `Destination to upload the archives to`)
)
flag.CommandLine.Parse(cmdline)
build.MaybeSkipArchive(build.Env())
for _, archive := range flag.Args() {
uploadArchive(archive, *store, *signer)
}
}
func doPurge(cmdline []string) {
var (
store = flag.String("store", defaultStore, `Destination from where to purge archives`)
limit = flag.Int("days", 30, `Age threshold above which to delete unstalbe archives`)
)
flag.CommandLine.Parse(cmdline)
if env := build.Env(); !env.IsCronJob {
log.Printf("skipping because not a cron job")
os.Exit(0)
}
// Create the azure authentication and list the current archives
auth := newConfig(*store)
blobs, err := blobstoreList(auth)
if err != nil {
log.Fatal(err)
}
// Iterate over the blobs, collect and sort all unstable builds
for i := 0; i < len(blobs); i++ {
if !strings.Contains(blobs[i].Name, "unstable") {
blobs = append(blobs[:i], blobs[i+1:]...)
i--
}
}
for i := 0; i < len(blobs); i++ {
for j := i + 1; j < len(blobs); j++ {
iTime, err := time.Parse(time.RFC1123, blobs[i].Properties.LastModified)
if err != nil {
log.Fatal(err)
}
jTime, err := time.Parse(time.RFC1123, blobs[j].Properties.LastModified)
if err != nil {
log.Fatal(err)
}
if iTime.After(jTime) {
blobs[i], blobs[j] = blobs[j], blobs[i]
}
}
}
// Filter out all archives more recent that the given threshold
for i, blob := range blobs {
timestamp, _ := time.Parse(time.RFC1123, blob.Properties.LastModified)
if time.Since(timestamp) < time.Duration(*limit)*24*time.Hour {
blobs = blobs[:i]
break
}
}
// Delete all marked as such and return
if err := blobstoreDelete(auth, blobs); err != nil {
log.Fatal(err)
}
}
func doList(cmdline []string) {
var store = flag.String("store", defaultStore, `Blobstore to list`)
flag.CommandLine.Parse(cmdline)
auth := newConfig(*store)
blobs, err := blobstoreList(auth)
if err != nil {
log.Fatal(err)
}
namelen := 0
for _, b := range blobs {
if len(b.Name) > namelen {
namelen = len(b.Name)
}
}
for _, b := range blobs {
name := b.Name + strings.Repeat(" ", namelen-len(b.Name))
fmt.Printf("%s size: %v, last-modified: %v\n", name, b.Properties.ContentLength, b.Properties.LastModified)
}
}
func newConfig(store string) Config {
return Config{
Account: strings.Split(store, "/")[0],
Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"),
Container: strings.SplitN(store, "/", 2)[1],
}
}
// uploadArchive uploads the given file to the blobstore.
func uploadArchive(archive, blobstore, signer string) {
// If signing was requested, generate the signature files
if signer != "" {
pgpkey, err := base64.StdEncoding.DecodeString(os.Getenv(signer))
if err != nil {
log.Fatalf("invalid base64 in variable %s", signer)
}
if err := signFile(archive, archive+".asc", string(pgpkey)); err != nil {
log.Fatalf("can't sign: %v", err)
}
}
// If uploading to Azure was requested, push the archive possibly with its signature
auth := newConfig(blobstore)
if err := blobstoreUpload(archive, filepath.Base(archive), auth); err != nil {
log.Fatal(err)
}
if signer != "" {
if err := blobstoreUpload(archive+".asc", filepath.Base(archive+".asc"), auth); err != nil {
log.Fatal(err)
}
}
}
// signFile parses a PGP private key from the specified string and creates a signature file
// into the output parameter of the input file.
// pgpkey should be a single key in armored format.
func signFile(input string, output string, pgpkey string) error {
// Parse the keyring and make sure we only have a single private key in it
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pgpkey))
if err != nil {
return err
}
if len(keys) != 1 {
return fmt.Errorf("key count mismatch: have %d, want %d", len(keys), 1)
}
// Create the input and output streams for signing
in, err := os.Open(input)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(output)
if err != nil {
return err
}
defer out.Close()
// Generate the signature and return
return openpgp.ArmoredDetachSign(out, keys[0], in, nil)
}
// Config is an authentication and configuration struct containing the data needed by the
// Azure SDK to interact with a speicifc container in the blobstore.
type Config struct {
Account string // Account name to authorize API requests with
Token string // Access token for the above account
Container string // Blob container to upload files into
}
// blobstoreUpload uploads a local file to the Azure Blob Storage. Note, this method
// assumes a max file size of 64MB (Azure limitation). Larger files will need a multi API
// call approach implemented.
//
// See: https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx#Anchor_3
func blobstoreUpload(path string, name string, config Config) error {
if *build.DryRunFlag {
fmt.Printf("would upload %q to %s/%s/%s\n", path, config.Account, config.Container, name)
return nil
}
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return err
}
client := rawClient.GetBlobService()
// Stream the file to upload into the designated blobstore container
in, err := os.Open(path)
if err != nil {
return err
}
defer in.Close()
info, err := in.Stat()
if err != nil {
return err
}
return client.CreateBlockBlobFromReader(config.Container, name, uint64(info.Size()), in, nil)
}
// blobstoreList lists all the files contained within an azure blobstore.
func blobstoreList(config Config) ([]storage.Blob, error) {
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return nil, err
}
client := rawClient.GetBlobService()
// List all the blobs from the container and return them
container := client.GetContainerReference(config.Container)
blobs, err := container.ListBlobs(storage.ListBlobsParameters{
MaxResults: 1024 * 1024 * 1024, // Yes, fetch all of them
Timeout: 3600, // Yes, wait for all of them
})
if err != nil {
return nil, err
}
return blobs.Blobs, nil
}
// blobstoreDelete iterates over a list of files to delete and removes them.
func blobstoreDelete(config Config, blobs []storage.Blob) error {
if *build.DryRunFlag {
for _, blob := range blobs {
fmt.Printf("would delete %s (%s) from %s/%s\n", blob.Name, blob.Properties.LastModified, config.Account, config.Container)
}
return nil
}
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return err
}
client := rawClient.GetBlobService()
// Iterate over the blobs and delete them
for _, blob := range blobs {
if err := client.DeleteBlob(config.Container, blob.Name, nil); err != nil {
return err
}
}
return nil
}

View file

@ -19,23 +19,21 @@
/* /*
The ci command is called from Continuous Integration scripts. The ci command is called from Continuous Integration scripts.
Usage: go run ci.go <command> <command flags/arguments> Usage: go run build/ci.go <command> <command flags/arguments>
Available commands are: Available commands are:
install [ -arch architecture ] [ packages... ] -- builds packages and executables install [ -arch architecture ] [ packages... ] -- builds packages and executables
test [ -coverage ] [ -misspell ] [ packages... ] -- runs the tests test [ -coverage ] [ -misspell ] [ packages... ] -- runs the tests
archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts archive [ -arch architecture ] [ -type zip|tar ] -- archives build artefacts
importkeys -- imports signing keys from env importkeys -- imports PGP signing keys from env
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
nsis -- creates a Windows NSIS installer nsis -- creates a Windows NSIS installer
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive aar [ -local ] [ -signer key-id ] [-deploy repo] -- creates an Android archive
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework xcode [ -local ] [ -signer key-id ] [-deploy repo] -- creates an iOS XCode framework
xgo [ -alltools ] [ options ] -- cross builds according to options xgo [ -alltools ] [ options ] -- cross builds according to options
purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
For all commands, -n prevents execution of external programs (dry run mode). For all commands, -n prevents execution of external programs (dry run mode).
*/ */
package main package main
@ -158,8 +156,6 @@ func main() {
doXCodeFramework(os.Args[2:]) doXCodeFramework(os.Args[2:])
case "xgo": case "xgo":
doXgo(os.Args[2:]) doXgo(os.Args[2:])
case "purge":
doPurge(os.Args[2:])
default: default:
log.Fatal("unknown command ", os.Args[1]) log.Fatal("unknown command ", os.Args[1])
} }
@ -345,11 +341,9 @@ func spellcheck(packages []string) {
func doArchive(cmdline []string) { func doArchive(cmdline []string) {
var ( var (
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging") arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)") atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`) ext string
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
ext string
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
switch *atype { switch *atype {
@ -367,18 +361,13 @@ func doArchive(cmdline []string) {
geth = "geth-" + base + ext geth = "geth-" + base + ext
alltools = "geth-alltools-" + base + ext alltools = "geth-alltools-" + base + ext
) )
maybeSkipArchive(env) build.MaybeSkipArchive(env)
if err := build.WriteArchive(geth, gethArchiveFiles); err != nil { if err := build.WriteArchive(geth, gethArchiveFiles); err != nil {
log.Fatal(err) log.Fatal(err)
} }
if err := build.WriteArchive(alltools, allToolsArchiveFiles); err != nil { if err := build.WriteArchive(alltools, allToolsArchiveFiles); err != nil {
log.Fatal(err) log.Fatal(err)
} }
for _, archive := range []string{geth, alltools} {
if err := archiveUpload(archive, *upload, *signer); err != nil {
log.Fatal(err)
}
}
} }
func archiveBasename(arch string, env build.Environment) string { func archiveBasename(arch string, env build.Environment) string {
@ -406,52 +395,6 @@ func archiveVersion(env build.Environment) string {
return version return version
} }
func archiveUpload(archive string, blobstore string, signer string) error {
// If signing was requested, generate the signature files
if signer != "" {
pgpkey, err := base64.StdEncoding.DecodeString(os.Getenv(signer))
if err != nil {
return fmt.Errorf("invalid base64 %s", signer)
}
if err := build.PGPSignFile(archive, archive+".asc", string(pgpkey)); err != nil {
return err
}
}
// If uploading to Azure was requested, push the archive possibly with its signature
if blobstore != "" {
auth := build.AzureBlobstoreConfig{
Account: strings.Split(blobstore, "/")[0],
Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"),
Container: strings.SplitN(blobstore, "/", 2)[1],
}
if err := build.AzureBlobstoreUpload(archive, filepath.Base(archive), auth); err != nil {
return err
}
if signer != "" {
if err := build.AzureBlobstoreUpload(archive+".asc", filepath.Base(archive+".asc"), auth); err != nil {
return err
}
}
}
return nil
}
// skips archiving for some build configurations.
func maybeSkipArchive(env build.Environment) {
if env.IsPullRequest {
log.Printf("skipping because this is a PR build")
os.Exit(0)
}
if env.IsCronJob {
log.Printf("skipping because this is a cron job")
os.Exit(0)
}
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
os.Exit(0)
}
}
// Debian Packaging // Debian Packaging
func doDebianSource(cmdline []string) { func doDebianSource(cmdline []string) {
@ -464,7 +407,7 @@ func doDebianSource(cmdline []string) {
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
*workdir = makeWorkdir(*workdir) *workdir = makeWorkdir(*workdir)
env := build.Env() env := build.Env()
maybeSkipArchive(env) build.MaybeSkipArchive(env)
// Import the signing key. // Import the signing key.
if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" { if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" {
@ -637,14 +580,12 @@ func doWindowsInstaller(cmdline []string) {
// Parse the flags and make skip installer generation on PRs // Parse the flags and make skip installer generation on PRs
var ( var (
arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging") arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
*workdir = makeWorkdir(*workdir) *workdir = makeWorkdir(*workdir)
env := build.Env() env := build.Env()
maybeSkipArchive(env) build.MaybeSkipArchive(env)
// Aggregate binaries that are included in the installer // Aggregate binaries that are included in the installer
var ( var (
@ -695,11 +636,6 @@ func doWindowsInstaller(cmdline []string) {
"/DARCH="+*arch, "/DARCH="+*arch,
filepath.Join(*workdir, "geth.nsi"), filepath.Join(*workdir, "geth.nsi"),
) )
// Sign and publish installer.
if err := archiveUpload(installer, *upload, *signer); err != nil {
log.Fatal(err)
}
} }
// Android archives // Android archives
@ -709,7 +645,6 @@ func doAndroidArchive(cmdline []string) {
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`) signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`) deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
env := build.Env() env := build.Env()
@ -735,15 +670,12 @@ func doAndroidArchive(cmdline []string) {
build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta) build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
// Skip Maven deploy and Azure upload for PR builds // Skip Maven deploy and Azure upload for PR builds
maybeSkipArchive(env) build.MaybeSkipArchive(env)
// Sign and upload the archive to Azure // Sign and upload the archive to Azure
archive := "geth-" + archiveBasename("android", env) + ".aar" archive := "geth-" + archiveBasename("android", env) + ".aar"
os.Rename("geth.aar", archive) os.Rename("geth.aar", archive)
if err := archiveUpload(archive, *upload, *signer); err != nil {
log.Fatal(err)
}
// Sign and upload all the artifacts to Maven Central // Sign and upload all the artifacts to Maven Central
os.Rename(archive, meta.Package+".aar") os.Rename(archive, meta.Package+".aar")
if *signer != "" && *deploy != "" { if *signer != "" && *deploy != "" {
@ -834,9 +766,7 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
func doXCodeFramework(cmdline []string) { func doXCodeFramework(cmdline []string) {
var ( var (
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`) deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
env := build.Env() env := build.Env()
@ -860,14 +790,8 @@ func doXCodeFramework(cmdline []string) {
build.MustRun(bind) build.MustRun(bind)
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive) build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
// Skip CocoaPods deploy and Azure upload for PR builds // Deploy to CocoaPods.
maybeSkipArchive(env) build.MaybeSkipArchive(env)
// Sign and upload the framework to Azure
if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil {
log.Fatal(err)
}
// Prepare and upload a PodSpec to CocoaPods
if *deploy != "" { if *deploy != "" {
meta := newPodMetadata(env, archive) meta := newPodMetadata(env, archive)
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta) build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
@ -971,62 +895,3 @@ func xgoTool(args []string) *exec.Cmd {
} }
return cmd return cmd
} }
// Binary distribution cleanups
func doPurge(cmdline []string) {
var (
store = flag.String("store", "", `Destination from where to purge archives (usually "gethstore/builds")`)
limit = flag.Int("days", 30, `Age threshold above which to delete unstalbe archives`)
)
flag.CommandLine.Parse(cmdline)
if env := build.Env(); !env.IsCronJob {
log.Printf("skipping because not a cron job")
os.Exit(0)
}
// Create the azure authentication and list the current archives
auth := build.AzureBlobstoreConfig{
Account: strings.Split(*store, "/")[0],
Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"),
Container: strings.SplitN(*store, "/", 2)[1],
}
blobs, err := build.AzureBlobstoreList(auth)
if err != nil {
log.Fatal(err)
}
// Iterate over the blobs, collect and sort all unstable builds
for i := 0; i < len(blobs); i++ {
if !strings.Contains(blobs[i].Name, "unstable") {
blobs = append(blobs[:i], blobs[i+1:]...)
i--
}
}
for i := 0; i < len(blobs); i++ {
for j := i + 1; j < len(blobs); j++ {
iTime, err := time.Parse(time.RFC1123, blobs[i].Properties.LastModified)
if err != nil {
log.Fatal(err)
}
jTime, err := time.Parse(time.RFC1123, blobs[j].Properties.LastModified)
if err != nil {
log.Fatal(err)
}
if iTime.After(jTime) {
blobs[i], blobs[j] = blobs[j], blobs[i]
}
}
}
// Filter out all archives more recent that the given threshold
for i, blob := range blobs {
timestamp, _ := time.Parse(time.RFC1123, blob.Properties.LastModified)
if time.Since(timestamp) < time.Duration(*limit)*24*time.Hour {
blobs = blobs[:i]
break
}
}
// Delete all marked as such and return
if err := build.AzureBlobstoreDelete(auth, blobs); err != nil {
log.Fatal(err)
}
}

View file

@ -1,111 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package build
import (
"fmt"
"os"
storage "github.com/Azure/azure-storage-go"
)
// AzureBlobstoreConfig is an authentication and configuration struct containing
// the data needed by the Azure SDK to interact with a speicifc container in the
// blobstore.
type AzureBlobstoreConfig struct {
Account string // Account name to authorize API requests with
Token string // Access token for the above account
Container string // Blob container to upload files into
}
// AzureBlobstoreUpload uploads a local file to the Azure Blob Storage. Note, this
// method assumes a max file size of 64MB (Azure limitation). Larger files will
// need a multi API call approach implemented.
//
// See: https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx#Anchor_3
func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig) error {
if *DryRunFlag {
fmt.Printf("would upload %q to %s/%s/%s\n", path, config.Account, config.Container, name)
return nil
}
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return err
}
client := rawClient.GetBlobService()
// Stream the file to upload into the designated blobstore container
in, err := os.Open(path)
if err != nil {
return err
}
defer in.Close()
info, err := in.Stat()
if err != nil {
return err
}
return client.CreateBlockBlobFromReader(config.Container, name, uint64(info.Size()), in, nil)
}
// AzureBlobstoreList lists all the files contained within an azure blobstore.
func AzureBlobstoreList(config AzureBlobstoreConfig) ([]storage.Blob, error) {
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return nil, err
}
client := rawClient.GetBlobService()
// List all the blobs from the container and return them
container := client.GetContainerReference(config.Container)
blobs, err := container.ListBlobs(storage.ListBlobsParameters{
MaxResults: 1024 * 1024 * 1024, // Yes, fetch all of them
Timeout: 3600, // Yes, wait for all of them
})
if err != nil {
return nil, err
}
return blobs.Blobs, nil
}
// AzureBlobstoreDelete iterates over a list of files to delete and removes them
// from the blobstore.
func AzureBlobstoreDelete(config AzureBlobstoreConfig, blobs []storage.Blob) error {
if *DryRunFlag {
for _, blob := range blobs {
fmt.Printf("would delete %s (%s) from %s/%s\n", blob.Name, blob.Properties.LastModified, config.Account, config.Container)
}
return nil
}
// Create an authenticated client against the Azure cloud
rawClient, err := storage.NewBasicClient(config.Account, config.Token)
if err != nil {
return err
}
client := rawClient.GetBlobService()
// Iterate over the blobs and delete them
for _, blob := range blobs {
if err := client.DeleteBlob(config.Container, blob.Name, nil); err != nil {
return err
}
}
return nil
}

View file

@ -19,6 +19,7 @@ package build
import ( import (
"flag" "flag"
"fmt" "fmt"
"log"
"os" "os"
"strings" "strings"
) )
@ -127,3 +128,20 @@ func applyEnvFlags(env Environment) Environment {
} }
return env return env
} }
// MaybeSkipArchive exits the process when build results of the given env should
// not be archived and uploaded.
func MaybeSkipArchive(env Environment) {
if env.IsPullRequest {
log.Printf("skipping archive because this is a PR build")
os.Exit(0)
}
if env.IsCronJob {
log.Printf("skipping archive because this is a cron job")
os.Exit(0)
}
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
log.Printf("skipping archive because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
os.Exit(0)
}
}

View file

@ -1,59 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// signFile reads the contents of an input file and signs it (in armored format)
// with the key provided, placing the signature into the output file.
package build
import (
"bytes"
"fmt"
"os"
"golang.org/x/crypto/openpgp"
)
// PGPSignFile parses a PGP private key from the specified string and creates a
// signature file into the output parameter of the input file.
//
// Note, this method assumes a single key will be container in the pgpkey arg,
// furthermore that it is in armored format.
func PGPSignFile(input string, output string, pgpkey string) error {
// Parse the keyring and make sure we only have a single private key in it
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pgpkey))
if err != nil {
return err
}
if len(keys) != 1 {
return fmt.Errorf("key count mismatch: have %d, want %d", len(keys), 1)
}
// Create the input and output streams for signing
in, err := os.Open(input)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(output)
if err != nil {
return err
}
defer out.Close()
// Generate the signature and return
return openpgp.ArmoredDetachSign(out, keys[0], in, nil)
}