2016-05-25 15:07:57 +03:00
// 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/>.
2021-10-08 18:23:25 +03:00
//go:build none
2016-05-25 15:07:57 +03:00
// +build none
/ *
The ci command is called from Continuous Integration scripts .
2017-11-28 12:45:48 +03:00
Usage : go run build / ci . go < command > < command flags / arguments >
2016-05-25 15:07:57 +03:00
Available commands are :
2018-01-02 13:40:56 +03:00
install [ - arch architecture ] [ - cc compiler ] [ packages ... ] -- builds packages and executables
2017-11-09 13:46:03 +03:00
test [ - coverage ] [ packages ... ] -- runs the tests
lint -- runs certain pre - selected linters
2020-11-27 14:13:54 +03:00
archive [ - arch architecture ] [ - type zip | tar ] [ - signer key - envvar ] [ - signify key - envvar ] [ - upload dest ] -- archives build artifacts
2017-03-01 11:47:55 +03:00
importkeys -- imports signing keys from env
debsrc [ - signer key - id ] [ - upload dest ] -- creates a debian source package
nsis -- creates a Windows NSIS installer
aar [ - local ] [ - sign key - id ] [ - deploy repo ] [ - upload dest ] -- creates an Android archive
xcode [ - local ] [ - sign key - id ] [ - deploy repo ] [ - upload dest ] -- creates an iOS XCode framework
2017-04-06 13:53:33 +03:00
purge [ - store blobstore ] [ - days threshold ] -- purges old archives from the blobstore
2016-05-25 15:07:57 +03:00
For all commands , - n prevents execution of external programs ( dry run mode ) .
* /
package main
import (
2016-10-17 15:17:14 +03:00
"bufio"
2016-05-25 15:07:57 +03:00
"bytes"
"encoding/base64"
"flag"
"fmt"
"log"
"os"
"os/exec"
2020-11-11 16:34:43 +03:00
"path"
2016-05-25 15:07:57 +03:00
"path/filepath"
2016-10-17 15:17:14 +03:00
"regexp"
2016-05-25 15:07:57 +03:00
"runtime"
2021-06-21 19:16:51 +03:00
"strconv"
2016-05-25 15:07:57 +03:00
"strings"
"time"
2019-11-18 21:29:01 +03:00
"github.com/cespare/cp"
2022-04-27 12:48:02 +03:00
"github.com/ethereum/go-ethereum/common"
2020-12-09 17:43:36 +03:00
"github.com/ethereum/go-ethereum/crypto/signify"
2016-11-02 19:21:05 +03:00
"github.com/ethereum/go-ethereum/internal/build"
2018-07-30 11:56:40 +03:00
"github.com/ethereum/go-ethereum/params"
2016-05-25 15:07:57 +03:00
)
var (
// Files that end up in the geth*.zip archive.
gethArchiveFiles = [ ] string {
"COPYING" ,
executablePath ( "geth" ) ,
}
// Files that end up in the geth-alltools*.zip archive.
allToolsArchiveFiles = [ ] string {
"COPYING" ,
executablePath ( "abigen" ) ,
2017-03-02 16:23:15 +03:00
executablePath ( "bootnode" ) ,
2016-05-25 15:07:57 +03:00
executablePath ( "evm" ) ,
executablePath ( "geth" ) ,
2017-04-11 02:25:53 +03:00
executablePath ( "puppeth" ) ,
2016-05-25 15:07:57 +03:00
executablePath ( "rlpdump" ) ,
2019-03-04 14:02:58 +03:00
executablePath ( "clef" ) ,
2016-05-25 15:07:57 +03:00
}
// A debian package is created for all executables listed here.
debExecutables = [ ] debExecutable {
{
2018-07-30 11:56:40 +03:00
BinaryName : "abigen" ,
2017-04-11 02:25:53 +03:00
Description : "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages." ,
2016-05-25 15:07:57 +03:00
} ,
2017-03-09 13:02:43 +03:00
{
2018-07-30 11:56:40 +03:00
BinaryName : "bootnode" ,
2017-03-09 13:02:43 +03:00
Description : "Ethereum bootnode." ,
} ,
2016-05-25 15:07:57 +03:00
{
2018-07-30 11:56:40 +03:00
BinaryName : "evm" ,
2016-05-25 15:07:57 +03:00
Description : "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode." ,
} ,
2016-11-28 13:25:19 +03:00
{
2018-07-30 11:56:40 +03:00
BinaryName : "geth" ,
2017-04-11 02:25:53 +03:00
Description : "Ethereum CLI client." ,
2016-11-28 13:25:19 +03:00
} ,
2016-05-25 15:07:57 +03:00
{
2018-07-30 11:56:40 +03:00
BinaryName : "puppeth" ,
2017-04-11 02:25:53 +03:00
Description : "Ethereum private network manager." ,
} ,
{
2018-07-30 11:56:40 +03:00
BinaryName : "rlpdump" ,
2017-04-11 02:25:53 +03:00
Description : "Developer utility tool that prints RLP structures." ,
} ,
2019-03-04 14:02:58 +03:00
{
BinaryName : "clef" ,
Description : "Ethereum account management tool." ,
} ,
2018-07-30 11:56:40 +03:00
}
// A debian package is created for all executables listed here.
debEthereum = debPackage {
Name : "ethereum" ,
Version : params . Version ,
Executables : debExecutables ,
}
// Debian meta packages to build and push to Ubuntu PPA
debPackages = [ ] debPackage {
debEthereum ,
}
2016-05-25 15:07:57 +03:00
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
2021-08-25 22:57:31 +03:00
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
2022-02-16 19:38:21 +03:00
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
debDistroGoBoots = map [ string ] string {
2022-05-04 12:41:24 +03:00
"trusty" : "golang-1.11" , // EOL: 04/2024
"xenial" : "golang-go" , // EOL: 04/2026
"bionic" : "golang-go" , // EOL: 04/2028
"focal" : "golang-go" , // EOL: 04/2030
"impish" : "golang-go" , // EOL: 07/2022
"jammy" : "golang-go" , // EOL: 04/2032
//"kinetic": "golang-go", // EOL: 07/2023
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
}
debGoBootPaths = map [ string ] string {
"golang-1.11" : "/usr/lib/go-1.11" ,
"golang-go" : "/usr/lib/go" ,
}
2020-11-11 16:34:43 +03:00
// This is the version of go that will be downloaded by
//
// go run ci.go install -dlgo
2022-04-14 08:45:04 +03:00
dlgoVersion = "1.18.1"
2016-05-25 15:07:57 +03:00
)
var GOBIN , _ = filepath . Abs ( filepath . Join ( "build" , "bin" ) )
func executablePath ( name string ) string {
if runtime . GOOS == "windows" {
name += ".exe"
}
return filepath . Join ( GOBIN , name )
}
func main ( ) {
log . SetFlags ( log . Lshortfile )
2022-04-27 12:48:02 +03:00
if ! common . FileExist ( filepath . Join ( "build" , "ci.go" ) ) {
2016-05-25 15:07:57 +03:00
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 "install" :
doInstall ( os . Args [ 2 : ] )
case "test" :
doTest ( os . Args [ 2 : ] )
2017-11-09 13:46:03 +03:00
case "lint" :
doLint ( os . Args [ 2 : ] )
2016-05-25 15:07:57 +03:00
case "archive" :
doArchive ( os . Args [ 2 : ] )
2021-06-17 10:47:45 +03:00
case "docker" :
2021-06-18 15:28:30 +03:00
doDocker ( os . Args [ 2 : ] )
2016-05-25 15:07:57 +03:00
case "debsrc" :
doDebianSource ( os . Args [ 2 : ] )
2016-11-09 00:55:39 +03:00
case "nsis" :
doWindowsInstaller ( os . Args [ 2 : ] )
2016-10-17 15:17:14 +03:00
case "aar" :
doAndroidArchive ( os . Args [ 2 : ] )
2016-11-09 12:13:37 +03:00
case "xcode" :
doXCodeFramework ( os . Args [ 2 : ] )
2017-04-06 13:53:33 +03:00
case "purge" :
doPurge ( os . Args [ 2 : ] )
2016-05-25 15:07:57 +03:00
default :
log . Fatal ( "unknown command " , os . Args [ 1 ] )
}
}
// Compiling
func doInstall ( cmdline [ ] string ) {
2016-11-02 20:36:48 +03:00
var (
2020-11-11 16:34:43 +03:00
dlgo = flag . Bool ( "dlgo" , false , "Download Go and build with it" )
2016-11-02 20:36:48 +03:00
arch = flag . String ( "arch" , "" , "Architecture to cross build for" )
2018-01-02 13:40:56 +03:00
cc = flag . String ( "cc" , "" , "C compiler to cross build with" )
2016-11-02 20:36:48 +03:00
)
2016-05-25 15:07:57 +03:00
flag . CommandLine . Parse ( cmdline )
2021-05-04 14:01:20 +03:00
// Configure the toolchain.
tc := build . GoToolchain { GOARCH : * arch , CC : * cc }
if * dlgo {
csdb := build . MustLoadChecksums ( "build/checksums.txt" )
tc . Root = build . DownloadGo ( csdb , dlgoVersion )
2016-05-25 15:07:57 +03:00
}
2017-03-23 16:56:06 +03:00
2021-05-04 14:01:20 +03:00
// Configure the build.
env := build . Env ( )
gobuild := tc . Go ( "build" , buildFlags ( env ) ... )
2020-11-11 16:34:43 +03:00
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
// better disable it. This check isn't the best, it should probably
// check for something in env instead.
2021-05-04 14:01:20 +03:00
if env . CI && runtime . GOARCH == "arm64" {
2020-11-11 16:34:43 +03:00
gobuild . Args = append ( gobuild . Args , "-p" , "1" )
}
2020-11-12 00:08:22 +03:00
// We use -trimpath to avoid leaking local paths into the built executables.
gobuild . Args = append ( gobuild . Args , "-trimpath" )
2020-11-11 16:34:43 +03:00
// Show packages during build.
gobuild . Args = append ( gobuild . Args , "-v" )
// Now we choose what we're even building.
// Default: collect all 'main' packages in cmd/ and build those.
packages := flag . Args ( )
if len ( packages ) == 0 {
packages = build . FindMainPackages ( "./cmd" )
}
// Do the build!
for _ , pkg := range packages {
args := make ( [ ] string , len ( gobuild . Args ) )
copy ( args , gobuild . Args )
args = append ( args , "-o" , executablePath ( path . Base ( pkg ) ) )
args = append ( args , pkg )
build . MustRun ( & exec . Cmd { Path : gobuild . Path , Args : args , Env : gobuild . Env } )
2016-11-02 20:36:48 +03:00
}
2016-05-25 15:07:57 +03:00
}
2020-11-11 16:34:43 +03:00
// buildFlags returns the go tool flags for building.
2016-09-30 23:03:08 +03:00
func buildFlags ( env build . Environment ) ( flags [ ] string ) {
2017-03-29 13:20:57 +03:00
var ld [ ] string
2016-09-30 23:03:08 +03:00
if env . Commit != "" {
2017-03-29 13:20:57 +03:00
ld = append ( ld , "-X" , "main.gitCommit=" + env . Commit )
2019-05-08 16:44:28 +03:00
ld = append ( ld , "-X" , "main.gitDate=" + env . Date )
2017-03-29 13:20:57 +03:00
}
2020-11-11 16:34:43 +03:00
// Strip DWARF on darwin. This used to be required for certain things,
// and there is no downside to this, so we just keep doing it.
2017-03-29 13:20:57 +03:00
if runtime . GOOS == "darwin" {
ld = append ( ld , "-s" )
}
2021-10-11 16:48:41 +03:00
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
if runtime . GOOS == "linux" {
ld = append ( ld , "-extldflags" , "-Wl,-z,stack-size=0x800000" )
}
2017-03-29 13:20:57 +03:00
if len ( ld ) > 0 {
flags = append ( flags , "-ldflags" , strings . Join ( ld , " " ) )
2016-05-25 15:07:57 +03:00
}
return flags
}
// Running The Tests
//
// "tests" also includes static analysis tools such as vet.
func doTest ( cmdline [ ] string ) {
2021-05-04 14:01:20 +03:00
var (
dlgo = flag . Bool ( "dlgo" , false , "Download Go and build with it" )
arch = flag . String ( "arch" , "" , "Run tests for given architecture" )
cc = flag . String ( "cc" , "" , "Sets C compiler binary" )
coverage = flag . Bool ( "coverage" , false , "Whether to record code coverage" )
verbose = flag . Bool ( "v" , false , "Whether to log verbosely" )
2021-10-11 20:37:18 +03:00
race = flag . Bool ( "race" , false , "Execute the race detector" )
2021-05-04 14:01:20 +03:00
)
2016-05-25 15:07:57 +03:00
flag . CommandLine . Parse ( cmdline )
2016-10-28 20:05:01 +03:00
2021-05-04 14:01:20 +03:00
// Configure the toolchain.
tc := build . GoToolchain { GOARCH : * arch , CC : * cc }
if * dlgo {
csdb := build . MustLoadChecksums ( "build/checksums.txt" )
tc . Root = build . DownloadGo ( csdb , dlgoVersion )
2016-05-25 15:07:57 +03:00
}
2021-05-04 14:01:20 +03:00
gotest := tc . Go ( "test" )
2017-03-23 17:46:03 +03:00
2016-09-26 14:41:18 +03:00
// Test a single package at a time. CI builders are slow
// and some tests run into timeouts under load.
2020-05-05 15:11:00 +03:00
gotest . Args = append ( gotest . Args , "-p" , "1" )
2016-05-25 15:07:57 +03:00
if * coverage {
gotest . Args = append ( gotest . Args , "-covermode=atomic" , "-cover" )
}
2019-11-18 17:48:20 +03:00
if * verbose {
gotest . Args = append ( gotest . Args , "-v" )
}
2021-10-11 20:37:18 +03:00
if * race {
gotest . Args = append ( gotest . Args , "-race" )
}
2017-11-09 13:46:03 +03:00
2021-05-04 14:01:20 +03:00
packages := [ ] string { "./..." }
if len ( flag . CommandLine . Args ( ) ) > 0 {
packages = flag . CommandLine . Args ( )
}
2016-05-25 15:07:57 +03:00
gotest . Args = append ( gotest . Args , packages ... )
build . MustRun ( gotest )
}
2019-11-18 11:49:18 +03:00
// doLint runs golangci-lint on requested packages.
2017-11-09 13:46:03 +03:00
func doLint ( cmdline [ ] string ) {
2019-11-18 11:49:18 +03:00
var (
cachedir = flag . String ( "cachedir" , "./build/cache" , "directory for caching golangci-lint binary." )
)
2017-11-09 13:46:03 +03:00
flag . CommandLine . Parse ( cmdline )
packages := [ ] string { "./..." }
if len ( flag . CommandLine . Args ( ) ) > 0 {
packages = flag . CommandLine . Args ( )
2017-01-13 12:54:17 +03:00
}
2019-11-18 11:49:18 +03:00
linter := downloadLinter ( * cachedir )
lflags := [ ] string { "run" , "--config" , ".golangci.yml" }
build . MustRunCommand ( linter , append ( lflags , packages ... ) ... )
fmt . Println ( "You have achieved perfection." )
}
// downloadLinter downloads and unpacks golangci-lint.
func downloadLinter ( cachedir string ) string {
2022-03-25 15:46:33 +03:00
const version = "1.45.2"
2019-11-18 11:49:18 +03:00
csdb := build . MustLoadChecksums ( "build/checksums.txt" )
2022-01-31 15:17:18 +03:00
arch := runtime . GOARCH
2022-03-15 12:24:45 +03:00
ext := ".tar.gz"
2022-03-16 09:27:16 +03:00
if runtime . GOOS == "windows" {
ext = ".zip"
}
if arch == "arm" {
2022-01-31 15:17:18 +03:00
arch += "v" + os . Getenv ( "GOARM" )
}
base := fmt . Sprintf ( "golangci-lint-%s-%s-%s" , version , runtime . GOOS , arch )
2022-03-15 12:24:45 +03:00
url := fmt . Sprintf ( "https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s" , version , base , ext )
archivePath := filepath . Join ( cachedir , base + ext )
2019-11-18 11:49:18 +03:00
if err := csdb . DownloadFile ( url , archivePath ) ; err != nil {
log . Fatal ( err )
}
2020-11-11 16:34:43 +03:00
if err := build . ExtractArchive ( archivePath , cachedir ) ; err != nil {
2019-11-18 11:49:18 +03:00
log . Fatal ( err )
2017-11-10 20:06:45 +03:00
}
2019-11-18 11:49:18 +03:00
return filepath . Join ( cachedir , base , "golangci-lint" )
2017-01-13 12:54:17 +03:00
}
2016-05-25 15:07:57 +03:00
// Release Packaging
func doArchive ( cmdline [ ] string ) {
var (
2020-11-27 14:13:54 +03:00
arch = flag . String ( "arch" , runtime . GOARCH , "Architecture cross packaging" )
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) ` )
signify = flag . String ( "signify" , "" , ` Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY) ` )
upload = flag . String ( "upload" , "" , ` Destination to upload the archives (usually "gethstore/builds") ` )
ext string
2016-05-25 15:07:57 +03:00
)
flag . CommandLine . Parse ( cmdline )
switch * atype {
case "zip" :
ext = ".zip"
case "tar" :
ext = ".tar.gz"
default :
log . Fatal ( "unknown archive type: " , atype )
}
2016-09-30 23:03:08 +03:00
2016-11-03 15:44:16 +03:00
var (
2021-05-04 14:01:20 +03:00
env = build . Env ( )
2018-07-30 11:56:40 +03:00
basegeth = archiveBasename ( * arch , params . ArchiveVersion ( env . Commit ) )
geth = "geth-" + basegeth + ext
alltools = "geth-alltools-" + basegeth + ext
2016-11-03 15:44:16 +03:00
)
2016-09-30 23:03:08 +03:00
maybeSkipArchive ( env )
2016-11-03 15:44:16 +03:00
if err := build . WriteArchive ( geth , gethArchiveFiles ) ; err != nil {
2016-05-25 15:07:57 +03:00
log . Fatal ( err )
}
2016-11-03 15:44:16 +03:00
if err := build . WriteArchive ( alltools , allToolsArchiveFiles ) ; err != nil {
2016-05-25 15:07:57 +03:00
log . Fatal ( err )
}
2019-05-17 02:06:20 +03:00
for _ , archive := range [ ] string { geth , alltools } {
2020-11-27 14:13:54 +03:00
if err := archiveUpload ( archive , * upload , * signer , * signify ) ; err != nil {
2016-11-02 19:21:05 +03:00
log . Fatal ( err )
}
}
2016-05-25 15:07:57 +03:00
}
2018-07-30 11:56:40 +03:00
func archiveBasename ( arch string , archiveVersion string ) string {
2016-11-02 20:36:48 +03:00
platform := runtime . GOOS + "-" + arch
2016-11-04 14:48:20 +03:00
if arch == "arm" {
platform += os . Getenv ( "GOARM" )
}
2016-10-17 15:17:14 +03:00
if arch == "android" {
platform = "android-all"
}
2016-11-09 12:13:37 +03:00
if arch == "ios" {
platform = "ios-all"
}
2018-07-30 11:56:40 +03:00
return platform + "-" + archiveVersion
2016-05-25 15:07:57 +03:00
}
2020-12-09 17:43:36 +03:00
func archiveUpload ( archive string , blobstore string , signer string , signifyVar string ) error {
2016-11-02 19:21:05 +03:00
// If signing was requested, generate the signature files
if signer != "" {
2019-02-12 12:55:25 +03:00
key := getenvBase64 ( signer )
if err := build . PGPSignFile ( archive , archive + ".asc" , string ( key ) ) ; err != nil {
2016-11-02 19:21:05 +03:00
return err
}
}
2020-12-09 17:43:36 +03:00
if signifyVar != "" {
key := os . Getenv ( signifyVar )
untrustedComment := "verify with geth-release.pub"
trustedComment := fmt . Sprintf ( "%s (%s)" , archive , time . Now ( ) . UTC ( ) . Format ( time . RFC1123 ) )
if err := signify . SignFile ( archive , archive + ".sig" , key , untrustedComment , trustedComment ) ; err != nil {
2020-11-27 14:13:54 +03:00
return err
}
}
2016-11-02 19:21:05 +03:00
// 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 ] ,
}
2016-11-09 01:46:46 +03:00
if err := build . AzureBlobstoreUpload ( archive , filepath . Base ( archive ) , auth ) ; err != nil {
2016-11-02 19:21:05 +03:00
return err
}
if signer != "" {
2016-11-09 01:46:46 +03:00
if err := build . AzureBlobstoreUpload ( archive + ".asc" , filepath . Base ( archive + ".asc" ) , auth ) ; err != nil {
2016-11-02 19:21:05 +03:00
return err
}
}
2020-12-09 17:43:36 +03:00
if signifyVar != "" {
2020-11-27 14:13:54 +03:00
if err := build . AzureBlobstoreUpload ( archive + ".sig" , filepath . Base ( archive + ".sig" ) , auth ) ; err != nil {
return err
}
}
2016-11-02 19:21:05 +03:00
}
return nil
}
2016-09-30 23:03:08 +03:00
// skips archiving for some build configurations.
func maybeSkipArchive ( env build . Environment ) {
if env . IsPullRequest {
2021-05-04 14:01:20 +03:00
log . Printf ( "skipping archive creation because this is a PR build" )
2016-09-30 23:03:08 +03:00
os . Exit ( 0 )
}
2017-04-06 13:53:33 +03:00
if env . IsCronJob {
2021-05-04 14:01:20 +03:00
log . Printf ( "skipping archive creation because this is a cron job" )
2017-04-06 13:53:33 +03:00
os . Exit ( 0 )
}
2016-11-15 16:11:27 +03:00
if env . Branch != "master" && ! strings . HasPrefix ( env . Tag , "v1." ) {
2021-07-29 18:36:15 +03:00
log . Printf ( "skipping archive creation because branch %q, tag %q is not on the inclusion list" , env . Branch , env . Tag )
2016-09-30 23:03:08 +03:00
os . Exit ( 0 )
}
}
2021-06-17 10:47:45 +03:00
// Builds the docker images and optionally uploads them to Docker Hub.
2021-06-18 15:28:30 +03:00
func doDocker ( cmdline [ ] string ) {
2021-06-17 10:47:45 +03:00
var (
2021-06-18 15:28:30 +03:00
image = flag . Bool ( "image" , false , ` Whether to build and push an arch specific docker image ` )
manifest = flag . String ( "manifest" , "" , ` Push a multi-arch docker image for the specified architectures (usually "amd64,arm64") ` )
upload = flag . String ( "upload" , "" , ` Where to upload the docker image (usually "ethereum/client-go") ` )
2021-06-17 10:47:45 +03:00
)
flag . CommandLine . Parse ( cmdline )
// Skip building and pushing docker images for PR builds
env := build . Env ( )
maybeSkipArchive ( env )
2021-06-18 15:28:30 +03:00
// Retrieve the upload credentials and authenticate
user := getenvBase64 ( "DOCKER_HUB_USERNAME" )
pass := getenvBase64 ( "DOCKER_HUB_PASSWORD" )
if len ( user ) > 0 && len ( pass ) > 0 {
auther := exec . Command ( "docker" , "login" , "-u" , string ( user ) , "--password-stdin" )
auther . Stdin = bytes . NewReader ( pass )
build . MustRun ( auther )
}
2021-06-17 10:47:45 +03:00
// Retrieve the version infos to build and push to the following paths:
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-latest - Pushes to the master branch, Geth & tools
// - ethereum/client-go:alltools-stable - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:release-<major>.<minor> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-release-<major>.<minor> - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth & tools
var tags [ ] string
switch {
case env . Branch == "master" :
tags = [ ] string { "latest" }
case strings . HasPrefix ( env . Tag , "v1." ) :
2021-08-10 17:49:52 +03:00
tags = [ ] string { "stable" , fmt . Sprintf ( "release-1.%d" , params . VersionMinor ) , "v" + params . Version }
2021-06-17 10:47:45 +03:00
}
2021-06-18 15:28:30 +03:00
// If architecture specific image builds are requested, build and push them
if * image {
2021-06-21 19:16:51 +03:00
build . MustRunCommand ( "docker" , "build" , "--build-arg" , "COMMIT=" + env . Commit , "--build-arg" , "VERSION=" + params . VersionWithMeta , "--build-arg" , "BUILDNUM=" + env . Buildnum , "--tag" , fmt . Sprintf ( "%s:TAG" , * upload ) , "." )
build . MustRunCommand ( "docker" , "build" , "--build-arg" , "COMMIT=" + env . Commit , "--build-arg" , "VERSION=" + params . VersionWithMeta , "--build-arg" , "BUILDNUM=" + env . Buildnum , "--tag" , fmt . Sprintf ( "%s:alltools-TAG" , * upload ) , "-f" , "Dockerfile.alltools" , "." )
2021-06-17 10:47:45 +03:00
2021-06-18 15:28:30 +03:00
// Tag and upload the images to Docker Hub
for _ , tag := range tags {
gethImage := fmt . Sprintf ( "%s:%s-%s" , * upload , tag , runtime . GOARCH )
toolImage := fmt . Sprintf ( "%s:alltools-%s-%s" , * upload , tag , runtime . GOARCH )
2021-06-17 10:47:45 +03:00
2021-06-21 19:16:51 +03:00
// If the image already exists (non version tag), check the build
// number to prevent overwriting a newer commit if concurrent builds
// are running. This is still a tiny bit racey if two published are
// done at the same time, but that's extremely unlikely even on the
// master branch.
for _ , img := range [ ] string { gethImage , toolImage } {
if exec . Command ( "docker" , "pull" , img ) . Run ( ) != nil {
continue // Generally the only failure is a missing image, which is good
}
buildnum , err := exec . Command ( "docker" , "inspect" , "--format" , "{{index .Config.Labels \"buildnum\"}}" , img ) . CombinedOutput ( )
if err != nil {
log . Fatalf ( "Failed to inspect container: %v\nOutput: %s" , err , string ( buildnum ) )
}
buildnum = bytes . TrimSpace ( buildnum )
if len ( buildnum ) > 0 && len ( env . Buildnum ) > 0 {
oldnum , err := strconv . Atoi ( string ( buildnum ) )
if err != nil {
log . Fatalf ( "Failed to parse old image build number: %v" , err )
}
newnum , err := strconv . Atoi ( env . Buildnum )
if err != nil {
log . Fatalf ( "Failed to parse current build number: %v" , err )
}
if oldnum > newnum {
log . Fatalf ( "Current build number %d not newer than existing %d" , newnum , oldnum )
} else {
log . Printf ( "Updating %s from build %d to %d" , img , oldnum , newnum )
}
}
}
2021-06-18 15:28:30 +03:00
build . MustRunCommand ( "docker" , "image" , "tag" , fmt . Sprintf ( "%s:TAG" , * upload ) , gethImage )
build . MustRunCommand ( "docker" , "image" , "tag" , fmt . Sprintf ( "%s:alltools-TAG" , * upload ) , toolImage )
build . MustRunCommand ( "docker" , "push" , gethImage )
build . MustRunCommand ( "docker" , "push" , toolImage )
}
2021-06-17 10:47:45 +03:00
}
2021-06-18 15:28:30 +03:00
// If multi-arch image manifest push is requested, assemble it
if len ( * manifest ) != 0 {
2021-06-21 19:16:51 +03:00
// Since different architectures are pushed by different builders, wait
// until all required images are updated.
var mismatch bool
for i := 0 ; i < 2 ; i ++ { // 2 attempts, second is race check
mismatch = false // hope there's no mismatch now
for _ , tag := range tags {
for _ , arch := range strings . Split ( * manifest , "," ) {
gethImage := fmt . Sprintf ( "%s:%s-%s" , * upload , tag , arch )
toolImage := fmt . Sprintf ( "%s:alltools-%s-%s" , * upload , tag , arch )
for _ , img := range [ ] string { gethImage , toolImage } {
if out , err := exec . Command ( "docker" , "pull" , img ) . CombinedOutput ( ) ; err != nil {
log . Printf ( "Required image %s unavailable: %v\nOutput: %s" , img , err , out )
mismatch = true
break
}
buildnum , err := exec . Command ( "docker" , "inspect" , "--format" , "{{index .Config.Labels \"buildnum\"}}" , img ) . CombinedOutput ( )
if err != nil {
log . Fatalf ( "Failed to inspect container: %v\nOutput: %s" , err , string ( buildnum ) )
}
buildnum = bytes . TrimSpace ( buildnum )
if string ( buildnum ) != env . Buildnum {
log . Printf ( "Build number mismatch on %s: want %s, have %s" , img , env . Buildnum , buildnum )
mismatch = true
break
}
}
if mismatch {
break
}
}
if mismatch {
break
}
}
if mismatch {
// Build numbers mismatching, retry in a short time to
// avoid concurrent failes in both publisher images. If
// however the retry failed too, it means the concurrent
// builder is still crunching, let that do the publish.
if i == 0 {
time . Sleep ( 30 * time . Second )
}
continue
}
break
}
if mismatch {
log . Println ( "Relinquishing publish to other builder" )
return
}
2021-06-18 15:28:30 +03:00
// Assemble and push the Geth manifest image
for _ , tag := range tags {
gethImage := fmt . Sprintf ( "%s:%s" , * upload , tag )
var gethSubImages [ ] string
for _ , arch := range strings . Split ( * manifest , "," ) {
gethSubImages = append ( gethSubImages , gethImage + "-" + arch )
}
build . MustRunCommand ( "docker" , append ( [ ] string { "manifest" , "create" , gethImage } , gethSubImages ... ) ... )
build . MustRunCommand ( "docker" , "manifest" , "push" , gethImage )
}
// Assemble and push the alltools manifest image
for _ , tag := range tags {
toolImage := fmt . Sprintf ( "%s:alltools-%s" , * upload , tag )
var toolSubImages [ ] string
for _ , arch := range strings . Split ( * manifest , "," ) {
toolSubImages = append ( toolSubImages , toolImage + "-" + arch )
}
build . MustRunCommand ( "docker" , append ( [ ] string { "manifest" , "create" , toolImage } , toolSubImages ... ) ... )
build . MustRunCommand ( "docker" , "manifest" , "push" , toolImage )
}
2021-06-17 10:47:45 +03:00
}
}
2016-05-25 15:07:57 +03:00
// Debian Packaging
2016-09-30 23:03:08 +03:00
func doDebianSource ( cmdline [ ] string ) {
var (
2020-11-11 16:34:43 +03:00
cachedir = flag . String ( "cachedir" , "./build/cache" , ` Filesystem path to cache the downloaded Go bundles at ` )
signer = flag . String ( "signer" , "" , ` Signing key name, also used as package author ` )
upload = flag . String ( "upload" , "" , ` Where to upload the source package (usually "ethereum/ethereum") ` )
sshUser = flag . String ( "sftp-user" , "" , ` Username for SFTP upload (usually "geth-ci") ` )
workdir = flag . String ( "workdir" , "" , ` Output directory for packages (uses temp dir if unset) ` )
now = time . Now ( )
2016-09-30 23:03:08 +03:00
)
2016-05-25 15:07:57 +03:00
flag . CommandLine . Parse ( cmdline )
2016-09-30 23:03:08 +03:00
* workdir = makeWorkdir ( * workdir )
env := build . Env ( )
2021-05-04 14:01:20 +03:00
tc := new ( build . GoToolchain )
2016-09-30 23:03:08 +03:00
maybeSkipArchive ( env )
2016-05-25 15:07:57 +03:00
// Import the signing key.
2019-02-12 12:55:25 +03:00
if key := getenvBase64 ( "PPA_SIGNING_KEY" ) ; len ( key ) > 0 {
2016-05-25 15:07:57 +03:00
gpg := exec . Command ( "gpg" , "--import" )
gpg . Stdin = bytes . NewReader ( key )
build . MustRun ( gpg )
}
2019-11-18 11:49:18 +03:00
// Download and verify the Go source package.
2020-11-11 16:34:43 +03:00
gobundle := downloadGoSources ( * cachedir )
2019-11-18 11:49:18 +03:00
2019-11-21 17:14:31 +03:00
// Download all the dependencies needed to build the sources and run the ci script
2021-05-04 14:01:20 +03:00
srcdepfetch := tc . Go ( "mod" , "download" )
srcdepfetch . Env = append ( srcdepfetch . Env , "GOPATH=" + filepath . Join ( * workdir , "modgopath" ) )
2019-11-21 17:14:31 +03:00
build . MustRun ( srcdepfetch )
2021-05-04 14:01:20 +03:00
cidepfetch := tc . Go ( "run" , "./build/ci.go" )
cidepfetch . Env = append ( cidepfetch . Env , "GOPATH=" + filepath . Join ( * workdir , "modgopath" ) )
2019-11-21 17:14:31 +03:00
cidepfetch . Run ( ) // Command fails, don't care, we only need the deps to start it
2019-11-20 15:56:07 +03:00
2019-11-18 11:49:18 +03:00
// Create Debian packages and upload them.
2018-07-30 11:56:40 +03:00
for _ , pkg := range debPackages {
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
for distro , goboot := range debDistroGoBoots {
2019-11-18 11:49:18 +03:00
// Prepare the debian package with the go-ethereum sources.
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
meta := newDebMetadata ( distro , goboot , * signer , env , now , pkg . Name , pkg . Version , pkg . Executables )
2018-07-30 11:56:40 +03:00
pkgdir := stageDebianSource ( * workdir , meta )
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
2019-11-18 20:18:50 +03:00
// Add Go source code
2020-11-11 16:34:43 +03:00
if err := build . ExtractArchive ( gobundle , pkgdir ) ; err != nil {
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
log . Fatalf ( "Failed to extract Go sources: %v" , err )
}
if err := os . Rename ( filepath . Join ( pkgdir , "go" ) , filepath . Join ( pkgdir , ".go" ) ) ; err != nil {
log . Fatalf ( "Failed to rename Go source folder: %v" , err )
}
2019-11-18 20:18:50 +03:00
// Add all dependency modules in compressed form
2019-11-20 15:56:07 +03:00
os . MkdirAll ( filepath . Join ( pkgdir , ".mod" , "cache" ) , 0755 )
if err := cp . CopyAll ( filepath . Join ( pkgdir , ".mod" , "cache" , "download" ) , filepath . Join ( * workdir , "modgopath" , "pkg" , "mod" , "cache" , "download" ) ) ; err != nil {
2019-11-18 21:29:01 +03:00
log . Fatalf ( "Failed to copy Go module dependencies: %v" , err )
}
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
// Run the packaging and upload to the PPA
2019-11-21 19:18:59 +03:00
debuild := exec . Command ( "debuild" , "-S" , "-sa" , "-us" , "-uc" , "-d" , "-Zxz" , "-nc" )
2018-07-30 11:56:40 +03:00
debuild . Dir = pkgdir
build . MustRun ( debuild )
2016-05-25 15:07:57 +03:00
2019-02-14 18:10:09 +03:00
var (
2021-03-04 14:04:50 +03:00
basename = fmt . Sprintf ( "%s_%s" , meta . Name ( ) , meta . VersionString ( ) )
source = filepath . Join ( * workdir , basename + ".tar.xz" )
dsc = filepath . Join ( * workdir , basename + ".dsc" )
changes = filepath . Join ( * workdir , basename + "_source.changes" )
buildinfo = filepath . Join ( * workdir , basename + "_source.buildinfo" )
2019-02-14 18:10:09 +03:00
)
2018-07-30 11:56:40 +03:00
if * signer != "" {
build . MustRunCommand ( "debsign" , changes )
}
if * upload != "" {
2021-03-04 14:04:50 +03:00
ppaUpload ( * workdir , * upload , * sshUser , [ ] string { source , dsc , changes , buildinfo } )
2018-07-30 11:56:40 +03:00
}
2016-05-25 15:07:57 +03:00
}
}
}
2020-11-11 16:34:43 +03:00
// downloadGoSources downloads the Go source tarball.
func downloadGoSources ( cachedir string ) string {
2019-11-18 11:49:18 +03:00
csdb := build . MustLoadChecksums ( "build/checksums.txt" )
2020-11-11 16:34:43 +03:00
file := fmt . Sprintf ( "go%s.src.tar.gz" , dlgoVersion )
2019-11-18 11:49:18 +03:00
url := "https://dl.google.com/go/" + file
dst := filepath . Join ( cachedir , file )
if err := csdb . DownloadFile ( url , dst ) ; err != nil {
log . Fatal ( err )
}
return dst
}
2019-02-14 18:10:09 +03:00
func ppaUpload ( workdir , ppa , sshUser string , files [ ] string ) {
2019-02-12 12:55:25 +03:00
p := strings . Split ( ppa , "/" )
if len ( p ) != 2 {
log . Fatal ( "-upload PPA name must contain single /" )
}
2019-02-14 18:10:09 +03:00
if sshUser == "" {
sshUser = p [ 0 ]
2019-02-12 12:55:25 +03:00
}
2019-02-14 18:10:09 +03:00
incomingDir := fmt . Sprintf ( "~%s/ubuntu/%s" , p [ 0 ] , p [ 1 ] )
// Create the SSH identity file if it doesn't exist.
var idfile string
2019-02-12 12:55:25 +03:00
if sshkey := getenvBase64 ( "PPA_SSH_KEY" ) ; len ( sshkey ) > 0 {
2019-02-14 18:10:09 +03:00
idfile = filepath . Join ( workdir , "sshkey" )
2022-04-27 12:48:02 +03:00
if ! common . FileExist ( idfile ) {
2022-05-16 12:59:35 +03:00
os . WriteFile ( idfile , sshkey , 0600 )
2019-02-14 18:10:09 +03:00
}
}
// Upload
dest := sshUser + "@ppa.launchpad.net"
if err := build . UploadSFTP ( idfile , dest , incomingDir , files ) ; err != nil {
log . Fatal ( err )
2019-02-12 12:55:25 +03:00
}
}
func getenvBase64 ( variable string ) [ ] byte {
dec , err := base64 . StdEncoding . DecodeString ( os . Getenv ( variable ) )
if err != nil {
log . Fatal ( "invalid base64 " + variable )
}
return [ ] byte ( dec )
}
2016-09-30 23:03:08 +03:00
func makeWorkdir ( wdflag string ) string {
var err error
if wdflag != "" {
err = os . MkdirAll ( wdflag , 0744 )
} else {
2022-05-16 12:59:35 +03:00
wdflag , err = os . MkdirTemp ( "" , "geth-build-" )
2016-09-30 23:03:08 +03:00
}
if err != nil {
log . Fatal ( err )
}
return wdflag
}
func isUnstableBuild ( env build . Environment ) bool {
2016-11-18 21:55:19 +03:00
if env . Tag != "" {
2016-09-30 23:03:08 +03:00
return false
}
return true
2016-05-25 15:07:57 +03:00
}
2018-07-30 11:56:40 +03:00
type debPackage struct {
2019-05-17 02:06:20 +03:00
Name string // the name of the Debian package to produce, e.g. "ethereum"
Version string // the clean version of the debPackage, e.g. 1.8.12, without any metadata
2018-07-30 11:56:40 +03:00
Executables [ ] debExecutable // executables to be included in the package
}
2016-05-25 15:07:57 +03:00
type debMetadata struct {
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
Env build . Environment
GoBootPackage string
GoBootPath string
2016-09-30 23:03:08 +03:00
2018-07-30 11:56:40 +03:00
PackageName string
2016-05-25 15:07:57 +03:00
// go-ethereum version being built. Note that this
// is not the debian package version. The package version
// is constructed by VersionString.
Version string
2016-09-30 23:03:08 +03:00
Author string // "name <email>", also selects signing key
Distro , Time string
Executables [ ] debExecutable
2016-05-25 15:07:57 +03:00
}
2016-09-30 23:03:08 +03:00
type debExecutable struct {
2018-07-30 11:56:40 +03:00
PackageName string
BinaryName string
Description string
}
// Package returns the name of the package if present, or
// fallbacks to BinaryName
func ( d debExecutable ) Package ( ) string {
if d . PackageName != "" {
return d . PackageName
}
return d . BinaryName
2016-09-30 23:03:08 +03:00
}
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
func newDebMetadata ( distro , goboot , author string , env build . Environment , t time . Time , name string , version string , exes [ ] debExecutable ) debMetadata {
2016-05-25 15:07:57 +03:00
if author == "" {
// No signing key, use default author.
author = "Ethereum Builds <fjl@ethereum.org>"
}
return debMetadata {
travis, build, internal: use own Go bundle for PPA builds (#20240)
* build: bump PPAs to Go 1.13 (via longsleep), keep Trusty on 1.11
* travis, build, vendor: use own Go bundle for PPA builds
* travis, build, internal, vendor: smarter Go bundler, own untar
* build: updated ci-notes with new Go bundling, only make, don't test
2019-11-05 16:32:42 +03:00
GoBootPackage : goboot ,
GoBootPath : debGoBootPaths [ goboot ] ,
PackageName : name ,
Env : env ,
Author : author ,
Distro : distro ,
Version : version ,
Time : t . Format ( time . RFC1123Z ) ,
Executables : exes ,
2016-05-25 15:07:57 +03:00
}
}
// Name returns the name of the metapackage that depends
// on all executable packages.
func ( meta debMetadata ) Name ( ) string {
2016-09-30 23:03:08 +03:00
if isUnstableBuild ( meta . Env ) {
2018-07-30 11:56:40 +03:00
return meta . PackageName + "-unstable"
2016-05-25 15:07:57 +03:00
}
2018-07-30 11:56:40 +03:00
return meta . PackageName
2016-05-25 15:07:57 +03:00
}
// VersionString returns the debian version of the packages.
func ( meta debMetadata ) VersionString ( ) string {
vsn := meta . Version
2016-09-30 23:03:08 +03:00
if meta . Env . Buildnum != "" {
vsn += "+build" + meta . Env . Buildnum
2016-05-25 15:07:57 +03:00
}
if meta . Distro != "" {
vsn += "+" + meta . Distro
}
return vsn
}
// ExeList returns the list of all executable packages.
func ( meta debMetadata ) ExeList ( ) string {
names := make ( [ ] string , len ( meta . Executables ) )
for i , e := range meta . Executables {
names [ i ] = meta . ExeName ( e )
}
return strings . Join ( names , ", " )
}
// ExeName returns the package name of an executable package.
func ( meta debMetadata ) ExeName ( exe debExecutable ) string {
2016-09-30 23:03:08 +03:00
if isUnstableBuild ( meta . Env ) {
2018-07-30 11:56:40 +03:00
return exe . Package ( ) + "-unstable"
2016-05-25 15:07:57 +03:00
}
2018-07-30 11:56:40 +03:00
return exe . Package ( )
}
2016-05-25 15:07:57 +03:00
// ExeConflicts returns the content of the Conflicts field
// for executable packages.
func ( meta debMetadata ) ExeConflicts ( exe debExecutable ) string {
2016-09-30 23:03:08 +03:00
if isUnstableBuild ( meta . Env ) {
2016-05-25 15:07:57 +03:00
// Set up the conflicts list so that the *-unstable packages
// cannot be installed alongside the regular version.
//
// https://www.debian.org/doc/debian-policy/ch-relationships.html
// is very explicit about Conflicts: and says that Breaks: should
// be preferred and the conflicting files should be handled via
// alternates. We might do this eventually but using a conflict is
// easier now.
2018-07-30 11:56:40 +03:00
return "ethereum, " + exe . Package ( )
2016-05-25 15:07:57 +03:00
}
return ""
}
func stageDebianSource ( tmpdir string , meta debMetadata ) ( pkgdir string ) {
pkg := meta . Name ( ) + "-" + meta . VersionString ( )
pkgdir = filepath . Join ( tmpdir , pkg )
if err := os . Mkdir ( pkgdir , 0755 ) ; err != nil {
log . Fatal ( err )
}
// Copy the source code.
build . MustRunCommand ( "git" , "checkout-index" , "-a" , "--prefix" , pkgdir + string ( filepath . Separator ) )
// Put the debian build files in place.
debian := filepath . Join ( pkgdir , "debian" )
2018-07-30 11:56:40 +03:00
build . Render ( "build/deb/" + meta . PackageName + "/deb.rules" , filepath . Join ( debian , "rules" ) , 0755 , meta )
build . Render ( "build/deb/" + meta . PackageName + "/deb.changelog" , filepath . Join ( debian , "changelog" ) , 0644 , meta )
build . Render ( "build/deb/" + meta . PackageName + "/deb.control" , filepath . Join ( debian , "control" ) , 0644 , meta )
build . Render ( "build/deb/" + meta . PackageName + "/deb.copyright" , filepath . Join ( debian , "copyright" ) , 0644 , meta )
2016-05-25 15:07:57 +03:00
build . RenderString ( "8\n" , filepath . Join ( debian , "compat" ) , 0644 , meta )
build . RenderString ( "3.0 (native)\n" , filepath . Join ( debian , "source/format" ) , 0644 , meta )
for _ , exe := range meta . Executables {
2016-09-30 23:03:08 +03:00
install := filepath . Join ( debian , meta . ExeName ( exe ) + ".install" )
docs := filepath . Join ( debian , meta . ExeName ( exe ) + ".docs" )
2018-07-30 11:56:40 +03:00
build . Render ( "build/deb/" + meta . PackageName + "/deb.install" , install , 0644 , exe )
build . Render ( "build/deb/" + meta . PackageName + "/deb.docs" , docs , 0644 , exe )
2016-05-25 15:07:57 +03:00
}
return pkgdir
}
2016-08-08 13:41:55 +03:00
2016-11-09 00:55:39 +03:00
// Windows installer
func doWindowsInstaller ( cmdline [ ] string ) {
// Parse the flags and make skip installer generation on PRs
var (
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) ` )
2020-11-27 14:13:54 +03:00
signify = flag . String ( "signify key" , "" , ` Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY) ` )
2016-11-09 00:55:39 +03:00
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) ` )
)
flag . CommandLine . Parse ( cmdline )
* workdir = makeWorkdir ( * workdir )
env := build . Env ( )
maybeSkipArchive ( env )
// Aggregate binaries that are included in the installer
var (
devTools [ ] string
allTools [ ] string
gethTool string
)
for _ , file := range allToolsArchiveFiles {
if file == "COPYING" { // license, copied later
continue
}
allTools = append ( allTools , filepath . Base ( file ) )
if filepath . Base ( file ) == "geth.exe" {
gethTool = file
} else {
devTools = append ( devTools , file )
}
}
// Render NSIS scripts: Installer NSIS contains two installer sections,
// first section contains the geth binary, second section holds the dev tools.
templateData := map [ string ] interface { } {
"License" : "COPYING" ,
"Geth" : gethTool ,
"DevTools" : devTools ,
}
build . Render ( "build/nsis.geth.nsi" , filepath . Join ( * workdir , "geth.nsi" ) , 0644 , nil )
build . Render ( "build/nsis.install.nsh" , filepath . Join ( * workdir , "install.nsh" ) , 0644 , templateData )
build . Render ( "build/nsis.uninstall.nsh" , filepath . Join ( * workdir , "uninstall.nsh" ) , 0644 , allTools )
2016-12-11 02:01:57 +03:00
build . Render ( "build/nsis.pathupdate.nsh" , filepath . Join ( * workdir , "PathUpdate.nsh" ) , 0644 , nil )
2016-11-09 00:55:39 +03:00
build . Render ( "build/nsis.envvarupdate.nsh" , filepath . Join ( * workdir , "EnvVarUpdate.nsh" ) , 0644 , nil )
2019-11-18 21:29:01 +03:00
if err := cp . CopyFile ( filepath . Join ( * workdir , "SimpleFC.dll" ) , "build/nsis.simplefc.dll" ) ; err != nil {
2022-05-04 00:37:37 +03:00
log . Fatalf ( "Failed to copy SimpleFC.dll: %v" , err )
2019-11-18 21:29:01 +03:00
}
if err := cp . CopyFile ( filepath . Join ( * workdir , "COPYING" ) , "COPYING" ) ; err != nil {
2022-05-04 00:37:37 +03:00
log . Fatalf ( "Failed to copy copyright note: %v" , err )
2019-11-18 21:29:01 +03:00
}
2016-11-09 00:55:39 +03:00
// Build the installer. This assumes that all the needed files have been previously
// built (don't mix building and packaging to keep cross compilation complexity to a
// minimum).
2018-07-30 11:56:40 +03:00
version := strings . Split ( params . Version , "." )
2016-11-09 00:55:39 +03:00
if env . Commit != "" {
version [ 2 ] += "-" + env . Commit [ : 8 ]
}
2018-07-30 11:56:40 +03:00
installer , _ := filepath . Abs ( "geth-" + archiveBasename ( * arch , params . ArchiveVersion ( env . Commit ) ) + ".exe" )
2016-11-09 00:55:39 +03:00
build . MustRunCommand ( "makensis.exe" ,
"/DOUTPUTFILE=" + installer ,
"/DMAJORVERSION=" + version [ 0 ] ,
"/DMINORVERSION=" + version [ 1 ] ,
"/DBUILDVERSION=" + version [ 2 ] ,
"/DARCH=" + * arch ,
filepath . Join ( * workdir , "geth.nsi" ) ,
)
// Sign and publish installer.
2020-11-27 14:13:54 +03:00
if err := archiveUpload ( installer , * upload , * signer , * signify ) ; err != nil {
2016-11-09 00:55:39 +03:00
log . Fatal ( err )
}
2016-11-09 12:13:37 +03:00
}
2016-10-17 15:17:14 +03:00
// Android archives
func doAndroidArchive ( cmdline [ ] string ) {
var (
2020-11-27 14:13:54 +03:00
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) ` )
signify = flag . String ( "signify" , "" , ` Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY) ` )
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") ` )
2016-10-17 15:17:14 +03:00
)
flag . CommandLine . Parse ( cmdline )
env := build . Env ( )
2021-05-04 14:01:20 +03:00
tc := new ( build . GoToolchain )
2016-10-17 15:17:14 +03:00
2017-01-20 12:33:58 +03:00
// Sanity check that the SDK and NDK are installed and set
if os . Getenv ( "ANDROID_HOME" ) == "" {
log . Fatal ( "Please ensure ANDROID_HOME points to your Android SDK" )
}
2021-05-04 14:01:20 +03:00
// Build gomobile.
install := tc . Install ( GOBIN , "golang.org/x/mobile/cmd/gomobile@latest" , "golang.org/x/mobile/cmd/gobind@latest" )
install . Env = append ( install . Env )
build . MustRun ( install )
// Ensure all dependencies are available. This is required to make
// gomobile bind work because it expects go.sum to contain all checksums.
build . MustRun ( tc . Go ( "mod" , "download" ) )
2016-10-17 15:17:14 +03:00
// Build the Android archive and Maven resources
2018-05-02 12:07:44 +03:00
build . MustRun ( gomobileTool ( "bind" , "-ldflags" , "-s -w" , "--target" , "android" , "--javapkg" , "org.ethereum" , "-v" , "github.com/ethereum/go-ethereum/mobile" ) )
2016-10-17 15:17:14 +03:00
2016-11-25 13:34:19 +03:00
if * local {
// If we're building locally, copy bundle to build dir and skip Maven
os . Rename ( "geth.aar" , filepath . Join ( GOBIN , "geth.aar" ) )
2020-09-28 21:11:30 +03:00
os . Rename ( "geth-sources.jar" , filepath . Join ( GOBIN , "geth-sources.jar" ) )
2016-11-25 13:34:19 +03:00
return
}
2016-10-17 15:17:14 +03:00
meta := newMavenMetadata ( env )
2016-11-09 12:13:37 +03:00
build . Render ( "build/mvn.pom" , meta . Package + ".pom" , 0755 , meta )
2016-10-17 15:17:14 +03:00
// Skip Maven deploy and Azure upload for PR builds
maybeSkipArchive ( env )
2016-11-15 11:25:51 +03:00
// Sign and upload the archive to Azure
2018-07-30 11:56:40 +03:00
archive := "geth-" + archiveBasename ( "android" , params . ArchiveVersion ( env . Commit ) ) + ".aar"
2016-11-15 11:25:51 +03:00
os . Rename ( "geth.aar" , archive )
2020-11-27 14:13:54 +03:00
if err := archiveUpload ( archive , * upload , * signer , * signify ) ; err != nil {
2016-11-15 11:25:51 +03:00
log . Fatal ( err )
}
2016-10-17 15:17:14 +03:00
// Sign and upload all the artifacts to Maven Central
2016-11-15 11:25:51 +03:00
os . Rename ( archive , meta . Package + ".aar" )
2016-10-17 15:17:14 +03:00
if * signer != "" && * deploy != "" {
// Import the signing key into the local GPG instance
2019-02-12 12:55:25 +03:00
key := getenvBase64 ( * signer )
2018-05-09 02:13:53 +03:00
gpg := exec . Command ( "gpg" , "--import" )
gpg . Stdin = bytes . NewReader ( key )
build . MustRun ( gpg )
keyID , err := build . PGPKeyID ( string ( key ) )
if err != nil {
log . Fatal ( err )
2016-10-17 15:17:14 +03:00
}
// Upload the artifacts to Sonatype and/or Maven Central
repo := * deploy + "/service/local/staging/deploy/maven2"
2016-11-09 12:13:37 +03:00
if meta . Develop {
2016-10-17 15:17:14 +03:00
repo = * deploy + "/content/repositories/snapshots"
}
2018-04-10 14:32:48 +03:00
build . MustRunCommand ( "mvn" , "gpg:sign-and-deploy-file" , "-e" , "-X" ,
2016-10-17 15:17:14 +03:00
"-settings=build/mvn.settings" , "-Durl=" + repo , "-DrepositoryId=ossrh" ,
2018-05-09 02:13:53 +03:00
"-Dgpg.keyname=" + keyID ,
2016-11-09 12:13:37 +03:00
"-DpomFile=" + meta . Package + ".pom" , "-Dfile=" + meta . Package + ".aar" )
2016-10-17 15:17:14 +03:00
}
}
func gomobileTool ( subcmd string , args ... string ) * exec . Cmd {
cmd := exec . Command ( filepath . Join ( GOBIN , "gomobile" ) , subcmd )
cmd . Args = append ( cmd . Args , args ... )
cmd . Env = [ ] string {
2018-03-19 20:00:45 +03:00
"PATH=" + GOBIN + string ( os . PathListSeparator ) + os . Getenv ( "PATH" ) ,
2016-10-17 15:17:14 +03:00
}
for _ , e := range os . Environ ( ) {
2020-07-23 13:34:08 +03:00
if strings . HasPrefix ( e , "GOPATH=" ) || strings . HasPrefix ( e , "PATH=" ) || strings . HasPrefix ( e , "GOBIN=" ) {
2016-10-17 15:17:14 +03:00
continue
}
cmd . Env = append ( cmd . Env , e )
}
2020-07-23 13:34:08 +03:00
cmd . Env = append ( cmd . Env , "GOBIN=" + GOBIN )
2016-10-17 15:17:14 +03:00
return cmd
}
type mavenMetadata struct {
Version string
2016-11-09 12:13:37 +03:00
Package string
Develop bool
2016-10-17 15:17:14 +03:00
Contributors [ ] mavenContributor
}
type mavenContributor struct {
Name string
Email string
}
func newMavenMetadata ( env build . Environment ) mavenMetadata {
// Collect the list of authors from the repo root
contribs := [ ] mavenContributor { }
if authors , err := os . Open ( "AUTHORS" ) ; err == nil {
defer authors . Close ( )
scanner := bufio . NewScanner ( authors )
for scanner . Scan ( ) {
// Skip any whitespace from the authors list
line := strings . TrimSpace ( scanner . Text ( ) )
if line == "" || line [ 0 ] == '#' {
continue
}
// Split the author and insert as a contributor
2016-11-22 19:35:49 +03:00
re := regexp . MustCompile ( "([^<]+) <(.+)>" )
2016-10-17 15:17:14 +03:00
parts := re . FindStringSubmatch ( line )
if len ( parts ) == 3 {
contribs = append ( contribs , mavenContributor { Name : parts [ 1 ] , Email : parts [ 2 ] } )
}
}
}
2016-11-09 12:13:37 +03:00
// Render the version and package strings
2018-07-30 11:56:40 +03:00
version := params . Version
2016-11-09 12:13:37 +03:00
if isUnstableBuild ( env ) {
version += "-SNAPSHOT"
}
2016-10-17 15:17:14 +03:00
return mavenMetadata {
2016-11-09 12:13:37 +03:00
Version : version ,
Package : "geth-" + version ,
Develop : isUnstableBuild ( env ) ,
2016-10-17 15:17:14 +03:00
Contributors : contribs ,
}
}
2016-11-09 12:13:37 +03:00
// XCode frameworks
func doXCodeFramework ( cmdline [ ] string ) {
var (
2020-11-27 14:13:54 +03:00
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) ` )
signify = flag . String ( "signify" , "" , ` Environment variable holding the signify signing key (e.g. IOS_SIGNIFY_KEY) ` )
deploy = flag . String ( "deploy" , "" , ` Destination to deploy the archive (usually "trunk") ` )
upload = flag . String ( "upload" , "" , ` Destination to upload the archives (usually "gethstore/builds") ` )
2016-11-09 12:13:37 +03:00
)
flag . CommandLine . Parse ( cmdline )
env := build . Env ( )
2021-05-04 14:01:20 +03:00
tc := new ( build . GoToolchain )
// Build gomobile.
2022-04-12 21:24:02 +03:00
build . MustRun ( tc . Install ( GOBIN , "golang.org/x/mobile/cmd/gomobile" , "golang.org/x/mobile/cmd/gobind" ) )
2016-11-09 12:13:37 +03:00
// Build the iOS XCode framework
2019-08-22 14:16:48 +03:00
bind := gomobileTool ( "bind" , "-ldflags" , "-s -w" , "--target" , "ios" , "-v" , "github.com/ethereum/go-ethereum/mobile" )
2016-11-09 12:13:37 +03:00
2016-11-25 13:34:19 +03:00
if * local {
// If we're building locally, use the build folder and stop afterwards
2020-07-23 13:34:08 +03:00
bind . Dir = GOBIN
2016-11-25 13:34:19 +03:00
build . MustRun ( bind )
return
}
2021-05-04 14:01:20 +03:00
// Create the archive.
maybeSkipArchive ( env )
2018-07-30 11:56:40 +03:00
archive := "geth-" + archiveBasename ( "ios" , params . ArchiveVersion ( env . Commit ) )
2021-05-04 22:45:45 +03:00
if err := os . MkdirAll ( archive , 0755 ) ; err != nil {
log . Fatal ( err )
}
2016-11-09 12:13:37 +03:00
bind . Dir , _ = filepath . Abs ( archive )
build . MustRun ( bind )
build . MustRunCommand ( "tar" , "-zcvf" , archive + ".tar.gz" , archive )
// Sign and upload the framework to Azure
2020-11-27 14:13:54 +03:00
if err := archiveUpload ( archive + ".tar.gz" , * upload , * signer , * signify ) ; err != nil {
2016-11-09 12:13:37 +03:00
log . Fatal ( err )
}
// Prepare and upload a PodSpec to CocoaPods
if * deploy != "" {
2016-11-22 19:35:49 +03:00
meta := newPodMetadata ( env , archive )
2016-11-28 13:56:13 +03:00
build . Render ( "build/pod.podspec" , "Geth.podspec" , 0755 , meta )
2020-11-05 12:52:35 +03:00
build . MustRunCommand ( "pod" , * deploy , "push" , "Geth.podspec" , "--allow-warnings" )
2016-11-09 12:13:37 +03:00
}
}
type podMetadata struct {
Version string
Commit string
2016-11-22 19:35:49 +03:00
Archive string
2016-11-09 12:13:37 +03:00
Contributors [ ] podContributor
2016-10-17 15:17:14 +03:00
}
2016-11-09 12:13:37 +03:00
type podContributor struct {
Name string
Email string
}
2016-11-22 19:35:49 +03:00
func newPodMetadata ( env build . Environment , archive string ) podMetadata {
2016-11-09 12:13:37 +03:00
// Collect the list of authors from the repo root
contribs := [ ] podContributor { }
if authors , err := os . Open ( "AUTHORS" ) ; err == nil {
defer authors . Close ( )
scanner := bufio . NewScanner ( authors )
for scanner . Scan ( ) {
// Skip any whitespace from the authors list
line := strings . TrimSpace ( scanner . Text ( ) )
if line == "" || line [ 0 ] == '#' {
continue
}
// Split the author and insert as a contributor
2016-11-22 19:35:49 +03:00
re := regexp . MustCompile ( "([^<]+) <(.+)>" )
2016-11-09 12:13:37 +03:00
parts := re . FindStringSubmatch ( line )
if len ( parts ) == 3 {
contribs = append ( contribs , podContributor { Name : parts [ 1 ] , Email : parts [ 2 ] } )
}
}
}
2018-07-30 11:56:40 +03:00
version := params . Version
2016-11-09 12:13:37 +03:00
if isUnstableBuild ( env ) {
2016-11-28 13:56:13 +03:00
version += "-unstable." + env . Buildnum
2016-11-09 12:13:37 +03:00
}
return podMetadata {
2016-11-22 19:35:49 +03:00
Archive : archive ,
2016-11-28 13:56:13 +03:00
Version : version ,
2016-11-09 12:13:37 +03:00
Commit : env . Commit ,
Contributors : contribs ,
}
2016-11-09 00:55:39 +03:00
}
2017-04-06 13:53:33 +03:00
// Binary distribution cleanups
func doPurge ( cmdline [ ] string ) {
var (
store = flag . String ( "store" , "" , ` Destination from where to purge archives (usually "gethstore/builds") ` )
2018-09-28 21:05:46 +03:00
limit = flag . Int ( "days" , 30 , ` Age threshold above which to delete unstable archives ` )
2017-04-06 13:53:33 +03:00
)
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 )
}
2020-07-04 21:10:48 +03:00
fmt . Printf ( "Found %d blobs\n" , len ( blobs ) )
2017-04-06 13:53:33 +03:00
// Iterate over the blobs, collect and sort all unstable builds
for i := 0 ; i < len ( blobs ) ; i ++ {
2022-03-02 14:05:14 +03:00
if ! strings . Contains ( * blobs [ i ] . Name , "unstable" ) {
2017-04-06 13:53:33 +03:00
blobs = append ( blobs [ : i ] , blobs [ i + 1 : ] ... )
i --
}
}
for i := 0 ; i < len ( blobs ) ; i ++ {
for j := i + 1 ; j < len ( blobs ) ; j ++ {
2022-03-02 14:05:14 +03:00
if blobs [ i ] . Properties . LastModified . After ( * blobs [ j ] . Properties . LastModified ) {
2017-04-06 13:53:33 +03:00
blobs [ i ] , blobs [ j ] = blobs [ j ] , blobs [ i ]
}
}
}
// Filter out all archives more recent that the given threshold
for i , blob := range blobs {
2022-03-02 14:05:14 +03:00
if time . Since ( * blob . Properties . LastModified ) < time . Duration ( * limit ) * 24 * time . Hour {
2017-04-06 13:53:33 +03:00
blobs = blobs [ : i ]
break
}
}
2020-07-04 21:10:48 +03:00
fmt . Printf ( "Deleting %d blobs\n" , len ( blobs ) )
2017-04-06 13:53:33 +03:00
// Delete all marked as such and return
if err := build . AzureBlobstoreDelete ( auth , blobs ) ; err != nil {
log . Fatal ( err )
}
}