mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
Merge 42c86a09ed into 4fe30bf5ad
This commit is contained in:
commit
7a017f8b7c
5 changed files with 342 additions and 324 deletions
305
build/azure.go
Normal file
305
build/azure.go
Normal 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
|
||||||
|
}
|
||||||
173
build/ci.go
173
build/ci.go
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
@ -342,11 +338,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 {
|
||||||
|
|
@ -364,18 +358,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 {
|
||||||
|
|
@ -403,52 +392,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) {
|
||||||
|
|
@ -461,7 +404,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 != "" {
|
||||||
|
|
@ -634,14 +577,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 (
|
||||||
|
|
@ -692,11 +633,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
|
||||||
|
|
@ -706,7 +642,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()
|
||||||
|
|
@ -732,15 +667,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 != "" {
|
||||||
|
|
@ -831,9 +763,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()
|
||||||
|
|
@ -857,14 +787,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)
|
||||||
|
|
@ -968,62 +892,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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue