From 7559434f86bab95dae9b05b7ac7de89ac345a0cb Mon Sep 17 00:00:00 2001 From: otsmr Date: Thu, 28 May 2026 13:59:14 +0200 Subject: [PATCH] switch to fastlane for github and f-droid releases --- .github/workflows/release_github.yml | 68 --------- .gitignore | 3 +- android/.gitignore | 2 + android/key.github.properties.example | 8 ++ fastlane/Fastfile | 129 ++++++++++++++++++ .../services/api/mediafiles/upload.api.dart | 2 +- 6 files changed, 142 insertions(+), 70 deletions(-) delete mode 100644 .github/workflows/release_github.yml create mode 100644 android/key.github.properties.example diff --git a/.github/workflows/release_github.yml b/.github/workflows/release_github.yml deleted file mode 100644 index ea0a9a8f..00000000 --- a/.github/workflows/release_github.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Publish on Github - -on: - workflow_dispatch: {} - push: - branches: - - main - paths: - - pubspec.yaml - -jobs: - build_and_publish: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Install requirements - run: sudo apt-get install protobuf-compiler - - - name: Cloning sub-repos - run: git submodule update --init --recursive - - # - name: Check flutter code - # run: | - # flutter pub get - # flutter analyze - # flutter test - - - name: Check flutter code - run: flutter pub get - - - name: Create key.properties file - run: | - echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> ./android/key.properties - echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> ./android/key.properties - echo "keyAlias=github-releases-signature" >> ./android/key.properties - echo "storeFile=./keystore.jks" >> ./android/key.properties - - - name: Create keystore file - env: - KEYSTORE_FILE: ${{ secrets.KEYSTORE_FILE }} - run: echo $KEYSTORE_FILE | base64 --decode > ./android/app/keystore.jks - - - name: Build Android APK - run: flutter build apk --release --split-per-abi - - - name: Extract pubspec version - run: | - echo "PUBSPEC_VERSION=$(grep -oP 'version:\s*\K[^+]+(?=\+)' pubspec.yaml)" >> $GITHUB_ENV - - - name: Upload Release Binaries (stable) - uses: ncipollo/release-action@v1.18.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - tag: v${{ env.PUBSPEC_VERSION }} - allowUpdates: true - artifacts: build/app/outputs/flutter-apk/*.apk \ No newline at end of file diff --git a/.gitignore b/.gitignore index f87ae92b..f6e6d121 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,5 @@ app.*.map.json android/.kotlin/ devtools_options.yaml rust/target -rust_dependencies/target \ No newline at end of file +rust_dependencies/target +fastlane/repo/status/running.json diff --git a/android/.gitignore b/android/.gitignore index 55afd919..8da9e3f0 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -9,5 +9,7 @@ GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. # See https://flutter.dev/to/reference-keystore key.properties +key.github.properties +key.properties.backup **/*.keystore **/*.jks diff --git a/android/key.github.properties.example b/android/key.github.properties.example new file mode 100644 index 00000000..934506b5 --- /dev/null +++ b/android/key.github.properties.example @@ -0,0 +1,8 @@ +# Example signing credentials configuration for GitHub Releases. +# Copy this file to 'key.github.properties' and fill in your actual credentials. +# Do not commit the actual 'key.github.properties' file to version control. + +storePassword=YOUR_GITHUB_RELEASE_STORE_PASSWORD +keyPassword=YOUR_GITHUB_RELEASE_KEY_PASSWORD +keyAlias=github-releases-signature +storeFile=/absolute/path/to/your/github-release-keystore.jks diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 966178d2..e8ca079f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -12,4 +12,133 @@ platform :android do skip_upload_screenshots: true ) end + + desc "Build the application locally and upload it as a new GitHub release" + lane :release_github do + # Read pubspec.yaml to get the version + pubspec_path = File.expand_path("../pubspec.yaml", __dir__) + unless File.exist?(pubspec_path) + UI.user_error!("Could not find pubspec.yaml at #{pubspec_path}") + end + + pubspec_content = File.read(pubspec_path) + version_match = pubspec_content.match(/^version:\s*([^+]+)/) + unless version_match + UI.user_error!("Could not extract version from pubspec.yaml") + end + + version = version_match[1].strip + tag_name = "v#{version}" + UI.message("Extracted version: #{version} (tag: #{tag_name})") + + # Load release notes from CHANGELOG.md + changelog_path = File.expand_path("../CHANGELOG.md", __dir__) + release_notes = "Automated local release via Fastlane" + if File.exist?(changelog_path) + changelog_content = File.read(changelog_path) + escaped_version = Regexp.escape(version) + pattern = /##\s*\[?#{escaped_version}\]?(.*?)(?=##\s*|\z)/m + match = changelog_content.match(pattern) + if match + release_notes = match[1].strip + UI.message("Loaded release notes from CHANGELOG.md:\n#{release_notes}") + else + UI.important("Could not find release notes for version #{version} in CHANGELOG.md. Using default description.") + end + else + UI.important("CHANGELOG.md not found at #{changelog_path}. Using default description.") + end + + # Handle key.properties swapping if key.github.properties exists + key_properties_path = File.expand_path("../android/key.properties", __dir__) + github_properties_path = File.expand_path("../android/key.github.properties", __dir__) + backup_properties_path = File.expand_path("../android/key.properties.backup", __dir__) + + swapped_properties = false + if File.exist?(github_properties_path) + UI.message("Found key.github.properties. Swapping in for the build...") + if File.exist?(key_properties_path) + FileUtils.cp(key_properties_path, backup_properties_path) + end + FileUtils.cp(github_properties_path, key_properties_path) + swapped_properties = true + else + UI.message("No key.github.properties found. Building with default key.properties...") + end + + begin + # Build the Android application + UI.message("Building Android APK...") + Dir.chdir(File.expand_path("..", __dir__)) do + sh("flutter build apk --release --split-per-abi") + end + ensure + # Restore original key.properties if swapped + if swapped_properties + UI.message("Restoring original key.properties...") + if File.exist?(backup_properties_path) + FileUtils.cp(backup_properties_path, key_properties_path) + FileUtils.rm(backup_properties_path) + else + FileUtils.rm_f(key_properties_path) + end + end + end + + # Find built APKs + apk_glob = File.expand_path("../build/app/outputs/flutter-apk/*-release.apk", __dir__) + apks = Dir.glob(apk_glob) + + if apks.empty? + UI.user_error!("No release APKs found matching #{apk_glob}") + end + + UI.message("Found APKs to upload: #{apks.join(', ')}") + + # Retrieve GitHub Token (fall back to gh auth token) + github_token = ENV["GITHUB_TOKEN"] + if github_token.nil? || github_token.empty? + UI.message("GITHUB_TOKEN env variable not set. Retrieving token via GitHub CLI (gh auth token)...") + begin + github_token = sh("gh auth token").strip + rescue => e + UI.user_error!("Failed to retrieve token from gh CLI. Make sure gh is installed and authenticated, or GITHUB_TOKEN environment variable is set. Error: #{e}") + end + end + + UI.message("Creating GitHub Release #{tag_name}...") + set_github_release( + repository_name: "twonlyapp/twonly-app", + api_token: github_token, + tag_name: tag_name, + name: "Release #{tag_name}", + description: release_notes, + upload_assets: apks + ) + UI.success("Successfully uploaded release #{tag_name} to GitHub!") + + # F-Droid deployment + fdroid_repo_dir = "/Users/tobi/Documents/drive/twonly/F-Droid/repo" + UI.message("Starting F-Droid deployment...") + FileUtils.mkdir_p(fdroid_repo_dir) + + apks.each do |apk_path| + basename = File.basename(apk_path) + new_name = "eu.twonly_v#{version}-#{basename}" + dest_path = File.join(fdroid_repo_dir, new_name) + UI.message("Copying APK to F-Droid repo: #{dest_path}") + FileUtils.cp(apk_path, dest_path) + end + + fdroid_dir = "/Users/tobi/Documents/drive/twonly/F-Droid" + update_script = File.join(fdroid_dir, "update.sh") + if File.exist?(update_script) + UI.message("Executing F-Droid update script...") + Dir.chdir(fdroid_dir) do + sh("chmod +x ./update.sh && ./update.sh") + end + else + UI.important("F-Droid update script not found at #{update_script}") + end + end end diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart index 4fac6607..deb7be04 100644 --- a/lib/src/services/api/mediafiles/upload.api.dart +++ b/lib/src/services/api/mediafiles/upload.api.dart @@ -51,7 +51,7 @@ Future reuploadMediaFiles() async { final contacts = {}; - for (var receipt in receipts) { + for (final receipt in receipts) { if (receipt.retryCount > 1 && receipt.lastRetry != null) { final twentyFourHoursAgo = DateTime.now().subtract( const Duration(hours: 6),