Resolve merge conflicts by: - Keeping version 0.2.2 from refactor branch (next release) - Using fixed gh CLI release workflow from main (atomic release creation) - Merging CHANGELOG histories: installer releases (0.1.x) and protocol work (2.x) Conflicts resolved: - .github/workflows/release.yml: Use gh CLI for atomic releases - CHANGELOG.md: Merged both release histories chronologically - pyproject.toml: Keep 0.2.2 for next refactor release 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| deploy.yml | ||
| README.md | ||
| release.yml | ||
| test.yml | ||
CI/CD Workflows
This directory contains GitHub Actions and Gitea Actions workflows for automated testing.
Workflows
test.yml - Automated Test Suite
This workflow runs on every push and pull request. It includes two separate jobs that run in parallel:
Job 1: Unit Tests
- Purpose: Test core fragmentation and prioritization logic
- Files tested:
tests/test_fragmentation.pytests/test_prioritization.py
- Coverage:
BLEFragmentation.pymodule - Matrix: Python 3.8, 3.9, 3.10, 3.11
Job 2: Integration Tests
- Purpose: Test full BLE stack integration without hardware
- Files tested: All test files with marker
-m "not hardware" - Coverage: All
src/RNS/Interfaces/modules - Runtime: ~2 minutes per Python version
- Matrix: Python 3.8, 3.9, 3.10, 3.11
- Tests included:
- Error recovery tests
- Peer interface tests
- Integration tests
- Prioritization tests
- Plus fragmentation unit tests
PR Status Checks
When you create a pull request, you'll see two separate status checks:
✓ Unit Tests (Python 3.8)
✓ Unit Tests (Python 3.9)
✓ Unit Tests (Python 3.10)
✓ Unit Tests (Python 3.11)
✓ Integration Tests (Python 3.8)
✓ Integration Tests (Python 3.9)
✓ Integration Tests (Python 3.10)
✓ Integration Tests (Python 3.11)
Both sets of checks must pass before merging.
Coverage Reports
Coverage reports are uploaded to Codecov for Python 3.11 runs:
- Unit coverage: Tagged with
flags: unit - Integration coverage: Tagged with
flags: integration
This allows tracking coverage trends separately for unit vs integration tests.
Local Testing
To run the same tests locally that CI runs:
# Unit tests
pytest tests/test_fragmentation.py tests/test_prioritization.py -v \
--cov=src/RNS/Interfaces/BLEFragmentation.py \
--cov-report=term-missing
# Integration tests (excludes v2.2 protocol tests that need full RNS)
pytest tests/ -v -m "not hardware" \
--ignore=tests/test_v2_2_identity_handshake.py \
--ignore=tests/test_v2_2_mac_sorting.py \
--ignore=tests/test_v2_2_race_conditions.py \
--cov=src/RNS/Interfaces \
--cov-report=term-missing \
--tb=short
Note: The v2.2 protocol test suites (test_v2_2_*.py) are excluded from CI because they require the full RNS module environment. These tests document expected behavior and will run when the interface is integrated into the main Reticulum repository.
Why Two Jobs?
Separating unit and integration tests provides several benefits:
- Faster Feedback: Unit tests complete quickly (~30s), giving rapid feedback
- Clearer Failures: Know immediately if it's a core logic issue or integration problem
- Parallel Execution: Both jobs run simultaneously, total time = max(unit, integration)
- Separate Coverage: Track unit test coverage separately from integration coverage
- Granular Status: See exactly which test category failed in PR checks
deploy.yml - Continuous Deployment
This workflow automatically deploys code to Raspberry Pi devices on your local network after tests pass.
Deployment Flow
- Trigger: Push to any branch (when
src/**changes) - Dependencies: Waits for
unit-testsandintegration-teststo pass - Runner: Executes on self-hosted runner (must be on same network as Pis)
- Deployment Steps (per Pi):
- Navigate to repository directory
- Fetch and checkout the pushed branch
- Pull latest changes
- Copy
src/RNS/Interfaces/*.pyto~/.reticulum/interfaces/ - Restart
rnsdservice
Required Secrets
Configure these in GitHub Settings → Secrets and variables → Actions:
| Secret | Description | Example |
|---|---|---|
PI_HOSTS |
Comma-separated list of Pi hostnames/IPs | pi1.local,pi2.local,192.168.1.100 |
PI_REPO_PATH |
Absolute path to repository on Pis | /home/pi/ble-reticulum |
PI_USER |
SSH username for Pi access | pi |
PI_SSH_KEY |
SSH private key for passwordless authentication | -----BEGIN OPENSSH PRIVATE KEY-----... |
SSH Configuration
For containerized runners (k3s, Docker, etc.):
Since the runner is ephemeral, the SSH key is stored in GitHub Secrets and configured at runtime:
# 1. Generate SSH key pair (on any machine)
ssh-keygen -t ed25519 -C "github-runner-deployment" -f ~/.ssh/github_runner_deploy
# Press Enter for no passphrase (required for automation)
# 2. Copy public key to each Raspberry Pi
ssh-copy-id -i ~/.ssh/github_runner_deploy.pub pi@pi1.local
ssh-copy-id -i ~/.ssh/github_runner_deploy.pub pi@pi2.local
# 3. Add private key to GitHub Secrets
# Copy the private key content:
cat ~/.ssh/github_runner_deploy
# Then add to GitHub Settings → Secrets → PI_SSH_KEY
# (Paste the entire key including -----BEGIN and -----END lines)
# 4. Test from any machine with the private key
ssh -i ~/.ssh/github_runner_deploy pi@pi1.local 'echo "Connection successful"'
For persistent runners:
If your runner has persistent storage, you can use traditional SSH key setup:
# On the self-hosted runner
ssh-keygen -t ed25519 -C "github-runner"
ssh-copy-id pi@pi1.local
ssh-copy-id pi@pi2.local
# Then set PI_SSH_KEY to the private key content
cat ~/.ssh/id_ed25519
Deployment Status
The workflow fails if ANY Pi fails to deploy. Check job logs for:
- Individual Pi deployment status (✓ success / ✗ failed)
- Deployment summary with success/failure counts
- GitHub Actions summary with commit info
Troubleshooting Deployment
Deployment skipped:
- Check that tests passed (deployment depends on test jobs)
- Verify changes were in
src/**directory
SSH connection failed:
- Verify Pi is reachable:
ping pi1.local - Check SSH keys are configured correctly
- Ensure
PI_HOSTSsecret matches actual hostnames
Git operations failed:
- Verify
PI_REPO_PATHis correct - Ensure repository exists on Pis
- Check branch exists on remote
rnsd restart failed:
- Check if systemd service exists:
systemctl status rnsd - Verify user has sudo permissions (for systemd)
- Check if rnsd binary is in PATH
Workflow Triggers
test.yml
- Push to any branch
- Pull request to any branch
deploy.yml
- Push to any branch (only if
src/**or workflow file changes) - Automatically runs after tests pass
Dependencies
The workflows install:
- System:
libglib2.0-dev,libdbus-1-dev(for BLE D-Bus support) - Python:
pytest,pytest-asyncio,pytest-cov,pytest-timeout - BLE:
bleak(BLE client library),bluezero(GATT server),dbus-python - Reticulum:
rns(required for tests)
Modifying Workflows
To add new tests:
- Add test file to
tests/directory - Mark appropriately:
- Unit tests: Include in unit test job command
- Integration tests: Will run automatically with
-m "not hardware" - Hardware tests: Mark with
@pytest.mark.hardwareto exclude from CI
The workflow will automatically pick up marked integration tests.
Troubleshooting
Workflow not triggering
- Check that workflow file is in
.github/workflows/(GitHub) or.gitea/workflows/(Gitea) - Ensure YAML syntax is valid
- Check branch name matches trigger pattern
Tests failing in CI but passing locally
- Check Python version (CI tests multiple versions)
- Verify all dependencies are in
requirements.txt - Check for environment-specific paths or configs
Coverage upload failing
- This is non-fatal (continue-on-error: true)
- Usually due to Codecov token issues
- Tests still pass/fail correctly
Related Documentation
- Testing guide: TESTING.md
- Contributing guide: CONTRIBUTING.md
- Project README: README.md