Thank you for your interest in contributing to gh-arc! This document provides guidelines and instructions for contributing to the project.
- Getting Started
- Development Setup
- Project Structure
- Development Workflow
- Code Style and Standards
- Testing
- Pull Request Process
- Additional Resources
gh-arc is a GitHub CLI extension written in Go that implements a trunk-based development workflow. Before contributing, you should:
- Understand the domain: Familiarize yourself with trunk-based development and code review workflows
- Read the documentation: Review the README.md to understand what
gh-arcdoes - Explore the codebase: Look at the project structure and existing code
- Go 1.23.4+: The project requires Go 1.23.4 or later
- GitHub CLI (
gh): Install from https://cli.github.com/ - Git: Version control system
- Basic understanding of:
- Go programming language
- GitHub API and pull request workflows
- Git operations (branches, commits, diffs, rebasing)
- CLI tool development
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/gh-arc.git
cd gh-arc# Download Go dependencies
go mod download
# Verify everything compiles
go build -o gh-arc# Install the extension locally for testing
gh extension install .# Check that the extension is installed
gh extension list
# Run the extension
gh arc version
# Check authentication
gh arc auth# If not already authenticated, login with required scopes
gh auth login --scopes "user:email,read:user"
# Or refresh existing authentication
gh auth refresh --scopes "user:email,read:user"gh-arc/
├── cmd/ # Cobra command definitions
│ ├── root.go # Root command and global flags
│ ├── auth.go # Authentication verification
│ ├── diff.go # PR creation/update command
│ ├── list.go # PR listing command
│ ├── version.go # Version information
│ └── *_test.go # Command tests
│
├── internal/ # Internal packages (not importable by other projects)
│ ├── cache/ # HTTP response caching with TTL
│ ├── codeowners/ # CODEOWNERS file parsing
│ ├── config/ # Configuration management (Viper)
│ ├── diff/ # Diff output formatting
│ ├── filter/ # PR filtering logic
│ ├── format/ # PR table formatting
│ ├── git/ # Git operations (go-git)
│ ├── github/ # GitHub API client (go-gh)
│ │ ├── client.go # Base client with auth
│ │ ├── pullrequest.go # PR operations
│ │ ├── ratelimit.go # Rate limit handling
│ │ ├── retry.go # Retry logic with backoff
│ │ ├── cache.go # Response caching
│ │ └── errors.go # Error types
│ ├── lint/ # Linting support
│ ├── logger/ # Structured logging (zerolog)
│ ├── template/ # PR template parsing
│ └── version/ # Version management
│
├── main.go # Entry point
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── README.md # User documentation
├── CLAUDE.md # AI assistant instructions
└── CONTRIBUTING.md # This file
cmd/root.go: Defines the root command, global flags, and initialization logicinternal/github/client.go: GitHub API client wrapper with rate limiting and cachinginternal/git/git.go: Git repository operations using go-gitinternal/config/config.go: Configuration loading and management with Viperinternal/logger/logger.go: Centralized logging configuration
We follow TDD principles:
- Write a failing test that describes the desired behavior
- Write minimal code to make the test pass
- Refactor the code while keeping tests green
- Repeat for the next feature
- Extract common functionality into helper functions or packages
- Reuse existing utilities before creating new ones
- Look for patterns and abstract them appropriately
- Implement only what's needed for current requirements
- Don't add speculative features or over-engineer solutions
- Keep code simple and focused
- Each code change should represent a logical unit of work
- Follow official Go style guidelines: https://go.dev/doc/effective_go
- Use
gofmtfor formatting:go fmt ./... - Use
go vetfor static analysis:go vet ./... - Write godoc comments for all exported functions, types, and packages
- Packages: lowercase, single word (e.g.,
github,config) - Files: lowercase with underscores (e.g.,
pull_request.go) - Types: PascalCase (e.g.,
GitClient,PROptions) - Functions/Methods: camelCase or PascalCase depending on visibility
- Constants: PascalCase or UPPER_CASE for exported/private
- Test files:
*_test.go(e.g.,client_test.go)
// Good: Wrap errors with context
if err != nil {
return fmt.Errorf("failed to fetch PR: %w", err)
}
// Good: Define custom error types for specific cases
var ErrNotFound = errors.New("resource not found")
// Good: Check error types
if errors.Is(err, ErrNotFound) {
// Handle not found
}// Use the centralized logger from internal/logger
logger := logger.Get()
// Different log levels
logger.Debug().Msg("detailed debugging information")
logger.Info().Msg("general information")
logger.Warn().Msg("warning message")
logger.Error().Err(err).Msg("error occurred")
// Add context with fields
logger.Info().
Str("repo", "owner/repo").
Int("pr_number", 123).
Msg("processing PR")// Access configuration through viper
createAsDraft := viper.GetBool("diff.createAsDraft")
defaultBranch := viper.GetString("github.defaultBranch")
// Provide defaults
branch := viper.GetString("github.defaultBranch")
if branch == "" {
branch = "main"
}- Place tests in
*_test.gofiles alongside the code they test - Use table-driven tests for multiple similar test cases
- Write tests for both happy paths and error cases
- Aim for high test coverage (>80% for critical packages)
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run tests for a specific package
go test ./internal/github
# Run a specific test
go test -run TestClientNew ./internal/github
# Run tests with verbose output
go test -v ./...See docs/contributing/TESTING.md for detailed testing guidelines and examples.
- Write tests for your changes
- Run all tests:
go test ./... - Run go vet:
go vet ./... - Format code:
go fmt ./... - Update documentation if you changed functionality
- Test manually with
gh arccommands - Commit frequently with clear messages
-
Title: Use writing good commit messages guidelines
- Example:
Add support for diff with Linear integration
- Example:
-
Description: Include:
- What changed and why
- How to test the changes
- Links to related issues
- Screenshots (if UI-related)
-
Size: Keep PRs focused and reasonably sized
- Prefer smaller, incremental PRs over large rewrites
- Split large features into multiple PRs if possible
-
Tests: All PRs must include tests
- New features require new tests
- Bug fixes require tests that would have caught the bug
-
Documentation: Update docs if needed
- README.md for user-facing changes
- Code comments for complex logic
- CONTRIBUTING.md for development process changes
- Consider creating an ADR for significant architectural decisions
- Submit PR: Create a pull request with a clear description
- CI Checks: Ensure all automated checks pass
- Code Review: Maintainers will review your code
- Address Feedback: Make requested changes
- Approval: Once approved, a maintainer will merge your PR
- Delete your feature branch
- Update your fork's master branch
- Celebrate! 🎉
- Architecture Guide - Detailed explanation of the codebase architecture
- Testing Guide - Comprehensive testing patterns and examples
- Workflows Guide - Common development tasks and workflows
- Troubleshooting Guide - Debugging and problem-solving
- Architecture Decision Records (ADRs) - Documents of significant architectural decisions
- Go Documentation
- Effective Go
- GitHub CLI Extensions
- go-gh Library
- go-git Library
- Cobra CLI Framework
- Trunk-Based Development
- Issues: Open an issue for bugs or feature requests
- Discussions: Use GitHub Discussions for questions
- Code Review: Ask questions in PR comments
- Be respectful and constructive
- Welcome newcomers and help them learn
- Focus on what is best for the community
- Show empathy towards other contributors
Thank you for contributing to gh-arc!