diff --git a/.buildkite/unity.2020.yml b/.buildkite/unity.2020.yml index d69060fc8..c3d3032d2 100644 --- a/.buildkite/unity.2020.yml +++ b/.buildkite/unity.2020.yml @@ -19,9 +19,9 @@ steps: upload: - features/fixtures/maze_runner/mazerunner_2020.apk - features/fixtures/build_android_apk.log - commands: - - bundle install - - rake test:android:build + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_android.feature" retry: automatic: - exit_status: "*" @@ -30,31 +30,9 @@ steps: # # Build iOS test fixtures # - - label: ":ios: Generate Xcode project - Unity 2020" - timeout_in_minutes: 20 - key: "generate-fixture-project-2020" - env: - UNITY_VERSION: *2020 - plugins: - artifacts#v1.9.0: - download: - - Bugsnag.unitypackage - upload: - - features/fixtures/unity.log - - project_2020.tgz - commands: - - bundle install - - rake test:ios:generate_xcode - - tar -zvcf project_2020.tgz features/fixtures/maze_runner/mazerunner_xcode - retry: - automatic: - - exit_status: "*" - limit: 1 - - label: ":ios: Build iOS test fixture for Unity 2020" timeout_in_minutes: 10 key: "build-ios-fixture-2020" - depends_on: "generate-fixture-project-2020" env: XCODE_VERSION: "15.3.0" UNITY_VERSION: *2020 @@ -62,14 +40,12 @@ steps: artifacts#v1.9.0: download: - Bugsnag.unitypackage - - project_2020.tgz upload: - features/fixtures/maze_runner/mazerunner_2020.ipa - features/fixtures/unity.log - commands: - - bundle install - - tar -zxf project_2020.tgz features/fixtures/maze_runner - - rake test:ios:build_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_ios.feature" retry: automatic: - exit_status: "*" diff --git a/.buildkite/unity.2021.yml b/.buildkite/unity.2021.yml index 59528c7b0..8c3c78b6d 100644 --- a/.buildkite/unity.2021.yml +++ b/.buildkite/unity.2021.yml @@ -19,33 +19,9 @@ steps: upload: - features/fixtures/maze_runner/mazerunner_2021.apk - features/fixtures/build_android_apk.log - commands: - - bundle install - - rake test:android:build - retry: - automatic: - - exit_status: "*" - limit: 1 - - # - # Build iOS test fixtures - # - - label: ":ios: Generate Xcode project - Unity 2021" - timeout_in_minutes: 20 - key: "generate-fixture-project-2021" - env: - UNITY_VERSION: *2021 - plugins: - artifacts#v1.5.0: - download: - - Bugsnag.unitypackage - upload: - - features/fixtures/unity.log - - project_2021.tgz - commands: - - bundle install - - rake test:ios:generate_xcode - - tar -zvcf project_2021.tgz features/fixtures/maze_runner/mazerunner_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_android.feature" retry: automatic: - exit_status: "*" @@ -54,7 +30,6 @@ steps: - label: ":ios: Build iOS test fixture for Unity 2021" timeout_in_minutes: 10 key: "build-ios-fixture-2021" - depends_on: "generate-fixture-project-2021" env: UNITY_VERSION: *2021 XCODE_VERSION: "15.3.0" @@ -62,14 +37,12 @@ steps: artifacts#v1.5.0: download: - Bugsnag.unitypackage - - project_2021.tgz upload: - features/fixtures/maze_runner/mazerunner_2021.ipa - features/fixtures/unity.log - commands: - - bundle install - - tar -zxf project_2021.tgz features/fixtures/maze_runner - - rake test:ios:build_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_ios.feature" retry: automatic: - exit_status: "*" diff --git a/.buildkite/unity.2022.yml b/.buildkite/unity.2022.yml index 99c2b40ee..65004ac98 100644 --- a/.buildkite/unity.2022.yml +++ b/.buildkite/unity.2022.yml @@ -19,30 +19,9 @@ steps: upload: - features/fixtures/maze_runner/mazerunner_2022.apk - features/fixtures/build_android_apk.log - commands: - - bundle install - - rake test:android:build - retry: - automatic: - - exit_status: "*" - limit: 1 - - - label: ":ios: Generate Xcode project - Unity 2022" - timeout_in_minutes: 10 - key: "generate-fixture-project-2022" - env: - UNITY_VERSION: *2022 - plugins: - artifacts#v1.9.0: - download: - - Bugsnag.unitypackage - upload: - - features/fixtures/unity.log - - project_2022.tgz - commands: - - bundle install - - rake test:ios:generate_xcode - - tar -zvcf project_2022.tgz features/fixtures/maze_runner/mazerunner_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_android.feature" retry: automatic: - exit_status: "*" @@ -51,7 +30,6 @@ steps: - label: ":ios: Build iOS test fixture for Unity 2022" timeout_in_minutes: 10 key: "build-ios-fixture-2022" - depends_on: "generate-fixture-project-2022" env: UNITY_VERSION: *2022 XCODE_VERSION: "15.3.0" @@ -59,14 +37,12 @@ steps: artifacts#v1.9.0: download: - Bugsnag.unitypackage - - project_2022.tgz upload: - features/fixtures/maze_runner/mazerunner_2022.ipa - features/fixtures/unity.log - commands: - - bundle install - - tar -zxf project_2022.tgz features/fixtures/maze_runner - - rake test:ios:build_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_ios.feature" retry: automatic: - exit_status: "*" diff --git a/.buildkite/unity.6000.yml b/.buildkite/unity.6000.yml index 17abbcd98..2659b9ec7 100644 --- a/.buildkite/unity.6000.yml +++ b/.buildkite/unity.6000.yml @@ -19,30 +19,9 @@ steps: upload: - features/fixtures/maze_runner/mazerunner_6000.apk - features/fixtures/build_android_apk.log - commands: - - bundle install - - rake test:android:build - retry: - automatic: - - exit_status: "*" - limit: 1 - - - label: ":ios: Generate Xcode project - Unity 6000" - timeout_in_minutes: 10 - key: "generate-fixture-project-6000" - env: - UNITY_VERSION: *6000 - plugins: - artifacts#v1.9.0: - download: - - Bugsnag.unitypackage - upload: - - features/fixtures/unity.log - - project_6000.tgz - commands: - - bundle install - - rake test:ios:generate_xcode - - tar -zvcf project_6000.tgz features/fixtures/maze_runner/mazerunner_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_android.feature" retry: automatic: - exit_status: "*" @@ -51,7 +30,6 @@ steps: - label: ":ios: Build iOS test fixture for Unity 6000" timeout_in_minutes: 10 key: "build-ios-fixture-6000" - depends_on: "generate-fixture-project-6000" env: UNITY_VERSION: *6000 XCODE_VERSION: "16.0.0" @@ -59,14 +37,12 @@ steps: artifacts#v1.9.0: download: - Bugsnag.unitypackage - - project_6000.tgz upload: - features/fixtures/maze_runner/mazerunner_6000.ipa - features/fixtures/unity.log - commands: - - bundle install - - tar -zxf project_6000.tgz features/fixtures/maze_runner - - rake test:ios:build_xcode + command: + - "bundle install" + - "bundle exec maze-runner --os macos features/build/build_ios.feature" retry: automatic: - exit_status: "*" diff --git a/.gitignore b/.gitignore index 739813160..e6d5de1e5 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ upm/UPMImportProject/Library upm-package upm/UPMImportProject/.vscode UPMImportProject.sln +!features/build/ + diff --git a/Bugsnag/Assets/Bugsnag/Editor/BugsnagAddScriptingSymbol.cs b/Bugsnag/Assets/Bugsnag/Editor/BugsnagAddScriptingSymbol.cs index ad634716d..3ffa36c2d 100644 --- a/Bugsnag/Assets/Bugsnag/Editor/BugsnagAddScriptingSymbol.cs +++ b/Bugsnag/Assets/Bugsnag/Editor/BugsnagAddScriptingSymbol.cs @@ -1,9 +1,8 @@ using UnityEditor; -using UnityEngine; namespace BugsnagUnity.Editor { [InitializeOnLoad] - public class BugsnagAddScriptingSymbol : MonoBehaviour + public class BugsnagAddScriptingSymbol { private const string DEFINE_SYMBOL = "BUGSNAG_UNITY_WEB_REQUEST"; @@ -38,4 +37,4 @@ static void SetScriptingSymbol(BuildTargetGroup buildTargetGroup) BugsnagPlayerSettingsCompat.SetScriptingDefineSymbols(buildTargetGroup, existingSymbols); } } -} \ No newline at end of file +} diff --git a/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.EDM.cs b/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.EDM.cs index 47aef9c77..efde79da4 100644 --- a/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.EDM.cs +++ b/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.EDM.cs @@ -20,7 +20,7 @@ public partial class BugsnagEditor : EditorWindow private static string KotlinLibsDirPath = "/Bugsnag/Plugins/Android/Kotlin"; - [MenuItem(EDM_MENU_ITEM, false, 1)] + [MenuItem(EDM_MENU_ITEM, false, 51)] private static void ToggleEDM() { if (IsEDMEnabled()) diff --git a/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.cs b/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.cs index dc070068b..4490d3211 100644 --- a/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.cs +++ b/Bugsnag/Assets/Bugsnag/Editor/BugsnagEditor.cs @@ -15,7 +15,7 @@ public partial class BugsnagEditor : EditorWindow private bool _showBasicConfig = true; - private bool _showAdvancedSettings, _showAppInformation, _showEndpoints, _showEnabledErrorTypes, _showSwitch; + private bool _showAdvancedSettings, _showAppInformation, _showEndpoints, _showEnabledErrorTypes, _showSwitch, _showSymbolUpload; public Texture DarkIcon, LightIcon; @@ -116,6 +116,13 @@ private void DrawSettingsEditorWindow() DrawSwitchOptions(so); } + GUILayout.Space(5); + _showSymbolUpload = EditorGUILayout.Foldout(_showSymbolUpload, new GUIContent("Automatic Symbol Upload ⓘ", "Android and Xcode builds only"), true); + if (_showSymbolUpload) + { + DrawSymbolUploadOptions(so); + } + GUILayout.Space(10); GUILayout.EndVertical(); @@ -141,12 +148,14 @@ private void DrawEndpoints(SerializedObject so) EditorGUI.indentLevel++; EditorGUILayout.PropertyField(so.FindProperty("NotifyEndpoint")); EditorGUILayout.PropertyField(so.FindProperty("SessionEndpoint")); + EditorGUILayout.PropertyField(so.FindProperty("UploadEndpoint")); EditorGUI.indentLevel--; } private void DrawAppInfo(SerializedObject so, BugsnagSettingsObject settings) { EditorGUI.indentLevel++; + EditorGUIUtility.labelWidth = 270; EditorGUILayout.PropertyField(so.FindProperty("AppType")); EditorGUILayout.PropertyField(so.FindProperty("AppVersion")); EditorGUILayout.PropertyField(so.FindProperty("ReleaseStage")); @@ -196,6 +205,7 @@ private void DrawAdvancedSettings(SerializedObject so, BugsnagSettingsObject set private void DrawSwitchOptions(SerializedObject so) { EditorGUI.indentLevel++; + EditorGUIUtility.labelWidth = 270; EditorGUILayout.PropertyField(so.FindProperty("SwitchCacheIndex")); EditorGUILayout.PropertyField(so.FindProperty("SwitchCacheMaxSize")); EditorGUILayout.PropertyField(so.FindProperty("SwitchCacheMountName")); @@ -203,6 +213,15 @@ private void DrawSwitchOptions(SerializedObject so) EditorGUI.indentLevel--; } + private void DrawSymbolUploadOptions(SerializedObject so) + { + EditorGUI.indentLevel++; + EditorGUIUtility.labelWidth = 270; + EditorGUILayout.PropertyField(so.FindProperty("AutoUploadSymbols")); + EditorGUILayout.PropertyField(so.FindProperty("BugsnagCLIExecutablePath")); + EditorGUI.indentLevel--; + } + private void DrawEnabledErrorTypesDropdown(BugsnagSettingsObject settings) { diff --git a/Bugsnag/Assets/Bugsnag/Editor/BugsnagPlayerSettingsCompat.cs b/Bugsnag/Assets/Bugsnag/Editor/BugsnagPlayerSettingsCompat.cs index 759a5ae2a..140e30749 100644 --- a/Bugsnag/Assets/Bugsnag/Editor/BugsnagPlayerSettingsCompat.cs +++ b/Bugsnag/Assets/Bugsnag/Editor/BugsnagPlayerSettingsCompat.cs @@ -16,6 +16,15 @@ public static ScriptingImplementation GetScriptingBackend(BuildTargetGroup build #endif } + public static string GetApplicationIdentifier(BuildTargetGroup buildTargetGroup) + { +#if UNITY_6000_0_OR_NEWER + return PlayerSettings.GetApplicationIdentifier(NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup)); +#else + return PlayerSettings.GetApplicationIdentifier(buildTargetGroup); +#endif + } + // Get Scripting Define Symbols public static string GetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup) { diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload.meta b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload.meta new file mode 100644 index 000000000..47c556265 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3755067830dfc4b978446dae96fd6f4e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs new file mode 100644 index 000000000..3ff1cd007 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs @@ -0,0 +1,46 @@ +using UnityEditor; +using UnityEngine; + +namespace BugsnagUnity.Editor +{ + + public class AutoSymbolUploadToggleMenu + { + private const string MENU_PATH = "Window/BugSnag/Enable Symbol Uploads"; + + [MenuItem(MENU_PATH,false,50)] + private static void ToggleAutoUpload() + { + SetEnabled(!IsEnabled()); + } + + [MenuItem(MENU_PATH, true)] + private static bool ToggleAutoUploadValidate() + { + Menu.SetChecked(MENU_PATH, IsEnabled()); + return true; + } + + static bool IsEnabled() + { + var settings = GetSettingsObject(); + return settings != null && settings.AutoUploadSymbols; + } + + static void SetEnabled(bool enabled) + { + var settings = GetSettingsObject(); + if(settings != null) + { + settings.AutoUploadSymbols = enabled; + EditorUtility.SetDirty(settings); + } + } + + private static BugsnagSettingsObject GetSettingsObject() + { + var config = Resources.Load("Bugsnag/BugsnagSettingsObject"); + return config; + } + } +} \ No newline at end of file diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs.meta b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs.meta new file mode 100644 index 000000000..0de830e07 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/AutoSymbolUploadToggleMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8e80cdd98acc4474a34c3bbce86be9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs new file mode 100644 index 000000000..99b4cb2f9 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs @@ -0,0 +1,206 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace BugsnagUnity.Editor +{ + internal class BugsnagCLI + { + private const string DOWNLOADED_CLI_VERSION = "2.9.1"; + private readonly string DOWNLOADED_CLI_PATH = Path.Combine(Application.dataPath, "../bugsnag/bin/bugsnag_cli"); + private readonly string DOWNLOADED_CLI_URL = $"https://github.com/bugsnag/bugsnag-cli/releases/download/v{DOWNLOADED_CLI_VERSION}/"; + private readonly string _cliExecutablePath; + public BugsnagCLI() + { + var config = BugsnagSettingsObject.LoadBuildTimeSettingsObject(); + + if (string.IsNullOrEmpty(config.BugsnagCLIExecutablePath)) + { + _cliExecutablePath = DownloadBugsnagCLI(); + } + else + { + _cliExecutablePath = config.BugsnagCLIExecutablePath; + } + if (!File.Exists(_cliExecutablePath)) + { + throw new Exception($"BugSnag CLI not found at path: {_cliExecutablePath}"); + } + } + + public void UploadAndroidSymbols(string buildOutputPath, string apiKey, string versionName, int versionCode, string uploadEndpoint, string bundleId) + { + string args = $"upload unity-android --api-key={apiKey} --verbose --project-root={Application.dataPath} {buildOutputPath}"; + + if (!string.IsNullOrEmpty(versionName)) + { + args += $" --version-name={versionName}"; + } + if (versionCode > -1) + { + args += $" --version-code={versionCode}"; + } + if (!string.IsNullOrEmpty(uploadEndpoint)) + { + args += $" --upload-api-root-url={uploadEndpoint}"; + } + if (!string.IsNullOrEmpty(bundleId)) + { + args += $" --application-id={bundleId}"; + } + int exitCode = StartProcess(_cliExecutablePath, args, out string output, out string error); + + if (exitCode != 0) + { + throw new Exception($"Error uploading symbols: {error}\nOutput: {output}"); + } + } + + public string DownloadBugsnagCLI() + { + EnsureDirectoryExists(); + if (File.Exists(DOWNLOADED_CLI_PATH) && GetCurrentCliVersion() == DOWNLOADED_CLI_VERSION) + { + return DOWNLOADED_CLI_PATH; + } + DownloadCLI(); + MakeFileExecutable(DOWNLOADED_CLI_PATH); + return DOWNLOADED_CLI_PATH; + } + + private void EnsureDirectoryExists() + { + var directory = Path.GetDirectoryName(DOWNLOADED_CLI_PATH); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + } + + private void DownloadCLI() + { + string url = GetDownloadUrl(); + if (string.IsNullOrEmpty(url)) + { + throw new InvalidOperationException($"Unsupported platform: {RuntimeInformation.OSDescription}"); + } + + string tempPath = DOWNLOADED_CLI_PATH + ".tmp"; + + try + { + int exitCode = StartProcess("curl", $"-L {url} --output {tempPath}", out string output, out string error); + if (exitCode != 0) + { + throw new InvalidOperationException($"Download failed: {error}"); + } + + if (File.Exists(DOWNLOADED_CLI_PATH)) + { + File.Delete(DOWNLOADED_CLI_PATH); + } + + File.Move(tempPath, DOWNLOADED_CLI_PATH); + } + finally + { + if (File.Exists(tempPath)) + { + File.Delete(tempPath); + } + } + } + + private string GetDownloadUrl() + { + string fileName = null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + fileName = RuntimeInformation.OSArchitecture == Architecture.Arm64 + ? "arm64-macos-bugsnag-cli" + : "x86_64-macos-bugsnag-cli"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName = RuntimeInformation.OSArchitecture == Architecture.X86 + ? "i386-windows-bugsnag-cli.exe" + : "x86_64-windows-bugsnag-cli.exe"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + fileName = RuntimeInformation.OSArchitecture == Architecture.X86 + ? "i386-linux-bugsnag-cli" + : "x86_64-linux-bugsnag-cli"; + } + + return fileName != null ? DOWNLOADED_CLI_URL + fileName : null; + } + + private void MakeFileExecutable(string path) + { + if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) + { + int exitCode = StartProcess("chmod", $"+x {path}", out _, out _); + if (exitCode != 0) + { + throw new InvalidOperationException($"Failed to make file at {path} executable"); + } + } + } + + private string GetCurrentCliVersion() + { + if (!File.Exists(DOWNLOADED_CLI_PATH)) + { + UnityEngine.Debug.LogError($"BugSnag CLI not found at {DOWNLOADED_CLI_PATH}"); + return null; + } + + int exitCode = StartProcess(DOWNLOADED_CLI_PATH, "--version", out string output, out string error); + if (exitCode != 0) + { + UnityEngine.Debug.LogError($"Error checking CLI version: {error}"); + return null; + } + + return output.Trim(); + } + + private int StartProcess(string fileName, string arguments, out string standardOutput, out string standardError) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + standardOutput = process.StandardOutput.ReadToEnd(); + standardError = process.StandardError.ReadToEnd(); + process.WaitForExit(); + return process.ExitCode; + } + + public string GetIosDsymUploadCommand(string apiKey, string uploadEndpoint) + { + var command = $"{_cliExecutablePath} upload xcode-build --api-key={apiKey} $DWARF_DSYM_FOLDER_PATH"; + if (!string.IsNullOrEmpty(uploadEndpoint)) + { + command += $" --upload-api-root-url={uploadEndpoint}"; + } + return command; + } + + } +} + diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs.meta b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs.meta new file mode 100644 index 000000000..a005fa954 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagCLI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25157365d8631442b8dafca4ae99c601 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs new file mode 100644 index 000000000..769da1f33 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs @@ -0,0 +1,168 @@ +using System.IO; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +#if UNITY_IOS || UNITY_STANDALONE_OSX +using UnityEditor.iOS.Xcode; +#endif +#if UNITY_6000_0_OR_NEWER && UNITY_ANDROID +using UnityEditor.Android; +#endif +using UnityEngine; + +namespace BugsnagUnity.Editor +{ + + internal class BugsnagSymbolUploader : IPostprocessBuildWithReport + { + public int callbackOrder => 1; + + private const string DSYM_UPLOAD_SCRIPT_TEMPLATE = @"#!/bin/bash +if [ ""$ACTION"" == ""install"" ]; then + if ! ; then + echo ""Failed to upload dSYMs to BugSnag."" + else + echo ""Successfully uploaded dSYMs to BugSnag."" + fi +fi +"; + + private const string DSYM_UPLOAD_BUILD_PHASE_NAME = "BugSnag dSYM Upload"; + private const string DSYM_UPLOAD_SHELL_NAME = "/bin/sh"; + + public void OnPostprocessBuild(BuildReport report) + { + if (!IsSupportedPlatform(report.summary.platform)) + { + return; + } + + var config = BugsnagSettingsObject.LoadBuildTimeSettingsObject(); + if (config == null || !config.AutoUploadSymbols) + { + return; + } + var buildOutputPath = Path.GetDirectoryName(report.summary.outputPath); + +#if UNITY_ANDROID + EditorUtility.DisplayProgressBar("BugSnag Symbol Upload", "Uploading Android symbol files", 0.0f); + try + { + var bundleId = BugsnagPlayerSettingsCompat.GetApplicationIdentifier(report.summary.platformGroup); + UploadAndroidSymbols(buildOutputPath, config, bundleId); + } + catch (System.Exception e) + { + Debug.LogError($"Failed to upload Android symbol files to BugSnag. Error: {e.Message}"); + } + EditorUtility.ClearProgressBar(); +#elif UNITY_IOS || UNITY_STANDALONE_OSX + AddXcodePostBuildScript(report.summary.outputPath , config); +#endif + + } + + private bool IsSupportedPlatform(BuildTarget platform) + { + return platform == BuildTarget.Android || platform == BuildTarget.iOS || platform == BuildTarget.StandaloneOSX; + } + + private void UploadAndroidSymbols(string buildOutputPath, BugsnagSettingsObject config, string bundleId) + { + if (!IsAndroidSymbolCreationEnabled()) + { + Debug.LogError("Cannot upload Android symbols to BugSnag because Android symbol creation is disabled in the Unity Build Settings."); + return; + } + var cli = new BugsnagCLI(); + cli.UploadAndroidSymbols(buildOutputPath, config.ApiKey, config.AppVersion, config.VersionCode, config.UploadEndpoint, bundleId); + } + + private bool IsAndroidSymbolCreationEnabled() + { +#if UNITY_ANDROID +#if UNITY_6000_0_OR_NEWER + return UserBuildSettings.DebugSymbols.level == Unity.Android.Types.DebugSymbolLevel.SymbolTable || + UserBuildSettings.DebugSymbols.level == Unity.Android.Types.DebugSymbolLevel.Full; +#elif UNITY_2021_1_OR_NEWER + return EditorUserBuildSettings.androidCreateSymbols == AndroidCreateSymbols.Public || + EditorUserBuildSettings.androidCreateSymbols == AndroidCreateSymbols.Debugging; +#else + return EditorUserBuildSettings.androidCreateSymbolsZip; +#endif +#endif +#pragma warning disable CS0162 // Unreachable code detected + return false; +#pragma warning restore CS0162 // Unreachable code detected + } + + private void AddXcodePostBuildScript(string pathToBuiltProject, BugsnagSettingsObject config) + { +#if UNITY_IOS || UNITY_STANDALONE_OSX + +#if UNITY_IOS + var pbxProjectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); +#endif +#if UNITY_STANDALONE_OSX + var pbxProjectPath = GetMacosXcodeProjectPath(pathToBuiltProject); + if (!File.Exists(pbxProjectPath)) + { + //Xcode export not enabled, do nothing + return; + } +#endif + PBXProject pbxProject = new PBXProject(); + pbxProject.ReadFromFile(pbxProjectPath); +#if UNITY_IOS + + var targetGuid = pbxProject.GetUnityMainTargetGuid(); +#endif +#if UNITY_STANDALONE_OSX + var targetGuid = pbxProject.TargetGuidByName(Application.productName); +#endif + + foreach (var guid in pbxProject.GetAllBuildPhasesForTarget(targetGuid)) + { + if (DSYM_UPLOAD_BUILD_PHASE_NAME == pbxProject.GetBuildPhaseName(guid)) + { + var editedProject = RemoveShellScriptPhase(pbxProject.WriteToString(), guid); + pbxProject.ReadFromString(editedProject); + } + } + + var uploadScript = GetDsymUploadCommand(config); + pbxProject.AddShellScriptBuildPhase(targetGuid, DSYM_UPLOAD_BUILD_PHASE_NAME, DSYM_UPLOAD_SHELL_NAME, uploadScript); + pbxProject.WriteToFile(pbxProjectPath); +#endif + } + + // The PBX library is built to expect the layout of a unity iphone xcode project, so we have to manually find the macos xcode project path + string GetMacosXcodeProjectPath(string outputPath) + { + string[] parts = outputPath.Split('/'); + string xcprojFile = parts[parts.Length - 1] + ".xcodeproj"; + return outputPath + "/" + xcprojFile + "/project.pbxproj"; + } + + private string RemoveShellScriptPhase(string project, string guid) + { + // Search for and remove the phase object from the XML. only match the guid followed by the braces + var matches = System.Text.RegularExpressions.Regex.Matches(project, guid + @"\s*\/\*\s*\w+\s*\*\/\s*=\s*\{([\s\S]*?)\};"); + foreach (System.Text.RegularExpressions.Match match in matches) + { + if (match.Groups[1].Value.Contains(guid)) + { + project = project.Replace(match.Groups[0].Value, ""); + } + } + return project; + } + + private string GetDsymUploadCommand(BugsnagSettingsObject config) + { + var cli = new BugsnagCLI(); + var command = cli.GetIosDsymUploadCommand(config.ApiKey, config.UploadEndpoint); + return DSYM_UPLOAD_SCRIPT_TEMPLATE.Replace("", command); + } + } +} \ No newline at end of file diff --git a/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs.meta b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs.meta new file mode 100644 index 000000000..076e25704 --- /dev/null +++ b/Bugsnag/Assets/Bugsnag/Editor/SymbolUpload/BugsnagSymbolUploader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a240efcadfd144d44ad32d2c6ca9f17d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Bugsnag/Runtime/AssemblyInfo.cs b/Bugsnag/Assets/Bugsnag/Runtime/AssemblyInfo.cs index e427edf37..9dcda8eb6 100644 --- a/Bugsnag/Assets/Bugsnag/Runtime/AssemblyInfo.cs +++ b/Bugsnag/Assets/Bugsnag/Runtime/AssemblyInfo.cs @@ -1,2 +1,2 @@ using System.Reflection; -[assembly: AssemblyVersion("8.4.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("8.5.0.0")] \ No newline at end of file diff --git a/Bugsnag/Assets/Bugsnag/Runtime/BugsnagSettingsObject.cs b/Bugsnag/Assets/Bugsnag/Runtime/BugsnagSettingsObject.cs index dbf1f9b91..4b9f70361 100644 --- a/Bugsnag/Assets/Bugsnag/Runtime/BugsnagSettingsObject.cs +++ b/Bugsnag/Assets/Bugsnag/Runtime/BugsnagSettingsObject.cs @@ -2,6 +2,8 @@ using UnityEngine; using BugsnagUnity.Payload; using System; +using System.Runtime.CompilerServices; +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("BugsnagEditor")] namespace BugsnagUnity { @@ -9,6 +11,14 @@ namespace BugsnagUnity public class BugsnagSettingsObject : ScriptableObject { + // Build time settings + + public bool AutoUploadSymbols; + public string BugsnagCLIExecutablePath; + public string UploadEndpoint; + + // Runtime Settings + public bool StartAutomaticallyAtLaunch = true; public bool AutoDetectErrors = true; public bool AutoTrackSessions = true; @@ -48,6 +58,16 @@ public class BugsnagSettingsObject : ScriptableObject public int SwitchCacheIndex = 0; public int SwitchCacheMaxSize = 10485760; + public static BugsnagSettingsObject LoadBuildTimeSettingsObject() + { + var settings = Resources.Load("Bugsnag/BugsnagSettingsObject"); + if (settings == null) + { + throw new Exception("No BugsnagSettingsObject found."); + } + return settings; + } + public static Configuration LoadConfiguration() { var settings = Resources.Load("Bugsnag/BugsnagSettingsObject"); diff --git a/Bugsnag/Assets/Bugsnag/Runtime/Native/Android/NativeInterface.cs b/Bugsnag/Assets/Bugsnag/Runtime/Native/Android/NativeInterface.cs index 7db1bd811..e7106b2b5 100644 --- a/Bugsnag/Assets/Bugsnag/Runtime/Native/Android/NativeInterface.cs +++ b/Bugsnag/Assets/Bugsnag/Runtime/Native/Android/NativeInterface.cs @@ -1140,7 +1140,7 @@ internal static bool IsUnity2019OrNewer() return true; } } - catch (System.Exception _) + catch { // use legacy API on older versions return false; } diff --git a/Bugsnag/Assets/Plugins.meta b/Bugsnag/Assets/Plugins.meta new file mode 100644 index 000000000..7437daf1c --- /dev/null +++ b/Bugsnag/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd78a52fe8f4944e0a2c189c2b714a47 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS.meta b/Bugsnag/Assets/Plugins/MacOS.meta new file mode 100644 index 000000000..9bc823f19 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8c9c4421b26f4c8da2aad5169c85b2c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle.meta new file mode 100644 index 000000000..cd2decdbb --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle.meta @@ -0,0 +1,109 @@ +fileFormatVersion: 2 +guid: 731d6752cd77e428c8372840906bc55f +folderAsset: yes +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux: 1 + Exclude Linux64: 1 + Exclude LinuxUniversal: 1 + Exclude OSXUniversal: 0 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + Exclude tvOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents.meta new file mode 100644 index 000000000..f949434c9 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81ac556d5793e40a599e060c17ac1a65 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers.meta new file mode 100644 index 000000000..7ecae758e --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e26d5f5e43d84082b74d9d1886d1dd5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h new file mode 100644 index 000000000..8c3867f6e --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h @@ -0,0 +1,7 @@ + +#ifndef very_crashy_h +#define very_crashy_h + +extern "C" void crashy_signal_runner(float num); + +#endif diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h.meta new file mode 100644 index 000000000..2c48c3d2e --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Headers/very_crashy.h.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b407dafcd31304c4c8b5900c3c1041fa +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist new file mode 100644 index 000000000..448f452d1 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 20G527 + CFBundleDevelopmentRegion + en + CFBundleExecutable + NativeCrashy + CFBundleIdentifier + com.bugsnag.NativeCrashy + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + NativeCrashy + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 13C100 + DTPlatformName + macosx + DTPlatformVersion + 12.1 + DTSDKBuild + 21C46 + DTSDKName + macosx12.1 + DTXcode + 1321 + DTXcodeBuild + 13C100 + LSMinimumSystemVersion + 10.13 + NSHumanReadableCopyright + Copyright © 2019 Bugsnag, Inc. All rights reserved. + + diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist.meta new file mode 100644 index 000000000..b1ee91f1c --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/Info.plist.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 642c1fd1bfc0042cba34bd8b650eceff +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS.meta new file mode 100644 index 000000000..e5dc4a6d7 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 315b8733c81194a4f8f409d5880598d1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy new file mode 100755 index 000000000..0b6cf63fb Binary files /dev/null and b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy differ diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy.meta new file mode 100644 index 000000000..4a4e6f663 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/MacOS/NativeCrashy.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1aa3f3f0baf9245f3af51594e3ea5105 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature.meta new file mode 100644 index 000000000..89b6649e8 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e543b388a25684f51b6e648c8cb3be21 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources new file mode 100644 index 000000000..b03490c68 --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources @@ -0,0 +1,123 @@ + + + + + files + + files2 + + Headers/very_crashy.h + + hash2 + + IUut7ssTkaX2do6LywMHBan/n92fPldPHdw/EV557hA= + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources.meta b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources.meta new file mode 100644 index 000000000..a47c675ed --- /dev/null +++ b/Bugsnag/Assets/Plugins/MacOS/NativeCrashy.bundle/Contents/_CodeSignature/CodeResources.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ac48d5276982145a78ecf1f6aed49dde +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/iOS.meta b/Bugsnag/Assets/Plugins/iOS.meta new file mode 100644 index 000000000..8f8bf5876 --- /dev/null +++ b/Bugsnag/Assets/Plugins/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa30e2757ef824a25a43ea814cf117a1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Plugins/iOS/native_code.mm b/Bugsnag/Assets/Plugins/iOS/native_code.mm new file mode 100644 index 000000000..24c23b14e --- /dev/null +++ b/Bugsnag/Assets/Plugins/iOS/native_code.mm @@ -0,0 +1,23 @@ +#include +#include + +extern "C" { + void RaiseCocoaSignal(); + void TriggerCocoaCppException(); + void TriggerCocoaAppHang(); +} + +void RaiseCocoaSignal() { + abort(); +} + +void TriggerCocoaCppException() { + throw std::runtime_error("CocoaCppException"); +} + +void TriggerCocoaAppHang() { + dispatch_async(dispatch_get_main_queue(), ^{ + sleep(10000); //unit is seconds + }); +} + diff --git a/Bugsnag/Assets/Plugins/iOS/native_code.mm.meta b/Bugsnag/Assets/Plugins/iOS/native_code.mm.meta new file mode 100644 index 000000000..fa0112114 --- /dev/null +++ b/Bugsnag/Assets/Plugins/iOS/native_code.mm.meta @@ -0,0 +1,90 @@ +fileFormatVersion: 2 +guid: e23a968953c794cd1a6a2cd8c8ee86f9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 0 + Exclude tvOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 0 + settings: + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Bugsnag/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset b/Bugsnag/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset index 8b391db30..c96a16f74 100644 --- a/Bugsnag/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset +++ b/Bugsnag/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset @@ -12,6 +12,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 3e0efee821abb422baf5bb3728925f09, type: 3} m_Name: BugsnagSettingsObject m_EditorClassIdentifier: + AutoUploadSymbols: 1 + BugsnagCLIExecutablePath: + UploadEndpoint: StartAutomaticallyAtLaunch: 1 AutoDetectErrors: 1 AutoTrackSessions: 1 diff --git a/Bugsnag/Assets/Scenes/SampleScene.unity b/Bugsnag/Assets/Scenes/SampleScene.unity index 4bc852e91..bf1a88c38 100644 --- a/Bugsnag/Assets/Scenes/SampleScene.unity +++ b/Bugsnag/Assets/Scenes/SampleScene.unity @@ -308,6 +308,140 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &726503136 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 726503137} + - component: {fileID: 726503140} + - component: {fileID: 726503139} + - component: {fileID: 726503138} + m_Layer: 5 + m_Name: CocoaCPPException + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &726503137 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726503136} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1572721881} + m_Father: {fileID: 1173133381} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &726503138 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726503136} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 726503139} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1303533345} + m_TargetAssemblyTypeName: Testing, Assembly-CSharp + m_MethodName: DoTriggerCocoaCppException + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &726503139 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726503136} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &726503140 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726503136} + m_CullTransparentMesh: 1 --- !u!1 &854009149 GameObject: m_ObjectHideFlags: 0 @@ -366,7 +500,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: TEST + m_text: C# Exception m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} @@ -454,6 +588,7 @@ GameObject: - component: {fileID: 1173133381} - component: {fileID: 1173133383} - component: {fileID: 1173133382} + - component: {fileID: 1173133384} m_Layer: 5 m_Name: Panel m_TagString: Untagged @@ -474,6 +609,7 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1840475927} + - {fileID: 726503137} m_Father: {fileID: 494610504} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -520,6 +656,30 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1173133380} m_CullTransparentMesh: 1 +--- !u!114 &1173133384 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1173133380} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 4 + m_StartCorner: 0 + m_StartAxis: 0 + m_CellSize: {x: 400, y: 200} + m_Spacing: {x: 0, y: 20} + m_Constraint: 1 + m_ConstraintCount: 1 --- !u!1 &1255953785 GameObject: m_ObjectHideFlags: 0 @@ -632,6 +792,141 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1572721880 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1572721881} + - component: {fileID: 1572721883} + - component: {fileID: 1572721882} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1572721881 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1572721880} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 726503137} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1572721882 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1572721880} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: CocoaCPPException + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 0 + m_fontSizeMax: 0 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 0 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &1572721883 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1572721880} + m_CullTransparentMesh: 1 --- !u!1 &1840475926 GameObject: m_ObjectHideFlags: 0 @@ -645,7 +940,7 @@ GameObject: - component: {fileID: 1840475929} - component: {fileID: 1840475928} m_Layer: 5 - m_Name: Button + m_Name: csharpException m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -667,10 +962,10 @@ RectTransform: m_Father: {fileID: 1173133381} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 472.4227, y: 254.04} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1840475928 MonoBehaviour: diff --git a/Bugsnag/Assets/Scripts/Testing.cs b/Bugsnag/Assets/Scripts/Testing.cs index 91e64edff..b8cfe4707 100644 --- a/Bugsnag/Assets/Scripts/Testing.cs +++ b/Bugsnag/Assets/Scripts/Testing.cs @@ -1,11 +1,45 @@ using System.Collections; using System.Collections.Generic; +#if UNITY_IOS || UNITY_STANDALONE_OSX + +using System.Runtime.InteropServices; +#endif using UnityEngine; public class Testing : MonoBehaviour { - // Start is called before the first frame update - public void Throw(){ - throw new System.Exception("This is an exception"); + // Start is called before the first frame update + public void Throw() + { + throw new System.Exception("This is an exception"); } + + public void DoTriggerCocoaCppException() + { +#if UNITY_IOS + TriggerCocoaCppException(); +#endif + +#if UNITY_STANDALONE_OSX + crashy_signal_runner(8); +#endif + } + +#if UNITY_STANDALONE_OSX + + [DllImport("NativeCrashy")] + private static extern void crashy_signal_runner(float num); + +#endif + +#if UNITY_IOS + [DllImport("__Internal")] + private static extern void RaiseCocoaSignal(); + + [DllImport("__Internal")] + private static extern void TriggerCocoaCppException(); + + [DllImport("__Internal")] + private static extern void TriggerCocoaAppHang(); +#endif } diff --git a/Bugsnag/Packages/manifest.json b/Bugsnag/Packages/manifest.json index 8ea1d9280..d31652864 100644 --- a/Bugsnag/Packages/manifest.json +++ b/Bugsnag/Packages/manifest.json @@ -1,14 +1,10 @@ { "dependencies": { - "com.unity.collab-proxy": "2.5.1", - "com.unity.ide.rider": "3.0.31", "com.unity.ide.visualstudio": "2.0.22", "com.unity.ide.vscode": "1.2.5", "com.unity.test-framework": "1.1.33", - "com.unity.textmeshpro": "3.0.6", - "com.unity.timeline": "1.6.5", + "com.unity.textmeshpro": "3.0.9", "com.unity.ugui": "1.0.0", - "com.unity.visualscripting": "1.9.4", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/CHANGELOG.md b/CHANGELOG.md index f50e1df62..3342e531e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog -## 8.4.0 (2025-12-10) +## 8.5.0 (2025-01-27) + +You can now automatically upload all your Android and iOS files for stacktrace symbolication in a few simple steps. Please see our online docs for information on how to enable this feature or script it yourself: https://docs.bugsnag.com/platforms/unity/showing-full-stacktraces/ + +### Enhancements + +- Add auto symbol uploads for Android and Xcode builds. [#871](https://github.com/bugsnag/bugsnag-unity/pull/871) + +## 8.4.0 (2025-12-01) ### Enhancements diff --git a/Gemfile b/Gemfile index 51eb06ffb..717dfe443 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,6 @@ gem 'rake' gem 'xcpretty' gem 'xcodeproj' gem 'cocoapods' -gem 'rake' gem 'danger' unless Gem.win_platform? diff --git a/features/build/build_android.feature b/features/build/build_android.feature new file mode 100644 index 000000000..ed988ef78 --- /dev/null +++ b/features/build/build_android.feature @@ -0,0 +1,24 @@ +Feature: Build Android + + @skip_unity_2020 # Unity 2020 only produces 4 symbol files + Scenario: Auto Symbol Upload + When I run the script "features/scripts/prepare_fixture.sh" synchronously + When I run the script "features/scripts/build_android.sh release" synchronously + Then I wait to receive 6 sourcemaps + Then the sourcemaps Content-Type header is valid multipart form-data + And the sourcemap payload field "apiKey" equals "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the sourcemap payload field "versionCode" equals "123" + And the sourcemap payload field "versionName" equals "1.2.3" + And the sourcemap payload field "appId" equals "com.bugsnag.fixtures.unity.notifier.android" + + + @unity_2020_only + Scenario: Auto Symbol Upload Unity 2020 + When I run the script "features/scripts/prepare_fixture.sh" synchronously + When I run the script "features/scripts/build_android.sh release" synchronously + Then I wait to receive 4 sourcemaps + Then the sourcemaps Content-Type header is valid multipart form-data + And the sourcemap payload field "apiKey" equals "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the sourcemap payload field "versionCode" equals "123" + And the sourcemap payload field "versionName" equals "1.2.3" + And the sourcemap payload field "appId" equals "com.bugsnag.fixtures.unity.notifier.android" diff --git a/features/build/build_ios.feature b/features/build/build_ios.feature new file mode 100644 index 000000000..ac9eb3a38 --- /dev/null +++ b/features/build/build_ios.feature @@ -0,0 +1,9 @@ +Feature: Build iOS + + Scenario: Auto Symbol Upload + When I run the script "features/scripts/prepare_fixture.sh" synchronously + When I run the script "features/scripts/generate_xcode_project.sh release" synchronously + When I run the script "features/scripts/build_ios.sh release" synchronously + Then I wait to receive 2 sourcemaps + Then the sourcemaps Content-Type header is valid multipart form-data + And the sourcemap payload field "apiKey" equals "a35a2a72bd230ac0aa0f52715bbdc6aa" diff --git a/features/csharp/csharp_events.feature b/features/csharp/csharp_events.feature index d59918e0d..9ce1a2010 100644 --- a/features/csharp/csharp_events.feature +++ b/features/csharp/csharp_events.feature @@ -12,7 +12,7 @@ Feature: csharp events And the event "unhandled" is false And custom metadata is included in the event And the stack frame methods should match: - | NotifySmokeTest.Run() | Main+d__8.MoveNext() | + | NotifySmokeTest.Run() | Main+d__8.MoveNext() | BugsnagUnity.Client.Notify(Exception exception, HandledState handledState, Func callback) | And expected device metadata is included in the event And expected app metadata is included in the event And the error payload field "events.0.severityReason.unhandledOverridden" is false diff --git a/features/fixtures/maze_runner/Assets/Editor/Builder.cs b/features/fixtures/maze_runner/Assets/Editor/Builder.cs index 59eea7505..7c37d4467 100644 --- a/features/fixtures/maze_runner/Assets/Editor/Builder.cs +++ b/features/fixtures/maze_runner/Assets/Editor/Builder.cs @@ -1,12 +1,9 @@ using System.Linq; using UnityEngine; -using BugsnagUnity.Editor; - -#if UNITY_EDITOR +using BugsnagUnity; using UnityEditor; -using UnityEditor.Callbacks; - -public class Builder : MonoBehaviour { +public class Builder : MonoBehaviour +{ static void BuildStandalone(string folder, BuildTarget target, bool dev) { @@ -72,14 +69,37 @@ public static void AndroidDev() BuildAndroid(true); } + private static void EnableAndroidSymbolUpload() + { +#if UNITY_ANDROID + #if UNITY_2020_1_OR_NEWER + EditorUserBuildSettings.androidCreateSymbolsZip = true; + #else + EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Public; + #endif +#endif + var settingsObject = BugsnagSettingsObject.LoadBuildTimeSettingsObject(); + settingsObject.ApiKey = "a35a2a72bd230ac0aa0f52715bbdc6aa"; + settingsObject.StartAutomaticallyAtLaunch = false; + settingsObject.AutoUploadSymbols = true; + settingsObject.UploadEndpoint = "http://localhost:9339"; + settingsObject.AppVersion = "1.2.3"; + settingsObject.VersionCode = 123; + EditorUtility.SetDirty(settingsObject); + } + public static void AndroidRelease() { + EnableAndroidSymbolUpload(); BuildAndroid(false); } // Generates the Mazerunner APK static void BuildAndroid(bool dev) { +#if UNITY_ANDROID + Debug.Log("Building Android app..."); + PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.ARM64; PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, "com.bugsnag.fixtures.unity.notifier.android"); var opts = CommonMobileBuildOptions(dev ? "mazerunner_dev.apk" : "mazerunner.apk", dev); opts.target = BuildTarget.Android; @@ -90,6 +110,7 @@ static void BuildAndroid(bool dev) var result = BuildPipeline.BuildPlayer(opts); Debug.Log("Result: " + result); +#endif } @@ -102,8 +123,20 @@ public static void IosDev() public static void IosRelease() { + EnableIosSymbolUpload(); IosBuild(false); } + + private static void EnableIosSymbolUpload() + { + var settingsObject = BugsnagSettingsObject.LoadBuildTimeSettingsObject(); + settingsObject.ApiKey = "a35a2a72bd230ac0aa0f52715bbdc6aa"; + settingsObject.StartAutomaticallyAtLaunch = false; + settingsObject.AutoUploadSymbols = true; + settingsObject.UploadEndpoint = "http://localhost:9339"; + settingsObject.AppVersion = "1.2.3"; + EditorUtility.SetDirty(settingsObject); + } static void IosBuild(bool dev) { Debug.Log("Building iOS app..."); @@ -121,7 +154,7 @@ public static void SwitchBuild() { Debug.Log("Building Switch app..."); PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Switch, "com.bugsnag.fixtures.unity.notifier.ios"); - var opts = CommonMobileBuildOptions("mazerunner.nspd",false); + var opts = CommonMobileBuildOptions("mazerunner.nspd", false); opts.target = BuildTarget.Switch; opts.options = BuildOptions.Development; @@ -145,4 +178,3 @@ private static BuildPlayerOptions CommonMobileBuildOptions(string outputFile, bo return opts; } } -#endif diff --git a/features/fixtures/maze_runner/Assets/Resources.meta b/features/fixtures/maze_runner/Assets/Resources.meta new file mode 100644 index 000000000..037476e64 --- /dev/null +++ b/features/fixtures/maze_runner/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ecdc2c24362b642e49c8f2cc8485e7d7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset b/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset new file mode 100644 index 000000000..dbdac0529 --- /dev/null +++ b/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset @@ -0,0 +1,68 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3e0efee821abb422baf5bb3728925f09, type: 3} + m_Name: BugsnagSettingsObject + m_EditorClassIdentifier: + AutoUploadSymbols: 0 + BugsnagCLIExecutablePath: + UploadEndpoint: + StartAutomaticallyAtLaunch: 1 + AutoDetectErrors: 1 + AutoTrackSessions: 1 + ApiKey: + AppType: + AppHangThresholdMillis: 0 + AppVersion: + BundleVersion: + BreadcrumbLogLevel: 4 + Context: + DiscardClasses: [] + EnabledReleaseStages: [] + EnabledErrorTypes: + ANRs: 1 + AppHangs: 1 + OOMs: 1 + Crashes: 1 + ThermalKills: 1 + UnityLog: 1 + EnabledBreadcrumbTypes: + Error: 1 + Log: 1 + Navigation: 1 + Process: 1 + Request: 1 + State: 1 + User: 1 + LaunchDurationMillis: 5000 + MaximumBreadcrumbs: 100 + MaxPersistedEvents: 32 + MaxPersistedSessions: 128 + MaxReportedThreads: 200 + MaxStringValueLength: 10000 + NotifyEndpoint: https://notify.bugsnag.com + NotifyLogLevel: 0 + PersistUser: 1 + SessionEndpoint: https://sessions.bugsnag.com + SendThreads: 1 + RedactedKeys: + - .*password.* + ReleaseStage: + ReportExceptionLogsAsHandled: 1 + SendLaunchCrashesSynchronously: 1 + SecondsPerUniqueLog: 5 + Telemetry: 0000000001000000 + VersionCode: -1 + GenerateAnonymousId: 1 + SwitchCacheType: 1 + SwitchCacheMountName: BugsnagCache + SwitchCacheIndex: 0 + SwitchCacheMaxSize: 10485760 diff --git a/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset.meta b/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset.meta new file mode 100644 index 000000000..05532b6d7 --- /dev/null +++ b/features/fixtures/maze_runner/Assets/Resources/Bugsnag/BugsnagSettingsObject.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46b8a9d872bd745bfb19dfdd85908faa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/features/scripts/build_android.sh b/features/scripts/build_android.sh index 0fcad6229..4c1417a33 100755 --- a/features/scripts/build_android.sh +++ b/features/scripts/build_android.sh @@ -23,7 +23,7 @@ popd pushd "$script_path/../fixtures" # Run unity and immediately exit afterwards, log all output -DEFAULT_CLI_ARGS="-quit -batchmode -nographics -logFile build_android_apk.log" +DEFAULT_CLI_ARGS="-quit -batchmode -nographics -logFile build_android_apk.log -buildTarget Android" project_path=`pwd`/maze_runner diff --git a/features/scripts/build_ios.sh b/features/scripts/build_ios.sh index bece9a7b1..95ed299f6 100755 --- a/features/scripts/build_ios.sh +++ b/features/scripts/build_ios.sh @@ -14,15 +14,17 @@ fi BUILD_TYPE=$1 -pushd "${0%/*}" - script_path=`pwd` -popd -pushd "$script_path/../fixtures" -project_path=`pwd`/maze_runner +pushd "${0%/*}" >/dev/null + script_path=$(pwd) +popd >/dev/null +pushd "$script_path/../fixtures" >/dev/null + project_path="$(pwd)/maze_runner" +popd >/dev/null # Clean any previous builds -find $project_path/output/ -name "*.ipa" -exec rm '{}' \; +find "$project_path/output/" -name "*.ipa" -exec rm '{}' \; +# Determine which Xcode project and IPA name to use if [ "$BUILD_TYPE" == "dev" ]; then XCODE_PROJECT="mazerunner_dev_xcode/Unity-iPhone.xcodeproj" OUTPUT_IPA="mazerunner_dev_${UNITY_VERSION:0:4}.ipa" @@ -34,33 +36,36 @@ else exit 1 fi -# Archive and export the project -xcrun xcodebuild -project $project_path/$XCODE_PROJECT \ - -scheme Unity-iPhone \ - -configuration Debug \ - -archivePath $project_path/archive/Unity-iPhone.xcarchive \ - -allowProvisioningUpdates \ - -allowProvisioningDeviceRegistration \ - -quiet \ - GCC_WARN_INHIBIT_ALL_WARNINGS=YES \ - archive +# ARCHIVE (equivalent to Product > Archive) +xcrun xcodebuild \ + -project "$project_path/$XCODE_PROJECT" \ + -scheme Unity-iPhone \ + -configuration Release \ + clean archive \ + -archivePath "$project_path/archive/Unity-iPhone.xcarchive" \ + -allowProvisioningUpdates \ + -allowProvisioningDeviceRegistration \ + -quiet \ + GCC_WARN_INHIBIT_ALL_WARNINGS=YES -if [ $? -ne 0 ] -then +if [ $? -ne 0 ]; then echo "Failed to archive project" exit 1 fi +# EXPORT ARCHIVE xcrun xcodebuild -exportArchive \ - -archivePath $project_path/archive/Unity-iPhone.xcarchive \ - -exportPath $project_path/output/ \ - -quiet \ - -exportOptionsPlist $script_path/exportOptions.plist + -archivePath "$project_path/archive/Unity-iPhone.xcarchive" \ + -exportPath "$project_path/output/" \ + -exportOptionsPlist "$script_path/exportOptions.plist" \ + -quiet if [ $? -ne 0 ]; then echo "Failed to export app" exit 1 fi -# Move to known location for running (note - the name of the .ipa differs between Xcode versions) -find $project_path/output/ -name "*.ipa" -exec mv '{}' $project_path/$OUTPUT_IPA \; +# MOVE IPA TO A KNOWN LOCATION +find "$project_path/output/" -name "*.ipa" -exec mv '{}' "$project_path/$OUTPUT_IPA" \; + +echo "Successfully built and exported: $OUTPUT_IPA" \ No newline at end of file diff --git a/features/scripts/generate_xcode_project.sh b/features/scripts/generate_xcode_project.sh index 6dc9462af..cb637e697 100755 --- a/features/scripts/generate_xcode_project.sh +++ b/features/scripts/generate_xcode_project.sh @@ -22,7 +22,7 @@ popd pushd "$script_path/../fixtures" -DEFAULT_CLI_ARGS="-quit -nographics -batchmode -logFile unity.log" +DEFAULT_CLI_ARGS="-quit -nographics -batchmode -logFile unity.log -buildTarget iOS" project_path=`pwd`/maze_runner diff --git a/features/steps/unity_steps.rb b/features/steps/unity_steps.rb index 45b7bb6d5..aea18add7 100644 --- a/features/steps/unity_steps.rb +++ b/features/steps/unity_steps.rb @@ -19,6 +19,12 @@ def execute_command(action, scenario_name = '') raise 'Test fixture did not GET /command' unless Maze::Server.commands.remaining.empty? end +Then('the sourcemaps Content-Type header is valid multipart form-data') do + expected = /^multipart\/form-data; boundary=([^;]+)/ + actual = Maze::Server.sourcemaps.current[:request]['content-type'] + Maze.check.match(expected, actual) +end + When('I clear the Bugsnag cache') do case Maze::Helper.get_current_platform when 'macos', 'webgl' @@ -151,18 +157,33 @@ def check_error_reporting_api(notifier_name) stacktrace = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.exceptions.0.stacktrace') expected_frame_values = expected_values.raw - flunk('The stacktrace is empty') if stacktrace.length == 0 + flunk('The stacktrace is empty') if stacktrace.empty? + # Extract just the method names from the stacktrace methods = stacktrace.map { |item| item['method'] } + # For each set of expected frames, check if at least one method matches expected_frame_values.each do |expected_frames| method_index = 0 frame_matches = false + + # Keep iterating over `methods` until we find a match or exhaust all until frame_matches || method_index.eql?(methods.size) method = methods[method_index] - frame_matches = expected_frames.any? { |frame| frame == method } + + # Sanitize the actual method to ignore any numeric portion in 'd__' + sanitized_method = method.gsub(/d__\d+/, 'd__REPLACEMENT') + + # Check each expected frame for a match by also sanitizing it + frame_matches = expected_frames.any? do |frame| + sanitized_frame = frame.gsub(/d__\d+/, 'd__REPLACEMENT') + sanitized_method == sanitized_frame + end + method_index += 1 end + + # Fail if we didn't find a match for this set of expected frames Maze.check.true(frame_matches, "None of the methods match the expected frames #{expected_frames}") end end diff --git a/features/support/env.rb b/features/support/env.rb index ded754708..343e58aed 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -18,6 +18,14 @@ end end +Before('@unity_2020_only') do + if ENV['UNITY_VERSION'] + unity_version = ENV['UNITY_VERSION'][0..3].to_i + if unity_version != 2020 + skip_this_scenario('Skipping scenario, this scenario is only for Unity 2020') + end + end +end Before('@skip_webgl') do |_scenario| skip_this_scenario('Skipping scenario') unless Maze.config.browser.nil? @@ -180,7 +188,9 @@ AfterAll do case Maze::Helper.get_current_platform when 'macos' - app_name = Maze.config.app.gsub /\.app$/, '' - Maze::Runner.run_command("log show --predicate '(process == \"#{app_name}\")' --style syslog --start '#{Maze.start_time}' > #{app_name}.log") + if !Maze.config.app.nil? + app_name = Maze.config.app.gsub /\.app$/, '' + Maze::Runner.run_command("log show --predicate '(process == \"#{app_name}\")' --style syslog --start '#{Maze.start_time}' > #{app_name}.log") + end end end