sentry-unity icon indicating copy to clipboard operation
sentry-unity copied to clipboard

Setting Options parameters programmatically for use during build time

Open bitsandfoxes opened this issue 3 years ago • 16 comments

The SDK offers programmatic configuration when letting it self-initialize during runtime. This functionality does not yet extend to the options used to setup the native SDKs during build time.

E.g. Users want to programmatically set their environment for the native SDKs.

bitsandfoxes avatar Mar 03 '22 16:03 bitsandfoxes

Can we offer a code snippet users can use until there's a built-in feature? Like, pulling our scriptable object in some C# build-time script and modify it?

bruno-garcia avatar Mar 03 '22 16:03 bruno-garcia

Something like this should work as a workaround and we can add this to the docs?

class CustomSentryBuildProcessor : IPreprocessBuildWithReport
{
    public int callbackOrder => 0;

    public void OnPreprocessBuild(BuildReport report)
    {
        var options = AssetDatabase.LoadAssetAtPath(
            ScriptableSentryUnityOptions.GetConfigPath(ScriptableSentryUnityOptions.ConfigName),
            typeof(ScriptableSentryUnityOptions)) as ScriptableSentryUnityOptions;
        
        if (options != null)
        {
            options.EnvironmentOverride = "MyCustomEnvironment";
        }
    }
}

bitsandfoxes avatar Mar 03 '22 18:03 bitsandfoxes

We just need to be careful with the callbackOrder in case we want to use it ourselves at some point.

bitsandfoxes avatar Mar 03 '22 19:03 bitsandfoxes

I've had a look into this (before going on with other issues). The approach I've taken is the same as for c# scriptable options - allow users to define a script (in the Assets folder), objective-c in case of iOS, java/kotlin for Android, c/c++ for standalone native. The script is then added to the project during build (we can do that in our platform-specific build pre/post process) and the contained code is called (in our generated main.mm/cpp), similarly to what we do in c#. Android may require a bit more work because AFAICT we don't update the app initialization yet but may have to start.

vaind avatar Mar 10 '22 13:03 vaind

The #626 PR prepares the editor changes. The idea is to add a new script-select for each supported platform.

image

vaind avatar Mar 10 '22 13:03 vaind

Hey, just want to chime in here since I am one of the users who contacted support on that topic. Just to give you an idea of our use case. We use an automated build pipeline for building for different targets from the same repo state. We can add parameters to the pipeline and in our custom unity build scripts these can be accessed. but how the sentry plugin currently works is that it reads config from the scriptable object options which are currently defined at "editor time" and have to be committed to the repo. so right now our solution is to do it manually. before we create a live build, we adjust the config (environment in our case), commit it, create build, set config back. the only way to remove the manual steps here is currently to adjust the Scriptable Object options at build time like the posted snippet above seems to do.

adding the option to additionally add native option scripts seems at least in our case overkill, since it would require knowledge for native code which not all Unity devs have. Unless you provide easy-to-use snippets/templates for doing this.

In our case all we want to do is set the Environment (Override) from production to live in our build pipeline when we create a live build. to give you an idea about our specific case. This works currently with Environment Override but only for the C# Layer and not for the native layer.

hope I made things clear. and thanks for working on this matter and listening to the user. :-)

DazzlingBernardo avatar Mar 17 '22 11:03 DazzlingBernardo

@DazzlingBernardo

We can add parameters to the pipeline and in our custom unity build scripts these can be accessed

How exactly are you passing these params and accessing them in the scripts? Maybe you could do the same in a single programmatic option config script (the option in the screenshot above). Actually that script currently runs in two situations:

  1. during build time - the resulting config will also be used by the native initialization. (@bitsandfoxes correct me here if I'm wrong but that's what I've observed - at least for Android; I haven't specifically checked iOS)
  2. during runtime initialization.

vaind avatar Mar 17 '22 11:03 vaind

during build time - the resulting config will also be used by the native initialization.

Unfortunately, the Options Configuration via the script in the screenshot won't affect the native options for mobile. We currently do not call Configure during build time since we did not want to mix the same callback to run at build time and runtime.

But: For mobile, whatever you do to the Sentry Scriptable Options Object before the game actually builds, ends up in the native options as well:

  • iOS: https://github.com/getsentry/sentry-unity/blob/ee152cbc029390c385ef69157336fd409dc12f37/src/Sentry.Unity.Editor.iOS/NativeOptions.cs#L34
  • Android: https://github.com/getsentry/sentry-unity/blob/ee152cbc029390c385ef69157336fd409dc12f37/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs#L118 So the snippet above would allow you to set the environment for all layers.

We're aware of this pain point and we're actively looking into addressing it properly.

bitsandfoxes avatar Mar 17 '22 17:03 bitsandfoxes

We currently do not call Configure during build time since we did not want to mix the same callback to run at build time and runtime.

are you absolutely sure? because I'm pretty sure it is called when the android manifest is being generated (pre-build)... Stack trace:

  • https://github.com/getsentry/sentry-unity/blob/ee152cbc029390c385ef69157336fd409dc12f37/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs#L26
  • https://github.com/getsentry/sentry-unity/blob/ee152cbc029390c385ef69157336fd409dc12f37/src/Sentry.Unity/ScriptableSentryUnityOptions.cs#L75
  • https://github.com/getsentry/sentry-unity/blob/ee152cbc029390c385ef69157336fd409dc12f37/src/Sentry.Unity/ScriptableSentryUnityOptions.cs#L131

vaind avatar Mar 17 '22 19:03 vaind

I stand corrected. That is a regression. Initially, we wanted to call it inside of SentryInitialization. So it would work but this is not intentional behaviour.

bitsandfoxes avatar Mar 17 '22 20:03 bitsandfoxes

@vaind

How exactly are you passing these params and accessing them in the scripts?

we set them as environment variables in a jenkinsfile configuration file like in this example

image

and access those in our build pipeline scripts in unity like this:

image

Maybe you could do the same in a single programmatic option config script (the option in the screenshot above). Actually that script currently runs in two situations:

  1. during build time - the resulting config will also be used by the native initialization. (@bitsandfoxes correct me here if I'm wrong but that's what I've observed - at least for Android; I haven't specifically checked iOS)
  2. during runtime initialization.

Last time I checked the Sentry Unity Plugin, and I checked out the source code, the ScriptableObject Config was applied only for the C# layer but not native part. Tested only IOS so far since I don't have an Android device currently.

DazzlingBernardo avatar Mar 18 '22 08:03 DazzlingBernardo

So it would work but this is not intentional behaviour.

I understand. On the other hand, it's pretty neat that you can have the same script and users could do a lot of programmatic configuration based on some logic (just like @DazzlingBernardo wants to) with very little effort from our end (updating the docs and adding a test so that it keeps working in the future).

Isn't it really what this issue is about? "Setting Options parameters programmatically for use during build time" - it already works, just needs a little effort to make it known.

vaind avatar Mar 18 '22 08:03 vaind

Something like this should work as a workaround and we can add this to the docs?

Quick update from my side. We now have a working solution based on the script posted above.

in our jenkins file for the live build we set an environment variable and then

public class BuildPreProcessor : IPreprocessBuildWithReport
    {
        public int callbackOrder => Int32.MaxValue;
        public void OnPreprocessBuild(BuildReport report)
        {
            if(Environment.GetEnvironmentVariable("ENVIRONMENT") == "live")
            {
                ScriptableSentryUnityOptions options = AssetDatabase.LoadAssetAtPath(
                    ScriptableSentryUnityOptions.GetConfigPath(),
                    typeof(ScriptableSentryUnityOptions)) as ScriptableSentryUnityOptions;
        
                if (options != null)
                {
                    options.EnvironmentOverride = "live";
                    EditorUtility.SetDirty(options);
                    AssetDatabase.SaveAssetIfDirty(options);
                }
            }
        }
    }

DazzlingBernardo avatar May 23 '22 07:05 DazzlingBernardo

Quick update from my side. We now have a working solution based on the script posted above.

Thanks a lot for the update. Glad to have confirmation that the workaround actually works in combination with your jenkins setup.

bitsandfoxes avatar May 30 '22 08:05 bitsandfoxes

I wanted to have a look at this a couple of times but I'm not sure about the approach.

The current editor setting "Programmatic options configuration" already supports setting stuff during build time as it is also called the same way at build time as well as at runtime.

@bitsandfoxes said above that's not intended behaviour.

Should we break that and instead provide two methods/a flag indicating whether the script is called at build time or runtime? Or do we keep it as is and just document it? Or any other approach you'd like to take?

I lean towards the former, i.e. changing the ScriptableOptionsConfiguration interface to clearly indicate the two behaviours.

// Current definition
public abstract class ScriptableOptionsConfiguration : ScriptableObject
{
    public abstract void Configure(SentryUnityOptions options);
}

// Proposal A: two methods
public abstract class ScriptableOptionsConfiguration : ScriptableObject
{
    public abstract void ConfigureAtBuild(SentryUnityOptions options);
    public abstract void ConfigureAtRuntime(SentryUnityOptions options);
}

// Proposal B: argument
public abstract class ScriptableOptionsConfiguration : ScriptableObject
{
    public enum ScriptableOptionsConfigurationPhase
    {
        Build,
        Runtime
    }
    public abstract void Configure(SentryUnityOptions options, ScriptableOptionsConfigurationPhase phase);
}

vaind avatar Jul 11 '22 10:07 vaind

I lean towards the former

I'd second proposal A with clear intent.

bitsandfoxes avatar Jul 11 '22 11:07 bitsandfoxes