How to run a setup method when a package is imported/updated?

Hello :slight_smile:
We are trying to run a setup method when a package is imported, we found out that methods with InitializeOnLoadMethod are called when a package is imported for the first time but not when it is updated :confused: Just to clarify our use case, we are using a private scoped registry, we want to create/update a custom gradle template on the Assets/Plugins folder every time a package is imported/updated.
Is there a proper way to call a method when a package is imported and/or updated?

Hi @caioteixeira5 !

There are no existing callbacks to know when a package is installed/updated or removed :frowning:

But you can effectively used a InitializeOnLoadMethod fct to know which packages are installed.
Btw, this fct will be called at each domain reload (install/update/remove of a package but also entering play-mode)

Hereā€™s a code snippet (works with Unity 2019.1.0b9) you may starts with:

using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using UnityEditor.PackageManager;

public static class InstalledPackages
{
    [InitializeOnLoadMethod]
    private static void InitializeOnLoad()
    {
        var listRequest = Client.List(true);
        while (!listRequest.IsCompleted)
            Thread.Sleep(100);

        if (listRequest.Error != null)
        {
            Debug.Log("Error: " + listRequest.Error.message);
            return;
        }

        var packages = listRequest.Result;
        var text = new StringBuilder("Packages:\n");
        foreach (var package in packages)
        {
            if (package.source == PackageSource.Registry)
                text.AppendLine($"{package.name}: {package.version} [{package.resolvedPath}]");
        }
       
        Debug.Log(text.ToString());
    }
}

Regards,

Manu73

1 Like

Thank you for the quick answer! :slight_smile:

I tested it in both Unity 2018.3 and 2019.1, but it seems to have the same behavior that I had before. This method is not called when a package is updated.
I actually have a piece of new information, this issue only happens when using a private registry. When I update an official package the method is called.

Hi @caioteixeira5 !

Sorry for this inconvenience.

I locally use Verdaccio and scoped registry in Packman and I was able to update my package published in my local registry.

The only difference I think, itā€™s that my package contains scripts that I have updated.

Domain reload (which will call InitializeOnLoadMethod) will only occur if there are changes in scripts.

Does your package only contains assets (aka non scripts)?
Also, in Unity General preferences, do you have Auto-Refresh checked?

Manu73

Hi @caioteixeira5 !

Iā€™ve updated the code snippet to take care of assets only change:

using System.Linq;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using UnityEditor.PackageManager;

public class InstalledPackages : AssetPostprocessor
{
    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        var inPackages = importedAssets.Any(path => path.StartsWith("Packages/")) ||
            deletedAssets.Any(path => path.StartsWith("Packages/")) ||
            movedAssets.Any(path => path.StartsWith("Packages/")) ||
            movedFromAssetPaths.Any(path => path.StartsWith("Packages/"));

        if (inPackages)
        {
            InitializeOnLoad();
        }
    }
   
    [InitializeOnLoadMethod]
    private static void InitializeOnLoad()
    {
        var listRequest = Client.List(true);
        while (!listRequest.IsCompleted)
            Thread.Sleep(100);

        if (listRequest.Error != null)
        {
            Debug.Log("Error: " + listRequest.Error.message);
            return;
        }

        var packages = listRequest.Result;
        var text = new StringBuilder("Packages:\n");
        foreach (var package in packages)
        {
            if (package.source == PackageSource.Registry)
                text.AppendLine($"{package.name}: {package.version} [{package.resolvedPath}]");
        }
       
        Debug.Log(text.ToString());
    }
}

You can adapt this code to only look in specific Packages.

1 Like

Thank you!
It is working now :slight_smile:
I am also using Verdaccio, my package has both assets and scripts. I am not sure why exactly this was not working, but modifying a hardcoded string on a script was not triggering a recompile for some reason.

Youā€™re welcome.

Hi @manu73 ! Is there any way to get a system like this working despite other scripts throwing errors? I tried both asmdefs and DLLs to no avail Trigger installer script on load despite other assemblies throwing errors?

What I want to do is create an ā€œinstallerā€ that automatically triggers and allows the user to install the missing dependencies. If my current approach doesnā€™t work, how would you recommend I do it instead?

Thank you!

3 Likes

Hey @QFSW did you make any progress on this topic? I am looking to do the same but if there are compiler errors Unity grinds to a halt, even if there are scripts which can execute.

Thanks!

Unfortunately not

I would also love functionality like a callback as well when a custom package is installed/updated. Just adding my tiny voice to this thread.

@manu73 thanks for the code Iā€™ll try that out.

Update: The code doesnā€™t seem to detect custom packages, only unity packages

Update2: figured it out. Deleting the if check for the package source found all package registries. By the way, the name is the full com.x.package name if youā€™re checking for a particular package. Thanks for the code!

Is it not possible to use PackageRegistrationEventArgs to detect when a Package is installed/updated/removed? I see this thread started in 2019 - could this API be relatively new? I havenā€™t tried it yet - Iā€™m currently still investigating what I will need to do and found this thread from google.

@flintmech : PackageRegistryEventArgs is supported since 2020.3 (available since 2020.2). It is used by
PackageManager.Events.registeredPackages which is supported since 2020.3 (available since 2020.2) and PackageManager.Events.registeringPackages with the same availability, the former (following my own observations, no guarantee) apparently invoked when a package is being installed (i.e. after installation has finished), the latter when uninstalling a package (obviously before uninstalling).

So yes, it is (relatively) new (at least younger than the thread), but this is the way to go for recent editors.

2 Likes

Hi, I need a callback for when a Sample is downloaded. Any chance thereā€™s an event for that as well? I havenā€™t been able to find it.

Of course, I could use the InitializeOnLoad trick but that seems hacky.