Merge pull request #35 from DavidAmunga/changeset-release/main #35
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Main Release" | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| force_release: | |
| description: "Force a release even if no changesets" | |
| required: false | |
| default: false | |
| type: boolean | |
| release_type: | |
| description: "Type of release" | |
| required: false | |
| default: "auto" | |
| type: choice | |
| options: | |
| - auto | |
| - patch | |
| - minor | |
| - major | |
| concurrency: | |
| group: main-release | |
| cancel-in-progress: false | |
| jobs: | |
| # Step 1: Determine if we should release | |
| check-release: | |
| name: "Check Release Status" | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_release: ${{ steps.check.outputs.should_release }} | |
| version: ${{ steps.check.outputs.version }} | |
| has_changesets: ${{ steps.check.outputs.has_changesets }} | |
| is_changeset_release: ${{ steps.check.outputs.is_changeset_release }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: lts/* | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Check release conditions | |
| id: check | |
| run: | | |
| # Check if this is a changeset release commit | |
| COMMIT_MSG=$(git log -1 --pretty=format:"%s") | |
| echo "Latest commit: $COMMIT_MSG" | |
| # Check recent commits for changeset patterns (look deeper for merge commits) | |
| RECENT_COMMITS=$(git log -5 --pretty=format:"%s") | |
| echo "Recent commits:" | |
| echo "$RECENT_COMMITS" | |
| IS_CHANGESET_RELEASE="false" | |
| if [[ "$COMMIT_MSG" == *"chore: release version packages"* ]] || \ | |
| [[ "$COMMIT_MSG" == *"Version Packages"* ]] || \ | |
| [[ "$COMMIT_MSG" == *"changeset-release/main"* ]] || \ | |
| [[ "$RECENT_COMMITS" == *"chore: release version packages"* ]] || \ | |
| [[ "$RECENT_COMMITS" == *"Version Packages"* ]]; then | |
| IS_CHANGESET_RELEASE="true" | |
| echo "✅ This is a changeset release commit" | |
| fi | |
| # Check for pending changesets | |
| HAS_CHANGESETS="false" | |
| if [ -d ".changeset" ] && [ "$(ls -1 .changeset/*.md 2>/dev/null | wc -l)" -gt 0 ]; then | |
| # Exclude README.md and config.json | |
| CHANGESET_COUNT=$(ls -1 .changeset/*.md 2>/dev/null | grep -v README.md | wc -l) | |
| if [ "$CHANGESET_COUNT" -gt 0 ]; then | |
| HAS_CHANGESETS="true" | |
| echo "✅ Found $CHANGESET_COUNT pending changesets" | |
| fi | |
| fi | |
| # Additional check: Look for version changes in the last commit | |
| VERSION_CHANGED="false" | |
| if git diff HEAD~1 HEAD --name-only | grep -q "package.json\|src-tauri/Cargo.toml\|src-tauri/tauri.conf.json"; then | |
| echo "📦 Version files changed in recent commit" | |
| VERSION_CHANGED="true" | |
| fi | |
| # Determine if we should release | |
| SHOULD_RELEASE="false" | |
| if [[ "$IS_CHANGESET_RELEASE" == "true" ]] || \ | |
| [[ "$VERSION_CHANGED" == "true" && "$IS_CHANGESET_RELEASE" == "true" ]] || \ | |
| [[ "${{ github.event.inputs.force_release }}" == "true" ]] || \ | |
| [[ "$HAS_CHANGESETS" == "true" && "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| SHOULD_RELEASE="true" | |
| echo "✅ Release conditions met" | |
| else | |
| echo "ℹ️ No release needed" | |
| fi | |
| # Get current version | |
| VERSION=$(node -p "require('./package.json').version") | |
| echo "should_release=$SHOULD_RELEASE" >> $GITHUB_OUTPUT | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "has_changesets=$HAS_CHANGESETS" >> $GITHUB_OUTPUT | |
| echo "is_changeset_release=$IS_CHANGESET_RELEASE" >> $GITHUB_OUTPUT | |
| # Step 2: Handle changesets (version bumping) | |
| handle-changesets: | |
| name: "Handle Changesets" | |
| runs-on: ubuntu-latest | |
| needs: check-release | |
| if: needs.check-release.outputs.has_changesets == 'true' && needs.check-release.outputs.is_changeset_release == 'false' | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: lts/* | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Create Release Pull Request | |
| uses: changesets/action@v1 | |
| with: | |
| version: pnpm run version | |
| title: "chore: release version packages" | |
| commit: "chore: release version packages" | |
| createGithubReleases: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Step 3: Sync versions and create tag | |
| prepare-release: | |
| name: "Prepare Release" | |
| runs-on: ubuntu-latest | |
| needs: check-release | |
| if: needs.check-release.outputs.should_release == 'true' | |
| permissions: | |
| contents: write | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| tag: ${{ steps.version.outputs.tag }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: lts/* | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Sync versions | |
| run: | | |
| echo "📦 Syncing all version files..." | |
| pnpm run sync-versions | |
| # Check if there are any changes to commit | |
| if ! git diff --quiet; then | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add . | |
| git commit -m "chore: sync version files" | |
| git push | |
| echo "✅ Version files synced and committed" | |
| else | |
| echo "ℹ️ No version sync changes needed" | |
| fi | |
| - name: Get version and create tag | |
| id: version | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| TAG="v$VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| # Configure git | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Check if tag exists locally | |
| if ! git tag -l "$TAG" | grep -q "$TAG"; then | |
| # Check if tag exists remotely | |
| if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then | |
| echo "ℹ️ Tag $TAG already exists remotely, fetching it" | |
| git fetch origin "refs/tags/$TAG:refs/tags/$TAG" | |
| else | |
| echo "🏷️ Creating new tag: $TAG" | |
| git tag -a "$TAG" -m "Release $TAG" | |
| git push origin "$TAG" | |
| echo "✅ Created and pushed tag: $TAG" | |
| fi | |
| else | |
| echo "ℹ️ Tag $TAG already exists locally" | |
| fi | |
| # Step 4: Build and release | |
| build-and-release: | |
| name: "Build & Release" | |
| needs: [check-release, prepare-release] | |
| if: needs.check-release.outputs.should_release == 'true' | |
| permissions: | |
| contents: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: "macos-latest" | |
| args: "--target aarch64-apple-darwin" | |
| target: "aarch64-apple-darwin" | |
| - platform: "macos-latest" | |
| args: "--target x86_64-apple-darwin" | |
| target: "x86_64-apple-darwin" | |
| - platform: "ubuntu-22.04" | |
| args: "" | |
| target: "x86_64-unknown-linux-gnu" | |
| - platform: "windows-latest" | |
| args: "" | |
| target: "x86_64-pc-windows-msvc" | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: lts/* | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install system dependencies (Ubuntu) | |
| if: matrix.platform == 'ubuntu-22.04' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf | |
| - name: Install create-dmg (macOS) | |
| if: matrix.platform == 'macos-latest' | |
| run: | | |
| brew install create-dmg | |
| - name: Import Apple Code Signing Certificate | |
| if: matrix.platform == 'macos-latest' | |
| run: | | |
| # Create keychain | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| # Import certificate | |
| echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12 | |
| security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain | |
| # Clean up | |
| rm certificate.p12 | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build frontend | |
| run: pnpm build | |
| - name: Extract changelog for release | |
| id: changelog | |
| shell: bash | |
| continue-on-error: false | |
| run: | | |
| set +e # Disable exit on error for this script | |
| VERSION="${{ needs.prepare-release.outputs.version }}" | |
| if [ -f "CHANGELOG.md" ]; then | |
| # Extract the changelog section for the current version | |
| CHANGELOG_CONTENT=$(awk -v version="$VERSION" ' | |
| /^## / { | |
| if (found) exit | |
| if ($0 ~ "## .*" version) found=1 | |
| next | |
| } | |
| found && /^## / { exit } | |
| found { print } | |
| ' CHANGELOG.md | sed '/^$/d' | head -30) | |
| # Debug output | |
| echo "Debug: VERSION=$VERSION" | |
| echo "Debug: CHANGELOG_CONTENT length: $(echo "$CHANGELOG_CONTENT" | wc -c | tr -d ' ')" | |
| if [ -n "$CHANGELOG_CONTENT" ]; then | |
| echo "Debug: Found changelog content for version $VERSION" | |
| else | |
| echo "Debug: No changelog content found for version $VERSION" | |
| fi | |
| # Get previous version for comparison link | |
| PREVIOUS_VERSION=$(awk '/^## / && !/'"$VERSION"'/ { print $2; exit }' CHANGELOG.md) | |
| if [ -n "$CHANGELOG_CONTENT" ]; then | |
| # Analyze change types - handle both "feat:" and "hash: feat:" formats | |
| FEATURES=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?feat:" | wc -l | tr -d ' ') | |
| FIXES=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?fix:" | wc -l | tr -d ' ') | |
| BREAKING=$(echo "$CHANGELOG_CONTENT" | grep -E "BREAKING CHANGE|!" | wc -l | tr -d ' ') | |
| DOCS=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?docs:" | wc -l | tr -d ' ') | |
| PERF=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?perf:" | wc -l | tr -d ' ') | |
| REFACTOR=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?refactor:" | wc -l | tr -d ' ') | |
| CHORE=$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?chore:" | wc -l | tr -d ' ') | |
| SECURITY=$(echo "$CHANGELOG_CONTENT" | grep -i "security\|vulnerability\|cve\|exploit" | wc -l | tr -d ' ') | |
| # Count total changes | |
| TOTAL_CHANGES=$(echo "$CHANGELOG_CONTENT" | grep "^-" | wc -l | tr -d ' ') | |
| # Create formatted changelog with emojis | |
| FORMATTED_CHANGELOG="" | |
| if [ "$FEATURES" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### ✨ New Features ($FEATURES)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?feat:" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$FIXES" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### 🐛 Bug Fixes ($FIXES)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?fix:" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$BREAKING" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### ⚠️ Breaking Changes ($BREAKING)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "BREAKING CHANGE|!" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$PERF" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### ⚡ Performance Improvements ($PERF)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?perf:" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$REFACTOR" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### 🔧 Code Refactoring ($REFACTOR)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?refactor:" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$DOCS" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### 📚 Documentation ($DOCS)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -E "(^- [a-f0-9]+: )?docs:" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| if [ "$SECURITY" -gt 0 ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### 🔒 Security Updates ($SECURITY)"$'\n'"$(echo "$CHANGELOG_CONTENT" | grep -i "security\|vulnerability\|cve\|exploit" | sed 's/^- /- /')"$'\n\n' | |
| fi | |
| # Add other changes if any | |
| OTHER_CHANGES=$(echo "$CHANGELOG_CONTENT" | grep -v -E "feat:|fix:|BREAKING CHANGE|!|perf:|refactor:|docs:|chore:|security|vulnerability|cve|exploit" | grep "^-" || true) | |
| if [ -n "$OTHER_CHANGES" ]; then | |
| FORMATTED_CHANGELOG="${FORMATTED_CHANGELOG}### 🔄 Other Changes"$'\n'"$OTHER_CHANGES"$'\n\n' | |
| fi | |
| # If no categorized changes found, use original content | |
| if [ -z "$FORMATTED_CHANGELOG" ]; then | |
| FORMATTED_CHANGELOG="$CHANGELOG_CONTENT" | |
| fi | |
| echo "changelog<<EOF" >> $GITHUB_OUTPUT | |
| echo "$FORMATTED_CHANGELOG" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| # Output change statistics | |
| echo "features=$FEATURES" >> $GITHUB_OUTPUT | |
| echo "fixes=$FIXES" >> $GITHUB_OUTPUT | |
| echo "breaking=$BREAKING" >> $GITHUB_OUTPUT | |
| echo "docs=$DOCS" >> $GITHUB_OUTPUT | |
| echo "perf=$PERF" >> $GITHUB_OUTPUT | |
| echo "refactor=$REFACTOR" >> $GITHUB_OUTPUT | |
| echo "security=$SECURITY" >> $GITHUB_OUTPUT | |
| echo "total_changes=$TOTAL_CHANGES" >> $GITHUB_OUTPUT | |
| else | |
| echo "changelog=See CHANGELOG.md for details." >> $GITHUB_OUTPUT | |
| echo "features=0" >> $GITHUB_OUTPUT | |
| echo "fixes=0" >> $GITHUB_OUTPUT | |
| echo "breaking=0" >> $GITHUB_OUTPUT | |
| echo "security=0" >> $GITHUB_OUTPUT | |
| echo "total_changes=0" >> $GITHUB_OUTPUT | |
| fi | |
| echo "previous_version=$PREVIOUS_VERSION" >> $GITHUB_OUTPUT | |
| else | |
| echo "changelog=No changelog available." >> $GITHUB_OUTPUT | |
| echo "previous_version=0.0.2" >> $GITHUB_OUTPUT | |
| echo "features=0" >> $GITHUB_OUTPUT | |
| echo "fixes=0" >> $GITHUB_OUTPUT | |
| echo "breaking=0" >> $GITHUB_OUTPUT | |
| echo "security=0" >> $GITHUB_OUTPUT | |
| echo "total_changes=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Generate build timestamp | |
| id: timestamp | |
| run: echo "build_time=$(date -u +"%Y-%m-%d %H:%M UTC")" >> $GITHUB_OUTPUT | |
| - name: Build and release Tauri app | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| TAURI_BUNDLE_MACOS_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| CI: true | |
| with: | |
| tagName: ${{ needs.prepare-release.outputs.tag }} | |
| releaseName: "mpesa2csv ${{ needs.prepare-release.outputs.tag }}" | |
| releaseBody: | | |
| ## 🚀 What's New in v${{ needs.prepare-release.outputs.version }} | |
| ${{ steps.changelog.outputs.total_changes > 0 && format('📊 **Release Summary**: {0} changes', steps.changelog.outputs.total_changes) || '' }} | |
| ${{ steps.changelog.outputs.breaking > 0 && '> ⚠️ **BREAKING CHANGES**: This release contains breaking changes. Please review the changelog carefully before updating.' || '' }} | |
| ${{ steps.changelog.outputs.security > 0 && '> 🔒 **SECURITY UPDATE**: This release includes important security fixes. We recommend updating as soon as possible.' || '' }} | |
| ${{ steps.changelog.outputs.changelog }} | |
| ${{ steps.changelog.outputs.features > 0 && steps.changelog.outputs.fixes > 0 && format('🎯 **Highlights**: This release brings {0} new features and fixes {1} bugs for a better user experience.', steps.changelog.outputs.features, steps.changelog.outputs.fixes) || '' }} | |
| ${{ steps.changelog.outputs.features > 0 && steps.changelog.outputs.fixes == 0 && format('✨ **Feature Release**: Introducing {0} new features to enhance your workflow.', steps.changelog.outputs.features) || '' }} | |
| ${{ steps.changelog.outputs.features == 0 && steps.changelog.outputs.fixes > 0 && format('🔧 **Maintenance Release**: This update focuses on stability with {0} bug fixes.', steps.changelog.outputs.fixes) || '' }} | |
| --- | |
| ## 📥 Download & Installation | |
| Choose the appropriate download for your platform: | |
| | Platform | Architecture | Download | Installation | | |
| |----------|-------------|----------|--------------| | |
| | **Windows** | x64 (Intel/AMD) | [📥 Download .exe](https://github.com/DavidAmunga/mpesa2csv/releases/download/${{ needs.prepare-release.outputs.tag }}/mpesa2csv_${{ needs.prepare-release.outputs.version }}_x64-setup.exe) | Run the installer and follow the setup wizard | | |
| | **macOS** | Apple Silicon (M1/M2/M3) | [📥 Download .dmg](https://github.com/DavidAmunga/mpesa2csv/releases/download/${{ needs.prepare-release.outputs.tag }}/mpesa2csv_${{ needs.prepare-release.outputs.version }}_aarch64.dmg) | Open DMG and drag to Applications | | |
| | **macOS** | Intel x64 | [📥 Download .dmg](https://github.com/DavidAmunga/mpesa2csv/releases/download/${{ needs.prepare-release.outputs.tag }}/mpesa2csv_${{ needs.prepare-release.outputs.version }}_x64.dmg) | Open DMG and drag to Applications | | |
| | **Linux** | x64 (Ubuntu/Debian) | [📥 Download .deb](https://github.com/DavidAmunga/mpesa2csv/releases/download/${{ needs.prepare-release.outputs.tag }}/mpesa2csv_${{ needs.prepare-release.outputs.version }}_amd64.deb) | `sudo dpkg -i mpesa2csv_*.deb` | | |
| | **Linux** | x64 (Portable) | [📥 Download .AppImage](https://github.com/DavidAmunga/mpesa2csv/releases/download/${{ needs.prepare-release.outputs.tag }}/mpesa2csv_${{ needs.prepare-release.outputs.version }}_amd64.AppImage) | `chmod +x mpesa2csv_*.AppImage && ./mpesa2csv_*.AppImage` | | |
| ### 📊 Release Metadata | |
| - **Version**: `${{ needs.prepare-release.outputs.version }}` | |
| - **Build Date**: `${{ steps.timestamp.outputs.build_time }}` | |
| - **Total Changes**: ${{ steps.changelog.outputs.total_changes }} | |
| - **Platforms**: Windows, macOS (Intel + Apple Silicon), Linux | |
| ${{ steps.changelog.outputs.breaking > 0 && format('- **Breaking Changes**: {0}', steps.changelog.outputs.breaking) || '' }} | |
| ### 🔍 System Requirements | |
| - **Windows**: Windows 10 version 1903 or later | |
| - **macOS**: macOS 10.15 Catalina or later | |
| - **Linux**: Modern distribution with GTK 3.24+ and WebKit2GTK 4.1+ | |
| ### 🔄 Auto-Update | |
| > **Note**: If you have a previous version installed, the app will automatically notify you when this update is available and guide you through the update process. | |
| --- | |
| **Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ steps.changelog.outputs.previous_version || '0.0.2' }}...v${{ needs.prepare-release.outputs.version }} | |
| releaseDraft: false | |
| prerelease: false | |
| args: ${{ matrix.args }} | |
| # Step 5: Create release branch for hotfixes | |
| create-release-branch: | |
| name: "Create Release Branch" | |
| needs: [check-release, prepare-release, build-and-release] | |
| if: needs.check-release.outputs.should_release == 'true' | |
| uses: ./.github/workflows/reusable-create-release-branch.yml | |
| with: | |
| version: ${{ needs.prepare-release.outputs.tag }} | |
| max_branches: 5 | |
| secrets: inherit | |
| # Step 6: Notify about release completion | |
| notify-completion: | |
| name: "Release Complete" | |
| runs-on: ubuntu-latest | |
| needs: [prepare-release, build-and-release, create-release-branch] | |
| if: always() && needs.prepare-release.result == 'success' | |
| steps: | |
| - name: Release summary | |
| run: | | |
| echo "🎉 Release ${{ needs.prepare-release.outputs.tag }} completed!" | |
| echo "✅ Binaries built and published" | |
| echo "✅ Release branch created for hotfixes" | |
| echo "🔗 View release: https://github.com/${{ github.repository }}/releases/tag/${{ needs.prepare-release.outputs.tag }}" |