diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml new file mode 100644 index 0000000..56e15b7 --- /dev/null +++ b/.github/workflows/android-release.yml @@ -0,0 +1,128 @@ +# Publish Refetch to Google Play. +# +# Builds a signed App Bundle and uploads it through the Fastlane lanes in +# `android/fastlane/`. Manual only — there is no push trigger. +# +# Repository secrets (Settings → Secrets and variables → Actions): +# +# APP_IDENTIFIER Android applicationId (io.appwrite.refetch) +# PLAY_STORE_JSON_KEY_DATA Base64 Google Play service account JSON key +# ANDROID_KEYSTORE_BASE64 Base64 release keystore (.jks) +# ANDROID_KEYSTORE_PASSWORD Keystore password +# ANDROID_KEY_ALIAS Key alias inside the keystore +# ANDROID_KEY_PASSWORD Key password +# +# Encode the binary secrets with `base64 -w0 file` (Linux) or +# `base64 -i file` (macOS). + +name: Android Release + +on: + workflow_dispatch: + inputs: + release_type: + description: "Play Store track to publish to" + required: true + type: choice + default: internal + options: + - internal + - beta + - release + release_status: + description: "Release status (use 'draft' for the first-ever release on a draft app)" + required: false + type: choice + default: completed + options: + - completed + - draft + - halted + - inProgress + +jobs: + publish: + name: Build and upload to Play Store (${{ inputs.release_type }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + + env: + # Read by android/fastlane/{Appfile,Fastfile} and the play_publisher plugin. + APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }} + PLAY_STORE_JSON_KEY_DATA: ${{ secrets.PLAY_STORE_JSON_KEY_DATA }} + SUPPLY_RELEASE_STATUS: ${{ inputs.release_status }} + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + # Pinned to the version we develop against locally — bump in lockstep. + flutter-version: "3.44.6" + channel: stable + cache: true + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + bundler-cache: true + working-directory: android + + - name: Decode release keystore + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + mkdir -p android/app/keystore + KS="$PWD/android/app/keystore/upload-keystore.jks" + # `tr -d '\r\n'` defends against CRLF or wrapped secrets: base64 + # silently folds stray bytes into the output and the resulting + # "keystore" then trips the JDK's ASN.1 parser inside Gradle. + printf '%s' "$KEYSTORE_BASE64" | tr -d '\r\n' | base64 -d > "$KS" + if [ "$(stat -c%s "$KS")" -lt 1000 ]; then + echo "Decoded keystore is implausibly small — check ANDROID_KEYSTORE_BASE64." >&2 + exit 1 + fi + # Absolute storeFile: build.gradle.kts resolves a relative path + # against android/app, an absolute one is passed through unchanged. + printf "storePassword=%s\nkeyPassword=%s\nkeyAlias=%s\nstoreFile=%s\n" \ + "$ANDROID_KEYSTORE_PASSWORD" "$ANDROID_KEY_PASSWORD" "$ANDROID_KEY_ALIAS" \ + "$KS" > android/key.properties + + # Optional: android/app/build.gradle.kts only applies the FCM plugin when + # this file is present, so without the secret the build succeeds but + # ships without push notifications. + - name: Decode google-services.json + env: + GOOGLE_SERVICES_JSON: ${{ secrets.ANDROID_GOOGLE_SERVICES_JSON }} + run: | + if [ -z "$GOOGLE_SERVICES_JSON" ]; then + echo "::warning::ANDROID_GOOGLE_SERVICES_JSON is not set — building without FCM push." + exit 0 + fi + printf '%s' "$GOOGLE_SERVICES_JSON" | tr -d '\r\n' | base64 -d > android/app/google-services.json + grep -q '"project_info"' android/app/google-services.json || { + echo "Decoded google-services.json does not look like a Firebase config." >&2 + exit 1 + } + + - name: Install Flutter dependencies + run: flutter pub get + + - name: Run tests + run: flutter test + + - name: Run Fastlane ${{ inputs.release_type }} + working-directory: android + run: bundle exec fastlane ${{ inputs.release_type }} diff --git a/.github/workflows/ios-release.yml b/.github/workflows/ios-release.yml new file mode 100644 index 0000000..90620fb --- /dev/null +++ b/.github/workflows/ios-release.yml @@ -0,0 +1,110 @@ +# Publish Refetch to TestFlight / the App Store. +# +# Builds a signed IPA and uploads it through the Fastlane lanes in +# `ios/fastlane/`. Manual only — there is no push trigger. +# +# Repository secrets (Settings → Secrets and variables → Actions): +# +# APP_IDENTIFIER iOS bundle id (io.appwrite.refetch) +# APPLE_ID Apple Developer account email +# TEAM_ID Apple Developer Team ID (10 chars) +# ITC_TEAM_ID App Store Connect team id (numeric) +# APP_STORE_CONNECT_KEY_ID App Store Connect API key id +# APP_STORE_CONNECT_ISSUER_ID App Store Connect issuer id +# APP_STORE_CONNECT_KEY_CONTENT Base64-encoded .p8 private key +# MATCH_GIT_URL HTTPS URL of the private certificates repo +# MATCH_PASSWORD Passphrase the match repo is encrypted with +# MATCH_GIT_BASIC_AUTHORIZATION Base64 of "user:personal-access-token", +# used to clone MATCH_GIT_URL over HTTPS +# +# `bundle exec fastlane fetch_apple_info` prints ITC_TEAM_ID locally. +# If you prefer an SSH match URL, drop MATCH_GIT_BASIC_AUTHORIZATION and add +# an ssh-agent step instead. + +name: iOS Release + +on: + workflow_dispatch: + inputs: + release_type: + description: "Where to send the build" + required: true + type: choice + default: beta + options: + - beta # TestFlight + - release # App Store review + - build # signed IPA only, no upload + build_number: + description: "Override the build number (TestFlight rejects a re-upload at the same one). Blank uses pubspec.yaml." + required: false + type: string + +jobs: + publish: + name: Build and upload to App Store Connect (${{ inputs.release_type }}) + runs-on: macos-latest + timeout-minutes: 90 + + env: + # Read by ios/fastlane/{Appfile,Matchfile,Fastfile}. + APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }} + APPLE_ID: ${{ secrets.APPLE_ID }} + TEAM_ID: ${{ secrets.TEAM_ID }} + ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }} + APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} + APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} + APP_STORE_CONNECT_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_KEY_CONTENT }} + MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + # Keep fastlane's output readable in the Actions log. + FASTLANE_SKIP_UPDATE_CHECK: "1" + FASTLANE_HIDE_CHANGELOG: "1" + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + # Pinned to the version we develop against locally — bump in lockstep. + flutter-version: "3.44.6" + channel: stable + cache: true + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + bundler-cache: true + working-directory: ios + + - name: Install Flutter dependencies + run: flutter pub get + + - name: Run tests + run: flutter test + + # Generates Runner.xcworkspace and installs the pods that fastlane's + # build_app then archives with the match-provisioned signing identity. + - name: Build Flutter iOS + run: | + if [ -n "${{ inputs.build_number }}" ]; then + flutter build ios --release --no-codesign --build-number="${{ inputs.build_number }}" + else + flutter build ios --release --no-codesign + fi + + - name: Run Fastlane ${{ inputs.release_type }} + working-directory: ios + run: bundle exec fastlane ${{ inputs.release_type }} + + - name: Upload IPA artifact + if: success() + uses: actions/upload-artifact@v4 + with: + name: refetch-ios-${{ inputs.release_type }} + path: build/ios/*.ipa + if-no-files-found: warn diff --git a/.github/workflows/refresh-gemfile-lock.yml b/.github/workflows/refresh-gemfile-lock.yml new file mode 100644 index 0000000..12e2e34 --- /dev/null +++ b/.github/workflows/refresh-gemfile-lock.yml @@ -0,0 +1,56 @@ +# Regenerate android/Gemfile.lock and ios/Gemfile.lock on a runner and commit +# them back. Needed after any Gemfile change: Bundler runs frozen by default in +# CI, so the release workflows fail on a stale lock. + +name: Refresh Gemfile.lock + +on: + workflow_dispatch: + +permissions: + contents: write + +jobs: + refresh: + name: Resolve ${{ matrix.platform }}/Gemfile.lock + runs-on: ubuntu-latest + strategy: + # Serial: both jobs commit to the same branch. + max-parallel: 1 + matrix: + platform: [android, ios] + + steps: + - uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + # Deliberately no bundler-cache: that is what enforces frozen mode, + # and resolving a new lockfile is the entire point of this job. + bundler-cache: false + + - name: Resolve dependencies + working-directory: ${{ matrix.platform }} + run: | + bundle config set --local frozen false + bundle lock + # The iOS lanes run on macOS runners and developers are on macOS too, + # so keep the generic "ruby" platform in the lock alongside Linux. + bundle lock --add-platform ruby x86_64-linux + + - name: Commit the updated lockfile + working-directory: ${{ matrix.platform }} + run: | + # Stage first: `git diff` ignores untracked files, so a missing + # Gemfile.lock would look unchanged and be silently dropped. + git add Gemfile.lock + if git diff --cached --quiet -- Gemfile.lock; then + echo "${{ matrix.platform }}/Gemfile.lock already up to date — nothing to commit." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -m "chore(fastlane): refresh ${{ matrix.platform }}/Gemfile.lock" + git pull --rebase + git push diff --git a/.gitignore b/.gitignore index 8955ca8..363b7b2 100644 --- a/.gitignore +++ b/.gitignore @@ -52,8 +52,22 @@ app.*.map.json # Firebase config files contain project keys — each developer/CI supplies their own. **/google-services.json **/GoogleService-Info.plist +# Google Play service account keys — point fastlane at one with +# PLAY_STORE_JSON_KEY_PATH, or hand it to CI as PLAY_STORE_JSON_KEY_DATA. +**/*service_account*.json +**/*service-account*.json +**/play-store-*.json .env *.env # Local-only documentation (design specs, setup notes) — kept out of the repo. docs/ + +# Fastlane — generated docs, run reports and locally installed gems. +**/fastlane/README.md +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/test_output/ +**/.bundle/ +android/vendor/ +ios/vendor/ diff --git a/README.md b/README.md index f6fe52b..18e52ee 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,44 @@ Launcher icons are generated from `assets/icon/` (sources also kept as SVG): dart run flutter_launcher_icons ``` +## Releasing + +Both stores are driven by [fastlane](https://fastlane.tools), configured per +platform under `android/fastlane/` and `ios/fastlane/`. Store listing text lives +in `fastlane/metadata/` alongside the app. + +```bash +cd android && bundle install # or: cd ios && bundle install +bundle exec fastlane lanes +``` + +Shared lane names: `build` (no upload), `beta` (Play open testing / TestFlight), +`release` (Play production / App Store review), plus `upload_metadata`, +`upload_screenshots`, `upload_listing`, `download_metadata` and `screenshots`. +Android also has `internal` for the Play internal track. + +Everything project-specific is read from environment variables, so no secrets +are committed — set them in your CI provider or a local, gitignored `.env`: + +- **Android** — `APP_IDENTIFIER`, plus `PLAY_STORE_JSON_KEY_PATH` or + `PLAY_STORE_JSON_KEY_DATA`. Release signing still comes from + `android/key.properties`. The build/upload logic lives in + [fastlane-plugin-play_publisher](https://github.com/popupbits/fastlane-plugin-play_publisher), + which documents the optional vars (`SUPPLY_RELEASE_STATUS=draft` for a + first-ever release, `SKIP_FLUTTER_BUILD=1`, …). +- **iOS** — `APP_IDENTIFIER`, `APPLE_ID`, `TEAM_ID`, `ITC_TEAM_ID`, + `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`, + `APP_STORE_CONNECT_KEY_CONTENT` (base64 `.p8`), and `MATCH_GIT_URL` / + `MATCH_PASSWORD` for the private + [match](https://docs.fastlane.tools/actions/match/) certificates repo. + `fastlane fetch_apple_info` prints your `ITC_TEAM_ID`. + +In CI the same lanes run from **Actions → Android Release / iOS Release**, both +manual (`workflow_dispatch`) — pick a track and go. The secrets each one needs +are listed at the top of `.github/workflows/*.yml`. After changing either +`Gemfile`, run **Refresh Gemfile.lock** so the committed locks stay in sync; +Bundler runs frozen in CI and a stale lock fails the release. + ## License [MIT](LICENSE) — consistent with the diff --git a/android/Gemfile b/android/Gemfile index b63de10..2728fe3 100644 --- a/android/Gemfile +++ b/android/Gemfile @@ -2,5 +2,11 @@ source "https://rubygems.org" gem "fastlane", "~> 2.225" +# All Android build/publish logic lives in this plugin — the Fastfile in +# this repo is only lane names. +gem "fastlane-plugin-play_publisher", + git: "https://github.com/popupbits/fastlane-plugin-play_publisher", + tag: "v1.0.0" + # Optional plugins — uncomment as needed # gem "fastlane-plugin-firebase_app_distribution" diff --git a/android/Gemfile.lock b/android/Gemfile.lock new file mode 100644 index 0000000..8768397 --- /dev/null +++ b/android/Gemfile.lock @@ -0,0 +1,375 @@ +GIT + remote: https://github.com/popupbits/fastlane-plugin-play_publisher + revision: b73f84e1e1620b2d606f726c826ed9c35e111edd + tag: v1.0.0 + specs: + fastlane-plugin-play_publisher (1.0.0) + +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.8) + abbrev (0.1.2) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.4.0) + aws-partitions (1.1281.0) + aws-sdk-core (3.254.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-kms (1.130.0) + aws-sdk-core (~> 3, >= 3.254.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.229.0) + aws-sdk-core (~> 3, >= 3.254.1) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.1.2) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + csv (3.3.6) + declarative (0.0.20) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + erb (6.0.7) + excon (1.6.0) + logger + faraday (2.14.3) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-cookie_jar (0.0.8) + faraday (>= 0.8.0) + http-cookie (>= 1.0.0) + faraday-follow_redirects (0.5.0) + faraday (>= 1, < 3) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) + faraday-net_http (3.4.4) + net-http (~> 0.5) + faraday-retry (2.4.0) + faraday (~> 2.0) + fastimage (2.4.1) + fastlane (2.238.0) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) + addressable (>= 2.9.0, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.197) + babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) + colored (~> 1.2) + commander (~> 4.6) + csv (~> 3.3) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 2.0.0) + faraday (~> 2.7) + faraday-cookie_jar (~> 0.0.8) + faraday-follow_redirects (~> 0.3) + faraday-multipart (~> 1.0) + faraday-retry (~> 2.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.1.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.3.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + irb (>= 1.8) + json (< 3.0.0) + jwt (>= 2.10.3, < 4) + logger (>= 1.6, < 2.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_json (~> 1.12) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) + naturally (~> 2.2) + nkf (~> 0.2) + optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 4) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.106.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (1.2.5) + addressable (~> 2.9) + faraday (~> 2.13) + faraday-follow_redirects (~> 0.3) + googleauth (~> 1.14) + mini_mime (~> 1.1) + multi_json (~> 1.11) + representable (~> 3.0) + retriable (>= 3.1, < 5.0) + google-apis-iamcredentials_v1 (0.28.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.18.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.66.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.9.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (2.2.2) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.7.0) + google-cloud-storage (1.62.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) + google-cloud-core (~> 1.6) + googleauth (~> 1.9) + mini_mime (~> 1.0) + google-logging-utils (0.2.0) + googleauth (1.17.3) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) + os (>= 0.9, < 2.0) + pstore (~> 0.1) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.8) + domain_name (~> 0.5) + io-console (0.9.2) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jmespath (1.6.2) + json (2.21.2) + jwt (3.2.0) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.21.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + net-http (0.9.1) + uri (>= 0.11.1) + nkf (0.3.0) + optparse (0.8.1) + os (1.1.4) + ostruct (0.6.3) + plist (3.7.2) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pstore (0.2.1) + public_suffix (7.0.5) + rake (13.4.2) + rbs (4.1.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort + reline (0.7.0) + io-console (~> 0.5) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (4.2.0) + rexml (3.4.4) + rouge (3.28.0) + rubyzip (2.4.1) + security (0.1.5) + signet (0.22.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + trailblazer-option (0.1.2) + tsort (0.2.0) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + word_wrap (1.0.0) + xcodeproj (1.28.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + base64 + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + nkf + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + aarch64-linux + ruby + x86_64-linux + +DEPENDENCIES + fastlane (~> 2.225) + fastlane-plugin-play_publisher! + +CHECKSUMS + CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261 + abbrev (0.1.2) sha256=ad1b4eaaaed4cb722d5684d63949e4bde1d34f2a95e20db93aecfe7cbac74242 + addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af + artifactory (3.0.17) sha256=3023d5c964c31674090d655a516f38ca75665c15084140c08b7f2841131af263 + atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f + aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b + aws-partitions (1.1281.0) sha256=97fcc7d6a068ab790a1efcd8c6587c08fb108b4bed7ff1409cb47e0d642782ec + aws-sdk-core (3.254.1) sha256=518089e32134c3478cd4ec63d07fb966546a45e9e4cbf5f80d2cf16d5699d29b + aws-sdk-kms (1.130.0) sha256=a2e83662ca31b77a2a19c9aa2f40a98165a67270c18c718fe1c70d0cbd7cd749 + aws-sdk-s3 (1.229.0) sha256=7dd11a111875b3505d2ec0a0a383a6aab6c1badb3d4e94e7fbb958a24451f596 + aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 + babosa (1.0.4) sha256=18dea450f595462ed7cb80595abd76b2e535db8c91b350f6c4b3d73986c5bc99 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c + bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd + bundler (4.0.18) sha256=02d9a17429de1847b4e0c9f27a9ee4b20c0a74c0a641b4e77195d6019e3618ac + claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e + colored (1.2) sha256=9d82b47ac589ce7f6cab64b1f194a2009e9fd00c326a5357321f44afab2c1d2c + colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a + commander (4.6.0) sha256=7d1ddc3fccae60cc906b4131b916107e2ef0108858f485fdda30610c0f2913d9 + csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456 + declarative (0.0.20) sha256=8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9 + digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07 + domain_name (0.6.20240107) sha256=5f693b2215708476517479bf2b3802e49068ad82167bcd2286f899536a17d933 + dotenv (2.8.1) sha256=c5944793349ae03c432e1780a2ca929d60b88c7d14d52d630db0508c3a8a17d8 + emoji_regex (3.2.3) sha256=ecd8be856b7691406c6bf3bb3a5e55d6ed683ffab98b4aa531bb90e1ddcc564b + erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92 + excon (1.6.0) sha256=ebe2ab83c055ef8e117c62c91a3535b966a59a64868188219d49ab90cd83b65f + faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278 + faraday-cookie_jar (0.0.8) sha256=0140605823f8cc63c7028fccee486aaed8e54835c360cffc1f7c8c07c4299dbb + faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad + faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757 + faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588 + faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe + fastimage (2.4.1) sha256=c64bebd46b6fd8943ab70c1e6e85ff728f970f2e48f92ecd249b6bc3a540ad20 + fastlane (2.238.0) sha256=78e9252df2224c7e427012638e41051edfa29b133a40fda883fd2f1c13a77b98 + fastlane-plugin-play_publisher (1.0.0) + fastlane-sirp (1.1.0) sha256=10bc94f9682efd8e1badfb31452a76dd8981f1f3a33717c765fde6d75b54d847 + gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939 + google-apis-androidpublisher_v3 (0.106.0) sha256=b90b5312b70f8f731def88f24a2b8fb791fe1b979a7c2c14a905cb6cae19cf3f + google-apis-core (1.2.5) sha256=e21562b5627a0fc6a0c3165e429c3afed0f6a628eaa514e83f7204dda1adff69 + google-apis-iamcredentials_v1 (0.28.0) sha256=0a92ffe6cc39c569554af2a77a25dfc61519ed8bbb64ab04cffdd352dc5ef106 + google-apis-playcustomapp_v1 (0.18.0) sha256=44b277b9dee4a59ac5e9d98be1485edc5e382d2f9d73c79ae8908a455786a254 + google-apis-storage_v1 (0.66.0) sha256=fdf6d65d3fc77e7046b3494333a818ece9e2afd763bb161e1a7a9e70cf198351 + google-cloud-core (1.9.0) sha256=ab55409f51488e8deefb6edcc1ce4771dfb5da2fe7b3bc075709a030c2b682a4 + google-cloud-env (2.2.2) sha256=94bed40e05a67e9468ce1cb38389fba9a90aa8fc62fc9e173204c1dca59e21e7 + google-cloud-errors (1.7.0) sha256=6e682f42d89aae08689f36f28495de629d756da076bafff0003bac7f8042ebb3 + google-cloud-storage (1.62.0) sha256=e2c3c08bf8fd40d50be92304084942203314d4fc0ee52028e99f9359c3ad1330 + google-logging-utils (0.2.0) sha256=675462b4ea5affa825a3442694ca2d75d0069455a1d0956127207498fca3df7b + googleauth (1.17.3) sha256=9a655455885c0c17a8b70537acd2a992a7c7842c43c06c6612dfb0d93e755d58 + highline (2.0.3) sha256=2ddd5c127d4692721486f91737307236fe005352d12a4202e26c48614f719479 + http-cookie (1.0.8) sha256=b14fe0445cf24bf9ae098633e9b8d42e4c07c3c1f700672b09fbfe32ffd41aa6 + io-console (0.9.2) sha256=efa74f891dd03c0939a931dfc6e74c2813d904763d456ea9762b0525e748db08 + irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3 + jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 + json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a + jwt (3.2.0) sha256=5419b1fe37b1da0982bd07051f573a8b8789ab724c2aa7e785e4784a3ed217d7 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mini_magick (4.13.2) sha256=71d6258e0e8a3d04a9a0a09784d5d857b403a198a51dd4f882510435eb95ddd9 + mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef + multi_json (1.21.1) sha256=e6126a31808e3b4d19f483c775ceac34df190dffa62adfb63a165ee14ba68080 + multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8 + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 + nanaimo (0.4.0) sha256=faf069551bab17f15169c1f74a1c73c220657e71b6e900919897a10d991d0723 + naturally (2.3.0) sha256=459923cf76c2e6613048301742363200c3c7e4904c324097d54a67401e179e01 + net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + nkf (0.3.0) sha256=357a8dbeba38b727b75930f665146546076a394a1c243faf634ff176e3588895 + optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a + os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f + ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 + plist (3.7.2) sha256=d37a4527cc1116064393df4b40e1dbbc94c65fa9ca2eec52edf9a13616718a42 + pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570 + prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + pstore (0.2.1) sha256=03904d0f2c66579e96d1e6704cdabc0c88df7ea8ed8782d9f3569f6f6c702c1a + public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 + rbs (4.1.3) sha256=0c4474a9751cdc14364bfad0b3e53678323bbdc2c31683b0445932867dbab8c4 + rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469 + reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d + representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace + retriable (4.2.0) sha256=90c3b257472a1c02f2a3dfa64dd35a420f7a624cef1a5981e20fdac5730f8cdc + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + rouge (3.28.0) sha256=0d6de482c7624000d92697772ab14e48dca35629f8ddf3f4b21c99183fd70e20 + rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615 + security (0.1.5) sha256=3a977a0eca7706e804c96db0dd9619e0a94969fe3aac9680fcfc2bf9b8a833b7 + signet (0.22.0) sha256=b76d495ccb07ad35dbc89f3e920665a9d8ed717141955034005d7843dcfe4780 + simctl (1.6.10) sha256=b99077f4d13ad81eace9f86bf5ba4df1b0b893a4d1b368bd3ed59b5b27f9236b + terminal-notifier (2.0.0) sha256=7a0d2b2212ab9835c07f4b2e22a94cff64149dba1eed203c04835f7991078cea + terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2 + trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3 + tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f + tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48 + tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50 + tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542 + uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc + unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 + unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + word_wrap (1.0.0) sha256=f556d4224c812e371000f12a6ee8102e0daa724a314c3f246afaad76d82accc7 + xcodeproj (1.28.1) sha256=6f12670f00739d9817ca27ac89d6ef01cc86050e22a0bc08a3131487e5b5cddc + xcpretty (0.4.1) sha256=b14c50e721f6589ee3d6f5353e2c2cfcd8541fa1ea16d6c602807dd7327f3892 + xcpretty-travis-formatter (1.0.1) sha256=aacc332f17cb7b2cba222994e2adc74223db88724fe76341483ad3098e232f93 + +BUNDLED WITH + 4.0.18 diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index 76058ff..0d040ed 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -1,589 +1,57 @@ # --------------------------------------------------------------- # Fastfile — Android build & deployment lanes # --------------------------------------------------------------- -# This file is designed to be project-agnostic. All project-specific -# values (package name) are read from environment variables so the -# same file can be dropped into any Flutter project. +# All logic lives in fastlane-plugin-play_publisher. This file only +# names the lanes. See that plugin's README for the full env var +# reference and the draft-app behaviour. # -# Required env vars: -# APP_IDENTIFIER — Android application ID (package name) -# -# And one of: -# PLAY_STORE_JSON_KEY_PATH — Path to service account JSON key -# PLAY_STORE_JSON_KEY_DATA — Base64-encoded service account JSON +# Required env: APP_IDENTIFIER, and one of +# PLAY_STORE_JSON_KEY_PATH / PLAY_STORE_JSON_KEY_DATA # --------------------------------------------------------------- default_platform(:android) -def android_package_name - value = ENV["APP_IDENTIFIER"] - UI.user_error!("APP_IDENTIFIER env var must be set (Android applicationId)") if value.nil? || value.empty? - value -end - platform :android do - # ------------------------------------------------------------------ - # Helper: Resolve the Play Store JSON key - # ------------------------------------------------------------------ - desc "Resolve the Google Play service account JSON key" - private_lane :resolve_play_store_key do - key_path = ENV["PLAY_STORE_JSON_KEY_PATH"] - key_data = ENV["PLAY_STORE_JSON_KEY_DATA"] - - if key_path && !key_path.empty? && File.exist?(key_path) - UI.message("Using Play Store JSON key from PLAY_STORE_JSON_KEY_PATH: #{key_path}") - key_path - elsif key_data && !key_data.empty? - # Decode base64 data to a temp file - require "tempfile" - require "base64" - tmp = Tempfile.new(["play_store_key", ".json"]) - tmp.write(Base64.decode64(key_data)) - tmp.close - UI.message("Decoded Play Store JSON key from PLAY_STORE_JSON_KEY_DATA to #{tmp.path}") - tmp.path - else - UI.user_error!( - "Neither PLAY_STORE_JSON_KEY_PATH nor PLAY_STORE_JSON_KEY_DATA is set. " \ - "Provide one of them to authenticate with Google Play." - ) - end - end - - # ------------------------------------------------------------------ - # Build: Build AAB only (no upload) - # ------------------------------------------------------------------ - # Set SKIP_FLUTTER_BUILD=1 to reuse an already-built AAB at - # build/app/outputs/bundle/release/app-release.aab (e.g. when - # android_publish.sh was given an explicit aab path). - # ------------------------------------------------------------------ desc "Build release AAB only (no upload)" lane :build do - expected_aab = File.expand_path("../../build/app/outputs/bundle/release/app-release.aab", __dir__) - - if ENV["SKIP_FLUTTER_BUILD"] == "1" - UI.user_error!("SKIP_FLUTTER_BUILD=1 but no AAB found at #{expected_aab}") unless File.exist?(expected_aab) - UI.message("SKIP_FLUTTER_BUILD=1 — reusing existing AAB at #{expected_aab}") - else - sh("cd ../.. && flutter build appbundle --release") - UI.success("AAB built successfully at #{expected_aab}") - end - end - - # ------------------------------------------------------------------ - # Resolve the release_status to pass to the `supply` upload action. - # Defaults to "completed", but Google Play rejects that for apps that - # have never passed first review ("draft apps") with the error: - # "Only releases with status draft may be created on draft app." - # Set SUPPLY_RELEASE_STATUS=draft for a first-time release; after - # Google finishes the first review the restriction is lifted and you - # can drop the env var. - # Valid values: completed, draft, halted, inProgress - # ------------------------------------------------------------------ - def supply_release_status - value = (ENV["SUPPLY_RELEASE_STATUS"] || "completed").strip - allowed = %w[completed draft halted inProgress] - unless allowed.include?(value) - UI.user_error!("SUPPLY_RELEASE_STATUS=#{value} is invalid — must be one of: #{allowed.join(', ')}") - end - value + play_build end - # ------------------------------------------------------------------ - # Internal: Build AAB + upload to Google Play internal testing - # ------------------------------------------------------------------ desc "Build and upload to Google Play internal testing track" lane :internal do - build - - key_file = resolve_play_store_key - - supply( - track: "internal", - release_status: supply_release_status, - aab: "../build/app/outputs/bundle/release/app-release.aab", - json_key: key_file, - package_name: android_package_name, - skip_upload_metadata: true, - skip_upload_changelogs: false, - skip_upload_images: true, - skip_upload_screenshots: true, - ) + play_publish(track: "internal") end - # ------------------------------------------------------------------ - # Beta: Build AAB + upload to Google Play open testing (beta) - # ------------------------------------------------------------------ desc "Build and upload to Google Play beta (open testing) track" lane :beta do - build - - key_file = resolve_play_store_key - - supply( - track: "beta", - release_status: supply_release_status, - aab: "../build/app/outputs/bundle/release/app-release.aab", - json_key: key_file, - package_name: android_package_name, - skip_upload_metadata: true, - skip_upload_changelogs: false, - skip_upload_images: true, - skip_upload_screenshots: true, - ) + play_publish(track: "beta") end - # ------------------------------------------------------------------ - # Release: Build AAB + upload to Google Play production - # ------------------------------------------------------------------ desc "Build and upload to Google Play production track" lane :release do - build - - key_file = resolve_play_store_key - - supply( - track: "production", - release_status: supply_release_status, - aab: "../build/app/outputs/bundle/release/app-release.aab", - json_key: key_file, - package_name: android_package_name, - skip_upload_metadata: false, - skip_upload_changelogs: false, - skip_upload_images: true, - skip_upload_screenshots: true, - ) + play_publish(track: "production", upload_metadata: true) end - # ------------------------------------------------------------------ - # Upload lanes — drive Supply::Client directly - # ------------------------------------------------------------------ - # We bypass fastlane's `supply` upload action because its Uploader - # class crashes with `undefined method 'size' for nil:NilClass` - # (fastlane/fastlane#21530) on apps whose production track has no - # releases yet. Store listing text and images are app-level on - # Google Play — they aren't tied to a release — so driving the - # Publisher Edits API via Supply::Client lets us upload cleanly on - # brand-new apps AND on fully published ones. - # - # Three public lanes: - # upload_metadata — text fields only - # upload_screenshots — images + screenshots only - # upload_listing — text + images + screenshots - # All three share the helpers below. - # ------------------------------------------------------------------ - desc "Upload only text metadata (title, descriptions) to Play Store" lane :upload_metadata do - play_store_listing_upload(upload_text: true, upload_images: false) + play_upload_listing(text: true, images: false) end desc "Upload only screenshots + feature graphic / icon to Play Store" lane :upload_screenshots do - play_store_listing_upload(upload_text: false, upload_images: true) + play_upload_listing(text: false, images: true) end desc "Upload text metadata + images + screenshots to Play Store" lane :upload_listing do - play_store_listing_upload(upload_text: true, upload_images: true) - end - - # ------------------------------------------------------------------ - # Shared upload helper - # ------------------------------------------------------------------ - def play_store_listing_upload(upload_text:, upload_images:) - require "supply" - require "supply/client" - - metadata_dir = File.expand_path("./metadata/android", __dir__) - UI.user_error!("metadata directory does not exist: #{metadata_dir}") unless Dir.exist?(metadata_dir) - - locales = Dir.children(metadata_dir) - .select { |d| File.directory?(File.join(metadata_dir, d)) } - .reject { |d| d.start_with?(".") } - UI.user_error!("No locale directories under #{metadata_dir}") if locales.empty? - - # changes_not_sent_for_review=true tells Google to commit the Edit to - # the Play Console dashboard WITHOUT attempting to submit it for review. - # This is the supported escape hatch for: - # - draft apps (apps that have never passed first review), where any - # auto-submit attempt trips "Only releases with status draft may be - # created on draft app", - # - published apps where the developer prefers to review and submit - # changes manually through the Play Console UI before they go live. - # The developer can still manually click "Send for review" in the - # Play Console whenever they're ready. Opt out with - # SUPPLY_SUBMIT_FOR_REVIEW=1 if you want Google to auto-submit the edit - # (only works on already-published apps). - auto_submit_for_review = ENV["SUPPLY_SUBMIT_FOR_REVIEW"] == "1" - - Supply.config = FastlaneCore::Configuration.create( - Supply::Options.available_options, - { - package_name: android_package_name, - json_key: resolve_play_store_key, - changes_not_sent_for_review: !auto_submit_for_review, - }, - ) - - client = Supply::Client.make_from_config - client.begin_edit(package_name: android_package_name) - - begin - UI.message("Uploading to Play Store (locales: #{locales.join(', ')})") - locales.each do |lang| - upload_text_for_locale(client, metadata_dir, lang) if upload_text - upload_images_for_locale(client, metadata_dir, lang) if upload_images - end - - commit_edit_with_draft_app_fallback(client, auto_submit_for_review: auto_submit_for_review) - - if auto_submit_for_review - UI.success("Upload committed and submitted for review.") - else - UI.success("Upload committed. Open Play Console to review and send for review when ready.") - end - rescue => ex - safely_abort_edit(client) - explain_play_store_commit_error(ex) - raise - end - end - - # Commit the edit. On draft apps (never past first review), Google rejects - # the commit because the inherited release state includes non-draft - # releases. Catch that specific error, rewrite the inherited non-draft - # releases to draft within the same edit (leaving live release state - # untouched since changes_not_sent_for_review=true keeps everything - # pending), and retry. Published apps take the fast path on the first - # attempt without ever touching track state. - def commit_edit_with_draft_app_fallback(client, auto_submit_for_review:) - if auto_submit_for_review - UI.message("Committing edit and submitting for review...") - else - UI.message("Committing edit (staged for manual review)...") - end - - begin - client.commit_current_edit! - rescue => ex - raise ex unless ex.message.to_s.include?("Only releases with status draft may be created on draft app") - raise ex if auto_submit_for_review # can't use this workaround if submitting for review - - UI.important("Draft app detected — rewriting inherited non-draft releases to 'draft' status within this edit.") - UI.important("Live testers are NOT affected: changes_not_sent_for_review=true keeps everything pending until you click 'Send for review' in Play Console.") - - downgraded_any = rewrite_inherited_releases_to_draft!(client) - unless downgraded_any - UI.error("No non-draft inherited releases found to rewrite — the draft-app error is coming from somewhere else.") - raise ex - end - - UI.message("Retrying commit after downgrade...") - client.begin_edit(package_name: android_package_name) if client.current_edit.nil? - client.commit_current_edit! - end - end - - # Walk every track, find releases whose status isn't already "draft", - # flip them to draft, and PUT the track back via update_track. Returns - # true if any track was modified. - def rewrite_inherited_releases_to_draft!(client) - modified = false - tracks = client.tracks - tracks.each do |track| - next if track.releases.nil? || track.releases.empty? - needs_update = track.releases.any? { |r| r.status.to_s != "draft" } - next unless needs_update - - track.releases.each do |release| - next if release.status.to_s == "draft" - UI.important(" #{track.track}: #{release.name || '(no name)'} → draft (was: #{release.status})") - release.status = "draft" - end - - client.update_track(track.track, track) - modified = true - end - modified - end - - # Translate the common opaque Google Play API errors into something the - # user can act on without going to the GitHub issues tracker first. - def explain_play_store_commit_error(ex) - message = ex.message.to_s - - if message.include?("Only releases with status draft may be created on draft app") - UI.error("Upload failed: this app is still in \"draft\" state on Google Play.") - UI.important(<<~MSG.strip) - When an app has never had a production release submitted for review, - Google Play treats it as a draft app and rejects any Edit whose - inherited release state is not also \"draft\". Any internal/beta release - you uploaded directly through the Play Console or a prior `fastlane - internal` run is now blocking this Edit. - - Fix one of the following in the Play Console, then re-run the upload: - - 1. Complete the initial app setup checklist (Store presence → - Main store listing, plus any \"Set up your app\" tasks shown on - the Dashboard) and submit the first internal release for review. - Once Google finishes that first review, the draft-app restriction - is lifted permanently. - - 2. OR open Testing → Internal testing → Releases, find the current - release, and change its status to \"Draft\". Then re-run this lane. - - 3. OR promote the app straight to production with a real release - (`./scripts/android_publish.sh release`). - - This is a Google Play Console restriction, not a fastlane bug. - MSG - return - end - - if message.include?("The caller does not have permission") - UI.error("Upload failed: service account lacks store listing permissions.") - UI.important(<<~MSG.strip) - Go to Play Console → Users and permissions, open the service account - (`*@*.iam.gserviceaccount.com`), and grant this app the permission - \"Manage store presence\" (and \"Manage testing track releases\" if - you also want to upload AABs). Save, then wait ~5–10 minutes for - Google's permission cache to refresh before retrying. - MSG - return - end - - UI.error("Upload failed: #{ex.message}") - end - - # Upload title / short_description / full_description / video for one locale. - def upload_text_for_locale(client, metadata_dir, lang) - lang_dir = File.join(metadata_dir, lang) - fields = { - title: read_metadata_field(lang_dir, "title"), - short_description: read_metadata_field(lang_dir, "short_description"), - full_description: read_metadata_field(lang_dir, "full_description"), - video: read_metadata_field(lang_dir, "video"), - } - - if fields.values.all?(&:nil?) - UI.important(" #{lang}: no text fields to upload — skipping") - return - end - - client.update_listing_for_language(language: lang, **fields) - UI.success(" #{lang}: text ok") - end - - # Upload feature graphic + icon + tv banner + all screenshot sets for one locale. - def upload_images_for_locale(client, metadata_dir, lang) - images_root = File.join(metadata_dir, lang, Supply::IMAGES_FOLDER_NAME) - return unless Dir.exist?(images_root) - - # Single-file image types (featureGraphic, icon, tvBanner) - Supply::IMAGES_TYPES.each do |image_type| - image_path = Dir.glob(File.join(images_root, "#{image_type}.*")).first - next unless image_path - - begin - client.upload_image(image_path: image_path, image_type: image_type, language: lang) - UI.success(" #{lang}/#{image_type}") - rescue => ex - UI.error(" #{lang}/#{image_type}: failed — #{ex.message}") - end - end - - # Multi-file screenshot types (phone/7in/10in/tv/wear) - Supply::SCREENSHOT_TYPES.each do |image_type| - type_dir = File.join(images_root, image_type) - next unless Dir.exist?(type_dir) - - files = Dir.glob(File.join(type_dir, "*.{png,jpg,jpeg}")).sort - next if files.empty? - - begin - client.clear_screenshots(image_type: image_type, language: lang) - files.each { |f| client.upload_image(image_path: f, image_type: image_type, language: lang) } - UI.success(" #{lang}/#{image_type}: #{files.size}") - rescue => ex - UI.error(" #{lang}/#{image_type}: failed — #{ex.message}") - end - end - end - - # Read one metadata .txt file, returning nil if the file is missing or - # blank (so the Publisher API receives nil for "unchanged field" semantics - # instead of blowing away the existing value with an empty string). - def read_metadata_field(lang_dir, field) - path = File.join(lang_dir, "#{field}.txt") - return nil unless File.file?(path) - content = File.read(path, encoding: "UTF-8") - content.strip.empty? ? nil : content - end - - # Abort the current Edit without letting a cleanup failure mask the real - # upload error that triggered this call. - def safely_abort_edit(client) - client.abort_current_edit - rescue StandardError - nil + play_upload_listing end - # ------------------------------------------------------------------ - # Download: Fetch current metadata from Play Store - # ------------------------------------------------------------------ - # Drives Supply::Client directly instead of Supply::Setup#perform_download - # for three reasons: - # - # 1. `perform_download` calls `client.latest_version` which crashes with - # NoMethodError on apps that have no production release - # (fastlane/fastlane#21529) — the `releases` array on internal/beta - # tracks can contain nil entries and the upstream `reject` block - # doesn't compact them first. - # - # 2. `perform_download` silently no-ops if `metadata_path` already exists. - # We want a clean download every time, so we write into a tempdir and - # atomically swap into place. - # - # 3. `perform_download` occasionally misses locales that exist in the - # Play Console UI but haven't been touched via the Edit API. We merge - # the API-discovered locales with locales found in the existing local - # metadata dir and any listed in SUPPLY_DOWNLOAD_LANGUAGES. - # - # Existing local metadata is preserved as a timestamped .bak on each run. - # ------------------------------------------------------------------ desc "Download current metadata from Play Store" lane :download_metadata do - require "supply" - require "supply/client" - require "fileutils" - require "tmpdir" - - key_file = resolve_play_store_key - metadata_dir = File.expand_path("./metadata/android", __dir__) - - local_langs = if Dir.exist?(metadata_dir) - Dir.children(metadata_dir).select { |d| File.directory?(File.join(metadata_dir, d)) } - else - [] - end - forced_langs = (ENV["SUPPLY_DOWNLOAD_LANGUAGES"] || "") - .split(",").map(&:strip).reject(&:empty?) - - Supply.config = FastlaneCore::Configuration.create( - Supply::Options.available_options, - { - package_name: android_package_name, - json_key: key_file, - }, - ) - - client = Supply::Client.make_from_config - client.begin_edit(package_name: android_package_name) - - Dir.mktmpdir("supply-download-") do |tmp| - tmp_metadata = File.join(tmp, "android") - FileUtils.mkdir_p(tmp_metadata) - - begin - api_langs = client.listings.map(&:language) - all_langs = (api_langs + local_langs + forced_langs).uniq - - if all_langs.empty? - UI.user_error!( - "No locales to download — Play Store returned none and no local/forced " \ - "locales given. If you know a locale exists in the Console, pass it via " \ - "SUPPLY_DOWNLOAD_LANGUAGES=en-US,ne-NP (etc)." - ) - end - - UI.message("Downloading listings for: #{all_langs.join(', ')}") - - all_langs.each do |lang| - listing = client.listing_for_language(lang) - - if Supply::AVAILABLE_METADATA_FIELDS.all? { |k| listing.send(k).to_s.strip.empty? } - UI.important(" #{lang}: no data on Play Store — skipping") - next - end - - containing = File.join(tmp_metadata, lang) - FileUtils.mkdir_p(containing) - Supply::AVAILABLE_METADATA_FIELDS.each do |key| - path = File.join(containing, "#{key}.txt") - File.open(path, "w:UTF-8") { |f| f.write(listing.send(key).to_s) } - end - UI.success(" #{lang}") - - # Download images + screenshots for this locale. Best-effort — any - # individual image_type that fails (no asset of that kind uploaded, - # transient HTTP error, etc.) is logged and skipped without aborting - # the whole lane. Set SUPPLY_SKIP_IMAGE_DOWNLOAD=1 to turn this off. - unless ENV["SUPPLY_SKIP_IMAGE_DOWNLOAD"] == "1" - require "net/http" - require "fastimage" - - UI.message(" Checking images for #{lang}...") - image_download_total = 0 - (Supply::IMAGES_TYPES + Supply::SCREENSHOT_TYPES).each do |image_type| - begin - urls = client.fetch_images(image_type: image_type, language: lang).map(&:url) - if urls.nil? || urls.empty? - UI.message(" #{image_type}: none") - next - end - - is_single = Supply::IMAGES_TYPES.include?(image_type) - base_path = File.join(tmp_metadata, lang, Supply::IMAGES_FOLDER_NAME, image_type) - - if is_single - FileUtils.mkdir_p(File.dirname(base_path)) - else - FileUtils.mkdir_p(base_path) - end - - urls.each_with_index do |url, idx| - ext = FastImage.type(url) || "png" - file_path = if is_single - "#{base_path}.#{ext}" - else - File.join(base_path, "#{idx + 1}_#{lang}.#{ext}") - end - File.binwrite(file_path, Net::HTTP.get(URI.parse(url))) - end - - image_download_total += urls.size - UI.success(" #{image_type}: #{urls.size}") - rescue => ex - UI.error(" #{image_type}: failed — #{ex.message}") - end - end - UI.message(" Images downloaded for #{lang}: #{image_download_total}") - end - end - ensure - client.abort_current_edit - end - - unless Dir.exist?(tmp_metadata) && !Dir.empty?(tmp_metadata) - UI.user_error!("Downloaded no metadata into #{tmp_metadata}") - end - - if Dir.exist?(metadata_dir) - backup = "#{metadata_dir}.bak.#{Time.now.to_i}" - FileUtils.mv(metadata_dir, backup) - UI.message("Backed up existing local metadata to #{backup}") - end - - FileUtils.mkdir_p(File.dirname(metadata_dir)) - FileUtils.mv(tmp_metadata, metadata_dir) - UI.success("Downloaded Play Store metadata to #{metadata_dir}") - end + play_download_metadata end - # ------------------------------------------------------------------ - # Screenshots: Frame raw integration-test screenshots with frameit - # ------------------------------------------------------------------ desc "Frame raw Android screenshots using frameit" lane :screenshots do frameit( diff --git a/android/fastlane/metadata/android/en-US/changelogs/1.txt b/android/fastlane/metadata/android/en-US/changelogs/1.txt new file mode 100644 index 0000000..81fe995 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/1.txt @@ -0,0 +1 @@ +Initial release of Refetch: a curated tech-news feed with discussions, voting, submissions, and push notifications — for mobile and desktop. diff --git a/android/fastlane/metadata/android/en-US/images/featureGraphic.png b/android/fastlane/metadata/android/en-US/images/featureGraphic.png new file mode 100644 index 0000000..2852c5c Binary files /dev/null and b/android/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/android/fastlane/metadata/android/en-US/images/icon.png b/android/fastlane/metadata/android/en-US/images/icon.png new file mode 100644 index 0000000..0d8a9e4 Binary files /dev/null and b/android/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock new file mode 100644 index 0000000..e3fb6e2 --- /dev/null +++ b/ios/Gemfile.lock @@ -0,0 +1,366 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.8) + abbrev (0.1.2) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.4.0) + aws-partitions (1.1281.0) + aws-sdk-core (3.254.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-kms (1.130.0) + aws-sdk-core (~> 3, >= 3.254.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.229.0) + aws-sdk-core (~> 3, >= 3.254.1) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.1.2) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + csv (3.3.6) + declarative (0.0.20) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + erb (6.0.7) + excon (1.6.0) + logger + faraday (2.14.3) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-cookie_jar (0.0.8) + faraday (>= 0.8.0) + http-cookie (>= 1.0.0) + faraday-follow_redirects (0.5.0) + faraday (>= 1, < 3) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) + faraday-net_http (3.4.4) + net-http (~> 0.5) + faraday-retry (2.4.0) + faraday (~> 2.0) + fastimage (2.4.1) + fastlane (2.238.0) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) + addressable (>= 2.9.0, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.197) + babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) + colored (~> 1.2) + commander (~> 4.6) + csv (~> 3.3) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 2.0.0) + faraday (~> 2.7) + faraday-cookie_jar (~> 0.0.8) + faraday-follow_redirects (~> 0.3) + faraday-multipart (~> 1.0) + faraday-retry (~> 2.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.1.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.3.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + irb (>= 1.8) + json (< 3.0.0) + jwt (>= 2.10.3, < 4) + logger (>= 1.6, < 2.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_json (~> 1.12) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) + naturally (~> 2.2) + nkf (~> 0.2) + optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 4) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.106.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (1.2.5) + addressable (~> 2.9) + faraday (~> 2.13) + faraday-follow_redirects (~> 0.3) + googleauth (~> 1.14) + mini_mime (~> 1.1) + multi_json (~> 1.11) + representable (~> 3.0) + retriable (>= 3.1, < 5.0) + google-apis-iamcredentials_v1 (0.28.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.18.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.66.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.9.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (2.2.2) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.7.0) + google-cloud-storage (1.62.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) + google-cloud-core (~> 1.6) + googleauth (~> 1.9) + mini_mime (~> 1.0) + google-logging-utils (0.2.0) + googleauth (1.17.3) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) + os (>= 0.9, < 2.0) + pstore (~> 0.1) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.8) + domain_name (~> 0.5) + io-console (0.9.2) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jmespath (1.6.2) + json (2.21.2) + jwt (3.2.0) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.21.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + net-http (0.9.1) + uri (>= 0.11.1) + nkf (0.3.0) + optparse (0.8.1) + os (1.1.4) + ostruct (0.6.3) + plist (3.7.2) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pstore (0.2.1) + public_suffix (7.0.5) + rake (13.4.2) + rbs (4.1.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort + reline (0.7.0) + io-console (~> 0.5) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (4.2.0) + rexml (3.4.4) + rouge (3.28.0) + rubyzip (2.4.1) + security (0.1.5) + signet (0.22.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + trailblazer-option (0.1.2) + tsort (0.2.0) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + word_wrap (1.0.0) + xcodeproj (1.28.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + base64 + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + nkf + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + aarch64-linux + ruby + x86_64-linux + +DEPENDENCIES + fastlane (~> 2.225) + +CHECKSUMS + CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261 + abbrev (0.1.2) sha256=ad1b4eaaaed4cb722d5684d63949e4bde1d34f2a95e20db93aecfe7cbac74242 + addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af + artifactory (3.0.17) sha256=3023d5c964c31674090d655a516f38ca75665c15084140c08b7f2841131af263 + atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f + aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b + aws-partitions (1.1281.0) sha256=97fcc7d6a068ab790a1efcd8c6587c08fb108b4bed7ff1409cb47e0d642782ec + aws-sdk-core (3.254.1) sha256=518089e32134c3478cd4ec63d07fb966546a45e9e4cbf5f80d2cf16d5699d29b + aws-sdk-kms (1.130.0) sha256=a2e83662ca31b77a2a19c9aa2f40a98165a67270c18c718fe1c70d0cbd7cd749 + aws-sdk-s3 (1.229.0) sha256=7dd11a111875b3505d2ec0a0a383a6aab6c1badb3d4e94e7fbb958a24451f596 + aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 + babosa (1.0.4) sha256=18dea450f595462ed7cb80595abd76b2e535db8c91b350f6c4b3d73986c5bc99 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c + bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd + bundler (4.0.18) sha256=02d9a17429de1847b4e0c9f27a9ee4b20c0a74c0a641b4e77195d6019e3618ac + claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e + colored (1.2) sha256=9d82b47ac589ce7f6cab64b1f194a2009e9fd00c326a5357321f44afab2c1d2c + colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a + commander (4.6.0) sha256=7d1ddc3fccae60cc906b4131b916107e2ef0108858f485fdda30610c0f2913d9 + csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456 + declarative (0.0.20) sha256=8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9 + digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07 + domain_name (0.6.20240107) sha256=5f693b2215708476517479bf2b3802e49068ad82167bcd2286f899536a17d933 + dotenv (2.8.1) sha256=c5944793349ae03c432e1780a2ca929d60b88c7d14d52d630db0508c3a8a17d8 + emoji_regex (3.2.3) sha256=ecd8be856b7691406c6bf3bb3a5e55d6ed683ffab98b4aa531bb90e1ddcc564b + erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92 + excon (1.6.0) sha256=ebe2ab83c055ef8e117c62c91a3535b966a59a64868188219d49ab90cd83b65f + faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278 + faraday-cookie_jar (0.0.8) sha256=0140605823f8cc63c7028fccee486aaed8e54835c360cffc1f7c8c07c4299dbb + faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad + faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757 + faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588 + faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe + fastimage (2.4.1) sha256=c64bebd46b6fd8943ab70c1e6e85ff728f970f2e48f92ecd249b6bc3a540ad20 + fastlane (2.238.0) sha256=78e9252df2224c7e427012638e41051edfa29b133a40fda883fd2f1c13a77b98 + fastlane-sirp (1.1.0) sha256=10bc94f9682efd8e1badfb31452a76dd8981f1f3a33717c765fde6d75b54d847 + gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939 + google-apis-androidpublisher_v3 (0.106.0) sha256=b90b5312b70f8f731def88f24a2b8fb791fe1b979a7c2c14a905cb6cae19cf3f + google-apis-core (1.2.5) sha256=e21562b5627a0fc6a0c3165e429c3afed0f6a628eaa514e83f7204dda1adff69 + google-apis-iamcredentials_v1 (0.28.0) sha256=0a92ffe6cc39c569554af2a77a25dfc61519ed8bbb64ab04cffdd352dc5ef106 + google-apis-playcustomapp_v1 (0.18.0) sha256=44b277b9dee4a59ac5e9d98be1485edc5e382d2f9d73c79ae8908a455786a254 + google-apis-storage_v1 (0.66.0) sha256=fdf6d65d3fc77e7046b3494333a818ece9e2afd763bb161e1a7a9e70cf198351 + google-cloud-core (1.9.0) sha256=ab55409f51488e8deefb6edcc1ce4771dfb5da2fe7b3bc075709a030c2b682a4 + google-cloud-env (2.2.2) sha256=94bed40e05a67e9468ce1cb38389fba9a90aa8fc62fc9e173204c1dca59e21e7 + google-cloud-errors (1.7.0) sha256=6e682f42d89aae08689f36f28495de629d756da076bafff0003bac7f8042ebb3 + google-cloud-storage (1.62.0) sha256=e2c3c08bf8fd40d50be92304084942203314d4fc0ee52028e99f9359c3ad1330 + google-logging-utils (0.2.0) sha256=675462b4ea5affa825a3442694ca2d75d0069455a1d0956127207498fca3df7b + googleauth (1.17.3) sha256=9a655455885c0c17a8b70537acd2a992a7c7842c43c06c6612dfb0d93e755d58 + highline (2.0.3) sha256=2ddd5c127d4692721486f91737307236fe005352d12a4202e26c48614f719479 + http-cookie (1.0.8) sha256=b14fe0445cf24bf9ae098633e9b8d42e4c07c3c1f700672b09fbfe32ffd41aa6 + io-console (0.9.2) sha256=efa74f891dd03c0939a931dfc6e74c2813d904763d456ea9762b0525e748db08 + irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3 + jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 + json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a + jwt (3.2.0) sha256=5419b1fe37b1da0982bd07051f573a8b8789ab724c2aa7e785e4784a3ed217d7 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mini_magick (4.13.2) sha256=71d6258e0e8a3d04a9a0a09784d5d857b403a198a51dd4f882510435eb95ddd9 + mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef + multi_json (1.21.1) sha256=e6126a31808e3b4d19f483c775ceac34df190dffa62adfb63a165ee14ba68080 + multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8 + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 + nanaimo (0.4.0) sha256=faf069551bab17f15169c1f74a1c73c220657e71b6e900919897a10d991d0723 + naturally (2.3.0) sha256=459923cf76c2e6613048301742363200c3c7e4904c324097d54a67401e179e01 + net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + nkf (0.3.0) sha256=357a8dbeba38b727b75930f665146546076a394a1c243faf634ff176e3588895 + optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a + os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f + ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 + plist (3.7.2) sha256=d37a4527cc1116064393df4b40e1dbbc94c65fa9ca2eec52edf9a13616718a42 + pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570 + prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + pstore (0.2.1) sha256=03904d0f2c66579e96d1e6704cdabc0c88df7ea8ed8782d9f3569f6f6c702c1a + public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 + rbs (4.1.3) sha256=0c4474a9751cdc14364bfad0b3e53678323bbdc2c31683b0445932867dbab8c4 + rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469 + reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d + representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace + retriable (4.2.0) sha256=90c3b257472a1c02f2a3dfa64dd35a420f7a624cef1a5981e20fdac5730f8cdc + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + rouge (3.28.0) sha256=0d6de482c7624000d92697772ab14e48dca35629f8ddf3f4b21c99183fd70e20 + rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615 + security (0.1.5) sha256=3a977a0eca7706e804c96db0dd9619e0a94969fe3aac9680fcfc2bf9b8a833b7 + signet (0.22.0) sha256=b76d495ccb07ad35dbc89f3e920665a9d8ed717141955034005d7843dcfe4780 + simctl (1.6.10) sha256=b99077f4d13ad81eace9f86bf5ba4df1b0b893a4d1b368bd3ed59b5b27f9236b + terminal-notifier (2.0.0) sha256=7a0d2b2212ab9835c07f4b2e22a94cff64149dba1eed203c04835f7991078cea + terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2 + trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3 + tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f + tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48 + tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50 + tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542 + uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc + unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 + unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + word_wrap (1.0.0) sha256=f556d4224c812e371000f12a6ee8102e0daa724a314c3f246afaad76d82accc7 + xcodeproj (1.28.1) sha256=6f12670f00739d9817ca27ac89d6ef01cc86050e22a0bc08a3131487e5b5cddc + xcpretty (0.4.1) sha256=b14c50e721f6589ee3d6f5353e2c2cfcd8541fa1ea16d6c602807dd7327f3892 + xcpretty-travis-formatter (1.0.1) sha256=aacc332f17cb7b2cba222994e2adc74223db88724fe76341483ad3098e232f93 + +BUNDLED WITH + 4.0.18 diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..620e46e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 78e96a4..52f7527 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -82,6 +82,10 @@ platform :ios do # ------------------------------------------------------------------ desc "Sync code signing certificates and profiles via match" private_lane :sync_signing do + # CI runners have no unlocked login keychain, so match cannot install the + # certificate without a UI prompt. setup_ci creates a temporary one. + setup_ci if is_ci + load_asc_api_key match( diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..ff5ddb3 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end