Restore config loading, keep exported Version, keep cgo off
Bring back the in-progress viper and go-homedir configuration loading from the original root command as a persistent pre-run on the root command, with a --config flag and a config line in sutil info. Restore the exported main.Version with a nolint directive instead of renaming it. Export CGO_ENABLED=0 from the Makefile, drop -race from make test, and remove the C toolchain from the Docker builder stage.
This commit is contained in:
+2
-4
@@ -33,9 +33,7 @@ RUN make fmt-check-md
|
||||
# Build stage
|
||||
# golang:1.26-alpine, 2026-10-27
|
||||
FROM golang@sha256:a2012b4a1d1f9f7e9f44e0d39b805a901e856b1dd80b4e6ee4338db547050308 AS builder
|
||||
# gcc and musl-dev are needed only because `make test` uses -race, which
|
||||
# requires cgo; the release binary below is built with CGO_ENABLED=0.
|
||||
RUN apk add --no-cache make gcc musl-dev
|
||||
RUN apk add --no-cache make
|
||||
WORKDIR /src
|
||||
|
||||
# Force BuildKit to run the lint and docs stages before proceeding
|
||||
@@ -49,7 +47,7 @@ RUN make test
|
||||
|
||||
ARG VERSION=dev
|
||||
RUN CGO_ENABLED=0 go build -trimpath \
|
||||
-ldflags="-s -w -X main.version=${VERSION}" \
|
||||
-ldflags="-s -w -X main.Version=${VERSION}" \
|
||||
-o /sutil ./cmd/sutil
|
||||
|
||||
# Runtime stage: the binary plus the full source it was built from
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
BINARY := sutil
|
||||
VERSION := $(shell git describe --always --dirty 2>/dev/null || echo dev)
|
||||
GOLDFLAGS := -X main.version=$(VERSION)
|
||||
GOLDFLAGS := -X main.Version=$(VERSION)
|
||||
GO_DIRS := cmd internal pkg
|
||||
|
||||
# golangci/golangci-lint:v2.12.2, 2026-05-06
|
||||
@@ -25,6 +25,10 @@ MARKDOWN_GLOB := '**/*.md'
|
||||
|
||||
all: build
|
||||
|
||||
# cgo stays off everywhere: builds are static and the tests do not use
|
||||
# -race, which would require a C toolchain.
|
||||
export CGO_ENABLED := 0
|
||||
|
||||
# Build the sutil binary in the repo root with the git version embedded.
|
||||
build:
|
||||
go build -trimpath -ldflags "$(GOLDFLAGS)" -o $(BINARY) ./cmd/$(BINARY)
|
||||
@@ -36,9 +40,9 @@ install:
|
||||
# Run the tests quietly; on failure rerun verbosely for diagnostics and
|
||||
# still fail.
|
||||
test:
|
||||
@go test -timeout 30s -race -cover ./... || \
|
||||
@go test -timeout 30s -cover ./... || \
|
||||
{ echo "--- Rerunning with -v for details ---"; \
|
||||
go test -timeout 30s -race -v ./...; exit 1; }
|
||||
go test -timeout 30s -v ./...; exit 1; }
|
||||
|
||||
# Lint with the pinned golangci-lint image when docker is available so local
|
||||
# results match CI exactly. Inside the Docker build (where there is no
|
||||
|
||||
+6
-4
@@ -8,12 +8,14 @@ import (
|
||||
"sneak.berlin/go/x/internal/cli"
|
||||
)
|
||||
|
||||
// version is the build version. The Makefile and Dockerfile set it at link
|
||||
// time with -ldflags "-X main.version=...".
|
||||
var version = "dev"
|
||||
// Version is the build version. The Makefile and Dockerfile set it at link
|
||||
// time with -ldflags "-X main.Version=...".
|
||||
//
|
||||
//nolint:gochecknoglobals // set by the linker; there is no other way
|
||||
var Version = "dev"
|
||||
|
||||
func main() {
|
||||
err := cli.New(cli.Params{Version: version}).Execute()
|
||||
err := cli.New(cli.Params{Version: Version, ConfigDir: ""}).Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -4,15 +4,25 @@ go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/kavfixnel/words v1.0.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
golang.org/x/crypto v0.58.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/sys v0.49.0 // indirect
|
||||
golang.org/x/text v0.43.0 // indirect
|
||||
|
||||
@@ -1,15 +1,46 @@
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kavfixnel/words v1.0.0 h1:k7/KXUnLIVNCAlBLXhdq1GunnxXmQqglZooHA+NRzuQ=
|
||||
github.com/kavfixnel/words v1.0.0/go.mod h1:i2LITxcnsJnKfT+ruhPGVwzdcRDnsgQTL9TIooX9XAU=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
|
||||
+30
-4
@@ -7,6 +7,7 @@ package cli
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Params configures New.
|
||||
@@ -14,23 +15,48 @@ type Params struct {
|
||||
// Version is the version string embedded at build time. It is shown
|
||||
// by `sutil version`, `sutil --version`, and `sutil info`.
|
||||
Version string
|
||||
|
||||
// ConfigDir is the directory searched for the optional .sneak
|
||||
// configuration file. Empty means the user's home directory; tests set
|
||||
// it to a temporary directory so the real home is never consulted.
|
||||
ConfigDir string
|
||||
}
|
||||
|
||||
// app carries the state shared by every command: the build parameters,
|
||||
// the --config flag, and the configuration loaded before any command runs.
|
||||
type app struct {
|
||||
params Params
|
||||
configFile string
|
||||
config *viper.Viper
|
||||
}
|
||||
|
||||
// New builds the root sutil command with every subcommand attached.
|
||||
func New(p Params) *cobra.Command {
|
||||
a := &app{params: p, configFile: "", config: nil}
|
||||
|
||||
root := &cobra.Command{
|
||||
Use: "sutil",
|
||||
Short: "sneak's shell utility",
|
||||
Long: `sutil is a small collection of shell utilities built on the packages in
|
||||
sneak.berlin/go/x: fast pseudorandom byte streams, memorable passwords,
|
||||
BIP39 mnemonics, and crypto_box keypairs derived from them.`,
|
||||
Version: p.Version,
|
||||
SilenceUsage: true,
|
||||
BIP39 mnemonics, and crypto_box keypairs derived from them.
|
||||
|
||||
Before any command runs, sutil loads the optional configuration file named
|
||||
by --config, or else ~/.sneak.yaml (or .json, .toml, ...) if it exists, and
|
||||
makes environment variables available alongside it.`,
|
||||
Version: p.Version,
|
||||
SilenceUsage: true,
|
||||
PersistentPreRunE: a.loadConfig,
|
||||
}
|
||||
|
||||
root.PersistentFlags().StringVar(
|
||||
&a.configFile, "config", "",
|
||||
"config file (default is "+configName+".* in the home directory)",
|
||||
)
|
||||
|
||||
root.AddCommand(
|
||||
newVersionCommand(p),
|
||||
newInfoCommand(p),
|
||||
newInfoCommand(a),
|
||||
newGenerateCommand(),
|
||||
newCBKCommand(),
|
||||
)
|
||||
|
||||
@@ -3,6 +3,8 @@ package cli_test
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -54,6 +56,56 @@ func TestInfoCommand(t *testing.T) {
|
||||
assert.Contains(t, output, "version: "+testVersion+"\n")
|
||||
assert.Contains(t, output, "go: go")
|
||||
assert.Contains(t, output, "module: sneak.berlin/go/x\n")
|
||||
assert.Contains(t, output, "config: (none)\n")
|
||||
}
|
||||
|
||||
func TestConfigFileDiscoveredInConfigDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
configDir := t.TempDir()
|
||||
path := filepath.Join(configDir, ".sneak.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte("example: 1\n"), 0o600))
|
||||
|
||||
params := cli.Params{Version: testVersion, ConfigDir: configDir}
|
||||
|
||||
output, err := executeWithParams(t, params, "", "info")
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "Using config file: "+path+"\n")
|
||||
assert.Contains(t, output, "config: "+path+"\n")
|
||||
}
|
||||
|
||||
func TestConfigFlagLoadsNamedFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "elsewhere.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte("example: 1\n"), 0o600))
|
||||
|
||||
output, err := execute(t, "", "--config", path, "info")
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "Using config file: "+path+"\n")
|
||||
assert.Contains(t, output, "config: "+path+"\n")
|
||||
}
|
||||
|
||||
func TestConfigFlagRejectsMissingFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "missing.yaml")
|
||||
|
||||
_, err := execute(t, "", "--config", path, "version")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestConfigRejectsMalformedFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
configDir := t.TempDir()
|
||||
path := filepath.Join(configDir, ".sneak.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte("not: [valid\n"), 0o600))
|
||||
|
||||
params := cli.Params{Version: testVersion, ConfigDir: configDir}
|
||||
|
||||
_, err := executeWithParams(t, params, "", "version")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGenerateRandomSeededIsDeterministic(t *testing.T) {
|
||||
@@ -157,12 +209,24 @@ func TestCBKDeriveRejectsInvalidMnemonic(t *testing.T) {
|
||||
require.ErrorIs(t, err, bip39wordlist.ErrInvalidWordCount)
|
||||
}
|
||||
|
||||
// execute runs sutil with the given stdin and arguments and returns
|
||||
// everything it wrote to stdout and stderr.
|
||||
// execute runs sutil with the given stdin and arguments, looking for config
|
||||
// in an empty temporary directory, and returns everything it wrote to
|
||||
// stdout and stderr.
|
||||
func execute(t *testing.T, stdin string, args ...string) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
root := cli.New(cli.Params{Version: testVersion})
|
||||
params := cli.Params{Version: testVersion, ConfigDir: t.TempDir()}
|
||||
|
||||
return executeWithParams(t, params, stdin, args...)
|
||||
}
|
||||
|
||||
// executeWithParams is execute with explicit Params.
|
||||
func executeWithParams(
|
||||
t *testing.T, params cli.Params, stdin string, args ...string,
|
||||
) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
root := cli.New(params)
|
||||
|
||||
var output bytes.Buffer
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// configName is the base name of the optional configuration file looked
|
||||
// up in the config directory; viper tries every extension it supports, so
|
||||
// ~/.sneak.yaml, ~/.sneak.json, ~/.sneak.toml, and so on all work.
|
||||
const configName = ".sneak"
|
||||
|
||||
// loadConfig runs before every command. It reads the optional
|
||||
// configuration file into a.config and makes environment variables
|
||||
// available through it. The file is the one named by --config, or else
|
||||
// configName in Params.ConfigDir, which defaults to the user's home
|
||||
// directory. A missing default file is not an error; a file that is named
|
||||
// explicitly but missing, or one that cannot be parsed, is.
|
||||
func (a *app) loadConfig(cmd *cobra.Command, _ []string) error {
|
||||
configDir := a.params.ConfigDir
|
||||
if configDir == "" {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding home directory: %w", err)
|
||||
}
|
||||
|
||||
configDir = home
|
||||
}
|
||||
|
||||
config := viper.New()
|
||||
config.AddConfigPath(configDir)
|
||||
config.SetConfigName(configName)
|
||||
config.AutomaticEnv()
|
||||
|
||||
if a.configFile != "" {
|
||||
config.SetConfigFile(a.configFile)
|
||||
}
|
||||
|
||||
err := config.ReadInConfig()
|
||||
|
||||
var notFound viper.ConfigFileNotFoundError
|
||||
if errors.As(err, ¬Found) {
|
||||
a.config = config
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading config file: %w", err)
|
||||
}
|
||||
|
||||
a.config = config
|
||||
|
||||
_, err = fmt.Fprintln(
|
||||
cmd.ErrOrStderr(), "Using config file:", config.ConfigFileUsed(),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// configFileUsed returns the path of the configuration file that
|
||||
// loadConfig read, or an empty string if there was none.
|
||||
func (a *app) configFileUsed() string {
|
||||
if a.config == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return a.config.ConfigFileUsed()
|
||||
}
|
||||
+15
-5
@@ -15,31 +15,41 @@ import (
|
||||
// module support.
|
||||
var ErrNoBuildInfo = errors.New("sutil: no build information in this binary")
|
||||
|
||||
// noConfigFile is what `sutil info` prints when no config file was loaded.
|
||||
const noConfigFile = "(none)"
|
||||
|
||||
// newInfoCommand returns `sutil info`, which prints the build version, the
|
||||
// Go toolchain version, the module path, and the VCS metadata recorded by
|
||||
// the Go toolchain at build time.
|
||||
func newInfoCommand(p Params) *cobra.Command {
|
||||
// Go toolchain version, the module path, the configuration file in use,
|
||||
// and the VCS metadata recorded by the Go toolchain at build time.
|
||||
func newInfoCommand(a *app) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Print build information",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return writeBuildInfo(cmd.OutOrStdout(), p.Version)
|
||||
return writeBuildInfo(
|
||||
cmd.OutOrStdout(), a.params.Version, a.configFileUsed(),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// writeBuildInfo writes one "key: value" line per fact to w.
|
||||
func writeBuildInfo(w io.Writer, version string) error {
|
||||
func writeBuildInfo(w io.Writer, version, configFile string) error {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return ErrNoBuildInfo
|
||||
}
|
||||
|
||||
if configFile == "" {
|
||||
configFile = noConfigFile
|
||||
}
|
||||
|
||||
lines := []string{
|
||||
"version: " + version,
|
||||
"go: " + info.GoVersion,
|
||||
"module: " + info.Main.Path,
|
||||
"config: " + configFile,
|
||||
}
|
||||
|
||||
for _, setting := range info.Settings {
|
||||
|
||||
Reference in New Issue
Block a user