feat/release-telemetry-webui #28

Merged
gary merged 4 commits from feat/release-telemetry-webui into main 2026-07-07 15:11:19 +00:00
8 changed files with 176 additions and 9 deletions
Showing only changes of commit 94e41fe5b5 - Show all commits
+132
View File
@@ -0,0 +1,132 @@
name: Release
# Builds release artifacts when a version tag (v*) is pushed:
# - cross-compiled CLI + server binaries attached to the Gitea release
# - version-tagged docker images for the CLI and web server
# The owner usually creates the Gitea release by hand with notes; this
# workflow attaches assets to it (creating a bare release only when none
# exists) and skips assets that are already attached, so re-runs are safe.
on:
push:
tags: ["v*"]
jobs:
binaries:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Build release binaries
env:
GOTOOLCHAIN: local
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
mkdir -p dist
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64; do
GOOS="${target%/*}"
GOARCH="${target#*/}"
EXT=""
[ "$GOOS" = "windows" ] && EXT=".exe"
OUT="dist/${GOOS}_${GOARCH}"
mkdir -p "$OUT"
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" \
go build -trimpath -ldflags "-s -w -X main.version=${TAG}" \
-o "${OUT}/exploredns${EXT}" ./cmd/exploredns
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" \
go build -trimpath -ldflags "-s -w -X main.version=${TAG}" \
-o "${OUT}/exploredns-server${EXT}" ./cmd/server
if [ "$GOOS" = "windows" ]; then
(cd "$OUT" && zip -q "../exploredns_${TAG}_${GOOS}_${GOARCH}.zip" exploredns.exe exploredns-server.exe)
else
tar -czf "dist/exploredns_${TAG}_${GOOS}_${GOARCH}.tar.gz" -C "$OUT" exploredns exploredns-server
fi
rm -rf "$OUT"
done
(cd dist && sha256sum -- * > SHA256SUMS)
ls -l dist
- name: Attach assets to Gitea release
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
API: ${{ github.server_url }}/api/v1
REPO: ${{ github.repository }}
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
AUTH="Authorization: token ${TOKEN}"
# Look up the release for this tag; create a bare one only when
# none exists (the owner writes release notes by hand).
STATUS=$(curl -sS -o release.json -w '%{http_code}' -H "$AUTH" \
"${API}/repos/${REPO}/releases/tags/${TAG}")
if [ "$STATUS" = "404" ]; then
curl -sS -f -o release.json -H "$AUTH" -H 'Content-Type: application/json' \
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\"}" \
"${API}/repos/${REPO}/releases"
elif [ "$STATUS" != "200" ]; then
echo "release lookup failed with HTTP ${STATUS}" >&2
cat release.json >&2
exit 1
fi
RELEASE_ID=$(jq -r '.id' release.json)
echo "release id: ${RELEASE_ID}"
# Existing asset names, so re-runs skip instead of failing.
curl -sS -f -H "$AUTH" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets" \
| jq -r '.[].name' > existing.txt
for f in dist/*; do
NAME=$(basename "$f")
if grep -Fxq "$NAME" existing.txt; then
echo "skip ${NAME} (already attached)"
continue
fi
echo "upload ${NAME}"
curl -sS -f -o /dev/null -H "$AUTH" \
-F "attachment=@${f}" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${NAME}"
done
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: gitea.hansenits.com.au
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push CLI image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.cli
push: true
build-args: |
VERSION=${{ github.ref_name }}
tags: |
gitea.hansenits.com.au/hits/exploredns-cli:latest
gitea.hansenits.com.au/hits/exploredns-cli:${{ github.ref_name }}
- name: Build and push web image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.web
push: true
build-args: |
VERSION=${{ github.ref_name }}
tags: |
gitea.hansenits.com.au/hits/exploredns-web:latest
gitea.hansenits.com.au/hits/exploredns-web:${{ github.ref_name }}
+2 -1
View File
@@ -8,7 +8,8 @@ RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/exploredns ./cmd/exploredns ARG VERSION=dev
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" -o /out/exploredns ./cmd/exploredns
# Final stage # Final stage
FROM alpine:3.21 FROM alpine:3.21
+2 -1
View File
@@ -8,7 +8,8 @@ RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server ARG VERSION=dev
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" -o /out/server ./cmd/server
# Final stage # Final stage
FROM alpine:3.21 FROM alpine:3.21
+4 -2
View File
@@ -3,14 +3,16 @@ SERVER_BINARY_NAME=exploredns-server
BUILD_DIR=bin BUILD_DIR=bin
GO=go GO=go
GOFLAGS=-v GOFLAGS=-v
VERSION?=$(shell git describe --tags --always 2>/dev/null || echo dev)
LDFLAGS=-ldflags "-X main.version=$(VERSION)"
.PHONY: build build-server build-all test lint clean cover deploy deploy-status .PHONY: build build-server build-all test lint clean cover deploy deploy-status
build: build:
$(GO) build $(GOFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/exploredns $(GO) build $(GOFLAGS) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/exploredns
build-server: build-server:
$(GO) build $(GOFLAGS) -o $(BUILD_DIR)/$(SERVER_BINARY_NAME) ./cmd/server $(GO) build $(GOFLAGS) $(LDFLAGS) -o $(BUILD_DIR)/$(SERVER_BINARY_NAME) ./cmd/server
build-all: build build-server build-all: build build-server
+14
View File
@@ -13,6 +13,9 @@ import (
"gitea.hansenits.com.au/hits/ExploreDNS/internal/traverse" "gitea.hansenits.com.au/hits/ExploreDNS/internal/traverse"
) )
// version is stamped at build time via -ldflags "-X main.version=...".
var version = "dev"
func main() { func main() {
cfg := config.DefaultConfig() cfg := config.DefaultConfig()
@@ -42,6 +45,12 @@ func main() {
flag.BoolVar(&dFlag, "debug", false, "Print debug diagnostics to stderr") flag.BoolVar(&dFlag, "debug", false, "Print debug diagnostics to stderr")
flag.BoolVar(&ddFlag, "dd", false, "Like -d plus library-level debug") flag.BoolVar(&ddFlag, "dd", false, "Like -d plus library-level debug")
// Version: long and short form share the same variable (mirrors
// dnstraverse's -V).
var showVersion bool
flag.BoolVar(&showVersion, "version", false, "Print version and exit")
flag.BoolVar(&showVersion, "V", false, "Print version and exit (shorthand)")
// Quiet: long and short form share the same variable. // Quiet: long and short form share the same variable.
var quietVal bool var quietVal bool
flag.BoolVar(&quietVal, "quiet", cfg.Quiet, "Suppress the header block") flag.BoolVar(&quietVal, "quiet", cfg.Quiet, "Suppress the header block")
@@ -69,6 +78,11 @@ func main() {
flag.Parse() flag.Parse()
if showVersion {
fmt.Printf("exploredns %s\n", version)
return
}
args := flag.Args() args := flag.Args()
cfg.QueryType = *queryType cfg.QueryType = *queryType
+5 -1
View File
@@ -13,17 +13,21 @@ import (
"gitea.hansenits.com.au/hits/ExploreDNS/web/api" "gitea.hansenits.com.au/hits/ExploreDNS/web/api"
) )
// version is stamped at build time via -ldflags "-X main.version=...".
var version = "dev"
func main() { func main() {
addr := flag.String("addr", ":8080", "listen address (host:port)") addr := flag.String("addr", ":8080", "listen address (host:port)")
flag.Parse() flag.Parse()
srv := api.NewServer(*addr) srv := api.NewServer(*addr)
srv.SetVersion(version)
if err := srv.Start(); err != nil { if err := srv.Start(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err) fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1) os.Exit(1)
} }
log.Printf("ExploreDNS API server listening on %s", srv.Addr()) log.Printf("ExploreDNS API server %s listening on %s", version, srv.Addr())
quit := make(chan os.Signal, 1) quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+3
View File
@@ -167,6 +167,9 @@ func PrintUsage() {
{"--show-results", "Show the results (default true)"}, {"--show-results", "Show the results (default true)"},
{"--show-summary-results", "Show the summary results (default true)"}, {"--show-summary-results", "Show the summary results (default true)"},
}}, }},
{"General Options", [][2]string{
{"--version, -V", "Print version and exit"},
}},
} }
for _, group := range flagGroups { for _, group := range flagGroups {
+11 -1
View File
@@ -28,13 +28,22 @@ var staticFiles embed.FS
// Server is the HTTP API server. // Server is the HTTP API server.
type Server struct { type Server struct {
addr string addr string
version string
srv *http.Server srv *http.Server
cancel context.CancelFunc cancel context.CancelFunc
} }
// NewServer creates a new Server that listens on addr (e.g. ":8080"). // NewServer creates a new Server that listens on addr (e.g. ":8080").
func NewServer(addr string) *Server { func NewServer(addr string) *Server {
return &Server{addr: addr} return &Server{addr: addr, version: "dev"}
}
// SetVersion records the build version reported by GET /api/health.
// Call before Start; empty values are ignored.
func (s *Server) SetVersion(v string) {
if v != "" {
s.version = v
}
} }
// Start builds the HTTP handler, begins listening, and returns when the // Start builds the HTTP handler, begins listening, and returns when the
@@ -44,6 +53,7 @@ func (s *Server) Start() error {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
s.cancel = cancel s.cancel = cancel
h := newHandler(ctx) h := newHandler(ctx)
h.version = s.version
sub, err := fs.Sub(staticFiles, "static") sub, err := fs.Sub(staticFiles, "static")
if err != nil { if err != nil {