From ca0885c9198945273db989c7a519102f0bcd569a Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Mon, 10 Nov 2025 14:10:08 -0500 Subject: [PATCH 1/3] feat: Add release infrastructure for v0.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Python packaging and automated release workflow to enable versioned releases of the BLE interface. Changes: - Add pyproject.toml with package metadata and dependencies - Add GitHub Actions release workflow with validation and artifact generation - Add CHANGELOG.md documenting v0.1.0 installation system features The release workflow validates version consistency, runs tests, generates release artifacts (installer, config, source tarball), and creates GitHub releases automatically from git tags. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/release.yml | 355 ++++++++++++++++++++++++++++++++++ CHANGELOG.md | 45 +++++ pyproject.toml | 73 +++++++ 3 files changed, 473 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 CHANGELOG.md create mode 100644 pyproject.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fe53327 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,355 @@ +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/RNS/__init__.py + touch src/RNS/Interfaces/__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/RNS/Interfaces \ + --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 + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ needs.validate.outputs.tag }} + name: "BLE-Reticulum ${{ needs.validate.outputs.tag }}" + body_path: RELEASE_NOTES.md + draft: false + prerelease: false + files: | + release-artifacts/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - 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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8dc7343 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + +All notable changes to the BLE-Reticulum project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.0] - 2025-11-10 + +### Added +- **Installation system** + - Cross-platform installer script (`install.sh`) supporting Debian, Ubuntu, Arch Linux, and Raspberry Pi OS + - ARM architecture support (32-bit armhf and 64-bit arm64) + - Custom configuration directory support via `--config` flag + - Python symlink resolution for correct interpreter detection + - Automatic PATH configuration for user installations + +- **BlueZ configuration automation** + - Automatic BlueZ experimental mode enablement (fixes BLE connection issues) + - Bluetooth adapter auto-power-on functionality + - rfkill auto-unblocking for Bluetooth devices + - Systemd service integration with proper permissions + +- **CI/CD infrastructure** + - GitHub Actions workflows for automated testing + - Multi-distribution testing matrix (Debian, Ubuntu, Arch, Raspberry Pi OS) + - ARM architecture testing on Raspberry Pi OS + - Non-interactive installation mode for CI environments + +- **Installer robustness** + - Root/non-root detection with appropriate sudo handling + - Graceful degradation when systemd unavailable + - Virtual environment detection and support + - Compatibility with PEP 668 (externally-managed-environment) + - Platform-specific dependency handling (libffi-dev for 32-bit ARM) + +### Changed +- Improved error messages and user feedback during installation +- Enhanced logging for troubleshooting installation issues + +### Fixed +- Path handling for system vs. user installations +- Permission issues with Bluetooth capabilities (setcap) +- Dependency resolution across different Linux distributions +- PyGObject version conflicts on Arch Linux diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7949d48 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,73 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "ble-reticulum" +version = "0.1.0" +description = "Bluetooth Low Energy (BLE) interface for Reticulum Network Stack" +readme = "README.md" +requires-python = ">=3.8" +license = "MIT" +authors = [ + {name = "Torlando Tech", email = "torlando-tech@users.noreply.github.com"} +] +keywords = ["reticulum", "bluetooth", "ble", "mesh", "networking"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Communications", + "Topic :: System :: Networking", +] + +# Core package has no required dependencies +# Reticulum is assumed to be installed separately +dependencies = [] + +[project.optional-dependencies] +# Linux platform support with BlueZ +linux = [ + "bleak==1.1.1", + "bluezero>=0.9.1", + "dbus-python>=1.2.18", +] + +# Development dependencies +dev = [ + "pytest>=7.0.0", + "pytest-asyncio>=0.21.0", + "pytest-cov>=4.0.0", + "pytest-timeout>=2.1.0", +] + +# Full installation with all dependencies +full = [ + "ble-reticulum[linux]", +] + +[project.urls] +Homepage = "https://github.com/torlando-tech/ble-reticulum" +Repository = "https://github.com/torlando-tech/ble-reticulum" +Issues = "https://github.com/torlando-tech/ble-reticulum/issues" + +[tool.setuptools] +packages = ["RNS.Interfaces"] +package-dir = {"" = "src"} + +[tool.setuptools.package-data] +"RNS.Interfaces" = ["*.py"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +asyncio_mode = "auto" +addopts = "-v --tb=short" From bb89096e95f3cc4fed44b7e5e682790455e47cbe Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Mon, 10 Nov 2025 14:46:22 -0500 Subject: [PATCH 2/3] fix(ci): Use gh CLI for atomic release creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace softprops/action-gh-release with gh CLI to create releases and upload assets in a single atomic operation. This prevents issues with repository rules that make releases immutable immediately, which was causing asset upload failures. Previous error: - Release created successfully but became immutable - Asset upload failed with "Cannot upload assets to an immutable release" Solution: - gh release create uploads all assets in one operation - Avoids the gap between release creation and asset upload 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/release.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fe53327..5f2e528 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -326,17 +326,13 @@ jobs: name: release-notes - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ needs.validate.outputs.tag }} - name: "BLE-Reticulum ${{ needs.validate.outputs.tag }}" - body_path: RELEASE_NOTES.md - draft: false - prerelease: false - files: | - release-artifacts/* env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + 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: | From fb31d0c200acdaf95e3d25b1a42c262f25c39b38 Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Mon, 10 Nov 2025 15:40:23 -0500 Subject: [PATCH 3/3] chore: Bump version to 0.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump version to test fixed release workflow with atomic release creation. Changes: - Update pyproject.toml version from 0.1.0 to 0.1.1 - Add CHANGELOG entry documenting release workflow fix This allows testing the corrected workflow (gh release create) without being blocked by v0.1.0 tag recreation restrictions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 5 +++++ pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dc7343..806031b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to the BLE-Reticulum project will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.1] - 2025-11-10 + +### Fixed +- **Release workflow**: Use `gh release create` for atomic release creation to prevent asset upload failures with immutable releases. Previously, `softprops/action-gh-release` created releases and uploaded assets in separate operations, which failed when repository rules made releases immutable immediately. + ## [0.1.0] - 2025-11-10 ### Added diff --git a/pyproject.toml b/pyproject.toml index 7949d48..78e8337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ble-reticulum" -version = "0.1.0" +version = "0.1.1" description = "Bluetooth Low Energy (BLE) interface for Reticulum Network Stack" readme = "README.md" requires-python = ">=3.8"