From 4f7627972e4997965be6f3c406904ef613e14c20 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 30 Sep 2016 22:03:08 +0200 Subject: [PATCH] build: improve debian packaging This commit tweaks the debian packaging tool: * All build environment metadata can now be overriden on the command line. This allows testing the CI build behaviour locally. * -unstable packages now actually contain the binaries (oops) * packages use Go 1.7 to build * archiving is skipped for PR builds --- .travis.yml | 2 +- Makefile | 28 +++--- build/ci-notes.md | 29 ++++++- build/ci.go | 189 +++++++++++++++++++---------------------- build/deb.changelog | 2 +- build/deb.control | 2 +- build/deb.rules | 2 +- internal/build/env.go | 114 +++++++++++++++++++++++++ internal/build/util.go | 14 ++- 9 files changed, 250 insertions(+), 132 deletions(-) create mode 100644 internal/build/env.go diff --git a/.travis.yml b/.travis.yml index b3b5edb27..a7a3041ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ matrix: - debhelper - dput script: - - go run build/ci.go travis-debsrc + - go run build/ci.go debsrc -signer "Felix Lange (Geth CI Testing Key) " -upload ppa:lp-fjl/geth-ci-testing install: - go get golang.org/x/tools/cmd/cover diff --git a/Makefile b/Makefile index 6cb64e8a4..f23b32f42 100644 --- a/Makefile +++ b/Makefile @@ -42,12 +42,12 @@ geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 get @ls -ld $(GOBIN)/geth-linux-* geth-linux-386: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth @echo "Linux 386 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep 386 geth-linux-amd64: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth @echo "Linux amd64 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep amd64 @@ -56,32 +56,32 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar @ls -ld $(GOBIN)/geth-linux-* | grep arm geth-linux-arm-5: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth @echo "Linux ARMv5 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-5 geth-linux-arm-6: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth @echo "Linux ARMv6 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-6 geth-linux-arm-7: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth @echo "Linux ARMv7 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-7 geth-linux-arm64: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth @echo "Linux ARM64 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm64 geth-linux-mips64: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth @echo "Linux MIPS64 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mips64 geth-linux-mips64le: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth @echo "Linux MIPS64le cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mips64le @@ -90,12 +90,12 @@ geth-darwin: geth-darwin-386 geth-darwin-amd64 @ls -ld $(GOBIN)/geth-darwin-* geth-darwin-386: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth @echo "Darwin 386 cross compilation done:" @ls -ld $(GOBIN)/geth-darwin-* | grep 386 geth-darwin-amd64: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth @echo "Darwin amd64 cross compilation done:" @ls -ld $(GOBIN)/geth-darwin-* | grep amd64 @@ -104,21 +104,21 @@ geth-windows: geth-windows-386 geth-windows-amd64 @ls -ld $(GOBIN)/geth-windows-* geth-windows-386: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth @echo "Windows 386 cross compilation done:" @ls -ld $(GOBIN)/geth-windows-* | grep 386 geth-windows-amd64: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth @echo "Windows amd64 cross compilation done:" @ls -ld $(GOBIN)/geth-windows-* | grep amd64 geth-android: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth @echo "Android cross compilation done:" @ls -ld $(GOBIN)/geth-android-* geth-ios: - build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth @echo "iOS framework cross compilation done:" @ls -ld $(GOBIN)/geth-ios-* diff --git a/build/ci-notes.md b/build/ci-notes.md index 989cba6dd..39375ff4c 100644 --- a/build/ci-notes.md +++ b/build/ci-notes.md @@ -1,5 +1,4 @@ -Debian Packaging ----------------- +# Debian Packaging Tagged releases and develop branch commits are available as installable Debian packages for Ubuntu. Packages are built for the all Ubuntu versions which are supported by @@ -8,6 +7,7 @@ Canonical: - Trusty Tahr (14.04 LTS) - Wily Werewolf (15.10) - Xenial Xerus (16.04 LTS) +- Yakkety Yak (16.10) Packages of develop branch commits have suffix -unstable and cannot be installed alongside the stable version. Switching between release streams requires user intervention. @@ -21,6 +21,29 @@ variable which Travis CI makes available to certain builds. We want to build go-ethereum with the most recent version of Go, irrespective of the Go version that is available in the main Ubuntu repository. In order to make this possible, our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on -golang-1.6, which is co-installable alongside the regular golang package. PPA dependencies +golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies can be edited at https://launchpad.net/%7Elp-fjl/+archive/ubuntu/geth-ci-testing/+edit-dependencies +## Building Packages Locally (for testing) + +You need to run Ubuntu to do test packaging. + +Add the gophers PPA and install Go 1.7 and Debian packaging tools: + + $ sudo apt-add-repository ppa:gophers/ubuntu/archive + $ sudo apt-get update + $ sudo apt-get install build-essential golang-1.7 devscripts debhelper + +Create the source packages: + + $ go run build/ci.go debsrc -workdir dist + +Then go into the source package directory for your running distribution and build the package: + + $ cd dist/ethereum-unstable-1.5.0+xenial + $ dpkg-buildpackage + +Built packages are placed in the dist/ directory. + + $ cd .. + $ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb diff --git a/build/ci.go b/build/ci.go index 87e8b6275..cf43969f6 100644 --- a/build/ci.go +++ b/build/ci.go @@ -120,8 +120,6 @@ func main() { doArchive(os.Args[2:]) case "debsrc": doDebianSource(os.Args[2:]) - case "travis-debsrc": - doTravisDebianSource(os.Args[2:]) case "xgo": doXgo(os.Args[2:]) default: @@ -132,8 +130,8 @@ func main() { // Compiling func doInstall(cmdline []string) { - commitHash := flag.String("gitcommit", "", "Git commit hash embedded into binary.") flag.CommandLine.Parse(cmdline) + env := build.Env() // Check Go version. People regularly open issues about compilation // failure with outdated Go. This should save them the trouble. @@ -150,13 +148,17 @@ func doInstall(cmdline []string) { packages = flag.Args() } - goinstall := goTool("install", makeBuildFlags(*commitHash)...) + goinstall := goTool("install", buildFlags(env)...) goinstall.Args = append(goinstall.Args, "-v") goinstall.Args = append(goinstall.Args, packages...) build.MustRun(goinstall) } -func makeBuildFlags(commitHash string) (flags []string) { +func buildFlags(env build.Environment) (flags []string) { + if os.Getenv("GO_OPENCL") != "" { + flags = append(flags, "-tags", "opencl") + } + // Since Go 1.5, the separator char for link time assignments // is '=' and using ' ' prints a warning. However, Go < 1.5 does // not support using '='. @@ -164,26 +166,9 @@ func makeBuildFlags(commitHash string) (flags []string) { if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") { sep = "=" } - - if os.Getenv("GO_OPENCL") != "" { - flags = append(flags, "-tags", "opencl") - } - - // Set gitCommit constant via link-time assignment. If this is a git checkout, we can - // just get the current commit hash through git. Otherwise we fall back to the hash - // that was passed as -gitcommit. - // - // -gitcommit is required for Debian package builds. The source package doesn't - // contain .git but we still want to embed the commit hash into the packaged binary. - // The hash is rendered into the debian/rules build script when the source package is - // created. - if _, err := os.Stat(filepath.Join(".git", "HEAD")); !os.IsNotExist(err) { - if c := build.GitCommit(); c != "" { - commitHash = c - } - } - if commitHash != "" { - flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+commitHash) + // Set gitCommit constant via link-time assignment. + if env.Commit != "" { + flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit) } return flags } @@ -253,7 +238,11 @@ func doArchive(cmdline []string) { default: log.Fatal("unknown archive type: ", atype) } - base := makeArchiveBasename() + + env := build.Env() + maybeSkipArchive(env) + + base := archiveBasename(env) if err := build.WriteArchive("geth-"+base, ext, gethArchiveFiles); err != nil { log.Fatal(err) } @@ -262,36 +251,41 @@ func doArchive(cmdline []string) { } } -func makeArchiveBasename() string { +func archiveBasename(env build.Environment) string { // date := time.Now().UTC().Format("200601021504") platform := runtime.GOOS + "-" + runtime.GOARCH archive := platform + "-" + build.VERSION() - if commit := build.GitCommit(); commit != "" { - archive += "-" + commit[:8] + if env.Commit != "" { + archive += "-" + env.Commit[:8] } return archive } +// 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.Branch != "develop" && !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 -// CLI entry point for Travis CI. -func doTravisDebianSource(cmdline []string) { +func doDebianSource(cmdline []string) { + var ( + signer = flag.String("signer", "", `Signing key name, also used as package author`) + upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`) + workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) + now = time.Now() + ) flag.CommandLine.Parse(cmdline) - - // Package only whitelisted branches. - switch { - case os.Getenv("TRAVIS_REPO_SLUG") != "ethereum/go-ethereum": - log.Printf("skipping because this is a fork build") - return - case os.Getenv("TRAVIS_PULL_REQUEST") != "false": - log.Printf("skipping because this is a PR build") - return - case os.Getenv("TRAVIS_BRANCH") != "develop" && !strings.HasPrefix(os.Getenv("TRAVIS_TAG"), "v1."): - log.Printf("skipping because branch %q tag %q is not on the whitelist", - os.Getenv("TRAVIS_BRANCH"), - os.Getenv("TRAVIS_TAG")) - return - } + *workdir = makeWorkdir(*workdir) + env := build.Env() + maybeSkipArchive(env) // Import the signing key. if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" { @@ -304,46 +298,16 @@ func doTravisDebianSource(cmdline []string) { build.MustRun(gpg) } - // Assign unstable status to non-tag builds. - unstable := "true" - if os.Getenv("TRAVIS_BRANCH") != "develop" && os.Getenv("TRAVIS_TAG") != "" { - unstable = "false" - } - - doDebianSource([]string{ - "-signer", "Felix Lange (Geth CI Testing Key) ", - "-buildnum", os.Getenv("TRAVIS_BUILD_NUMBER"), - "-upload", "ppa:lp-fjl/geth-ci-testing", - "-unstable", unstable, - }) -} - -// CLI entry point for doing packaging locally. -func doDebianSource(cmdline []string) { - var ( - signer = flag.String("signer", "", `Signing key name, also used as package author`) - upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`) - buildnum = flag.String("buildnum", "", `Build number (included in version)`) - unstable = flag.Bool("unstable", false, `Use package name suffix "-unstable"`) - now = time.Now() - ) - flag.CommandLine.Parse(cmdline) - - // Create the debian worktree in /tmp. - tmpdir, err := ioutil.TempDir("", "eth-deb-build-") - if err != nil { - log.Fatal(err) - } - + // Create the packages. for _, distro := range debDistros { - meta := newDebMetadata(distro, *signer, *buildnum, *unstable, now) - pkgdir := stageDebianSource(tmpdir, meta) + meta := newDebMetadata(distro, *signer, env, now) + pkgdir := stageDebianSource(*workdir, meta) debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc") debuild.Dir = pkgdir build.MustRun(debuild) changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString()) - changes = filepath.Join(tmpdir, changes) + changes = filepath.Join(*workdir, changes) if *signer != "" { build.MustRunCommand("debsign", changes) } @@ -353,35 +317,53 @@ func doDebianSource(cmdline []string) { } } -type debExecutable struct { - Name, Description string +func makeWorkdir(wdflag string) string { + var err error + if wdflag != "" { + err = os.MkdirAll(wdflag, 0744) + } else { + wdflag, err = ioutil.TempDir("", "eth-deb-build-") + } + if err != nil { + log.Fatal(err) + } + return wdflag +} + +func isUnstableBuild(env build.Environment) bool { + if env.Branch != "develop" && env.Tag != "" { + return false + } + return true } type debMetadata struct { + Env build.Environment + // go-ethereum version being built. Note that this // is not the debian package version. The package version // is constructed by VersionString. Version string - Author string // "name ", also selects signing key - Buildnum string // build number - Distro, Commit, Time string - Executables []debExecutable - Unstable bool + Author string // "name ", also selects signing key + Distro, Time string + Executables []debExecutable } -func newDebMetadata(distro, author, buildnum string, unstable bool, t time.Time) debMetadata { +type debExecutable struct { + Name, Description string +} + +func newDebMetadata(distro, author string, env build.Environment, t time.Time) debMetadata { if author == "" { // No signing key, use default author. author = "Ethereum Builds " } return debMetadata{ - Unstable: unstable, + Env: env, Author: author, Distro: distro, - Commit: build.GitCommit(), Version: build.VERSION(), - Buildnum: buildnum, Time: t.Format(time.RFC1123Z), Executables: debExecutables, } @@ -390,7 +372,7 @@ func newDebMetadata(distro, author, buildnum string, unstable bool, t time.Time) // Name returns the name of the metapackage that depends // on all executable packages. func (meta debMetadata) Name() string { - if meta.Unstable { + if isUnstableBuild(meta.Env) { return "ethereum-unstable" } return "ethereum" @@ -399,8 +381,8 @@ func (meta debMetadata) Name() string { // VersionString returns the debian version of the packages. func (meta debMetadata) VersionString() string { vsn := meta.Version - if meta.Buildnum != "" { - vsn += "+build" + meta.Buildnum + if meta.Env.Buildnum != "" { + vsn += "+build" + meta.Env.Buildnum } if meta.Distro != "" { vsn += "+" + meta.Distro @@ -419,7 +401,7 @@ func (meta debMetadata) ExeList() string { // ExeName returns the package name of an executable package. func (meta debMetadata) ExeName(exe debExecutable) string { - if meta.Unstable { + if isUnstableBuild(meta.Env) { return exe.Name + "-unstable" } return exe.Name @@ -428,7 +410,7 @@ func (meta debMetadata) ExeName(exe debExecutable) string { // ExeConflicts returns the content of the Conflicts field // for executable packages. func (meta debMetadata) ExeConflicts(exe debExecutable) string { - if meta.Unstable { + if isUnstableBuild(meta.Env) { // Set up the conflicts list so that the *-unstable packages // cannot be installed alongside the regular version. // @@ -461,8 +443,8 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) { 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 { - install := filepath.Join(debian, exe.Name+".install") - docs := filepath.Join(debian, exe.Name+".docs") + install := filepath.Join(debian, meta.ExeName(exe)+".install") + docs := filepath.Join(debian, meta.ExeName(exe)+".docs") build.Render("build/deb.install", install, 0644, exe) build.Render("build/deb.docs", docs, 0644, exe) } @@ -473,18 +455,19 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) { // Cross compilation func doXgo(cmdline []string) { + flag.CommandLine.Parse(cmdline) + env := build.Env() + // Make sure xgo is available for cross compilation gogetxgo := goTool("get", "github.com/karalabe/xgo") build.MustRun(gogetxgo) // Execute the actual cross compilation - pkg := cmdline[len(cmdline)-1] - args := append(cmdline[:len(cmdline)-1], makeBuildFlags("")...) - - build.MustRun(xgoTool(append(args, pkg)...)) + xgo := xgoTool(append(buildFlags(env), flag.Args()...)) + build.MustRun(xgo) } -func xgoTool(args ...string) *exec.Cmd { +func xgoTool(args []string) *exec.Cmd { cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...) cmd.Env = []string{ "GOPATH=" + build.GOPATH(), diff --git a/build/deb.changelog b/build/deb.changelog index a221f5470..83f804a83 100644 --- a/build/deb.changelog +++ b/build/deb.changelog @@ -1,5 +1,5 @@ {{.Name}} ({{.VersionString}}) {{.Distro}}; urgency=low - * git build of {{.Commit}} + * git build of {{.Env.Commit}} -- {{.Author}} {{.Time}} diff --git a/build/deb.control b/build/deb.control index 4a65c7fac..d39393d29 100644 --- a/build/deb.control +++ b/build/deb.control @@ -2,7 +2,7 @@ Source: {{.Name}} Section: science Priority: extra Maintainer: {{.Author}} -Build-Depends: debhelper (>= 8.0.0), golang-1.6 +Build-Depends: debhelper (>= 8.0.0), golang-1.7 Standards-Version: 3.9.5 Homepage: https://ethereum.org Vcs-Git: git://github.com/ethereum/go-ethereum.git diff --git a/build/deb.rules b/build/deb.rules index 3dfadb08d..11efe15fc 100644 --- a/build/deb.rules +++ b/build/deb.rules @@ -5,7 +5,7 @@ #export DH_VERBOSE=1 override_dh_auto_build: - build/env.sh /usr/lib/go-1.6/bin/go run build/ci.go install -gitcommit {{.Commit}} + build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} override_dh_auto_test: diff --git a/internal/build/env.go b/internal/build/env.go new file mode 100644 index 000000000..87b4c57ac --- /dev/null +++ b/internal/build/env.go @@ -0,0 +1,114 @@ +// 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 . + +package build + +import ( + "flag" + "fmt" + "os" +) + +var ( + // These flags override values in build env. + GitCommitFlag = flag.String("git-commit", "", `Overrides git commit hash embedded into executables`) + GitBranchFlag = flag.String("git-branch", "", `Overrides git branch being built`) + GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`) + BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`) + PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`) +) + +// Environment contains metadata provided by the build environment. +type Environment struct { + Name string // name of the environment + Repo string // name of GitHub repo + Commit, Branch, Tag string // Git info + Buildnum string + IsPullRequest bool +} + +func (env Environment) String() string { + return fmt.Sprintf("%s env (commit:%s branch:%s tag:%s buildnum:%s pr:%t)", + env.Name, env.Commit, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest) +} + +// Env returns metadata about the current CI environment, falling back to LocalEnv +// if not running on CI. +func Env() Environment { + switch { + case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true": + return Environment{ + Name: "travis", + Repo: os.Getenv("TRAVIS_REPO_SLUG"), + Commit: os.Getenv("TRAVIS_COMMIT"), + Branch: os.Getenv("TRAVIS_BRANCH"), + Tag: os.Getenv("TRAVIS_TAG"), + Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"), + IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false", + } + case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True": + return Environment{ + Name: "appveyor", + Repo: os.Getenv("APPVEYOR_REPO_NAME"), + Commit: os.Getenv("APPVEYOR_REPO_COMMIT"), + Branch: os.Getenv("APPVEYOR_REPO_BRANCH"), + Tag: os.Getenv("APPVEYOR_REPO_TAG"), + Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"), + IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", + } + default: + return LocalEnv() + } +} + +// LocalEnv returns build environment metadata gathered from git. +func LocalEnv() Environment { + env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"}) + if _, err := os.Stat(".git"); err != nil { + return env + } + if env.Commit == "" { + env.Commit = RunGit("rev-parse", "HEAD") + } + if env.Branch == "" { + env.Branch = RunGit("symbolic-ref", "-q", "--short", "HEAD") + } + // Note that we don't get the current git tag. It would slow down + // builds and isn't used by anything. + return env +} + +func applyEnvFlags(env Environment) Environment { + if !flag.Parsed() { + panic("you need to call flag.Parse before Env or LocalEnv") + } + if *GitCommitFlag != "" { + env.Commit = *GitCommitFlag + } + if *GitBranchFlag != "" { + env.Branch = *GitBranchFlag + } + if *GitTagFlag != "" { + env.Tag = *GitTagFlag + } + if *BuildnumFlag != "" { + env.Buildnum = *BuildnumFlag + } + if *PullRequestFlag { + env.IsPullRequest = true + } + return env +} diff --git a/internal/build/util.go b/internal/build/util.go index eead824b2..be41efed7 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -29,9 +29,7 @@ import ( "text/template" ) -var ( - DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") -) +var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") // MustRun executes the given command and exits the host process for // any error. @@ -69,6 +67,7 @@ func GOPATH() string { return strings.Join(newpath, string(filepath.ListSeparator)) } +// VERSION returns the content of the VERSION file. func VERSION() string { version, err := ioutil.ReadFile("VERSION") if err != nil { @@ -77,10 +76,8 @@ func VERSION() string { return string(bytes.TrimSpace(version)) } -func GitCommit() string { - return RunGit("rev-parse", "HEAD") -} - +// RunGit runs a git subcommand and returns its output. +// The command must complete successfully. func RunGit(args ...string) string { cmd := exec.Command("git", args...) var stdout, stderr bytes.Buffer @@ -94,12 +91,13 @@ func RunGit(args ...string) string { return strings.TrimSpace(stdout.String()) } -// Render renders the given template file. +// Render renders the given template file into outputFile. func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) { tpl := template.Must(template.ParseFiles(templateFile)) render(tpl, outputFile, outputPerm, x) } +// RenderString renders the given template string into outputFile. func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) { tpl := template.Must(template.New("").Parse(templateContent)) render(tpl, outputFile, outputPerm, x)