620 lines
15 KiB
Markdown

# Pull Request Guidelines
This guide covers the complete pull request (PR) process for contributing code to Changemaker Lite V2, from creation to merge.
## Before Submitting a PR
### 1. Create or Find an Issue
**For features**:
- Search [existing issues](https://github.com/changemaker-lite/v2/issues) first
- If none exists, [create a feature request](https://github.com/changemaker-lite/v2/issues/new?template=feature_request.md)
- Wait for maintainer approval before implementing
- Discuss implementation approach in the issue
**For bugs**:
- Search [existing bug reports](https://github.com/changemaker-lite/v2/issues?q=is%3Aissue+label%3Abug) first
- If none exists, [create a bug report](https://github.com/changemaker-lite/v2/issues/new?template=bug_report.md)
- Include reproduction steps and expected vs actual behavior
- Verify bug still exists on latest `v2` branch
!!! tip "Avoid Wasted Effort"
Always create an issue and get approval before spending time on a large feature. Maintainers may have alternative approaches or priorities.
### 2. Test Your Changes
Run all checks locally before submitting:
```bash
# Type checking
cd api && npx tsc --noEmit
cd admin && npx tsc --noEmit
# Linting
cd api && npm run lint
cd admin && npm run lint
# Unit tests
cd api && npm test
cd admin && npm test
# Integration tests (if applicable)
cd api && npm run test:integration
# Build
cd api && npm run build
cd admin && npm run build
```
**All checks must pass** before submitting PR.
### 3. Update Documentation
If your changes affect:
- **API endpoints**: Update [API reference](../api-reference/index.md)
- **User features**: Update [user guides](../user-guides/admin-guide.md)
- **Environment variables**: Update [`.env.example`](https://github.com/changemaker-lite/v2/blob/v2/.env.example)
- **Database schema**: Update [database docs](../database/schema.md)
- **Configuration**: Update [deployment guides](../deployment/production.md)
!!! warning "Documentation is Required"
PRs with new features will not be merged without corresponding documentation updates.
## PR Title Format
Use **Conventional Commits** format:
```
type(scope): short description
```
### Types
| Type | When to Use |
|------|-------------|
| `feat` | New feature for users |
| `fix` | Bug fix |
| `docs` | Documentation changes |
| `style` | Code formatting (no behavior change) |
| `refactor` | Code restructuring (no behavior change) |
| `perf` | Performance improvements |
| `test` | Test additions or fixes |
| `chore` | Build process, tooling, dependencies |
| `ci` | CI/CD configuration |
| `revert` | Reverting a previous commit |
### Scopes
Common scopes by area:
**Backend**:
- `api` - General API changes
- `auth` - Authentication/authorization
- `campaigns` - Campaign module
- `locations` - Location module
- `shifts` - Shift module
- `canvass` - Canvassing module
- `email` - Email sending
- `database` - Database schema/migrations
**Frontend**:
- `admin` - Admin pages
- `public` - Public pages
- `volunteer` - Volunteer portal
- `components` - React components
- `store` - Zustand stores
- `ui` - UI/UX changes
**Infrastructure**:
- `docker` - Docker/Docker Compose
- `nginx` - Nginx configuration
- `monitoring` - Prometheus/Grafana
- `deployment` - Deployment scripts/config
### Examples
**Good titles**:
-`feat(campaigns): add campaign export to CSV`
-`fix(geocoding): handle null responses from Nominatim`
-`docs(api): document campaign endpoints`
-`refactor(auth): extract JWT middleware to separate file`
-`perf(locations): add database index on postalCode`
**Bad titles**:
-`Update campaigns.tsx` (too vague)
-`Bug fix` (no scope or description)
-`WIP: New feature` (don't submit WIP PRs)
-`Fixed everything` (not descriptive)
## PR Description Template
Use this template for your PR description:
```markdown
## What
[Clear description of what this PR does]
## Why
[Why this change is needed, link to issue]
## How
[Brief explanation of implementation approach]
## Testing
[How to test these changes]
## Screenshots
[For UI changes, include before/after screenshots]
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] No console errors
- [ ] All CI checks pass
- [ ] Follows code style guidelines
Fixes #[issue-number]
```
### Example PR Description
```markdown
## What
Adds a CSV export button to the campaigns page that allows admins to download all campaigns with their metadata.
## Why
Users need to export campaign data for reporting and analysis in external tools like Excel.
Fixes #456
## How
- Added export button to CampaignsPage header
- Created `/api/influence/campaigns/export` endpoint
- Implemented CSV generation using `csv-stringify` library
- Added SUPER_ADMIN/INFLUENCE_ADMIN role check
## Testing
1. Login as admin user
2. Navigate to Campaigns page (/app/influence/campaigns)
3. Click "Export CSV" button in page header
4. Verify CSV file downloads with correct data
5. Open CSV in Excel/Google Sheets to verify formatting
## Screenshots
![Export button in campaigns page](https://user-images.githubusercontent.com/export-button.png)
## Checklist
- [x] Tests added (export endpoint integration test)
- [x] Documentation updated (API reference, admin guide)
- [x] No console errors
- [x] All CI checks pass
- [x] Follows code style guidelines
Fixes #456
```
## Creating the PR
### 1. Push Your Branch
```bash
# Ensure your branch is up to date
git fetch upstream
git rebase upstream/v2 # or: git merge upstream/v2
# Push to your fork
git push origin feature/your-feature-name
# If you rebased, force push (with care!)
git push --force-with-lease origin feature/your-feature-name
```
### 2. Open PR on GitHub
1. Go to your fork on GitHub
2. Click **"Pull requests"** tab
3. Click **"New pull request"**
4. **Base repository**: `changemaker-lite/v2` **base**: `v2`
5. **Head repository**: `YOUR-USERNAME/changemaker-lite` **compare**: `feature/your-feature-name`
6. Click **"Create pull request"**
7. Fill out the PR template (see above)
8. Click **"Create pull request"**
### 3. Request Reviewers
- PRs are automatically assigned to maintainers
- You can request specific reviewers if you know who to ask
- For urgent PRs, mention `@changemaker-lite/maintainers`
## Code Review Process
### Automated Checks
After submitting, CI/CD runs these checks:
1. **Lint**: ESLint rules
2. **Type Check**: TypeScript compilation
3. **Tests**: Unit + integration tests
4. **Build**: Production build
5. **Security**: Dependency vulnerability scan
**Status badges** appear on your PR:
-**Green checkmark**: All checks passed
-**Red X**: Some checks failed
- 🟡 **Yellow dot**: Checks in progress
**Fix failing checks** before requesting review.
### Maintainer Review
A maintainer will review your code and provide feedback:
**Review categories**:
1. **Code quality**:
- Follows code style guidelines
- No unnecessary complexity
- Proper error handling
- No security vulnerabilities
2. **Functionality**:
- Solves the problem correctly
- Edge cases handled
- No regressions
3. **Tests**:
- Adequate test coverage (>80%)
- Tests are meaningful
- Tests pass consistently
4. **Documentation**:
- Code comments for complex logic
- API documentation updated
- User guide updated (if needed)
### Review Outcomes
**Approved** ✅:
- Maintainer approves PR
- Ready to merge (after squash)
**Request Changes** 🔄:
- Maintainer requests modifications
- Address feedback and push new commits
- Re-request review after changes
**Comment** 💬:
- Feedback without blocking merge
- Optional to address
## Addressing Feedback
### 1. Read Feedback Carefully
- Understand the requested change
- Ask clarifying questions if unclear
- Don't take criticism personally (it's about code, not you)
### 2. Make Changes
```bash
# Make requested changes
# Edit files...
# Commit changes
git add .
git commit -m "refactor: address review feedback
- Extracted duplicate logic into helper function
- Added error handling for edge case
- Updated tests to cover new scenario"
# Push to same branch
git push origin feature/your-feature-name
```
**Commits are added to existing PR automatically.**
### 3. Respond to Comments
- **Acknowledge feedback**: "Good catch, fixed in abc1234"
- **Explain changes**: "Refactored this to use a switch statement instead"
- **Ask questions**: "I'm not sure how to handle X, suggestions?"
- **Mark resolved**: Click "Resolve conversation" after addressing
### 4. Re-Request Review
After addressing all feedback:
1. Click **"Reviewers"** section
2. Click circular arrow next to reviewer's name
3. Or comment `@reviewer Ready for re-review`
## Common Review Feedback
### Code Quality Issues
**Issue**: Large function with too many responsibilities
**Feedback**:
> This function is doing too much. Can you extract the geocoding logic into a separate function?
**Fix**:
```typescript
// Before
async function createLocation(data) {
// 50 lines of validation, geocoding, database insert...
}
// After
async function createLocation(data) {
const validated = validateLocationData(data);
const geocoded = await geocodeAddress(validated.address);
return insertLocation({ ...validated, ...geocoded });
}
```
**Issue**: Magic numbers or strings
**Feedback**:
> What does `30` represent here? Use a named constant.
**Fix**:
```typescript
// Before
if (visits.length >= 30) { }
// After
const VISIT_RATE_LIMIT = 30;
if (visits.length >= VISIT_RATE_LIMIT) { }
```
**Issue**: Missing error handling
**Feedback**:
> What happens if the API call fails? Add error handling.
**Fix**:
```typescript
// Before
const reps = await fetch(url).then(r => r.json());
// After
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`API returned ${response.status}`);
}
const reps = await response.json();
} catch (error) {
logger.error('Failed to fetch representatives', error);
throw new Error('Unable to lookup representatives');
}
```
### Test Coverage Issues
**Issue**: Missing test for edge case
**Feedback**:
> Add a test for when postal code is invalid.
**Fix**:
```typescript
it('should return 400 for invalid postal code', async () => {
const response = await request(app)
.post('/api/influence/representatives/lookup')
.send({ postalCode: 'INVALID' });
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
});
```
### Documentation Issues
**Issue**: Missing API documentation
**Feedback**:
> Add this endpoint to the API reference docs.
**Fix**: Update `docs/v2/api-reference/campaigns.md` with new endpoint.
### Performance Issues
**Issue**: N+1 query problem
**Feedback**:
> This causes N+1 queries. Use Prisma `include` to join.
**Fix**:
```typescript
// Before (N+1)
const campaigns = await prisma.campaign.findMany();
for (const campaign of campaigns) {
campaign.createdBy = await prisma.user.findUnique({ where: { id: campaign.createdByUserId } });
}
// After (single query)
const campaigns = await prisma.campaign.findMany({
include: { createdBy: true }
});
```
## Merge Process
### Squash and Merge
Changemaker Lite uses **squash and merge** for all PRs:
1. Maintainer clicks **"Squash and merge"**
2. All commits in PR are squashed into one commit
3. Commit message = PR title + description summary
4. Merged to `v2` branch
**Why squash?**
- Clean linear history
- Easier to revert if needed
- No messy "WIP" or "fix typo" commits
### After Merge
Once your PR is merged:
1. **Celebrate!** 🎉 You've contributed to Changemaker Lite
2. **Update your fork**:
```bash
git checkout v2
git pull upstream v2
git push origin v2
```
3. **Delete feature branch** (optional):
```bash
git branch -d feature/your-feature-name
git push origin --delete feature/your-feature-name
```
4. **Update issue**: GitHub auto-closes issue with `Fixes #N`
5. **Check release notes**: Your contribution will be mentioned in next release
## PR Checklist
Use this before submitting:
### Pre-Submission
- [ ] **Issue created** and approved by maintainer
- [ ] **Branch created** from latest `v2`
- [ ] **Changes implemented** following code style
- [ ] **Self-review** - read your own code critically
- [ ] **Manual testing** - verify changes work as expected
### Code Quality
- [ ] **TypeScript**: No type errors (`npx tsc --noEmit`)
- [ ] **Linting**: No lint errors (`npm run lint`)
- [ ] **Formatting**: Code formatted (`npm run format`)
- [ ] **No console logs**: Remove debug statements
- [ ] **No commented code**: Remove old code
- [ ] **Error handling**: All errors caught and logged
### Tests
- [ ] **Unit tests**: Added/updated tests
- [ ] **Tests pass**: `npm test` succeeds
- [ ] **Coverage**: Maintained or improved (>80%)
- [ ] **Integration tests**: Added if needed
- [ ] **Edge cases**: Tested invalid inputs
### Documentation
- [ ] **Code comments**: Complex logic documented
- [ ] **API docs**: New endpoints documented
- [ ] **User docs**: User guide updated (if user-facing)
- [ ] **README**: Updated if needed
- [ ] **.env.example**: New env vars added
### UI (if applicable)
- [ ] **Responsive**: Works on mobile/tablet/desktop
- [ ] **Accessibility**: Keyboard navigation works
- [ ] **Browser testing**: Works in Chrome, Firefox, Safari
- [ ] **Loading states**: Spinners for async operations
- [ ] **Error states**: Error messages shown to user
- [ ] **Screenshots**: Included in PR description
### Final Checks
- [ ] **CI passing**: All automated checks green
- [ ] **PR template**: Description complete
- [ ] **Commit messages**: Follow conventional commits
- [ ] **No merge conflicts**: Branch rebased/merged with `v2`
- [ ] **Reviewers requested**: Maintainers notified
## Troubleshooting PRs
### CI Checks Failing
**Lint failures**:
```bash
cd api && npm run lint:fix
cd admin && npm run lint:fix
git add . && git commit -m "chore: fix lint errors" && git push
```
**Type errors**:
```bash
cd api && npx tsc --noEmit # Shows errors
# Fix type errors in code
git add . && git commit -m "fix: resolve type errors" && git push
```
**Test failures**:
```bash
cd api && npm test # Run locally to see errors
# Fix failing tests
git add . && git commit -m "test: fix failing tests" && git push
```
### Merge Conflicts
**Resolving conflicts**:
```bash
# Fetch latest upstream
git fetch upstream
# Rebase onto v2
git rebase upstream/v2
# If conflicts, resolve them
# Edit conflicted files, then:
git add .
git rebase --continue
# Force push (since history changed)
git push --force-with-lease origin feature/your-feature-name
```
### PR Not Getting Reviewed
**If no review after 5 business days**:
1. **Check CI**: Ensure all checks pass
2. **Ping maintainer**: Comment "@changemaker-lite/maintainers Friendly ping for review"
3. **Join Discord**: Ask in #contributors channel
4. **Email**: dev@cmlite.org for urgent PRs
**Reasons for delays**:
- Maintainers busy with other priorities
- PR too large (break into smaller PRs)
- Missing context (add more details to description)
- Waiting on related PRs to merge first
## Related Documentation
- [Contributing Guide](index.md) - Overview
- [Development Setup](development-setup.md) - Environment setup
- [Code of Conduct](code-of-conduct.md) - Community standards
- [Roadmap](roadmap.md) - Future plans
## Questions?
- **Discord**: [#contributors channel](https://discord.gg/changemaker-lite)
- **Discussions**: [Ask in Q&A](https://github.com/changemaker-lite/v2/discussions)
- **Email**: dev@cmlite.org
Thank you for contributing! Every PR helps make Changemaker Lite better. 🚀