Skip to content

Commit

Permalink
Allow disabling installing dependencies on restore (#518)
Browse files Browse the repository at this point in the history
* Support nuget request timeout

* Support lock packages

* Fix typo

* Add default value for timeout

* Apply PR review + add tooltips

* Fix code

---------

Co-authored-by: JoC0de <[email protected]>
  • Loading branch information
tete1030 and JoC0de authored Jul 29, 2023
1 parent ce4df2e commit 7436d6d
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 63 deletions.
3 changes: 1 addition & 2 deletions src/NuGetForUnity.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ public static int Main(string[] args)
Application.SetUnityProjectPath(projectPath);

// need to disable dependency installation as UnityPreImportedLibraryResolver.GetAlreadyImportedLibs is not working outside Unity.
NugetHelper.InstallDependencies = false;
NugetHelper.Restore();
NugetHelper.Restore(false);
FixRoslynAnalyzerImportSettings();
return Debug.HasError ? 1 : 0;
}
Expand Down
21 changes: 19 additions & 2 deletions src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,24 @@ public void InstallStyleCopTest()
Assert.IsFalse(NugetHelper.IsInstalled(styleCopId), "The package is STILL installed: {0} {1}", styleCopId.Id, styleCopId.Version);
}

[Test]
public void InstallStyleCopWithoutDependenciesTest()
{
var styleCopPlusId = new NugetPackageIdentifier("StyleCopPlus.MSBuild", "4.7.49.5");
var styleCopId = new NugetPackageIdentifier("StyleCop.MSBuild", "4.7.49.0");

NugetHelper.InstallIdentifier(styleCopPlusId, installDependencies: false);

// StyleCopPlus depends on StyleCop, so they should both be installed
// it depends on version 4.7.49.0, so ensure it is also installed
Assert.IsTrue(NugetHelper.IsInstalled(styleCopPlusId), "The package was NOT installed: {0} {1}", styleCopPlusId.Id, styleCopPlusId.Version);
Assert.IsFalse(NugetHelper.IsInstalled(styleCopId), "The package SHOULD NOT be installed: {0} {1}", styleCopId.Id, styleCopId.Version);

// cleanup and uninstall everything
NugetHelper.UninstallAll(NugetHelper.InstalledPackages.ToList());
Assert.IsFalse(NugetHelper.IsInstalled(styleCopPlusId), "The package is STILL installed: {0} {1}", styleCopPlusId.Id, styleCopPlusId.Version);
}

[Test]
public void InstallSignalRClientTest()
{
Expand Down Expand Up @@ -604,8 +622,7 @@ public void TestPostprocessUninstall(string packageId, string packageVersion)

var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal);
filepath = filepath.Substring(assetsIndex);
NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[]{filepath},
null, null, null);
NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null);

Assert.IsFalse(NugetHelper.IsInstalled(package), "The package is STILL installed: {0} {1}", package.Id, package.Version);
}
Expand Down
62 changes: 53 additions & 9 deletions src/NuGetForUnity/Editor/NugetConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ public class NugetConfigFile
/// </summary>
public const string FileName = "NuGet.config";

/// <summary>
/// Default timeout in seconds for all web requests.
/// </summary>
private const int DefaultRequestTimeout = 10;

private const string RequestTimeoutSecondsConfigKey = "RequestTimeoutSeconds";

private const string LockPackagesOnRestoreConfigKey = "LockPackagesOnRestore";

/// <summary>
/// The incomplete path that is saved. The path is expanded and made public via the property above.
/// </summary>
Expand Down Expand Up @@ -47,7 +56,7 @@ public class NugetConfigFile
public string DefaultPushSource { get; private set; }

/// <summary>
/// True to output verbose log messages to the console. False to output the normal level of messages.
/// Gets or sets a value indicating whether to output verbose log messages to the console. False to output the normal level of messages.
/// </summary>
public bool Verbose { get; set; }

Expand All @@ -62,11 +71,22 @@ public class NugetConfigFile
/// </summary>
public bool ReadOnlyPackageFiles { get; set; }

/// <summary>
/// Gets or sets the timeout in seconds used for all web requests to NuGet sources.
/// </summary>
public int RequestTimeoutSeconds { get; set; } = DefaultRequestTimeout;

/// <summary>
/// Gets or sets a value indicating whether the installed packages should be fixed, so only the packages that are configure inside the
/// 'package.config' are installed without installing the dependencies of them.
/// </summary>
public bool LockPackagesOnRestore { get; set; }

/// <summary>
/// Saves this NuGet.config file to disk.
/// </summary>
/// <param name="filepath">The file-path to where this NuGet.config will be saved.</param>
public void Save(string filepath)
/// <param name="filePath">The file-path to where this NuGet.config will be saved.</param>
public void Save(string filePath)
{
var configFile = new XDocument();

Expand Down Expand Up @@ -157,6 +177,22 @@ public void Save(string filepath)
config.Add(addElement);
}

if (RequestTimeoutSeconds != DefaultRequestTimeout)
{
addElement = new XElement("add");
addElement.Add(new XAttribute("key", RequestTimeoutSecondsConfigKey));
addElement.Add(new XAttribute("value", RequestTimeoutSeconds));
config.Add(addElement);
}

if (LockPackagesOnRestore)
{
addElement = new XElement("add");
addElement.Add(new XAttribute("key", LockPackagesOnRestoreConfigKey));
addElement.Add(new XAttribute("value", LockPackagesOnRestore.ToString().ToLower()));
config.Add(addElement);
}

var configuration = new XElement("configuration");
configuration.Add(packageSources);
configuration.Add(disabledPackageSources);
Expand All @@ -166,21 +202,21 @@ public void Save(string filepath)

configFile.Add(configuration);

var fileExists = File.Exists(filepath);
var fileExists = File.Exists(filePath);

// remove the read only flag on the file, if there is one.
if (fileExists)
{
var attributes = File.GetAttributes(filepath);
var attributes = File.GetAttributes(filePath);

if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
attributes &= ~FileAttributes.ReadOnly;
File.SetAttributes(filepath, attributes);
File.SetAttributes(filePath, attributes);
}
}

configFile.Save(filepath);
configFile.Save(filePath);
}

/// <summary>
Expand Down Expand Up @@ -304,16 +340,24 @@ public static NugetConfigFile Load(string filePath)
{
configFile.ReadOnlyPackageFiles = bool.Parse(value);
}
else if (string.Equals(key, RequestTimeoutSecondsConfigKey, StringComparison.OrdinalIgnoreCase))
{
configFile.RequestTimeoutSeconds = int.Parse(value);
}
else if (string.Equals(key, LockPackagesOnRestoreConfigKey, StringComparison.OrdinalIgnoreCase))
{
configFile.LockPackagesOnRestore = bool.Parse(value);
}
}
}

return configFile;
}

/// <summary>
/// Creates a NuGet.config file with the default settings at the given full filepath.
/// Creates a NuGet.config file with the default settings at the given full file-path.
/// </summary>
/// <param name="filePath">The full filepath where to create the NuGet.config file.</param>
/// <param name="filePath">The full file-path where to create the NuGet.config file.</param>
/// <returns>The loaded <see cref="NugetConfigFile" /> loaded off of the newly created default file.</returns>
public static NugetConfigFile CreateDefaultFile(string filePath)
{
Expand Down
72 changes: 34 additions & 38 deletions src/NuGetForUnity/Editor/NugetHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,6 @@ static NugetHelper()
Directory.CreateDirectory(PackOutputDirectory);
}

/// <summary>
/// Gets or sets a value indicating whether when installing a NuGet package we also install its dependencies.
/// This is required by the NuGetForUnity.Cli as the CLI only installs packages listed explicitly inside
/// the <see cref="PackagesConfigFile" /> because dependency resolution wouldn't work seamlessly
/// as it can't detect libraries imported by Unity <see cref="UnityPreImportedLibraryResolver" />.
/// </summary>
internal static bool InstallDependencies { get; set; } = true;

/// <summary>
/// The loaded NuGet.config file that holds the settings for NuGet.
/// </summary>
Expand All @@ -131,14 +123,6 @@ public static PackagesConfigFile PackagesConfigFile
}
}

/// <summary>
/// Invalidates the currently loaded 'packages.config' so it is reloaded when it is accessed the next time.
/// </summary>
internal static void ReloadPackagesConfig()
{
packagesConfigFile = null;
}

/// <summary>
/// Gets the packages that are actually installed in the project.
/// </summary>
Expand All @@ -160,6 +144,14 @@ private static Dictionary<string, NugetPackage> InstalledPackagesDictionary
}
}

/// <summary>
/// Invalidates the currently loaded 'packages.config' so it is reloaded when it is accessed the next time.
/// </summary>
internal static void ReloadPackagesConfig()
{
packagesConfigFile = null;
}

/// <summary>
/// Loads the NuGet.config file.
/// </summary>
Expand Down Expand Up @@ -905,13 +897,13 @@ void AddPackageToInstalled(NugetPackage package)
{
// set root packages as manually installed if none are marked as such
foreach (var rootPackage in GetInstalledRootPackages())
{
PackagesConfigFile.SetManuallyInstalledFlag(rootPackage);
}

PackagesConfigFile.Save(PackagesConfigFilePath);
{
PackagesConfigFile.SetManuallyInstalledFlag(rootPackage);
}

PackagesConfigFile.Save(PackagesConfigFilePath);
}
}

stopwatch.Stop();
LogVerbose("Getting installed packages took {0} ms", stopwatch.ElapsedMilliseconds);
Expand Down Expand Up @@ -1163,7 +1155,11 @@ private static void CopyStream(Stream input, Stream output)
/// <param name="package">The identifier of the package to install.</param>
/// <param name="refreshAssets">True to refresh the Unity asset database. False to ignore the changes (temporarily).</param>
/// <param name="isUpdate">True to indicate we're calling method as result of Update and don't want to go through IsAlreadyImportedInEngine</param>
internal static bool InstallIdentifier(NugetPackageIdentifier package, bool refreshAssets = true, bool isUpdate = false)
/// <param name="installDependencies">True to also install all dependencies of the <paramref name="package" />.</param>
internal static bool InstallIdentifier(NugetPackageIdentifier package,
bool refreshAssets = true,
bool isUpdate = false,
bool installDependencies = true)
{
if (!isUpdate && IsAlreadyImportedInEngine(package, false))
{
Expand All @@ -1173,14 +1169,14 @@ internal static bool InstallIdentifier(NugetPackageIdentifier package, bool refr

var foundPackage = GetSpecificPackage(package);

if (foundPackage != null)
if (foundPackage == null)
{
foundPackage.IsManuallyInstalled = package.IsManuallyInstalled;
return Install(foundPackage, refreshAssets, isUpdate);
Debug.LogErrorFormat("Could not find {0} {1} or greater.", package.Id, package.Version);
return false;
}

Debug.LogErrorFormat("Could not find {0} {1} or greater.", package.Id, package.Version);
return false;
foundPackage.IsManuallyInstalled = package.IsManuallyInstalled;
return Install(foundPackage, refreshAssets, isUpdate, installDependencies);
}

/// <summary>
Expand All @@ -1204,8 +1200,9 @@ public static void LogVerbose(string format, params object[] args)
/// </summary>
/// <param name="package">The package to install.</param>
/// <param name="refreshAssets">True to refresh the Unity asset database. False to ignore the changes (temporarily).</param>
/// <param name="isUpdate">True to indicate we're calling method as result of Update and don't want to go through IsAlreadyImportedInEngine</param>
public static bool Install(NugetPackage package, bool refreshAssets = true, bool isUpdate = false)
/// <param name="isUpdate">True to indicate we're calling method as result of Update and don't want to go through IsAlreadyImportedInEngine.</param>
/// <param name="installDependencies">True to also install all dependencies of the <paramref name="package" />.</param>
public static bool Install(NugetPackage package, bool refreshAssets = true, bool isUpdate = false, bool installDependencies = true)
{
if (!isUpdate && IsAlreadyImportedInEngine(package, false))
{
Expand Down Expand Up @@ -1238,6 +1235,7 @@ public static bool Install(NugetPackage package, bool refreshAssets = true, bool
package.Version);
return Update(installedPackage, package, false);
}

LogVerbose(
"{0} {1} is installed. {2} or greater is needed, so using installed version.",
installedPackage.Id,
Expand Down Expand Up @@ -1266,7 +1264,7 @@ public static bool Install(NugetPackage package, bool refreshAssets = true, bool
0.1f);
}

if (InstallDependencies)
if (installDependencies)
{
// install all dependencies for target framework
var frameworkGroup = GetNullableBestDependencyFrameworkGroupForCurrentSettings(package);
Expand All @@ -1285,7 +1283,7 @@ public static bool Install(NugetPackage package, bool refreshAssets = true, bool
foreach (var dependency in frameworkGroup.Dependencies)
{
LogVerbose("Installing Dependency: {0} {1}", dependency.Id, dependency.Version);
var installed = InstallIdentifier(dependency);
var installed = InstallIdentifier(dependency, refreshAssets, installDependencies);
if (!installed)
{
throw new Exception(string.Format("Failed to install dependency: {0} {1}.", dependency.Id, dependency.Version));
Expand Down Expand Up @@ -1424,6 +1422,7 @@ private static void WarnIfDotNetAuthenticationIssue(Exception e)
/// Get the specified URL from the web. Throws exceptions if the request fails.
/// </summary>
/// <param name="url">URL that will be loaded.</param>
/// <param name="userName">UserName that will be passed in the Authorization header or the request. If null, authorization is omitted.</param>
/// <param name="password">Password that will be passed in the Authorization header or the request. If null, authorization is omitted.</param>
/// <param name="timeOut">Timeout in milliseconds or null to use the default timeout values of HttpWebRequest.</param>
/// <returns>Stream containing the result.</returns>
Expand All @@ -1437,11 +1436,7 @@ public static Stream RequestUrl(string url, string userName, string password, in
var getRequest = (HttpWebRequest)WebRequest.Create(url);
#pragma warning restore SYSLIB0014 // Type or member is obsolete
getRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.None;
if (timeOut.HasValue)
{
getRequest.Timeout = timeOut.Value;
getRequest.ReadWriteTimeout = timeOut.Value;
}
getRequest.Timeout = timeOut ?? NugetConfigFile.RequestTimeoutSeconds * 1000;

if (string.IsNullOrEmpty(password))
{
Expand Down Expand Up @@ -1471,7 +1466,8 @@ public static Stream RequestUrl(string url, string userName, string password, in
/// <summary>
/// Restores all packages defined in packages.config.
/// </summary>
public static void Restore()
/// <param name="installDependencies">True to also install all dependencies of the packages listed in the <see cref="PackagesConfigFile" />.</param>
public static void Restore(bool installDependencies = true)
{
UpdateInstalledPackages();

Expand All @@ -1498,7 +1494,7 @@ public static void Restore()
string.Format("Restoring {0} {1}", package.Id, package.Version),
currentProgress);
LogVerbose("---Restoring {0} {1}", package.Id, package.Version);
InstallIdentifier(package);
InstallIdentifier(package, installDependencies: installDependencies);
somethingChanged = true;
}

Expand Down
Loading

0 comments on commit 7436d6d

Please sign in to comment.