From 0317c987e592801822490f6a70ecfdfc0a30035c Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Fri, 15 Mar 2024 13:11:02 -0400 Subject: [PATCH 1/6] Add action to generate snapshot builds --- .../plugin/emerge/actions/emerge_action.rb | 64 ++----- .../emerge/actions/emerge_snapshot_action.rb | 160 ++++++++++++++++++ .../plugin/emerge/helper/emerge_helper.rb | 85 ++++++++-- lib/fastlane/plugin/emerge/version.rb | 2 +- 4 files changed, 242 insertions(+), 69 deletions(-) create mode 100644 lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb diff --git a/lib/fastlane/plugin/emerge/actions/emerge_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_action.rb index 3e0405f..a7d7e6c 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_action.rb @@ -88,62 +88,18 @@ def self.run(params) return end - filename = File.basename(file_path) - url = 'https://api.emergetools.com/upload' params = { - filename: filename + prNumber: pr_number, + branch: branch, + sha: sha, + baseSha: base_sha, + repoName: repo_name, + gitlabProjectId: gitlab_project_id, + orderFileVersion: order_file_version, + tag: tag || "default" } - if pr_number - params[:prNumber] = pr_number - end - if branch - params[:branch] = branch - end - if sha - params[:sha] = sha - end - if base_sha - params[:baseSha] = base_sha - end - if repo_name - params[:repoName] = repo_name - end - if gitlab_project_id - params[:gitlabProjectId] = gitlab_project_id - end - if order_file_version - params[:orderFileVersion] = order_file_version - end - params[:tag] = tag || "default" - FastlaneCore::PrintTable.print_values( - config: params, - hide_keys: [], - title: "Summary for Emerge #{Fastlane::Emerge::VERSION}" - ) - resp = Faraday.post( - url, - params.to_json, - 'Content-Type' => 'application/json', 'X-API-Token' => api_token, 'User-Agent' => "fastlane-plugin-emerge/#{Fastlane::Emerge::VERSION}" - ) - case resp.status - when 200 - json = JSON.parse(resp.body) - upload_id = json["upload_id"] - upload_url = json["uploadURL"] - warning = json["warning"] - if warning - UI.important(warning) - end - return Helper::EmergeHelper.perform_upload(upload_url, upload_id, file_path) - when 403 - UI.error("Invalid API token") - when 400 - UI.error("Invalid parameters") - json = JSON.parse(resp.body) - UI.error("Error: #{json['errorMessage']}") - else - UI.error("Upload failed") - end + upload_id = Helper::EmergeHelper.perform_upload(api_token, params, file_path) + UI.success("🎉 Your app is processing, you can find the results at https://emergetools.com/build/#{upload_id}") end def self.copy_dsyms(from, to) diff --git a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb new file mode 100644 index 0000000..6d71f60 --- /dev/null +++ b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb @@ -0,0 +1,160 @@ +require 'fastlane/action' +require 'fastlane_core/print_table' +require_relative '../helper/emerge_helper' +require_relative '../helper/git' +require_relative '../helper/github' +require 'pathname' +require 'tmpdir' +require 'json' +require 'fileutils' + +module Fastlane + module Actions + class EmergeSnapshotAction < Action + def self.run(params) + api_token = params[:api_token] + + git_params = Helper::EmergeHelper.make_git_params + pr_number = params[:pr_number] || git_params.pr_number + branch = params[:branch] || git_params.branch + sha = params[:sha] || git_params.sha + base_sha = params[:base_sha] || git_params.base_sha + repo_name = params[:repo_name] || git_params.repo_name + gitlab_project_id = params[:gitlab_project_id] + tag = params[:tag] + config_path = params[:config_path] + scheme = params[:scheme] + configuration = params[:configuration] + + Dir.mktmpdir do |temp_dir| + archive_path = "#{temp_dir}/build/snapshot.xcarchive" + other_action.gym( + scheme: scheme, + configuration: configuration, + skip_codesigning: true, + clean: true, + include_symbols: true, + export_method: "development", + export_team_id: ENV['APPLE_TEAM_ID'], + skip_package_ipa: true, + output_directory: "#{temp_dir}/build", + archive_path: archive_path + ) + + copy_config(config_path, archive_path) + Xcodeproj::Plist.write_to_path({ "NAME" => "Emerge Upload" }, "#{archive_path}/Info.plist") + dsym_path = "#{archive_path}/dSYMs" + if Dir.exist?(dsym_path) + UI.message("Removing dSYMs from xcarchive") + FileUtils.rm_rf(dsym_path) + end + + zip_file_path = "#{temp_dir}/build/archive.xcarchive.zip" + ZipAction.run( + path: archive_path, + output_path: zip_file_path, + exclude: [], + include: [] + ) + + params = { + appIdSuffix: 'snapshots', + prNumber: pr_number, + branch: branch, + sha: sha, + baseSha: base_sha, + repoName: repo_name, + gitlabProjectId: gitlab_project_id, + tag: tag || "default" + } + upload_id = Helper::EmergeHelper.perform_upload(api_token, params, zip_file_path) + UI.success("🎉 Your app is processing, you can find the results at https://emergetools.com/snapshot/#{upload_id}") + end + end + + def self.copy_config(config_path, tmp_dir) + return if config_path.nil? + + expanded_path = File.expand_path(config_path) + unless File.exist?(expanded_path) + UI.error("No config file found at path '#{expanded_path}'.\nUploading without config file") + return + end + + emerge_config_path = "#{tmp_dir}/emerge_config.yaml" + FileUtils.cp(expanded_path, emerge_config_path) + end + + def self.description + "Fastlane plugin for Emerge to generate iOS snapshots" + end + + def self.authors + ["Emerge Tools"] + end + + def self.return_value + "If successful, returns the upload id of the generated snapshot build" + end + + def self.details + "" + end + + def self.available_options + [ + FastlaneCore::ConfigItem.new(key: :api_token, + env_name: "EMERGE_API_TOKEN", + description: "An API token for Emerge", + optional: false, + type: String), + FastlaneCore::ConfigItem.new(key: :scheme, + description: "The scheme of your app to build", + optional: false, + type: String), + FastlaneCore::ConfigItem.new(key: :configuration, + description: "The configuration of your app to use", + optional: false, + default_value: "Debug", + type: String), + FastlaneCore::ConfigItem.new(key: :config_path, + description: "Path to Emerge YAML config path", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :pr_number, + description: "The PR number that triggered this upload", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :branch, + description: "The current git branch", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :sha, + description: "The git SHA that triggered this build", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :base_sha, + description: "The git SHA of the base build", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :repo_name, + description: "Full name of the respository this upload was triggered from. For example: EmergeTools/Emerge", + optional: true, + type: String), + FastlaneCore::ConfigItem.new(key: :gitlab_project_id, + description: "Id of the gitlab project this upload was triggered from", + optional: true, + type: Integer), + FastlaneCore::ConfigItem.new(key: :tag, + description: "String to label the build. Useful for grouping builds together in our dashboard, like development, default, or pull-request", + optional: true, + type: String) + ] + end + + def self.is_supported?(platform) + platform == :ios + end + end + end +end diff --git a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb index 1a48a92..9480758 100644 --- a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb +++ b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb @@ -18,21 +18,18 @@ def initialize(sha:, base_sha:, branch:, pr_number: nil, repo_name: nil) module Helper class EmergeHelper - def self.perform_upload(upload_url, upload_id, file_path) - UI.message("Starting upload") - response = Faraday.put(upload_url) do |req| - req.headers['Content-Type'] = 'application/zip' - req.headers['Content-Length'] = File.size(file_path).to_s - req.body = Faraday::UploadIO.new(file_path, 'application/zip') - end - case response.status - when 200 - UI.success("🎉 Your app is processing, you can find the results at https://emergetools.com/build/#{upload_id}") - return upload_id - else - UI.error("Upload failed") + API_URL = 'https://api.emergetools.com/upload'.freeze + + def self.perform_upload(api_token, params, file_path) + begin + cleaned_params = clean_params(params) + print_summary(cleaned_params) + + upload_response = create_upload(api_token, cleaned_params) + handle_upload_response(api_token, upload_response, file_path) + rescue StandardError => e + UI.user_error!(e.message) end - return nil end def self.make_git_params @@ -56,6 +53,66 @@ def self.make_git_params UI.message("Got git result #{git_result.inspect}") git_result end + + private_class_method + + def self.clean_params(params) + params.reject { |_, v| v.nil? } + end + + def self.print_summary(params) + FastlaneCore::PrintTable.print_values( + config: params, + hide_keys: [], + title: "Summary for Emerge Upload #{Fastlane::Emerge::VERSION}" + ) + end + + def self.create_upload(api_token, params) + response = Faraday.post(API_URL, params.to_json, headers(api_token, params, 'application/json')) + parse_response(response) + end + + def self.headers(api_token, params, content_type) + { + 'Content-Type' => content_type, + 'X-API-Token' => api_token, + 'User-Agent' => "fastlane-plugin-emerge/#{Fastlane::Emerge::VERSION}" + } + end + + def self.parse_response(response) + case response.status + when 200 + JSON.parse(response.body) + when 400 + error_message = JSON.parse(response.body)['errorMessage'] + raise "Invalid parameters: #{error_message}" + when 401, 403 + raise 'Invalid API token' + else + raise "Creating upload failed with status #{response.status}" + end + end + + def self.handle_upload_response(api_token, response, file_path) + upload_url = response.fetch('uploadURL') + upload_id = response.fetch('upload_id') + + UI.message('Starting zip file upload') + upload_file(api_token, upload_url, file_path) + upload_id + end + + def self.upload_file(api_token, upload_url, file_path) + response = Faraday.put(upload_url) do |req| + req.headers = headers(api_token, nil, 'application/zip') + req.headers['Content-Length'] = File.size(file_path).to_s + req.body = Faraday::UploadIO.new(file_path, 'application/zip') + end + + raise "Uploading zip file failed #{response.status}" unless response.status == 200 + end end end end diff --git a/lib/fastlane/plugin/emerge/version.rb b/lib/fastlane/plugin/emerge/version.rb index f23acd7..fc8c065 100644 --- a/lib/fastlane/plugin/emerge/version.rb +++ b/lib/fastlane/plugin/emerge/version.rb @@ -1,5 +1,5 @@ module Fastlane module Emerge - VERSION = "0.8.0" + VERSION = "0.9.0" end end From f7092f7093da61f7949ef6ceea204f8b68655f96 Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Fri, 15 Mar 2024 13:16:15 -0400 Subject: [PATCH 2/6] factor --- .../plugin/emerge/actions/emerge_action.rb | 17 ++--------- .../emerge/actions/emerge_snapshot_action.rb | 15 +--------- .../plugin/emerge/helper/emerge_helper.rb | 29 +++++++++++++------ 3 files changed, 23 insertions(+), 38 deletions(-) diff --git a/lib/fastlane/plugin/emerge/actions/emerge_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_action.rb index a7d7e6c..78eeb01 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_action.rb @@ -50,7 +50,7 @@ def self.run(params) FileUtils.cp(l, linkmap_folder) end end - copy_config(config_path, "#{d}/archive.xcarchive") + Helper::EmergeHelper.copy_config(config_path, "#{d}/archive.xcarchive") FileUtils.cp_r(file_path, application_folder) copy_dsyms("#{absolute_path.dirname}/*.dsym", dsym_folder) copy_dsyms("#{absolute_path.dirname}/*/*.dsym", dsym_folder) @@ -73,7 +73,7 @@ def self.run(params) FileUtils.cp(l, linkmap_folder) end end - copy_config(config_path, file_path) + Helper::EmergeHelper.copy_config(config_path, file_path) Actions::ZipAction.run( path: file_path, output_path: zip_path, @@ -109,19 +109,6 @@ def self.copy_dsyms(from, to) end end - def self.copy_config(config_path, tmp_dir) - return if config_path.nil? - - expanded_path = File.expand_path(config_path) - unless File.exist?(expanded_path) - UI.error("No config file found at path '#{expanded_path}'.\nUploading without config file") - return - end - - emerge_config_path = "#{tmp_dir}/emerge_config.yaml" - FileUtils.cp(expanded_path, emerge_config_path) - end - def self.description "Fastlane plugin for Emerge" end diff --git a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb index 6d71f60..8b9357f 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb @@ -41,7 +41,7 @@ def self.run(params) archive_path: archive_path ) - copy_config(config_path, archive_path) + Helper::EmergeHelper.copy_config(config_path, archive_path) Xcodeproj::Plist.write_to_path({ "NAME" => "Emerge Upload" }, "#{archive_path}/Info.plist") dsym_path = "#{archive_path}/dSYMs" if Dir.exist?(dsym_path) @@ -72,19 +72,6 @@ def self.run(params) end end - def self.copy_config(config_path, tmp_dir) - return if config_path.nil? - - expanded_path = File.expand_path(config_path) - unless File.exist?(expanded_path) - UI.error("No config file found at path '#{expanded_path}'.\nUploading without config file") - return - end - - emerge_config_path = "#{tmp_dir}/emerge_config.yaml" - FileUtils.cp(expanded_path, emerge_config_path) - end - def self.description "Fastlane plugin for Emerge to generate iOS snapshots" end diff --git a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb index 9480758..b0c2c02 100644 --- a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb +++ b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb @@ -21,15 +21,13 @@ class EmergeHelper API_URL = 'https://api.emergetools.com/upload'.freeze def self.perform_upload(api_token, params, file_path) - begin - cleaned_params = clean_params(params) - print_summary(cleaned_params) - - upload_response = create_upload(api_token, cleaned_params) - handle_upload_response(api_token, upload_response, file_path) - rescue StandardError => e - UI.user_error!(e.message) - end + cleaned_params = clean_params(params) + print_summary(cleaned_params) + + upload_response = create_upload(api_token, cleaned_params) + handle_upload_response(api_token, upload_response, file_path) + rescue StandardError => e + UI.user_error!(e.message) end def self.make_git_params @@ -54,6 +52,19 @@ def self.make_git_params git_result end + def self.copy_config(config_path, tmp_dir) + return if config_path.nil? + + expanded_path = File.expand_path(config_path) + unless File.exist?(expanded_path) + UI.error("No config file found at path '#{expanded_path}'.\nUploading without config file") + return + end + + emerge_config_path = "#{tmp_dir}/emerge_config.yaml" + FileUtils.cp(expanded_path, emerge_config_path) + end + private_class_method def self.clean_params(params) From 40d2e4ddaefd325504f35d8ddbf864f3497df54a Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Fri, 15 Mar 2024 14:28:59 -0400 Subject: [PATCH 3/6] Fetch team id from appfile if present --- .../plugin/emerge/actions/emerge_snapshot_action.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb index 8b9357f..ac9c333 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb @@ -25,6 +25,7 @@ def self.run(params) config_path = params[:config_path] scheme = params[:scheme] configuration = params[:configuration] + team_id = params[:team_id] || CredentialsManager::AppfileConfig.try_fetch_value(:team_id) Dir.mktmpdir do |temp_dir| archive_path = "#{temp_dir}/build/snapshot.xcarchive" @@ -35,7 +36,7 @@ def self.run(params) clean: true, include_symbols: true, export_method: "development", - export_team_id: ENV['APPLE_TEAM_ID'], + export_team_id: team_id, skip_package_ipa: true, output_directory: "#{temp_dir}/build", archive_path: archive_path @@ -104,6 +105,11 @@ def self.available_options optional: false, default_value: "Debug", type: String), + FastlaneCore::ConfigItem.new(key: :team_id, + env_name: "EXPORT_TEAM_ID", + description: "The Apple Team ID to use for exporting the archive. If not provided, we will try to use the team_id from the Appfile", + optional: true, + type: String), FastlaneCore::ConfigItem.new(key: :config_path, description: "Path to Emerge YAML config path", optional: true, From c622058c39246c9eb6d6e2523b72ec0132edbc39 Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Fri, 15 Mar 2024 19:20:39 -0400 Subject: [PATCH 4/6] don't remove dSYMs --- .../plugin/emerge/actions/emerge_snapshot_action.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb index ac9c333..87978b8 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb @@ -34,7 +34,6 @@ def self.run(params) configuration: configuration, skip_codesigning: true, clean: true, - include_symbols: true, export_method: "development", export_team_id: team_id, skip_package_ipa: true, @@ -44,11 +43,6 @@ def self.run(params) Helper::EmergeHelper.copy_config(config_path, archive_path) Xcodeproj::Plist.write_to_path({ "NAME" => "Emerge Upload" }, "#{archive_path}/Info.plist") - dsym_path = "#{archive_path}/dSYMs" - if Dir.exist?(dsym_path) - UI.message("Removing dSYMs from xcarchive") - FileUtils.rm_rf(dsym_path) - end zip_file_path = "#{temp_dir}/build/archive.xcarchive.zip" ZipAction.run( From 6dcdc0dd2dae59dc916ffd21e6f4f415be7e24c0 Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Mon, 18 Mar 2024 13:20:28 -0400 Subject: [PATCH 5/6] Bring back warning --- lib/fastlane/plugin/emerge/helper/emerge_helper.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb index b0c2c02..001c9b4 100644 --- a/lib/fastlane/plugin/emerge/helper/emerge_helper.rb +++ b/lib/fastlane/plugin/emerge/helper/emerge_helper.rb @@ -110,6 +110,11 @@ def self.handle_upload_response(api_token, response, file_path) upload_url = response.fetch('uploadURL') upload_id = response.fetch('upload_id') + warning = response.dig('warning') + if warning + UI.important(warning) + end + UI.message('Starting zip file upload') upload_file(api_token, upload_url, file_path) upload_id From b6e470368579d36f4b02d95293e9280bf30a8a73 Mon Sep 17 00:00:00 2001 From: Trevor Elkins Date: Tue, 19 Mar 2024 12:22:44 -0400 Subject: [PATCH 6/6] Cleanup --- .../emerge/actions/emerge_snapshot_action.rb | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb index 87978b8..29b281a 100644 --- a/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb +++ b/lib/fastlane/plugin/emerge/actions/emerge_snapshot_action.rb @@ -28,23 +28,18 @@ def self.run(params) team_id = params[:team_id] || CredentialsManager::AppfileConfig.try_fetch_value(:team_id) Dir.mktmpdir do |temp_dir| - archive_path = "#{temp_dir}/build/snapshot.xcarchive" - other_action.gym( + archive_name = "#{scheme}-Emerge-Snapshots" + archive_path = "#{temp_dir}/build/#{archive_name}.xcarchive" + make_debug_build( scheme: scheme, configuration: configuration, - skip_codesigning: true, - clean: true, - export_method: "development", - export_team_id: team_id, - skip_package_ipa: true, - output_directory: "#{temp_dir}/build", + team_id: team_id, archive_path: archive_path ) - Helper::EmergeHelper.copy_config(config_path, archive_path) Xcodeproj::Plist.write_to_path({ "NAME" => "Emerge Upload" }, "#{archive_path}/Info.plist") - zip_file_path = "#{temp_dir}/build/archive.xcarchive.zip" + zip_file_path = "#{temp_dir}/build/#{archive_name}.xcarchive.zip" ZipAction.run( path: archive_path, output_path: zip_file_path, @@ -67,6 +62,19 @@ def self.run(params) end end + def self.make_debug_build(scheme:, configuration:, team_id:, archive_path:) + other_action.gym( + scheme: scheme, + configuration: configuration, + skip_codesigning: true, + clean: true, + export_method: "development", + export_team_id: team_id, + skip_package_ipa: true, + archive_path: archive_path + ) + end + def self.description "Fastlane plugin for Emerge to generate iOS snapshots" end