Building from source
Build, test, and lint go-udap locally
Building from source
Prerequisites
- Go 1.26 or later (see
go.mod) - Task (optional, for build automation)
- prek (optional, for pre-commit hooks)
Build
Using Task (Recommended)
# Build optimized binary for current platform
task build
# Run tests
task test
# Run tests with verbose output
task test:verbose
# Format code
task fmt
# Run linter
task lint
# Clean build artifacts
task cleanUsing Go Directly
# Development build
go build -o go-udap .
# Optimized build (smaller binary)
go build -ldflags="-s -w" -trimpath -o go-udap .
# Run tests (with race detector)
go test -race ./...Testing
Running Tests
# All tests
task test
# Verbose output
task test:verbose
# With coverage report
task test:coverageManual Testing
With a physical Squeezebox device:
- Put the device in setup mode by holding the front button for ~3 seconds until the LED blinks slow red (or factory-reset by holding ~6 seconds until it blinks fast red — see the wiki).
- Run
go-udap discover --infoto confirm the device appears. - Configure with
set(use--reboot/-rto apply changes that take effect on reboot). - Read back with
readto verify (output is filtered to non-default values — pass--allto see everything).
Code Style
- Follow standard Go formatting (
go fmt) - Use
go vetfor static analysis - Structured logging with key-value pairs
- Context-aware functions for cancellation support
Project Structure
go-udap/
├── main.go # 16-line entry point; calls cli.Run
├── cli/ # CLI surface (one file per subcommand)
│ ├── cli.go # dispatcher, global flag hoisting
│ ├── discover.go info.go read.go get.go set.go reboot.go
│ ├── find.go # discover-and-find-by-MAC helper
│ ├── params.go # CLI flag table derived from udap.Parameters
│ ├── source.go # layered set sources (file/stdin/flags)
│ ├── config.go # INI parser
│ ├── output.go # formatParamMap, formatDeviceInfo
│ ├── progress.go stderr.go # TTY progress bar + log/bar mutex
│ └── *_test.go # unit tests
├── udap/ # protocol + transport
│ ├── client.go # UDP socket, packet builders, capture
│ ├── discovery.go # broadcast + listener, RWMutex-protected device map
│ ├── config.go # GetData / SetData / Reset (WithContext)
│ ├── protocol.go # Packet struct, ParsePacket, constants
│ ├── parameters.go # ★ single source of truth for 26 NVRAM params
│ ├── getdata_response.go # offset/length/value response decoder
│ ├── loopback.go # isUDAPRequestPacket — kernel-loopback filter
│ ├── validation.go # parameter / packet validation
│ ├── logger.go # structured logger (takes io.Writer)
│ ├── socket_unix.go # SO_BROADCAST via SyscallConn().Control
│ ├── socket_windows.go
│ ├── testdata/captures/*.bin # captured Net::UDAP wire payloads
│ └── *_test.go
├── docs/superpowers/ # planning specs/plans (history)
├── Taskfile.yml # Task automation
├── go.mod / go.sum # module definition (Go 1.26.3, only pflag dep)
└── README.md / CLAUDE.md