-
Notifications
You must be signed in to change notification settings - Fork 0
Contributing Guide
Welcome to the Hyperloop UPV Control Station project! This guide will help you contribute effectively to our mission-critical software.
We welcome contributions from team members, collaborators, and the broader hyperloop community. This guide outlines our development process, standards, and best practices.
- Safety First: All code must prioritize system safety and reliability
- Real-Time Performance: Maintain sub-millisecond response times for critical operations
- Cross-Platform Support: Ensure compatibility across Windows, macOS, and Linux
- Modular Design: Keep components loosely coupled and highly cohesive
Before contributing, ensure you have a properly configured development environment:
# Clone the repository
git clone https://github.com/HyperloopUPV-H8/software.git
cd software
# Follow the setup guide
# See: Development-Environment-Setup.md
Review these essential documents:
- System Architecture - Overall system design
- Development Environment Setup - Setup instructions
- Quick Start Guide - Basic usage
# Create a feature branch
git checkout -b feature/your-feature-name
# Set up pre-commit hooks (recommended)
npm install -g pre-commit
pre-commit install
We use a simplified Git flow:
main (production-ready)
βββ develop (integration branch)
β βββ feature/new-sensor-integration
β βββ feature/ui-improvements
β βββ bugfix/blcu-communication-fix
βββ hotfix/critical-security-patch
-
Features:
feature/description-of-feature
-
Bug fixes:
bugfix/description-of-fix
-
Hotfixes:
hotfix/critical-issue-description
-
Documentation:
docs/documentation-update
- Create Issue: Start by creating a GitHub issue describing your planned work
-
Create Branch: Create a feature branch from
develop
- Develop: Implement your changes following our coding standards
- Test: Ensure all tests pass and add new tests for your code
- Document: Update documentation as needed
- Submit PR: Create a pull request for review
// Use gofmt and golint
go fmt ./...
golint ./...
// Package naming: lowercase, single word
package vehicle
// Function naming: PascalCase for exported, camelCase for internal
func ProcessSensorData() error { /* exported */ }
func parsePacket() error { /* internal */ }
// Constants: PascalCase with descriptive names
const (
DefaultTimeout = 30 * time.Second
MaxRetries = 3
)
// Always handle errors explicitly
data, err := readSensorData()
if err != nil {
return fmt.Errorf("failed to read sensor data: %w", err)
}
// Use structured logging
log.WithFields(log.Fields{
"sensor_id": sensorID,
"error": err,
}).Error("Sensor read failed")
// Use contexts for cancellation
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Protect shared state with mutexes
type SafeCounter struct {
mu sync.RWMutex
value int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
// Use Prettier and ESLint
npm run format
npm run lint
// Interface naming: PascalCase with 'I' prefix for complex types
interface ISensorData {
id: number;
value: number;
timestamp: Date;
}
// Component naming: PascalCase
const SensorDisplay: React.FC<ISensorDisplayProps> = ({ data }) => {
// Component implementation
};
// Hook naming: camelCase starting with 'use'
const useSensorData = (sensorId: string) => {
// Hook implementation
};
// Use Redux Toolkit for global state
const sensorsSlice = createSlice({
name: 'sensors',
initialState,
reducers: {
updateSensorData: (state, action) => {
state.sensors[action.payload.id] = action.payload.data;
},
},
});
// Use Zustand for local component state
const useLocalState = create<LocalState>((set) => ({
isLoading: false,
setLoading: (loading) => set({ isLoading: loading }),
}));
// Use React.memo for expensive components
const ExpensiveComponent = React.memo(({ data }) => {
// Component implementation
});
// Use useMemo for expensive calculations
const processedData = useMemo(() => {
return expensiveCalculation(rawData);
}, [rawData]);
// Use useCallback for event handlers
const handleClick = useCallback((id: string) => {
onSensorClick(id);
}, [onSensorClick]);
// Test file naming: *_test.go
func TestProcessSensorData(t *testing.T) {
// Arrange
input := &SensorData{ID: 1, Value: 42.0}
// Act
result, err := ProcessSensorData(input)
// Assert
assert.NoError(t, err)
assert.Equal(t, expected, result)
}
// Use table-driven tests for multiple scenarios
func TestValidateInput(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{"valid input", "valid", false},
{"invalid input", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateInput(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateInput() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSensorIntegration(t *testing.T) {
// Setup test environment
server := startTestServer()
defer server.Close()
// Test real scenarios
client := NewClient(server.URL)
response, err := client.GetSensorData("sensor1")
assert.NoError(t, err)
assert.NotNil(t, response)
}
// Use React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import { SensorDisplay } from './SensorDisplay';
describe('SensorDisplay', () => {
it('displays sensor data correctly', () => {
const mockData = { id: 1, value: 42, timestamp: new Date() };
render(<SensorDisplay data={mockData} />);
expect(screen.getByText('42')).toBeInTheDocument();
});
it('handles click events', () => {
const handleClick = jest.fn();
render(<SensorDisplay data={mockData} onClick={handleClick} />);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledWith(mockData.id);
});
});
import { renderHook, act } from '@testing-library/react';
import { useSensorData } from './useSensorData';
describe('useSensorData', () => {
it('fetches sensor data', async () => {
const { result } = renderHook(() => useSensorData('sensor1'));
await act(async () => {
// Wait for async operations
});
expect(result.current.data).toBeDefined();
expect(result.current.isLoading).toBe(false);
});
});
- Backend: Minimum 80% coverage for critical paths
- Frontend: Minimum 70% coverage for components and hooks
- Integration: Cover all API endpoints and user workflows
# Check backend coverage
cd backend
go test -cover ./...
# Check frontend coverage
cd control-station
npm run test:coverage
// Package comment describes the package purpose
// Package vehicle manages pod vehicle state and board communication.
package vehicle
// Function comments explain purpose, parameters, and return values
// ProcessSensorData validates and processes incoming sensor measurements.
// It returns the processed data or an error if validation fails.
//
// Parameters:
// - data: Raw sensor data from the vehicle
//
// Returns:
// - *ProcessedData: Validated and normalized sensor data
// - error: Validation or processing error
func ProcessSensorData(data *SensorData) (*ProcessedData, error) {
// Implementation
}
/**
* Hook for managing sensor data state and real-time updates
*
* @param sensorId - Unique identifier for the sensor
* @param options - Configuration options for data fetching
* @returns Object containing sensor data, loading state, and error state
*
* @example
* ```typescript
* const { data, isLoading, error } = useSensorData('VCU_SPEED', {
* refreshInterval: 1000
* });
* ```
*/
export const useSensorData = (
sensorId: string,
options?: SensorDataOptions
): SensorDataHook => {
// Implementation
};
When adding new features, update relevant README files:
- Root README.md: High-level project overview
- Package READMEs: Component-specific documentation
- Wiki Pages: Detailed guides and tutorials
Document significant changes in:
- CHANGELOG.md: User-facing changes
- Migration guides: Breaking changes
- Wiki updates: New features and procedures
<type>(<scope>): <description>
Examples:
feat(backend): add BLCU firmware upload support
fix(frontend): resolve WebSocket reconnection issues
docs(wiki): update installation guide for Windows
## Summary
Brief description of changes and motivation.
## Changes Made
- List of specific changes
- New features or fixes implemented
- Files modified
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing completed
- [ ] Cross-platform testing (if applicable)
## Documentation
- [ ] Code comments updated
- [ ] README updated (if needed)
- [ ] Wiki documentation updated (if needed)
## Breaking Changes
List any breaking changes and migration steps.
## Related Issues
Closes #123
Relates to #456
- Code Quality: Follows coding standards
- Security: No security vulnerabilities introduced
- Performance: No performance regressions
- Tests: Adequate test coverage
- Documentation: Changes are documented
- Backwards Compatibility: No unexpected breaking changes
- Self-Review: Code reviewed by author first
- Tests Pass: All automated tests pass
- Linting: Code passes all linting checks
- Manual Testing: Functionality tested manually
- Documentation: All changes documented
We follow Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backwards compatible)
- PATCH: Bug fixes (backwards compatible)
-
Pre-Release
- All tests pass
- Documentation updated
- CHANGELOG.md updated
- Version numbers incremented
-
Release
- Create release branch
- Build and test release artifacts
- Create GitHub release
- Deploy to production (if applicable)
-
Post-Release
- Monitor for issues
- Update documentation
- Communicate changes to team
- Input Validation: Validate all external inputs
- Error Handling: Don't leak sensitive information in errors
- Authentication: Secure all API endpoints appropriately
- Encryption: Use TLS for all network communications
# Regularly audit dependencies
npm audit
go list -m -u all
# Update dependencies carefully
npm update
go get -u ./...
- Never commit secrets to version control
- Use environment variables for configuration
- Rotate credentials regularly
- Use secure storage for sensitive data
For security issues:
- Do NOT create public GitHub issues
- Contact the security team directly
- Provide detailed information about the vulnerability
- Allow time for fix before public disclosure
We are committed to providing a welcoming environment for all contributors:
- Be Respectful: Treat everyone with respect and courtesy
- Be Inclusive: Welcome people of all backgrounds and experience levels
- Be Collaborative: Work together towards common goals
- Be Professional: Maintain professional communication
- GitHub Issues: Bug reports and feature requests
- Pull Requests: Code review and discussion
- Wiki Discussions: Documentation and process discussions
- Team Chat: Internal team communications
We value all contributions:
- Contributors: Listed in README and release notes
- Major Contributors: Special recognition for significant contributions
- Maintainers: Core team members with commit access
- Profile regularly: Use Go's built-in profiler
- Optimize hot paths: Focus on frequently called functions
- Memory management: Avoid memory leaks and excessive allocations
- Concurrency: Use goroutines efficiently
- Bundle size: Keep bundle sizes reasonable
- Rendering: Optimize React component rendering
- Network: Minimize API calls and payload sizes
- Caching: Implement appropriate caching strategies
- Go: Official Go Documentation
- React: React Documentation
- TypeScript: TypeScript Handbook
- Git: Pro Git Book
- System Architecture - System design overview
- Development Environment Setup - Setup guide
- Common Issues - Troubleshooting guide
π Thank you for contributing to Hyperloop UPV!
Your contributions help advance the future of transportation. Together, we're building systems that will safely transport passengers at incredible speeds.