How get PackageInfo by name and version?

PackageSample.cs uses the following code to get samples of a specific package version:

public static IEnumerable<Sample> FindByPackage(string packageName, string packageVersion)
        {
            var package = PackageDatabase.instance.GetPackage(packageName);
            if (package != null)
            {
                var version = package.versions.installed;
                if (!string.IsNullOrEmpty(packageVersion))
                    version = package.versions.FirstOrDefault(v => v.version == packageVersion);
                if (version != null)
                    return version.samples;
            }
            return new List<Sample>();
        }

Unfortunately, PackageDatabase, IPackageVersion etc are internal, so I can’t follow that pattern.
I’d like to access PackageInfo by name and version. Is there another way to get at that info?

PackageDatabaseInternal has all the nice helpers for this (e.g. GetPackageAndVersion).

And there’s more internal stuff:
Is there an alternative to get all packages to this internal class? Or to get all currently installed packages?

@okcompute_unity would be great to get your input again :slight_smile:

HI @fherbst , I think you can find what you’re looking for in Unity - Scripting API: Client.

The Client.Search function accepts a package id or package name. The SearchRequest it returns which will contain an array of PackageInfo for the different versions of that package when the request is complete. You can then search through that array of PackageInfo objects to find the version you’re looking for.

Thanks, but at least from the description this clearly says “Searches the Unity package registry for the given package.”, so I didn’t even bother trying (I’m using a lot of custom packages on our own registry). Additionally, I’d at best consider that a workaround - all information is already locally available, an additional web search might not be feasible with no internet available, … offline mode still is documented as only looking at Unity packages. What about embedded ones, local ones, … is the documentation just wrong and this is actually searching “all packages”?

OK, after testing this I can confirm that this doesn’t work for local packages at all. It will happily claim “success” with a totally wrong version that is not installed but it doesn’t find the actually used local package.

Ah, yes, if you’re using packages from another registry then this may not work. We’re working on making Client.Search include scoped registries, but I do not believe it does so right now.

I will check if there are any alternatives for retrieving PackageInfo, but I’m under the impression that the Client API is how users are intended to interact with Package Manager.

I think you could try opening a bug for Search not working on local packages.

Workaround at least for installed packages is to search Assetdatabase for all “package.json” and use “PackageInfo.FromAsset” on them - voila. All actually installed packages. From there, all available versions can be found.

3 Likes

Since the Client-API always returns a PackageInfo with the PackageStatus Unavailable I implemented your suggestion and this works fine for me and also is way faster. Thanks for the hint!

List<PackageInfo> packageJsons = AssetDatabase.FindAssets("package")
                .Select(AssetDatabase.GUIDToAssetPath).Where(x => AssetDatabase.LoadAssetAtPath<TextAsset>(x) != null)
                .Select(PackageInfo.FindForAssetPath).ToList();

return packageJsons.Any(x => x.name == _hdrpPackageName && x.version == _hdrpPackageVersion);
8 Likes
#nullable enable
using System.Linq;
using SimpleJSON;
using UnityEditor;
using UnityEngine;

public static class PackageData
{
    public static string GetVersion(string name = "com.unity.ugui")
    {
        var package = AssetDatabase
            .FindAssets("package")
            .Select(AssetDatabase.GUIDToAssetPath)
            .Where(x => x.EndsWith("package.json"))
            .Where(x => AssetDatabase.LoadAssetAtPath<TextAsset>(x) is not null)
            .Select(s => AssetDatabase.LoadAssetAtPath<TextAsset>(s).text)
            .Select(JSON.Parse)
            .FirstOrDefault(j => j["name"] == name);
       
        string? ver = package?["version"];
        if (ver is null) return "";
        return $"version {ver}";
    }
}

Thanks @unity-marcus ! Your code was returning null for some reason for my setup, but I wrote this function to catch the null reference. Here’s the code to maybe save the next person a few minutes:

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

// ... within some class ...

private PackageInfo GetPackageInfo(string packageName)
{
    return UnityEditor.AssetDatabase.FindAssets("package")
        .Select(UnityEditor.AssetDatabase.GUIDToAssetPath)
            .Where(x => UnityEditor.AssetDatabase.LoadAssetAtPath<TextAsset>(x) != null)
        .Select(PackageInfo.FindForAssetPath)
            .Where(x => x != null)
        .First(x => x.name == packageName);
}

Where its use case would be:

string packageName = "com.unity.cinemachine";
Debug.Log(GetPackageInfo(packageName).version);
1 Like