Thank you for considering contributing to this project! This document outlines the workflow for both human and AI contributors.
- Branch Naming
- Commit Messages
- Running Tests
- Validating AWTRIX Payloads
- Cache Behavior
- Pull Request Process
- Code Style
Use the following prefixes for branch names:
feature/- New features (e.g.,feature/add-retry-logic)fix/- Bug fixes (e.g.,fix/date-formatting-bug)docs/- Documentation updates (e.g.,docs/update-readme)refactor/- Code refactoring (e.g.,refactor/simplify-cache)test/- Test additions or improvements (e.g.,test/add-domain-tests)chore/- Maintenance tasks (e.g.,chore/update-dependencies)
Follow Conventional Commits format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
feat: New featurefix: Bug fixdocs: Documentation changestest: Adding or updating testsrefactor: Code changes that neither fix a bug nor add a featurechore: Maintenance tasksperf: Performance improvements
feat(alarm): add support for custom notification sound
fix(cache): ensure atomic writes on Windows systems
docs: update cron examples in README
test(domain): add edge cases for month boundary handling
task testThis will:
- Run all tests with race detector
- Generate coverage report
- Create
coverage.htmlfor viewing
go test -v ./internal/domainopen coverage.html
# OR
go tool cover -html=coverage.txt- Overall coverage: >95%
- Domain logic (
internal/domain): 100% - New code must include tests
Before submitting changes that affect AWTRIX payloads, validate the JSON structure.
{
"draw": [
{"df": [0, 0, 4, 7, "#FFFF00"]},
{"df": [5, 0, 4, 7, "#099999"]},
{"df": [10, 0, 4, 7, "#4584D3"]},
{"dt": [15, 1, "TOM", "#FF0000"]}
],
"noScroll": true,
"duration": 5
}{
"text": "TRASH: Bio 14-tgl., Gelbe Tonne",
"background": "#FF0000",
"blinkText": 500,
"hold": true,
"duration": 0,
"noScroll": false
}- JSON is valid (use
jqor online validator) - All colors are 6-char hex with
#prefix (e.g.,#FF0000) - Draw commands use correct syntax (
df,dt) - Chip positions follow spacing rule (0, 5, 10, 15...)
- Text position is at x=15
- Text is max 4 characters and uppercase
-
Start a local MQTT broker:
docker run -it -p 1883:1883 eclipse-mosquitto
-
Subscribe to messages:
mosquitto_sub -h localhost -t "test_prefix/#" -v -
Run the command:
./bin/bdg status --city-id=123 --area-id=456 \ --mqtt-broker=tcp://localhost:1883 \ --mqtt-prefix=test_prefix
-
Verify the payload in mosquitto_sub output
- Delete cache files programmatically
- Modify existing cache files in place
- Assume cache structure (always check for existence)
✅ ALWAYS:
- Use atomic writes (temp file + rename)
- Handle cache misses gracefully (return nil, nil)
- Respect the cache-first policy (never refetch if file exists)
When modifying cache behavior:
- Test with empty cache directory
- Test with existing cache files
- Test with corrupted cache files
- Test cache writes under disk full conditions
- Test cache reads with permission errors
- Location:
~/.cache/bdg/ - Naming:
YYYY-MM.json(e.g.,2025-12.json) - Content: Raw API response JSON (pretty-printed with 2-space indent)
Example:
{
"year": "2025",
"month": "12",
"dates": [
{
"title": "Bio 14-tgl.",
"trash_name": "BARNIM_BIO",
"day": "2025-12-30",
"description": "",
"color": "#566322"
}
]
}-
Fork and Branch
git checkout -b feature/your-feature-name
-
Make Changes
- Write code
- Add tests
- Update documentation
-
Run Quality Checks
task test task lint -
Commit
git add . git commit -m "feat: add your feature"
-
Push and Create PR
git push origin feature/your-feature-name
-
PR Checklist
- Tests pass
- Linter passes
- Coverage >95%
- Documentation updated
- CHANGELOG updated (if applicable)
- Manual testing completed (if applicable)
Follow Effective Go and Go Code Review Comments.
Key points:
- Use
gofmt(automatically applied bytask lint) - Error strings should not be capitalized or end with punctuation
- Prefer short variable names in limited scopes
- Document exported functions and types
- Domain logic: Pure functions, no I/O
- Error handling: Wrap errors with context using
fmt.Errorf("context: %w", err) - Time handling: Always pass
time.Timeas parameter, never use globaltime.Now() - Tests: Use table-driven tests with descriptive names
All code must pass golangci-lint:
task lintFix automatically when possible:
golangci-lint run --fix ./...- Read AGENTS.md for detailed architecture and design decisions
- Check existing issues and discussions
- Open a new issue for clarification
Releases are automated using GoReleaser and GitHub Actions.
- Update CHANGELOG.md with changes
- Commit all changes
- Create and push a version tag:
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0- GitHub Actions will automatically:
- Run tests on all platforms
- Build binaries for Linux, macOS, Windows
- Create checksums
- Generate release notes
- Publish to GitHub Releases
Before creating a tag, test the release process:
# Validate GoReleaser config
task release-check
# Build snapshot (no publish)
task releaseBinaries will be in dist/ directory.
By contributing, you agree that your contributions will be licensed under the same license as the project.