mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-19 05:45:15 +01:00
595 lines
21 KiB
Ruby
595 lines
21 KiB
Ruby
# Define app identifiers once for reuse across lanes
|
|
def app_identifiers
|
|
[
|
|
"io.bluewallet.bluewallet",
|
|
"io.bluewallet.bluewallet.watch",
|
|
"io.bluewallet.bluewallet.watch.extension",
|
|
"io.bluewallet.bluewallet.Stickers",
|
|
"io.bluewallet.bluewallet.MarketWidget"
|
|
]
|
|
end
|
|
|
|
default_platform(:android)
|
|
project_root = File.expand_path("..", __dir__)
|
|
|
|
# ===========================
|
|
# Android Lanes
|
|
# ===========================
|
|
|
|
platform :android do
|
|
|
|
desc "Prepare the keystore file"
|
|
lane :prepare_keystore do
|
|
Dir.chdir(project_root) do
|
|
keystore_file_hex = ENV['KEYSTORE_FILE_HEX']
|
|
UI.user_error!("KEYSTORE_FILE_HEX environment variable is missing") if keystore_file_hex.nil?
|
|
|
|
UI.message("Creating keystore from HEX...")
|
|
File.write("bluewallet-release-key.keystore.hex", keystore_file_hex)
|
|
|
|
sh("xxd -plain -revert bluewallet-release-key.keystore.hex > bluewallet-release-key.keystore") do |status|
|
|
UI.user_error!("Error reverting hex to keystore") unless status.success?
|
|
end
|
|
UI.message("Keystore created successfully.")
|
|
|
|
File.delete("bluewallet-release-key.keystore.hex")
|
|
end
|
|
end
|
|
|
|
desc "Update version, build number, and sign APK"
|
|
lane :update_version_build_and_sign_apk do
|
|
Dir.chdir(project_root) do
|
|
build_number = ENV['BUILD_NUMBER']
|
|
UI.user_error!("BUILD_NUMBER environment variable is missing") if build_number.nil?
|
|
|
|
# Extract versionName from build.gradle
|
|
version_name = sh("grep versionName android/app/build.gradle | awk '{print $2}' | tr -d '\"'").strip
|
|
|
|
# Update versionCode in build.gradle
|
|
UI.message("Updating versionCode in build.gradle to #{build_number}...")
|
|
build_gradle_path = "android/app/build.gradle"
|
|
build_gradle_contents = File.read(build_gradle_path)
|
|
new_build_gradle_contents = build_gradle_contents.gsub(/versionCode\s+\d+/, "versionCode #{build_number}")
|
|
File.write(build_gradle_path, new_build_gradle_contents)
|
|
|
|
# Determine branch name
|
|
branch_name = ENV['GITHUB_HEAD_REF'] || `git rev-parse --abbrev-ref HEAD`.strip.gsub(/[\/\\:?*"<>|]/, '_')
|
|
branch_name = 'master' if branch_name.nil? || branch_name.empty?
|
|
|
|
# Define APK name based on branch
|
|
signed_apk_name = branch_name != 'master' ? "BlueWallet-#{version_name}-#{build_number}-#{branch_name}.apk" : "BlueWallet-#{version_name}-#{build_number}.apk"
|
|
|
|
# Build APK
|
|
UI.message("Building APK...")
|
|
sh("cd android && ./gradlew assembleRelease")
|
|
UI.message("APK build completed.")
|
|
|
|
# Define APK name based on branch
|
|
signed_apk_name = branch_name != 'master' ?
|
|
"BlueWallet-#{version_name}-#{build_number}-#{branch_name}".gsub(/[\/\\:?*"<>|]/, '_') + ".apk" :
|
|
"BlueWallet-#{version_name}-#{build_number}.apk"
|
|
|
|
# Define paths
|
|
unsigned_apk_path = "android/app/build/outputs/apk/release/app-release-unsigned.apk"
|
|
signed_apk_path = "android/app/build/outputs/apk/release/#{signed_apk_name}"
|
|
# Rename APK
|
|
if File.exist?(unsigned_apk_path)
|
|
UI.message("Renaming APK to #{signed_apk_name}...")
|
|
FileUtils.mv(unsigned_apk_path, signed_apk_path)
|
|
ENV['APK_OUTPUT_PATH'] = File.expand_path(signed_apk_path)
|
|
else
|
|
UI.error("Unsigned APK not found at path: #{unsigned_apk_path}")
|
|
next
|
|
end
|
|
|
|
# Sign APK
|
|
UI.message("Signing APK with apksigner...")
|
|
apksigner_path = "#{ENV['ANDROID_HOME']}/build-tools/34.0.0/apksigner"
|
|
sh("#{apksigner_path} sign --ks #{project_root}/bluewallet-release-key.keystore --ks-pass=pass:#{ENV['KEYSTORE_PASSWORD']} #{signed_apk_path}")
|
|
UI.message("APK signed successfully: #{signed_apk_path}")
|
|
end
|
|
end
|
|
|
|
desc "Upload APK to BrowserStack and post result as PR comment"
|
|
lane :upload_to_browserstack_and_comment do
|
|
Dir.chdir(project_root) do
|
|
# Determine APK path
|
|
apk_path = ENV['APK_PATH']
|
|
if apk_path.nil? || apk_path.empty?
|
|
UI.message("No APK path provided, searching for APK...")
|
|
apk_path = `find ./ -name "*.apk"`.strip
|
|
UI.user_error!("No APK file found") if apk_path.nil? || apk_path.empty?
|
|
end
|
|
|
|
# Upload to BrowserStack
|
|
UI.message("Uploading APK to BrowserStack: #{apk_path}...")
|
|
upload_to_browserstack_app_live(
|
|
file_path: apk_path,
|
|
browserstack_username: ENV['BROWSERSTACK_USERNAME'],
|
|
browserstack_access_key: ENV['BROWSERSTACK_ACCESS_KEY']
|
|
)
|
|
|
|
# Extract BrowserStack URL
|
|
app_url = ENV['BROWSERSTACK_LIVE_APP_ID']
|
|
UI.user_error!("BrowserStack upload failed, no app URL returned") if app_url.nil? || app_url.empty?
|
|
|
|
# Prepare PR comment
|
|
apk_filename = File.basename(apk_path)
|
|
apk_download_url = ENV['APK_OUTPUT_PATH'] # Ensure this path is accessible
|
|
browserstack_hashed_id = app_url.gsub('bs://', '')
|
|
pr_number = ENV['GITHUB_PR_NUMBER']
|
|
|
|
comment_identifier = '### APK Successfully Uploaded to BrowserStack'
|
|
|
|
comment = <<~COMMENT
|
|
#{comment_identifier}
|
|
|
|
You can test it on the following devices:
|
|
|
|
- [Google Pixel 9 (Android 15)](https://app-live.browserstack.com/dashboard#os=android&os_version=15.0&device=Google+Pixel+8&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Google Pixel 8 (Android 14)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Google+Pixel+8&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Google Pixel 7 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Google+Pixel+7&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Google Pixel 5 (Android 12)](https://app-live.browserstack.com/dashboard#os=android&os_version=12.0&device=Google+Pixel+5&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Google Pixel 3a (Android 9)](https://app-live.browserstack.com/dashboard#os=android&os_version=9.0&device=Google+Pixel+3a&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
|
|
- [Samsung Galaxy Z Fold 6 (Android 14)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Samsung+Galaxy+Z+Fold+6&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Samsung Galaxy Z Fold 5 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Z+Fold+5&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Samsung Galaxy Tab S9 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Tab+S9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
- [Samsung Galaxy Note 9 (Android 8.1)](https://app-live.browserstack.com/dashboard#os=android&os_version=8.1&device=Samsung+Galaxy+Note+9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
|
|
- [OnePlus 11R (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=OnePlus+11R&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
|
**Filename**: [#{apk_filename}](#{apk_download_url})
|
|
**BrowserStack App URL**: #{app_url}
|
|
COMMENT
|
|
|
|
# Delete Previous BrowserStack Comments
|
|
if pr_number
|
|
begin
|
|
repo = ENV['GITHUB_REPOSITORY'] # Format: "owner/repo"
|
|
repo_owner, repo_name = repo.split('/')
|
|
|
|
UI.message("Fetching existing comments for PR ##{pr_number}...")
|
|
|
|
comments_json = `gh api -X GET /repos/#{repo_owner}/#{repo_name}/issues/#{pr_number}/comments`
|
|
comments = JSON.parse(comments_json)
|
|
|
|
comments.each do |comment|
|
|
if comment['body'].start_with?(comment_identifier)
|
|
comment_id = comment['id']
|
|
UI.message("Deleting previous comment ID: #{comment_id}...")
|
|
`gh api -X DELETE /repos/#{repo_owner}/#{repo_name}/issues/comments/#{comment_id}`
|
|
UI.success("Deleted comment ID: #{comment_id}")
|
|
end
|
|
end
|
|
|
|
rescue => e
|
|
UI.error("Failed to delete previous comments: #{e.message}")
|
|
end
|
|
else
|
|
UI.important("No PR number found. Skipping deletion of previous comments.")
|
|
end
|
|
|
|
# Post New Comment to PR
|
|
if pr_number
|
|
begin
|
|
escaped_comment = comment.gsub("'", "'\\''")
|
|
sh("GH_TOKEN=#{ENV['GH_TOKEN']} gh pr comment #{pr_number} --body '#{escaped_comment}'")
|
|
UI.success("Posted new comment to PR ##{pr_number}")
|
|
rescue => e
|
|
UI.error("Failed to post comment to PR: #{e.message}")
|
|
end
|
|
else
|
|
UI.important("No PR number found. Skipping PR comment.")
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
# ===========================
|
|
# iOS Lanes
|
|
# ===========================
|
|
|
|
platform :ios do
|
|
|
|
desc "Register new devices from a file"
|
|
lane :register_devices_from_txt do
|
|
UI.message("Registering new devices from file...")
|
|
|
|
csv_path = "../../devices.txt" # Update this with the actual path to your file
|
|
|
|
# Register devices using the devices_file parameter
|
|
register_devices(
|
|
devices_file: csv_path
|
|
)
|
|
|
|
UI.message("Devices registered successfully.")
|
|
|
|
# Update provisioning profiles for all app identifiers
|
|
app_identifiers.each do |app_identifier|
|
|
match(
|
|
type: "development",
|
|
app_identifier: app_identifier,
|
|
readonly: false, # Regenerate provisioning profile if needed
|
|
force_for_new_devices: true,
|
|
clone_branch_directly: true
|
|
)
|
|
end
|
|
|
|
UI.message("Development provisioning profiles updated.")
|
|
end
|
|
|
|
desc "Create a temporary keychain"
|
|
lane :create_temp_keychain do
|
|
UI.message("Creating a temporary keychain...")
|
|
|
|
create_keychain(
|
|
name: "temp_keychain",
|
|
password: ENV["KEYCHAIN_PASSWORD"],
|
|
default_keychain: true,
|
|
unlock: true,
|
|
timeout: 3600,
|
|
lock_when_sleeps: true
|
|
)
|
|
|
|
UI.message("Temporary keychain created successfully.")
|
|
end
|
|
|
|
desc "Synchronize certificates and provisioning profiles"
|
|
lane :setup_provisioning_profiles do
|
|
UI.message("Setting up provisioning profiles...")
|
|
|
|
platform = "ios"
|
|
|
|
# Iterate over app identifiers to fetch provisioning profiles
|
|
app_identifiers.each do |app_identifier|
|
|
match(
|
|
git_basic_authorization: ENV["GIT_ACCESS_TOKEN"],
|
|
git_url: ENV["GIT_URL"],
|
|
type: "appstore",
|
|
clone_branch_directly: true, # Skip if the branch already exists
|
|
platform: platform,
|
|
app_identifier: app_identifier,
|
|
team_id: ENV["ITC_TEAM_ID"],
|
|
team_name: ENV["ITC_TEAM_NAME"],
|
|
readonly: true,
|
|
keychain_name: "temp_keychain",
|
|
keychain_password: ENV["KEYCHAIN_PASSWORD"]
|
|
)
|
|
end
|
|
end
|
|
|
|
desc "Fetch development certificates and provisioning profiles for Mac Catalyst"
|
|
lane :fetch_dev_profiles_catalyst do
|
|
match(
|
|
type: "development",
|
|
platform: "catalyst",
|
|
app_identifier: app_identifiers,
|
|
readonly: true,
|
|
clone_branch_directly: true
|
|
)
|
|
end
|
|
|
|
desc "Fetch App Store certificates and provisioning profiles for Mac Catalyst"
|
|
lane :fetch_appstore_profiles_catalyst do
|
|
match(
|
|
type: "appstore",
|
|
platform: "catalyst",
|
|
app_identifier: app_identifiers,
|
|
readonly: true,
|
|
clone_branch_directly: true
|
|
)
|
|
end
|
|
|
|
desc "Setup provisioning profiles for Mac Catalyst"
|
|
lane :setup_catalyst_provisioning_profiles do
|
|
app_identifiers.each do |app_identifier|
|
|
match(
|
|
type: "development",
|
|
platform: "catalyst",
|
|
app_identifier: app_identifier,
|
|
readonly: false,
|
|
force_for_new_devices: true,
|
|
clone_branch_directly: true
|
|
)
|
|
|
|
match(
|
|
type: "appstore",
|
|
platform: "catalyst",
|
|
app_identifier: app_identifier,
|
|
readonly: false,
|
|
clone_branch_directly: true
|
|
)
|
|
end
|
|
end
|
|
|
|
desc "Clear derived data"
|
|
lane :clear_derived_data_lane do
|
|
UI.message("Clearing derived data...")
|
|
clear_derived_data
|
|
end
|
|
|
|
desc "Increment build number"
|
|
lane :increment_build_number_lane do
|
|
UI.message("Incrementing build number to current timestamp...")
|
|
|
|
# Set the new build number
|
|
increment_build_number(
|
|
xcodeproj: "ios/BlueWallet.xcodeproj",
|
|
build_number: ENV["NEW_BUILD_NUMBER"]
|
|
)
|
|
|
|
UI.message("Build number set to: #{ENV['NEW_BUILD_NUMBER']}")
|
|
end
|
|
|
|
desc "Install CocoaPods dependencies"
|
|
lane :install_pods do
|
|
UI.message("Installing CocoaPods dependencies...")
|
|
cocoapods(podfile: "ios/Podfile")
|
|
end
|
|
|
|
|
|
desc "Upload IPA to TestFlight"
|
|
lane :upload_to_testflight_lane do
|
|
|
|
branch_name = ENV['BRANCH_NAME'] || "unknown-branch"
|
|
last_commit_message = ENV['LATEST_COMMIT_MESSAGE'] || "No commit message found"
|
|
|
|
|
|
changelog = <<~CHANGELOG
|
|
Build Information:
|
|
CHANGELOG
|
|
|
|
# Include the branch name only if it is not 'master'
|
|
if branch_name != 'master'
|
|
changelog += <<~CHANGELOG
|
|
- Branch: #{branch_name}
|
|
CHANGELOG
|
|
end
|
|
|
|
changelog += <<~CHANGELOG
|
|
- Commit: #{last_commit_message}
|
|
CHANGELOG
|
|
|
|
ipa_path = ENV['IPA_OUTPUT_PATH']
|
|
|
|
if ipa_path.nil? || ipa_path.empty? || !File.exist?(ipa_path)
|
|
UI.user_error!("IPA file not found at path: #{ipa_path}")
|
|
end
|
|
|
|
UI.message("Uploading IPA to TestFlight from path: #{ipa_path}")
|
|
UI.message("Changelog:\n#{changelog}")
|
|
|
|
|
|
upload_to_testflight(
|
|
api_key_path: "./appstore_api_key.json",
|
|
ipa: ipa_path,
|
|
skip_waiting_for_build_processing: true,
|
|
changelog: changelog
|
|
)
|
|
|
|
UI.success("Successfully uploaded IPA to TestFlight!")
|
|
end
|
|
|
|
desc "Upload iOS source maps to Bugsnag"
|
|
lane :upload_bugsnag_sourcemaps do
|
|
bugsnag_api_key = ENV['BUGSNAG_API_KEY']
|
|
bugsnag_release_stage = ENV['BUGSNAG_RELEASE_STAGE'] || "production"
|
|
version = ENV['PROJECT_VERSION']
|
|
build_number = ENV['NEW_BUILD_NUMBER']
|
|
|
|
UI.user_error!("BUGSNAG_API_KEY environment variable is missing") if bugsnag_api_key.nil?
|
|
UI.user_error!("PROJECT_VERSION environment variable is missing") if version.nil?
|
|
UI.user_error!("NEW_BUILD_NUMBER environment variable is missing") if build_number.nil?
|
|
|
|
ios_sourcemap = "./ios/build/Build/Products/Release-iphonesimulator/main.jsbundle.map"
|
|
|
|
if File.exist?(ios_sourcemap)
|
|
UI.message("Uploading iOS source map to Bugsnag...")
|
|
bugsnag_sourcemaps_upload(
|
|
api_key: bugsnag_api_key,
|
|
source_map: ios_sourcemap,
|
|
minified_file: "./ios/main.jsbundle",
|
|
code_bundle_id: "#{version}-#{build_number}",
|
|
release_stage: bugsnag_release_stage,
|
|
app_version: version
|
|
)
|
|
UI.success("iOS source map uploaded successfully.")
|
|
else
|
|
UI.error("iOS source map not found at #{ios_sourcemap}")
|
|
end
|
|
end
|
|
|
|
desc "Build the iOS app"
|
|
lane :build_app_lane do
|
|
Dir.chdir(project_root) do
|
|
UI.message("Building the application from: #{Dir.pwd}")
|
|
|
|
workspace_path = File.join(project_root, "ios", "BlueWallet.xcworkspace")
|
|
export_options_path = File.join(project_root, "ios", "export_options.plist")
|
|
|
|
clear_derived_data_lane
|
|
|
|
begin
|
|
build_ios_app(
|
|
scheme: "BlueWallet",
|
|
workspace: workspace_path,
|
|
export_method: "app-store",
|
|
include_bitcode: false,
|
|
configuration: "Release",
|
|
skip_profile_detection: false,
|
|
include_symbols: true,
|
|
export_team_id: ENV["ITC_TEAM_ID"],
|
|
export_options: export_options_path,
|
|
output_directory: File.join(project_root, "ios", "build"),
|
|
output_name: "BlueWallet_#{ENV['PROJECT_VERSION']}_#{ENV['NEW_BUILD_NUMBER']}.ipa",
|
|
buildlog_path: File.join(project_root, "ios", "build_logs"),
|
|
silent: false,
|
|
clean: true
|
|
)
|
|
rescue => e
|
|
UI.user_error!("build_ios_app failed: #{e.message}")
|
|
end
|
|
|
|
# Use File.join to construct paths without extra slashes
|
|
ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH]
|
|
|
|
if ipa_path && File.exist?(ipa_path)
|
|
UI.message("IPA successfully found at: #{ipa_path}")
|
|
ENV['IPA_OUTPUT_PATH'] = ipa_path
|
|
sh("echo 'IPA_OUTPUT_PATH=#{ipa_path}' >> $GITHUB_ENV") # Export for GitHub Actions
|
|
else
|
|
UI.user_error!("IPA not found after build_ios_app.")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
# ===========================
|
|
# Global Lanes
|
|
# ===========================
|
|
|
|
|
|
|
|
desc "Deploy to TestFlight"
|
|
lane :deploy do |options|
|
|
UI.message("Starting deployment process...")
|
|
|
|
# Update WWDR Certificate
|
|
update_wwdr_certificate
|
|
|
|
# Setup App Store Connect API Key
|
|
setup_app_store_connect_api_key
|
|
|
|
# Setup Provisioning Profiles
|
|
setup_provisioning_profiles
|
|
|
|
# Clear Derived Data
|
|
clear_derived_data_lane
|
|
|
|
# Increment Build Number
|
|
increment_build_number_lane
|
|
|
|
# Install CocoaPods if not already installed
|
|
unless File.directory?("Pods")
|
|
install_pods
|
|
end
|
|
|
|
# Build the iOS App
|
|
build_app_lane
|
|
|
|
# Upload IPA to TestFlight
|
|
upload_to_testflight_lane
|
|
|
|
# Clean up and delete the temporary keychain
|
|
delete_keychain(name: "temp_keychain")
|
|
|
|
# Mark deployment as completed for the current commit
|
|
last_commit = last_git_commit
|
|
already_built_flag = ".already_built_#{last_commit[:sha]}"
|
|
File.write(already_built_flag, Time.now.to_s)
|
|
end
|
|
|
|
desc "Update 'What's New' section in App Store Connect for the 'Prepare for Submission' version"
|
|
lane :update_release_notes do |options|
|
|
require 'spaceship'
|
|
|
|
UI.message("Logging in to App Store Connect...")
|
|
Spaceship::ConnectAPI.login
|
|
|
|
app = Spaceship::ConnectAPI::App.find(app_identifiers.first)
|
|
|
|
UI.user_error!("Could not find the app with identifier: #{app_identifiers.first}") unless app
|
|
|
|
# Retry logic for fetching or creating the edit version
|
|
retries = 5
|
|
begin
|
|
prepare_version = app.get_edit_app_store_version(platform: Spaceship::ConnectAPI::Platform::IOS)
|
|
|
|
if prepare_version.nil?
|
|
UI.message("No version in 'Prepare for Submission' found. Creating a new version...")
|
|
latest_version = app.get_latest_version(platform: Spaceship::ConnectAPI::Platform::IOS)
|
|
new_version_number = (latest_version.version_string.to_f + 0.1).to_s
|
|
prepare_version = app.create_version!(platform: Spaceship::ConnectAPI::Platform::IOS, version_string: new_version_number)
|
|
UI.message("Created new version: #{new_version_number}")
|
|
else
|
|
UI.message("Found existing version in 'Prepare for Submission': #{prepare_version.version_string}")
|
|
end
|
|
rescue => e
|
|
retries -= 1
|
|
if retries > 0
|
|
delay = 20
|
|
UI.message("Cannot find edit app info... Retrying after #{delay} seconds (remaining: #{retries})")
|
|
sleep(delay)
|
|
retry
|
|
else
|
|
UI.user_error!("Failed to fetch or create the app version: #{e.message}")
|
|
end
|
|
end
|
|
|
|
# Extract existing metadata
|
|
localized_metadata = prepare_version.get_app_store_version_localizations
|
|
|
|
# Get enabled locales
|
|
enabled_locales = localized_metadata.map(&:locale)
|
|
|
|
# Define release notes
|
|
release_notes_text = options[:release_notes]
|
|
if release_notes_text.nil? || release_notes_text.strip.empty?
|
|
release_notes_path = "../release-notes.txt"
|
|
unless File.exist?(release_notes_path)
|
|
UI.error("Release notes file does not exist at path: #{release_notes_path}")
|
|
UI.user_error!("No release notes provided and no file found. Failing the lane.")
|
|
end
|
|
release_notes_text = File.read(release_notes_path)
|
|
end
|
|
|
|
# Define localized release notes
|
|
localized_release_notes = {
|
|
'en-US' => release_notes_text, # English (U.S.) - Primary
|
|
'ar-SA' => release_notes_text, # Arabic
|
|
'zh-Hans' => release_notes_text, # Chinese (Simplified)
|
|
'hr' => release_notes_text, # Croatian
|
|
'da' => release_notes_text, # Danish
|
|
'nl-NL' => release_notes_text, # Dutch
|
|
'fi' => release_notes_text, # Finnish
|
|
'fr-FR' => release_notes_text, # French
|
|
'de-DE' => release_notes_text, # German
|
|
'el' => release_notes_text, # Greek
|
|
'he' => release_notes_text, # Hebrew
|
|
'hu' => release_notes_text, # Hungarian
|
|
'it' => release_notes_text, # Italian
|
|
'ja' => release_notes_text, # Japanese
|
|
'ms' => release_notes_text, # Malay
|
|
'nb' => release_notes_text, # Norwegian
|
|
'pl' => release_notes_text, # Polish
|
|
'pt-BR' => release_notes_text, # Portuguese (Brazil)
|
|
'pt-PT' => release_notes_text, # Portuguese (Portugal)
|
|
'ro' => release_notes_text, # Romanian
|
|
'ru' => release_notes_text, # Russian
|
|
'es-MX' => release_notes_text, # Spanish (Mexico)
|
|
'es-ES' => release_notes_text, # Spanish (Spain)
|
|
'sv' => release_notes_text, # Swedish
|
|
'th' => release_notes_text, # Thai
|
|
}.select { |locale, _| enabled_locales.include?(locale) } # Only include enabled locales
|
|
|
|
# Review release notes updates
|
|
UI.message("Review the following release notes updates:")
|
|
localized_release_notes.each do |locale, notes|
|
|
UI.message("Locale: #{locale} - Notes: #{notes}")
|
|
end
|
|
|
|
unless options[:force_yes]
|
|
confirm = UI.confirm("Do you want to proceed with these release notes updates?")
|
|
UI.user_error!("User aborted the lane.") unless confirm
|
|
end
|
|
|
|
# Update release notes in App Store Connect
|
|
localized_release_notes.each do |locale, notes|
|
|
app_store_version_localization = localized_metadata.find { |loc| loc.locale == locale }
|
|
if app_store_version_localization
|
|
app_store_version_localization.update(attributes: { "whats_new" => notes })
|
|
else
|
|
UI.error("No localization found for locale #{locale}")
|
|
end
|
|
end
|
|
end
|
|
end |