mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
The submit_review fastlane lane identified the just-uploaded build by CFBundleVersion alone. In this project the production iOS profile resets the build number per marketing version, so every release sits at build 2 and Spaceship::ConnectAPI::Build.all returns the whole TestFlight history. The .find call picked an arbitrary match, which is how 0.1.76's binary got attached to the 0.1.75 App Store version slot and submitted for review under the wrong label. The release workflow now forwards app_version and app_build_version from the build_ios outputs into the fastlane step as env vars, and the Fastfile filters builds by marketing version plus build number to pin the exact binary. The standalone resubmit workflow takes the same identifiers as optional workflow_dispatch inputs; with both unset it falls back to "most recently uploaded iOS build" so manual reruns still work. Pinning fastlane in the Gemfile to lock the Spaceship behavior this lane depends on.
113 lines
4.4 KiB
Ruby
113 lines
4.4 KiB
Ruby
default_platform(:ios)
|
|
|
|
APP_IDENTIFIER = "sh.paseo"
|
|
|
|
platform :ios do
|
|
desc "Submit the latest TestFlight build to App Store Review"
|
|
lane :submit_review do
|
|
api_key = app_store_connect_api_key(
|
|
key_id: ENV.fetch("ASC_KEY_ID"),
|
|
issuer_id: ENV.fetch("ASC_ISSUER_ID"),
|
|
key_filepath: ENV.fetch("ASC_KEY_P8"),
|
|
)
|
|
|
|
require "spaceship"
|
|
Spaceship::ConnectAPI.token = Spaceship::ConnectAPI::Token.create(
|
|
key_id: ENV.fetch("ASC_KEY_ID"),
|
|
issuer_id: ENV.fetch("ASC_ISSUER_ID"),
|
|
filepath: ENV.fetch("ASC_KEY_P8"),
|
|
)
|
|
app = Spaceship::ConnectAPI::App.find(APP_IDENTIFIER)
|
|
UI.user_error!("Could not find app #{APP_IDENTIFIER} on App Store Connect") if app.nil?
|
|
|
|
# The release workflow forwards the marketing version and CFBundleVersion of
|
|
# the binary it just built. Use them to identify the exact build, since
|
|
# CFBundleVersion is not unique across marketing versions in this project
|
|
# (the production profile resets it per app_version). The resubmit workflow
|
|
# leaves these unset and falls back to "most recently uploaded iOS build".
|
|
target_version = ENV["APP_VERSION"].to_s.strip
|
|
target_build = ENV["APP_BUILD_VERSION"].to_s.strip
|
|
target_version = nil if target_version.empty?
|
|
target_build = nil if target_build.empty?
|
|
|
|
if target_version
|
|
UI.message("Targeting App Store version #{target_version}#{target_build ? " build #{target_build}" : ""} from workflow input")
|
|
else
|
|
UI.message("No APP_VERSION provided; selecting the most recently uploaded iOS build")
|
|
end
|
|
|
|
valid_build = nil
|
|
deadline = Time.now + (30 * 60)
|
|
loop do
|
|
builds = Spaceship::ConnectAPI::Build.all(
|
|
app_id: app.id,
|
|
sort: "-uploadedDate",
|
|
limit: 100,
|
|
).select { |b| b.pre_release_version&.platform == "IOS" }
|
|
builds = builds.select { |b| b.pre_release_version&.version == target_version } if target_version
|
|
builds = builds.select { |b| b.version == target_build } if target_build
|
|
|
|
build = builds.first
|
|
if build.nil?
|
|
UI.user_error!("Timed out waiting for build to appear on App Store Connect.") if Time.now > deadline
|
|
UI.message("No matching iOS build visible on App Store Connect yet; waiting...")
|
|
sleep(30)
|
|
next
|
|
end
|
|
|
|
state = build.processing_state
|
|
UI.message("Build #{build.pre_release_version.version} (#{build.version}) processing state: #{state || 'unknown'}")
|
|
if state == "VALID"
|
|
valid_build = build
|
|
break
|
|
end
|
|
if state == "INVALID" || state == "FAILED"
|
|
UI.user_error!("Build #{build.version} failed App Store processing (state: #{state}).")
|
|
end
|
|
if Time.now > deadline
|
|
UI.user_error!("Timed out waiting for build #{build.version} to finish processing on App Store Connect.")
|
|
end
|
|
sleep(30)
|
|
end
|
|
|
|
app_version = valid_build.pre_release_version.version
|
|
build_number = valid_build.version
|
|
UI.message("Submitting build #{build_number} as App Store version #{app_version} for review")
|
|
|
|
# Ensure an editable App Store version exists for this marketing version.
|
|
# deliver requires a "Prepare for Submission" version on App Store Connect
|
|
# and won't auto-create one when skip_metadata + skip_screenshots are set.
|
|
app.ensure_version!(app_version, platform: Spaceship::ConnectAPI::Platform::IOS)
|
|
|
|
# Apple requires a non-empty "What's New" on every new App Store version
|
|
# before it can be submitted for review. With skip_metadata: true, deliver
|
|
# never sets this, so we populate it directly per-locale.
|
|
edit_version = app.get_edit_app_store_version(platform: Spaceship::ConnectAPI::Platform::IOS)
|
|
release_notes = "Bug fixes and improvements."
|
|
edit_version.get_app_store_version_localizations.each do |localization|
|
|
if localization.whats_new.to_s.strip.empty?
|
|
UI.message("Setting whatsNew on #{localization.locale}")
|
|
localization.update(attributes: { whatsNew: release_notes })
|
|
end
|
|
end
|
|
|
|
deliver(
|
|
api_key: api_key,
|
|
app_identifier: APP_IDENTIFIER,
|
|
app_version: app_version,
|
|
build_number: build_number.to_s,
|
|
submit_for_review: true,
|
|
automatic_release: true,
|
|
force: true,
|
|
skip_binary_upload: true,
|
|
skip_screenshots: true,
|
|
skip_metadata: true,
|
|
precheck_include_in_app_purchases: false,
|
|
submission_information: {
|
|
add_id_info_uses_idfa: false,
|
|
export_compliance_uses_encryption: false,
|
|
},
|
|
)
|
|
end
|
|
end
|