Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

444 lines
15 KiB
Ruby

# Customise this file, documentation can be found here:
# https://github.com/KrauseFx/fastlane/tree/master/docs
$:.unshift File.dirname(__FILE__)
default_platform :ios
configurations_to_test_on_pull = [
{device: 'iPhone 14 Pro', os: '16.3'},
{device: 'iPad Pro (12.9-inch) (5th generation)', os: '15.7.1'}
]
def get_devices_by_scheme_from_options(options)
configs = options[:configurations] || [{device: 'iPhone 14 Pro', os: '16.3'}]
devices_by_scheme = {}
configs.each do |options|
scheme = options[:scheme] || 'Wikipedia'
device = options[:device] || 'iPhone 14 Pro'
os = options[:os] || '16.3'
sim = "#{device} (#{os})"
devices = devices_by_scheme[scheme] || []
devices << sim
devices_by_scheme[scheme] = devices
end
return devices_by_scheme
end
def get_version_number_from_options(options)
scheme = options[:scheme] || 'Wikipedia'
config = options[:configuration] || 'Release'
return get_version_number(target: scheme, configuration: config)
end
def clean_out_reference_images
UI.header "Step: Clear previous reference images"
UI.command "FileUtils.rm_rf(Dir.glob('../WikipediaUnitTests/ReferenceImages_64/*'))"
FileUtils.rm_rf(Dir.glob('../WikipediaUnitTests/ReferenceImages_64/*'))
end
platform :ios do
before_all do
# Set project for commit_version_bump, which seems to get confused by projects in other folders
ENV['FL_BUILD_NUMBER_PROJECT'] = "Wikipedia.xcodeproj"
ensure_git_status_clean if ENV['FL_ENSURE_CLEAN']
end
desc "Checks out the sha specified in the environment variables or the main branch"
lane :checkout do
sha = ENV['SHA']
if sha != nil
puts sha
commit = last_git_commit
sh "git checkout #{sha}"
end
end
desc "Runs linting (and eventually static analysis)"
lane :analyze do
read_xcversion
xcodebuild(
project: "Wikipedia.xcodeproj",
scheme: "Wikipedia",
configuration: "Debug",
sdk: 'iphonesimulator',
destination: 'platform=iOS Simulator,OS=15.1,name=iPhone 13 Pro',
analyze: true
)
end
desc "Runs tests on select platforms for verifying pull requests"
lane :verify_pull_request do |options|
verify({configurations: configurations_to_test_on_pull})
end
desc "Reads Xcode version from the .xcversion file and sets it using xcversion()"
lane :read_xcversion do |options|
version = `cat ../.xcversion`
if ENV['GITHUB_ACTIONS']
# GitHub actions keeps beta versions around that confuse xcversion()
# Use the absolute path to select the right version instead
# Remove trailing 0's to match GitHub's naming conventions
version_name = version.split('.').select { |n| n != '0' }.join('.')
`sudo xcode-select -s /Applications/Xcode_#{version_name}.app`
else
xcversion(version: version)
end
end
desc "Runs unit tests, generates reports."
lane :verify do |options|
slack_message = nil
if ENV.key?('CIRCLECI')
url = ENV['CIRCLE_BUILD_URL']
number = ENV['CIRCLE_BUILD_NUM']
job = ENV['CIRCLE_JOB']
slack_message = "<#{url}|CircleCI #{job} ##{number}>"
end
devices_by_scheme = get_devices_by_scheme_from_options(options)
devices_by_scheme.each do |scheme, devices|
opts = {
devices: devices,
scheme: scheme,
project: 'Wikipedia.xcodeproj',
configuration: 'Test',
disable_concurrent_testing: true,
output_files: "#{scheme}.junit",
output_types: 'junit',
slack_message: slack_message
}
scan(opts)
end
end
desc "Records visual tests."
lane :record_visual_tests do |options|
read_xcversion
clean_out_reference_images
options[:configurations] ||= configurations_to_test
devices_by_scheme = get_devices_by_scheme_from_options(options)
devices_by_scheme.each do |scheme, devices|
opts = {
devices: devices,
scheme: scheme,
disable_concurrent_testing: true,
project: 'Wikipedia.xcodeproj',
buildlog_path: './build',
configuration: 'Test',
xcargs: "GCC_PREPROCESSOR_DEFINITIONS='\$(value) WMF_VISUAL_TEST_RECORD_MODE=1'",
fail_build: false,
output_types: ''
}
scan(opts)
end
end
desc "Set the build number"
lane :set_build_number do |options|
build = options[:build] || 0
increment_build_number({
build_number: build
})
end
desc "Set version number"
lane :set_version_number do |options|
increment_version_number(
version_number: options[:version_number]
)
end
desc "Increment the app version patch"
lane :bump_patch do
increment_version_number(
bump_type: "patch"
)
end
desc "Increment the app version minor"
lane :bump_minor do
increment_version_number(
bump_type: "minor"
)
end
desc "Increment the app version major"
lane :bump_major do
increment_version_number(
bump_type: "major"
)
end
desc "Change version number and create PR with changes"
lane :change_version do |options|
ensure_git_status_clean(show_diff: true)
size = options[:size]
branch = "fastlane_version_bump"
case size
when "patch"
bump_patch()
when "minor"
bump_minor()
when "major"
bump_major()
end
commit_version_bump(message: "Version bump")
push_to_git_remote(
remote: "origin",
remote_branch: branch,
force: true,
set_upstream: true
)
create_pull_request(
api_token: ENV['GITHUB_TOKEN'],
repo: "wikimedia/wikipedia-ios",
title: "Version bump",
head: branch,
base: "main",
body: "Please merge this in."
)
end
desc "Add a build tag for the current build number and push to repo. While this tags a build, tag_release sets a release tag."
lane :tag do |options|
prefix = options[:prefix] || "betas"
build_number = options[:build_number] || options[:build] || get_build_number
tag_name = "#{prefix}/#{build_number}"
message = options[:message] || ""
add_git_tag(tag: tag_name, message: message)
sh "git push origin --tags"
end
desc "Add a release tag for the latest beta and push to repo. For tagging non-releases, use `tag`."
lane :tag_release do |options|
specified_build = options[:build_tag]
if specified_build.nil? || specified_build.empty?
latest_beta = get_latest_build_number()
sh "git checkout betas/#{latest_beta}"
else
sh "git checkout #{specified_build}"
end
version = options[:version] || get_version_number_from_options(options)
tag_name = "releases/#{version}"
add_git_tag(tag: tag_name)
sh "git push origin --tags && git checkout main"
set_github_release(
repository_name: "wikimedia/wikipedia-ios",
api_token: ENV['GITHUB_TOKEN'],
name: version,
tag_name: tag_name,
description: "release to iOS app store"
)
end
desc "Build the app for distribution"
lane :build do |options|
project_dir = ".."
build_dir = "../build"
product_name = options[:product_name] || "Wikipedia"
project_name = options[:project] || product_name
scheme_name = options[:scheme] || product_name
version = options[:version] || ''
number = options[:number] || ''
read_xcversion
sh "xcodebuild -project \"#{project_dir}/#{project_name}.xcodeproj\" -scheme \"#{scheme_name}\" -archivePath \"#{build_dir}/#{product_name}.xcarchive\" archive"
sh "xcodebuild -exportArchive -exportOptionsPlist ExportOptions.plist -archivePath \"#{build_dir}/#{product_name}.xcarchive\" -exportPath \"#{build_dir}\""
end
desc "Pushes both the production and staging app to TestFlight and tags the release. Only releases to internal testers. (This is very similar to `push_production`, although this command also tags the build in git.)"
lane :deploy do |options|
tag_prefix = "betas"
last_build = get_latest_build_number(prefix: tag_prefix)
build = options[:build] || last_build + 1
last_public_release_tag = get_latest_tag_with_prefix(prefix: "releases/")[0..-2]
last_public_release_number = last_public_release_tag.gsub("releases/", "")
full_merges_since_last_build = get_recent_commits(start: "#{tag_prefix}/#{last_build}", is_brief: false)
full_merges_since_last_release = get_recent_commits(start: last_public_release_tag, is_brief: false)
full_changelog = "\n__New since last build (#{last_build})__\n#{full_merges_since_last_build.empty? ? 'None' : full_merges_since_last_build}\n\n" +
"__New since last public release (#{last_public_release_number})__\n#{full_merges_since_last_release.empty? ? 'None' : full_merges_since_last_release}"
brief_merges_since_last_build = get_recent_commits(start: "betas/#{last_build}", is_brief: true)
brief_merges_since_last_release = get_recent_commits(start: last_public_release_tag, is_brief: true)
brief_changelog = "__New since last build (#{last_build})__\n#{brief_merges_since_last_build.empty? ? 'None' : brief_merges_since_last_build}\n\n" +
"__New since last public release (#{last_public_release_number})__\n#{brief_merges_since_last_release.empty? ? 'None' : brief_merges_since_last_release}"
push_production(build: build, changelog: brief_changelog)
tag(prefix: tag_prefix, build_number: build, message: full_changelog)
push_staging(build: build)
end
desc "Updates version, builds, and pushes the production build to TestFlight. Only releases to internal testers."
lane :push_production do |options|
push(
product_name: "Wikipedia",
app_identifier: "org.wikimedia.wikipedia",
build: options[:build],
changelog: options[:changelog]
)
end
desc "Updates version, builds, and pushes the staging build to TestFlight. Only releases to internal testers."
lane :push_staging do |options|
push(
product_name: "Staging",
app_identifier: "org.wikimedia.wikipedia.tfbeta",
build: options[:build]
)
end
desc "Updates version, builds, and pushes experimental build to TestFlight. Only releases to internal testers."
lane :push_experimental do |options|
tag_prefix = "alphas"
build = options[:build] || get_latest_build_number(prefix: tag_prefix) + 1
push(
product_name: "Experimental",
app_identifier: "org.wikimedia.wikipedia.tfalpha",
build: build
)
tag(prefix: tag_prefix, build_number: build)
end
lane :get_latest_tag_with_prefix do |options|
prefix = options[:prefix] || "betas/"
`git tag -l #{prefix}* --sort=-creatordate | head -n 1`
end
lane :get_latest_build_number do |options|
prefix = options[:prefix] || "betas"
prefix = "#{prefix}/"
get_latest_tag_with_prefix(prefix: prefix)[prefix.length..-1].to_i
end
lane :get_recent_commits do |options|
start = options[:start] || "HEAD"
is_brief = options[:is_brief] || false
format = is_brief ? '• %b (%s)' : '• %h - %b (%s)'
small_sed = "s/(Merge pull.*\\(#[0-9]*\\).*)/(\\1)/"
sed_removal = is_brief ? small_sed : "s/Merge pull request //g"
if is_brief
list_without_localizations = `git log #{start}..HEAD --merges --first-parent origin/main --grep="Merge branch 'main' into" --grep="Localisation updates from" --invert-grep --pretty=format:"#{format}" | sed '#{sed_removal}'`
localization_numbers = `git log #{start}..HEAD --merges --first-parent origin/main --grep="Localisation updates from" --pretty=format:"%s" | sed "s/Merge pull.*\\(#[0-9]*\\).*/\\1/" | xargs`
localizations_list = "• Localisation updates from https://translatewiki.net. Pull requests: " + localization_numbers
localization_numbers.empty? ? list_without_localizations : "#{list_without_localizations}\n#{localizations_list}"
else
`git log #{start}..HEAD --merges --first-parent origin/main --grep="Merge branch 'main' into" --invert-grep --pretty=format:"#{format}" | sed '#{sed_removal}'`
end
end
desc "updates version, builds, and pushes to TestFlight"
lane :push do |options|
build_dir = "build"
product_name = options[:product_name] || "Wikipedia"
app_identifier = options[:app_identifier] || "org.wikimedia.wikipedia"
ipa_path = "#{build_dir}/#{product_name}.ipa"
build_number = options[:build] || get_latest_build_number(prefix: "betas") + 1
version = get_version_number_from_options(options)
apple_key = `cat ~/AuthKey.p8`
issuer_id = `cat ~/issuerID.txt`
key_id = `cat ~/keyID.txt`
api_key = app_store_connect_api_key(
key_id: key_id,
issuer_id: issuer_id,
key_content: apple_key
)
increment_build_number(
build_number: build_number
)
# the changelog was overwriting whatever custom test notes we added
# changelog_from_git_commits(
# pretty: '- (%ae) %s', # Optional, lets you provide a custom format to apply to each commit when generating the changelog text
# tag_match_pattern: "#{tag_prefix}/*", # Optional, lets you search for a tag name that matches a glob(7) pattern
# include_merges: false # Optional, lets you filter out merge commits
# )
build(
product_name: product_name,
project: "Wikipedia",
number: build_number,
version: version
)
pilot(
api_key: api_key,
ipa: ipa_path,
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
app_identifier: app_identifier,
beta_app_feedback_email: "mobile-ios-wikipedia@wikimedia.org",
changelog: options[:changelog]
)
end
desc "Upload app store metadata"
lane :upload_app_store_metadata do
deliver(skip_binary_upload: true, skip_screenshots: true)
end
desc "Download dSYMs from iTunes Connect"
lane :dsyms do |options|
app_identifier = options[:app_identifier]
version = options[:version]
build_number = options[:build_number] || latest_testflight_build_number(app_identifier: app_identifier)
ipa_path = options[:ipa_path]
output_directory = options[:output_directory] || "build"
raise "Missing parameters" unless app_identifier && version && build_number && ipa_path && output_directory
download_dsyms(
build_number: build_number.to_s,
version: version.to_s,
app_identifier: app_identifier,
output_directory: output_directory
) # Download dSYM files from iTC
end
lane :dsyms_alpha do |options|
app_identifier = "org.wikimedia.wikipedia.tfalpha"
ipa_path = "build/Wikipedia Alpha.ipa"
version = options[:version] || get_version_number_from_options(options)
dsyms(
app_identifier: app_identifier,
version: version,
build_number: options[:build_number],
ipa_path: ipa_path
)
end
lane :dsyms_beta do |options|
app_identifier = "org.wikimedia.wikipedia"
ipa_path = "build/Wikipedia.ipa"
version = options[:version] || get_version_number_from_options(options)
dsyms(
app_identifier: app_identifier,
version: version,
build_number: options[:build_number] || options[:build],
ipa_path: ipa_path
)
end
lane :dsyms_beta_app do |options|
app_identifier = "org.wikimedia.wikipedia.tfbeta"
ipa_path = "build/Wikipedia.ipa"
version = options[:version] || get_version_number_from_options(options)
dsyms(
app_identifier: app_identifier,
version: version,
build_number: options[:build_number] || options[:build],
ipa_path: ipa_path
)
end
end