Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooling] Migrate Prototype Builds from App Center to Firebase App Distribution #2857

Merged
merged 10 commits into from
Mar 27, 2025
11 changes: 3 additions & 8 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,15 @@ steps:
queue: mac
plugins: [$CI_TOOLKIT]

- block: Deploy Prototype Build
prompt: Share a Prototype Build via App Center?
- input: Deploy Prototype Build
prompt: Share a Prototype Build via Firebase App Distribution?
key: prototype_triggered
# Block steps have implicit dependency on the steps that come before them.
# See https://buildkite.com/docs/pipelines/trigger-step
#
# Make it depend on nothing so we don't have to wait for previous steps to finish before the deployment can start.
depends_on: ~

- group: Prototype Build
depends_on: prototype_triggered
steps:
- label: Prototype Build - Build
key: build_prototype
depends_on: prototype_triggered
command: .buildkite/commands/prototype-build.sh
agents:
queue: mac
Expand Down
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ gem 'cocoapods-check', '~> 1.1'
gem 'commonmarker'
gem 'danger-dangermattic', '~> 1.0'
gem 'fastlane', '~> 2.216'
gem 'fastlane-plugin-appcenter', '~> 2.1'
gem 'fastlane-plugin-firebase_app_distribution', '~> 0.10'
gem 'fastlane-plugin-sentry', '~> 1.14'
gem 'fastlane-plugin-wpmreleasetoolkit', '~> 12.4'
gem 'fastlane-plugin-wpmreleasetoolkit', '~> 13.0'
gem 'rubocop', '~> 1.60'
gem 'watchbuild'

Expand Down
16 changes: 11 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,12 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-appcenter (2.1.2)
fastlane-plugin-firebase_app_distribution (0.10.0)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
fastlane-plugin-sentry (1.25.1)
os (~> 1.1, >= 1.1.4)
fastlane-plugin-wpmreleasetoolkit (12.4.0)
fastlane-plugin-wpmreleasetoolkit (13.0.0)
activesupport (>= 6.1.7.1)
buildkit (~> 1.5)
chroma (= 0.2.0)
Expand Down Expand Up @@ -245,6 +247,10 @@ GEM
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-firebaseappdistribution_v1 (0.3.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-firebaseappdistribution_v1alpha (0.2.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
Expand Down Expand Up @@ -324,7 +330,7 @@ GEM
racc (1.8.1)
rainbow (3.1.1)
rake (13.2.1)
rake-compiler (1.2.8)
rake-compiler (1.2.9)
rake
rb_sys (0.9.102)
rchardet (1.8.0)
Expand Down Expand Up @@ -405,9 +411,9 @@ DEPENDENCIES
commonmarker
danger-dangermattic (~> 1.0)
fastlane (~> 2.216)
fastlane-plugin-appcenter (~> 2.1)
fastlane-plugin-firebase_app_distribution (~> 0.10)
fastlane-plugin-sentry (~> 1.14)
fastlane-plugin-wpmreleasetoolkit (~> 12.4)
fastlane-plugin-wpmreleasetoolkit (~> 13.0)
rake (>= 12.0, < 14.0)
rubocop (~> 1.60)
watchbuild
Expand Down
117 changes: 33 additions & 84 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ ASC_API_KEY_ENV_VARS = %w[
APP_STORE_CONNECT_API_KEY_KEY
].freeze

APPCENTER_OWNER_NAME = 'automattic'
APPCENTER_OWNER_TYPE = 'organization'
APPCENTER_APP_SLUG = 'Pocket-Casts-Prototype-Builds'
FIREBASE_APP_ID = '1:124902176124:ios:3999f43bfa1fcd1b1620f9'
FIREBASE_TESTERS_GROUP = 'pocketcasts-ios---prototype-builds'

FROZEN_STRINGS_PATH = File.join(FASTLANE_FOLDER, 'Frozen.strings')

Expand Down Expand Up @@ -651,21 +650,15 @@ platform :ios do
remove_app_clip_dependency!
UI.important('Remember not to commit the modified project file! The change is only meant to allow building for Enterprise where App Clips are not supported.')

version_settings = Xcodeproj::Config.new(File.new(VERSION_XCCONFIG_PATH)).to_hash
build_number = generate_prototype_build_number

UI.message("Updating build number to #{build_number}.")

version_settings[BUILD_CODE_KEY] = build_number

new_config = Xcodeproj::Config.new(version_settings)
new_config.save_as(Pathname.new(VERSION_XCCONFIG_PATH))
build_number = ENV.fetch('BUILDKITE_BUILD_NUMBER', '0')
pr_or_branch = pull_request_number&.then { |num| "PR ##{num}" } || ENV.fetch('BUILDKITE_BRANCH', nil)

build_app(
scheme: SCHEME_ENTERPRISE,
include_bitcode: false,
include_symbols: true,
clean: true,
xcargs: { VERSION_SHORT: pr_or_branch, VERSION_LONG: build_number },
output_directory: ARTIFACTS_FOLDER,
output_name: PROTOTYPE_BUILD_NAME,
export_options: {
Expand All @@ -675,39 +668,42 @@ platform :ios do
)
end

# Distributes the Prototype Build to App Center
# Distributes the Prototype Build to Firebase App Distribution
#
# Typically called after having built the binary with `build_enterprise`
#
# @note Expects the `.ipa` and `.dSYM` files to be present in `ARTIFACTS_FOLDER/PROTOTYPE_BUILD_NAME.{ipa,app.dSYM.zip}`
#
lane :upload_enterprise do
require_env_vars!('APPCENTER_API_TOKEN')
require_env_vars!('FIREBASE_APP_DISTRIBUTION_ACCOUNT_KEY')

commit = ENV.fetch('BUILDKITE_COMMIT', 'Unknown')
pr = ENV.fetch('BUILDKITE_PULL_REQUEST', nil)
release_notes = <<~NOTES
- Branch: `#{ENV.fetch('BUILDKITE_BRANCH', 'Unknown')}`\n
- Commit: [#{commit[0...7]}](#{GITHUB_URL}/commit/#{commit})\n
- Pull Request: [##{pr}](#{GITHUB_URL}/pull/#{pr})\n
Pull Request: ##{pull_request_number || 'N/A'}
Branch: `#{ENV.fetch('BUILDKITE_BRANCH', 'N/A')}`
Commit: #{ENV.fetch('BUILDKITE_COMMIT', 'N/A')[0...7]}
NOTES

appcenter_upload(
api_token: get_required_env!('APPCENTER_API_TOKEN'),
owner_name: APPCENTER_OWNER_NAME,
owner_type: APPCENTER_OWNER_TYPE,
app_name: APPCENTER_APP_SLUG,
file: File.join(ARTIFACTS_FOLDER, "#{PROTOTYPE_BUILD_NAME}.ipa"),
dsym: File.join(ARTIFACTS_FOLDER, "#{PROTOTYPE_BUILD_NAME}.app.dSYM.zip"),
firebase_app_distribution(
app: FIREBASE_APP_ID,
ipa_path: File.join(ARTIFACTS_FOLDER, "#{PROTOTYPE_BUILD_NAME}.ipa"),
service_credentials_json_data: get_required_env!('FIREBASE_APP_DISTRIBUTION_ACCOUNT_KEY'),
release_notes: release_notes,
destinations: 'Collaborators',
notify_testers: false
groups: FIREBASE_TESTERS_GROUP
)

next unless is_ci
next if pull_request_number.nil?

annotate_pr_with_appcenter_link
annotate_buildkite_with_appcenter_link
# Post PR Comment
comment_body = prototype_build_details_comment(
app_display_name: 'Pocket Casts Prototype Build',
fold: true
)
comment_on_pr(
project: GITHUB_REPO,
pr_number: pull_request_number,
reuse_identifier: 'prototype-build-link',
body: comment_body
)
end

# Uploads DSYM Symbols
Expand Down Expand Up @@ -1300,6 +1296,12 @@ platform :ios do
UI.message "Checked out branch `#{git_branch}` as requested by user.\n"
end

def pull_request_number
# Buildkite sets this env var to the PR number if on a PR, but to 'false' (and not nil) if not on a PR
pr_num = ENV.fetch('BUILDKITE_PULL_REQUEST', 'false')
pr_num == 'false' ? nil : Integer(pr_num)
end

# Create a commit with the typical message to use for a version bump
#
def commit_version_bump
Expand All @@ -1310,59 +1312,6 @@ platform :ios do
)
end

# Generate a build number for Prototype Builds, based on the PR number and short commit SHA1
#
# @note This function uses Buildkite-specific ENV vars
#
def generate_prototype_build_number
if ENV['BUILDKITE']
commit = ENV.fetch('BUILDKITE_COMMIT', nil)[0, 7]
branch = ENV.fetch('BUILDKITE_BRANCH', nil)
pr_num = ENV.fetch('BUILDKITE_PULL_REQUEST', nil)

pr_num == 'false' ? "#{branch}-#{commit}" : "pr#{pr_num}-#{commit}"
else
repo = Git.open(PROJECT_ROOT_FOLDER)
commit = repo.current_branch
branch = repo.revparse('HEAD')[0, 7]

"#{branch}-#{commit}"
end
end

# Post a comment on the PR with the info + QRCode to the Prototype Build in App Center
#
def annotate_pr_with_appcenter_link
comment_body = prototype_build_details_comment(
app_display_name: 'Pocket Casts Prototype Build',
app_center_org_name: APPCENTER_OWNER_NAME,
fold: true
)

comment_on_pr(
project: GITHUB_REPO,
pr_number: Integer(ENV.fetch('BUILDKITE_PULL_REQUEST', nil)),
reuse_identifier: 'prototype-build-link-pocket-casts',
body: comment_body
)
end

# Annotate a Buildkite CI build with info + links to the Prototype Build in App Center
#
def annotate_buildkite_with_appcenter_link
appcenter_id = lane_context.dig(SharedValues::APPCENTER_BUILD_INFORMATION, 'id')
version_data = Xcodeproj::Config.new(File.new(VERSION_XCCONFIG_PATH)).to_hash
metadata = version_data.merge(build_type: 'Prototype', 'appcenter:id': appcenter_id)
appcenter_install_url = "https://install.appcenter.ms/orgs/#{APPCENTER_OWNER_NAME}/apps/#{APPCENTER_APP_SLUG}/releases/#{appcenter_id}"
list = metadata.map { |k, v| " - **#{k}**: #{v}" }.join("\n")

buildkite_annotate(
context: 'appcenter-info-pocket-casts',
style: 'info',
message: "Pocket Casts iOS [App Center Build](#{appcenter_install_url}) Info:\n\n#{list}"
)
end

#####################################################################################
# Versioning Methods
#####################################################################################
Expand Down