|
314 | 314 | end |
315 | 315 | end |
316 | 316 |
|
| 317 | + # Builds a single app and uploads it to TestFlight for *internal* testers, |
| 318 | + # stamping the build code with the Buildkite build number. |
| 319 | + # |
| 320 | + # This is the per-commit "continuous delivery" build from the "Faster Releases |
| 321 | + # for WordPress and Jetpack" RFC. It is intentionally additive: the existing |
| 322 | + # release lanes are untouched and remain the source of truth until this flow |
| 323 | + # is proven. |
| 324 | + # |
| 325 | + # The marketing version (`VERSION_SHORT`) is read from `Version.public.xcconfig` |
| 326 | + # as-is. The build code is `<marketing version>.0.<buildkite build number>` |
| 327 | + # (e.g. `27.0.0.4567`). Buildkite build numbers increase monotonically, so each |
| 328 | + # build for a given marketing version gets a unique, higher build code — which |
| 329 | + # is all App Store Connect requires. |
| 330 | + # |
| 331 | + # "Internal-only" means no external groups and no external-tester notifications |
| 332 | + # (matching the existing Reader upload). Distributing to the a8c staff beta |
| 333 | + # group, and separately to the public beta group, is wired up in later phases |
| 334 | + # of the RFC. |
| 335 | + # |
| 336 | + # @param [String] app One of `wordpress`, `jetpack`, or `reader`. |
| 337 | + # |
| 338 | + # @called_by CI |
| 339 | + # |
| 340 | + desc 'Builds one app and uploads it to TestFlight for internal testers' |
| 341 | + lane :build_and_upload_app_for_testflight do |app:| |
| 342 | + app = app.to_s.downcase |
| 343 | + |
| 344 | + case app |
| 345 | + when 'wordpress' |
| 346 | + scheme = 'WordPress' |
| 347 | + output_name = APP_STORE_CONNECT_BUILD_NAME_WORDPRESS |
| 348 | + beta_app_description_path = WORDPRESS_BETA_APP_DESCRIPTION_PATH |
| 349 | + sentry_project_slug = SENTRY_PROJECT_SLUG_WORDPRESS |
| 350 | + app_identifier = WORDPRESS_BUNDLE_IDENTIFIER |
| 351 | + update_certs_and_profiles_wordpress_app_store |
| 352 | + when 'jetpack' |
| 353 | + scheme = 'Jetpack' |
| 354 | + output_name = APP_STORE_CONNECT_BUILD_NAME_JETPACK |
| 355 | + beta_app_description_path = JETPACK_BETA_APP_DESCRIPTION_PATH |
| 356 | + sentry_project_slug = SENTRY_PROJECT_SLUG_JETPACK |
| 357 | + app_identifier = JETPACK_BUNDLE_IDENTIFIER |
| 358 | + update_certs_and_profiles_jetpack_app_store |
| 359 | + when 'reader' |
| 360 | + scheme = 'Reader' |
| 361 | + output_name = APP_STORE_CONNECT_BUILD_NAME_READER |
| 362 | + beta_app_description_path = BETA_APP_DESCRIPTION_PATH_READER |
| 363 | + sentry_project_slug = nil # Reader does not have a Sentry project yet |
| 364 | + app_identifier = nil |
| 365 | + update_certs_and_profiles_app_store_reader |
| 366 | + else |
| 367 | + UI.user_error!("Unknown app '#{app}'. Expected one of: wordpress, jetpack, reader") |
| 368 | + end |
| 369 | + |
| 370 | + build_number = ENV.fetch('BUILDKITE_BUILD_NUMBER', nil) |
| 371 | + UI.user_error!('BUILDKITE_BUILD_NUMBER is not set — this lane is meant to run on CI') if build_number.nil? |
| 372 | + |
| 373 | + build_code = "#{release_version_current}.0.#{build_number}" |
| 374 | + UI.important("Building #{scheme} #{release_version_current} (#{build_code}) for internal TestFlight distribution") |
| 375 | + |
| 376 | + sentry_check_cli_installed |
| 377 | + |
| 378 | + build_app( |
| 379 | + scheme: scheme, |
| 380 | + workspace: WORKSPACE_PATH, |
| 381 | + clean: true, |
| 382 | + output_directory: BUILD_PRODUCTS_PATH, |
| 383 | + output_name: output_name, |
| 384 | + derived_data_path: DERIVED_DATA_PATH, |
| 385 | + export_team_id: get_required_env('EXT_EXPORT_TEAM_ID'), |
| 386 | + xcargs: { VERSION_LONG: build_code }, |
| 387 | + export_options: { **COMMON_EXPORT_OPTIONS, method: 'app-store' } |
| 388 | + ) |
| 389 | + |
| 390 | + upload_app_to_testflight_internal( |
| 391 | + ipa_path: lane_context[SharedValues::IPA_OUTPUT_PATH], |
| 392 | + beta_app_description_path: beta_app_description_path |
| 393 | + ) |
| 394 | + |
| 395 | + # Upload symbols so crashes from these builds symbolicate in Sentry. |
| 396 | + next if sentry_project_slug.nil? |
| 397 | + |
| 398 | + sentry_debug_files_upload( |
| 399 | + auth_token: get_required_env('SENTRY_AUTH_TOKEN'), |
| 400 | + org_slug: SENTRY_ORG_SLUG, |
| 401 | + project_slug: sentry_project_slug, |
| 402 | + path: lane_context[SharedValues::DSYM_OUTPUT_PATH] |
| 403 | + ) |
| 404 | + |
| 405 | + upload_gutenberg_sourcemaps( |
| 406 | + sentry_project_slug: sentry_project_slug, |
| 407 | + release_version: release_version_current, |
| 408 | + build_version: build_code, |
| 409 | + app_identifier: app_identifier |
| 410 | + ) |
| 411 | + end |
| 412 | + |
| 413 | + # Convenience wrapper that builds and uploads each app to TestFlight, one after |
| 414 | + # another. CI builds each app in parallel via a Buildkite matrix instead; this |
| 415 | + # lane is handy for running the whole set locally. |
| 416 | + # |
| 417 | + # Reader is omitted until its App Store archive is fixed (broken since #25321). |
| 418 | + # The per-app lane still supports `app: 'reader'`; just re-add it here once it builds. |
| 419 | + # |
| 420 | + desc 'Builds and uploads WordPress and Jetpack to TestFlight (internal)' |
| 421 | + lane :build_all_apps_for_testflight do |
| 422 | + %w[wordpress jetpack].each do |app| |
| 423 | + build_and_upload_app_for_testflight(app: app) |
| 424 | + end |
| 425 | + end |
| 426 | + |
317 | 427 | # Builds the WordPress app for a Prototype Build ("WordPress Alpha" scheme), and uploads it to Firebase App Distribution |
318 | 428 | # |
319 | 429 | # @called_by CI |
@@ -426,6 +536,33 @@ def upload_build_to_testflight(ipa_path:, whats_new_path:, distribution_groups:, |
426 | 536 | ) |
427 | 537 | end |
428 | 538 |
|
| 539 | + # Uploads an already-built IPA to TestFlight for internal testers only. |
| 540 | + # |
| 541 | + # @param [String] ipa_path Path to the built IPA. |
| 542 | + # @param [String] beta_app_description_path Path to the beta app description file. |
| 543 | + # |
| 544 | + def upload_app_to_testflight_internal(ipa_path:, beta_app_description_path:) |
| 545 | + # TBD (RFC D4): the "what's new" text for per-commit internal builds is not |
| 546 | + # finalized yet. For now, generate a minimal placeholder from the commit |
| 547 | + # metadata so the upload has something to show. Public-beta builds will get |
| 548 | + # richer notes generated from the PRs merged since the previous public build. |
| 549 | + changelog = "Automated build from `#{ENV.fetch('BUILDKITE_BRANCH', 'unknown')}` (#{ENV.fetch('BUILDKITE_COMMIT', 'unknown')[0...7]})." |
| 550 | + whats_new_path = File.join(Dir.tmpdir, 'testflight_whats_new.txt') |
| 551 | + |
| 552 | + begin |
| 553 | + File.write(whats_new_path, changelog) |
| 554 | + |
| 555 | + upload_build_to_testflight( |
| 556 | + ipa_path: ipa_path, |
| 557 | + whats_new_path: whats_new_path, |
| 558 | + distribution_groups: [], # Internal-only for now (RFC D2): no external groups. |
| 559 | + beta_app_description_path: beta_app_description_path |
| 560 | + ) |
| 561 | + ensure |
| 562 | + FileUtils.rm_rf(whats_new_path) |
| 563 | + end |
| 564 | + end |
| 565 | + |
429 | 566 | # Send a Slack message to the specified channel |
430 | 567 | # |
431 | 568 | # @param [String] message The message to send to the channel |
|
0 commit comments