Thank you for your interest in contributing to e5s! This document provides guidelines for contributing to the project.
This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.
Before creating bug reports, please check the existing issues to avoid duplicates. When creating a bug report, include:
- A clear and descriptive title
- Detailed steps to reproduce the problem
- Expected behavior vs actual behavior
- Your environment (Go version, OS, SPIRE version)
- Any relevant logs or error messages
Use the bug report template when creating an issue.
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, include:
- A clear and descriptive title
- A detailed description of the proposed functionality
- Explain why this enhancement would be useful
- List any alternative solutions you've considered
Use the feature request template when creating an issue.
- Fork the repository and create your branch from
main - Follow the coding style - Run
golangci-lint runbefore submitting - Write tests - Ensure your changes are covered by tests
- Update documentation - Update README.md or docs/ if needed
- Run tests - Ensure
go test -race ./...passes - Run security checks - Ensure
gosec ./...andgovulncheck ./...pass - Sign your commits - Use
git commit -sto add a Signed-off-by line - Write a clear commit message - Follow conventional commit format
We follow the Conventional Commits specification:
type(scope): brief description
Detailed explanation of the change.
Fixes #123
Types:
feat:- New featurefix:- Bug fixdocs:- Documentation changestest:- Adding or updating testsrefactor:- Code refactoringperf:- Performance improvementschore:- Maintenance tasksci:- CI/CD changessecurity:- Security fixes
- Update the README.md or documentation with details of changes if applicable
- Update tests to cover your changes
- Ensure all CI checks pass (linting, tests, security scans)
- Your PR will be reviewed by maintainers
- Address any feedback from reviewers
- Once approved, your PR will be merged
Development:
See the COMPATIBILITY.md
Integration Testing:
See docs/how-to/run-integration-tests.md for detailed setup instructions and version requirements.
-
Clone your fork:
git clone https://github.com/YOUR_USERNAME/e5s.git cd e5s -
Install dependencies:
go mod download
-
Run tests:
go test -race -v ./... -
Run linters:
golangci-lint run gosec ./...
-
Run security checks:
govulncheck ./...
Integration tests require a running SPIRE deployment. See TESTING.md for setup instructions.
- Write unit tests for new functionality
- Ensure tests are deterministic and don't rely on timing
- Use table-driven tests where appropriate
- Mock external dependencies (SPIRE Workload API)
- Test error cases and edge conditions
Example test structure:
func TestFeature(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
// Test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Feature(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Feature() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Feature() = %v, want %v", got, tt.want)
}
})
}
}If you discover a security vulnerability, do not open a public issue. Instead, report it through:
- GitHub Security Advisories: https://github.com/sufield/e5s/security/advisories/new
- Email: security@sufield.dev
See SECURITY.md for more information.
- Follow standard Go conventions and idioms
- Use
gofmtorgoimportsfor formatting - Keep functions small and focused
- Write clear, descriptive variable and function names
- Add comments for exported functions and complex logic
- Prefer explicit error handling over panic
- Use early returns to reduce nesting
- Update README.md for user-facing changes
- Add godoc comments for exported functions and types
- Update docs/ for significant features
- Include code examples in documentation where helpful
All example code lives in the repository and is compiled in CI to ensure it stays valid:
- examples/basic-server/ - High-level API server example
- examples/basic-client/ - High-level API client example
- examples/middleware/ - Custom middleware patterns example
When you change public APIs or add new features:
Example code is the source of truth. Update the actual working examples:
# Edit the example file
vim examples/basic-server/main.go
# Verify it compiles
make build-examples
# Verify it still works (if possible)
go run ./examples/basic-serverMark important code sections with example markers so docs can reference them:
// example-start:feature-name
func ImportantFeature() {
// Your code here
}
// example-end:feature-nameExisting markers:
-
examples/basic-server/main.go:server-setup- Basic server configurationauthenticated-endpoint- Handler with peer identity extractionserver-start- Starting the server with e5s.Start()
-
examples/basic-client/main.go:client-request- Making an mTLS request
-
examples/middleware/main.go:auth-middleware- Authentication middleware pattern
Instead of copying code into docs, reference the actual example files:
## Example Server
See [examples/basic-server/main.go](examples/basic-server/main.go) for a working example.
The sections are marked with `example-start`/`example-end` comments:
- Server setup (marker: `server-setup`)
- Authenticated endpoint (marker: `authenticated-endpoint`)Benefits of this approach:
- Docs always reference real, tested code
- Changes to examples automatically propagate
- CI catches when examples break
- Users see actual working patterns
CI automatically builds all examples on every push:
# What CI runs (you can run this locally too)
make build-examplesThis ensures:
- All example code compiles
- No broken imports or syntax errors
- Examples stay valid as APIs evolve
Location: .github/workflows/security.yml contains the "Build examples" step
Update examples when you:
- Add new public APIs
- Change existing behavior that affects usage
- Add demonstrable new features
- Fix bugs that change how code should be written
When updating examples:
- Update the actual example code in
cmd/orexamples/ - Verify it compiles:
make build-examples - Add or update example markers if needed
- Update line number references in documentation
- Test the example still works correctly (if possible)
- Update any related documentation that references the example
In addition to the runnable example programs above, this project uses Godoc examples (Example*() functions in *_test.go files) to provide API documentation that appears on pkg.go.dev.
Godoc examples are special test functions that serve as executable documentation. They:
- Live in
example_test.gofiles alongside regular tests - Appear automatically in package documentation on pkg.go.dev
- Are compiled (and optionally executed) by
go test - Follow Go's official example naming conventions
See the Go blog post on examples for full details.
example_test.go # High-level API examples (e5s package)
spiffehttp/example_test.go # Low-level mTLS API examples
spire/example_test.go # SPIRE adapter examples
func ExampleServe() // Documents the Serve function
func ExampleServe_withConfig() // Second example for Serve function
func ExamplePeerID() // Documents the PeerID function
func Example_authorization() // Package-level example demonstrating authorizationNaming rules:
ExampleFoo()- Documents function/typeFooExampleFoo_suffix()- Additional examples forFoo(suffix describes the scenario)Example()- Documents the package as a wholeExample_suffix()- Package-level examples (suffix describes the use case)
Godoc Examples (example_test.go):
- Purpose: API documentation on pkg.go.dev
- Audience: Developers learning the API
- Scope: Individual functions/types
- Location:
*_test.gofiles - Tested: Compiled by
go test(run if they have// Output:comments)
Example Programs (examples/*):
- Purpose: Complete, runnable applications
- Audience: Developers building similar systems
- Scope: Full workflows and integration patterns
- Location: Separate
mainpackages inexamples/directory - Tested: Built by CI (
make build-examples)
Use both: Godoc examples for API docs, example programs for real-world patterns.
Most Godoc examples in this project compile but don't execute because they require SPIRE infrastructure:
// ExampleStart demonstrates starting an mTLS server.
//
// This example requires a running SPIRE agent and e5s.yaml configuration file.
func ExampleStart() {
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
id, ok := e5s.PeerID(r)
if !ok {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "Hello, %s!\n", id)
})
shutdown, err := e5s.Start("e5s.yaml", http.DefaultServeMux)
if err != nil {
log.Fatal(err)
}
defer shutdown()
// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
// No "// Output:" comment, so this compiles but doesn't execute
}No // Output: comment means the example compiles but doesn't run. This is appropriate for:
- Code requiring external infrastructure (SPIRE, databases, etc.)
- Network operations
- Server startup code
Update Godoc examples when you:
- Add new exported functions or types - Every public API should have at least one example
- Change function signatures - Update examples to match new parameters
- Add new common use cases - Add
ExampleFoo_newUseCase()examples - Deprecate APIs - Update examples to show the replacement API
Godoc examples are automatically tested in CI:
go test ./...compiles all examples (catches API breakage)- Examples without
// Output:are compile-only (appropriate for SPIRE-dependent code) - Examples with
// Output:are executed and output is verified
CI already runs go test ./... which verifies all Godoc examples compile correctly.
When adding or updating Godoc examples:
- Add example to appropriate
*_test.gofile - Follow naming convention (
ExampleFooorExampleFoo_suffix) - Add clear godoc comment explaining what the example demonstrates
- Note if example requires SPIRE infrastructure
- Omit
// Output:comment (since examples require SPIRE) - Verify it compiles:
go test -run=^Example ./... - Check formatting:
gofmt -s -w .
By contributing, you agree that your contributions will be licensed under the Apache License 2.0.
Feel free to open an issue with the question label, or start a discussion in the GitHub Discussions tab.
Contributors will be recognized in:
- The project's README.md contributors section
- Release notes for significant contributions
- The project's GitHub contributors page
Thank you for contributing to e5s!