Re-vendor the shared files from sneak/prompts at 0653579 (closes #52)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Takes .golangci.yml, .dockerignore, .gitignore, .editorconfig, the workflow and REPO_POLICIES.md from sneak/prompts 0653579, with this repository's own entries after the shared content. Lint (golangci-lint v2.14.0) and test are Dockerfile phases that script/lint and script/test build alone with a new CACHEBUST value, keeping Go's and golangci-lint's caches; the image build depends on both. make fmt and make fmt-check also format the markdown with prettier, pinned in yarn.lock. The agent guidance moves to AGENTS.md. The Go code is fixed for what the new configuration finds, and the AS and prefix detail reads now return the rows' error after their loops. Model: opus-5-5
This commit was merged in pull request #85.
This commit is contained in:
+27
-12
@@ -2,6 +2,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -27,6 +28,12 @@ const (
|
||||
maxPort = 65535
|
||||
)
|
||||
|
||||
var (
|
||||
errRelativeXDGDataHome = errors.New("XDG_DATA_HOME must be an absolute path")
|
||||
errUnsupportedOS = errors.New("unsupported operating system")
|
||||
errInvalidPort = errors.New("PORT must be a whole number")
|
||||
)
|
||||
|
||||
// Config holds configuration for the entire application
|
||||
type Config struct {
|
||||
// StateDir is the directory for all application state (database, snapshots)
|
||||
@@ -38,11 +45,14 @@ type Config struct {
|
||||
// MaxRuntime is the maximum runtime (0 = run forever)
|
||||
MaxRuntime time.Duration
|
||||
|
||||
// EnableBatchedDatabaseWrites enables batched database operations for better performance
|
||||
// EnableBatchedDatabaseWrites enables batched database operations for
|
||||
// better performance
|
||||
EnableBatchedDatabaseWrites bool
|
||||
|
||||
// RouteExpirationTimeout is how long a route can go without being refreshed before expiring
|
||||
// Default is 2 hours which is conservative for BGP (typical BGP hold time is 90-180 seconds)
|
||||
// RouteExpirationTimeout is how long a route can go without being
|
||||
// refreshed before expiring
|
||||
// Default is 2 hours which is conservative for BGP (typical BGP hold
|
||||
// time is 90-180 seconds)
|
||||
RouteExpirationTimeout time.Duration
|
||||
}
|
||||
|
||||
@@ -59,11 +69,14 @@ func New() (*Config, error) {
|
||||
}
|
||||
|
||||
return &Config{
|
||||
StateDir: stateDir,
|
||||
Port: port,
|
||||
MaxRuntime: 0, // Run forever by default
|
||||
EnableBatchedDatabaseWrites: true, // Enable batching by default
|
||||
RouteExpirationTimeout: defaultRouteExpirationMinutes * time.Minute, // For active route monitoring
|
||||
StateDir: stateDir,
|
||||
Port: port,
|
||||
// Run forever by default
|
||||
MaxRuntime: 0,
|
||||
// Enable batching by default
|
||||
EnableBatchedDatabaseWrites: true,
|
||||
// For active route monitoring
|
||||
RouteExpirationTimeout: defaultRouteExpirationMinutes * time.Minute,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -89,7 +102,7 @@ func getStateDirectory() (string, error) {
|
||||
// the database somewhere unexpected.
|
||||
xdgData := os.Getenv("XDG_DATA_HOME")
|
||||
if xdgData != "" && !filepath.IsAbs(xdgData) {
|
||||
return "", fmt.Errorf("XDG_DATA_HOME must be an absolute path, got %q", xdgData)
|
||||
return "", fmt.Errorf("%w, got %q", errRelativeXDGDataHome, xdgData)
|
||||
}
|
||||
|
||||
// Unix-like: /var/lib/berlin.sneak.app.routewatch if root, else XDG_DATA_HOME
|
||||
@@ -111,7 +124,7 @@ func getStateDirectory() (string, error) {
|
||||
return filepath.Join(home, ".local", "share", AppIdentifier), nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
return "", fmt.Errorf("%w: %s", errUnsupportedOS, runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +138,8 @@ func getPort() (int, error) {
|
||||
// ParseUint, unlike Atoi, refuses a sign: the health check URL cannot use "+9090"
|
||||
port, err := strconv.ParseUint(value, 10, 0)
|
||||
if err != nil || port < 1 || port > maxPort {
|
||||
return 0, fmt.Errorf("PORT must be a whole number from 1 to %d, got %q", maxPort, value)
|
||||
return 0, fmt.Errorf("%w from 1 to %d, got %q",
|
||||
errInvalidPort, maxPort, value)
|
||||
}
|
||||
|
||||
return int(port), nil
|
||||
@@ -134,7 +148,8 @@ func getPort() (int, error) {
|
||||
// EnsureDirectories creates all necessary directories if they don't exist
|
||||
func (c *Config) EnsureDirectories() error {
|
||||
// Ensure state directory exists
|
||||
if err := os.MkdirAll(c.StateDir, dirPermissions); err != nil {
|
||||
err := os.MkdirAll(c.StateDir, dirPermissions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create state directory: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ func TestNewRefusesInvalidPort(t *testing.T) {
|
||||
t.Setenv("PORT", value)
|
||||
t.Setenv("XDG_DATA_HOME", "")
|
||||
|
||||
if _, err := New(); err == nil {
|
||||
_, err := New()
|
||||
if err == nil {
|
||||
t.Errorf("New() with PORT=%q returned no error", value)
|
||||
}
|
||||
})
|
||||
@@ -51,12 +52,16 @@ func TestNewRefusesRelativeXDGDataHome(t *testing.T) {
|
||||
t.Setenv("PORT", "")
|
||||
|
||||
t.Setenv("XDG_DATA_HOME", "relative/path")
|
||||
if _, err := New(); err == nil {
|
||||
|
||||
_, err := New()
|
||||
if err == nil {
|
||||
t.Error("New() with a relative XDG_DATA_HOME returned no error")
|
||||
}
|
||||
|
||||
t.Setenv("XDG_DATA_HOME", "/var/lib")
|
||||
if _, err := New(); err != nil {
|
||||
|
||||
_, err = New()
|
||||
if err != nil {
|
||||
t.Errorf("New() with XDG_DATA_HOME=/var/lib: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user