Thank you for your interest in contributing! This document provides guidelines for contributing to this project.
- Code of Conduct
- Getting Started
- Development Workflow
- Code Standards
- Testing Requirements
- Pull Request Process
- Architecture Decision Records
- Community
We are committed to providing a welcoming and inclusive environment for all contributors, regardless of background, experience level, gender identity, sexual orientation, disability, personal appearance, race, ethnicity, age, religion, or nationality.
- Use welcoming and inclusive language
- Be respectful of differing viewpoints and experiences
- Gracefully accept constructive criticism
- Focus on what is best for the community
- Show empathy towards other community members
- Harassment, discrimination, or intimidation
- Trolling, insulting/derogatory comments, and personal attacks
- Public or private harassment
- Publishing others' private information without permission
- Other conduct which could reasonably be considered inappropriate
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting the project maintainers. All complaints will be reviewed and investigated promptly and fairly.
- Go 1.26 or higher
- Bun (no version pinned; CI installs the latest — see installation guide)
- Git for version control
- Docker (optional, for testing with LDAP)
-
Fork and clone the repository:
git clone https://github.com/YOUR-USERNAME/ldap-selfservice-password-changer.git cd ldap-selfservice-password-changer -
Install dependencies:
bun install
-
Configure environment:
cp .env.local.example .env.local # Edit .env.local with your LDAP/SMTP settings -
Start development server:
bun run dev
-
Verify setup:
- Application runs on
http://localhost:3000(default) - Hot reload works for TypeScript and Go changes
- Application runs on
For detailed setup instructions, see the Development Guide.
We use a feature branch workflow:
-
Create a feature branch from
main:git checkout main git pull origin main git checkout -b feature/your-feature-name
-
Make your changes with descriptive commits:
git add . git commit -m "feat: add password strength indicator"
-
Push to your fork:
git push -u origin feature/your-feature-name
-
Open a Pull Request against
main
Use Conventional Commits format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, no logic change)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasksperf: Performance improvements
Examples:
feat(reset): add email-based password reset
fix(validators): correct uppercase character detection
docs(api): update JSON-RPC examples
test(ratelimit): add concurrent request tests
refactor(rpc): extract token validation logicStyle Guide:
- Use
gofmtfor formatting (automatic) - Follow Effective Go principles
- Keep functions focused (single responsibility)
- Use descriptive names (avoid abbreviations)
GoDoc Comments:
// GenerateToken generates a cryptographically secure random token.
// Returns a base64 URL-safe encoded string of 32 random bytes (256 bits).
func GenerateToken() (string, error) {
// Implementation
}Error Handling:
// ✅ Good: Wrap errors with context
if err != nil {
return fmt.Errorf("failed to connect to LDAP: %w", err)
}
// ❌ Bad: Return raw errors
if err != nil {
return err
}Package Structure:
- Keep packages focused on single responsibility
- Internal packages in
internal/(not exported) - Add package documentation in
doc.goor first file
Style Guide:
- Use TypeScript strict mode (enforced in
tsconfig.json) - Use
prettierfor formatting - No
anytypes (use proper type annotations) - Use ES modules with
.jsextensions in imports
JSDoc Comments (for exported functions):
/**
* Validates that a password contains minimum required numbers.
* @param amount - Minimum number of digits required
* @param fieldName - Name of the field for error messages
* @returns Validation function that returns error message or empty string
*/
export const mustIncludeNumbers =
(amount: number, fieldName: string) =>
(v: string): string => {
// Implementation
};Type Safety:
// ✅ Good: Explicit types
const form = document.querySelector<HTMLFormElement>("#form");
if (!form) throw new Error("Form not found");
// ❌ Bad: No type checking
const form = document.querySelector("#form");Accessibility:
- Use semantic HTML (
<button>not<div onclick>) - Include ARIA labels for icon-only buttons
- Ensure 7:1 contrast ratios (WCAG AAA)
- Support keyboard navigation
Atomic Design:
- Atoms: Basic elements (buttons, icons, links) in
templates/atoms/ - Molecules: Composite components (forms, headers) in
templates/molecules/ - Pages: Full page templates in
templates/
Guidelines:
- Use Tailwind utility classes (avoid custom CSS)
- Follow density variants:
comfortable:andcompact: - Support dark mode with
dark:variants - Use CSS variables for theme colors
The coverage gate is enforced by Codecov; its project and patch targets live in
the repository's Codecov configuration, which is the authority for the numbers.
Current per-package coverage is on the
Codecov dashboard,
and go test -cover ./... prints it locally.
Per-package percentages are deliberately not restated here. They used to be, and every one of them had drifted from reality by the time anyone read them.
# Run all tests
go test ./... -v
# Run with coverage
go test ./... -cover
# Run specific package
go test ./internal/validators -v
# Run integration tests (requires Docker)
go test ./internal/rpchandler -vUnit Tests:
func TestValidateMinLength(t *testing.T) {
tests := []struct {
name string
password string
minLength int
wantErr bool
}{
{"valid length", "12345678", 8, false},
{"too short", "1234567", 8, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateMinLength(tt.password, tt.minLength)
if (err != nil) != tt.wantErr {
t.Errorf("got error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}Integration Tests — guarded by the integration build tag and driven by
environment variables pointing at real services. There is no testcontainers
dependency; the services come from docker compose --profile test up, and
make test-integration runs go test -v -race -tags=integration ./.... A test
whose variables are unset skips rather than fails.
//go:build integration
func TestIntegration_NewHandler(t *testing.T) {
ldapServer := getEnvOrSkip(t, "LDAP_SERVER")
// ... test against that server
}- Test files:
*_test.goin same package as source - Table-driven tests for multiple cases
- Use
testify/assertfor assertions (if needed) - Mock external dependencies (LDAP, SMTP)
-
Run tests:
go test ./... -cover -
Format code:
gofmt -w . bunx prettier --write .
-
Build assets:
bun run build:assets
-
Update documentation if needed:
- API changes → update
docs/api-reference.md - New features → update
README.mdand relevant docs - Breaking changes → create migration guide
- API changes → update
-
Test manually:
- Test password change flow
- Test password reset flow (if modified)
- Test accessibility (keyboard navigation, screen reader)
- Test dark mode and density modes
- Code follows project style guidelines
- All tests pass (
go test ./...) - Test coverage maintained or improved
- Documentation updated (if applicable)
- Commit messages follow Conventional Commits format
- No sensitive data (passwords, tokens) in code or commits
- Accessibility tested (if UI changes)
- Dark mode tested (if UI changes)
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed
## Screenshots (if UI changes)
[Add screenshots here]
## Related Issues
Fixes #123- Automated checks run on PR (CI/CD)
- Code review by maintainers
- Address feedback and push updates
- Approval and merge by maintainers
For significant architectural or design decisions, create an ADR:
-
Create ADR file:
docs/adr/NNNN-short-title.md
-
Use the template:
# ADR-NNNN: Title **Status**: Proposed | Accepted | Deprecated **Date**: YYYY-MM-DD **Authors**: @username ## Context What is the issue we're facing? ## Decision What decision did we make? ## Consequences What are the positive and negative outcomes? ## Alternatives Considered What other options were evaluated?
-
Update index in
docs/README.md
See existing ADRs:
- ADR-0001: Standardize Form Field Names
- ADR-0002: Password Reset Functionality
- ADR-0003: Configurable Reset Email Templates
- Documentation: Check docs/ directory first
- Issues: Search existing issues
- Discussions: Use GitHub Discussions for questions
When reporting bugs, include:
- Environment: OS, Go version, browser (if frontend issue)
- Steps to reproduce: Clear, step-by-step instructions
- Expected behavior: What should happen
- Actual behavior: What actually happens
- Logs/Screenshots: Relevant error messages or screenshots
For feature requests:
- Use case: Describe the problem you're solving
- Proposed solution: How would you implement it?
- Alternatives: What other approaches did you consider?
- Scope: Is this a small enhancement or major feature?
Do not open public issues for security vulnerabilities.
Report security issues to the maintainers privately:
- See SECURITY.md for reporting process
- Use GitHub Security Advisories for responsible disclosure
- README - Project overview
- Development Guide - Setup and workflows
- API Reference - JSON-RPC API
- Architecture - System design
- Security - Threat model and controls
- Accessibility - WCAG compliance
By contributing to this project, you agree that your contributions will be licensed under the project's MIT License.
Thank you for contributing! Your efforts help make this project better for everyone.