# SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later # run fastlane # bundle exec fastlane # update fastlane # bundle update fastlane # update plugins # bundle exec fastlane update_plugins # This is the minimum version number required. fastlane_version "2.58.0" ## config # add following to your shell rc: # export FASTLANE_NEXTCLOUD_STORE_FILE="" # export FASTLANE_NEXTCLOUD_STORE_PASSWORD="" # export FASTLANE_NEXTCLOUD_KEY_ALIAS="" # export FASTLANE_NEXTCLOUD_KEY_PASSWORD="" # export FASTLANE_NEXTCLOUD_GITHUB_API_TOKEN="" # export FASTLANE_NEXTCLOUD_HUAWEI_CLIENT_ID="" # export FASTLANE_NEXTCLOUD_HUAWEI_CLIENT_SECRET="" # export FASTLANE_NEXTCLOUD_HUAWEI_APP_ID="" skip_docs ## public lanes lane :screenshotsPhone do beautifyPhone() build_for_screengrab() screengrab( device_type: "phone", app_apk_path: APK_LOCATION, tests_apk_path: TEST_APK_LOCATION ) end lane :screenshotsTablet do beautifyPhone() build_for_screengrab() screengrab( device_type: "sevenInch", app_apk_path: APK_LOCATION, tests_apk_path: TEST_APK_LOCATION ) end desc "Release phase 1: make gplay/generic for RC, then test it" lane :RC_releasePhase1 do disableLogger() makeReleases() enableLogger() end desc "Release phase 2 for RC: checks, tag, upload gplay to playstore with values from build.gradle" lane :RC_releasePhase2 do |options| checkReleaseRequirements_RC() info = androidVersion promptVersion(info) checkChangelog(info) checkLibrary_RC() checkIfScreenshotsExist() checkIfAPKexists() tag(info) uploadToPlaystore_RC(info) createGithubRelease_RC(info) fdroidMergeRequest_RC(info) createChangelogPullRequest_RC(info) end desc "Release phase 1: make gplay/generic for FINAL, then test it" lane :Final_releasePhase1 do makeReleases() end desc "Release phase 2 for FINAL: checks, tag, upload gplay to playstore with values from build.gradle" lane :Final_releasePhase2 do |options| checkReleaseRequirements_Final() info = androidVersion promptVersion(info) checkChangelog(info) checkLibrary_Final() checkIfScreenshotsExist() checkIfAPKexists() tag(info) uploadToPlaystore_Final(info) createGithubRelease_Final(info) fdroidMergeRequest_Final(info) createChangelogPullRequest_Final(info) uploadToHuawei_Final(info) end desc "Makes gplay and generic releases in ../releases/" lane :makeReleases do info = androidVersion sh("rm -rf ../release/ && mkdir -p ../release") gradle(task: 'clean') createGenericRelease(versionCode:info["versionCode"]) createGplayRelease(versionCode:info["versionCode"]) createHuaweiRelease() end desc "Create GPlay release" lane :createGplayRelease do |options| buildBundle(flavor:"Gplay") sh("mv ../app/build/outputs/bundle/gplayRelease/app-gplay-release.aab ../release/") buildApkFromBundle(outputDir: "../release", bundlePath: "../release/app-gplay-release.aab") sh("rm ../release/toc.pb") sh("mv ../release/universal.apk ../release/gplay-release-#{options[:versionCode]}.apk") end desc "Create Generic release" lane :createGenericRelease do |options| SignedRelease(flavor:"Generic") sh("mv ../app/build/outputs/apk/generic/release/*.apk ../release/") sh("mv ../release/generic-release-#{options[:versionCode]}.apk ../release/nextcloud-#{options[:versionCode]}.apk") end desc "Create Huawei release" lane :createHuaweiRelease do SignedRelease(flavor:"Huawei") sh("mv ../app/build/outputs/apk/huawei/release/*.apk ../release/") end desc "Create Dev release" lane :createDevRelease do SignedRelease(flavor:"VersionDev") sh("mv ../app/build/outputs/apk/versionDev/release/*.apk ../release/") end desc "Beautify phone for screenshots: set time, remove other icons, no charging" lane :beautifyPhone do sh("adb shell settings put global sysui_demo_allowed 1") sh("adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1200") sh("adb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e level 4 -e datatype false") sh("adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false") sh("adb shell am broadcast -a com.android.systemui.demo -e command battery -e plugged false -e level 100") end # to install # bundle exec fastlane add_plugin huawei_appgallery_connect # bundle install --path build/vendor/bundle private_lane :uploadToHuawei_Final do |options| huawei_appgallery_connect( client_id: ENV["FASTLANE_NEXTCLOUD_HUAWEI_CLIENT_ID"], client_secret: ENV["FASTLANE_NEXTCLOUD_HUAWEI_CLIENT_SECRET"], app_id: ENV["FASTLANE_NEXTCLOUD_HUAWEI_APP_ID"], apk_path: "release/huawei-release-" + options["versionCode"] + ".apk", submit_for_review: true, delay_before_submit_for_review: 30, ) end ## private lanes desc "Build debug and test APK for screenshots" private_lane :build_for_screengrab do build_android_app( task: 'assemble', flavor: 'Generic', build_type: 'Debug' ) APK_LOCATION = lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS].select{ |i| i[/00/] }[0] build_android_app( task: 'assemble', flavor: 'Generic', build_type: 'DebugAndroidTest' ) TEST_APK_LOCATION = lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS].select{ |i| i[/androidTest/] }[0] end private_lane :checkReleaseRequirements_common do sh("if ! command -v fdroid &>/dev/null; then echo 'fdroid command not found' && exit 1; fi") sh("if ! command -v bundletool &>/dev/null; then echo 'bundletool command not found' && exit 1; fi") end private_lane :checkReleaseRequirements_RC do checkReleaseRequirements_common() sh("if ! command -v fdroidMergeRequestRC &>/dev/null; then echo 'fdroidMergeRequestRC command not found'; exit 1; fi") sh("if ! command -v createChangelogPullRequestRC &>/dev/null; then echo 'createChangelogPullRequestRC command not found'; exit 1; fi") end private_lane :checkReleaseRequirements_Final do checkReleaseRequirements_common() sh("if ! command -v fdroidMergeRequestFinal &>/dev/null; then echo 'fdroidMergeRequestFinal command not found'; exit 1; fi") sh("if ! command -v createChangelogPullRequestFinal &>/dev/null; then echo 'createChangelogPullRequestFinal command not found'; exit 1; fi") end private_lane :createChangelogPullRequest_RC do |options| sh("createChangelogPullRequestRC " + options["tag"] + " \"" + options["versionName"] + "\" " + options["versionCode"] + " \"" + options["branch"] + "\" ") end private_lane :createChangelogPullRequest_Final do |options| sh("createChangelogPullRequestFinal " + options["tag"] + " \"" + options["versionName"] + "\" " + options["versionCode"] + " \"" + options["branch"] + "\" ") end private_lane :checkIfAPKexists do |options| sh("if [ $(/bin/ls -1 ../release | wc -l) -ne 4 ]; then echo 'Release artifacts not present or wrong number present; aborting!' ; exit 1 ; fi") end desc "compute version" private_lane :androidVersion do File.open("../app/build.gradle","r") do |f| text = f.read # everything between Document and Authors major = text.match(/def versionMajor = ([0-9]*)$/) minor = text.match(/def versionMinor = ([0-9]*)$/) patch = text.match(/def versionPatch = ([0-9]*)$/) build = text.match(/def versionBuild = ([0-9]*).*$/) majorInt = major[1].to_i minorInt = minor[1].to_i patchInt = patch[1].to_i buildInt = build[1].to_i versionCode = majorInt * 10000000 + minorInt * 10000 + patchInt * 100 + buildInt if buildInt > 89 name = major[1] + "." + minor[1] + "." + patch[1] tag = "stable-" + major[1] + "." + minor[1] + "." + patch[1] branch = "stable-" + major[1] + "." + minor[1] elsif buildInt > 50 name = major[1] + "." + minor[1] + "." + patch[1] + " RC" + (buildInt - 50).to_s tag = "rc-" + major[1] + "." + minor[1] + "." + patch[1] + "-" + format('%02d', (buildInt - 50)) branch = "stable-" + major[1] + "." + minor[1] else name = major[1] + "." + minor[1] + "." + patch[1] + " Alpha " + format('%02d', (buildInt + 1)) tag = "/" branch = "stable-" + major[1] + "." + minor[1] end { "versionCode" => versionCode.to_s, "versionName" => name, "tag" => tag, "branch" => branch } end end desc "Show versions and prompt for confirmation" private_lane :promptVersion do |options| print "VersionCode: " + options["versionCode"].to_s + "\n" print "Name: " + options["versionName"] + "\n" print "Tag: " + options["tag"] + "\n" print "Branch: " + options["branch"] + "\n" print "\ndisable IPv6 to upload to Gplay!!!\n" answer = prompt(text: "is this okay?", boolean: true) if !answer exit end end desc "check if library is set correctly" private_lane :checkLibrary_RC do sh(" if [ $(egrep 'androidLibraryVersion.*master.*' ../build.gradle -c) -eq 1 ] ; then echo 'Library is set to master tag; aborting!' ; exit 1 ; fi") end desc "check if library is set correctly: must NOT contain master nor rc" private_lane :checkLibrary_Final do sh(" if [ $(grep 'androidLibraryVersion' ../build.gradle | egrep 'master|rc' -c) -eq 1 ] ; then echo 'Library is still set to rc tag; aborting!' ; exit 1 ; fi") end desc "check if screenshots exists and exit" private_lane :checkIfScreenshotsExist do sh(" if [ $(find metadata/android/*/images -type f -not -name icon.png | grep -c . ) -gt 0 ] ; then echo 'Screenshots in fastlane folder exist; aborting!' ; exit 1 ; fi") end private_lane :tag do |options| add_git_tag( tag: options["tag"], sign: true ) push_git_tags( tag: options["tag"]) end private_lane :disableLogger do sh("sed -i s'#false#true#' ../app/src/main/res/values/setup.xml") end private_lane :enableLogger do sh("sed -i s'#true#false#' ../app/src/main/res/values/setup.xml") end desc "Upload to play store (beta)" private_lane :uploadToPlaystore_RC do |options| upload_to_play_store( skip_upload_images: true, track: 'beta', aab: "release/app-gplay-release.aab" ) end desc "Upload to play store" private_lane :uploadToPlaystore_Final do |options| upload_to_play_store( skip_upload_images: true, skip_upload_apk: true, aab: "release/app-gplay-release.aab" ) end desc "Build app bundle" private_lane :buildBundle do |options| gradle( task: 'bundle', flavor: options[:flavor], build_type: 'Release', print_command: false, properties: { "android.injected.signing.store.file" => ENV["FASTLANE_NEXTCLOUD_UPLOAD_STORE_FILE"], "android.injected.signing.store.password" => ENV["FASTLANE_NEXTCLOUD_UPLOAD_STORE_PASSWORD"], "android.injected.signing.key.alias" => ENV["FASTLANE_NEXTCLOUD_UPLOAD_KEY_ALIAS"], "android.injected.signing.key.password" => ENV["FASTLANE_NEXTCLOUD_UPLOAD_KEY_PASSWORD"], } ) end desc "Generate signed universal APK from app bundle" private_lane :buildApkFromBundle do |options| sh(command:"bundletool build-apks --mode universal --output '#{options[:outputDir]}' --output-format DIRECTORY" + " --ks '#{ENV["FASTLANE_NEXTCLOUD_STORE_FILE"]}'"+ " --ks-pass 'pass:#{ENV["FASTLANE_NEXTCLOUD_STORE_PASSWORD"]}'"+ " --ks-key-alias '#{ENV["FASTLANE_NEXTCLOUD_KEY_ALIAS"]}'"+ " --key-pass 'pass:#{ENV["FASTLANE_NEXTCLOUD_KEY_PASSWORD"]}'"+ " --bundle '#{options[:bundlePath]}'", log:false) end desc "Build signed APK" private_lane :SignedRelease do |options| gradle( task: 'assemble', flavor: options[:flavor], build_type: 'Release', print_command: false, properties: { "android.injected.signing.store.file" => ENV["FASTLANE_NEXTCLOUD_STORE_FILE"], "android.injected.signing.store.password" => ENV["FASTLANE_NEXTCLOUD_STORE_PASSWORD"], "android.injected.signing.key.alias" => ENV["FASTLANE_NEXTCLOUD_KEY_ALIAS"], "android.injected.signing.key.password" => ENV["FASTLANE_NEXTCLOUD_KEY_PASSWORD"], } ) end private_lane :createGithubRelease_RC do |options| set_github_release( repository_name: "nextcloud/android", api_token: ENV["FASTLANE_NEXTCLOUD_GITHUB_API_TOKEN"], name: options["versionName"], tag_name: options["tag"], is_prerelease: true, description: (File.read("metadata/android/en-US/changelogs/" + options["versionCode"] + ".txt") rescue "No changelog provided"), upload_assets: [ "release/gplay-release-" + options["versionCode"] + ".apk", "release/nextcloud-" + options["versionCode"] + ".apk" ] ) end private_lane :createGithubRelease_Final do |options| set_github_release( repository_name: "nextcloud/android", api_token: ENV["FASTLANE_NEXTCLOUD_GITHUB_API_TOKEN"], name: options["versionName"], tag_name: options["tag"], description: (File.read("metadata/android/en-US/changelogs/" + options["versionCode"] + ".txt") rescue "No changelog provided"), upload_assets: [ "release/gplay-release-" + options["versionCode"] + ".apk", "release/nextcloud-" + options["versionCode"] + ".apk" ] ) end private_lane :fdroidMergeRequest_RC do |options| sh("fdroidMergeRequestRC " + options["tag"] + " \"" + options["versionName"] + "\" " + options["versionCode"]) end private_lane :fdroidMergeRequest_Final do |options| sh("fdroidMergeRequestFinal " + options["tag"] + " \"" + options["versionName"] + "\" " + options["versionCode"]) end private_lane :checkChangelog do |options| sh(" if [ ! -e metadata/android/en-US/changelogs/" + options["versionCode"] + ".txt ] ; then echo 'Changelog fastlane/metadata/android/en-US/changelogs/" + options["versionCode"] + ".txt does not exist' ; exit 1 ; fi") sh(" if [ $(wc -m metadata/android/en-US/changelogs/" + options["versionCode"] + ".txt | cut -d' ' -f1) -ge 500 ] ; then echo 'Changlog more than 500 chars' ; exit 1 ; fi") end