Fix NuGet fallback folder packages
- Creates a `Godot.Offline.Config` file to configurate NuGet with Godot's fallback folder. This is easier because now we can assume we can override the entire file since user config will likely be in the default `NuGet.Config` file or an additional `*.config` file. - Ensure the NuGet fallback folder is created at the same time it is added to the NuGet configuration so future builds don't fail. - Add `GodotSharp` and `GodotSharpEditor` packages to the fallback folder. - Add `.nupkg.metadata` file to packages in fallback folder. - Refer to `Godot.SourceGenerators` using the specific non-floating version since floating versions don't seem to work with fallbackPackageFolders.
This commit is contained in:
parent
20d6672846
commit
02bd0724f5
4 changed files with 46 additions and 92 deletions
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<!-- C# source generators -->
|
<!-- C# source generators -->
|
||||||
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
|
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
|
||||||
<PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
|
<PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Godot API references -->
|
<!-- Godot API references -->
|
||||||
|
|
|
@ -21,10 +21,13 @@
|
||||||
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
|
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateGodotNupkgsVersionsCode><![CDATA[
|
<GenerateGodotNupkgsVersionsCode><![CDATA[
|
||||||
namespace $(RootNamespace) {
|
namespace $(RootNamespace)
|
||||||
public class GeneratedGodotNupkgsVersions {
|
{
|
||||||
|
public class GeneratedGodotNupkgsVersions
|
||||||
|
{
|
||||||
public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
|
public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
|
||||||
public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
|
public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
|
||||||
|
public const string GodotSharp = "$(PackageVersion_GodotSharp)"%3b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]]></GenerateGodotNupkgsVersionsCode>
|
]]></GenerateGodotNupkgsVersionsCode>
|
||||||
|
|
|
@ -22,71 +22,13 @@ namespace GodotTools.Build
|
||||||
public static string GodotFallbackFolderPath
|
public static string GodotFallbackFolderPath
|
||||||
=> Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
|
=> Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
|
||||||
|
|
||||||
private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
|
|
||||||
{
|
|
||||||
var xmlDoc = new XmlDocument();
|
|
||||||
xmlDoc.Load(nuGetConfigPath);
|
|
||||||
|
|
||||||
const string nuGetConfigRootName = "configuration";
|
|
||||||
|
|
||||||
var rootNode = xmlDoc.DocumentElement;
|
|
||||||
|
|
||||||
if (rootNode == null)
|
|
||||||
{
|
|
||||||
// No root node, create it
|
|
||||||
rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
|
|
||||||
xmlDoc.AppendChild(rootNode);
|
|
||||||
|
|
||||||
// Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
|
|
||||||
XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
|
|
||||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
|
|
||||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value =
|
|
||||||
"https://api.nuget.org/v3/index.json";
|
|
||||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
|
|
||||||
rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check that the root node is the expected one
|
|
||||||
if (rootNode.Name != nuGetConfigRootName)
|
|
||||||
throw new FormatException("Invalid root Xml node for NuGet.Config. " +
|
|
||||||
$"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
|
|
||||||
rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
|
|
||||||
|
|
||||||
// Check if it already has our fallback package folder
|
|
||||||
for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
|
|
||||||
{
|
|
||||||
if (xmlNode.NodeType != XmlNodeType.Element)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var xmlElement = (XmlElement)xmlNode;
|
|
||||||
if (xmlElement.Name == "add" &&
|
|
||||||
xmlElement.Attributes["key"]?.Value == name &&
|
|
||||||
xmlElement.Attributes["value"]?.Value == path)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlElement newEntry = xmlDoc.CreateElement("add");
|
|
||||||
newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
|
|
||||||
newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
|
|
||||||
|
|
||||||
fallbackFoldersNode.AppendChild(newEntry);
|
|
||||||
|
|
||||||
xmlDoc.Save(nuGetConfigPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all the paths where the user NuGet.Config files can be found.
|
/// Returns all the paths where the Godot.Offline.Config files can be found.
|
||||||
/// Does not determine whether the returned files exist or not.
|
/// Does not determine whether the returned files exist or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static string[] GetAllUserNuGetConfigFilePaths()
|
private static string[] GetAllGodotNuGetConfigFilePaths()
|
||||||
{
|
{
|
||||||
// Where to find 'NuGet/NuGet.Config':
|
// Where to find 'NuGet/config/Godot.Offline.Config':
|
||||||
//
|
//
|
||||||
// - Mono/.NETFramework (standalone NuGet):
|
// - Mono/.NETFramework (standalone NuGet):
|
||||||
// Uses Environment.SpecialFolder.ApplicationData
|
// Uses Environment.SpecialFolder.ApplicationData
|
||||||
|
@ -98,10 +40,12 @@ namespace GodotTools.Build
|
||||||
|
|
||||||
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
|
|
||||||
|
const string configFileName = "Godot.Offline.Config";
|
||||||
|
|
||||||
if (Utils.OS.IsWindows)
|
if (Utils.OS.IsWindows)
|
||||||
{
|
{
|
||||||
// %APPDATA% for both
|
// %APPDATA% for both
|
||||||
return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
|
return new[] { Path.Combine(applicationData, "NuGet", "config", configFileName) };
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = new string[2];
|
var paths = new string[2];
|
||||||
|
@ -111,20 +55,20 @@ namespace GodotTools.Build
|
||||||
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
|
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
|
||||||
if (!string.IsNullOrEmpty(dotnetCliHome))
|
if (!string.IsNullOrEmpty(dotnetCliHome))
|
||||||
{
|
{
|
||||||
paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
|
paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "config", configFileName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string home = Environment.GetEnvironmentVariable("HOME");
|
string home = Environment.GetEnvironmentVariable("HOME");
|
||||||
if (string.IsNullOrEmpty(home))
|
if (string.IsNullOrEmpty(home))
|
||||||
throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
|
throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
|
||||||
paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
|
paths[0] = Path.Combine(home, ".nuget", "NuGet", "config", configFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mono/.NETFramework (standalone NuGet)
|
// Mono/.NETFramework (standalone NuGet)
|
||||||
|
|
||||||
// ApplicationData is $HOME/.config on Linux/macOS
|
// ApplicationData is $HOME/.config on Linux/macOS
|
||||||
paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
|
paths[1] = Path.Combine(applicationData, "NuGet", "config", configFileName);
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
@ -141,28 +85,26 @@ namespace GodotTools.Build
|
||||||
// The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
|
// The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the specified fallback folder to the user NuGet.Config files,
|
/// Adds the specified fallback folder to the Godot.Offline.Config files,
|
||||||
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
|
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
|
public static void AddFallbackFolderToGodotNuGetConfigs(string name, string path)
|
||||||
{
|
{
|
||||||
foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
|
// Make sure the fallback folder exists to avoid error:
|
||||||
|
// MSB4018: The "ResolvePackageAssets" task failed unexpectedly.
|
||||||
|
System.IO.Directory.CreateDirectory(path);
|
||||||
|
|
||||||
|
foreach (string nuGetConfigPath in GetAllGodotNuGetConfigFilePaths())
|
||||||
{
|
{
|
||||||
if (!System.IO.File.Exists(nuGetConfigPath))
|
string defaultConfig = @$"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||||
{
|
|
||||||
// It doesn't exist, so we create a default one
|
|
||||||
const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<fallbackPackageFolders>
|
||||||
<add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
|
<add key=""{name}"" value=""{path}"" />
|
||||||
</packageSources>
|
</fallbackPackageFolders>
|
||||||
</configuration>
|
</configuration>
|
||||||
";
|
";
|
||||||
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
|
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
|
||||||
System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
|
System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
|
||||||
}
|
|
||||||
|
|
||||||
AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +131,7 @@ namespace GodotTools.Build
|
||||||
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
|
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
|
||||||
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
|
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
|
||||||
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
|
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
|
||||||
|
string nupkgMetadataDestPath = Path.Combine(destDir, ".nupkg.metadata");
|
||||||
|
|
||||||
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
|
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
|
||||||
return; // Already added (for speed we don't check if every file is properly extracted)
|
return; // Already added (for speed we don't check if every file is properly extracted)
|
||||||
|
@ -197,12 +140,18 @@ namespace GodotTools.Build
|
||||||
|
|
||||||
// Generate .nupkg.sha512 file
|
// Generate .nupkg.sha512 file
|
||||||
|
|
||||||
using (var alg = SHA512.Create())
|
byte[] hash = SHA512.HashData(File.ReadAllBytes(nupkgPath));
|
||||||
{
|
string base64Hash = Convert.ToBase64String(hash);
|
||||||
alg.ComputeHash(File.ReadAllBytes(nupkgPath));
|
File.WriteAllText(nupkgSha512DestPath, base64Hash);
|
||||||
string base64Hash = Convert.ToBase64String(alg.Hash);
|
|
||||||
File.WriteAllText(nupkgSha512DestPath, base64Hash);
|
// Generate .nupkg.metadata file
|
||||||
}
|
// Spec: https://github.com/NuGet/Home/wiki/Nupkg-Metadata-File
|
||||||
|
|
||||||
|
File.WriteAllText(nupkgMetadataDestPath, @$"{{
|
||||||
|
""version"": 2,
|
||||||
|
""contentHash"": ""{base64Hash}"",
|
||||||
|
""source"": null
|
||||||
|
}}");
|
||||||
|
|
||||||
// Extract nupkg
|
// Extract nupkg
|
||||||
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
|
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
|
||||||
|
@ -251,7 +200,7 @@ namespace GodotTools.Build
|
||||||
entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
|
entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
|
||||||
entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
|
entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
|
||||||
// Nuspec at root level. We already extracted it previously but in lower case.
|
// Nuspec at root level. We already extracted it previously but in lower case.
|
||||||
entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
|
!entryFullName.Contains('/') && entryFullName.EndsWith(".nuspec"))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -297,6 +246,8 @@ namespace GodotTools.Build
|
||||||
{
|
{
|
||||||
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
|
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
|
||||||
("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
|
("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
|
||||||
|
("GodotSharp", GeneratedGodotNupkgsVersions.GodotSharp),
|
||||||
|
("GodotSharpEditor", GeneratedGodotNupkgsVersions.GodotSharp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace GodotTools
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
|
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
|
||||||
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
|
NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
|
||||||
fallbackFolder);
|
fallbackFolder);
|
||||||
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
|
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
|
||||||
}
|
}
|
||||||
|
@ -497,7 +497,7 @@ namespace GodotTools
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
|
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
|
||||||
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
|
NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
|
||||||
NuGetUtils.GodotFallbackFolderPath);
|
NuGetUtils.GodotFallbackFolderPath);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
Loading…
Reference in a new issue