diff --git a/plugins/fastlane-plugin-android_cd/lib/fastlane/plugin/android_cd/actions/build_and_deploy_action.rb b/plugins/fastlane-plugin-android_cd/lib/fastlane/plugin/android_cd/actions/build_and_deploy_action.rb index 316ab0e..912ddd9 100644 --- a/plugins/fastlane-plugin-android_cd/lib/fastlane/plugin/android_cd/actions/build_and_deploy_action.rb +++ b/plugins/fastlane-plugin-android_cd/lib/fastlane/plugin/android_cd/actions/build_and_deploy_action.rb @@ -7,7 +7,7 @@ module Fastlane def self.run(params) # Check parameters unless Helper::AndroidCdHelper.is_set(params[:beta_type]) - UI.error("Parameters beta_type cannot be null") + UI.error("❌ Parameters beta_type cannot be null") puts("Error on beta type parameter") end diff --git a/plugins/fastlane-plugin-ios_cd/.circleci/config.yml b/plugins/fastlane-plugin-ios_cd/.circleci/config.yml deleted file mode 100644 index a049044..0000000 --- a/plugins/fastlane-plugin-ios_cd/.circleci/config.yml +++ /dev/null @@ -1,43 +0,0 @@ -# Ruby CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-ruby/ for more details -# -version: 2 -jobs: - build: - docker: - # specify the version you desire here - - image: circleci/ruby:2.5 - - working_directory: ~/repo - - steps: - - checkout - - # Download and cache dependencies - - restore_cache: - keys: - - v1-dependencies-{{ checksum "Gemfile" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - - run: - name: install dependencies - command: bundle check || bundle install --jobs=4 --retry=3 --path vendor/bundle - - - save_cache: - paths: - - ./vendor - key: v1-dependencies-{{ checksum "Gemfile" }} - - # run tests! - - run: - name: run tests - command: bundle exec rake - - # collect reports - - store_test_results: - path: ~/repo/test-results - - store_artifacts: - path: ~/repo/test-results - destination: test-results diff --git a/plugins/fastlane-plugin-ios_cd/Gemfile b/plugins/fastlane-plugin-ios_cd/Gemfile index 7e8dba6..c16379a 100644 --- a/plugins/fastlane-plugin-ios_cd/Gemfile +++ b/plugins/fastlane-plugin-ios_cd/Gemfile @@ -2,5 +2,7 @@ source('https://rubygems.org') gemspec +gem 'json' + plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/build_and_deploy_action.rb b/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/build_and_deploy_action.rb new file mode 100644 index 0000000..97c408d --- /dev/null +++ b/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/build_and_deploy_action.rb @@ -0,0 +1,116 @@ +require 'fastlane/action' +require_relative '../helper/ios_cd_helper' + +module Fastlane + module Actions + class BuildAndDeploy < Action + def self.run(params) + # Check parameters + unless Helper::IosCdHelper.is_set(params[:beta_type]) + UI.error("❌ Parameters beta_type cannot be null") + puts("Error on beta type parameter") + end + + UI.message("⌛️ Building and deploying to Store in #{params[:beta_type]}..") + + # Decrypt the keys archive and Extract the keys archive + Helper::IosCdHelper.decrypt_ios_keys('.') + + # Retrieve credentials + creds = Helper::IosCdHelper.parseIosCredentials + + UI.message(creds.to_s) + + # Ensure temporary keychain exists + Fastlane::Actions.ensure_temp_keychain(creds['temp_keychain_user'], creds['temp_keychain_password']) + + # Obtain App Store Connect API key + api_key = Fastlane::Actions.app_store_connect_api_key( + key_id: creds['apple_key_id'], + issuer_id: creds['apple_issuer_id'], + key_content: creds['apple_key_content'], + duration: 1200, + in_house: false + ) + + # Increment build number for latest TestFlight build + Fastlane::Actions.increment_build_number({ + build_number: Fastlane::Actions.latest_testflight_build_number + 1, + xcodeproj: "Runner.xcodeproj" + }) + + # Install Cocoapods + Fastlane::Actions.cocoapods( + clean_install: true + ) + + # Set up code signing using match + # Configures and runs `match` which manages code signing certificates and provisioning profiles for the project. + # The function takes the app's bundle identifier, an authorization token for the project's Git repository, and the name and password for a temporary keychain used to store the signing certificate. + # It uses the App Store Connect API key to access the App Store and increment the build number. + # It then runs `gym` to build and sign the app using the selected provisioning profile, and finally, uses `pilot` to upload the app to TestFlight for beta testing. + Fastlane::Actions.match( + type: 'appstore', + app_identifier: creds['app_identifier_extensions'], + git_basic_authorization: Base64.strict_encode64(ENV["GIT_AUTHORIZATION"]), + keychain_name: creds['temp_keychain_user'], + keychain_password: creds['temp_keychain_password'], + api_key: api_key + ) + + # Build and export app using Gym + # Builds and packages an iOS app or framework for distribution to the App Store, TestFlight, or Enterprise distribution. + Fastlane::Actions.gym( + configuration: "Release", + workspace: "Runner.xcworkspace", + scheme: "your_schema", + export_method: "app-store", + export_options: { + provisioningProfiles: creds['provisioning_profiles'] + } + ) + + # Upload build to App Store Connect using Pilot + Fastlane::Actions.pilot( + apple_id: creds['developer_app_id'].to_s, + app_identifier: creds['developer_app_identifier'].to_s, + skip_waiting_for_build_processing: true, + skip_submission: true, + distribute_external: false, + notify_external_testers: false, + ipa: "./Runner.ipa" + ) + + Fastlane::Actions.delete_temp_keychain + end + + def self.description + "Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing." + end + + def self.authors + ["SAS Wyatt Studio"] + end + + def self.return_value + end + + def self.details + "The Fastlane Testflight / Appstore deloyment action streamlines the build and deployment to internal, external and production channels. Allow you to promote builds on testflight beta tests." + end + + def self.available_options + [ + FastlaneCore::ConfigItem.new(key: :beta_type, + env_name: "IOS_CD_TEST_TYPE", + optional: false, + description: "Type of test (internal, external, production)") + ] + end + + def self.is_supported?(platform) + true + end + end + end +end diff --git a/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/ios_cd_action.rb b/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/ios_cd_action.rb deleted file mode 100644 index c8ae752..0000000 --- a/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/actions/ios_cd_action.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'fastlane/action' -require_relative '../helper/ios_cd_helper' - -module Fastlane - module Actions - class IosCdAction < Action - def self.run(params) - UI.message("The ios_cd plugin is working!") - end - - def self.description - "Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing." - end - - def self.authors - ["Malo Léon"] - end - - def self.return_value - # If your method provides a return value, you can describe here what it does - end - - def self.details - # Optional: - "The Fastlane Testflight / Appstore deloyment action streamlines the build and deployment to internal, external and production channels. Allow you to promote builds on testflight beta tests." - end - - def self.available_options - [ - # FastlaneCore::ConfigItem.new(key: :your_option, - # env_name: "IOS_CD_YOUR_OPTION", - # description: "A description of your option", - # optional: false, - # type: String) - ] - end - - def self.is_supported?(platform) - # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example) - # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform - # - # [:ios, :mac, :android].include?(platform) - true - end - end - end -end diff --git a/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/helper/ios_cd_helper.rb b/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/helper/ios_cd_helper.rb index 956e06f..c5ee24d 100644 --- a/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/helper/ios_cd_helper.rb +++ b/plugins/fastlane-plugin-ios_cd/lib/fastlane/plugin/ios_cd/helper/ios_cd_helper.rb @@ -1,15 +1,71 @@ +require 'fastlane/action' +require 'json' require 'fastlane_core/ui/ui' module Fastlane - UI = FastlaneCore::UI unless Fastlane.const_defined?("UI") - module Helper class IosCdHelper - # class methods that you define here become available in your action - # as `Helper::IosCdHelper.your_method` - # - def self.show_message - UI.message("Hello from the ios_cd plugin helper!") + # Define method to delete temporary keychain + def delete_temp_keychain(name) + if File.exist?(File.expand_path("~/Library/Keychains/#{name}-db")) + Fastlane::Actions.delete_keychain( + name: name + ) + end + end + + # Define method to create temporary keychain + def create_temp_keychain(name, password) + Fastlane::Actions.create_keychain( + name: name, + password: password, + unlock: false, + timeout: 0 + ) + end + + # Define method to ensure that temporary keychain exists + def ensure_temp_keychain(name, password) + delete_temp_keychain(name) + create_temp_keychain(name, password) + end + + # Check if a parameter is set or not + def self.is_set(variable) + str_variable = variable + str_variable = variable.strip if variable.class.to_s == "String" + variable && !(str_variable.nil? || str_variable.empty?) + end + + # Decrypts ios credentials + def self.decrypt_ios_keys(ios_directory) + # Define the GPG command with options + gpg_command = "gpg --quiet --batch --yes --decrypt --passphrase=#{ENV['IOS_KEYS_SECRET_PASSPHRASE']} \ + --output #{ios_directory}/ios_keys.zip #{ios_directory}/ios_keys.zip.gpg" + + # Execute the GPG command using system + system(gpg_command) + + # Check if the command executed successfully + if $?.success? + + # Move the extracted files to the current directory + `jar xvf #{ios_directory} && mv #{ios_directory}/ios_keys/* #{ios_directory}` + else + puts("Erreur lors de la décompression du fichier GPG") + end + end + + # Parse credential file + def parseIosCredentials(ios_directory) + if File.exists?("#{ios_directory}/ios_crendentials.json") + # Read file and decrypt it + file = File.read("#{ios_directory}/ios_crendentials.json") + JSON.parse(file) + + else + UI.error("❌ Ios credentials doesn't exist") + puts("json file doesn't exist") end end end diff --git a/plugins/fastlane-plugin-ios_cd/spec/ios_cd_action_spec.rb b/plugins/fastlane-plugin-ios_cd/spec/ios_cd_action_spec.rb index 46ca2b9..a5a0dfb 100644 --- a/plugins/fastlane-plugin-ios_cd/spec/ios_cd_action_spec.rb +++ b/plugins/fastlane-plugin-ios_cd/spec/ios_cd_action_spec.rb @@ -1,9 +1,9 @@ -describe Fastlane::Actions::IosCdAction do +describe Fastlane::Actions::BuildAndDeploy do describe '#run' do it 'prints a message' do expect(Fastlane::UI).to receive(:message).with("The ios_cd plugin is working!") - Fastlane::Actions::IosCdAction.run(nil) + Fastlane::Actions::BuildAndDeploy.run(nil) end end end