389 lines
14 KiB
Ruby
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 |