7.5 KiB
Development
This guide covers the day-to-day workflow for working on goget — building, testing, releasing, and contributing.
Requirements
- Go 1.26+
- Platform: Linux (amd64, arm64, riscv64, loong64) or FreeBSD (amd64, arm64, riscv64)
just— task runner (brew install just/apt install just/pacman -S just)groff(optional) — forjust manmanpage validation
All Go source files carry the build constraint //go:build linux || freebsd.
goget does not build or run on macOS or Windows.
First-time Setup
git clone https://codeberg.org/petrbalvin/goget.git
cd goget
just install # go mod download
just test # verify everything works on your machine
just build # produce bin/goget
A successful just build produces a static bin/goget binary with no
runtime dependencies beyond the Go standard library and golang.org/x/
packages.
Task Reference
Every task is defined in justfile and is the canonical
way to interact with the project.
| Task | What it does |
|---|---|
just |
List all recipes (default) |
just install |
go mod download |
just run |
go run ./cmd/goget (development loop) |
just build |
gofmt -l check + go vet ./... + build bin/goget |
just test |
go test -race -count=1 ./... |
just uninstall |
Remove bin/ and coverage.out |
just man |
Validate man/goget.1 with groff (skips if absent) |
just install-man |
Install manpage to ~/.local/share/man/man1/ (gzipped, XDG) |
just uninstall-man |
Remove installed manpage |
just build is strict: it runs gofmt -l (any output is a failure)
plus go vet ./.... Both must report zero warnings before the binary
compiles. This is exactly what CI checks; running it locally first
saves a round-trip.
Development Loop
graph TD
Edit[Edit source in cmd/, internal/, pkg/]
Test[just test]
Vet[just build runs gofmt + vet + build]
Man[just man - groff validation]
Commit[Conventional Commits message]
Push[Push to development branch]
CI[Forgejo CI: test.yml]
Edit --> Test
Test -->|fail| Edit
Test --> Vet
Vet -->|fail| Edit
Vet --> Man
Man -->|fail| Edit
Man --> Commit
Commit --> Push
Push --> CI
CI -->|fail| Edit
CI -->|pass| Merge[Merge / open PR]
Typical iteration:
# Make a focused change.
$EDITOR cmd/goget/flags.go
# Quick check that compile + format + vet pass.
just build
# Run the relevant tests with the race detector.
just test
# Validate any manpage edits.
just man
Building from Source
just build does three things in order:
- Copies
man/goget.1intocmd/goget/man-page-data/— the manpage is embedded into the binary via//go:embed, and//go:embedcan only reference files within the embedding package's directory. - Runs
gofmt -lovercmd/,internal/, andpkg/. Any output is a formatting violation that breaks the build. - Runs
go vet ./...— must report zero warnings. - Compiles
bin/gogetwith-ldflags "-s -w"(strips DWARF).
The resulting binary is fully static. ldd bin/goget should report
not a dynamic executable on Linux.
Testing
just test # full suite, race detector, count=1
go test ./internal/protocol/gemini/... # focused
go test -run TestFoo ./internal/core/ # one test
Conventions:
- Tests live next to the code as
*_test.go. - Use table-driven tests for parsers, validators, and protocol handlers.
- Cover both happy path and error path.
- Test names follow
TestFunctionName_Scenario. - Fuzz tests use
testing.Fand live next to the code they exercise.
The race detector is always on in CI. New code that introduces a data race must be fixed, not silenced.
Manpage Authoring
The single source of truth is man/goget.1 (troff). Editing this file is
what most users eventually read via man goget and what
--generate-man-page prints.
Workflow:
$EDITOR man/goget.1
just man # validates the troff
just build # copies the file into cmd/goget/man-page-data/
just test # cmd/goget/man_page_test.go asserts every CLI flag
# is documented and the embedded source still parses
cmd/goget/man_page_test.go enforces documentation coverage — adding a
new flag to defineFlags without documenting it in man/goget.1 breaks
the build.
Logging
The project uses internal/log for structured output. Prefer log.Infof,
log.Errorf, etc. over ad-hoc fmt.Fprintf(os.Stderr, ...) or
cli.Print*. The backlog tracks a migration of the remaining ~140 legacy
callsites to internal/log.
Branching & Releases
graph LR
Feature[Feature branches]
Development[development]
Main[main]
Tag[vX.Y.Z tag]
Release[Forgejo release]
Feature --> Development
Development --> Main
Main --> Tag
Tag --> Release
development— the only branch that receives day-to-day work. All pull requests targetdevelopment.main— only changes whendevelopmentis merged for a release. Never commit directly tomain.- Tags —
vX.Y.Ztags trigger the release workflow.
Cutting a Release
- Bump
Versionininternal/core/version.go. - Move the top
## [development]section inCHANGELOG.mdto## [X.Y.Z] — YYYY-MM-DD, inserting a fresh empty## [development]section above it. - Ensure CI is green on
development. - Merge
development→main. - Tag and push:
git tag -a vX.Y.Z -m "goget vX.Y.Z" git push origin main git push origin vX.Y.Z - The release workflow builds binaries for all seven platform targets,
extracts the matching
CHANGELOG.mdsection, and publishes the release on Codeberg.
CI
Two Forgejo Actions workflows live under
.forgejo/workflows/:
test.yml
Triggered on every push to development and on every pull request
targeting development.
| Job | What it does |
|---|---|
vet |
gofmt -l check + go vet ./... (no output = pass) |
test |
go test -race -count=1 ./... + coverage gate |
Run the same locally before opening a PR:
go vet ./...
just test
release.yml
Triggered by pushing a v* tag. Verifies that the tag matches
internal/core.Version, runs the quality gate, builds static binaries
for all seven targets, and publishes the release.
Requires a FORGEJO_TOKEN secret with release-creation permissions on
the runner.
Code Style
- Follow Effective Go.
- Keep lines under 100 characters.
camelCasefor locals,PascalCasefor exported symbols.- Prefix every file with a one-line comment describing its purpose.
- Use
//go:build linux || freebsdon every source file. - Use long CLI flags only (
--header, not-H). - Stick to the Go standard library and
golang.org/x/. External dependencies require an explicit justification. - Error messages: English, lowercase, no trailing period, always with
context (e.g.
open config: permission denied). - Run
gofmtbefore committing.
Reporting Bugs
Open an issue at https://codeberg.org/petrbalvin/goget/issues with:
goget --versionoutput.- Operating system and architecture.
- The full command used and its output.
- Expected vs actual behaviour.
For security issues, email opensource@petrbalvin.org instead of opening a public issue.