479 lines
12 KiB
Markdown
479 lines
12 KiB
Markdown
# Contributing to Changemaker Lite
|
|
|
|
Thank you for your interest in contributing to Changemaker Lite! This guide will help you get started with contributing code, documentation, bug reports, and feature requests.
|
|
|
|
## Welcome!
|
|
|
|
Changemaker Lite is an open-source political campaign platform built by volunteers for organizers. We welcome contributions from developers, designers, writers, and community organizers of all experience levels.
|
|
|
|
**Our mission**: Provide free, self-hosted tools for grassroots political campaigns to compete with well-funded opponents.
|
|
|
|
## Ways to Contribute
|
|
|
|
### 1. Code Contributions
|
|
|
|
Help build new features or fix bugs in:
|
|
|
|
- **Backend API** (TypeScript + Express + Prisma)
|
|
- **Admin Frontend** (React + Vite + Ant Design)
|
|
- **Media API** (TypeScript + Fastify + Drizzle)
|
|
- **Infrastructure** (Docker, Nginx, PostgreSQL, Redis)
|
|
|
|
[→ Development Setup Guide](development-setup.md){ .md-button .md-button--primary }
|
|
|
|
### 2. Documentation
|
|
|
|
Improve guides, tutorials, and API documentation:
|
|
|
|
- **User guides** - Help organizers use the platform
|
|
- **Developer docs** - API reference, architecture guides
|
|
- **Tutorials** - Step-by-step walkthroughs
|
|
- **Translations** - Localize docs for other languages
|
|
|
|
[→ Documentation Guide](documentation.md){ .md-button }
|
|
|
|
### 3. Bug Reports
|
|
|
|
Found a bug? Help us fix it:
|
|
|
|
- Search existing issues first
|
|
- Provide clear reproduction steps
|
|
- Include error messages and logs
|
|
- Test on latest version
|
|
|
|
[→ Report a Bug](https://github.com/changemaker-lite/v2/issues/new?template=bug_report.md){ .md-button }
|
|
|
|
### 4. Feature Requests
|
|
|
|
Suggest new features or enhancements:
|
|
|
|
- Check roadmap first
|
|
- Describe the use case
|
|
- Explain why it's valuable
|
|
- Consider implementation complexity
|
|
|
|
[→ Request a Feature](https://github.com/changemaker-lite/v2/discussions/new?category=ideas){ .md-button }
|
|
|
|
### 5. Testing
|
|
|
|
Help test new features and releases:
|
|
|
|
- Test beta releases on staging
|
|
- Verify bug fixes
|
|
- Test migration procedures
|
|
- Report edge cases
|
|
|
|
### 6. Community Support
|
|
|
|
Help other users:
|
|
|
|
- Answer questions in Discussions
|
|
- Share your setup experiences
|
|
- Write blog posts or tutorials
|
|
- Present at community calls
|
|
|
|
### 7. Design
|
|
|
|
Improve user experience:
|
|
|
|
- UI/UX design mockups
|
|
- User flow improvements
|
|
- Accessibility enhancements
|
|
- Mobile responsiveness
|
|
|
|
## Code of Conduct
|
|
|
|
Changemaker Lite is committed to providing a welcoming and inclusive environment for all contributors.
|
|
|
|
**Our values**:
|
|
|
|
- **Respect**: Treat everyone with kindness and professionalism
|
|
- **Inclusivity**: Welcome contributors from all backgrounds
|
|
- **Collaboration**: Work together constructively
|
|
- **Constructive feedback**: Focus on improvement, not criticism
|
|
|
|
[→ Full Code of Conduct](code-of-conduct.md){ .md-button }
|
|
|
|
**Unacceptable behavior**:
|
|
|
|
- Harassment, discrimination, or hate speech
|
|
- Personal attacks or trolling
|
|
- Publishing private information
|
|
- Spam or self-promotion
|
|
|
|
**Enforcement**: Violations will result in warnings, temporary bans, or permanent bans depending on severity.
|
|
|
|
**Reporting**: Email conduct@cmlite.org to report violations confidentially.
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
Before contributing code, ensure you have:
|
|
|
|
- **Node.js 20+** installed
|
|
- **Docker Desktop** (or Docker + Docker Compose)
|
|
- **Git** for version control
|
|
- **Code editor** (VSCode recommended)
|
|
- **GitHub account** for pull requests
|
|
|
|
### Quick Start
|
|
|
|
1. **Fork the repository** on GitHub
|
|
2. **Clone your fork** locally
|
|
3. **Set up development environment** ([guide](development-setup.md))
|
|
4. **Find an issue** to work on
|
|
5. **Create a branch** for your changes
|
|
6. **Make your changes** with tests
|
|
7. **Submit a pull request** ([guide](pull-requests.md))
|
|
|
|
### Finding Issues to Work On
|
|
|
|
**Good first issues**: Look for issues tagged `good-first-issue` in GitHub Issues.
|
|
|
|
**Help wanted**: Issues tagged `help-wanted` need contributors.
|
|
|
|
**By skill level**:
|
|
- `beginner` - Simple fixes, documentation
|
|
- `intermediate` - Feature enhancements, refactoring
|
|
- `advanced` - Architecture changes, performance optimization
|
|
|
|
**By area**:
|
|
- `backend` - API, database, services
|
|
- `frontend` - React components, UI/UX
|
|
- `infrastructure` - Docker, Nginx, deployment
|
|
- `documentation` - Guides, tutorials, API docs
|
|
|
|
[→ Browse Issues](https://github.com/changemaker-lite/v2/issues){ .md-button }
|
|
|
|
## Contribution Workflow
|
|
|
|
### 1. Claim an Issue
|
|
|
|
Before starting work:
|
|
|
|
1. **Comment on the issue**: "I'd like to work on this"
|
|
2. **Wait for assignment**: Maintainer will assign you
|
|
3. **Ask questions**: Clarify requirements before coding
|
|
|
|
!!! tip "Avoid Duplicate Work"
|
|
Always check if someone is already assigned before starting work.
|
|
|
|
### 2. Create a Branch
|
|
|
|
```bash
|
|
# Update main branch
|
|
git checkout main
|
|
git pull upstream main
|
|
|
|
# Create feature branch
|
|
git checkout -b feature/campaign-export
|
|
|
|
# Or for bug fixes
|
|
git checkout -b fix/geocoding-error
|
|
```
|
|
|
|
**Branch naming**:
|
|
- `feature/description` - New features
|
|
- `fix/description` - Bug fixes
|
|
- `docs/description` - Documentation
|
|
- `refactor/description` - Code refactoring
|
|
- `test/description` - Test additions
|
|
|
|
### 3. Make Changes
|
|
|
|
Follow our coding standards:
|
|
|
|
- **TypeScript**: Strict mode, type all functions
|
|
- **ESLint**: Run `npm run lint` before committing
|
|
- **Prettier**: Auto-format with `npm run format`
|
|
- **Tests**: Add tests for new features
|
|
- **Comments**: Document complex logic
|
|
|
|
```typescript
|
|
// Good: Type-safe function with comments
|
|
/**
|
|
* Geocodes an address using the specified provider.
|
|
* Falls back to next provider if the first fails.
|
|
*
|
|
* @param address - Full address string
|
|
* @param provider - Geocoding provider (default: nominatim)
|
|
* @returns Promise resolving to { lat, lng, quality }
|
|
*/
|
|
async function geocodeAddress(
|
|
address: string,
|
|
provider: GeocodingProvider = 'nominatim'
|
|
): Promise<GeocodingResult> {
|
|
// Implementation
|
|
}
|
|
```
|
|
|
|
### 4. Test Your Changes
|
|
|
|
```bash
|
|
# Backend tests
|
|
cd api && npm test
|
|
|
|
# Frontend tests
|
|
cd admin && npm test
|
|
|
|
# Type checking
|
|
cd api && npx tsc --noEmit
|
|
cd admin && npx tsc --noEmit
|
|
|
|
# Linting
|
|
cd api && npm run lint
|
|
cd admin && npm run lint
|
|
|
|
# Integration tests
|
|
docker compose up -d
|
|
./scripts/test-integration.sh
|
|
```
|
|
|
|
### 5. Commit Your Changes
|
|
|
|
**Commit message format** (Conventional Commits):
|
|
|
|
```
|
|
type(scope): short description
|
|
|
|
Longer description (optional)
|
|
|
|
Fixes #123
|
|
```
|
|
|
|
**Types**:
|
|
- `feat` - New feature
|
|
- `fix` - Bug fix
|
|
- `docs` - Documentation
|
|
- `style` - Formatting, whitespace
|
|
- `refactor` - Code restructuring
|
|
- `test` - Test additions
|
|
- `chore` - Build, tooling
|
|
|
|
**Examples**:
|
|
```
|
|
feat(campaigns): add campaign export to CSV
|
|
|
|
Adds a new export button to the campaigns page that downloads
|
|
all campaigns as a CSV file.
|
|
|
|
Fixes #456
|
|
|
|
---
|
|
|
|
fix(geocoding): handle null responses from Nominatim
|
|
|
|
Prevents crash when Nominatim returns empty result for
|
|
invalid addresses.
|
|
|
|
Fixes #789
|
|
|
|
---
|
|
|
|
docs(api): document campaign endpoints
|
|
|
|
Adds comprehensive API documentation for all campaign endpoints
|
|
including request/response examples.
|
|
```
|
|
|
|
### 6. Push and Create Pull Request
|
|
|
|
```bash
|
|
# Push to your fork
|
|
git push origin feature/campaign-export
|
|
|
|
# Create pull request on GitHub
|
|
# Fill out the PR template
|
|
```
|
|
|
|
[→ Pull Request Guidelines](pull-requests.md){ .md-button }
|
|
|
|
### 7. Code Review
|
|
|
|
After submitting your PR:
|
|
|
|
1. **Automated checks** run (lint, tests, build)
|
|
2. **Maintainer review** provides feedback
|
|
3. **Address feedback** with new commits
|
|
4. **Request re-review** after changes
|
|
5. **Merge** after approval
|
|
|
|
**Be patient**: Reviews may take 1-3 business days. If no response after 5 days, politely ping the maintainer.
|
|
|
|
## Development Guidelines
|
|
|
|
### Code Style
|
|
|
|
**TypeScript**:
|
|
```typescript
|
|
// Use interfaces for object shapes
|
|
interface Campaign {
|
|
id: string;
|
|
title: string;
|
|
slug: string;
|
|
active: boolean;
|
|
}
|
|
|
|
// Use types for unions/aliases
|
|
type SupportLevel = 'STRONG_SUPPORT' | 'SUPPORT' | 'UNDECIDED' | 'OPPOSED' | 'STRONG_OPPOSED';
|
|
|
|
// Prefer async/await over promises
|
|
async function getCampaigns(): Promise<Campaign[]> {
|
|
const campaigns = await prisma.campaign.findMany();
|
|
return campaigns;
|
|
}
|
|
```
|
|
|
|
**React**:
|
|
```tsx
|
|
// Use functional components
|
|
const CampaignsPage: React.FC = () => {
|
|
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
|
|
|
|
useEffect(() => {
|
|
fetchCampaigns();
|
|
}, []);
|
|
|
|
return <Table dataSource={campaigns} />;
|
|
};
|
|
|
|
// Extract reusable components
|
|
const CampaignCard: React.FC<{ campaign: Campaign }> = ({ campaign }) => {
|
|
return <Card title={campaign.title} />;
|
|
};
|
|
```
|
|
|
|
**Prisma**:
|
|
```typescript
|
|
// Use type-safe queries
|
|
const campaigns = await prisma.campaign.findMany({
|
|
where: { active: true },
|
|
include: { createdBy: true },
|
|
orderBy: { createdAt: 'desc' }
|
|
});
|
|
|
|
// Use transactions for multi-step operations
|
|
await prisma.$transaction(async (tx) => {
|
|
await tx.campaign.update({ where: { id }, data: { active: false } });
|
|
await tx.campaignEmail.updateMany({ where: { campaignId: id }, data: { status: 'CANCELLED' } });
|
|
});
|
|
```
|
|
|
|
### Testing Guidelines
|
|
|
|
**Unit tests**:
|
|
```typescript
|
|
// api/src/modules/campaigns/campaigns.service.test.ts
|
|
describe('CampaignService', () => {
|
|
it('should create campaign with valid data', async () => {
|
|
const campaign = await campaignService.create({
|
|
title: 'Test Campaign',
|
|
slug: 'test-campaign',
|
|
createdByUserId: 'user-id'
|
|
});
|
|
|
|
expect(campaign).toHaveProperty('id');
|
|
expect(campaign.title).toBe('Test Campaign');
|
|
});
|
|
|
|
it('should throw error for duplicate slug', async () => {
|
|
await expect(
|
|
campaignService.create({ title: 'Test', slug: 'existing', createdByUserId: 'user-id' })
|
|
).rejects.toThrow('Slug already exists');
|
|
});
|
|
});
|
|
```
|
|
|
|
**Integration tests**:
|
|
```typescript
|
|
// api/tests/campaigns.integration.test.ts
|
|
describe('Campaigns API', () => {
|
|
it('GET /api/influence/campaigns returns campaigns', async () => {
|
|
const response = await request(app)
|
|
.get('/api/influence/campaigns')
|
|
.set('Authorization', `Bearer ${adminToken}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(Array.isArray(response.body.data)).toBe(true);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Communication Channels
|
|
|
|
### GitHub
|
|
|
|
- **Issues**: Bug reports, feature requests
|
|
- **Discussions**: General questions, ideas
|
|
- **Pull Requests**: Code contributions
|
|
|
|
### Email
|
|
|
|
- **General**: hello@cmlite.org
|
|
- **Security**: security@cmlite.org
|
|
- **Code of Conduct**: conduct@cmlite.org
|
|
|
|
### Community Calls
|
|
|
|
- **Monthly Contributors Call**: First Tuesday of month, 7pm UTC
|
|
- **Quarterly Community Call**: Last Friday of quarter, 6pm UTC
|
|
|
|
[→ Join Calls](https://cmlite.org/community){ .md-button }
|
|
|
|
## Recognition
|
|
|
|
We appreciate all contributors! Your name will be:
|
|
|
|
- **Added to CONTRIBUTORS.md** after first merged PR
|
|
- **Listed in release notes** for significant contributions
|
|
- **Featured on website** for major features
|
|
- **Invited to community calls** as a contributor
|
|
|
|
### Hall of Fame
|
|
|
|
**Top Contributors** (all time):
|
|
|
|
1. @contributor1 - 234 commits
|
|
2. @contributor2 - 189 commits
|
|
3. @contributor3 - 156 commits
|
|
|
|
[→ Full Contributors List](https://github.com/changemaker-lite/v2/graphs/contributors)
|
|
|
|
## License
|
|
|
|
By contributing to Changemaker Lite, you agree that your contributions will be licensed under the **MIT License**.
|
|
|
|
This means:
|
|
- Your code can be used by anyone
|
|
- Attribution is required (copyright notice)
|
|
- No warranty is provided
|
|
|
|
See [LICENSE](https://github.com/changemaker-lite/v2/blob/main/LICENSE) for full terms.
|
|
|
|
## Questions?
|
|
|
|
- **Need help getting started?** Ask in [Discussions](https://github.com/changemaker-lite/v2/discussions)
|
|
- **Have a question about an issue?** Comment on the issue
|
|
- **Stuck on development setup?** Check [Development Setup Guide](development-setup.md)
|
|
- **Want to chat?** Join our monthly contributors call
|
|
|
|
## Related Documentation
|
|
|
|
- [Code of Conduct](code-of-conduct.md) - Community standards
|
|
- [Development Setup](development-setup.md) - Environment setup
|
|
- [Pull Request Guidelines](pull-requests.md) - PR process
|
|
- [Roadmap](roadmap.md) - Future plans
|
|
|
|
## Next Steps
|
|
|
|
Ready to contribute?
|
|
|
|
1. **[Read the Code of Conduct](code-of-conduct.md)** - Understand community standards
|
|
2. **[Set up your environment](development-setup.md)** - Install dependencies
|
|
3. **[Find an issue](https://github.com/changemaker-lite/v2/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)** - Pick something to work on
|
|
4. **[Submit your first PR](pull-requests.md)** - Make your contribution
|
|
|
|
Thank you for contributing to Changemaker Lite! Together, we're building tools for democratic change.
|