feat/prepare-android-pipeline-for-rc #24
@ -4,7 +4,7 @@
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-android_cd`, add the dependencies into your `PluginFile` :
|
||||
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-android_cd` , add the dependencies into your `PluginFile` :
|
||||
|
||||
```ruby
|
||||
gem "fastlane-plugin-android_cd", git: "https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-fastlane-plugins.git", branch: "main", glob: 'plugins/fastlane-plugin-android_cd/*.gemspec'
|
||||
@ -22,7 +22,7 @@ To use these actions, you need to add some information to your Android folder so
|
||||
|
||||
In your android folder, place a compressed folder containing:
|
||||
|
||||
- The application's signing key (in the .jks format). If this is a new application, you can generate this key with the following command:
|
||||
* The application's signing key (in the .jks format). If this is a new application, you can generate this key with the following command:
|
||||
|
||||
```shel
|
||||
keytool -genkey -v -keystore key_store_name.keystore -alias key_alias_name -keyalg RSA -keysize 2048 -validity 10000
|
||||
@ -30,29 +30,39 @@ keytool -genkey -v -keystore key_store_name.keystore -alias key_alias_name -keya
|
||||
|
||||
During the execution of the above command, you will be prompted to provide a password for your key. Make sure to remember this password, as you will need it later.
|
||||
|
||||
- The key.properties file containing sensitive data for using the signing key, such as the path of the key or the password. If this file does not exist yet, create it and fill in the following fields:
|
||||
* The key.properties file containing sensitive data for using the signing key, such as the path of the key or the password. If this file does not exist yet, create it and fill in the following fields:
|
||||
|
||||
- `storeFile`: the relative or absolute path to the key storage file that contains the private key used to sign the Android application.
|
||||
- `storePassword`: the password used to access the key storage file.
|
||||
- `keyAlias`: the alias of the key used to sign the application.
|
||||
- `keyPassword`: the password used to access the key.
|
||||
+ `storeFile`: the relative or absolute path to the key storage file that contains the private key used to sign the Android application.
|
||||
+ `storePassword`: the password used to access the key storage file.
|
||||
+ `keyAlias`: the alias of the key used to sign the application.
|
||||
+ `keyPassword`: the password used to access the key.
|
||||
|
||||
- The credentials in JSON format to allow the fastlane action to connect to the Google Play Store console and upload the new build. To retrieve them, follow these steps:
|
||||
* The credentials in JSON format to allow the fastlane action to connect to the Google Play Store console and upload the new build. To retrieve them, follow these steps:
|
||||
|
||||
- Go to your Google Play Store console.
|
||||
- In the Settings menu, select `API access`, then click `Create Service Account`
|
||||
- Navigate to the provided Google Developers Console link in the dialog
|
||||
- Click `Create Service Account` at the top of the Google Developers Console
|
||||
- Provide the required details, then click `Create`
|
||||
- Click `Select a role`, select `Service Accounts`, then click `Service Account User`
|
||||
- In the Service Accounts dashboard, navigate to the Actions column, tap the menu for the service account that you created, then click `Create Key`
|
||||
- Select JSON as the key type, then click `Save`
|
||||
- Back on the Google Play Console, click `Done` to close the dialog
|
||||
- Click on `Grant Access` for the newly added service account
|
||||
- Make sure that the role of this service account has the permission to upload builds
|
||||
- Click `Add User` to close the dialog
|
||||
+ Go to your Google Play Store console.
|
||||
+ In the Settings menu, select `API access`, then click `Create Service Account`
|
||||
+ Navigate to the provided Google Developers Console link in the dialog
|
||||
+ Click `Create Service Account` at the top of the Google Developers Console
|
||||
+ Provide the required details, then click `Create`
|
||||
+ Click `Select a role`, select `Service Accounts`, then click `Service Account User`
|
||||
+ In the Service Accounts dashboard, navigate to the Actions column, tap the menu for the service account that you created, then click `Create Key`
|
||||
+ Select JSON as the key type, then click `Save`
|
||||
+ Back on the Google Play Console, click `Done` to close the dialog
|
||||
+ Click on `Grant Access` for the newly added service account
|
||||
+ Make sure that the role of this service account has the permission to upload builds
|
||||
+ Click `Add User` to close the dialog
|
||||
|
||||
Once you have all three elements in your folder, it's time to encrypt them. Indeed, this folder contains elements that are too sensitive to be referenced on git. Compress the folder, place the compressed folder in the android folder of your Flutter project, and execute the following command:
|
||||
Once you have all three elements in your folder, it's time to encrypt them. Indeed, this folder contains elements that are too sensitive to be referenced on git.
|
||||
|
||||
Place them in a folder named `android_keys` and compress it using the following command:
|
||||
|
||||
```shell
|
||||
jar cfvM android_keys.zip android_keys
|
||||
```
|
||||
|
||||
> The plugin will use `jar xvf android/android_keys.zip && mv android/android_keys/* android/` to extract the secret files.
|
||||
|
||||
Then, encrypt the compressed folder using GPG.
|
||||
|
||||
```shell
|
||||
gpg --quiet --batch --yes --symmetric --passphrase="<android-key-passphrase>" --output android/android_keys.zip.gpg android/android_keys.zip
|
||||
@ -74,7 +84,7 @@ build_and_deploy(beta_type: "internal")
|
||||
promote(from: "internal", destination: "beta")
|
||||
```
|
||||
|
||||
note that beta type can be `production`, `beta`, `alpha`, `internal`.
|
||||
note that beta type can be `production` , `beta` , `alpha` , `internal` .
|
||||
|
||||
## Run tests for this plugin
|
||||
|
||||
|
@ -14,24 +14,11 @@ module Fastlane
|
||||
|
||||
# Decrypt the keys archive and Extract the keys archive
|
||||
Helper::AndroidCdHelper.decrypt_android_keys('.')
|
||||
|
||||
UI.message("👉🏼 Credentials decrypted")
|
||||
|
||||
# Clean the project before building
|
||||
other_action.gradle(task: "clean")
|
||||
|
||||
# Set the build number based on the number of commits
|
||||
build_number = other_action.number_of_commits
|
||||
|
||||
# Build the Android App Bundle
|
||||
other_action.gradle(
|
||||
task: "bundle",
|
||||
build_type: "Release",
|
||||
print_command: true,
|
||||
project_dir: './',
|
||||
properties: {
|
||||
"android.injected.version.code" => build_number
|
||||
}
|
||||
other_action.flutter_build(
|
||||
build: 'appbundle'
|
||||
)
|
||||
|
||||
UI.message("👉🏼 App built")
|
||||
@ -44,15 +31,12 @@ module Fastlane
|
||||
skip_upload_metadata: true,
|
||||
skip_upload_images: true,
|
||||
skip_upload_screenshots: true,
|
||||
release_status: "draft",
|
||||
version_code: build_number
|
||||
release_status: "draft"
|
||||
)
|
||||
|
||||
# Delete artifacts files
|
||||
artifacts = ['android_keys.zip', 'key.jks', 'key.properties', 'service_account_key.json']
|
||||
artifacts.each do |file|
|
||||
File.delete(file) if File.exist?(file)
|
||||
end
|
||||
Helper::AndroidCdHelper.delete_artifacts(artifacts)
|
||||
|
||||
UI.success('🍺 Successfully build & deploy appbundle to Google Play Store')
|
||||
end
|
||||
|
@ -26,6 +26,13 @@ module Fastlane
|
||||
str_variable = variable.strip if variable.class.to_s == "String"
|
||||
variable && !(str_variable.nil? || str_variable.empty?)
|
||||
end
|
||||
|
||||
# Delete built files
|
||||
def self.delete_artifacts(artifacts)
|
||||
artifacts.each do |file|
|
||||
File.delete(file) if File.exist?(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -21,25 +21,19 @@ module Fastlane
|
||||
|
||||
# Decrypt the keys archive and Extract the keys archive
|
||||
Helper::IosCdHelper.decrypt_ios_keys('.')
|
||||
UI.message("👉🏼 Credentials decrypted.")
|
||||
UI.message("🍺 Credentials decrypted.")
|
||||
|
||||
# Retrieve credentials
|
||||
creds = Helper::IosCdHelper.parse_ios_credentials('.')
|
||||
UI.message("👉🏼 Credentials parsed.")
|
||||
UI.message("🍺 Credentials parsed.")
|
||||
|
||||
# Delete decrypted artifacts
|
||||
artifacts = ['ios_keys.zip', 'ios_credentials.json']
|
||||
artifacts.each do |file|
|
||||
File.delete(file) if File.exist?(file)
|
||||
end
|
||||
Helper::IosCdHelper.delete_artifacts(artifacts)
|
||||
|
||||
# Check credentials
|
||||
required_fields = ['developer_app_id', 'username', 'developer_app_identifier', 'app_identifier_extensions', 'apple_issuer_id', 'apple_key_id', 'team_id', 'team_name', 'apple_key_content', 'git_url', 'git_basic_authorization', 'provisioning_profiles', 'temp_keychain_user', 'temp_keychain_password']
|
||||
missing_fields = required_fields - creds.keys
|
||||
|
||||
unless missing_fields.empty?
|
||||
raise ArgumentError, "❌ missing keys in credential json file : #{missing_fields}"
|
||||
end
|
||||
Helper::IosCdHelper.check_required_fields(required_fields, creds.keys)
|
||||
|
||||
# Delete keychain if existing
|
||||
if File.exist?(File.expand_path("~/Library/Keychains/#{name}-db"))
|
||||
@ -55,7 +49,7 @@ module Fastlane
|
||||
unlock: false,
|
||||
timeout: 0
|
||||
)
|
||||
UI.message("👉🏼 New keychain created")
|
||||
UI.message("🍺 New keychain created")
|
||||
|
||||
# Obtain App Store Connect API key
|
||||
api_key = other_action.app_store_connect_api_key(
|
||||
@ -65,7 +59,7 @@ module Fastlane
|
||||
duration: 500,
|
||||
in_house: false
|
||||
)
|
||||
UI.message("👉🏼 API Key formated")
|
||||
UI.message("🍺 API Key formated")
|
||||
|
||||
last_testflight_build_number =
|
||||
other_action.latest_testflight_build_number(
|
||||
@ -82,7 +76,7 @@ module Fastlane
|
||||
build_number: last_testflight_build_number,
|
||||
xcodeproj: "Runner.xcodeproj"
|
||||
)
|
||||
UI.message("👉🏼 Build number incremented")
|
||||
UI.message("🍺 Build number incremented")
|
||||
|
||||
# Install Cocoapods
|
||||
other_action.cocoapods(
|
||||
@ -91,7 +85,7 @@ module Fastlane
|
||||
integrate: true,
|
||||
podfile: "./Podfile"
|
||||
)
|
||||
UI.message("👉🏼 Pod got")
|
||||
UI.message("🍺 Pod got")
|
||||
|
||||
# Set up code signing using match
|
||||
# Configures and runs `match` which manages code signing certificates and provisioning profiles for the project.
|
||||
@ -105,14 +99,13 @@ module Fastlane
|
||||
git_basic_authorization: Base64.strict_encode64(creds['git_basic_authorization']),
|
||||
keychain_name: creds['temp_keychain_user'].to_s,
|
||||
keychain_password: creds['temp_keychain_password'].to_s,
|
||||
git_url: creds['git_url'].to_s,
|
||||
username: creds['username'].to_s,
|
||||
team_id: creds['team_id'].to_s,
|
||||
team_name: creds['team_name'].to_s,
|
||||
git_url: creds['git_url'].to_s,
|
||||
storage_mode: "git"
|
||||
)
|
||||
UI.message("👉🏼 App signed")
|
||||
UI.message("🍺 App signed")
|
||||
|
||||
# Build and export app using Gym
|
||||
# Builds and packages an iOS app or framework for distribution to the App Store, TestFlight, or Enterprise distribution.
|
||||
@ -124,7 +117,7 @@ module Fastlane
|
||||
provisioningProfiles: creds['provisioning_profiles']
|
||||
}
|
||||
)
|
||||
UI.message("👉🏼 App built")
|
||||
UI.message("🍺 App built")
|
||||
|
||||
# Upload build to App Store Connect using Pilot
|
||||
other_action.pilot(
|
||||
@ -147,9 +140,7 @@ module Fastlane
|
||||
|
||||
# Delete build artifacts
|
||||
artifacts = ['Runner.app.dSYM.zip', 'Runner.ipa']
|
||||
artifacts.each do |file|
|
||||
File.delete(file) if File.exist?(file)
|
||||
end
|
||||
Helper::IosCdHelper.delete_artifacts(artifacts)
|
||||
end
|
||||
|
||||
def self.description
|
||||
|
@ -42,6 +42,31 @@ module Fastlane
|
||||
puts("json file doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
# Check json fields
|
||||
def self.check_required_fields(required_fields, json)
|
||||
missing_fields = required_fields - json
|
||||
|
||||
unless missing_fields.empty?
|
||||
raise ArgumentError, "❌ missing keys in credential json file : #{missing_fields}"
|
||||
end
|
||||
end
|
||||
|
||||
# Check if set of environment variables are defined
|
||||
def self.check_environment_variables(variables)
|
||||
variables.each do |variable|
|
||||
unless ENV.key?(variable)
|
||||
raise "❌ The environment variable '#{variable}' is not defined."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Delete built files
|
||||
def self.delete_artifacts(artifacts)
|
||||
artifacts.each do |file|
|
||||
File.delete(file) if File.exist?(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user