Files
coco 723ce1af5c a
2026-07-03 15:12:48 +08:00

389 lines
14 KiB
Ruby

# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
# CONSTANTS
releaseNotesBodyHeader = "What's new:"
releaseNotesFileBody = "../app/version/release-notes"
releaseNotesLocales= ["en-US", "en-GB", "en-CA"]
releaseNotesMaxLength = 500
errorMessageCancelled = "Release cancelled 😢"
aarbExecutable = "AndroidAsanaBridge"
asanaBridgeInstallationProblem = "Android Asana Release Bridge not installed or configured correctly - see https://app.asana.com/0/0/1203116937958001/f for instructions"
default_platform(:android)
platform :android do
desc "Generate release notes for the Play Store"
private_lane :release_notes_playstore do
releaseNotesBody = File.read(releaseNotesFileBody)
formatted = "#{releaseNotesBodyHeader}\n#{releaseNotesBody}"
validateReleaseNotes(releaseNotes: formatted)
UI.message("\n#{formatted}")
formatted
end
desc "Generate release notes for GitHub"
private_lane :release_notes_github do
releaseNotesBody = File.read(releaseNotesFileBody)
formatted = "\#\# #{releaseNotesBodyHeader}\n#{releaseNotesBody}"
UI.message("\n#{formatted}")
formatted
end
desc "Upload APK to Play Store, in production track with a very small rollout percentage"
lane :deploy_playstore do
update_fastlane_release_notes()
props = property_file_read(file: "app/version/version.properties")
version = props["VERSION"]
apkPath = "app/build/outputs/apk/play/release/duckduckgo-#{version}-play-release.apk"
upload_to_play_store(
apk: apkPath,
track: 'production',
rollout: '0.001', # ie. 0.1%
skip_upload_screenshots: true,
skip_upload_images: true,
validate_only: false
)
cleanup_fastlane_release_notes()
annotate_release()
end
desc "Annotate release"
private_lane :annotate_release do
props = property_file_read(file: "app/version/version.properties")
version = props["VERSION"]
http_status = sh("curl -s -o /dev/null -w '%{http_code}' https://improving.duckduckgo.com/t/m_new_release_android?appVersion=#{version}", log: false).to_i
if http_status >= 200 && http_status < 300
# Successful response
puts "Release annotation successful with status code #{http_status}"
else
# Unsuccessful response
puts "Release annotation failed with status code #{http_status}"
end
end
desc "Upload APK to ad-hoc internal app sharing"
private_lane :deploy_adhoc do
props = property_file_read(file: "app/version/version.properties")
version = props["VERSION"]
apkPath = "app/build/outputs/apk/play/release/duckduckgo-#{version}-play-release.apk"
upload_to_play_store_internal_app_sharing(
apk: apkPath
)
end
desc "Upload APK to Play Store internal testing track"
lane :deploy_dogfood do |options|
UI.message("Apk path: #{options[:apk_path]}")
upload_to_play_store(
apk: options[:apk_path],
track: 'internal',
skip_upload_screenshots: true,
skip_upload_images: true,
validate_only: false
)
end
desc "Deploy APK to GitHub"
lane :deploy_github do
props = property_file_read(file: "app/version/version.properties")
version = props["VERSION"]
releaseNotes = release_notes_github()
apkPath = "app/build/outputs/apk/play/release/duckduckgo-#{version}-play-release.apk"
token = ENV["GITHUB_UPLOAD_TOKEN"]
UI.message ("Upload new app version to GitHub\nVersion: #{version}\nRelease Notes:\n=====\n#{releaseNotes}\n=====\n")
set_github_release(
repository_name: "DuckDuckGo/Android",
api_token: token,
name: version,
tag_name: version,
description: releaseNotes,
upload_assets: [apkPath],
is_draft: false,
is_prerelease: false)
end
desc "Update local changelist metadata"
private_lane :update_fastlane_release_notes do
releaseNotes = release_notes_playstore()
flversion=gradle(task: '-q fastlaneVersionCode').lines.map(&:chomp)[0]
UI.message("App version for fastlane is #{flversion}.\nRelease notes for Play Store:\n\n#{releaseNotes}")
releaseNotesLocales.each do |locale|
File.open("../fastlane/metadata/android/#{locale}/changelogs/#{flversion}.txt", 'w') do |file| file.write("#{releaseNotes}") end
end
end
desc "Clean up local changelist metadata"
private_lane :cleanup_fastlane_release_notes do
flversion=gradle(task: '-q fastlaneVersionCode').lines.map(&:chomp)[0]
releaseNotesLocales.each do |locale|
sh("rm '../fastlane/metadata/android/#{locale}/changelogs/#{flversion}.txt'")
end
end
# Note, this currently relies on having `git flow` tools installed.
# This dependency could be removed with a little more time to tidy up this script to do the branching/merging manually.
desc "Create new release"
lane :release do |options|
ensure_git_status_clean
ensure_git_branch( branch: 'develop' )
options_release_number = options[:release_number]
options_release_notes = options[:release_notes]
options_notes_type = options[:notes_type]
newVersion = determine_version_number(
release_number: options_release_number
)
releaseNotes = determine_release_notes(
release_notes: options_release_notes,
notes_type: options_notes_type
)
isInteractiveMode = options_release_number == nil || (options_notes_type == nil && options_release_notes == nil) || (options_notes_type == "CUSTOM" && options_release_notes == nil)
# For interactive flows, we want to confirm the data inputted by the user before proceeding
if !isInteractiveMode
UI.message("This are the release information:\n\nVersion=#{newVersion}\nRelease Notes:\n#{releaseNotes}\n")
do_create_release_branch(newVersion: newVersion, releaseNotes: releaseNotes)
do_create_and_push_tags(newVersion: newVersion)
else
if UI.confirm("Are you sure you're happy with this release?\n\nVersion=#{newVersion}\nRelease Notes:\n#{releaseNotes}\n")
UI.success "Creating release branch for release/#{newVersion}"
do_create_release_branch(newVersion: newVersion, releaseNotes: releaseNotes)
if UI.confirm(text:"If you have any other changes to make to the release branch, do them now. Enter `y` when ready to create and push tags")
do_create_and_push_tags(newVersion: newVersion)
else
UI.error errorMessageCancelled
end
else
UI.error errorMessageCancelled
end
end
end
private_lane :do_create_release_branch do |options|
newVersion = options[:newVersion]
releaseNotes = options[:releaseNotes]
sh "git flow release start #{newVersion}"
File.open('../app/version/version.properties', 'w') do |file|
file.write("VERSION=#{newVersion}")
end
File.open('../app/version/release-notes', 'w') do |file|
file.write("""#{releaseNotes}""")
end
end
private_lane :do_create_and_push_tags do |options|
newVersion = options[:newVersion]
git_commit(
message: "Updated release notes and version number for new release - #{newVersion}",
path: "*",
allow_nothing_to_commit: true,
skip_git_hooks: true
)
sh "git flow release finish -mnFS '#{newVersion}' '#{newVersion}'"
push_git_tags(tag: newVersion)
sh "git push origin main"
sh "git push origin develop"
UI.header("#{newVersion} tag has been successfully created. 🎉")
end
private_lane :determine_release_notes do |options|
notes_type = options[:notes_type]
custom_release_notes = options[:release_notes]
existingReleaseNotes = File.read(releaseNotesFileBody)
# This handles the flow when no options were passed. We will prompt the user for input.
releaseNotes = if (notes_type == nil && custom_release_notes == nil) then
commits = changelog_from_git_commits(
between: [last_git_tag, "HEAD"],
pretty: "- %s",
date_format: "short",
match_lightweight_tag: false,
merge_commit_filtering: "exclude_merges"
)
UI.important("Existing release notes:\n")
UI.message("#{existingReleaseNotes}\n")
choice = UI.select "What do you want to do for release notes?", ["KEEP EXISTING", "CUSTOM",
"Bug fixes and other improvements",
]
retrieve_notes_for_type(
notes_type: choice,
existingReleaseNotes: existingReleaseNotes
)
else
# Force notes_type to custom when custom_release_notes is present.
if custom_release_notes != nil
retrieve_notes_for_type(
notes_type: "CUSTOM",
release_notes: custom_release_notes,
existingReleaseNotes: existingReleaseNotes
)
else
retrieve_notes_for_type(
notes_type: notes_type,
release_notes: custom_release_notes,
existingReleaseNotes: existingReleaseNotes
)
end
end
validateReleaseNotes(releaseNotes: releaseNotes)
releaseNotes
end
desc "Validates the release notes to ensure they are suitable for the Play Store"
private_lane :validateReleaseNotes do |options|
releaseNotesLength = options[:releaseNotes].length
if (releaseNotesLength > releaseNotesMaxLength)
UI.user_error!("Release notes are too long for Play Store (#{releaseNotesLength} characters). Max size allowed: #{releaseNotesMaxLength}")
end
end
private_lane :retrieve_notes_for_type do |options|
notes_type = options[:notes_type]
custom_release_notes = options[:release_notes]
rl = case notes_type
when "KEEP EXISTING"
options[:existingReleaseNotes]
when "CUSTOM"
if custom_release_notes == nil then
prompt(text: "Release Notes: ", multi_line_end_keyword: "END")
else
custom_release_notes
end
else
notes_type
end
end
desc "Start new hotfix"
lane :"hotfix-start" do
UI.important("Starting a new hotfix")
ensure_git_status_clean
sh('git checkout develop && git pull')
sh('git checkout main && git pull')
newVersion = determine_version_number()
releaseNotes = determine_release_notes()
sh("git flow hotfix start #{newVersion}")
File.open('../app/version/version.properties', 'w') do |file|
file.write("VERSION=#{newVersion}")
end
File.open('../app/version/release-notes', 'w') do |file|
file.write("""#{releaseNotes}""")
end
git_commit(
message: "Updated release notes and version number for new release - #{newVersion}",
path: "*",
allow_nothing_to_commit: true,
skip_git_hooks: true
)
UI.important(text:"Hotfix branch created. Apply your changes that need to be included in the hotfix now. Run `fastlane hotfix-finish` when you've made your changes and are happy it all works.")
end
desc "Finish a hotfix in progress"
lane :"hotfix-finish" do
ensure_git_status_clean
ensure_git_branch( branch: '^hotfix/*' )
version = property_file_read(file: "app/version/version.properties")["VERSION"]
sh("git flow hotfix finish -m '#{version}' '#{version}'")
sh "git push origin #{version}"
sh "git push origin main"
sh "git push origin develop"
UI.header("🎉 #{version} tag has been successfully created, and hotfix has been merged back into main and develop. Everything has been pushed.")
end
desc "Prompt for version number"
private_lane :"determine_version_number" do |options|
release_number = options[:release_number]
if release_number == nil
prompt(text: "\nLast release was: #{last_git_tag}\nEnter New Version Number:")
else
release_number
end
end
desc "Prepares the Asana release board with a new release task, tags tasks waiting for release etc.."
lane :asana_release_prep do
begin
sh aarbExecutable, "action=verify"
rescue => ex
UI.user_error!("#{aarbExecutable} not installed. Install the tool and ensure it can be executed by executing `#{aarbExecutable}`")
end
newVersion = determine_version_number()
if UI.confirm("About to create a new release task for #{newVersion}. Ready to continue?")
UI.message("Creating release task...")
sh aarbExecutable, "version=#{newVersion}", "action=createRelease,tagPendingTasks,addLinksToDescription,removePendingTasks", "board=real"
else
UI.error(errorMessageCancelled)
end
end
end