From 023f28022316f5c00554332c9d26ff2cb2e8ba3b Mon Sep 17 00:00:00 2001 From: Devasy Patel <110348311+Devasy23@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:41:53 +0530 Subject: [PATCH 1/3] feat: Implement automated release workflow for Android APKs, including version bumping and GitHub Releases. --- .github/workflows/release.yml | 95 +++++++++++++++++++ docs/RELEASE_WORKFLOW.md | 166 ++++++++++++++++++++++++++++++++++ scripts/bump_version.dart | 74 +++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 docs/RELEASE_WORKFLOW.md create mode 100644 scripts/bump_version.dart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d85aa73 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,95 @@ +name: Release Build + +on: + push: + branches: + - main + +jobs: + release: + name: Build and Release APK + runs-on: ubuntu-latest + + permissions: + contents: write # Required to create releases and push commits + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for proper versioning + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.2' + channel: 'stable' + cache: true + + - name: Install dependencies + working-directory: ./workout-logger + run: flutter pub get + + - name: Bump version + id: bump_version + run: | + dart scripts/bump_version.dart + echo "Version bumped successfully" + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Commit version bump + id: commit_version + run: | + git add workout-logger/pubspec.yaml + git commit -m "chore: bump version to ${{ steps.bump_version.outputs.version }}" || echo "No changes to commit" + git push origin main + + - name: Create Git tag + run: | + git tag "v${{ steps.bump_version.outputs.version }}" + git push origin "v${{ steps.bump_version.outputs.version }}" + + - name: Build APK + working-directory: ./workout-logger + run: flutter build apk --release + + - name: Rename APK + run: | + mv workout-logger/build/app/outputs/flutter-apk/app-release.apk \ + workout-logger/build/app/outputs/flutter-apk/repforge-v${{ steps.bump_version.outputs.version }}.apk + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.bump_version.outputs.version }} + name: RepForge v${{ steps.bump_version.outputs.version }} + body: | + ## RepForge v${{ steps.bump_version.outputs.version }} + + ### 📱 Download + Download the APK file below to install on your Android device. + + ### 🔄 Changes + This release was automatically generated from the latest changes merged to main. + + ### 📊 Build Information + - **Version**: ${{ steps.bump_version.outputs.version }} + - **Build Date**: ${{ github.event.head_commit.timestamp }} + - **Commit**: ${{ github.sha }} + files: | + workout-logger/build/app/outputs/flutter-apk/repforge-v${{ steps.bump_version.outputs.version }}.apk + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/RELEASE_WORKFLOW.md b/docs/RELEASE_WORKFLOW.md new file mode 100644 index 0000000..0783cc9 --- /dev/null +++ b/docs/RELEASE_WORKFLOW.md @@ -0,0 +1,166 @@ +# Automated Release Workflow + +This repository uses GitHub Actions to automatically create releases when PRs are merged to the `main` branch. + +## 🚀 How It Works + +1. **PR Merge**: When a pull request is merged to `main`, the workflow triggers automatically +2. **Version Bump**: The script increments the patch version and build number in `pubspec.yaml` +3. **Commit & Tag**: Changes are committed and a new git tag is created (e.g., `v1.0.3`) +4. **Build APK**: Flutter builds a release APK for Android +5. **Create Release**: A GitHub release is created with the APK attached + +## 📋 Version Bumping Strategy + +- **Automatic**: Patch version increments on every merge (e.g., `1.0.2` → `1.0.3`) +- **Build number**: Also increments automatically (e.g., `+3` → `+4`) +- **Manual bumps**: For minor/major version changes, edit `pubspec.yaml` manually before merging + +### Version Format +```yaml +version: MAJOR.MINOR.PATCH+BUILD +# Example: 1.0.2+3 +``` + +## 🔧 Setup Instructions + +### 1. Enable GitHub Actions +Ensure GitHub Actions is enabled in your repository settings: +- Go to **Settings** → **Actions** → **General** +- Under "Actions permissions", select "Allow all actions and reusable workflows" + +### 2. Configure Branch Protection (Optional but Recommended) +If you have branch protection on `main`: +- Go to **Settings** → **Branches** → **Branch protection rules** +- Edit the rule for `main` +- Under "Allow specified actors to bypass required pull requests", add `github-actions[bot]` +- This allows the workflow to push version bump commits + +### 3. Verify Workflow Permissions +The workflow needs write permissions to create releases: +- Go to **Settings** → **Actions** → **General** +- Under "Workflow permissions", ensure "Read and write permissions" is selected +- Check "Allow GitHub Actions to create and approve pull requests" (optional) + +## 📦 First Release + +To trigger your first automated release: + +1. Create a feature branch: + ```bash + git checkout -b feature/test-release + ``` + +2. Make any change (or just update README): + ```bash + echo "# Test" >> README.md + git add README.md + git commit -m "test: trigger first automated release" + git push origin feature/test-release + ``` + +3. Create and merge a PR to `main` + +4. Check the **Actions** tab to see the workflow running + +5. Once complete, check the **Releases** section for your new release with APK + +## 📱 Installing the APK + +After each release: +1. Go to **Releases** in your GitHub repository +2. Download the latest `repforge-vX.X.X.apk` file +3. Transfer to your Android device +4. Enable "Install from unknown sources" in Android settings +5. Install the APK + +## 🔐 Production Signing (Recommended) + +Currently, the APK is signed with debug keys. For production releases: + +1. Generate a release keystore: + ```bash + keytool -genkey -v -keystore release-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release + ``` + +2. Add keystore to GitHub Secrets: + - Encode keystore: `base64 release-keystore.jks > keystore.txt` + - Add to **Settings** → **Secrets** → **Actions**: + - `KEYSTORE_BASE64`: Contents of `keystore.txt` + - `KEYSTORE_PASSWORD`: Your keystore password + - `KEY_ALIAS`: Your key alias (e.g., "release") + - `KEY_PASSWORD`: Your key password + +3. Update `android/app/build.gradle.kts` to use release signing + +4. Update the workflow to decode and use the keystore + +## 🛠️ Manual Version Bumping + +To manually control version numbers: + +### Bump Minor Version (e.g., 1.0.3 → 1.1.0) +Edit `pubspec.yaml` before merging: +```yaml +version: 1.1.0+5 +``` + +### Bump Major Version (e.g., 1.1.0 → 2.0.0) +Edit `pubspec.yaml` before merging: +```yaml +version: 2.0.0+6 +``` + +The workflow will still increment from whatever version you set. + +## 📊 Monitoring Releases + +- **Actions Tab**: View workflow runs and logs +- **Releases Tab**: See all published releases +- **Tags**: View all version tags in the repository + +## 🐛 Troubleshooting + +### Workflow fails with "Permission denied" +- Check that workflow permissions are set to "Read and write" +- Verify branch protection settings allow `github-actions[bot]` to push + +### APK not attached to release +- Check the workflow logs in the Actions tab +- Verify the build step completed successfully +- Ensure the APK path in the workflow matches the actual build output + +### Version not bumping +- Check that `scripts/bump_version.dart` has execute permissions +- Verify the script can parse your `pubspec.yaml` format +- Review workflow logs for script errors + +## 📝 Files Created + +- `.github/workflows/release.yml` - Main workflow configuration +- `scripts/bump_version.dart` - Version bumping script +- `docs/RELEASE_WORKFLOW.md` - This documentation + +## 🔄 Workflow Diagram + +``` +PR Merged to main + ↓ +Checkout code + ↓ +Setup Flutter & Java + ↓ +Bump version in pubspec.yaml + ↓ +Commit & push version change + ↓ +Create git tag (v1.0.3) + ↓ +Build release APK + ↓ +Create GitHub Release + ↓ +Upload APK to release + ↓ +✅ Done! +``` diff --git a/scripts/bump_version.dart b/scripts/bump_version.dart new file mode 100644 index 0000000..9c85c7e --- /dev/null +++ b/scripts/bump_version.dart @@ -0,0 +1,74 @@ +import 'dart:io'; + +/// Script to automatically bump version in pubspec.yaml +/// Increments patch version and build number +/// Usage: dart scripts/bump_version.dart +void main() async { + final pubspecFile = File('workout-logger/pubspec.yaml'); + + if (!await pubspecFile.exists()) { + print('Error: pubspec.yaml not found'); + exit(1); + } + + final content = await pubspecFile.readAsString(); + final lines = content.split('\n'); + + String? newVersion; + final updatedLines = []; + + for (var line in lines) { + if (line.startsWith('version:')) { + // Extract current version (format: version: 1.0.2+3) + final versionMatch = RegExp( + r'version:\s*(\d+)\.(\d+)\.(\d+)\+(\d+)', + ).firstMatch(line); + + if (versionMatch == null) { + print('Error: Could not parse version from: $line'); + exit(1); + } + + final major = int.parse(versionMatch.group(1)!); + final minor = int.parse(versionMatch.group(2)!); + final patch = int.parse(versionMatch.group(3)!); + final build = int.parse(versionMatch.group(4)!); + + // Increment patch and build + final newPatch = patch + 1; + final newBuild = build + 1; + + newVersion = '$major.$minor.$newPatch+$newBuild'; + updatedLines.add('version: $newVersion'); + + print( + 'Bumping version: ${versionMatch.group(1)}.${versionMatch.group(2)}.${versionMatch.group(3)}+${versionMatch.group(4)} → $newVersion', + ); + } else { + updatedLines.add(line); + } + } + + if (newVersion == null) { + print('Error: Version line not found in pubspec.yaml'); + exit(1); + } + + // Write updated content back to file + await pubspecFile.writeAsString(updatedLines.join('\n')); + + // Output new version for GitHub Actions to use + print('NEW_VERSION=$newVersion'); + + // Also write to GitHub Actions output if running in CI + final githubOutput = Platform.environment['GITHUB_OUTPUT']; + if (githubOutput != null) { + final outputFile = File(githubOutput); + await outputFile.writeAsString( + 'version=$newVersion\n', + mode: FileMode.append, + ); + } + + exit(0); +} From 82e61e7eb55f0343c723f9408952474939d4f9c5 Mon Sep 17 00:00:00 2001 From: Devasy Patel <110348311+Devasy23@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:52:28 +0530 Subject: [PATCH 2/3] feat: Implement automated release workflow with version bumping, APK build, and GitHub release creation. --- .github/workflows/release.yml | 14 +++++++++--- docs/RELEASE_WORKFLOW.md | 4 +++- scripts/bump_version.dart | 43 ++++++++++++++++++++++------------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d85aa73..fc3f51f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,7 @@ on: jobs: release: name: Build and Release APK + if: github.actor != 'github-actions[bot]' runs-on: ubuntu-latest permissions: @@ -52,10 +53,17 @@ jobs: id: commit_version run: | git add workout-logger/pubspec.yaml - git commit -m "chore: bump version to ${{ steps.bump_version.outputs.version }}" || echo "No changes to commit" - git push origin main + if git diff --staged --quiet; then + echo "No changes to commit" + echo "committed=false" >> $GITHUB_OUTPUT + else + git commit -m "chore: bump version to ${{ steps.bump_version.outputs.version }}" + git push origin main + echo "committed=true" >> $GITHUB_OUTPUT + fi - name: Create Git tag + if: steps.commit_version.outputs.committed == 'true' run: | git tag "v${{ steps.bump_version.outputs.version }}" git push origin "v${{ steps.bump_version.outputs.version }}" @@ -70,7 +78,7 @@ jobs: workout-logger/build/app/outputs/flutter-apk/repforge-v${{ steps.bump_version.outputs.version }}.apk - name: Create GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.bump_version.outputs.version }} name: RepForge v${{ steps.bump_version.outputs.version }} diff --git a/docs/RELEASE_WORKFLOW.md b/docs/RELEASE_WORKFLOW.md index 0783cc9..318c519 100644 --- a/docs/RELEASE_WORKFLOW.md +++ b/docs/RELEASE_WORKFLOW.md @@ -17,6 +17,7 @@ This repository uses GitHub Actions to automatically create releases when PRs ar - **Manual bumps**: For minor/major version changes, edit `pubspec.yaml` manually before merging ### Version Format + ```yaml version: MAJOR.MINOR.PATCH+BUILD # Example: 1.0.2+3 @@ -25,6 +26,7 @@ version: MAJOR.MINOR.PATCH+BUILD ## 🔧 Setup Instructions ### 1. Enable GitHub Actions + Ensure GitHub Actions is enabled in your repository settings: - Go to **Settings** → **Actions** → **General** - Under "Actions permissions", select "Allow all actions and reusable workflows" @@ -143,7 +145,7 @@ The workflow will still increment from whatever version you set. ## 🔄 Workflow Diagram -``` +```text PR Merged to main ↓ Checkout code diff --git a/scripts/bump_version.dart b/scripts/bump_version.dart index 9c85c7e..48501ae 100644 --- a/scripts/bump_version.dart +++ b/scripts/bump_version.dart @@ -1,9 +1,10 @@ import 'dart:io'; /// Script to automatically bump version in pubspec.yaml -/// Increments patch version and build number -/// Usage: dart scripts/bump_version.dart -void main() async { +/// Increments build number by default. +/// Increments patch version ONLY if 'patch' argument is provided. +/// Usage: dart scripts/bump_version.dart [patch] +void main(List args) async { final pubspecFile = File('workout-logger/pubspec.yaml'); if (!await pubspecFile.exists()) { @@ -17,11 +18,15 @@ void main() async { String? newVersion; final updatedLines = []; + // Check if patch bump is requested + final shouldBumpPatch = args.contains('patch'); + for (var line in lines) { if (line.startsWith('version:')) { - // Extract current version (format: version: 1.0.2+3) + // Extract current version (format: version: 1.0.2+3 or 1.0.2) + // Group 1: Major, Group 2: Minor, Group 3: Patch, Group 4: Build (optional) final versionMatch = RegExp( - r'version:\s*(\d+)\.(\d+)\.(\d+)\+(\d+)', + r'version:\s*(\d+)\.(\d+)\.(\d+)(?:\+(\d+))?', ).firstMatch(line); if (versionMatch == null) { @@ -32,18 +37,20 @@ void main() async { final major = int.parse(versionMatch.group(1)!); final minor = int.parse(versionMatch.group(2)!); final patch = int.parse(versionMatch.group(3)!); - final build = int.parse(versionMatch.group(4)!); + final build = int.parse(versionMatch.group(4) ?? '0'); - // Increment patch and build - final newPatch = patch + 1; + // Calculate new versions + final newPatch = shouldBumpPatch ? patch + 1 : patch; final newBuild = build + 1; newVersion = '$major.$minor.$newPatch+$newBuild'; updatedLines.add('version: $newVersion'); - print( - 'Bumping version: ${versionMatch.group(1)}.${versionMatch.group(2)}.${versionMatch.group(3)}+${versionMatch.group(4)} → $newVersion', - ); + final oldVersionStr = + '${versionMatch.group(1)}.${versionMatch.group(2)}.${versionMatch.group(3)}' + + (versionMatch.group(4) != null ? '+${versionMatch.group(4)}' : ''); + + print('Bumping version: $oldVersionStr → $newVersion'); } else { updatedLines.add(line); } @@ -63,11 +70,15 @@ void main() async { // Also write to GitHub Actions output if running in CI final githubOutput = Platform.environment['GITHUB_OUTPUT']; if (githubOutput != null) { - final outputFile = File(githubOutput); - await outputFile.writeAsString( - 'version=$newVersion\n', - mode: FileMode.append, - ); + try { + final outputFile = File(githubOutput); + await outputFile.writeAsString( + 'version=$newVersion\n', + mode: FileMode.append, + ); + } catch (e) { + print('Warning: Could not write to GITHUB_OUTPUT: $e'); + } } exit(0); From 961b41afd9338d1394e755cab3aca2bfa033945c Mon Sep 17 00:00:00 2001 From: Devasy Patel <110348311+Devasy@users.noreply.github.com> Date: Tue, 27 Jan 2026 01:01:04 +0530 Subject: [PATCH 3/3] Update .github/workflows/release.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc3f51f..f6f3e65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: - name: Bump version id: bump_version run: | - dart scripts/bump_version.dart + dart scripts/bump_version.dart patch echo "Version bumped successfully" - name: Configure Git