Stamp the git tag or short commit into the binary (closes #10)
check / check (push) Failing after 3s
check / check (push) Failing after 3s
rtnetmon had no version. main.Version is now set at link time and logged in the first startup line. The Dockerfile takes it from the VERSION build argument when one is given, otherwise from `git describe --tags --always` of the .git the build context now carries, and fails the build if the context carries .git and no version comes out. .dockerignore follows the canonical copy: .git is sent, .git/config, which can hold a credential, is not. Model: opus-5-5
This commit is contained in:
+25
-10
@@ -1,19 +1,28 @@
|
||||
# .dockerignore does NOT use .gitignore semantics. Docker matches with
|
||||
# moby/patternmatcher: filepath.Match plus `**`, so `*` does not cross
|
||||
# `/` and an unprefixed pattern is anchored at the context root. Every
|
||||
# depth-independent pattern therefore needs `**/`; only genuinely
|
||||
# root-anchored entries go unprefixed. Never transplant these into
|
||||
# .gitignore, where `**/` is wrong.
|
||||
# depth-independent pattern therefore needs `**/`, or `config/.env` and
|
||||
# `certs/server.key` still ship while this file reads as solved. Only
|
||||
# genuinely root-anchored entries go unprefixed. Never transplant these
|
||||
# into .gitignore, where `**/` is wrong.
|
||||
#
|
||||
# Matching is case-sensitive, so secrets use character ranges rather
|
||||
# than an ALL-CAPS twin, which would still miss `Server.Key`.
|
||||
#
|
||||
# Extend with this repo's own host-built artifacts, written anchored:
|
||||
# `/myapp`, never `**/myapp`, which also matches `cmd/myapp/` and
|
||||
# deletes the package directory from the context.
|
||||
|
||||
# Excluding .git means `git describe` cannot run in any build stage and
|
||||
# fails quietly there; rtnetmon embeds no version, so this is safe.
|
||||
.git
|
||||
# .git is sent without its config. Without a VERSION build argument the
|
||||
# stage that compiles runs `git describe --tags --always` on .git, which
|
||||
# does not need .git/config; that file can hold a credential, such as a
|
||||
# password in a remote URL or the token the CI checkout step stores there.
|
||||
.git/config
|
||||
|
||||
# Agent scratch: one full checkout of the repo per in-flight agent.
|
||||
# Anchored because agents run at the repo root here.
|
||||
# Anchored because it occurs once where agents run at the repo root.
|
||||
# KNOWN GAP: a repo running agents in subdirectories still ships
|
||||
# `services/api/.claude/` and must add its own anchored entry.
|
||||
.claude
|
||||
|
||||
# This repo's own host-built artifacts, root-anchored so `bin/` is not
|
||||
@@ -22,12 +31,15 @@
|
||||
/rtnetmon
|
||||
main.go.old
|
||||
|
||||
# Environment files.
|
||||
# Environment files. `*.env` covers bare `.env` and the `prod.env`
|
||||
# convention. Re-include a committed template with a negation if the
|
||||
# build needs one: `!docs/example.env`.
|
||||
**/*.[eE][nN][vV]
|
||||
**/.[eE][nN][vV].*
|
||||
**/.[eE][nN][vV][rR][cC]
|
||||
|
||||
# Private keys and the bundles carrying them.
|
||||
# Private keys and the bundles carrying them. Public certificates
|
||||
# (*.crt, *.cer) are deliberately absent: they are legitimate inputs.
|
||||
**/*.[pP][eE][mM]
|
||||
**/*.[kK][eE][yY]
|
||||
**/*.[pP]12
|
||||
@@ -37,11 +49,14 @@ main.go.old
|
||||
**/[iI][dD]_[eE][cC][dD][sS][aA]
|
||||
**/[iI][dD]_[eE][dD]25519
|
||||
|
||||
# Dependencies: restored inside the image, never copied in.
|
||||
**/node_modules
|
||||
|
||||
# OS metadata.
|
||||
**/.DS_Store
|
||||
**/Thumbs.db
|
||||
|
||||
# Editor state.
|
||||
# Editor state: never a build input, and it churns COPY.
|
||||
**/*.swp
|
||||
**/*.swo
|
||||
**/*~
|
||||
|
||||
+15
-1
@@ -34,4 +34,18 @@ WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -trimpath -o /rtnetmon ./cmd/rtnetmon/
|
||||
# The version stamped into the binary: the VERSION build argument when one
|
||||
# is given, otherwise `git describe --tags --always` of the .git the build
|
||||
# context carries: the tag on a tagged commit, tag-N-gHASH on a commit after
|
||||
# one, the short commit when no tag is reachable. git ships in this base
|
||||
# image. A context that carries .git and still yields no version fails the
|
||||
# build. With neither, as from a source tarball, the binary reports dev.
|
||||
ARG VERSION
|
||||
RUN version="${VERSION:-$(git describe --tags --always)}"; \
|
||||
if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \
|
||||
[ "$version" = unknown ]; }; then \
|
||||
echo "no version could be derived although the build context carries .git" >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
CGO_ENABLED=0 go build -trimpath -ldflags="-X main.Version=${version:-dev}" \
|
||||
-o /rtnetmon ./cmd/rtnetmon/
|
||||
|
||||
@@ -8,8 +8,12 @@ import (
|
||||
"git.eeqj.de/sneak/rtnetmon/internal/cli"
|
||||
)
|
||||
|
||||
// Version is the git tag or short commit, set at link time with -X by the
|
||||
// Dockerfile. Builds that do not set it report dev.
|
||||
var Version = "dev" //nolint:gochecknoglobals // set at link time with -X
|
||||
|
||||
func main() {
|
||||
err := cli.Execute()
|
||||
err := cli.Execute(Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ package cli
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// NewRootCmd exposes newRootCmd for external tests.
|
||||
func NewRootCmd() *cobra.Command { return newRootCmd() }
|
||||
func NewRootCmd() *cobra.Command { return newRootCmd("dev") }
|
||||
|
||||
@@ -51,8 +51,9 @@ func defaultTCPHosts() []string {
|
||||
}
|
||||
}
|
||||
|
||||
// newRootCmd builds the cobra root command with its flags bound.
|
||||
func newRootCmd() *cobra.Command {
|
||||
// newRootCmd builds the cobra root command with its flags bound. version is
|
||||
// logged at startup.
|
||||
func newRootCmd(version string) *cobra.Command {
|
||||
cfg := &config{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "rtnetmon",
|
||||
@@ -60,7 +61,7 @@ func newRootCmd() *cobra.Command {
|
||||
Long: `rtnetmon is a dual-interface network monitoring dashboard that provides
|
||||
real-time visibility into network health, packet loss, and latency.`,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runMonitor(cmd, cfg)
|
||||
return runMonitor(cmd, cfg, version)
|
||||
},
|
||||
}
|
||||
registerFlags(cmd, cfg)
|
||||
@@ -88,8 +89,8 @@ func registerFlags(cmd *cobra.Command, cfg *config) {
|
||||
|
||||
// runMonitor detects the interfaces to monitor, then constructs and runs the
|
||||
// monitor from cfg.
|
||||
func runMonitor(cmd *cobra.Command, cfg *config) error {
|
||||
monitor.Logf(cfg.LogFile, "Starting rtnetmon")
|
||||
func runMonitor(cmd *cobra.Command, cfg *config, version string) error {
|
||||
monitor.Logf(cfg.LogFile, "Starting rtnetmon %s", version)
|
||||
|
||||
specs, err := detectInterfaces(cmd, cfg)
|
||||
if err != nil {
|
||||
@@ -169,7 +170,7 @@ func detectInterfaces(
|
||||
return specs, nil
|
||||
}
|
||||
|
||||
// Execute builds the root command and runs it.
|
||||
func Execute() error {
|
||||
return newRootCmd().Execute()
|
||||
// Execute builds the root command and runs it. version is logged at startup.
|
||||
func Execute(version string) error {
|
||||
return newRootCmd(version).Execute()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user