diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2bbb52e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,87 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag matches VERSION file + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + FILE_VERSION="$(cat VERSION | tr -d '[:space:]')" + if [ "$TAG_VERSION" != "$FILE_VERSION" ]; then + echo "::error::Tag $GITHUB_REF_NAME does not match VERSION file ($FILE_VERSION)" + exit 1 + fi + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Python dependencies + run: pip install -e ".[dev]" + + - name: Run tests + run: pytest --tb=short -q + + - name: Build Python package + run: | + pip install build + python -m build + + - name: Install PlatformIO + run: pip install platformio + + - name: Build firmware + run: | + cd firmware + pio run + mkdir -p ../release-artifacts + cp /tmp/kl_pio_build/esp32s3_waveshare/.pio/build/esp32s3_waveshare/firmware.bin ../release-artifacts/ 2>/dev/null || \ + cp /tmp/kl_pio_build/esp32s3_waveshare/firmware.bin ../release-artifacts/ 2>/dev/null || \ + echo "::warning::Firmware binary not found at expected path, skipping artifact" + + - name: Collect artifacts + run: | + mkdir -p release-artifacts + cp dist/*.whl release-artifacts/ 2>/dev/null || true + cp dist/*.tar.gz release-artifacts/ 2>/dev/null || true + ls -la release-artifacts/ + + - name: Generate changelog + id: changelog + run: | + # Find the previous tag + PREV_TAG=$(git tag --sort=-v:refname | grep '^v' | head -2 | tail -1) + if [ "$PREV_TAG" = "$GITHUB_REF_NAME" ] || [ -z "$PREV_TAG" ]; then + # First release — show all commits + echo "## What's Changed" > changelog.md + git log --pretty=format:"- %s (%h)" >> changelog.md + else + echo "## What's Changed since $PREV_TAG" > changelog.md + git log --pretty=format:"- %s (%h)" "$PREV_TAG".."$GITHUB_REF_NAME" >> changelog.md + fi + echo "" >> changelog.md + echo "" >> changelog.md + echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG:-initial}...${{ github.ref_name }}" >> changelog.md + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + body_path: changelog.md + files: release-artifacts/* + generate_release_notes: false + draft: false + prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta') }} diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..23c210f --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,99 @@ +# Release Process + +## Versioning Scheme + +Kill_LIFE follows [Semantic Versioning](https://semver.org/) (SemVer): + +- **MAJOR** (X.0.0) — incompatible API changes, breaking firmware protocol changes +- **MINOR** (0.X.0) — new features (MCP tools, firmware capabilities, new board support) +- **PATCH** (0.0.X) — bug fixes, documentation, non-breaking improvements + +Pre-release suffixes: `-alpha.N`, `-beta.N`, `-rc.N` + +The canonical version lives in the `VERSION` file at the repository root. +`pyproject.toml` must stay in sync. + +## Creating a Release + +1. **Prepare the release branch** (optional for patch releases): + ```bash + git checkout -b release/0.2.0 main + ``` + +2. **Update the version**: + ```bash + echo "0.2.0" > VERSION + ``` + Also update `pyproject.toml`: + ```toml + version = "0.2.0" + ``` + +3. **Commit the version bump**: + ```bash + git add VERSION pyproject.toml + git commit -m "chore: bump version to 0.2.0" + ``` + +4. **Create an annotated tag**: + ```bash + git tag -a v0.2.0 -m "Release 0.2.0 — short description of highlights" + ``` + +5. **Push the tag** (this triggers the release workflow): + ```bash + git push origin main + git push origin v0.2.0 + ``` + +6. The GitHub Actions workflow will: + - Validate the tag matches the `VERSION` file + - Run tests + - Build the Python package and firmware binary + - Create a GitHub Release with auto-generated changelog + - Attach build artifacts (`.whl`, `.tar.gz`, `firmware.bin`) + +## Hotfix Process + +For urgent fixes against a released version: + +1. **Branch from the release tag**: + ```bash + git checkout -b hotfix/0.1.1 v0.1.0 + ``` + +2. **Apply the fix**, update `VERSION` to `0.1.1`, commit. + +3. **Tag and push**: + ```bash + git tag -a v0.1.1 -m "Hotfix 0.1.1 — description of fix" + git push origin hotfix/0.1.1 + git push origin v0.1.1 + ``` + +4. **Merge the hotfix back into main**: + ```bash + git checkout main + git merge hotfix/0.1.1 + ``` + +## Backport Policy + +- **Critical security fixes**: backported to the latest minor release branch. +- **Bug fixes**: backported only if the affected version is still actively used on deployed hardware. +- **Features**: never backported. Users should upgrade to the latest minor release. + +Backport branches follow the naming convention `backport/-to-`. + +## Pre-releases + +For testing before a stable release: + +```bash +echo "0.2.0-rc.1" > VERSION +git tag -a v0.2.0-rc.1 -m "Release candidate 1 for 0.2.0" +git push origin v0.2.0-rc.1 +``` + +Pre-releases are automatically marked as such on GitHub when the tag contains +`-alpha`, `-beta`, or `-rc`. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0