Skip to content

Instantly share code, notes, and snippets.

@nickludlam
Last active November 21, 2021 14:33
Show Gist options
  • Save nickludlam/cdb7905ae474044a8fbc74f7f33a9f9b to your computer and use it in GitHub Desktop.
Save nickludlam/cdb7905ae474044a8fbc74f7f33a9f9b to your computer and use it in GitHub Desktop.
A simple action to allow Fastlane to trigger Unity in batch mode, executing a specific method call that you can use to customise your build process
public static class AutoBuilder
{
static string defaultBundleIdentifier = "com.yourcompany.yourproduct";
static string[] GetScenePaths()
{
List<string> scenes = new List<string>();
for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
{
if (EditorBuildSettings.scenes[i].enabled)
{
scenes.Add(EditorBuildSettings.scenes[i].path);
}
}
return scenes.ToArray();
}
private static String GetCommandLineArgumentValue(string key)
{
String[] args = System.Environment.GetCommandLineArgs();
int argIndex = Array.IndexOf(args, key);
if (argIndex > -1 && args.Length >= argIndex)
{
return args[argIndex + 1];
}
else
{
return null;
}
}
private static String GetOutputDir(string defaultOverride = null)
{
String outputDir = (defaultOverride != null) ? defaultOverride : defaultProjectDir;
string foundArg = GetCommandLineArgumentValue("-outputDir");
if (foundArg != null)
{
outputDir = foundArg;
}
return outputDir;
}
private static void SetVersion()
{
string foundArg = GetCommandLineArgumentValue("-buildVersion");
if (foundArg != null)
{
PlayerSettings.bundleVersion = foundArg;
}
}
private static void SetBuildNumber()
{
string foundArg = GetCommandLineArgumentValue("-buildNumber");
if (foundArg != null)
{
PlayerSettings.iOS.buildNumber = foundArg;
}
}
private static string SetBundleIdentifier()
{
string foundArg = GetCommandLineArgumentValue("-bundleIdentifier");
if (foundArg != null)
{
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.iOS, foundArg);
}
return foundArg;
}
public static void PerformiOSReleaseBuild()
{
BuildOptions buildOpts = BuildOptions.StrictMode;
PerformiOSBuild(buildOpts, false, GetOutputDir());
}
static void PerformiOSBuild(BuildOptions buildOpts, bool analyticsEnabled, string outputDir){
SetVersion();
SetBuildNumber();
string newBundleIdentifier = SetBundleIdentifier();
if (newBundleIdentifier == null || newBundleIdentifier.Length == 0)
{
newBundleIdentifier = PlayerSettings.applicationIdentifier;
}
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.iOS, BuildTarget.iOS);
BuildReport rep = BuildPipeline.BuildPlayer(GetScenePaths(), outputDir, BuildTarget.iOS, buildOpts);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.iOS, defaultBundleIdentifier);
if (rep.summary.totalErrors > 0)
{
throw new Exception("BuildPlayer failure with " + rep.summary.totalErrors + " errors");
}
}
}
# The repo is arranged as follows:
#
# Fastlane/ - Fastlane config directory
# yourproject-unity/ - The Unity project files
# builds/ - Output directory for builds
# Root path
git_root = File.join(File.expand_path(File.dirname(__FILE__)), "..")
# Config
unity_binary_path = ENV.fetch("UNITY_BINARY_PATH", "/Applications/Unity/Unity.app/Contents/MacOS/Unity")
unity_project_path = "#{git_root}/yourproject-unity"
unity_output_path = "#{git_root}/builds/dev-build"
unity_execute_method = "AutoBuilder.SomeStaticMethod"
app_identifier = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)
unity_batch(
executable: unity_binary_path,
project_path: unity_project_path,
output_directory: unity_output_path,
execute_method: unity_execute_method,
build_number: Actions.lane_context[SharedValues::BUILD_NUMBER],
build_version: Actions.lane_context[SharedValues::VERSION_NUMBER],
bundle_identifier: app_identifier,
custom_arguments: options[:custom_arguments]
)
module Fastlane
module Actions
module SharedValues
end
class UnityBatchAction < Action
def self.run(params)
if !File.exists?(params[:executable])
UI.user_error!("No Unity binary found at using params[:executable]=#{params[:executable]}.")
return
end
cmd = ""
cmd << "#{params[:executable]}"
cmd << " -batchMode"
cmd << " -projectPath \"#{params[:project_path]}\""
cmd << " -outputDir \"#{params[:output_directory]}\""
cmd << " -buildNumber #{params[:build_number]}" if params[:build_number]
cmd << " -buildVersion #{params[:build_version]}" if params[:build_version]
cmd << " -bundleIdentifier #{params[:bundle_identifier]}" if params[:bundle_identifier]
cmd << " -buildTarget iPhone"
cmd << " -executeMethod #{params[:execute_method]}" if params[:execute_method]
if params[:custom_arguments]
params[:custom_arguments].each do |key, value|
cmd << " #{key} #{value}"
end
end
cmd << " -quit"
UI.command(cmd)
Bundler.original_system(cmd)
exit_code = $?.exitstatus
if exit_code != 0
UI.command_output("Unity exit code was #{exit_code}")
error_str = diagnose_error()
UI.user_error!(error_str)
else
UI.success "Unity export completed"
end
end
def self.diagnose_error
# Unity on the Mac logs to the following path
editor_log_path = File.expand_path("~/Library/Logs/Unity/Editor.log")
# These are the strings we look for in the Editor output
error_conditions = ["It looks like another Unity instance is running with this project open.",
"FileNotFoundException"]
# Grep for each string in error_conditions
log_contents = File.readlines(editor_log_path)
error_conditions.each do |search_string|
if log_contents.grep(/#{search_string}/).any?
return "Found a Unity Editor error in #{editor_log_path}! '#{search_string}'"
end
end
# sh "cat #{editor_log_path}"
return "There was a problem running Unity. Check the Editor log at #{editor_log_path} for more details"
end
def self.description
'Run Unity in batch mode and execute a specific method'
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :executable,
env_name: "FL_UNITY_EXECUTABLE",
description: "Path for Unity executable",
default_value: "/Applications/Unity/Unity.app/Contents/MacOS/Unity"),
FastlaneCore::ConfigItem.new(key: :project_path,
env_name: "FL_UNITY_PROJECT_PATH",
description: "Path for Unity project",
default_value: "#{Dir.pwd}"),
FastlaneCore::ConfigItem.new(key: :output_directory,
env_name: "FL_UNITY_OUTPUT_DIR",
description: "Path to export the build into",
default_value: "#{Dir.pwd}/builds"),
FastlaneCore::ConfigItem.new(key: :execute_method,
env_name: "FL_UNITY_EXECUTE_METHOD",
description: "Method to execute",
optional: true,
default_value: nil),
FastlaneCore::ConfigItem.new(key: :build_number,
env_name: "FL_UNITY_BUILD_NUMBER",
description: "Associated build number",
type: Integer,
optional: true,
default_value: 1),
FastlaneCore::ConfigItem.new(key: :build_version,
env_name: "FL_UNITY_BUILD_VERSION",
description: "Associated build version string",
optional: true,
default_value: "0.0.1"),
FastlaneCore::ConfigItem.new(key: :bundle_identifier,
env_name: "FL_UNITY_BUILD_BUNDLE_IDENTIFIER",
description: "Application Bundle ID",
optional: true,
default_value: "com.unity.defaultapp"),
FastlaneCore::ConfigItem.new(key: :custom_arguments,
env_name: "FL_UNITY_CUSTOM_ARGS",
description: "A hash of additional custom arguments",
type: Hash,
optional: true,
default_value: {})
]
end
def self.authors
["nickludlam"]
end
def self.is_supported?(platform)
true
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment