From fee557330d46742c531e90eaf03e784973baafaa Mon Sep 17 00:00:00 2001 From: stopflock Date: Sat, 15 Nov 2025 13:23:37 -0600 Subject: [PATCH] Update actions workflow, disable dev mode --- .github/workflows/workflow.yml | 103 ++++++++++++++++++++++----------- DEVELOPER.md | 78 +++++++++++++++++++++++++ README.md | 3 + assets/changelog.json | 3 + lib/dev_config.dart | 2 +- 5 files changed, 155 insertions(+), 34 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 6b203cb..56b4b5f 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,33 +1,40 @@ -name: Build Release +name: Build and Release on: - push: - tags: - - '*' - workflow_dispatch: + release: + types: [published] permissions: contents: write jobs: get-version: - name: Get Version + name: Get Version and Release Info runs-on: ubuntu-latest outputs: version: ${{ steps.set-version.outputs.version }} + is_prerelease: ${{ steps.set-info.outputs.is_prerelease }} + should_upload_to_stores: ${{ steps.set-info.outputs.should_upload_to_stores }} steps: - name: Checkout repository uses: actions/checkout@v5 - - name: Get version from lib/dev_config.dart + - name: Get version from pubspec.yaml id: set-version run: | echo version=$(grep "version:" pubspec.yaml | head -1 | cut -d ':' -f 2 | tr -d ' ' | cut -d '+' -f 1) >> $GITHUB_OUTPUT -# - name: Extract version from pubspec.yaml -# id: extract_version -# run: | -# version=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r') -# echo "VERSION=$version" >> $GITHUB_ENV + - name: Determine release actions + id: set-info + run: | + echo "is_prerelease=${{ github.event.release.prerelease }}" >> $GITHUB_OUTPUT + + if [ "${{ github.event.release.prerelease }}" = "true" ]; then + echo "should_upload_to_stores=false" >> $GITHUB_OUTPUT + echo "✅ Pre-release - will build and attach assets, no store uploads" + else + echo "should_upload_to_stores=true" >> $GITHUB_OUTPUT + echo "✅ Full release - will build, attach assets, and upload to stores" + fi build-android-apk: name: Build Android APK @@ -246,27 +253,6 @@ jobs: path: Runner.ipa if-no-files-found: 'error' - - name: Upload to App Store Connect - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') - env: - APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} - APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} - APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }} - run: | - # Create the private keys directory and decode API key - mkdir -p ~/private_keys - echo -n "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8 - - # Upload to App Store Connect / TestFlight - xcrun altool --upload-app \ - --type ios \ - --file Runner.ipa \ - --apiKey $APP_STORE_CONNECT_API_KEY_ID \ - --apiIssuer $APP_STORE_CONNECT_ISSUER_ID - - # Clean up sensitive files - rm -rf ~/private_keys - attach-to-release: name: Attach Assets to Release needs: [get-version, build-android-apk, build-android-aab, build-ios] @@ -300,3 +286,54 @@ jobs: deflock_v${{ needs.get-version.outputs.version }}.apk deflock_v${{ needs.get-version.outputs.version }}.aab deflock_v${{ needs.get-version.outputs.version }}.ipa + + upload-to-stores: + name: Upload to App Stores + needs: [get-version, build-android-aab, build-ios] + runs-on: macos-latest # Need macOS for iOS uploads + if: needs.get-version.outputs.should_upload_to_stores == 'true' + steps: + - name: Download AAB artifact for Google Play + uses: actions/download-artifact@v4 + with: + name: deflock_v${{ needs.get-version.outputs.version }}.aab + + - name: Download IPA artifact for App Store + uses: actions/download-artifact@v4 + with: + name: deflock_v${{ needs.get-version.outputs.version }}.ipa + + # Temporarily disabled - uncomment when Google Play service account is ready + # - name: Upload to Google Play Store + # uses: r0adkll/upload-google-play@v1 + # with: + # serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} + # packageName: me.deflock.deflockapp + # releaseFiles: app-release.aab + # track: internal # Uploads to Internal Testing track for review before production + # status: completed + # inAppUpdatePriority: 0 + + - name: Upload to App Store Connect + env: + APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} + APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }} + run: | + # Create the private keys directory and decode API key + mkdir -p ~/private_keys + echo -n "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8 + + # Upload to App Store Connect / TestFlight + xcrun altool --upload-app \ + --type ios \ + --file Runner.ipa \ + --apiKey $APP_STORE_CONNECT_API_KEY_ID \ + --apiIssuer $APP_STORE_CONNECT_ISSUER_ID + + # Clean up sensitive files + rm -rf ~/private_keys + + - name: Clean up artifacts + run: | + rm -f app-release.aab Runner.ipa diff --git a/DEVELOPER.md b/DEVELOPER.md index b04c4e3..cafc913 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -494,6 +494,84 @@ void updateMultipleThings() { --- +## Release Process & GitHub Actions + +The app uses a **clean, release-triggered workflow** that rebuilds from scratch for maximum reliability: + +### How It Works + +**Trigger: GitHub Release Creation** +- Create a GitHub release → Workflow automatically builds, attaches assets, and optionally uploads to stores +- **Pre-release checkbox** controls store uploads: + - ✅ **Checked** → Build + attach assets (no store uploads) + - ✅ **Unchecked** → Build + attach assets + upload to App/Play stores + +### Release Types + +**Development/Beta Releases** +1. Create GitHub release from any tag/branch +2. ✅ **Check "pre-release"** checkbox +3. Publish → Assets built and attached, no store uploads + +**Production Releases** +1. Create GitHub release from main/stable branch +2. ❌ **Leave "pre-release" unchecked** +3. Publish → Assets built and attached + uploaded to stores + +### Store Upload Destinations + +**Google Play Store:** +- Uploads to **Internal Testing** track +- Requires manual promotion to Beta/Production +- You maintain full control over public release + +**App Store Connect:** +- Uploads to **TestFlight** +- Requires manual App Store submission +- You maintain full control over public release + +### Required Secrets + +**For Google Play Store Upload:** +- `GOOGLE_PLAY_SERVICE_ACCOUNT_JSON` - Complete JSON service account key (plain text) + +**For iOS App Store Upload:** +- `APP_STORE_CONNECT_API_KEY_ID` - App Store Connect API key ID +- `APP_STORE_CONNECT_ISSUER_ID` - App Store Connect issuer ID +- `APP_STORE_CONNECT_API_KEY_BASE64` - Base64-encoded .p8 API key file + +**For Building:** +- `OSM_PROD_CLIENTID` - OpenStreetMap production OAuth2 client ID +- `OSM_SANDBOX_CLIENTID` - OpenStreetMap sandbox OAuth2 client ID +- Android signing secrets (keystore, passwords, etc.) +- iOS signing certificates and provisioning profiles + +### Google Play Store Setup + +1. **Google Cloud Console:** + - Create Service Account with "Project Editor" role + - Enable Google Play Android Developer API + - Download JSON key file + +2. **Google Play Console:** + - Add service account email to Users & Permissions + - Grant "Release Manager" permissions for your app + - Complete first manual release to activate app listing + +3. **GitHub Secrets:** + - Store entire JSON key as `GOOGLE_PLAY_SERVICE_ACCOUNT_JSON` (plain text) + +### Workflow Benefits + +✅ **Brutalist simplicity** - One trigger, clear behavior +✅ **No external dependencies** - Only uses trusted `r0adkll/upload-google-play@v1` +✅ **Explicit control** - GitHub's UI checkbox controls store uploads +✅ **Always rebuilds** - No stale artifacts or cross-workflow complexity +✅ **Safe defaults** - Pre-release prevents accidental production uploads +✅ **No tag coordination** - Works with any commit, tag, or branch + +--- + ## Build & Development Setup ### Prerequisites diff --git a/README.md b/README.md index f7cfe96..70c3499 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ A comprehensive Flutter app for mapping public surveillance infrastructure with **See [DEVELOPER.md](DEVELOPER.md)** for comprehensive technical documentation including: - Architecture overview and design decisions - Development setup and build instructions +- Release process and GitHub Actions automation - Code organization and contribution guidelines - Debugging tips and troubleshooting @@ -90,6 +91,8 @@ cp lib/keys.dart.example lib/keys.dart # Add OAuth2 client IDs, then: flutter run ``` +**Releases**: The app uses GitHub's release system for automated building and store uploads. Simply create a GitHub release and use the "pre-release" checkbox to control whether builds go to app stores - checked for beta releases, unchecked for production releases. + --- ## Roadmap diff --git a/assets/changelog.json b/assets/changelog.json index de3a4b3..056b12f 100644 --- a/assets/changelog.json +++ b/assets/changelog.json @@ -1,4 +1,7 @@ { + "1.3.2": { + "content": "• INFRA: Complete release workflow redesign - now triggered by GitHub release creation\n• INFRA: Added automatic Google Play Store uploads (Internal Testing track)\n• INFRA: GitHub pre-release checkbox controls whether builds go to app stores\n• INFRA: Brutalist approach - always rebuilds from scratch for maximum reliability" + }, "1.3.1": { "content": "• UX: Network status indicator always enabled\n• UX: Direction slider wider on small screens\n• UX: Fixed iOS keyboard missing 'Done' in settings\n• UX: Fixed multi-direction nodes in upload queue\n• UX: Improved suspected locations loading indicator; removed popup, fixed stuck spinner" }, diff --git a/lib/dev_config.dart b/lib/dev_config.dart index e86b6c5..a3e1a53 100644 --- a/lib/dev_config.dart +++ b/lib/dev_config.dart @@ -44,7 +44,7 @@ const String kClientName = 'DeFlock'; const String kSuspectedLocationsCsvUrl = 'https://stopflock.com/app/flock_utilities_mini_latest.csv'; // Development/testing features - set to false for production builds -const bool kEnableDevelopmentModes = true; // Set to false to hide sandbox/simulate modes and force production mode +const bool kEnableDevelopmentModes = false; // Set to false to hide sandbox/simulate modes and force production mode // Navigation features - set to false to hide navigation UI elements while in development const bool kEnableNavigationFeatures = kEnableDevelopmentModes; // Hide navigation until fully implemented