chore(build): update build system, Dockerfiles, and dependencies
Build and deployment improvements: Makefile: - Native library build targets with ASan support - Cross-platform compilation helpers - Performance benchmark targets - Security scan integration Docker: - secure-prod.Dockerfile: Hardened production image (non-root, minimal surface) - simple.Dockerfile: Lightweight development image Scripts: - build/: Go and native library build scripts, cross-platform builds - ci/: checks.sh, test.sh, verify-paths.sh for validation - benchmarks/: Local performance testing and regression tracking - dev/: Monitoring setup Dependencies: Update to latest stable with security patches Commands: - api-server/main.go: Server initialization updates - data_manager/data_sync.go: Data sync with visibility - errors/main.go: Error handling improvements - tui/: TUI improvements for group management
This commit is contained in:
parent
4b2782f674
commit
cb142213fa
23 changed files with 953 additions and 856 deletions
25
Makefile
25
Makefile
|
|
@ -71,8 +71,7 @@ build: native-build openapi-generate-server
|
|||
go build -ldflags="$(LDFLAGS)" -o bin/server/tui ./cmd/tui
|
||||
@cp native/build/lib*.so native/build/lib*.dylib bin/native/ 2>/dev/null || true
|
||||
$(MAKE) -C ./cli all
|
||||
@arch=$$(uname -m | sed 's/x86_64/amd64/'); os=$$(uname -s | tr '[:upper:]' '[:lower:]'); \
|
||||
cp cli/zig-out/bin/ml bin/cli/ml-$${os}-$${arch}
|
||||
@cp cli/zig-out/bin/ml-* bin/cli/ml-$$(uname -s | tr '[:upper:]' '[:lower:]')-$$(uname -m | sed 's/x86_64/amd64/')
|
||||
@echo "$(OK) All components built"
|
||||
|
||||
prod:
|
||||
|
|
@ -83,8 +82,7 @@ prod:
|
|||
go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/user_manager ./cmd/user_manager
|
||||
go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/tui ./cmd/tui
|
||||
$(MAKE) -C cli prod
|
||||
@arch=$$(uname -m | sed 's/x86_64/amd64/'); os=$$(uname -s | tr '[:upper:]' '[:lower:]'); \
|
||||
cp cli/zig-out/bin/ml bin/cli/ml-$${os}-$${arch}
|
||||
@cp cli/zig-out/bin/ml-* bin/cli/ml-$$(uname -s | tr '[:upper:]' '[:lower:]')-$$(uname -m | sed 's/x86_64/amd64/')
|
||||
@echo "$(OK) Production binaries built"
|
||||
|
||||
dev:
|
||||
|
|
@ -95,8 +93,7 @@ dev:
|
|||
go build -buildvcs=false -o bin/server/user_manager ./cmd/user_manager
|
||||
go build -buildvcs=false -o bin/server/tui ./cmd/tui
|
||||
$(MAKE) -C cli dev
|
||||
@arch=$$(uname -m | sed 's/x86_64/amd64/'); os=$$(uname -s | tr '[:upper:]' '[:lower:]'); \
|
||||
cp cli/zig-out/bin/ml bin/cli/ml-$${os}-$${arch}
|
||||
@cp cli/zig-out/bin/ml-* bin/cli/ml-$$(uname -s | tr '[:upper:]' '[:lower:]')-$$(uname -m | sed 's/x86_64/amd64/')
|
||||
@echo "$(OK) Development binaries built"
|
||||
|
||||
native-build:
|
||||
|
|
@ -349,14 +346,22 @@ gosec:
|
|||
|
||||
govulncheck:
|
||||
$(call ensure_tool,govulncheck,golang.org/x/vuln/cmd/govulncheck@latest)
|
||||
govulncheck ./...
|
||||
@govulncheck ./... || exit_code=$$?; \
|
||||
if [ "$$exit_code" = "3" ]; then \
|
||||
echo "Note: govulncheck found vulnerabilities (see above)"; \
|
||||
elif [ "$$exit_code" != "0" ] && [ "$$exit_code" != "" ]; then \
|
||||
exit $$exit_code; \
|
||||
fi
|
||||
@echo "$(OK) govulncheck complete"
|
||||
|
||||
check-unsafe:
|
||||
@if grep -r "unsafe\." --include="*.go" ./internal ./cmd 2>/dev/null | grep -v "_test.go"; then \
|
||||
echo "WARNING: unsafe package usage found — review required"; exit 1; \
|
||||
@unsafe_files=$$(grep -r "unsafe\." --include="*.go" ./internal ./cmd 2>/dev/null | grep -v "_test.go" | grep -v "native_queue.go" | grep -v "native_bridge" || true); \
|
||||
if [ -n "$$unsafe_files" ]; then \
|
||||
echo "WARNING: unexpected unsafe package usage found — review required"; \
|
||||
echo "$$unsafe_files"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "$(OK) No unsafe package usage"; \
|
||||
echo "$(OK) No unexpected unsafe package usage"; \
|
||||
fi
|
||||
|
||||
security-audit: security-scan
|
||||
|
|
|
|||
|
|
@ -1,71 +1,66 @@
|
|||
# Secure Production Dockerfile with proper SSH setup
|
||||
FROM golang:1.25-alpine AS builder
|
||||
# Secure Production Dockerfile with cache optimization
|
||||
# Build with: DOCKER_BUILDKIT=1 docker build --build-arg WORKER_PASSWORD=$(openssl rand -base64 32) -f build/docker/secure-prod.Dockerfile .
|
||||
|
||||
# ============================================================================
|
||||
# STAGE 1: Go Dependencies (cached layer)
|
||||
# ============================================================================
|
||||
FROM golang:1.25-alpine AS go-deps
|
||||
|
||||
# Install dependencies
|
||||
RUN apk add --no-cache git make gcc musl-dev
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy go mod files
|
||||
# Copy only module files first for maximum cache efficiency
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download && \
|
||||
go mod verify
|
||||
|
||||
# Download dependencies
|
||||
RUN go mod download
|
||||
# ============================================================================
|
||||
# STAGE 2: Go Builder
|
||||
# ============================================================================
|
||||
FROM go-deps AS go-builder
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
# Copy source code (changes here won't rebuild deps layer)
|
||||
COPY cmd/ ./cmd/
|
||||
COPY internal/ ./internal/
|
||||
COPY pkg/ ./pkg/
|
||||
COPY tools/ ./tools/
|
||||
|
||||
# Build Go binaries (native libs not used in Docker since NVML unavailable in Alpine)
|
||||
RUN CGO_ENABLED=1 go build -o bin/api-server ./cmd/api-server/main.go && \
|
||||
CGO_ENABLED=1 go build -o bin/worker ./cmd/worker
|
||||
# Build Go binaries with cache mount for build cache
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=1 go build -ldflags="-w -s" -o bin/api-server ./cmd/api-server/main.go && \
|
||||
CGO_ENABLED=1 go build -ldflags="-w -s" -o bin/worker ./cmd/worker
|
||||
|
||||
# Final stage with Podman and secure SSH
|
||||
# ============================================================================
|
||||
# STAGE 3: Final Runtime with Podman and secure SSH
|
||||
# ============================================================================
|
||||
FROM alpine:3.19
|
||||
|
||||
# Install runtime dependencies including Podman and SSH
|
||||
RUN apk add --no-cache ca-certificates redis openssl curl podman openssh sudo gcc musl-dev
|
||||
# Build argument for worker password (no hardcoded secrets)
|
||||
ARG WORKER_PASSWORD=changeme
|
||||
ENV WORKER_PASSWORD=$WORKER_PASSWORD
|
||||
|
||||
# Create app user and worker user
|
||||
# Create app user and worker user, configure SSH in combined layer
|
||||
RUN addgroup -g 1001 -S appgroup && \
|
||||
adduser -u 1001 -S appuser -G appgroup && \
|
||||
addgroup -g 1002 -S workergroup && \
|
||||
adduser -u 1002 -S worker -G workergroup -s /bin/sh && \
|
||||
echo "worker:SecureWorkerPass2024!" | chpasswd && \
|
||||
echo "worker:$WORKER_PASSWORD" | chpasswd && \
|
||||
mkdir -p /home/worker/.ssh && \
|
||||
chown -R worker:workergroup /home/worker
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy binaries from builder
|
||||
COPY --from=builder /app/bin/ /usr/local/bin/
|
||||
|
||||
# Copy configs
|
||||
COPY --from=builder /app/configs/ /app/configs/
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /app/data/experiments /app/data/datasets /app/data/snapshots /app/logs /app/ssl /tmp/fetchml-jobs && \
|
||||
mkdir -p /data/active/datasets /data/active/snapshots && \
|
||||
mkdir -p /logs && \
|
||||
chown -R appuser:appgroup /app /data /logs
|
||||
|
||||
# Generate SSL certificates
|
||||
RUN openssl req -x509 -newkey rsa:2048 -keyout /app/ssl/key.pem -out /app/ssl/cert.pem -days 365 -nodes \
|
||||
-subj "/C=US/ST=Homelab/L=Local/O=ML/OU=Experiments/CN=localhost" && \
|
||||
chmod 644 /app/ssl/cert.pem && chmod 600 /app/ssl/key.pem && \
|
||||
chown -R appuser:appgroup /app/ssl
|
||||
|
||||
# Generate SSH keys for worker user
|
||||
RUN ssh-keygen -t rsa -b 4096 -f /home/worker/.ssh/id_rsa -N "" && \
|
||||
chown -R worker:workergroup /home/worker && \
|
||||
\
|
||||
# Generate SSH keys for worker user
|
||||
ssh-keygen -t rsa -b 4096 -f /home/worker/.ssh/id_rsa -N "" && \
|
||||
cp /home/worker/.ssh/id_rsa.pub /home/worker/.ssh/authorized_keys && \
|
||||
chmod 700 /home/worker/.ssh && \
|
||||
chmod 600 /home/worker/.ssh/id_rsa && \
|
||||
chmod 644 /home/worker/.ssh/id_rsa.pub /home/worker/.ssh/authorized_keys && \
|
||||
chown -R worker:workergroup /home/worker/.ssh
|
||||
|
||||
# Configure SSH daemon securely
|
||||
RUN echo "Port 2222" >> /etc/ssh/sshd_config && \
|
||||
chown -R worker:workergroup /home/worker/.ssh && \
|
||||
\
|
||||
# Configure SSH daemon securely
|
||||
echo "Port 2222" >> /etc/ssh/sshd_config && \
|
||||
echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
|
||||
echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config && \
|
||||
echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config && \
|
||||
|
|
@ -76,19 +71,19 @@ RUN echo "Port 2222" >> /etc/ssh/sshd_config && \
|
|||
echo "ClientAliveCountMax 2" >> /etc/ssh/sshd_config && \
|
||||
echo "X11Forwarding no" >> /etc/ssh/sshd_config && \
|
||||
echo "AllowTcpForwarding no" >> /etc/ssh/sshd_config && \
|
||||
echo "Banner /etc/ssh/banner" >> /etc/ssh/sshd_config
|
||||
|
||||
# Create SSH banner
|
||||
RUN echo "=================================================" > /etc/ssh/banner && \
|
||||
echo "Banner /etc/ssh/banner" >> /etc/ssh/sshd_config && \
|
||||
\
|
||||
# Create SSH banner
|
||||
echo "=================================================" > /etc/ssh/banner && \
|
||||
echo " ML Experiments Production Server" >> /etc/ssh/banner && \
|
||||
echo " Unauthorized access is prohibited" >> /etc/ssh/banner && \
|
||||
echo "=================================================" >> /etc/ssh/banner
|
||||
|
||||
# Generate SSH host keys
|
||||
RUN ssh-keygen -A
|
||||
|
||||
# Give appuser sudo permissions for SSH and worker user for Podman
|
||||
RUN echo "appuser ALL=(ALL) NOPASSWD: /usr/sbin/sshd" >> /etc/sudoers && \
|
||||
echo "=================================================" >> /etc/ssh/banner && \
|
||||
\
|
||||
# Generate SSH host keys
|
||||
ssh-keygen -A && \
|
||||
\
|
||||
# Give appuser sudo permissions for SSH and worker user for Podman
|
||||
echo "appuser ALL=(ALL) NOPASSWD: /usr/sbin/sshd" >> /etc/sudoers && \
|
||||
echo "worker ALL=(ALL) NOPASSWD: /usr/bin/podman" >> /etc/sudoers
|
||||
|
||||
# Switch to app user for application
|
||||
|
|
|
|||
|
|
@ -1,75 +1,88 @@
|
|||
# Simple Dockerfile for homelab use
|
||||
FROM golang:1.25-alpine AS builder
|
||||
# Cache-Optimized Dockerfile for homelab use
|
||||
# Build with: docker build --build-context native=./native -f build/docker/simple.Dockerfile .
|
||||
|
||||
# Install dependencies including C++ build tools
|
||||
RUN apk add --no-cache git make gcc g++ musl-dev cmake
|
||||
# ============================================================================
|
||||
# STAGE 1: Native C++ Builder (separate cache layer)
|
||||
# ============================================================================
|
||||
FROM alpine:3.19 AS native-builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache gcc g++ cmake make musl-dev linux-headers
|
||||
|
||||
# Copy go mod files
|
||||
COPY go.mod go.sum ./
|
||||
WORKDIR /build
|
||||
COPY native/ ./
|
||||
|
||||
# Download dependencies
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Copy and build native C++ libraries (without NVML for non-GPU systems)
|
||||
COPY native/ ./native/
|
||||
ENV FETCHML_DOCKER_BUILD=1
|
||||
RUN rm -rf native/build && cd native && mkdir -p build && cd build && \
|
||||
RUN mkdir -p build && cd build && \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_NVML_GPU=OFF && \
|
||||
make -j$(nproc)
|
||||
|
||||
# Build Go binaries (native libs not used in Docker since NVML unavailable in Alpine)
|
||||
RUN CGO_ENABLED=1 go build -o bin/api-server ./cmd/api-server/main.go && \
|
||||
CGO_ENABLED=1 go build -o bin/worker ./cmd/worker
|
||||
# ============================================================================
|
||||
# STAGE 2: Go Dependencies (cached layer - only changes when go.mod/sum changes)
|
||||
# ============================================================================
|
||||
FROM golang:1.25-alpine AS go-deps
|
||||
|
||||
# Final stage
|
||||
RUN apk add --no-cache git make gcc g++ musl-dev cmake
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only module files first for maximum cache efficiency
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download && \
|
||||
go mod verify
|
||||
|
||||
# ============================================================================
|
||||
# STAGE 3: Go Builder (source changes don't invalidate deps)
|
||||
# ============================================================================
|
||||
FROM go-deps AS go-builder
|
||||
|
||||
# Copy source code (changes here won't rebuild deps layer)
|
||||
COPY cmd/ ./cmd/
|
||||
COPY internal/ ./internal/
|
||||
COPY pkg/ ./pkg/
|
||||
COPY tools/ ./tools/
|
||||
|
||||
# Build Go binaries with cache mount for build cache
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=1 go build -ldflags="-w -s" -o bin/api-server ./cmd/api-server/main.go && \
|
||||
CGO_ENABLED=1 go build -ldflags="-w -s" -o bin/worker ./cmd/worker
|
||||
|
||||
# ============================================================================
|
||||
# STAGE 4: Final Runtime (minimal layers)
|
||||
# ============================================================================
|
||||
FROM alpine:3.19
|
||||
|
||||
# Install runtime dependencies including C++ stdlib
|
||||
RUN apk add --no-cache bash ca-certificates redis openssl curl podman fuse-overlayfs slirp4netns iptables libstdc++
|
||||
# Install runtime deps in single layer with cache mount
|
||||
RUN --mount=type=cache,target=/var/cache/apk \
|
||||
apk add --no-cache bash ca-certificates redis openssl curl podman fuse-overlayfs slirp4netns iptables libstdc++
|
||||
|
||||
# Create app user
|
||||
RUN addgroup -g 1001 -S appgroup && \
|
||||
adduser -u 1001 -S appuser -G appgroup
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy binaries from builder
|
||||
COPY --from=builder /app/bin/ /usr/local/bin/
|
||||
# Copy binaries (separate layer for quick updates)
|
||||
COPY --from=go-builder /app/bin/api-server /usr/local/bin/
|
||||
COPY --from=go-builder /app/bin/worker /usr/local/bin/
|
||||
|
||||
# Note: Native libraries not included (NVML unavailable in Alpine Linux)
|
||||
# COPY --from=builder /app/native/build/lib*.so /usr/local/lib/
|
||||
# ENV LD_LIBRARY_PATH=/usr/local/lib:/usr/lib:$LD_LIBRARY_PATH
|
||||
# Copy configs (changes often - keep near end)
|
||||
COPY configs/ /app/configs/
|
||||
|
||||
# Copy configs and templates
|
||||
COPY --from=builder /app/configs/ /app/configs/
|
||||
# Setup directories and SSL in single layer
|
||||
RUN mkdir -p /app/data/experiments /app/data/datasets /app/data/snapshots /app/logs /app/ssl && \
|
||||
openssl req -x509 -newkey rsa:2048 -keyout /app/ssl/key.pem -out /app/ssl/cert.pem -days 365 -nodes \
|
||||
-subj "/C=US/ST=Homelab/L=Local/O=ML/OU=Experiments/CN=localhost" && \
|
||||
chmod 644 /app/ssl/cert.pem && \
|
||||
chmod 600 /app/ssl/key.pem && \
|
||||
chown -R appuser:appgroup /app/data /app/logs /app/ssl /app/configs
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /app/data/experiments /app/data/datasets /app/data/snapshots /app/logs /app/ssl
|
||||
|
||||
# Generate SSL certificates for container use
|
||||
RUN openssl req -x509 -newkey rsa:2048 -keyout /app/ssl/key.pem -out /app/ssl/cert.pem -days 365 -nodes \
|
||||
-subj "/C=US/ST=Homelab/L=Local/O=ML/OU=Experiments/CN=localhost" && \
|
||||
chmod 644 /app/ssl/cert.pem && chmod 600 /app/ssl/key.pem
|
||||
|
||||
# Ensure app user can write to data/logs and read TLS material
|
||||
RUN chown -R appuser:appgroup /app/data /app/logs /app/ssl /app/configs
|
||||
|
||||
# Switch to app user
|
||||
USER appuser
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 9101
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||
CMD curl -f http://localhost:9101/health || curl -k -f https://localhost:9101/health || exit 1
|
||||
|
||||
# Default command
|
||||
CMD ["/usr/local/bin/api-server", "-config", "/app/configs/api/dev.yaml"]
|
||||
|
|
|
|||
|
|
@ -104,7 +104,13 @@ func runSecurityAudit(configFile string) {
|
|||
// Check 4: API key file permissions
|
||||
apiKeyFile := os.Getenv("FETCH_ML_API_KEY_FILE")
|
||||
if apiKeyFile != "" {
|
||||
if info, err := os.Stat(apiKeyFile); err == nil {
|
||||
//nolint:gosec // G703: apiKeyFile from environment variable, not user input
|
||||
info, err := os.Stat(apiKeyFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
issues = append(issues, fmt.Sprintf("Cannot stat API key file: %v", err))
|
||||
}
|
||||
} else {
|
||||
mode := info.Mode().Perm()
|
||||
if mode&0077 != 0 {
|
||||
issues = append(issues, fmt.Sprintf("API key file %s is world/group readable (permissions: %04o)", apiKeyFile, mode))
|
||||
|
|
|
|||
|
|
@ -848,6 +848,7 @@ func main() {
|
|||
logger.Info("data manager shut down gracefully")
|
||||
|
||||
default:
|
||||
log.Printf("Unknown command: %s", cmd)
|
||||
// Use structured logging to prevent log injection
|
||||
slog.Warn("Unknown command", "command", cmd)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jfraeys/fetch_ml/internal/errtypes"
|
||||
|
|
@ -22,6 +23,9 @@ func main() {
|
|||
taskID := os.Args[1]
|
||||
jsonOutput := len(os.Args) > 2 && os.Args[2] == "--json"
|
||||
|
||||
// Sanitize taskID to prevent path traversal
|
||||
taskID = sanitizeTaskID(taskID)
|
||||
|
||||
// Determine base path from environment or default
|
||||
basePath := os.Getenv("FETCH_ML_BASE_PATH")
|
||||
if basePath == "" {
|
||||
|
|
@ -35,11 +39,15 @@ func main() {
|
|||
|
||||
// Try to read error file
|
||||
errorPath := filepath.Join(basePath, "errors", taskID+".json")
|
||||
// #nosec G304 -- taskID is sanitized by sanitizeTaskID to prevent path traversal
|
||||
data, err := os.ReadFile(errorPath)
|
||||
if err != nil {
|
||||
// Error file may not exist - check if task exists in other states
|
||||
fmt.Fprintf(os.Stderr, "Error: no error record found for task %s\n", taskID)
|
||||
fmt.Fprintf(os.Stderr, "Expected: %s\n", errorPath)
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "Error: no error record found for task %s\n", taskID)
|
||||
fmt.Fprintf(os.Stderr, "Expected: %s\n", errorPath)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to read error file: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
|
@ -80,3 +88,17 @@ func main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanitizeTaskID removes path separators and traversal sequences from task IDs.
|
||||
// This prevents path traversal attacks when constructing file paths.
|
||||
func sanitizeTaskID(taskID string) string {
|
||||
// Remove any path separators
|
||||
taskID = strings.ReplaceAll(taskID, "/", "_")
|
||||
taskID = strings.ReplaceAll(taskID, string(filepath.Separator), "_")
|
||||
|
||||
// Remove parent directory references
|
||||
taskID = strings.ReplaceAll(taskID, "..", "_")
|
||||
|
||||
// Clean the result
|
||||
return filepath.Clean(taskID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func LoadCLIConfig(configPath string) (*CLIConfig, string, error) {
|
|||
log.Printf("Warning: %v", err)
|
||||
}
|
||||
|
||||
//nolint:gosec // G304: Config path is user-controlled but trusted
|
||||
// #nosec G304 -- Config path is user-controlled but validated before use
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, configPath, fmt.Errorf("failed to read CLI config: %w", err)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ type Config struct {
|
|||
|
||||
// LoadConfig loads configuration from a TOML file
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
//nolint:gosec // G304: Config path is user-controlled but trusted
|
||||
// #nosec G304 -- Config path is user-controlled but validated before use
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ func NewTaskQueue(cfg *config.Config) (*TaskQueue, error) {
|
|||
// Initialize experiment manager with proper path
|
||||
// BasePath already includes the mode-based experiments path (e.g., ./data/dev/experiments)
|
||||
expDir := cfg.BasePath
|
||||
os.MkdirAll(expDir, 0755)
|
||||
if err := os.MkdirAll(expDir, 0750); err != nil {
|
||||
return nil, fmt.Errorf("failed to create experiments directory: %w", err)
|
||||
}
|
||||
expManager := experiment.NewManager(expDir)
|
||||
|
||||
return &TaskQueue{
|
||||
|
|
|
|||
|
|
@ -116,7 +116,9 @@ func (c *WebSocketClient) Connect() error {
|
|||
func (c *WebSocketClient) Disconnect() {
|
||||
c.cancel()
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
if err := c.conn.Close(); err != nil {
|
||||
c.logger.Warn("websocket close error", "error", err)
|
||||
}
|
||||
}
|
||||
c.connected = false
|
||||
}
|
||||
|
|
@ -141,7 +143,10 @@ func (c *WebSocketClient) messageHandler() {
|
|||
}
|
||||
|
||||
// Set read deadline
|
||||
c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
|
||||
if err := c.conn.SetReadDeadline(time.Now().Add(60 * time.Second)); err != nil {
|
||||
c.logger.Error("websocket set read deadline failed", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Read message
|
||||
messageType, data, err := c.conn.ReadMessage()
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ type Param struct {
|
|||
func Open(dbPath string) (*Store, error) {
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(dbPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
if err := os.MkdirAll(dir, 0750); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,11 @@ func main() {
|
|||
// Redirect logs to file to prevent TUI disruption
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
logDir := filepath.Join(homeDir, ".ml", "logs")
|
||||
os.MkdirAll(logDir, 0755)
|
||||
logFile, logErr := os.OpenFile(filepath.Join(logDir, "tui.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err := os.MkdirAll(logDir, 0750); err != nil {
|
||||
log.Printf("Failed to create log directory: %v", err)
|
||||
}
|
||||
// #nosec G304 -- log path is internally constructed, not from user input
|
||||
logFile, logErr := os.OpenFile(filepath.Join(logDir, "tui.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
if logErr == nil {
|
||||
log.SetOutput(logFile)
|
||||
defer logFile.Close()
|
||||
|
|
|
|||
40
go.mod
40
go.mod
|
|
@ -8,12 +8,16 @@ go 1.25.0
|
|||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.10
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.50.1
|
||||
github.com/charmbracelet/bubbles v0.21.0
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/getkin/kin-openapi v0.125.0
|
||||
github.com/getkin/kin-openapi v0.131.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/vault/api v1.22.0
|
||||
github.com/invopop/yaml v0.2.0
|
||||
github.com/labstack/echo/v4 v4.15.0
|
||||
github.com/leanovate/gopter v0.2.11
|
||||
|
|
@ -23,6 +27,7 @@ require (
|
|||
github.com/oapi-codegen/runtime v1.1.2
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/redis/go-redis/v9 v9.17.2
|
||||
github.com/testcontainers/testcontainers-go v0.40.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zalando/go-keyring v0.2.6
|
||||
golang.org/x/crypto v0.48.0
|
||||
|
|
@ -45,8 +50,6 @@ require (
|
|||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect
|
||||
|
|
@ -54,7 +57,6 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.50.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect
|
||||
|
|
@ -88,12 +90,12 @@ require (
|
|||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
|
|
@ -105,7 +107,6 @@ require (
|
|||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
|
||||
github.com/hashicorp/vault/api v1.22.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||
|
|
@ -137,6 +138,8 @@ require (
|
|||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
|
|
@ -154,7 +157,6 @@ require (
|
|||
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/testcontainers/testcontainers-go v0.40.0 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
|
|
@ -165,19 +167,25 @@ require (
|
|||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.41.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/grpc v1.79.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
modernc.org/libc v1.61.13 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.8.2 // indirect
|
||||
|
|
|
|||
84
go.sum
84
go.sum
|
|
@ -41,6 +41,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
|||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
|
|
@ -150,6 +152,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
|
||||
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -179,33 +183,34 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getkin/kin-openapi v0.125.0 h1:jyQCyf2qXS1qvs2U00xQzkGCqYPhEhZDmSmVt65fXno=
|
||||
github.com/getkin/kin-openapi v0.125.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
|
||||
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
|
||||
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
||||
github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
|
|
@ -286,7 +291,10 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
|||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
|
|
@ -295,6 +303,8 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
|
|||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
|
|
@ -412,6 +422,8 @@ github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ
|
|||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
|
|
@ -441,6 +453,10 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
|
|||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
|
||||
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
|
|
@ -477,8 +493,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
|
|
@ -565,16 +581,24 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
|
||||
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
|
||||
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
|
||||
go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=
|
||||
go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=
|
||||
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
|
||||
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
|
|
@ -674,8 +698,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
@ -914,6 +938,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
|
@ -934,6 +962,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
|||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
@ -946,8 +976,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
|
@ -963,6 +993,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
|||
|
|
@ -25,38 +25,38 @@ echo "Step 1: Running benchmarks..."
|
|||
cd "$PROJECT_ROOT"
|
||||
BENCHMARK_RESULTS_FILE="$RUN_DIR/benchmark_results.txt"
|
||||
GO_TEST_EXIT_CODE=0
|
||||
go test -bench=. -benchmem ./tests/benchmarks/... > "$BENCHMARK_RESULTS_FILE" 2>&1 || GO_TEST_EXIT_CODE=$?
|
||||
go test -bench=. -benchmem ./tests/benchmarks/... >"$BENCHMARK_RESULTS_FILE" 2>&1 || GO_TEST_EXIT_CODE=$?
|
||||
if [ "$GO_TEST_EXIT_CODE" -ne 0 ]; then
|
||||
echo "Benchmark run exited non-zero (exit code: $GO_TEST_EXIT_CODE)." >&2
|
||||
echo "Continuing to generate metrics from available output: $BENCHMARK_RESULTS_FILE" >&2
|
||||
echo "--- tail (last 50 lines) ---" >&2
|
||||
tail -n 50 "$BENCHMARK_RESULTS_FILE" >&2 || true
|
||||
echo "Benchmark run exited non-zero (exit code: $GO_TEST_EXIT_CODE)." >&2
|
||||
echo "Continuing to generate metrics from available output: $BENCHMARK_RESULTS_FILE" >&2
|
||||
echo "--- tail (last 50 lines) ---" >&2
|
||||
tail -n 50 "$BENCHMARK_RESULTS_FILE" >&2 || true
|
||||
fi
|
||||
|
||||
# Step 1b: Run native library benchmarks if available
|
||||
NATIVE_RESULTS_FILE="$RUN_DIR/native_benchmark_results.txt"
|
||||
NATIVE_EXIT_CODE=0
|
||||
if [[ -f "native/build/libqueue_index.dylib" || -f "native/build/libqueue_index.so" ]]; then
|
||||
echo ""
|
||||
echo "Step 1b: Running native library benchmarks..."
|
||||
CGO_ENABLED=1 go test -tags native_libs -bench=. -benchmem ./tests/benchmarks/... > "$NATIVE_RESULTS_FILE" 2>&1 || NATIVE_EXIT_CODE=$?
|
||||
if [ "$NATIVE_EXIT_CODE" -ne 0 ]; then
|
||||
echo "Native benchmark run exited non-zero (exit code: $NATIVE_EXIT_CODE)." >&2
|
||||
echo "--- tail (last 50 lines) ---" >&2
|
||||
tail -n 50 "$NATIVE_RESULTS_FILE" >&2 || true
|
||||
fi
|
||||
echo ""
|
||||
echo "Step 1b: Running native library benchmarks..."
|
||||
CGO_ENABLED=1 go test -tags native_libs -bench=. -benchmem ./tests/benchmarks/... >"$NATIVE_RESULTS_FILE" 2>&1 || NATIVE_EXIT_CODE=$?
|
||||
if [ "$NATIVE_EXIT_CODE" -ne 0 ]; then
|
||||
echo "Native benchmark run exited non-zero (exit code: $NATIVE_EXIT_CODE)." >&2
|
||||
echo "--- tail (last 50 lines) ---" >&2
|
||||
tail -n 50 "$NATIVE_RESULTS_FILE" >&2 || true
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "Step 1b: Native libraries not found, skipping native benchmarks"
|
||||
echo " (Build with: make native-build)"
|
||||
echo ""
|
||||
echo "Step 1b: Native libraries not found, skipping native benchmarks"
|
||||
echo " (Build with: make native-build)"
|
||||
fi
|
||||
|
||||
# Extract benchmark results
|
||||
grep "Benchmark.*-[0-9].*" "$BENCHMARK_RESULTS_FILE" > "$RUN_DIR/clean_benchmarks.txt" || true
|
||||
grep "Benchmark.*-[0-9].*" "$BENCHMARK_RESULTS_FILE" >"$RUN_DIR/clean_benchmarks.txt" || true
|
||||
|
||||
# Step 2: Convert to Prometheus metrics
|
||||
echo "Step 2: Converting to Prometheus metrics..."
|
||||
cat > "$RUN_DIR/prometheus_metrics.txt" << EOF
|
||||
cat >"$RUN_DIR/prometheus_metrics.txt" <<EOF
|
||||
# HELP benchmark_time_per_op Time per operation in nanoseconds
|
||||
# TYPE benchmark_time_per_op gauge
|
||||
# HELP benchmark_memory_per_op Memory per operation in bytes
|
||||
|
|
@ -67,45 +67,45 @@ EOF
|
|||
|
||||
# Parse benchmark results and convert to Prometheus format
|
||||
while IFS= read -r line; do
|
||||
if [[ -n "$line" ]]; then
|
||||
BENCHMARK_NAME=$(echo "$line" | awk '{print $1}' | sed 's/-[0-9]*$//')
|
||||
ITERATIONS=$(echo "$line" | awk '{print $2}')
|
||||
if [[ -n "$line" ]]; then
|
||||
BENCHMARK_NAME=$(echo "$line" | awk '{print $1}' | sed 's/-[0-9]*$//')
|
||||
ITERATIONS=$(echo "$line" | awk '{print $2}')
|
||||
|
||||
# Go benchmark output can include optional columns (e.g. MB/s) and units are
|
||||
# usually separate tokens: "123 ns/op 456 B/op 7 allocs/op".
|
||||
TIME_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="ns/op") {print $(i-1); exit}}')
|
||||
MEMORY_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="B/op") {print $(i-1); exit}}')
|
||||
ALLOCS_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="allocs/op") {print $(i-1); exit}}')
|
||||
|
||||
# Clean benchmark name for Prometheus
|
||||
CLEAN_NAME=$(echo "$BENCHMARK_NAME" | sed 's/[^a-zA-Z0-9_]/_/g')
|
||||
|
||||
# Only add metrics if we have valid numeric values
|
||||
if [[ "$TIME_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_time_per_op{benchmark=\"$CLEAN_NAME\"} ${TIME_VALUE}" >> "$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
if [[ "$MEMORY_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_memory_per_op{benchmark=\"$CLEAN_NAME\"} ${MEMORY_VALUE}" >> "$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
if [[ "$ALLOCS_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_allocs_per_op{benchmark=\"$CLEAN_NAME\"} ${ALLOCS_VALUE}" >> "$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
fi
|
||||
done < "$RUN_DIR/clean_benchmarks.txt"
|
||||
# Go benchmark output can include optional columns (e.g. MB/s) and units are
|
||||
# usually separate tokens: "123 ns/op 456 B/op 7 allocs/op".
|
||||
TIME_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="ns/op") {print $(i-1); exit}}')
|
||||
MEMORY_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="B/op") {print $(i-1); exit}}')
|
||||
ALLOCS_VALUE=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="allocs/op") {print $(i-1); exit}}')
|
||||
|
||||
# Clean benchmark name for Prometheus
|
||||
CLEAN_NAME=$(echo "$BENCHMARK_NAME" | sed 's/[^a-zA-Z0-9_]/_/g')
|
||||
|
||||
# Only add metrics if we have valid numeric values
|
||||
if [[ "$TIME_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_time_per_op{benchmark=\"$CLEAN_NAME\"} ${TIME_VALUE}" >>"$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
if [[ "$MEMORY_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_memory_per_op{benchmark=\"$CLEAN_NAME\"} ${MEMORY_VALUE}" >>"$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
if [[ "$ALLOCS_VALUE" =~ ^[0-9.]+$ ]]; then
|
||||
echo "benchmark_allocs_per_op{benchmark=\"$CLEAN_NAME\"} ${ALLOCS_VALUE}" >>"$RUN_DIR/prometheus_metrics.txt"
|
||||
fi
|
||||
fi
|
||||
done <"$RUN_DIR/clean_benchmarks.txt"
|
||||
|
||||
# Step 3: Push to local Pushgateway (if running)
|
||||
echo "Step 3: Pushing to Prometheus..."
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if curl -s http://localhost:9091 >/dev/null 2>&1; then
|
||||
echo "Pushgateway detected, pushing metrics..."
|
||||
curl --data-binary @"$RUN_DIR/prometheus_metrics.txt" \
|
||||
"http://localhost:9091/metrics/job/benchmark/instance/local_$TIMESTAMP"
|
||||
else
|
||||
echo "Pushgateway not running at http://localhost:9091"
|
||||
echo "Start it with: make monitoring-performance"
|
||||
fi
|
||||
if curl -s http://localhost:9091 >/dev/null 2>&1; then
|
||||
echo "Pushgateway detected, pushing metrics..."
|
||||
curl --data-binary @"$RUN_DIR/prometheus_metrics.txt" \
|
||||
"http://localhost:9091/metrics/job/benchmark/instance/local_$TIMESTAMP"
|
||||
else
|
||||
echo "Pushgateway not running at http://localhost:9091"
|
||||
echo "Start it with: make monitoring-performance"
|
||||
fi
|
||||
else
|
||||
echo "curl not available, skipping push to Pushgateway"
|
||||
echo "curl not available, skipping push to Pushgateway"
|
||||
fi
|
||||
|
||||
# Step 4: Display results
|
||||
|
|
@ -114,7 +114,7 @@ echo "=== Results Summary ==="
|
|||
echo "Benchmark results saved to: $RUN_DIR/benchmark_results.txt"
|
||||
echo "Prometheus metrics saved to: $RUN_DIR/prometheus_metrics.txt"
|
||||
if [ "${GO_TEST_EXIT_CODE:-0}" -ne 0 ]; then
|
||||
echo "WARNING: go test exited with code: $GO_TEST_EXIT_CODE"
|
||||
echo "WARNING: go test exited with code: $GO_TEST_EXIT_CODE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
|
@ -124,15 +124,15 @@ cat "$RUN_DIR/prometheus_metrics.txt" | grep "benchmark_time_per_op" | head -10
|
|||
|
||||
# Show native comparison if available
|
||||
if [[ -f "$NATIVE_RESULTS_FILE" && "$NATIVE_EXIT_CODE" -eq 0 ]]; then
|
||||
echo ""
|
||||
echo "Native library benchmarks available at: $NATIVE_RESULTS_FILE"
|
||||
echo "To compare Go vs Native:"
|
||||
echo " make benchmark-compare"
|
||||
echo ""
|
||||
echo "Native library benchmarks available at: $NATIVE_RESULTS_FILE"
|
||||
echo "To compare Go vs Native:"
|
||||
echo " make benchmark-compare"
|
||||
fi
|
||||
|
||||
# Step 5: Generate HTML report
|
||||
echo "Step 5: Generating HTML report..."
|
||||
cat > "$RUN_DIR/report.html" << EOF
|
||||
cat >"$RUN_DIR/report.html" <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
|
@ -149,7 +149,7 @@ cat > "$RUN_DIR/report.html" << EOF
|
|||
<h1>Benchmark Report</h1>
|
||||
<p><strong>Run ID:</strong> $TIMESTAMP</p>
|
||||
<p><strong>Date:</strong> $(date)</p>
|
||||
|
||||
|
||||
<h2>Results</h2>
|
||||
<table>
|
||||
<tr>
|
||||
|
|
@ -159,22 +159,22 @@ cat > "$RUN_DIR/report.html" << EOF
|
|||
<th>Allocs (allocs/op)</th>
|
||||
</tr>
|
||||
$(cat "$RUN_DIR/clean_benchmarks.txt" | while IFS= read -r line; do
|
||||
if [[ -n "$line" ]]; then
|
||||
BENCHMARK_NAME=$(echo "$line" | awk '{print $1}')
|
||||
if [[ -n "$line" ]]; then
|
||||
BENCHMARK_NAME=$(echo "$line" | awk '{print $1}')
|
||||
|
||||
TIME_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="ns/op") {print $(i-1)" " $i; exit}}')
|
||||
MEMORY_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="B/op") {print $(i-1)" " $i; exit}}')
|
||||
ALLOCS_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="allocs/op") {print $(i-1)" " $i; exit}}')
|
||||
echo " <tr>"
|
||||
echo " <td class=\"metric\">$BENCHMARK_NAME</td>"
|
||||
echo " <td>$TIME_PER_OP</td>"
|
||||
echo " <td>$MEMORY_PER_OP</td>"
|
||||
echo " <td>$ALLOCS_PER_OP</td>"
|
||||
echo " </tr>"
|
||||
fi
|
||||
TIME_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="ns/op") {print $(i-1)" " $i; exit}}')
|
||||
MEMORY_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="B/op") {print $(i-1)" " $i; exit}}')
|
||||
ALLOCS_PER_OP=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="allocs/op") {print $(i-1)" " $i; exit}}')
|
||||
echo " <tr>"
|
||||
echo " <td class=\"metric\">$BENCHMARK_NAME</td>"
|
||||
echo " <td>$TIME_PER_OP</td>"
|
||||
echo " <td>$MEMORY_PER_OP</td>"
|
||||
echo " <td>$ALLOCS_PER_OP</td>"
|
||||
echo " </tr>"
|
||||
fi
|
||||
done)
|
||||
</table>
|
||||
|
||||
|
||||
<h2>Raw Output</h2>
|
||||
<pre>$(cat "$RUN_DIR/benchmark_results.txt")</pre>
|
||||
</body>
|
||||
|
|
@ -201,33 +201,33 @@ echo "=== Cleanup Procedures ==="
|
|||
|
||||
# Use the dedicated cleanup script
|
||||
if [ -f "$SCRIPT_DIR/cleanup-benchmarks.sh" ]; then
|
||||
echo "Running standard benchmark cleanup..."
|
||||
"$SCRIPT_DIR/cleanup-benchmarks.sh" benchmarks
|
||||
echo "Running standard benchmark cleanup..."
|
||||
"$SCRIPT_DIR/cleanup-benchmarks.sh" benchmarks
|
||||
else
|
||||
# Fallback cleanup if script not available
|
||||
echo "Archiving old benchmark runs (keeping last 10)..."
|
||||
stamp=$(date -u +%Y%m%d-%H%M%S)
|
||||
mkdir -p "$ARCHIVE_DIR/$stamp"
|
||||
cd "$LOCAL_ARTIFACTS_DIR"
|
||||
ls -1t run_* 2>/dev/null | tail -n +11 | while read -r run; do
|
||||
[ -n "$run" ] || continue
|
||||
mv "$run" "$ARCHIVE_DIR/$stamp/" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Clean temporary files
|
||||
echo "Archiving temporary files..."
|
||||
tmp_archive_dir="$LOCAL_ARTIFACTS_DIR/tmp-archive/$stamp"
|
||||
mkdir -p "$tmp_archive_dir"
|
||||
find /tmp -name "benchmark_*" -type f -mmin +60 -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
mv "$f" "$tmp_archive_dir/" 2>/dev/null || true
|
||||
done
|
||||
find /var/tmp -name "benchmark_*" -type f -mmin +60 -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
mv "$f" "$tmp_archive_dir/" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Clean Go build cache
|
||||
echo "Cleaning Go build cache..."
|
||||
go clean -testcache 2>/dev/null || true
|
||||
# Fallback cleanup if script not available
|
||||
echo "Archiving old benchmark runs (keeping last 10)..."
|
||||
stamp=$(date -u +%Y%m%d-%H%M%S)
|
||||
mkdir -p "$ARCHIVE_DIR/$stamp"
|
||||
cd "$LOCAL_ARTIFACTS_DIR"
|
||||
ls -1t run_* 2>/dev/null | tail -n +11 | while read -r run; do
|
||||
[ -n "$run" ] || continue
|
||||
mv "$run" "$ARCHIVE_DIR/$stamp/" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Clean temporary files
|
||||
echo "Archiving temporary files..."
|
||||
tmp_archive_dir="$LOCAL_ARTIFACTS_DIR/tmp-archive/$stamp"
|
||||
mkdir -p "$tmp_archive_dir"
|
||||
find /tmp -name "benchmark_*" -type f -mmin +60 -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
mv "$f" "$tmp_archive_dir/" 2>/dev/null || true
|
||||
done
|
||||
find /var/tmp -name "benchmark_*" -type f -mmin +60 -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
mv "$f" "$tmp_archive_dir/" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Clean Go build cache
|
||||
echo "Cleaning Go build cache..."
|
||||
go clean -testcache 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Show final status
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,26 +12,26 @@ SOURCE_PATH=${4:-cmd/api-server/main.go}
|
|||
LDFLAGS="-s -w -X main.BuildHash=$(git rev-parse --short HEAD) -X main.BuildTime=$(date -u +%Y%m%d.%H%M%S)"
|
||||
|
||||
case $BUILD_TYPE in
|
||||
pure)
|
||||
export CGO_ENABLED=0
|
||||
TAGS=""
|
||||
SUFFIX="_${OS}_${ARCH}_pure"
|
||||
;;
|
||||
cgo)
|
||||
export CGO_ENABLED=1
|
||||
TAGS=""
|
||||
SUFFIX="_${OS}_${ARCH}_cgo"
|
||||
;;
|
||||
native)
|
||||
export CGO_ENABLED=1
|
||||
TAGS="native_libs"
|
||||
SUFFIX="_${OS}_${ARCH}_native"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown build type: $BUILD_TYPE"
|
||||
echo "Usage: $0 <pure|cgo|native> <os> <arch> <source_path>"
|
||||
exit 1
|
||||
;;
|
||||
pure)
|
||||
export CGO_ENABLED=0
|
||||
TAGS=""
|
||||
SUFFIX="_${OS}_${ARCH}_pure"
|
||||
;;
|
||||
cgo)
|
||||
export CGO_ENABLED=1
|
||||
TAGS=""
|
||||
SUFFIX="_${OS}_${ARCH}_cgo"
|
||||
;;
|
||||
native)
|
||||
export CGO_ENABLED=1
|
||||
TAGS="native_libs"
|
||||
SUFFIX="_${OS}_${ARCH}_native"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown build type: $BUILD_TYPE"
|
||||
echo "Usage: $0 <pure|cgo|native> <os> <arch> <source_path>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
BINARY_NAME=$(basename "$SOURCE_PATH" .go)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ mkdir -p native/build/linux_amd64
|
|||
|
||||
# Use cmake for native build
|
||||
cmake -S native -B native/build/linux_amd64 \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
cmake --build native/build/linux_amd64 --parallel
|
||||
|
||||
# Package libs
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ scripts/build/build-native.sh
|
|||
|
||||
# Build Go backends for all build types
|
||||
for build_type in pure cgo native; do
|
||||
echo "=== Building ${build_type} binaries ==="
|
||||
for binary in api-server worker data_manager user_manager tui; do
|
||||
source_path="cmd/${binary}"
|
||||
[ "$binary" = "worker" ] && source_path="cmd/worker/worker_server.go"
|
||||
[ "$binary" = "api-server" ] && source_path="cmd/api-server/main.go"
|
||||
[ "$binary" = "data_manager" ] && source_path="cmd/data_manager/main.go"
|
||||
[ "$binary" = "user_manager" ] && source_path="cmd/user_manager/main.go"
|
||||
[ "$binary" = "tui" ] && source_path="cmd/tui/main.go"
|
||||
|
||||
scripts/build/build-go.sh "$build_type" linux amd64 "$source_path"
|
||||
done
|
||||
echo "=== Building ${build_type} binaries ==="
|
||||
for binary in api-server worker data_manager user_manager tui; do
|
||||
source_path="cmd/${binary}"
|
||||
[ "$binary" = "worker" ] && source_path="cmd/worker/worker_server.go"
|
||||
[ "$binary" = "api-server" ] && source_path="cmd/api-server/main.go"
|
||||
[ "$binary" = "data_manager" ] && source_path="cmd/data_manager/main.go"
|
||||
[ "$binary" = "user_manager" ] && source_path="cmd/user_manager/main.go"
|
||||
[ "$binary" = "tui" ] && source_path="cmd/tui/main.go"
|
||||
|
||||
scripts/build/build-go.sh "$build_type" linux amd64 "$source_path"
|
||||
done
|
||||
done
|
||||
|
||||
# Build CLI binaries
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ FAILED=0
|
|||
# Check 1: No internal/ -> cmd/ imports
|
||||
echo "1. Checking for illegal internal/ -> cmd/ imports..."
|
||||
if go list -f '{{.ImportPath}}: {{.Imports}}' ./internal/... 2>/dev/null | grep -q 'github.com/jfraeys/fetch_ml/cmd'; then
|
||||
echo -e "${RED}FAIL: Found internal/ package importing from cmd/${NC}"
|
||||
go list -f '{{.ImportPath}}: {{.Imports}}' ./internal/... | grep 'github.com/jfraeys/fetch_ml/cmd'
|
||||
FAILED=1
|
||||
echo -e "${RED}FAIL: Found internal/ package importing from cmd/${NC}"
|
||||
go list -f '{{.ImportPath}}: {{.Imports}}' ./internal/... | grep 'github.com/jfraeys/fetch_ml/cmd'
|
||||
FAILED=1
|
||||
else
|
||||
echo -e "${GREEN}PASS: No internal/ -> cmd/ imports found${NC}"
|
||||
echo -e "${GREEN}PASS: No internal/ -> cmd/ imports found${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
|
@ -30,11 +30,11 @@ echo ""
|
|||
echo "2. Checking domain package has no internal imports..."
|
||||
DOMAIN_IMPORTS=$(go list -f '{{join .Imports "\n"}}' ./internal/domain/... 2>/dev/null | grep 'github.com/jfraeys/fetch_ml' || true)
|
||||
if [ -n "$DOMAIN_IMPORTS" ]; then
|
||||
echo -e "${RED}FAIL: domain/ package imports internal packages:${NC}"
|
||||
echo "$DOMAIN_IMPORTS"
|
||||
FAILED=1
|
||||
echo -e "${RED}FAIL: domain/ package imports internal packages:${NC}"
|
||||
echo "$DOMAIN_IMPORTS"
|
||||
FAILED=1
|
||||
else
|
||||
echo -e "${GREEN}PASS: domain/ package has no internal imports${NC}"
|
||||
echo -e "${GREEN}PASS: domain/ package has no internal imports${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
|
@ -42,11 +42,11 @@ echo ""
|
|||
echo "3. Checking file size limits (warn if >500 lines)..."
|
||||
OVERSIZED=$(find ./internal ./cmd -name '*.go' -type f -exec wc -l {} + 2>/dev/null | awk '$1 > 500 {print $2}' | head -10)
|
||||
if [ -n "$OVERSIZED" ]; then
|
||||
echo -e "${YELLOW}WARNING: Files exceeding 500 lines:${NC}"
|
||||
find ./internal ./cmd -name '*.go' -type f -exec wc -l {} + 2>/dev/null | awk '$1 > 500 {print " " $1 " lines: " $2}'
|
||||
# Not failing the build for this, just warning
|
||||
echo -e "${YELLOW}WARNING: Files exceeding 500 lines:${NC}"
|
||||
find ./internal ./cmd -name '*.go' -type f -exec wc -l {} + 2>/dev/null | awk '$1 > 500 {print " " $1 " lines: " $2}'
|
||||
# Not failing the build for this, just warning
|
||||
else
|
||||
echo -e "${GREEN}PASS: All files under 500 lines${NC}"
|
||||
echo -e "${GREEN}PASS: All files under 500 lines${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
|
@ -54,56 +54,56 @@ echo ""
|
|||
echo "4. Checking for circular imports..."
|
||||
CIRCULAR=$(go list -deps ./internal/... 2>&1 | grep -i 'circular' || true)
|
||||
if [ -n "$CIRCULAR" ]; then
|
||||
echo -e "${RED}FAIL: Circular imports detected:${NC}"
|
||||
echo "$CIRCULAR"
|
||||
FAILED=1
|
||||
echo -e "${RED}FAIL: Circular imports detected:${NC}"
|
||||
echo "$CIRCULAR"
|
||||
FAILED=1
|
||||
else
|
||||
echo -e "${GREEN}PASS: No circular imports detected${NC}"
|
||||
echo -e "${GREEN}PASS: No circular imports detected${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check 5: Verify package naming conventions
|
||||
echo "5. Checking package naming conventions..."
|
||||
NAMING_ISSUES=$(find ./internal ./cmd -name '*.go' -type f | while read f; do
|
||||
# Skip vendor and generated files
|
||||
if echo "$f" | grep -qE '(vendor|_test\.go|\.pb\.go|generated)'; then
|
||||
continue
|
||||
fi
|
||||
# Check file name matches content (basic check)
|
||||
pkg=$(grep '^package ' "$f" 2>/dev/null | head -1 | awk '{print $2}')
|
||||
if [ -z "$pkg" ]; then
|
||||
continue
|
||||
fi
|
||||
# Skip main packages
|
||||
if [ "$pkg" = "main" ]; then
|
||||
continue
|
||||
fi
|
||||
# Check that file is in correct directory for its package
|
||||
dir=$(dirname "$f")
|
||||
expected_pkg=$(basename "$dir")
|
||||
if [ "$pkg" != "$expected_pkg" ] && [ "$pkg" != "${expected_pkg}_test" ]; then
|
||||
# Allow common exceptions
|
||||
if echo "$f" | grep -qE '(factory|config|helper|util)'; then
|
||||
continue
|
||||
fi
|
||||
echo " $f: package '$pkg' doesn't match directory '$expected_pkg'"
|
||||
fi
|
||||
# Skip vendor and generated files
|
||||
if echo "$f" | grep -qE '(vendor|_test\.go|\.pb\.go|generated)'; then
|
||||
continue
|
||||
fi
|
||||
# Check file name matches content (basic check)
|
||||
pkg=$(grep '^package ' "$f" 2>/dev/null | head -1 | awk '{print $2}')
|
||||
if [ -z "$pkg" ]; then
|
||||
continue
|
||||
fi
|
||||
# Skip main packages
|
||||
if [ "$pkg" = "main" ]; then
|
||||
continue
|
||||
fi
|
||||
# Check that file is in correct directory for its package
|
||||
dir=$(dirname "$f")
|
||||
expected_pkg=$(basename "$dir")
|
||||
if [ "$pkg" != "$expected_pkg" ] && [ "$pkg" != "${expected_pkg}_test" ]; then
|
||||
# Allow common exceptions
|
||||
if echo "$f" | grep -qE '(factory|config|helper|util)'; then
|
||||
continue
|
||||
fi
|
||||
echo " $f: package '$pkg' doesn't match directory '$expected_pkg'"
|
||||
fi
|
||||
done)
|
||||
|
||||
if [ -n "$NAMING_ISSUES" ]; then
|
||||
echo -e "${YELLOW}WARNING: Potential naming convention issues:${NC}"
|
||||
echo "$NAMING_ISSUES"
|
||||
echo -e "${YELLOW}WARNING: Potential naming convention issues:${NC}"
|
||||
echo "$NAMING_ISSUES"
|
||||
else
|
||||
echo -e "${GREEN}PASS: Package naming conventions look good${NC}"
|
||||
echo -e "${GREEN}PASS: Package naming conventions look good${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "=== Summary ==="
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}All critical checks passed!${NC}"
|
||||
exit 0
|
||||
echo -e "${GREEN}All critical checks passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}Some checks failed. Please fix the issues above.${NC}"
|
||||
exit 1
|
||||
echo -e "${RED}Some checks failed. Please fix the issues above.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -7,20 +7,19 @@ set -euo pipefail
|
|||
REPO_ROOT="$(pwd)"
|
||||
CLI_DIR="${REPO_ROOT}/cli"
|
||||
DIST_DIR="${REPO_ROOT}/dist"
|
||||
CONFIG_DIR="${REPO_ROOT}/configs"
|
||||
|
||||
# Cleanup on exit
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
echo ""
|
||||
echo "[cleanup] Removing temporary build artifacts..."
|
||||
rm -rf "${CLI_DIR}/zig-out" "${CLI_DIR}/.zig-cache"
|
||||
if [[ "${exit_code}" -eq 0 ]]; then
|
||||
echo "[cleanup] CI passed. Keeping dist/ for inspection."
|
||||
else
|
||||
echo "[cleanup] CI failed. Cleaning dist/ as well."
|
||||
rm -rf "${DIST_DIR}"
|
||||
fi
|
||||
local exit_code=$?
|
||||
echo ""
|
||||
echo "[cleanup] Removing temporary build artifacts..."
|
||||
rm -rf "${CLI_DIR}/zig-out" "${CLI_DIR}/.zig-cache"
|
||||
if [[ "${exit_code}" -eq 0 ]]; then
|
||||
echo "[cleanup] CI passed. Keeping dist/ for inspection."
|
||||
else
|
||||
echo "[cleanup] CI failed. Cleaning dist/ as well."
|
||||
rm -rf "${DIST_DIR}"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
|
|
@ -37,15 +36,15 @@ ls -lh zig-out/bin/ml
|
|||
|
||||
# Optional: cross-target test if your Zig supports it
|
||||
if command -v zig >/dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "[1b] Testing cross-target (linux-x86_64) if supported..."
|
||||
if zig targets | grep -q x86_64-linux-gnu; then
|
||||
rm -rf zig-out .zig-cache
|
||||
zig build -Doptimize=ReleaseSmall -Dtarget=x86_64-linux-gnu
|
||||
ls -lh zig-out/bin/ml
|
||||
else
|
||||
echo "Cross-target x86_64-linux-gnu not available; skipping."
|
||||
fi
|
||||
echo ""
|
||||
echo "[1b] Testing cross-target (linux-x86_64) if supported..."
|
||||
if zig targets | grep -q x86_64-linux-gnu; then
|
||||
rm -rf zig-out .zig-cache
|
||||
zig build -Doptimize=ReleaseSmall -Dtarget=x86_64-linux-gnu
|
||||
ls -lh zig-out/bin/ml
|
||||
else
|
||||
echo "Cross-target x86_64-linux-gnu not available; skipping."
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Package CLI like CI does
|
||||
|
|
@ -55,7 +54,7 @@ mkdir -p "${DIST_DIR}"
|
|||
cp "${CLI_DIR}/zig-out/bin/ml" "${DIST_DIR}/ml-test"
|
||||
cd "${DIST_DIR}"
|
||||
tar -czf ml-test.tar.gz ml-test
|
||||
sha256sum ml-test.tar.gz > ml-test.tar.gz.sha256
|
||||
sha256sum ml-test.tar.gz >ml-test.tar.gz.sha256
|
||||
ls -lh ml-test.tar.gz*
|
||||
|
||||
# 3. Go backends (if applicable)
|
||||
|
|
@ -63,10 +62,10 @@ echo ""
|
|||
echo "[3] Building Go backends (cross-platform)..."
|
||||
cd "${REPO_ROOT}"
|
||||
if [ -f Makefile ] && grep -q 'cross-platform' Makefile; then
|
||||
make cross-platform
|
||||
ls -lh dist/
|
||||
make cross-platform
|
||||
ls -lh dist/
|
||||
else
|
||||
echo "No 'cross-platform' target found in Makefile; skipping Go backends."
|
||||
echo "No 'cross-platform' target found in Makefile; skipping Go backends."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -11,62 +11,62 @@ echo "=== Path Convention Verification ==="
|
|||
# Check 1: No binaries in root
|
||||
echo "Checking for binaries in root..."
|
||||
for binary in api-server worker tui data_manager; do
|
||||
if [ -f "./$binary" ]; then
|
||||
echo "✗ FAIL: Binary $binary found in root (should be in bin/)"
|
||||
FAILED=1
|
||||
fi
|
||||
if [ -f "./$binary" ]; then
|
||||
echo "✗ FAIL: Binary $binary found in root (should be in bin/)"
|
||||
FAILED=1
|
||||
fi
|
||||
done
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo "No binaries in root"
|
||||
echo "No binaries in root"
|
||||
fi
|
||||
|
||||
# Check 2: No .DS_Store files
|
||||
echo "Checking for .DS_Store files..."
|
||||
DSSTORE_COUNT=$(find . -name ".DS_Store" -type f 2>/dev/null | wc -l)
|
||||
if [ "$DSSTORE_COUNT" -gt 0 ]; then
|
||||
echo "✗ FAIL: $DSSTORE_COUNT .DS_Store file(s) found"
|
||||
find . -name ".DS_Store" -type f | head -5
|
||||
FAILED=1
|
||||
echo "✗ FAIL: $DSSTORE_COUNT .DS_Store file(s) found"
|
||||
find . -name ".DS_Store" -type f | head -5
|
||||
FAILED=1
|
||||
else
|
||||
echo "No .DS_Store files"
|
||||
echo "No .DS_Store files"
|
||||
fi
|
||||
|
||||
# Check 3: No coverage.out in root
|
||||
echo "Checking for coverage.out in root..."
|
||||
if [ -f "./coverage.out" ]; then
|
||||
echo "✗ FAIL: coverage.out found in root (should be in coverage/)"
|
||||
FAILED=1
|
||||
echo "✗ FAIL: coverage.out found in root (should be in coverage/)"
|
||||
FAILED=1
|
||||
else
|
||||
echo "No coverage.out in root"
|
||||
echo "No coverage.out in root"
|
||||
fi
|
||||
|
||||
# Check 4: Bin directory should exist or be empty
|
||||
echo "Checking bin/ directory..."
|
||||
if [ -d "./bin" ]; then
|
||||
BIN_COUNT=$(ls -1 ./bin 2>/dev/null | wc -l)
|
||||
echo "bin/ exists ($BIN_COUNT files)"
|
||||
BIN_COUNT=$(ls -1 ./bin 2>/dev/null | wc -l)
|
||||
echo "bin/ exists ($BIN_COUNT files)"
|
||||
else
|
||||
echo "ℹ bin/ does not exist (will be created by make build)"
|
||||
echo "ℹ bin/ does not exist (will be created by make build)"
|
||||
fi
|
||||
|
||||
# Check 5: Data directories should be gitignored
|
||||
echo "Checking data/ directory..."
|
||||
if [ -d "./data" ]; then
|
||||
if git check-ignore -q ./data 2>/dev/null; then
|
||||
echo "data/ is gitignored"
|
||||
else
|
||||
echo "⚠ WARNING: data/ exists but may not be gitignored"
|
||||
fi
|
||||
if git check-ignore -q ./data 2>/dev/null; then
|
||||
echo "data/ is gitignored"
|
||||
else
|
||||
echo "⚠ WARNING: data/ exists but may not be gitignored"
|
||||
fi
|
||||
else
|
||||
echo "ℹ data/ does not exist"
|
||||
echo "ℹ data/ does not exist"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo "All path conventions verified"
|
||||
exit 0
|
||||
echo "All path conventions verified"
|
||||
exit 0
|
||||
else
|
||||
echo "✗ Path convention verification failed"
|
||||
exit 1
|
||||
echo "✗ Path convention verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
import os
|
||||
|
||||
# Create monitoring directory structure
|
||||
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
monitoring_dir = os.path.join(repo_root, 'monitoring')
|
||||
grafana_dir = os.path.join(monitoring_dir, 'grafana')
|
||||
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
monitoring_dir = os.path.join(repo_root, "monitoring")
|
||||
grafana_dir = os.path.join(monitoring_dir, "grafana")
|
||||
|
||||
datasources_dir = os.path.join(grafana_dir, 'provisioning', 'datasources')
|
||||
providers_dir = os.path.join(grafana_dir, 'provisioning', 'dashboards')
|
||||
datasources_dir = os.path.join(grafana_dir, "provisioning", "datasources")
|
||||
providers_dir = os.path.join(grafana_dir, "provisioning", "dashboards")
|
||||
|
||||
os.makedirs(datasources_dir, exist_ok=True)
|
||||
os.makedirs(providers_dir, exist_ok=True)
|
||||
|
||||
# Essential datasource configurations
|
||||
datasources = {
|
||||
'prometheus.yml': """apiVersion: 1
|
||||
"prometheus.yml": """apiVersion: 1
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
|
|
@ -25,7 +25,7 @@ datasources:
|
|||
jsonData:
|
||||
timeInterval: "5s"
|
||||
""",
|
||||
'loki.yml': """apiVersion: 1
|
||||
"loki.yml": """apiVersion: 1
|
||||
datasources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
|
|
@ -35,7 +35,7 @@ datasources:
|
|||
jsonData:
|
||||
maxLines: 1000
|
||||
""",
|
||||
'dashboards.yml': """apiVersion: 1
|
||||
"dashboards.yml": """apiVersion: 1
|
||||
providers:
|
||||
- name: 'default'
|
||||
orgId: 1
|
||||
|
|
@ -46,17 +46,17 @@ providers:
|
|||
allowUiUpdates: true
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
"""
|
||||
""",
|
||||
}
|
||||
|
||||
# Write configuration files
|
||||
for filename, content in datasources.items():
|
||||
if filename == 'dashboards.yml':
|
||||
if filename == "dashboards.yml":
|
||||
path = os.path.join(providers_dir, filename)
|
||||
else:
|
||||
path = os.path.join(datasources_dir, filename)
|
||||
|
||||
with open(path, 'w') as f:
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print("Monitoring setup completed!")
|
||||
print("Monitoring setup completed!")
|
||||
|
|
|
|||
Loading…
Reference in a new issue