Merge branch 'master' into develop

This commit is contained in:
Caelan Sayler
2023-11-14 21:05:14 +00:00
16 changed files with 3817 additions and 130 deletions

View File

@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using NuGet.Versioning;
using SharpCompress.Archives.Zip;
using SharpCompress.Readers;
using Squirrel.MarkdownSharp;
using Squirrel.NuGet;
using Squirrel.SimpleSplat;
@@ -143,25 +142,25 @@ namespace Squirrel.CommandLine
static Task extractZipWithEscaping(string zipFilePath, string outFolder)
{
return Task.Run(() => {
using (var za = ZipArchive.Open(zipFilePath))
using (var reader = za.ExtractAllEntries()) {
while (reader.MoveToNextEntry()) {
var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
using (var fs = File.OpenRead(zipFilePath))
using (var za = new ZipArchive(fs))
foreach (var entry in za.Entries) {
var parts = entry.FullName.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts);
var fullTargetFile = Path.Combine(outFolder, decoded);
var fullTargetDir = Path.GetDirectoryName(fullTargetFile);
Directory.CreateDirectory(fullTargetDir);
var isDirectory = entry.IsDirectory();
Utility.Retry(() => {
if (reader.Entry.IsDirectory) {
Directory.CreateDirectory(Path.Combine(outFolder, decoded));
if (isDirectory) {
Directory.CreateDirectory(fullTargetFile);
} else {
reader.WriteEntryToFile(Path.Combine(outFolder, decoded));
entry.ExtractToFile(fullTargetFile, true);
}
}, 5);
}
}
});
}

View File

@@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
<PackageReference Include="NuGet.Protocol" Version="6.7.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;

View File

@@ -2,65 +2,60 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Writers;
using Squirrel.SimpleSplat;
namespace Squirrel
{
internal class EasyZip
internal static class EasyZip
{
private static IFullLogger Log = SquirrelLocator.CurrentMutable.GetService<ILogManager>().GetLogger(typeof(EasyZip));
public static void ExtractZipToDirectory(string inputFile, string outputDirectory)
{
Log.Info($"Extracting '{inputFile}' to '{outputDirectory}' using SharpCompress...");
using var archive = ZipArchive.Open(inputFile);
archive.WriteToDirectory(outputDirectory, new() {
PreserveFileTime = false,
Overwrite = true,
ExtractFullPath = true
});
Log.Info($"Extracting '{inputFile}' to '{outputDirectory}' using System.IO.Compression...");
Utility.DeleteFileOrDirectoryHard(outputDirectory);
ZipFile.ExtractToDirectory(inputFile, outputDirectory);
}
public static void CreateZipFromDirectory(string outputFile, string directoryToCompress, bool nestDirectory = false)
{
Log.Info($"Compressing '{directoryToCompress}' to '{outputFile}' using SharpCompress (DEFLATE)...");
using var archive = ZipArchive.Create();
archive.DeflateCompressionLevel = CompressionLevel.BestSpeed;
if (nestDirectory) {
AddAllFromDirectoryInNestedDir(archive, directoryToCompress);
throw new NotImplementedException();
//AddAllFromDirectoryInNestedDir(archive, directoryToCompress);
} else {
archive.AddAllFromDirectory(directoryToCompress);
Log.Info($"Compressing '{directoryToCompress}' to '{outputFile}' using System.IO.Compression...");
ZipFile.CreateFromDirectory(directoryToCompress, outputFile);
}
archive.SaveTo(outputFile, CompressionType.Deflate);
}
private static void AddAllFromDirectoryInNestedDir(
IWritableArchive writableArchive,
string filePath, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
//private static void AddAllFromDirectoryInNestedDir(
// IWritableArchive writableArchive,
// string filePath, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
//{
// var di = new DirectoryInfo(filePath);
// var parent = di.Parent;
// using (writableArchive.PauseEntryRebuilding())
// {
// foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
// {
// var fileInfo = new FileInfo(path);
// writableArchive.AddEntry(fileInfo.FullName.Substring(parent.FullName.Length), fileInfo.OpenRead(), true, fileInfo.Length,
// fileInfo.LastWriteTime);
// }
// }
//}
public static bool IsDirectory(this ZipArchiveEntry entry)
{
var di = new DirectoryInfo(filePath);
var parent = di.Parent;
using (writableArchive.PauseEntryRebuilding())
{
foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
{
var fileInfo = new FileInfo(path);
writableArchive.AddEntry(fileInfo.FullName.Substring(parent.FullName.Length), fileInfo.OpenRead(), true, fileInfo.Length,
fileInfo.LastWriteTime);
}
}
return entry.FullName.EndsWith("/") || entry.FullName.EndsWith("\\") || String.IsNullOrEmpty(entry.Name);
}
}
}

View File

@@ -47,11 +47,25 @@ namespace Squirrel
FileDate = fileDate;
}
public class VersionInfoItem
{
public uint CodePage { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public VersionInfoItem(uint codePage, string key, string value)
{
CodePage = codePage;
Key = key;
Value = value;
}
}
// vi can be null on exit
// Item1 = language | codepage
// Item2 = Key
// Item3 = Value
public static IEnumerable<(uint CodePage, string Key, string Value)> ReadVersionInfo(string fileName, out StringFileInfo vi)
public static IEnumerable<VersionInfoItem> ReadVersionInfo(string fileName, out StringFileInfo vi)
{
int num;
int size = GetFileVersionInfoSize(fileName, out num);
@@ -75,7 +89,7 @@ namespace Squirrel
// Item1 = language | codepage
// Item2 = Key
// Item3 = Value
public static IEnumerable<(uint CodePage, string Key, string Value)> ReadVersionInfo(byte[] buffer, out StringFileInfo vi)
public static IEnumerable<VersionInfoItem> ReadVersionInfo(byte[] buffer, out StringFileInfo vi)
{
int offset;
// The offset calculated here is unused
@@ -118,7 +132,7 @@ namespace Squirrel
return ReadVersionInfoInternal(buffer, fibs);
}
protected static IEnumerable<(uint CodePage, string Key, string Value)> ReadVersionInfoInternal(byte[] buffer, FileInfoBaseStruct fibs)
protected static IEnumerable<VersionInfoItem> ReadVersionInfoInternal(byte[] buffer, FileInfoBaseStruct fibs)
{
int sfiOrValOffset = (fibs.ValueOffset + fibs.ValueLength + 3) & (~3);
@@ -148,7 +162,7 @@ namespace Squirrel
int len = FindLengthUnicodeSZ(buffer, stri.ValueOffset, stri.ValueOffset + (stri.ValueLength * 2));
string value = Encoding.Unicode.GetString(buffer, stri.ValueOffset, len * 2);
yield return (langCharset, stri.Key, value);
yield return new VersionInfoItem(langCharset, stri.Key, value);
striOffset = nextStriOffset;
}

View File

@@ -1,8 +1,7 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Threading;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
// Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs

View File

@@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using SharpCompress.Archives.Zip;
using SharpCompress.Readers;
using Squirrel.SimpleSplat;
namespace Squirrel.NuGet
@@ -32,16 +31,16 @@ namespace Squirrel.NuGet
public ZipPackage(Stream zipStream, bool leaveOpen = false)
{
using var zip = ZipArchive.Open(zipStream, new() { LeaveStreamOpen = leaveOpen });
using var manifest = GetManifestEntry(zip).OpenEntryStream();
using var zip = new ZipArchive(zipStream, ZipArchiveMode.Read, leaveOpen);
using var manifest = GetManifestEntry(zip).Open();
ReadManifest(manifest);
Files = GetPackageFiles(zip).ToArray();
Frameworks = GetFrameworks(Files);
// we pre-load some images so the zip doesn't need to be opened again later
SetupSplashBytes = ReadFileToBytes(zip, z => Path.GetFileNameWithoutExtension(z.Key) == "splashimage");
SetupIconBytes = ReadFileToBytes(zip, z => z.Key == "setup.ico");
AppIconBytes = ReadFileToBytes(zip, z => z.Key == "app.ico") ?? ReadFileToBytes(zip, z => z.Key.EndsWith("app.ico"));
SetupSplashBytes = ReadFileToBytes(zip, z => Path.GetFileNameWithoutExtension(z.FullName) == "splashimage");
SetupIconBytes = ReadFileToBytes(zip, z => z.FullName == "setup.ico");
AppIconBytes = ReadFileToBytes(zip, z => z.FullName == "app.ico") ?? ReadFileToBytes(zip, z => z.FullName.EndsWith("app.ico"));
}
private byte[] ReadFileToBytes(ZipArchive archive, Func<ZipArchiveEntry, bool> predicate)
@@ -50,7 +49,7 @@ namespace Squirrel.NuGet
if (f == null)
return null;
using var stream = f.OpenEntryStream();
using var stream = f.Open();
if (stream == null)
return null;
@@ -63,7 +62,7 @@ namespace Squirrel.NuGet
private ZipArchiveEntry GetManifestEntry(ZipArchive zip)
{
var manifest = zip.Entries
.FirstOrDefault(f => f.Key.EndsWith(NugetUtil.ManifestExtension, StringComparison.OrdinalIgnoreCase));
.FirstOrDefault(f => f.FullName.EndsWith(NugetUtil.ManifestExtension, StringComparison.OrdinalIgnoreCase));
if (manifest == null)
throw new InvalidDataException("Invalid nupkg. Does not contain required '.nuspec' manifest.");
@@ -74,11 +73,11 @@ namespace Squirrel.NuGet
private IEnumerable<ZipPackageFile> GetPackageFiles(ZipArchive zip)
{
return from entry in zip.Entries
where !entry.IsDirectory
let uri = new Uri(entry.Key, UriKind.Relative)
let path = NugetUtil.GetPath(uri)
where IsPackageFile(path)
select new ZipPackageFile(uri);
where !entry.IsDirectory()
let uri = new Uri(entry.FullName, UriKind.Relative)
let path = NugetUtil.GetPath(uri)
where IsPackageFile(path)
select new ZipPackageFile(uri);
}
private string[] GetFrameworks(IEnumerable<ZipPackageFile> files)
@@ -110,21 +109,27 @@ namespace Squirrel.NuGet
{
if (!File.Exists(zipFilePath)) throw new ArgumentException("zipFilePath must exist");
progress ??= ((_) => { });
return Task.Run(() => {
using (Utility.GetTempDirectory(out var tmp))
using (var za = ZipArchive.Open(zipFilePath))
using (var reader = za.ExtractAllEntries()) {
using (var fs = File.OpenRead(zipFilePath))
using (var za = new ZipArchive(fs)) {
var totalItems = za.Entries.Count;
var currentItem = 0;
while (reader.MoveToNextEntry()) {
foreach (var entry in za.Entries) {
// Report progress early since we might be need to continue for non-matches
currentItem++;
var percentage = (currentItem * 100d) / totalItems;
progress((int) percentage);
var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
// extract .nuspec to app directory as '.version'
if (Utility.FileHasExtension(entry.FullName, NugetUtil.ManifestExtension)) {
Utility.Retry(() => entry.ExtractToFile(Path.Combine(tmp, Utility.SpecVersionFileName), true));
continue;
}
var parts = entry.FullName.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts);
if (!libFolderPattern.IsMatch(decoded)) continue;
@@ -135,18 +140,18 @@ namespace Squirrel.NuGet
Directory.CreateDirectory(fullTargetDir);
Utility.Retry(() => {
if (reader.Entry.IsDirectory) {
if (entry.IsDirectory()) {
Directory.CreateDirectory(fullTargetFile);
} else {
reader.WriteEntryToFile(fullTargetFile);
entry.ExtractToFile(fullTargetFile, true);
}
});
if (!reader.Entry.IsDirectory && PlatformUtil.IsMachOImage(fullTargetFile)) {
if (!entry.IsDirectory() && PlatformUtil.IsMachOImage(fullTargetFile)) {
PlatformUtil.ChmodFileAsExecutable(fullTargetFile);
}
}
Utility.DeleteFileOrDirectoryHard(outFinalFolder, renameFirst: true);
Directory.Move(tmp, outFinalFolder);
}
@@ -163,24 +168,24 @@ namespace Squirrel.NuGet
return Task.Run(() => {
using (Utility.GetTempDirectory(out var tmp))
using (var za = ZipArchive.Open(zipFilePath))
using (var reader = za.ExtractAllEntries()) {
using (var fs = File.OpenRead(zipFilePath))
using (var za = new ZipArchive(fs)) {
var totalItems = za.Entries.Count;
var currentItem = 0;
while (reader.MoveToNextEntry()) {
foreach (var entry in za.Entries) {
// Report progress early since we might be need to continue for non-matches
currentItem++;
var percentage = (currentItem * 100d) / totalItems;
progress((int) percentage);
// extract .nuspec to app directory as '.version'
if (Utility.FileHasExtension(reader.Entry.Key, NugetUtil.ManifestExtension)) {
Utility.Retry(() => reader.WriteEntryToFile(Path.Combine(tmp, Utility.SpecVersionFileName)));
if (Utility.FileHasExtension(entry.FullName, NugetUtil.ManifestExtension)) {
Utility.Retry(() => entry.ExtractToFile(Path.Combine(tmp, Utility.SpecVersionFileName), true));
continue;
}
var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
var parts = entry.FullName.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts);
if (!libFolderPattern.IsMatch(decoded)) continue;
@@ -191,7 +196,7 @@ namespace Squirrel.NuGet
Directory.CreateDirectory(fullTargetDir);
var failureIsOkay = false;
if (!reader.Entry.IsDirectory && decoded.Contains("_ExecutionStub.exe")) {
if (!entry.IsDirectory() && decoded.Contains("_ExecutionStub.exe")) {
// NB: On upgrade, many of these stubs will be in-use, nbd tho.
failureIsOkay = true;
@@ -209,10 +214,10 @@ namespace Squirrel.NuGet
try {
Utility.Retry(() => {
if (reader.Entry.IsDirectory) {
if (entry.IsDirectory()) {
Directory.CreateDirectory(fullTargetFile);
} else {
reader.WriteEntryToFile(fullTargetFile);
entry.ExtractToFile(fullTargetFile, true);
}
});
} catch (Exception e) {
@@ -220,7 +225,7 @@ namespace Squirrel.NuGet
LogHost.Default.WarnException("Can't write execution stub, probably in use", e);
}
}
Utility.DeleteFileOrDirectoryHard(outFinalFolder, renameFirst: true);
Directory.Move(tmp, outFinalFolder);
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives.Zip;
namespace Squirrel.NuGet
{
@@ -42,13 +41,6 @@ namespace Squirrel.NuGet
EffectivePath = effectivePath;
}
//public Stream GetEntryStream(Stream archiveStream)
//{
// using var zip = ZipArchive.Open(archiveStream, new() { LeaveStreamOpen = true });
// var entry = zip.Entries.FirstOrDefault(f => new Uri(f.Key, UriKind.Relative) == _entryKey);
// return entry?.OpenEntryStream();
//}
public bool IsLibFile() => IsFileInTopDirectory(NugetUtil.LibDirectory);
public bool IsContentFile() => IsFileInTopDirectory(NugetUtil.ContentDirectory);

View File

@@ -386,15 +386,35 @@ namespace Squirrel
static readonly Regex _versionStartRegex = new Regex(@"[\.-](0|[1-9]\d*)\.(0|[1-9]\d*)($|[^\d])", RegexOptions.Compiled);
static readonly Regex _ridRegex = new Regex(@"-(?<os>osx|win)\.?(?<ver>[\d\.]+)?(?:-(?<arch>(?:x|arm)\d{2}))?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
internal class EntryNameInfo
{
public string PackageName { get; set; }
public SemanticVersion Version { get; set; }
public bool IsDelta { get; set; }
public RID Rid { get; set; }
public EntryNameInfo()
{
}
public EntryNameInfo(string packageName, SemanticVersion version, bool isDelta, RID rid)
{
PackageName = packageName;
Version = version;
IsDelta = isDelta;
Rid = rid;
}
}
/// <summary>
/// Takes a filename such as 'My-Cool3-App-1.0.1-build.23-full.nupkg' and separates it into
/// it's name and version (eg. 'My-Cool3-App', and '1.0.1-build.23'). Returns null values if
/// the filename can not be parsed.
/// </summary>
internal static (string PackageName, SemanticVersion Version, bool IsDelta, RID Rid) ParseEntryFileName(string fileName)
internal static EntryNameInfo ParseEntryFileName(string fileName)
{
if (!fileName.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
return (null, null, false, null);
return new EntryNameInfo(null, null, false, null);
bool delta = Path.GetFileNameWithoutExtension(fileName).EndsWith("-delta", StringComparison.OrdinalIgnoreCase);
@@ -402,7 +422,7 @@ namespace Squirrel
var match = _versionStartRegex.Match(nameAndVer);
if (!match.Success)
return (null, null, delta, null);
return new EntryNameInfo(null, null, delta, null);
var verIdx = match.Index;
var name = nameAndVer.Substring(0, verIdx);
@@ -417,7 +437,7 @@ namespace Squirrel
}
var semVer = NuGetVersion.Parse(version);
return (name, semVer, delta, rid);
return new EntryNameInfo(name, semVer, delta, rid);
}
}
}

View File

@@ -481,14 +481,26 @@ namespace Squirrel
const string UninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall";
private class VCVersion
{
public SemanticVersion Ver { get; set; }
public RuntimeCpu Cpu { get; set; }
public VCVersion(SemanticVersion ver, RuntimeCpu cpu)
{
Ver = ver;
Cpu = cpu;
}
}
/// <summary>
/// Returns the list of currently installed VC++ redistributables, as reported by the
/// Windows Programs &amp; Features dialog.
/// </summary>
[SupportedOSPlatform("windows")]
public static (NuGetVersion Ver, RuntimeCpu Cpu)[] GetInstalledVCVersions()
private static VCVersion[] GetInstalledVCVersions()
{
List<(NuGetVersion Ver, RuntimeCpu Cpu)> results = new List<(NuGetVersion Ver, RuntimeCpu Cpu)>();
List<VCVersion> results = new();
void searchreg(RegistryKey view)
{
@@ -499,11 +511,11 @@ namespace Squirrel
var version = subKey.GetValue("DisplayVersion") as string;
if (NuGetVersion.TryParse(version, out var v)) {
if (name.IndexOf("arm64", StringComparison.InvariantCultureIgnoreCase) >= 0) {
results.Add((v, RuntimeCpu.arm64));
results.Add(new VCVersion(v, RuntimeCpu.arm64));
} else if (name.IndexOf("x64", StringComparison.InvariantCultureIgnoreCase) >= 0) {
results.Add((v, RuntimeCpu.x64));
results.Add(new VCVersion(v, RuntimeCpu.x64));
} else {
results.Add((v, RuntimeCpu.x86));
results.Add(new VCVersion(v, RuntimeCpu.x86));
}
}
}

View File

@@ -24,21 +24,12 @@
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
</ItemGroup>
<Choose>
<When Condition=" $(TargetFramework.StartsWith('net4')) ">
<ItemGroup>
<PackageReference Include="SharpCompress" Version="0.33.0" />
<PackageReference Include="System.ValueTuple" Version="4.5" />
<Reference Include="System.Web" />
<Reference Include="System.Net.Http" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="SharpCompress" Version="0.34.1" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) ">
<Reference Include="System.Web" />
<Reference Include="System.Net.Http" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\docs\artwork\Clowd_200.png" Pack="true" PackagePath="\" />

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Squirrel.SimpleSplat;
using System.Threading;
using System.Runtime.Versioning;
using SharpCompress.Compressors.Deflate;
using Squirrel.NuGet;
namespace Squirrel

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;

View File

@@ -8,10 +8,8 @@ using Squirrel;
using Squirrel.SimpleSplat;
using Xunit;
using System.Text;
using SharpCompress.Archives.Zip;
using SharpCompress.Readers;
using SharpCompress.Common;
using Squirrel.CommandLine;
using System.IO.Compression;
namespace Squirrel.Tests.TestHelpers
{
@@ -128,12 +126,7 @@ namespace Squirrel.Tests.TestHelpers
var zipPath = GetPath("fixtures", zipFile);
Assert.True(File.Exists(zipPath));
var opts = new ExtractionOptions() { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true };
using (var za = ZipArchive.Open(zipFile))
using (var reader = za.ExtractAllEntries()) {
reader.WriteEntryToDirectory(path, opts);
}
ZipFile.ExtractToDirectory(zipPath, path);
return ret;
}
}