From 33b82f238463642c8dcf68261555bc5821276e73 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 29 Sep 2026 08:47:38 +0200 Subject: [PATCH] extract Fastlane Signed-off-by: tobiasKaminsky --- .github/sync.yml | 6 + config/fastlane/common.Fastfile | 306 ++++++++++++++++++++++++ config/fastlane/common.Fastfile.license | 3 + config/root/Gemfile | 4 + config/root/Gemfile.license | 2 + config/root/Gemfile.lock | 263 ++++++++++++++++++++ config/root/Gemfile.lock.license | 2 + 7 files changed, 586 insertions(+) create mode 100644 config/fastlane/common.Fastfile create mode 100644 config/fastlane/common.Fastfile.license create mode 100644 config/root/Gemfile create mode 100644 config/root/Gemfile.license create mode 100644 config/root/Gemfile.lock create mode 100644 config/root/Gemfile.lock.license diff --git a/.github/sync.yml b/.github/sync.yml index 95da4bf..87228d1 100644 --- a/.github/sync.yml +++ b/.github/sync.yml @@ -13,6 +13,12 @@ group: - source: config/workflows/ dest: .github/workflows/ + - source: config/fastlane/ + dest: fastlane/ + + - source: config/root/ + dest: . + - repos: | nextcloud/android-library@master nextcloud/android-common@main diff --git a/config/fastlane/common.Fastfile b/config/fastlane/common.Fastfile new file mode 100644 index 0000000..4027e3b --- /dev/null +++ b/config/fastlane/common.Fastfile @@ -0,0 +1,306 @@ +# install fastlane via bundle +# cd fastlane +# bundle config set path fastlane +# bundle install + +# run fastlane +# bundle exec fastlane + +# update fastlane +# bundle update fastlane + +# update plugins +# bundle exec fastlane update_plugins + +BUILD_NUMBER_RC_START = 51 +BUILD_NUMBER_FINAL_START = 90 + +MAJOR_MULTIPLIER = 10000000 +MINOR_MULTIPLIER = 10000 +PATCH_MULTIPLIER = 100 + +TYPE_ALPHA = "alpha" +TYPE_RC = "rc" +TYPE_FINAL = "final" + +BUNDLE_PATH = "app/build/outputs/bundle/gplayRelease/app-gplay-release.aab" + +default_platform(:android) + +ENV['SUPPLY_UPLOAD_MAX_RETRIES']='10' + +platform :android do + desc "Run tests and build app" + lane :releasePhase1 do + sh("rm -rf ../release/ && mkdir -p ../release") + gradle(task: 'clean') + + versionInfo = getVersionInfo() + test() # needs to be overwritten in apps + + buildBundle() + buildAPKs(code: versionInfo["versionCode"]) + end + + desc "Print version info" + lane :printVersionInfo do + versionInfo = getVersionInfo() + versionComponents = parseVersionCode(versionInfo) + print "Version code: #{versionInfo["versionCode"]}\n" + print "Version name: #{versionInfo["versionName"]}\n" + print "Major: #{versionComponents["major"]}\n" + print "Minor: #{versionComponents["minor"]}\n" + print "Patch: #{versionComponents["patch"]}\n" + print "Build: #{versionComponents["build"]}\n" + end + + # Usage: fastlane incrementVersion type:(major|minor|patch|rc|final) + # For major, minor, and patch: will increment that version number by 1 and set the smaller ones to 0 + # For rc, final: will set build number to first rc/first final or increment it by 1 + desc "Increment version code and version name" + lane :incrementVersion do |options| + versionInfo = getVersionInfo() + versionComponents = parseVersionCode(versionInfo) + newVersionComponents = incrementVersionComponents(versionComponents: versionComponents, type: options[:type]) + versionNameGenerated = generateVersionName(newVersionComponents) + versionCodeGenerated = generateVersionCode(newVersionComponents) + + promptYesNo(text: "Version code: #{versionInfo["versionCode"]} -> #{versionCodeGenerated}\n" + + "Version name: #{versionInfo["versionName"]} -> #{versionNameGenerated}" + ) + writeVersions(versionCode: versionCodeGenerated, versionName: versionNameGenerated) + end + + desc "Upload Alpha version to play store" + lane :uploadAlphaToPlayStore do |options| + upload_to_play_store( + skip_upload_images: true, + skip_upload_aab: true, + skip_upload_changelogs: true, + skip_upload_metadata: true, + skip_upload_screenshots: true, + track: 'alpha', + apk: "/home/androiddaily/apks-#{APP}/android-#{APP}-" + options[:version] + ".apk", + ) + end + + + desc "Parse major, minor, patch and build from versionCode" + private_lane :parseVersionCode do |versionInfo| + versionCode = versionInfo["versionCode"] + build = versionCode % 100 + patch = (versionCode / PATCH_MULTIPLIER) % 100 + minor = (versionCode / MINOR_MULTIPLIER) % 100 + major = (versionCode / MAJOR_MULTIPLIER) % 100 + + type = getVersionType(build: build) + + + { "major" => major, "minor" => minor, "patch" => patch, "build" => build, "type" => type } + end + + desc "Get version type from build number" + private_lane :getVersionType do |options| + build = options[:build] + if build < BUILD_NUMBER_RC_START + type = TYPE_ALPHA + elsif build < BUILD_NUMBER_FINAL_START + type = TYPE_RC + else + type = TYPE_FINAL + end + type + end + + + + desc "Generate versionCode from version components" + private_lane :generateVersionCode do |versionComponents| + puts "Generating version code from #{versionComponents}" + major = versionComponents["major"] + minor = versionComponents["minor"] + patch = versionComponents["patch"] + build = versionComponents["build"] + test = major * MAJOR_MULTIPLIER + minor * MINOR_MULTIPLIER + patch * PATCH_MULTIPLIER + build + test + end + + desc "Compute version name from version code" + private_lane :generateVersionName do |versionComponents| + suffix = "" + buildNumber = versionComponents["build"] + puts "Generating version name from #{versionComponents}\n" + case + when versionComponents["type"] == TYPE_RC + rcNumber = (buildNumber - BUILD_NUMBER_RC_START) + 1 + suffix = " RC#{rcNumber}" + when versionComponents["type"] == TYPE_ALPHA + suffix = " Alpha#{buildNumber + 1}" + end + "#{versionComponents["major"]}.#{versionComponents["minor"]}.#{versionComponents["patch"]}#{suffix}" + end + + desc "Read versions from gradle file" + private_lane :getVersionInfo do + File.open("../app/build.gradle.kts","r") do |file| + text = file.read + versionName = text.match(/versionName = "(.*)"$/)[1] + versionCode = text.match(/versionCode = ([0-9]*)$/)[1].to_i + + { "versionCode" => versionCode, "versionName" => versionName } + end + end + + desc "Write versions to gradle file" + private_lane :writeVersions do |options| + File.open("../app/build.gradle","r+") do |file| + text = file.read + text.gsub!(/versionName "(.*)"$/, "versionName \"#{options[:versionName]}\"") + text.gsub!(/versionCode ([0-9]*)$/, "versionCode #{options[:versionCode]}") + file.rewind + file.write(text) + file.truncate(file.pos) + end + end + + private_lane :incrementVersionComponents do |options| + versionComponents = options[:versionComponents] + case options[:type] + when "major" + versionComponents["major"] = versionComponents["major"] + 1 + versionComponents["minor"] = 0 + versionComponents["patch"] = 0 + versionComponents["build"] = 0 + when "minor" + versionComponents["minor"] = versionComponents["minor"] + 1 + versionComponents["patch"] = 0 + versionComponents["build"] = 0 + when "patch" + versionComponents["patch"] = versionComponents["patch"] + 1 + versionComponents["build"] = 0 + when "rc" + if versionComponents["type"] != TYPE_RC + versionComponents["build"] = BUILD_NUMBER_RC_START + else + versionComponents["build"] = versionComponents["build"] + 1 + end + when "final" + if versionComponents["type"] != TYPE_FINAL + versionComponents["build"] = BUILD_NUMBER_FINAL_START + else + versionComponents["build"] = versionComponents["build"] + 1 + end + else + UI.user_error!("Unknown or missing version increment type #{options[:type]}. Usage: incrementVersion type:(major|minor|patch|rc|final)") + end + versionComponents["type"] = getVersionType(build: versionComponents["build"]) + versionComponents + end + + desc "Get tag name from version components" + private_lane :getTagName do |versionComponents| + if versionComponents["type"] == TYPE_FINAL + tag = "#{versionComponents["major"]}.#{versionComponents["minor"]}.#{versionComponents["patch"]}" + elsif versionComponents["type"] == TYPE_RC + rcNumber = (versionComponents["build"] - BUILD_NUMBER_RC_START) + 1 + rcNumberPadded = "%02d" % rcNumber + tag = "rc-#{versionComponents["major"]}.#{versionComponents["minor"]}.#{versionComponents["patch"]}-#{rcNumberPadded}" + else + UI.user_error!("Build number cannot be tagged: #{versionComponents["build"]}") + end + end + + desc "Check if version is releasable" + private_lane :checkReleasable do |versionComponents| + if versionComponents["type"] != TYPE_FINAL && versionComponents["type"] != TYPE_RC + UI.user_error!("Version is not releasable: #{versionComponents["type"]}") + end + end + + desc "Get play store track from version type" + private_lane :getPlayStoreTrack do |versionComponents| + case versionComponents["type"] + when TYPE_RC + track = "beta" + when TYPE_FINAL + track = "production" + else + UI.user_error!("Version is not releasable: #{versionComponents["type"]}") + end + end + + desc "Run tests" + private_lane :test do + gradle(task: "clean testGplayDebugUnitTest testGenericDebugUnitTest testQaDebugUnitTest") + end + + desc "Build app bundle" + private_lane :buildBundle do + gradle( + task: 'bundle', + flavor: 'Gplay', + build_type: 'Release', + print_command: false, + properties: GRADLE_SIGNING_PROPERTIES + ) + end + + desc "Build signed APK" + private_lane :SignedRelease do |options| + gradle( + task: 'assemble', + flavor: options[:flavor], + build_type: 'Release', + print_command: false, + properties: GRADLE_SIGNING_PROPERTIES + ) + + sh("mv ../app/build/outputs/apk/#{options[:flavor]}/release/*.apk ../release/nextcloud-#{APP}-#{options[:flavor]}-#{options[:versionCode]}.apk") + end + + desc "Build all APKs" + private_lane :buildAPKs do |options| + SignedRelease(flavor: "generic", versionCode: options[:code]) + SignedRelease(flavor: "gplay", versionCode: options[:code]) + + if APP == "files" + SignedRelease(flavor: "huawei", versionCode: options[:code]) + end + end + + desc "Create release tag" + private_lane :tag do |options| + tagName = options[:name] + add_git_tag( + tag: tagName, + sign: true + ) + push_git_tags(tag: tagName) + end + + desc "Upload release artifacts to Google Play" + private_lane :uploadToPlayStore do |options| + upload_to_play_store( + skip_upload_images: true, + skip_upload_apk: true, + track: options[:track], + aab: BUNDLE_PATH + ) + end + + desc "Check if release artifacts exist" + private_lane :checkArtifactsExist do + if !File.exist?("../#{BUNDLE_PATH}") + UI.user_error!("Bundle not found at #{BUNDLE_PATH}") + end + end + +end + +private_lane :promptYesNo do |options| + puts "\n" + options[:text] + answer = prompt(text: "is this okay?", boolean: true) + if !answer + UI.user_error!("Aborting") + end +end diff --git a/config/fastlane/common.Fastfile.license b/config/fastlane/common.Fastfile.license new file mode 100644 index 0000000..8362977 --- /dev/null +++ b/config/fastlane/common.Fastfile.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors +SPDX-FileCopyrightText: 2023 Álvaro Brey +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/config/root/Gemfile b/config/root/Gemfile new file mode 100644 index 0000000..e846838 --- /dev/null +++ b/config/root/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem "fastlane" +gem "abbrev" diff --git a/config/root/Gemfile.license b/config/root/Gemfile.license new file mode 100644 index 0000000..7a2ef8e --- /dev/null +++ b/config/root/Gemfile.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: CC0-1.0 diff --git a/config/root/Gemfile.lock b/config/root/Gemfile.lock new file mode 100644 index 0000000..0584204 --- /dev/null +++ b/config/root/Gemfile.lock @@ -0,0 +1,263 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.8) + abbrev (0.1.2) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.4.0) + aws-partitions (1.1290.0) + aws-sdk-core (3.257.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + rexml (~> 3.4, >= 3.4.2) + aws-sdk-kms (1.132.0) + aws-sdk-core (~> 3, >= 3.256.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.232.2) + aws-sdk-core (~> 3, >= 3.256.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.1.3) + cgi (0.5.2) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + csv (3.3.6) + declarative (0.0.20) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20260921) + dotenv (2.8.1) + emoji_regex (3.2.3) + erb (6.0.7) + excon (1.7.1) + logger + faraday (2.14.4) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-cookie_jar (0.0.8) + faraday (>= 0.8.0) + http-cookie (>= 1.0.0) + faraday-follow_redirects (0.5.0) + faraday (>= 1, < 3) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) + faraday-net_http (3.4.4) + net-http (~> 0.5) + faraday-retry (2.4.0) + faraday (~> 2.0) + fastimage (2.4.1) + fastlane (2.240.1) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) + addressable (>= 2.9.0, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.197) + babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) + cgi (~> 0.4) + colored (~> 1.2) + commander (~> 4.6) + csv (~> 3.3) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 2.0.0) + faraday (~> 2.7) + faraday-cookie_jar (~> 0.0.8) + faraday-follow_redirects (~> 0.3) + faraday-multipart (~> 1.0) + faraday-retry (~> 2.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.1.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.99) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.4.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + irb (>= 1.8) + json (< 3.0.0) + jwt (>= 2.10.3, < 4) + logger (>= 1.6, < 2.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_json (~> 1.12) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) + naturally (~> 2.2) + nkf (~> 0.2) + optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 3.4.0, < 4.0.0) + security (~> 0.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 4) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.109.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (1.2.5) + addressable (~> 2.9) + faraday (~> 2.13) + faraday-follow_redirects (~> 0.3) + googleauth (~> 1.14) + mini_mime (~> 1.1) + multi_json (~> 1.11) + representable (~> 3.0) + retriable (>= 3.1, < 5.0) + google-apis-iamcredentials_v1 (0.28.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.18.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.68.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.9.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (2.3.1) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.7.0) + google-cloud-storage (1.62.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) + google-cloud-core (~> 1.6) + googleauth (~> 1.9) + mini_mime (~> 1.0) + google-logging-utils (0.2.0) + googleauth (1.17.4) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) + os (>= 0.9, < 2.0) + pstore (~> 0.1) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.1.6) + domain_name (~> 0.5) + io-console (0.9.4) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jmespath (1.6.2) + json (2.21.2) + jwt (3.3.0) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.21.2) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + net-http (0.9.1) + uri (>= 0.11.1) + nkf (0.3.0) + optparse (0.8.1) + os (1.1.4) + ostruct (0.6.3) + plist (3.7.2) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pstore (0.2.1) + public_suffix (7.0.5) + rake (13.4.2) + rbs (4.2.0) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort + reline (0.7.0) + io-console (~> 0.5) + representable (3.3.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (4.2.0) + rexml (3.4.4) + rouge (3.28.0) + rubyzip (3.7.0) + security (0.3.0) + signet (0.22.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + trailblazer-option (0.1.2) + tsort (0.2.0) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (3.3.0) + unicode-emoji (~> 4.3) + unicode-emoji (4.3.0) + uri (1.1.1) + word_wrap (1.0.0) + xcodeproj (1.28.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + base64 + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + nkf + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + abbrev + fastlane + +BUNDLED WITH + 2.5.4 diff --git a/config/root/Gemfile.lock.license b/config/root/Gemfile.lock.license new file mode 100644 index 0000000..7a2ef8e --- /dev/null +++ b/config/root/Gemfile.lock.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: CC0-1.0