Contributing
This page is for contributors and maintainers. It describes how the project is organised, how to set up a development environment, and how the project is built, tested, and released.
Abstrax is a single Go binary. There is no web frontend, no database, and no services to run during development - development is building and testing a Go binary.
Requirements
- Go 1.22 or newer (the module declares
go 1.22). - A Linux environment for running most commands. The binary builds on any platform Go supports, but the commands themselves target Debian/Ubuntu.
Repository layout
The Go module lives in the cli/ directory.
cli/
cmd/abstrax/ Program entry point (main.go)
internal/
cli/ Cobra command definitions, one file per command group
actions/ Stable action name constants used in output and the future agent
backup/ Timestamped file/directory backup helpers
confirm/ Interactive yes/no confirmation prompts
exec/ Wrapper around os/exec with dry-run and verbose support
globals/ Parsed global flag values
output/ Human-readable and JSON output helpers
platform/ OS detection and Debian/Ubuntu constants
debian/ Paths and constants for Debian/Ubuntu
services/ One package per area, holding the real logic
user/ sshkey/ sshcfg/ pkgmanager/ svcmanager/ cron/ daemon/
project/ web/ ssl/ mysql/ cache/ firewall/ serverinfo/
validate/ Input validation helpers
version/ Version variables set at build time
packaging/ systemd unit and package post-install script
.github/workflows/ CI (test) and release workflows
.goreleaser.yaml Release build configuration
Architecture
The flow for every command is the same:
CLI command (internal/cli)
-> validates input (internal/validate)
-> builds a typed options struct
-> calls a service (internal/services/<area>)
-> service uses platform adapters and the exec runner
-> returns a structured result
-> CLI prints human output or JSON (internal/output)
Key conventions:
- Commands live in
internal/cli, one file per group (user.go,firewall.go, and so on). They are built with Cobra. - Services in
internal/services/<area>hold the real logic and are kept separate from the CLI layer. This separation is intentional so the planned hosted agent can reuse the same logic. - Action names in
internal/actionsare stable identifiers (for exampleuser.add). They appear in JSON output and are reserved for the future agent. Treat them as a stable API. - Output always goes through
internal/output, which renders either human text or JSON based on the--jsonflag. - Global flags are read from
internal/globals, not threaded through every function. - Command execution goes through
internal/exec, which supports--dry-run(printing the command instead of running it) and--verbose(printing the command before running it).
Install dependencies
From the cli/ directory:
go mod download
Build
go build -o abstrax ./cmd/abstrax
The resulting ./abstrax binary sits in the repo and is .gitignored. See Building from source for more, including version metadata.
Run
./abstrax --help
./abstrax doctor
doctor works without root and is a good smoke test after building. Most other commands require root; run them with sudo ./abstrax .... Use --dry-run to preview behaviour without making changes.
Tests
Run the full test suite with the race detector:
go test -v -race ./...
There are unit tests for the validation helpers (internal/validate) and platform detection (internal/platform). When you add behaviour, add tests alongside it.
Formatting and vetting
The CI checks formatting and runs go vet. Match it locally before opening a pull request:
# Formatting: no output means clean
gofmt -l .
# Static checks
go vet ./...
If gofmt -l . lists files, format them:
gofmt -w .
Continuous integration
The test workflow (.github/workflows/test.yml) runs on pushes to main and on pull requests. It performs, in order:
gofmt -l .- fails if any file is not formatted.go vet ./...go test -v -race ./...go build -o abstrax ./cmd/abstrax
Make sure all four pass locally before submitting changes.
Releases
Releases are built with GoReleaser via .github/workflows/release.yml:
- Pushes to
main(without a tag) buildlinux/amd64andlinux/arm64binaries and upload them as workflow artifacts. - Pushing a tag matching
v*runs GoReleaser, which builds the binaries, producestar.gzarchives and.deb/.rpmpackages, generates checksums, and creates a GitHub release.
See Building from source for the GoReleaser configuration details.
Contributing checklist
- Fork the repository and create a feature branch.
- Make your change, with tests where appropriate.
- Ensure
gofmt -l .is clean. - Ensure
go vet ./...passes. - Ensure
go test ./...passes. - Open a pull request.