Thank you for your interest in contributing to gohome! 🎉
We welcome contributions of all kinds: bug reports, feature requests, documentation improvements, and code contributions.
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Commit Convention
- Pull Request Process
- Reporting Bugs
- Suggesting Features
This project adheres to a code of conduct. By participating, you are expected to uphold this code. Please be respectful and constructive in all interactions.
- Go 1.21+ - Install Go
- Git - For version control
- golangci-lint - For code linting
- Make - For build automation
# 1. Fork the repository on GitHub
# 2. Clone your fork
git clone https://github.com/YOUR_USERNAME/gohome.git
cd gohome
# 3. Add upstream remote
git remote add upstream https://github.com/anIcedAntFA/gohome.git
# 4. Install dependencies
go mod tidy
# 5. Verify setup
make build
make test
make lint- Find or create an issue - Check existing issues or create a new one
- Comment on the issue - Let others know you're working on it
- Create a branch - Use descriptive names:
git checkout -b feat/add-json-export git checkout -b fix/clipboard-wsl2 git checkout -b docs/update-readme
# Run the CLI directly
go run cmd/gohome/main.go
# Build and test
make build
./bin/gohome --help
# Run tests
go test -v ./...
make test
# Run linter
make lintv1.3 focuses heavily on testing quality. All new code should include comprehensive tests.
- Unit Tests - Test individual functions/packages
- Table-Driven Tests - Use table-driven pattern for comprehensive coverage
- Coverage Target - Aim for >80% coverage for new packages
- Critical Packages - Parser, git client, and renderer require 100% coverage
# Run all tests
go test -v ./...
make test
# Run specific package tests
go test -v ./internal/scanner/
go test -v ./internal/parser/
go test -v ./internal/git/
# Run with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Check coverage for specific package
go test -cover ./internal/parser/
# Output: coverage: 100.0% of statements
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out | tail -1
# Output: total: (statements) 45.1%Table-Driven Tests (Recommended):
func TestParse(t *testing.T) {
tests := []struct {
name string
input string
wantType string
wantScope string
wantMessage string
}{
{
name: "feat_with_scope",
input: "feat(api): add user endpoint",
wantType: "feat",
wantScope: "api",
wantMessage: "add user endpoint",
},
// ... more test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Parse(tt.input)
if got.Type != tt.wantType {
t.Errorf("Type = %v, want %v", got.Type, tt.wantType)
}
// ... more assertions
})
}
}Output Capture (for stdout/stderr testing):
func TestPrintCommand(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Run function that prints to stdout
PrintSomething()
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
os.Stdout = oldStdout
output := buf.String()
if !strings.Contains(output, "expected text") {
t.Errorf("Output missing expected text")
}
}Temp Files/Directories:
func TestConfigFile(t *testing.T) {
tmpDir := t.TempDir() // Automatically cleaned up
configPath := filepath.Join(tmpDir, "config.json")
// Write test config
err := os.WriteFile(configPath, []byte(`{"days": 7}`), 0644)
if err != nil {
t.Fatal(err)
}
// Test loading config
cfg := LoadConfig(configPath)
if cfg.Days != 7 {
t.Errorf("Days = %d, want 7", cfg.Days)
}
}Mock Git Repos (for scanner/git tests):
func TestGitOperations(t *testing.T) {
tmpDir := t.TempDir()
repoPath := filepath.Join(tmpDir, "test-repo")
// Create git repo
os.Mkdir(repoPath, 0755)
exec.Command("git", "init").Dir = repoPath
exec.Command("git", "config", "user.name", "Test").Dir = repoPath
exec.Command("git", "config", "user.email", "test@example.com").Dir = repoPath
// Create and commit file
os.WriteFile(filepath.Join(repoPath, "test.txt"), []byte("test"), 0644)
exec.Command("git", "add", ".").Dir = repoPath
exec.Command("git", "commit", "-m", "feat: test commit").Dir = repoPath
// Test your git functions
logs, err := GetLogs(repoPath, "Test", "1.day.ago")
if len(logs) != 1 {
t.Errorf("Got %d logs, want 1", len(logs))
}
}internal/
├── parser/
│ ├── parser.go
│ └── parser_test.go # Tests for parser.go
├── git/
│ ├── client.go
│ └── client_test.go # Tests for client.go
└── renderer/
├── printer.go
└── printer_test.go # Tests for printer.go
| Package | Target | Status |
|---|---|---|
| internal/parser | 100% | ✅ Achieved (v1.3) |
| internal/git | 100% | ✅ Achieved (v1.3) |
| internal/renderer | 100% | ✅ Achieved (v1.3) |
| internal/scanner | >80% | ✅ 87.5% (v1.2) |
| internal/version | >90% | ✅ 92.0% (v1.3) |
| cmd/gohome/cmd | >80% | 🔄 In Progress |
Document tricky test cases with comments:
{
name: "breaking_change_exclamation",
input: "feat!: breaking change",
wantType: "misc", // ! not supported by current regex
},
{
name: "emoji_variant_selector",
input: "✏️ docs: update",
wantIcon: "✏", // Variant selector (️) not captured
},Example test command:
# Run all tests
go test -v ./...
# Run specific package tests
go test -v ./internal/scanner/
# Run with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out- Follow Effective Go guidelines
- Use gofmt / gofumpt for formatting
- Run golangci-lint before committing
- Keep functions small and focused (< 50 lines when possible)
- Add comments for exported functions and complex logic
gohome/
├── cmd/gohome/ # Main application entry point
├── internal/ # Private application code
│ ├── config/ # Configuration loading/parsing
│ ├── entity/ # Data models (Commit, Task)
│ ├── git/ # Git client wrapper
│ ├── parser/ # Conventional Commits parser
│ ├── renderer/ # Output formatters (text/table)
│ ├── scanner/ # Repository discovery
│ ├── spinner/ # Terminal spinner UI
│ ├── sys/ # System utilities (clipboard)
│ └── version/ # Version info (injected at build)
├── docs/ # Documentation and guides
├── scripts/ # Installation scripts
└── npm-package/ # NPM distribution wrapper
- Constructor functions: Use
New<Type>()pattern (e.g.,git.NewClient()) - Error handling: Return errors, use
log.Fatal()only in main - Input sanitization: Always sanitize git command arguments (see
git/client.go) - Config precedence: CLI flags override JSON file values
- Never execute unsanitized user input
- Use regex validation for git arguments
- Sanitize file paths to prevent traversal attacks
- See
internal/git/client.gofor sanitization examples
We follow Conventional Commits with emojis:
<emoji> <type>(<scope>): <subject>
[optional body]
[optional footer]
✨ feat- New feature🐛 fix- Bug fix📝 docs- Documentation only🎨 style- Code style (formatting, no logic change)♻️ refactor- Code restructure (no feature/bug change)⚡ perf- Performance improvement✅ test- Add/update tests🔨 chore- Build process, dependencies, tools🔧 ci- CI/CD configuration🚀 release- Version bump, release prep
scanner- Repository discoveryparser- Commit parsingconfig- Configurationrenderer- Output formattinggit- Git clientcli- CLI flags/commandsdocs- Documentationdeps- Dependencies
✨ feat(scanner): add recursive scanning up to 2 levels deep
🐛 fix(clipboard): add WSL2 support for clipboard operations
📝 docs(readme): update installation instructions for AUR
🔨 chore(deps): upgrade golangci-lint to v1.55
♻️ refactor(config): simplify time period merging logic
⚡ perf(scanner): implement concurrent repo scanning- Use imperative mood: "add" not "added" or "adds"
- Keep subject line under 72 characters
- Capitalize subject line
- No period at the end of subject
- Separate subject from body with blank line
- Explain what and why, not how
-
Sync with upstream
git fetch upstream git rebase upstream/main
-
Run checks
make lint # Must pass make test # Must pass make build # Must succeed
-
Update documentation - If you changed:
- CLI flags → Update
README.mdflags table - Config options → Update config example
- Architecture → Update
.github/copilot-instructions.md
- CLI flags → Update
-
Update CHANGELOG.md - Add entry under
[Unreleased]
-
Push to your fork
git push origin feat/your-feature
-
Create Pull Request on GitHub
- Use PR template (auto-populated)
- Link related issues with "Closes #123"
- Add screenshots/demos for UI changes
- Fill out all checklist items
-
Address review feedback
- Make requested changes
- Push new commits (don't force-push during review)
- Re-request review when ready
- Code follows project style guidelines
- Self-reviewed code and added comments
- Updated documentation (README, inline docs)
- No new warnings or errors
- Added/updated tests for changes
- All tests pass locally
- Ran
gofumpt -l -w .for formatting - Ran
golangci-lint runwith no issues - Updated CHANGELOG.md
- Delete your feature branch (GitHub will prompt)
- Sync your fork:
git checkout main git fetch upstream git merge upstream/main git push origin main
- Check existing issues
- Try the latest version
- Collect debug information
Use GitHub issue templates - they guide you through providing:
- Description - Clear summary of the bug
- Steps to Reproduce - Exact steps to trigger the bug
- Expected Behavior - What should happen
- Actual Behavior - What actually happens
- Environment - OS, Go version, gohome version
- Logs/Screenshots - Terminal output, error messages
- Problem Statement - What problem does this solve?
- Proposed Solution - How should it work?
- Alternatives Considered - Other approaches you thought about
- Additional Context - Screenshots, examples from other tools
- Open an issue before implementing large features
- Discuss approach with maintainers
- Check ROADMAP.md for planned features
Contributors are recognized in:
- GitHub Contributors list
- Release notes (for significant contributions)
- CHANGELOG.md
- Open a GitHub Discussion
- Comment on relevant issues
- Check README.md for documentation
Thank you for contributing to gohome! 🚀