Contributing to SpindleX¶
Thank you for your interest in contributing to SpindleX! This document provides guidelines and information for contributors.
Code of Conduct¶
By participating in this project, you agree to abide by the project's Code of Conduct. Please be respectful and constructive in all interactions.
Getting Started¶
Development Environment Setup¶
-
Fork and Clone
-
Create Virtual Environment
-
Install Development Dependencies
-
Install Pre-commit Hooks
Running Tests¶
# Run the fast local test suite
python -m pytest tests -m "not integration and not real_server and not slow and not performance"
# Run with coverage
python -m pytest --cov=spindlex --cov-report=html
# Run specific test categories
python -m pytest -m unit
python -m pytest -m integration
python -m pytest -m performance
# Run tests for specific modules
python -m pytest tests/protocol/test_protocol_utils.py
Code Quality¶
I maintain high code quality standards:
# Lint code
ruff check spindlex tests
# Check formatting
ruff format --check spindlex tests
# Format code
ruff check --fix spindlex tests
ruff format spindlex tests
# Type checking
mypy spindlex
# Security scanning
bandit -r spindlex -c pyproject.toml
# Build docs
mkdocs build --strict
Contributing Guidelines¶
Reporting Issues¶
When reporting issues, please include:
- Clear Description: What you expected vs. what happened
- Reproduction Steps: Minimal code to reproduce the issue
- Environment: Python version, OS, library version
- Error Messages: Full stack traces when applicable
Use the project's issue templates: - Bug Report - Feature Request - Security Issue (use GitHub Security Advisory)
Submitting Changes¶
The default development flow is:
- Create a short-lived branch from an up-to-date
main. - Open a pull request back to
main. - Fill the PR template completely.
- Select exactly one
Type of Changetoken. - Wait for the required
quality-gatecheck to pass. - Resolve review conversations before merge.
main is the protected integration branch. Maintainers should configure branch protection to require pull requests, conversation resolution, and the quality-gate status check before merge. Direct pushes to main should be reserved for emergency recovery by repository administrators.
-
Create Feature Branch
-
Make Changes
- Follow coding standards (see below)
- Add tests for new functionality
-
Update documentation as needed
-
Test Changes
-
Commit Changes
-
Push and Create PR
In the PR body, select one Type of Change:
bug: patch release after mergefeature: feature or stabilization work for the current beta minor line; patch release before1.0.0feature-minor: intentional beta minor-line feature; minor release before1.0.0breaking: breaking beta change; minor release before1.0.0docs: no releaserefactor: no releasetest: no release
Release-impact types (bug, feature, breaking) must include test evidence in the PR body.
Commit Message Format¶
I use conventional commits:
Types: - feat: New feature - fix: Bug fix - docs: Documentation changes - style: Code style changes (formatting, etc.) - refactor: Code refactoring - test: Adding or updating tests - chore: Maintenance tasks
Examples:
feat(client): add support for Ed25519 keys
fix(transport): handle connection timeout properly
docs(readme): update installation instructions
test(crypto): add tests for key generation
Coding Standards¶
Python Style¶
I follow PEP 8 with some modifications:
- Line Length: 88 characters
- Imports: Use Ruff import sorting
- Type Hints: Required for all public APIs
- Docstrings: Google style docstrings
Code Structure¶
"""Module docstring describing the module's purpose."""
import standard_library
import third_party_library
from spindlex import local_imports
class ExampleClass:
"""Class docstring.
Args:
param1: Description of parameter.
param2: Description of parameter.
Attributes:
attr1: Description of attribute.
"""
def __init__(self, param1: str, param2: int) -> None:
"""Initialize the class.
Args:
param1: Description.
param2: Description.
"""
self.attr1 = param1
self._private_attr = param2
def public_method(self, arg: str) -> bool:
"""Public method with proper docstring.
Args:
arg: Description of argument.
Returns:
Description of return value.
Raises:
ValueError: When arg is invalid.
"""
if not arg:
raise ValueError("arg cannot be empty")
return True
def _private_method(self) -> None:
"""Private method (single underscore)."""
pass
Testing Standards¶
- Test Coverage: Aim for >90% coverage
- Test Types: Unit, integration, and performance tests
- Test Structure: Use pytest fixtures and parametrization
- Mocking: Use unittest.mock for external dependencies
import pytest
from unittest.mock import Mock, patch
from spindlex.client.ssh_client import SSHClient
class TestSSHClient:
"""Test cases for SSHClient."""
@pytest.fixture
def client(self):
"""Provide a test client instance."""
return SSHClient()
def test_connect_success(self, client):
"""Test successful connection."""
# Test implementation
pass
@pytest.mark.parametrize("username,password,expected", [
("user1", "pass1", True),
("user2", "pass2", False),
])
def test_authentication(self, client, username, password, expected):
"""Test authentication with various credentials."""
# Test implementation
pass
@patch('spindlex.transport.transport.socket')
def test_connection_failure(self, mock_socket, client):
"""Test connection failure handling."""
mock_socket.side_effect = ConnectionError("Connection failed")
# Test implementation
pass
Documentation Standards¶
- API Documentation: All public APIs must have docstrings
- Type Hints: Required for all function signatures
- Examples: Include usage examples in docstrings
- MkDocs / mkdocstrings: Public APIs should have docstrings that render clearly in the generated documentation
def connect(
self,
hostname: str,
port: int = 22,
username: Optional[str] = None,
password: Optional[str] = None,
pkey: Optional[PKey] = None,
timeout: Optional[float] = None
) -> None:
"""Connect to SSH server.
Establishes an SSH connection to the specified server with the given
authentication credentials.
Args:
hostname: Server hostname or IP address.
port: SSH port number (default: 22).
username: Username for authentication.
password: Password for authentication (if using password auth).
pkey: Private key for authentication (if using key auth).
timeout: Connection timeout in seconds.
Raises:
AuthenticationException: If authentication fails.
TransportException: If connection cannot be established.
Example:
>>> client = SSHClient()
>>> client.connect('example.com', username='user', password='pass')
>>> # Use the connection
>>> client.close()
"""
Security Guidelines¶
Security-First Development¶
- Input Validation: Validate all inputs
- Constant-Time Operations: Use constant-time comparisons for secrets
- Memory Safety: Clear sensitive data from memory
- Logging: Never log sensitive information
Cryptographic Standards¶
- Modern Algorithms: Use only modern, secure algorithms
- Key Sizes: Enforce minimum key sizes
- Random Generation: Use cryptographically secure random generators
- Timing Attacks: Protect against timing-based attacks
Security Review Process¶
- Self Review: Check your code for security issues
- Automated Scanning: Run bandit and other security tools
- Peer Review: Have security-conscious developers review
- Security Team Review: For cryptographic or security-critical changes
Performance Guidelines¶
Performance Considerations¶
- Efficiency: Optimize hot paths and frequently called functions
- Memory Usage: Minimize memory allocations and leaks
- Async Support: Consider async alternatives for I/O operations
- Benchmarking: Add benchmarks for performance-critical code
Benchmarking¶
import time
from spindlex.crypto.pkey import Ed25519Key
def benchmark_key_generation():
"""Benchmark key generation performance."""
iterations = 100
start_time = time.perf_counter()
for _ in range(iterations):
Ed25519Key.generate()
end_time = time.perf_counter()
avg_time = (end_time - start_time) / iterations
print(f"Average key generation time: {avg_time:.4f}s")
assert avg_time < 0.1 # Should be fast
Documentation¶
Types of Documentation¶
- API Documentation: Auto-generated from docstrings
- User Guide: How-to guides and tutorials
- Examples: Practical code examples
- Security Guide: Security best practices
Building Documentation¶
# Install documentation dependencies
pip install -e .[docs]
# Build documentation
mkdocs build --strict
# View documentation
open site/index.html
Writing Documentation¶
- Clear Language: Use simple, clear language
- Code Examples: Include working code examples
- Cross-References: Link to related documentation
- Updates: Keep documentation in sync with code changes
Community¶
Communication Channels¶
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: General questions and discussions
- Security Issue: Use the GitHub Security Advisory system for security-related concerns.
Getting Help¶
- Documentation: Check the documentation first
- Search Issues: Look for existing issues
- Ask Questions: Use GitHub Discussions for questions
- Stack Overflow: Tag questions with
spindlex
Recognition¶
Contributors are recognized in: - CONTRIBUTORS.md: List of all contributors - Release Notes: Major contributions mentioned - Documentation: Author attribution where appropriate
Legal¶
Contributor License Agreement¶
By contributing to SpindleX, you agree that:
- Your contributions are your original work
- You have the right to submit the contributions
- Your contributions are licensed under the MIT license
- You grant the project creator the right to use your contributions
Copyright¶
- New Files: Include MIT license header
- Existing Files: Maintain existing copyright notices
- Third-Party Code: Clearly mark and attribute third-party code
Thank You¶
Thank you for contributing to SpindleX! Your contributions help make secure SSH communication accessible to Python developers worldwide.
For questions about contributing, please open a GitHub Discussion or contact me.