fastlane: lane refresh_dsyms para subir dSYMs a Crashlytics

La build phase de Xcode solo sube el dSYM de la compilacion local. No habia red
de seguridad, asi que un fallo del script deja los crashes sin simbolizar.

refresh_dsyms baja los dSYM de App Store Connect y los manda a Crashlytics. Se
ejecuta aparte de beta a proposito: beta usa skip_waiting_for_build_processing,
asi que justo tras el upload los dSYM aun no existen en ASC.

Dos detalles que salieron al ejecutarla de verdad:

- upload_symbols_to_crashlytics busca el binario en ./Pods, pero Firebase entra
  por SPM y vive en DerivedData. Se resuelve con un glob.
- ASC no tiene dSYMs para la build 64 (normal sin bitcode: Apple no recompila
  nada). Sin fallback la lane seria un no-op permanente, asi que tira de los
  dSYM del ultimo archive local (app + widget).

Verificado ejecutandola: sube los dos dSYM de la 64 correctamente.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
This commit is contained in:
alexandrev-tibco
2026-08-05 20:57:50 +02:00
parent 3694297d4f
commit 9f5db245a9
2 changed files with 62 additions and 0 deletions
+54
View File
@@ -38,6 +38,60 @@ platform :ios do
)
end
# Firebase arrives via SPM, so upload-symbols sits in DerivedData instead of
# the ./Pods path fastlane looks in by default.
def crashlytics_upload_symbols_binary
pattern = File.expand_path(
"~/Library/Developer/Xcode/DerivedData/MealMood-*/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols"
)
found = Dir.glob(pattern)
if found.empty?
UI.user_error!("upload-symbols not found. Build the project once so SPM checks out firebase-ios-sdk.")
end
found.max_by { |path| File.mtime(path) }
end
# Every dSYM (app + widget) of the most recently archived build.
def latest_archive_dsyms
pattern = File.expand_path("~/Library/Developer/Xcode/Archives/*/MealMood*.xcarchive/dSYMs/*.dSYM")
by_archive = Dir.glob(pattern).group_by { |path| path[/.*\.xcarchive/] }
newest = by_archive.max_by { |archive, _| File.mtime(archive) }
newest ? newest.last : []
end
# Safety net for the Xcode build phase, which only uploads the dSYM produced
# locally. Run it once App Store Connect has finished processing the build —
# `beta` uses skip_waiting_for_build_processing, so right after an upload the
# dSYMs are not there yet and this would download nothing.
desc "Download processed dSYMs from App Store Connect and send them to Crashlytics"
lane :refresh_dsyms do |options|
download_dsyms(
api_key: asc_api_key,
version: options[:version] || "latest",
build_number: options[:build]
)
dsyms = lane_context[SharedValues::DSYM_PATHS] || []
if dsyms.empty?
# Expected for a post-bitcode app: Apple recompiles nothing, so it keeps
# no dSYM of its own. Fall back to the ones the local archive produced.
dsyms = latest_archive_dsyms
UI.important("App Store Connect has no dSYMs for this build; using the latest local archive.")
end
if dsyms.empty?
UI.important("No dSYMs found either — nothing to upload.")
next
end
upload_symbols_to_crashlytics(
gsp_path: "MealMood/Resources/GoogleService-Info.plist",
binary_path: crashlytics_upload_symbols_binary,
dsym_paths: dsyms
)
clean_build_artifacts
end
desc "Upload metadata + localized screenshots for an existing TestFlight build (no rebuild, no submit)"
lane :publish do |options|
version = options[:version] || "1.2.0"
+8
View File
@@ -23,6 +23,14 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
Push a new beta build to TestFlight
### ios refresh_dsyms
```sh
[bundle exec] fastlane ios refresh_dsyms
```
Download processed dSYMs from App Store Connect and send them to Crashlytics
### ios publish
```sh