# This file contains the fastlane.tools configuration # You can find the documentation at https://docs.fastlane.tools # # For a list of all available actions, check out # # https://docs.fastlane.tools/actions # # For a list of all available plugins, check out # # https://docs.fastlane.tools/plugins/available-plugins # # Uncomment the line if you want fastlane to automatically update itself # update_fastlane default_platform(:android) # Returns an App Store Connect API key hash for use in mac lanes. # The .p8 is gitignored in desktop/secrets/. private_lane :appstore_api_key do app_store_connect_api_key( key_id: "F8BSZ7LC8Q", issuer_id: "a49b927f-0f0e-4d4c-85c6-ffddfac9125a", key_filepath: File.expand_path("../desktop/secrets/AuthKey_F8BSZ7LC8Q.p8", __dir__), in_house: false, ) end # Marketing version (CFBundleShortVersionString / packageVersion). Single source # of truth is libs.versions.toml `app`; prepareForRelease syncs it into Info.plist. def app_marketing_version toml = File.read(File.expand_path("../gradle/libs.versions.toml", __dir__)) toml[/^app\s*=\s*"([^"]+)"/, 1] || UI.user_error!("Could not read `app` version from libs.versions.toml") end # Run deliver with a bounded retry. Right after upload the build is processed in # TestFlight but can lag propagating to the App Store version API, so deliver # crashes with "Could not find build". That crash happens during build lookup — # before anything is attached or submitted — so retrying the whole call is safe. # https://github.com/fastlane/fastlane/issues/9012 def deliver_with_retry(params, attempts: 6, wait_seconds: 60) try = 0 begin try += 1 deliver(params) rescue => e raise if try >= attempts UI.important("deliver attempt #{try}/#{attempts} failed: #{e.message.to_s.lines.first&.strip}") UI.important("Build may still be propagating to App Store Connect; retrying in #{wait_seconds}s…") sleep(wait_seconds) retry end end platform :mac do desc "Build a sandboxed .pkg and upload to TestFlight only (beta; does not submit for review)." lane :desktop_testflight do desktop_build_and_upload(wait: false) end desc "Full Mac App Store release: build, upload, wait for processing, submit for review, auto-release on approval." lane :desktop_release do build_number = desktop_build_and_upload(wait: true) desktop_submit(build_number: build_number) end desc "Submit an already-processed TestFlight build for review + auto-release. build_number defaults to latest." lane :desktop_submit do |options| params = { api_key: appstore_api_key, app_identifier: "com.darkrockstudios.apps.hammer", platform: "osx", app_version: app_marketing_version, skip_binary_upload: true, skip_screenshots: true, metadata_path: "fastlane/metadata/osx", submit_for_review: true, automatic_release: true, run_precheck_before_submit: false, force: true, } params[:build_number] = options[:build_number] if options[:build_number] deliver_with_retry(params) end # Build number is the latest in TestFlight + 1 — per-platform, always above # what's shipped, and self-correcting on re-runs. Set BUILD_NUMBER to override. # Returns the build number so the release lane can submit that exact build. private_lane :desktop_build_and_upload do |options| api_key = appstore_api_key build_number = ENV["BUILD_NUMBER"] if build_number.nil? || build_number.empty? latest = latest_testflight_build_number( api_key: api_key, app_identifier: "com.darkrockstudios.apps.hammer", platform: "osx", initial_build_number: 0, ) build_number = (latest.to_i + 1).to_s end UI.message("Mac App Store build number: #{build_number}") sh("cd \"#{File.expand_path('..', __dir__)}\" && ./desktop/scripts/build-appstore.sh #{build_number}") pkg_path = Dir.glob(File.expand_path("../desktop/build/installers/main-release/pkg/*.pkg", __dir__)).first UI.user_error!("Build produced no .pkg") unless pkg_path UI.message("Uploading #{pkg_path}") # When releasing we must wait for App Store Connect to finish processing the # build before deliver can attach it to the version and submit it. upload_to_testflight( api_key: api_key, app_identifier: "com.darkrockstudios.apps.hammer", app_platform: "osx", pkg: pkg_path, skip_waiting_for_build_processing: !options[:wait], ) build_number end end platform :ios do desc "Build a signed .ipa and upload to TestFlight only (beta; does not submit for review)." lane :ios_testflight do ios_build_and_upload(wait: false) end desc "Full iOS App Store release: build, upload, wait for processing, submit for review, auto-release on approval." lane :ios_release do build_number = ios_build_and_upload(wait: true) ios_submit(build_number: build_number) end desc "Submit an already-processed TestFlight build for review + auto-release. build_number defaults to latest." lane :ios_submit do |options| params = { api_key: appstore_api_key, app_identifier: "com.darkrockstudios.apps.hammer.ios", platform: "ios", app_version: app_marketing_version, skip_binary_upload: true, skip_screenshots: true, metadata_path: "fastlane/metadata/ios", submit_for_review: true, automatic_release: true, run_precheck_before_submit: false, force: true, } params[:build_number] = options[:build_number] if options[:build_number] deliver_with_retry(params) end # Same pattern as :mac — build number is the latest in TestFlight + 1. # Set BUILD_NUMBER to override. Returns the build number. private_lane :ios_build_and_upload do |options| api_key = appstore_api_key build_number = ENV["BUILD_NUMBER"] if build_number.nil? || build_number.empty? latest = latest_testflight_build_number( api_key: api_key, app_identifier: "com.darkrockstudios.apps.hammer.ios", platform: "ios", initial_build_number: 0, ) build_number = (latest.to_i + 1).to_s end UI.message("iOS App Store build number: #{build_number}") # Bump CFBundleVersion in Info.plist before archiving. CI runners are # ephemeral so dirty state doesn't matter; locally, commit or revert # the resulting Info.plist change after the lane runs. set_info_plist_value( path: File.expand_path("../ios/ios/Info.plist", __dir__), key: "CFBundleVersion", value: build_number, ) build_app( project: File.expand_path("../ios/ios.xcodeproj", __dir__), scheme: "ios", configuration: "Release", output_directory: File.expand_path("../ios/build", __dir__), output_name: "Hammer.ipa", export_method: "app-store", export_options: { method: "app-store", teamID: "8P3G3HT4J5", signingStyle: "manual", provisioningProfiles: { "com.darkrockstudios.apps.hammer.ios" => "Hammer AppStore iOS", }, }, clean: true, silent: true, ) # When releasing we must wait for App Store Connect to finish processing the # build before deliver can attach it to the version and submit it. upload_to_testflight( api_key: api_key, app_identifier: "com.darkrockstudios.apps.hammer.ios", app_platform: "ios", ipa: lane_context[SharedValues::IPA_OUTPUT_PATH], skip_waiting_for_build_processing: !options[:wait], ) build_number end end platform :android do desc "Runs all the tests" lane :test do gradle( task: "test", gradle_path: "./gradlew" ) end desc "Submit a new Build to Google Play Internal" lane :internal do gradle( task: ":android:bundleRelease", gradle_path: "./gradlew" ) upload_to_play_store(track: 'internal') end desc "Submit a new Alpha Build to Google Play Close" lane :alpha do gradle( task: ":android:bundleRelease", gradle_path: "./gradlew" ) upload_to_play_store(track: 'alpha') end desc "Submit a new Beta Build to Google Play Open" lane :beta do gradle( task: ":android:bundleRelease", gradle_path: "./gradlew" ) upload_to_play_store(track: 'beta') end desc "Deploy a new version to the Google Play" lane :release do gradle( task: "clean bundleRelease", gradle_path: "./gradlew" ) upload_to_play_store( root_url: "https://androidpublisher.googleapis.com/", skip_upload_images: true, skip_upload_screenshots: true ) end end