🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
349 lines
12 KiB
YAML
349 lines
12 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*.*.*' # Match semantic version tags (v0.2.3, v1.0.0, etc.)
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: 'Git tag to release (e.g., v0.2.3)'
|
|
required: true
|
|
type: string
|
|
|
|
# Ensure only one release runs at a time
|
|
concurrency:
|
|
group: release
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
# ============================================================================
|
|
# JOB 1: Validate release preconditions
|
|
# ============================================================================
|
|
validate:
|
|
name: Validate Release
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
tag: ${{ steps.version.outputs.tag }}
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0 # Full history for branch checks
|
|
|
|
- name: Extract version from tag
|
|
id: version
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
TAG="${{ github.event.inputs.tag }}"
|
|
else
|
|
TAG=${GITHUB_REF#refs/tags/}
|
|
fi
|
|
VERSION=${TAG#v} # Remove 'v' prefix
|
|
|
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "Release version: $VERSION"
|
|
|
|
- name: Verify tag format
|
|
run: |
|
|
TAG="${{ steps.version.outputs.tag }}"
|
|
if ! [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "ERROR: Tag must follow semantic versioning (v0.2.3, v1.0.0, etc.)"
|
|
echo "Got: $TAG"
|
|
exit 1
|
|
fi
|
|
echo "✓ Tag format is valid"
|
|
|
|
- name: Verify release is from main branch
|
|
run: |
|
|
# Get the branch that contains this tag
|
|
BRANCH=$(git branch -r --contains ${{ steps.version.outputs.tag }} | grep 'origin/main' || echo "")
|
|
if [ -z "$BRANCH" ]; then
|
|
echo "ERROR: Tag ${{ steps.version.outputs.tag }} is not on main branch"
|
|
echo "Releases must be created from main branch only"
|
|
exit 1
|
|
fi
|
|
echo "✓ Tag is on main branch"
|
|
|
|
- name: Verify pyproject.toml version matches tag
|
|
run: |
|
|
PYPROJECT_VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
TAG_VERSION="${{ steps.version.outputs.version }}"
|
|
|
|
if [ "$PYPROJECT_VERSION" != "$TAG_VERSION" ]; then
|
|
echo "ERROR: Version mismatch!"
|
|
echo " pyproject.toml: $PYPROJECT_VERSION"
|
|
echo " Git tag: $TAG_VERSION"
|
|
echo ""
|
|
echo "Please update pyproject.toml to match the tag version"
|
|
exit 1
|
|
fi
|
|
echo "✓ pyproject.toml version matches tag ($PYPROJECT_VERSION)"
|
|
|
|
- name: Verify CHANGELOG.md has version entry
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
|
|
if ! grep -q "## \[$VERSION\]" CHANGELOG.md; then
|
|
echo "ERROR: CHANGELOG.md missing entry for version $VERSION"
|
|
echo ""
|
|
echo "Please add a changelog entry:"
|
|
echo " ## [$VERSION] - $(date +%Y-%m-%d)"
|
|
exit 1
|
|
fi
|
|
echo "✓ CHANGELOG.md has entry for $VERSION"
|
|
|
|
- name: Verify CHANGELOG entry is not empty
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
|
|
# Extract section between this version and next version/unreleased
|
|
CHANGELOG_SECTION=$(sed -n "/## \[$VERSION\]/,/## \[/p" CHANGELOG.md | head -n -1)
|
|
|
|
# Remove header line and whitespace
|
|
CONTENT=$(echo "$CHANGELOG_SECTION" | tail -n +2 | grep -v '^[[:space:]]*$' || echo "")
|
|
|
|
if [ -z "$CONTENT" ]; then
|
|
echo "ERROR: CHANGELOG.md entry for $VERSION is empty"
|
|
echo "Please add release notes describing the changes"
|
|
exit 1
|
|
fi
|
|
echo "✓ CHANGELOG.md entry is not empty"
|
|
|
|
- name: Validation summary
|
|
run: |
|
|
echo "## ✅ Release Validation Passed" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Version**: ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Tag**: ${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "All preconditions met. Proceeding with release..." >> $GITHUB_STEP_SUMMARY
|
|
|
|
# ============================================================================
|
|
# JOB 2: Run full test suite
|
|
# ============================================================================
|
|
test:
|
|
name: Run Tests
|
|
needs: validate
|
|
runs-on: ubuntu-latest
|
|
|
|
strategy:
|
|
matrix:
|
|
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python ${{ matrix.python-version }}
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: Install system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libglib2.0-dev libdbus-1-dev libcairo2-dev libgirepository1.0-dev
|
|
|
|
- name: Install Python dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install pytest pytest-asyncio pytest-cov pytest-timeout
|
|
pip install rns bleak bluezero dbus-python
|
|
|
|
- name: Create package structure
|
|
run: touch src/ble_reticulum/__init__.py
|
|
|
|
- name: Run tests
|
|
run: |
|
|
python -m 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/ble_reticulum \
|
|
--cov-report=term-missing \
|
|
--tb=short
|
|
|
|
# ============================================================================
|
|
# JOB 3: Build release artifacts
|
|
# ============================================================================
|
|
build:
|
|
name: Build Release Artifacts
|
|
runs-on: ubuntu-latest
|
|
needs: [validate, test]
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
|
|
# Extract release notes from CHANGELOG.md
|
|
- name: Extract release notes
|
|
id: release_notes
|
|
run: |
|
|
VERSION="${{ needs.validate.outputs.version }}"
|
|
|
|
# Extract section between this version and next version
|
|
sed -n "/## \[$VERSION\]/,/## \[/p" CHANGELOG.md | head -n -1 | tail -n +2 > RELEASE_NOTES.md
|
|
|
|
# Add installation instructions
|
|
cat >> RELEASE_NOTES.md << 'EOF'
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
### Quick Install (Recommended)
|
|
|
|
```bash
|
|
git clone https://github.com/torlando-tech/ble-reticulum.git
|
|
cd ble-reticulum
|
|
git checkout ${{ needs.validate.outputs.tag }}
|
|
chmod +x install.sh
|
|
./install.sh
|
|
```
|
|
|
|
### Direct Download
|
|
|
|
```bash
|
|
# Download installer
|
|
wget https://github.com/torlando-tech/ble-reticulum/releases/download/${{ needs.validate.outputs.tag }}/install.sh
|
|
chmod +x install.sh
|
|
|
|
# Download source
|
|
wget https://github.com/torlando-tech/ble-reticulum/releases/download/${{ needs.validate.outputs.tag }}/ble-reticulum-${{ needs.validate.outputs.version }}-source.tar.gz
|
|
tar xzf ble-reticulum-${{ needs.validate.outputs.version }}-source.tar.gz
|
|
cd ble-reticulum-${{ needs.validate.outputs.version }}
|
|
|
|
# Run installer
|
|
./install.sh
|
|
```
|
|
|
|
See [README.md](https://github.com/torlando-tech/ble-reticulum/blob/main/README.md) for full installation instructions.
|
|
|
|
## Upgrading
|
|
|
|
If upgrading from a previous version:
|
|
|
|
1. Pull latest code: `git pull origin main`
|
|
2. Checkout this release: `git checkout ${{ needs.validate.outputs.tag }}`
|
|
3. Re-run installer: `./install.sh`
|
|
4. Restart rnsd: `sudo systemctl restart rnsd` (or `pkill rnsd && rnsd &`)
|
|
|
|
## Verification
|
|
|
|
After installation, verify the interface is working:
|
|
|
|
```bash
|
|
rnsd --verbose # Should show BLE interface starting
|
|
rnstatus # Should list BLE interface
|
|
```
|
|
|
|
## Pre-built Wheels
|
|
|
|
Pi Zero W users with Python 3.13 will automatically receive pre-built wheels (saves ~20 minutes compilation time). See the [armv6l-wheels-v1](https://github.com/torlando-tech/ble-reticulum/releases/tag/armv6l-wheels-v1) release for details.
|
|
EOF
|
|
|
|
echo "✓ Release notes prepared"
|
|
|
|
# Create checksums for all files we'll upload
|
|
- name: Generate release artifacts
|
|
run: |
|
|
VERSION="${{ needs.validate.outputs.version }}"
|
|
mkdir -p release-artifacts
|
|
|
|
# Copy install script
|
|
cp install.sh release-artifacts/install.sh
|
|
|
|
# Copy example config
|
|
cp examples/config_example.toml release-artifacts/config_example.toml
|
|
|
|
# Create source archive
|
|
git archive --format=tar.gz --prefix=ble-reticulum-$VERSION/ HEAD > release-artifacts/ble-reticulum-$VERSION-source.tar.gz
|
|
|
|
# Create checksums
|
|
cd release-artifacts
|
|
sha256sum * > SHA256SUMS.txt
|
|
cd ..
|
|
|
|
echo "✓ Release artifacts created"
|
|
|
|
- name: Display checksums
|
|
run: |
|
|
echo "## Release Artifacts" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
cat release-artifacts/SHA256SUMS.txt >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Upload artifacts for release job
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-artifacts
|
|
path: release-artifacts/
|
|
retention-days: 5
|
|
|
|
- name: Upload release notes
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-notes
|
|
path: RELEASE_NOTES.md
|
|
retention-days: 5
|
|
|
|
# ============================================================================
|
|
# JOB 4: Create GitHub Release
|
|
# ============================================================================
|
|
release:
|
|
name: Create GitHub Release
|
|
runs-on: ubuntu-latest
|
|
needs: [validate, build]
|
|
permissions:
|
|
contents: write # Required to create releases
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: release-artifacts
|
|
path: release-artifacts/
|
|
|
|
- name: Download release notes
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: release-notes
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release create "${{ needs.validate.outputs.tag }}" \
|
|
release-artifacts/* \
|
|
--title "BLE-Reticulum ${{ needs.validate.outputs.tag }}" \
|
|
--notes-file RELEASE_NOTES.md
|
|
|
|
- name: Release summary
|
|
run: |
|
|
echo "## 🎉 Release Created!" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Version**: ${{ needs.validate.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "**URL**: https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Release Assets" >> $GITHUB_STEP_SUMMARY
|
|
echo "- ✅ install.sh" >> $GITHUB_STEP_SUMMARY
|
|
echo "- ✅ config_example.toml" >> $GITHUB_STEP_SUMMARY
|
|
echo "- ✅ ble-reticulum-${{ needs.validate.outputs.version }}-source.tar.gz" >> $GITHUB_STEP_SUMMARY
|
|
echo "- ✅ SHA256SUMS.txt" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "📦 [View Release](https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate.outputs.tag }})" >> $GITHUB_STEP_SUMMARY
|