Add Contributing
478
Contributing.md
Normal file
478
Contributing.md
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
# 🤝 Contributing to AdGuard Control Hub
|
||||||
|
|
||||||
|
Thank you for your interest in contributing! This guide will help you get started with contributing to AdGuard Control Hub.
|
||||||
|
|
||||||
|
## 📋 Ways to Contribute
|
||||||
|
|
||||||
|
### 🐛 Bug Reports
|
||||||
|
- Search existing issues before creating new ones
|
||||||
|
- Include detailed reproduction steps
|
||||||
|
- Provide system information and logs
|
||||||
|
- Use the bug report template
|
||||||
|
|
||||||
|
### 💡 Feature Requests
|
||||||
|
- Describe the problem you're trying to solve
|
||||||
|
- Explain your proposed solution
|
||||||
|
- Consider alternative approaches
|
||||||
|
- Discuss implementation complexity
|
||||||
|
|
||||||
|
### 📝 Documentation
|
||||||
|
- Fix typos and grammatical errors
|
||||||
|
- Improve existing documentation
|
||||||
|
- Add examples and use cases
|
||||||
|
- Translate content to other languages
|
||||||
|
|
||||||
|
### 💻 Code Contributions
|
||||||
|
- Bug fixes
|
||||||
|
- New features
|
||||||
|
- Performance improvements
|
||||||
|
- Code refactoring
|
||||||
|
- Test improvements
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
|
### 1. Set Up Development Environment
|
||||||
|
|
||||||
|
Follow the [Development Guide](Development) to set up your environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fork and clone the repository
|
||||||
|
git clone https://your-gitea-domain.com/your-username/adguard-control-hub.git
|
||||||
|
cd adguard-control-hub
|
||||||
|
|
||||||
|
# Set up Python environment
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
# Set up pre-commit hooks
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create Feature Branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create branch from main
|
||||||
|
git checkout main
|
||||||
|
git pull upstream main
|
||||||
|
git checkout -b feature/your-feature-name
|
||||||
|
|
||||||
|
# Or for bug fixes
|
||||||
|
git checkout -b fix/issue-description
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Make Changes
|
||||||
|
|
||||||
|
- Follow the [Code Style Guide](#code-style-guide)
|
||||||
|
- Write tests for new functionality
|
||||||
|
- Update documentation as needed
|
||||||
|
- Ensure all tests pass
|
||||||
|
|
||||||
|
### 4. Submit Pull Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Commit changes
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: add new feature description"
|
||||||
|
git push origin feature/your-feature-name
|
||||||
|
|
||||||
|
# Create pull request via web interface
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📏 Code Style Guide
|
||||||
|
|
||||||
|
### Python Code Standards
|
||||||
|
|
||||||
|
We follow PEP 8 with some modifications:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Line length: 127 characters (Black default)
|
||||||
|
# Use type hints for all function signatures
|
||||||
|
def process_client_data(client_name: str, settings: dict[str, Any]) -> bool:
|
||||||
|
"""Process client data with proper typing."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Use descriptive variable names
|
||||||
|
client_blocking_enabled = True # Good
|
||||||
|
cbe = True # Bad
|
||||||
|
|
||||||
|
# Use f-strings for string formatting
|
||||||
|
message = f"Client {client_name} updated successfully"
|
||||||
|
|
||||||
|
# Use early returns to reduce nesting
|
||||||
|
def validate_client(client: dict) -> bool:
|
||||||
|
"""Validate client configuration."""
|
||||||
|
if not client.get("name"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not client.get("ids"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation Standards
|
||||||
|
|
||||||
|
```python
|
||||||
|
class AdGuardClient:
|
||||||
|
"""Represent an AdGuard Home client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Friendly name for the client
|
||||||
|
ids: List of IP addresses, MAC addresses, or Client IDs
|
||||||
|
filtering_enabled: Whether DNS filtering is enabled
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: Client name as stored in AdGuard Home
|
||||||
|
blocked_services: List of blocked service identifiers
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> client = AdGuardClient("Kids iPad", ["192.168.1.100"])
|
||||||
|
>>> client.add_blocked_service("youtube")
|
||||||
|
>>> print(client.blocked_services)
|
||||||
|
["youtube"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def add_blocked_service(self, service_id: str) -> None:
|
||||||
|
"""Add a service to the blocked list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_id: Identifier of the service to block
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If service_id is not recognized
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> client.add_blocked_service("netflix")
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Message Format
|
||||||
|
|
||||||
|
We use Conventional Commits:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Feature additions
|
||||||
|
feat: add client bulk update service
|
||||||
|
feat(api): implement service scheduling support
|
||||||
|
|
||||||
|
# Bug fixes
|
||||||
|
fix: resolve authentication timeout issue
|
||||||
|
fix(switch): correct client switch state reporting
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs: update installation guide
|
||||||
|
docs(wiki): add troubleshooting for SSL issues
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
test: add integration tests for config flow
|
||||||
|
test(api): increase coverage for error handling
|
||||||
|
|
||||||
|
# Refactoring
|
||||||
|
refactor: simplify API error handling
|
||||||
|
refactor(coordinator): optimize data update logic
|
||||||
|
|
||||||
|
# Chores
|
||||||
|
chore: update dependencies
|
||||||
|
chore(ci): improve workflow performance
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing Requirements
|
||||||
|
|
||||||
|
### Test Coverage
|
||||||
|
|
||||||
|
- All new code must have tests
|
||||||
|
- Aim for >90% test coverage
|
||||||
|
- Include both unit and integration tests
|
||||||
|
- Test error conditions and edge cases
|
||||||
|
|
||||||
|
### Writing Tests
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from custom_components.adguard_hub.api import AdGuardHomeAPI
|
||||||
|
|
||||||
|
class TestAdGuardAPI:
|
||||||
|
"""Test AdGuard Home API wrapper."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def api(self):
|
||||||
|
"""Create API instance for testing."""
|
||||||
|
session = MagicMock()
|
||||||
|
return AdGuardHomeAPI(
|
||||||
|
host="localhost",
|
||||||
|
port=3000,
|
||||||
|
username="admin",
|
||||||
|
password="test",
|
||||||
|
session=session
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_get_clients_success(self, api):
|
||||||
|
"""Test successful client retrieval."""
|
||||||
|
# Mock API response
|
||||||
|
expected_response = {"clients": [{"name": "Test Client"}]}
|
||||||
|
api.session.request.return_value.__aenter__.return_value.json.return_value = expected_response
|
||||||
|
|
||||||
|
# Call method
|
||||||
|
result = await api.get_clients()
|
||||||
|
|
||||||
|
# Assert results
|
||||||
|
assert result == expected_response
|
||||||
|
api.session.request.assert_called_once()
|
||||||
|
|
||||||
|
async def test_get_clients_connection_error(self, api):
|
||||||
|
"""Test client retrieval with connection error."""
|
||||||
|
# Mock connection error
|
||||||
|
api.session.request.side_effect = aiohttp.ClientError("Connection failed")
|
||||||
|
|
||||||
|
# Verify exception is raised
|
||||||
|
with pytest.raises(aiohttp.ClientError):
|
||||||
|
await api.get_clients()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
pytest
|
||||||
|
|
||||||
|
# Run with coverage report
|
||||||
|
pytest --cov=custom_components.adguard_hub --cov-report=html
|
||||||
|
|
||||||
|
# Run specific test category
|
||||||
|
pytest tests/test_api.py -v
|
||||||
|
pytest -k "test_client" -v
|
||||||
|
|
||||||
|
# Run integration tests
|
||||||
|
pytest tests/test_integration.py -v -s
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Documentation Guidelines
|
||||||
|
|
||||||
|
### Wiki Pages
|
||||||
|
|
||||||
|
When adding features that affect users:
|
||||||
|
|
||||||
|
1. **Update relevant wiki pages**
|
||||||
|
2. **Add examples to documentation**
|
||||||
|
3. **Include troubleshooting steps**
|
||||||
|
4. **Update service reference**
|
||||||
|
|
||||||
|
### Code Comments
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Good comments explain WHY, not WHAT
|
||||||
|
def calculate_block_percentage(blocked: int, total: int) -> float:
|
||||||
|
"""Calculate blocking percentage with safe division."""
|
||||||
|
# Prevent division by zero which can occur during startup
|
||||||
|
# when statistics haven't been collected yet
|
||||||
|
if total == 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
return (blocked / total) * 100
|
||||||
|
```
|
||||||
|
|
||||||
|
### README Updates
|
||||||
|
|
||||||
|
For significant features:
|
||||||
|
- Update feature list
|
||||||
|
- Add configuration examples
|
||||||
|
- Include usage examples
|
||||||
|
- Update screenshots if needed
|
||||||
|
|
||||||
|
## 🔍 Code Review Process
|
||||||
|
|
||||||
|
### Pull Request Guidelines
|
||||||
|
|
||||||
|
1. **One feature per PR**: Keep changes focused and reviewable
|
||||||
|
2. **Descriptive title**: Clearly describe what the PR does
|
||||||
|
3. **Detailed description**: Explain the problem and solution
|
||||||
|
4. **Link related issues**: Reference issue numbers
|
||||||
|
5. **Include tests**: All new code must be tested
|
||||||
|
6. **Update docs**: Update relevant documentation
|
||||||
|
|
||||||
|
### PR Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Description
|
||||||
|
Brief description of changes and motivation.
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change that adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
- [ ] Documentation update
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [ ] Unit tests added/updated
|
||||||
|
- [ ] Integration tests added/updated
|
||||||
|
- [ ] Manual testing completed
|
||||||
|
- [ ] All tests pass
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- [ ] Code comments updated
|
||||||
|
- [ ] Wiki pages updated
|
||||||
|
- [ ] README updated (if needed)
|
||||||
|
- [ ] Service reference updated (if applicable)
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
- [ ] Code follows style guidelines
|
||||||
|
- [ ] Self-review completed
|
||||||
|
- [ ] Lint checks pass
|
||||||
|
- [ ] Security considerations reviewed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Review Process
|
||||||
|
|
||||||
|
1. **Automated Checks**: CI pipeline runs automatically
|
||||||
|
2. **Code Review**: Maintainer reviews code quality and design
|
||||||
|
3. **Testing**: Reviewer tests functionality manually
|
||||||
|
4. **Feedback**: Reviewer provides constructive feedback
|
||||||
|
5. **Iteration**: Author addresses feedback and updates PR
|
||||||
|
6. **Approval**: Maintainer approves and merges PR
|
||||||
|
|
||||||
|
## 🎯 Contribution Areas
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
|
||||||
|
- **Performance improvements**
|
||||||
|
- **Bug fixes**
|
||||||
|
- **Documentation improvements**
|
||||||
|
- **Test coverage increases**
|
||||||
|
- **Security enhancements**
|
||||||
|
|
||||||
|
### Feature Requests
|
||||||
|
|
||||||
|
- **New service blocking categories**
|
||||||
|
- **Advanced scheduling features**
|
||||||
|
- **Mobile app integration**
|
||||||
|
- **Statistics dashboard improvements**
|
||||||
|
- **Multi-language support**
|
||||||
|
|
||||||
|
### Good First Issues
|
||||||
|
|
||||||
|
Look for issues labeled:
|
||||||
|
- `good first issue`
|
||||||
|
- `help wanted`
|
||||||
|
- `documentation`
|
||||||
|
- `enhancement`
|
||||||
|
|
||||||
|
## 🔒 Security Considerations
|
||||||
|
|
||||||
|
### Reporting Security Issues
|
||||||
|
|
||||||
|
**Do not create public issues for security vulnerabilities.**
|
||||||
|
|
||||||
|
Instead:
|
||||||
|
1. Email security details to maintainers
|
||||||
|
2. Include detailed reproduction steps
|
||||||
|
3. Wait for confirmation before public disclosure
|
||||||
|
4. Coordinate responsible disclosure timeline
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Sanitize user input
|
||||||
|
def sanitize_client_name(name: str) -> str:
|
||||||
|
"""Sanitize client name to prevent injection."""
|
||||||
|
# Remove potentially dangerous characters
|
||||||
|
return re.sub(r'[<>"']', '', name.strip())
|
||||||
|
|
||||||
|
# Validate configuration
|
||||||
|
def validate_host(host: str) -> bool:
|
||||||
|
"""Validate host is safe to connect to."""
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(host)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
# Validate hostname format
|
||||||
|
return bool(re.match(r'^[a-zA-Z0-9.-]+$', host))
|
||||||
|
|
||||||
|
# Never log sensitive data
|
||||||
|
_LOGGER.debug("Connecting to %s (credentials hidden)", api_host)
|
||||||
|
# Not: _LOGGER.debug("Connecting with %s:%s", username, password)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏆 Recognition
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
All contributors are recognized in:
|
||||||
|
- Repository contributors list
|
||||||
|
- Release notes
|
||||||
|
- Documentation credits
|
||||||
|
- Special thanks in major releases
|
||||||
|
|
||||||
|
### Contribution Types
|
||||||
|
|
||||||
|
We value all types of contributions:
|
||||||
|
- **Code**: Features, fixes, improvements
|
||||||
|
- **Documentation**: Guides, examples, translations
|
||||||
|
- **Testing**: Bug reports, test cases, validation
|
||||||
|
- **Design**: UI improvements, user experience
|
||||||
|
- **Community**: Support, discussions, feedback
|
||||||
|
|
||||||
|
## 📞 Getting Help
|
||||||
|
|
||||||
|
### Communication Channels
|
||||||
|
|
||||||
|
- **GitHub Issues**: Bug reports and feature requests
|
||||||
|
- **Discussions**: General questions and ideas
|
||||||
|
- **Discord/Chat**: Real-time development discussion
|
||||||
|
- **Email**: Security issues and private communication
|
||||||
|
|
||||||
|
### Mentor Program
|
||||||
|
|
||||||
|
New contributors can:
|
||||||
|
- Ask for guidance on good first issues
|
||||||
|
- Request code review assistance
|
||||||
|
- Get help setting up development environment
|
||||||
|
- Pair program on complex features
|
||||||
|
|
||||||
|
### Development Questions
|
||||||
|
|
||||||
|
Common questions and where to get help:
|
||||||
|
|
||||||
|
| Question Type | Where to Ask |
|
||||||
|
|---------------|--------------|
|
||||||
|
| How to implement a feature | GitHub Discussions |
|
||||||
|
| Bug in my code | GitHub Issues |
|
||||||
|
| Home Assistant integration questions | HA Community Forum |
|
||||||
|
| AdGuard Home API questions | AdGuard GitHub |
|
||||||
|
| Development environment issues | GitHub Discussions |
|
||||||
|
|
||||||
|
## ✅ Contribution Checklist
|
||||||
|
|
||||||
|
Before submitting:
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- [ ] Code follows style guidelines (Black, isort, flake8)
|
||||||
|
- [ ] Type hints added for all functions
|
||||||
|
- [ ] Docstrings added for public methods
|
||||||
|
- [ ] Error handling implemented
|
||||||
|
- [ ] Logging added for debugging
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- [ ] Unit tests written and passing
|
||||||
|
- [ ] Integration tests added (if applicable)
|
||||||
|
- [ ] Manual testing completed
|
||||||
|
- [ ] Edge cases considered
|
||||||
|
- [ ] Error conditions tested
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [ ] Code comments explain complex logic
|
||||||
|
- [ ] Wiki pages updated (if user-facing changes)
|
||||||
|
- [ ] Examples added for new features
|
||||||
|
- [ ] README updated (if needed)
|
||||||
|
|
||||||
|
### Security & Performance
|
||||||
|
- [ ] Security implications considered
|
||||||
|
- [ ] Performance impact evaluated
|
||||||
|
- [ ] Resource usage optimized
|
||||||
|
- [ ] No sensitive data in logs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready to contribute?** Pick an issue, follow this guide, and submit your first pull request! We're excited to work with you. 🚀
|
||||||
Reference in New Issue
Block a user