Skip to content

Commit f1b87dd

Browse files
authored
feat: Created programmatic configuration for CLI options (getsentry#1887)
1 parent 3ea14cc commit f1b87dd

File tree

12 files changed

+281
-132
lines changed

12 files changed

+281
-132
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- The SDK now provides a dedicated sentry-cli scriptable config, available on the `Debug Symbols` tab. This allows for programmatic configuration of the used cli-options during build. ([#1887](https://github.com/getsentry/sentry-unity/pull/1887))
8+
59
### Fixes
610

711
- The SDK no longer sends events when it fails to initialize the native SDK on Windows and Linux and logs those instead. It also suppresses `EntryPointNotFoundException` if sentry-native is not available at runtime. Native crashes won't get capture but it'll continue to capture C# errors. ([#1898](https://github.com/getsentry/sentry-unity/pull/1898))
812
- The SDK no longer closes the underlying native SDK during the games shutdown if native support has not been enabled. This allows the SDK to support and capture errors in case of building the game as a library on a mobile (Android or iOS) game. ([#1897](https://github.com/getsentry/sentry-unity/pull/1897))
913

14+
### Features
15+
16+
- The SDK now provides a dedicated sentry-cli scriptable config, available on the `Debug Symbols` tab. This allows for programmatic configuration of the used cli-options during build. ([#1887](https://github.com/getsentry/sentry-unity/pull/1887))
17+
1018
### Dependencies
1119

1220
- Bump .NET SDK from v4.12.1 to v4.13.0 ([#1879](https://github.com/getsentry/sentry-unity/pull/1879), [#1885](https://github.com/getsentry/sentry-unity/pull/1885))
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &11400000
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 0
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: be9687ec81ccb4180b0ed8c055df62d8, type: 3}
13+
m_Name: SentryCliConfiguration
14+
m_EditorClassIdentifier:

samples/unity-of-bugs/Assets/Resources/Sentry/SentryCliConfiguration.asset.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using Sentry.Unity;
3+
using UnityEngine;
4+
5+
public class SentryCliConfiguration : SentryCliOptionsConfiguration
6+
{
7+
public override void Configure(SentryCliOptions cliOptions)
8+
{
9+
var isDevMachineWithSymbolUpload = Environment.GetEnvironmentVariable("DEV_SYMBOL_UPLOAD") == "true";
10+
if (isDevMachineWithSymbolUpload)
11+
{
12+
Debug.Log("'DEV_SYMBOL_UPLOAD' detected: Debug symbol upload has been enabled.");
13+
cliOptions.UploadSymbols = true;
14+
}
15+
}
16+
}

samples/unity-of-bugs/Assets/Scripts/SentryCliConfiguration.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using UnityEditor;
5+
using UnityEngine;
6+
7+
namespace Sentry.Unity.Editor.ConfigurationWindow;
8+
9+
internal static class OptionsConfigurationItem
10+
{
11+
private const string CreateScriptableObjectFlag = "Sentry/CreateScriptableOptionsObject";
12+
private const string ScriptNameKey = "Sentry/ScriptableOptionsScript";
13+
14+
public static T? Display<T>(T? value, string fieldName, string scriptName, string toolTip) where T : ScriptableObject
15+
{
16+
GUILayout.BeginHorizontal();
17+
var result = EditorGUILayout.ObjectField(
18+
new GUIContent(fieldName, toolTip),
19+
value,
20+
typeof(T),
21+
false
22+
) as T;
23+
if (GUILayout.Button("New", GUILayout.ExpandWidth(false)))
24+
{
25+
var t = typeof(T);
26+
if (t == typeof(SentryRuntimeOptionsConfiguration) || t == typeof(SentryBuildTimeOptionsConfiguration))
27+
{
28+
CreateDeprecatedConfigurationScript<T>(fieldName, scriptName);
29+
}
30+
else if (t == typeof(SentryCliOptionsConfiguration))
31+
{
32+
CreateConfigurationScript(fieldName, SentryCliOptionsConfiguration.Template, scriptName);
33+
}
34+
else
35+
{
36+
throw new Exception("Attempting to create a new instance of unsupported type " + typeof(T).FullName);
37+
}
38+
}
39+
GUILayout.EndHorizontal();
40+
return result;
41+
}
42+
43+
private static string SentryAssetPath(string scriptName) => $"Assets/Resources/Sentry/{scriptName}.asset";
44+
45+
private static void CreateDeprecatedConfigurationScript<T>(string fieldName, string scriptName)
46+
{
47+
const string directory = "Assets/Scripts";
48+
if (!AssetDatabase.IsValidFolder(directory))
49+
{
50+
AssetDatabase.CreateFolder(Path.GetDirectoryName(directory), Path.GetFileName(directory));
51+
}
52+
53+
var scriptPath = EditorUtility.SaveFilePanel(fieldName, directory, scriptName, "cs");
54+
if (string.IsNullOrEmpty(scriptPath))
55+
{
56+
return;
57+
}
58+
59+
if (scriptPath.StartsWith(Application.dataPath))
60+
{
61+
// AssetDatabase prefers a relative path
62+
scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length);
63+
}
64+
65+
scriptName = Path.GetFileNameWithoutExtension(scriptPath);
66+
67+
var template = new StringBuilder();
68+
template.AppendLine("using UnityEngine;");
69+
template.AppendLine("using Sentry.Unity;");
70+
template.AppendLine();
71+
template.AppendFormat("[CreateAssetMenu(fileName = \"{0}\", menuName = \"Sentry/{1}\", order = 999)]\n", SentryAssetPath(scriptName), scriptName);
72+
template.AppendFormat("public class {0} : {1}\n", scriptName, typeof(T).FullName);
73+
template.AppendLine("{");
74+
75+
if (typeof(T) == typeof(SentryBuildTimeOptionsConfiguration))
76+
{
77+
template.AppendLine(" /// Called during app build. Changes made here will affect build-time processing, symbol upload, etc.");
78+
template.AppendLine(" /// Additionally, because iOS, macOS and Android native error handling is configured at build time,");
79+
template.AppendLine(" /// you can make changes to these options here.");
80+
}
81+
else
82+
{
83+
template.AppendLine(" /// Called at the player startup by SentryInitialization.");
84+
template.AppendLine(" /// You can alter configuration for the C# error handling and also");
85+
template.AppendLine(" /// native error handling in platforms **other** than iOS, macOS and Android.");
86+
}
87+
88+
template.AppendLine(" /// Learn more at https://docs.sentry.io/platforms/unity/configuration/options/#programmatic-configuration");
89+
template.AppendFormat(" public override void Configure(SentryUnityOptions options{0})\n",
90+
typeof(T) == typeof(SentryBuildTimeOptionsConfiguration) ? ", SentryCliOptions cliOptions" : "");
91+
template.AppendLine(" {");
92+
if (typeof(T) != typeof(SentryBuildTimeOptionsConfiguration))
93+
{
94+
template.AppendLine(" // Note that changes to the options here will **not** affect iOS, macOS and Android events. (i.e. environment and release)");
95+
template.AppendLine(" // Take a look at `SentryBuildTimeOptionsConfiguration` instead.");
96+
}
97+
template.AppendLine(" // TODO implement");
98+
template.AppendLine(" }");
99+
template.AppendLine("}");
100+
101+
File.WriteAllText(scriptPath, template.ToString().Replace("\r\n", "\n"));
102+
103+
// The created script has to be compiled and the scriptable object can't immediately be instantiated.
104+
// So instead we work around this by setting a 'CreateScriptableObjectFlag' flag in the EditorPrefs to
105+
// trigger the creation after the scripts reloaded.
106+
EditorPrefs.SetBool(CreateScriptableObjectFlag, true);
107+
EditorPrefs.SetString(ScriptNameKey, scriptName);
108+
109+
AssetDatabase.ImportAsset(scriptPath);
110+
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(scriptPath);
111+
}
112+
113+
private static void CreateConfigurationScript(string fieldName, string template, string scriptName)
114+
{
115+
const string directory = "Assets/Scripts";
116+
if (!AssetDatabase.IsValidFolder(directory))
117+
{
118+
AssetDatabase.CreateFolder(Path.GetDirectoryName(directory), Path.GetFileName(directory));
119+
}
120+
121+
var scriptPath = EditorUtility.SaveFilePanel(fieldName, directory, scriptName, "cs");
122+
if (string.IsNullOrEmpty(scriptPath))
123+
{
124+
return;
125+
}
126+
127+
if (scriptPath.StartsWith(Application.dataPath))
128+
{
129+
// AssetDatabase prefers a relative path
130+
scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length);
131+
}
132+
133+
scriptName = Path.GetFileNameWithoutExtension(scriptPath);
134+
var script = template.Replace("{{ScriptName}}", scriptName);
135+
136+
File.WriteAllText(scriptPath, script);
137+
138+
// The created script has to be compiled and the scriptable object can't immediately be instantiated.
139+
// So instead we work around this by setting a 'CreateScriptableObjectFlag' flag in the EditorPrefs to
140+
// trigger the creation after the scripts reloaded.
141+
EditorPrefs.SetBool(CreateScriptableObjectFlag, true);
142+
EditorPrefs.SetString(ScriptNameKey, scriptName);
143+
144+
AssetDatabase.ImportAsset(scriptPath);
145+
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(scriptPath);
146+
}
147+
148+
[UnityEditor.Callbacks.DidReloadScripts]
149+
private static void OnScriptsReloaded()
150+
{
151+
if (!EditorPrefs.GetBool(CreateScriptableObjectFlag))
152+
{
153+
return;
154+
}
155+
156+
var scriptName = EditorPrefs.GetString(ScriptNameKey);
157+
EditorPrefs.DeleteKey(CreateScriptableObjectFlag);
158+
EditorPrefs.DeleteKey(ScriptNameKey);
159+
160+
SetScript(scriptName);
161+
}
162+
163+
internal static void SetScript(string scriptNameWithoutExtension)
164+
{
165+
var optionsConfigurationObject = ScriptableObject.CreateInstance(scriptNameWithoutExtension);
166+
AssetDatabase.CreateAsset(optionsConfigurationObject, SentryAssetPath(scriptNameWithoutExtension));
167+
AssetDatabase.Refresh();
168+
169+
var options = EditorWindow.GetWindow<SentryWindow>().Options;
170+
var cliOptions = EditorWindow.GetWindow<SentryWindow>().CliOptions;
171+
172+
switch (optionsConfigurationObject)
173+
{
174+
case SentryRuntimeOptionsConfiguration runtimeConfiguration:
175+
options.RuntimeOptionsConfiguration ??= runtimeConfiguration; // Don't overwrite if already set
176+
break;
177+
case SentryBuildTimeOptionsConfiguration buildTimeConfiguration:
178+
options.BuildTimeOptionsConfiguration ??= buildTimeConfiguration; // Don't overwrite if already set
179+
break;
180+
case SentryCliOptionsConfiguration cliConfiguration:
181+
cliOptions.CliOptionsConfiguration ??= cliConfiguration; // Don't overwrite if already set
182+
break;
183+
}
184+
}
185+
}

src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,15 @@ internal static void Display(SentryCliOptions cliOptions)
4747
cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Project) ? SentryWindow.ErrorIcon : null,
4848
"The project name in Sentry"),
4949
cliOptions.Project);
50+
51+
EditorGUILayout.Space();
52+
53+
cliOptions.CliOptionsConfiguration = OptionsConfigurationItem.Display(
54+
cliOptions.CliOptionsConfiguration,
55+
"Sentry CLI Config Script",
56+
"SentryCliConfiguration",
57+
"A scriptable object that inherits from 'SentryCliOptionsConfiguration'." +
58+
"It allows you to programmatically modify the options used during debug symbols upload."
59+
);
5060
}
5161
}

0 commit comments

Comments
 (0)