We love your input! We want to make contributing to CourierX as easy and transparent as possible.
- Fork the repo and create your branch from
main - If you've added code that should be tested, add tests
- If you've changed APIs, update the documentation
- Ensure the test suite passes
- Make sure your code lints
- Issue that pull request!
# Clone your fork
git clone https://github.com/your-username/courierx.git
cd courierx
# Run the automated setup script
./infra/scripts/setup-dev.sh
# This will:
# - Start Docker containers (PostgreSQL, Redis)
# - Set up Rails database
# - Start all services# Start infrastructure
docker-compose -f infra/docker-compose.yml up -d postgres redis
# Set up Rails
cd control-plane
cp .env.example .env
bundle install
bundle exec rails db:create db:migrate db:seed
# Set up Go Core
cd ../apps/core-go
cp .env.example .env
go mod download
# Start services
cd control-plane && bundle exec rails server -p 4000 &
cd apps/core-go && go run cmd/server/main.go &courierx/
├── apps/
│ ├── core-go/ # Go email sending engine
│ └── dashboard/ # Next.js dashboard (Milestone 4)
├── control-plane/ # Rails API (API-only mode)
├── infra/ # Infrastructure & deployment
└── docs/ # Documentation
- Update the README.md with details of changes to the interface
- Follow the story structure in MILESTONES.md
- Reference the story ID in your PR (e.g.,
[CP-001] Add Tenant model) - The PR will be merged once you have the sign-off of a maintainer
- Follow Ruby style guide
- Use Rubocop for linting:
bundle exec rubocop - Write RSpec tests for all models and controllers
- Code coverage >80% required
- Follow Go best practices
- Format with
gofmtand lint withgolangci-lint - Write tests using Go's testing package
- Code coverage >80% required
- Use TypeScript strict mode
- Follow React best practices
- Use ESLint + Prettier
- Write tests with Vitest or Jest
- Keep components small and focused
- Use conventional commit messages:
feat(models): add Tenant model with validationsfix(auth): resolve JWT expiration issuedocs: update API documentationtest: add unit tests for Provider model
- Reference story IDs:
feat(CP-001): add Tenant model - Keep commits atomic and well-described
cd control-plane
# Run all tests
bundle exec rspec
# Run specific test file
bundle exec rspec spec/models/tenant_spec.rb
# Run with coverage
COVERAGE=true bundle exec rspeccd apps/core-go
# Run all tests
go test ./...
# Run with coverage
go test ./... -cover
# Run with verbose output
go test ./... -vcd apps/dashboard
# Run unit tests
npm test
# Run E2E tests
npm run test:e2e
# Run with coverage
npm run test:coverageFollow the implementation guides in the documentation:
- New Rails Model: See Epic 1.1 in IMPLEMENTATION_GUIDE.md
- New API Endpoint: See Epic 2.1 in IMPLEMENTATION_GUIDE.md
- New Go Provider: See Epic 1.3 in IMPLEMENTATION_GUIDE.md
- New Dashboard Page: See Epic 4.1 in IMPLEMENTATION_GUIDE.md
- Create a new provider in
apps/core-go/internal/providers/ - Implement the
Providerinterface:type Provider interface { Send(ctx context.Context, email *Email) (*SendResult, error) HealthCheck(ctx context.Context) error Name() string }
- Add configuration in
control-plane/app/models/provider_account.rb - Add credential validation in the model
- Add webhook handling if supported (in Go Core)
- Write integration tests
- Document the provider setup
Example provider structure:
// apps/core-go/internal/providers/newprovider.go
package providers
type NewProviderConfig struct {
APIKey string
Region string
}
type NewProvider struct {
config NewProviderConfig
client *http.Client
}
func NewNewProvider(cfg NewProviderConfig) *NewProvider {
return &NewProvider{
config: cfg,
client: &http.Client{Timeout: 30 * time.Second},
}
}
func (p *NewProvider) Send(ctx context.Context, email *Email) (*SendResult, error) {
// Implementation
}
func (p *NewProvider) HealthCheck(ctx context.Context) error {
// Implementation
}
func (p *NewProvider) Name() string {
return "newprovider"
}When contributing, update relevant documentation:
- README.md - For user-facing changes
- MILESTONES.md - If adding new features or epics
- IMPLEMENTATION_GUIDE.md - For implementation details
- STORY_DETAILS.md - For acceptance criteria
- API docs - For API changes (use OpenAPI/Swagger)
- All code must be reviewed by at least one maintainer
- Address all review comments
- Ensure CI passes (tests, linting)
- Keep the PR focused and reasonably sized
- Be patient and respectful
- 🐛 Issues - Bug reports
- 💬 Discussions - Questions
- 📚 Documentation - Implementation guides
- Database changes: Always create a migration and update the schema
- API changes: Update both Rails API and any affected Go code
- Breaking changes: Document clearly and increment version appropriately
- Performance: Profile before optimizing, measure the impact
- Security: Never commit secrets, use environment variables
cd control-plane
bundle exec rails generate migration AddFieldToModel field:type
bundle exec rails db:migratecd control-plane
bundle exec rails generate model ModelName field:type
bundle exec rails db:migratecd apps/core-go
go get -u ./...
go mod tidy# Rails
cd control-plane
bundle exec rubocop
# Go
cd apps/core-go
golangci-lint run
# Fix auto-fixable issues
bundle exec rubocop -a # Rails
golangci-lint run --fix # Go- Ensure all tests pass
- Update CHANGELOG.md
- Update version numbers
- Create a git tag
- Maintainers will handle the release
By contributing, you agree that your contributions will be licensed under the MIT License.
Don't hesitate to ask questions in discussions or open an issue. We're here to help!