mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
* fix(mobile): block iOS uploads below the last shipped App Store version The closed-train guard looked up each candidate version's own App Store record, but a version only gets one once it is submitted for review. 0.0.34 reached TestFlight and was never submitted, so it had no record, nothing looked closed, and the patch-bump walk stopped there — while 0.0.35 had already shipped. Apple rejected the upload after a 24-minute build (90186 closed train, 90062 needs a higher CFBundleShortVersionString). Fetch the highest closed version once and treat everything at or below it as closed, comparing semver numerically so 0.0.10 outranks 0.0.9. Also read appVersionState alongside appStoreState: the latter is deprecated in App Store Connect API 3.3 and renames the shipped state to READY_FOR_DISTRIBUTION, so reading only the old field would silently find zero closed versions once Apple stops populating it. * chore(mobile): prepare 0.0.36 app.json sat at 0.0.32 while 0.0.35 shipped on the App Store, because release versions are resolved on the runner and never committed back. Close the four-version drift so the checked-in version matches reality and the iOS release no longer depends on the closed-train walk to find an open version. Bump Android versionCode 8 -> 9 in the same commit: the version is shared between platforms, and shipping 0.0.36 with the code that already shipped for 0.0.32 produces an APK that cannot install over the released build.
57 lines
1.8 KiB
Ruby
57 lines
1.8 KiB
Ruby
module IosReleaseVersion
|
|
module_function
|
|
|
|
SEMVER_PATTERN = /\A(\d+)\.(\d+)\.(\d+)\z/.freeze
|
|
|
|
def truthy?(value)
|
|
%w[1 true yes on].include?(value.to_s.strip.downcase)
|
|
end
|
|
|
|
# [major, minor, patch] for semver comparison, or nil for anything else.
|
|
def parse(version)
|
|
match = version.to_s.strip.match(SEMVER_PATTERN)
|
|
return nil unless match
|
|
|
|
[match[1].to_i, match[2].to_i, match[3].to_i]
|
|
end
|
|
|
|
def bump_patch(version)
|
|
parts = parse(version)
|
|
raise ArgumentError, "Cannot bump non-semver mobile version '#{version}'" unless parts
|
|
|
|
"#{parts[0]}.#{parts[1]}.#{parts[2] + 1}"
|
|
end
|
|
|
|
# Highest semver in `versions`, skipping entries App Store Connect reports in
|
|
# a non-semver shape. Compares numerically: "0.0.10" beats "0.0.9", which a
|
|
# string sort gets backwards.
|
|
def max_version(versions)
|
|
Array(versions)
|
|
.map { |version| version.to_s.strip }
|
|
.select { |version| parse(version) }
|
|
.max_by { |version| parse(version) }
|
|
end
|
|
|
|
# Apple rejects an upload whose CFBundleShortVersionString is not higher than
|
|
# the last approved version (90062) and reports that version's train as closed
|
|
# (90186). So every version at or below `highest_closed` is unusable, not just
|
|
# the ones whose own App Store record sits in a closed state.
|
|
def closed_train?(version, highest_closed)
|
|
candidate = parse(version)
|
|
ceiling = parse(highest_closed)
|
|
return false unless candidate && ceiling
|
|
|
|
(candidate <=> ceiling) <= 0
|
|
end
|
|
|
|
def resolve(requested:, bump_patch:, current_version:, train_closed:)
|
|
exact_version = requested.to_s.strip
|
|
return exact_version unless exact_version.empty?
|
|
return current_version unless truthy?(bump_patch)
|
|
|
|
candidate = bump_patch(current_version)
|
|
candidate = bump_patch(candidate) while train_closed.call(candidate)
|
|
candidate
|
|
end
|
|
end
|