default_platform(:android) platform :android do desc "Upload AAB to Google Play internal testing track" lane :upload do |options| track = options[:track] || "internal" # Find the AAB file - check project root for EAS builds or gradle output project_root = File.expand_path("..", __dir__) aab_path = options[:aab] unless aab_path # Check for EAS build AABs in project root, pick the newest eas_builds = Dir.glob(File.join(project_root, "build-*.aab")).sort_by { |f| File.mtime(f) } gradle_builds = Dir.glob(File.join(project_root, "android/app/build/outputs/bundle/release/*.aab")) aab_path = eas_builds.last || gradle_builds.first end # Resolve relative paths from project root aab_path = File.expand_path(aab_path, project_root) if aab_path && !aab_path.start_with?("/") UI.user_error!("No AAB file found. Build first or specify with aab: parameter") unless aab_path && File.exist?(aab_path) UI.message("Uploading #{aab_path} to #{track} track...") upload_to_play_store( track: track, aab: aab_path, skip_upload_metadata: true, skip_upload_images: true, skip_upload_screenshots: true, skip_upload_changelogs: false, release_status: "draft", ) UI.success("Successfully uploaded to #{track} track!") end desc "Upload AAB to production track" lane :upload_production do |options| upload(options.merge(track: "production")) end desc "Upload AAB to beta track" lane :upload_beta do |options| upload(options.merge(track: "beta")) end desc "Upload AAB and release it: commits the release on the given track (default production), triggering Play review and rollout on approval" lane :release do |options| track = options[:track] || "production" project_root = File.expand_path("..", __dir__) aab_path = options[:aab] UI.user_error!("Pass the exact verified artifact with aab: — never release an implicit build") unless aab_path aab_path = File.expand_path(aab_path, project_root) unless aab_path.start_with?("/") UI.user_error!("AAB not found at #{aab_path}") unless File.exist?(aab_path) rollout = options[:rollout] # e.g. "0.1" for 10% staged rollout; omit for full rollout UI.message("Releasing #{aab_path} to #{track} track#{rollout ? " at #{(rollout.to_f * 100).round}% rollout" : " at full rollout"}...") upload_to_play_store( track: track, aab: aab_path, skip_upload_metadata: true, skip_upload_images: true, skip_upload_screenshots: true, skip_upload_changelogs: false, release_status: rollout ? "inProgress" : "completed", rollout: rollout, ) UI.success("Release committed to #{track}. Google Play review runs automatically; rollout proceeds on approval.") end end platform :ios do # App Store Connect API key, supplied via environment (never committed): # ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_PATH (path to the .p8 file) private_lane :asc_api_key do UI.user_error!("Set ASC_KEY_ID, ASC_ISSUER_ID, and ASC_KEY_PATH") unless ENV["ASC_KEY_ID"] && ENV["ASC_ISSUER_ID"] && ENV["ASC_KEY_PATH"] app_store_connect_api_key( key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"], key_filepath: ENV["ASC_KEY_PATH"], ) end desc "Upload IPA to App Store Connect / TestFlight only (no App Review submission)" lane :upload do |options| ipa_path = options[:ipa] UI.user_error!("Pass the exact verified artifact with ipa:") unless ipa_path && File.exist?(File.expand_path(ipa_path)) upload_to_testflight( api_key: asc_api_key, ipa: File.expand_path(ipa_path), skip_waiting_for_build_processing: false, ) UI.success("Uploaded to App Store Connect; build is processing for TestFlight.") end desc "Upload IPA, create the App Store version, and submit it for App Review with automatic release on approval" lane :release do |options| ipa_path = options[:ipa] UI.user_error!("Pass the exact verified artifact with ipa:") unless ipa_path && File.exist?(File.expand_path(ipa_path)) api_key = asc_api_key upload_to_app_store( api_key: api_key, ipa: File.expand_path(ipa_path), submit_for_review: true, automatic_release: options.fetch(:automatic_release, true), phased_release: options.fetch(:phased_release, false), precheck_include_in_app_purchases: false, # Metadata lives in App Store Connect; only release notes change per version. skip_metadata: options[:release_notes].nil?, skip_screenshots: true, release_notes: options[:release_notes] ? { "en-GB" => options[:release_notes], "default" => options[:release_notes] } : nil, submission_information: { add_id_info_uses_idfa: false, export_compliance_uses_encryption: false, }, force: true, # skip interactive HTML preview; agents run non-interactively ) UI.success("Submitted for App Review. The version releases #{options.fetch(:automatic_release, true) ? "automatically on approval" : "manually after approval"}.") end end