Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Fix package parsing for id's with dots and dashes (see #25)
Browse files Browse the repository at this point in the history
  • Loading branch information
caesay committed Jan 10, 2022
1 parent cf740c5 commit 689b7ec
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 63 deletions.
22 changes: 0 additions & 22 deletions src/Squirrel/Internal/ReleaseExtensions.cs

This file was deleted.

25 changes: 17 additions & 8 deletions src/Squirrel/Internal/ReleasePackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal interface IReleasePackage
string InputPackageFile { get; }
string ReleasePackageFile { get; }
string SuggestedReleaseFileName { get; }
SemanticVersion Version { get; }
}

internal class ReleasePackage : IEnableLogger, IReleasePackage
Expand All @@ -42,7 +43,7 @@ public string SuggestedReleaseFileName {
}
}

public SemanticVersion Version { get { return InputPackageFile.ToSemanticVersion(); } }
public SemanticVersion Version => ReleaseEntry.ParseEntryFileName(InputPackageFile).Version;

#if NET5_0_OR_GREATER
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
Expand All @@ -58,15 +59,23 @@ internal string CreateReleasePackage(string outputFile, Func<string, string> rel

var package = new ZipPackage(InputPackageFile);

var dontcare = default(SemanticVersion);

// NB: Our test fixtures use packages that aren't SemVer compliant,
// we don't really care that they aren't valid
if (!ModeDetector.InUnitTestRunner() && !SemanticVersion.TryParseStrict(package.Version.ToString(), out dontcare)) {
throw new Exception(
String.Format(
"Your package version is currently {0}, which is *not* SemVer-compatible, change this to be a SemVer version number",
package.Version.ToString()));
if (!ModeDetector.InUnitTestRunner()) {
// verify that the .nuspec version is semver compliant
if (!SemanticVersion.TryParseStrict(package.Version.ToString(), out var verdontcare)) {
throw new Exception(
String.Format(
"Your package version is currently {0}, which is *not* SemVer-compatible, change this to be a SemVer version number",
package.Version.ToString()));
}

// verify that the suggested filename can be round-tripped as an assurance
// someone won't run across an edge case and install a broken app somehow
var idtest = ReleaseEntry.ParseEntryFileName(SuggestedReleaseFileName);
if (idtest.PackageName != package.Id || idtest.Version != package.Version) {
throw new Exception($"The package id/version could not be properly parsed.");
}
}

// we can tell from here what platform(s) the package targets
Expand Down
64 changes: 41 additions & 23 deletions src/Squirrel/ReleaseEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,19 @@ public class ReleaseEntry : IEnableLogger, IReleaseEntry
/// <summary>
/// Create a new instance of <see cref="ReleaseEntry"/>.
/// </summary>
protected ReleaseEntry(string sha1, string filename, long filesize, bool isDelta, string baseUrl = null, string query = null, float? stagingPercentage = null)
protected ReleaseEntry(string sha1, string filename, long filesize, string baseUrl = null, string query = null, float? stagingPercentage = null)
{
Contract.Requires(sha1 != null && sha1.Length == 40);
Contract.Requires(filename != null);
Contract.Requires(filename.Contains(Path.DirectorySeparatorChar) == false);
Contract.Requires(filesize > 0);

SHA1 = sha1; BaseUrl = baseUrl; Filename = filename; Query = query; Filesize = filesize; IsDelta = isDelta; StagingPercentage = stagingPercentage;
SHA1 = sha1; BaseUrl = baseUrl; Filename = filename; Query = query; Filesize = filesize; StagingPercentage = stagingPercentage;

var identity = ParseEntryFileName(Filename);
Version = identity.Version;
PackageName = identity.PackageName;
IsDelta = identity.IsDelta;
}

/// <inheritdoc />
Expand All @@ -105,20 +110,11 @@ public string EntryAsString {

/// <inheritdoc />
[IgnoreDataMember]
public SemanticVersion Version { get { return Filename.ToSemanticVersion(); } }

static readonly Regex packageNameRegex = new Regex(@"^([\w-]+)-\d+\..+\.nupkg$");
public SemanticVersion Version { get; }

/// <inheritdoc />
[IgnoreDataMember]
public string PackageName {
get {
var match = packageNameRegex.Match(Filename);
return match.Success ?
match.Groups[1].Value :
Filename.Substring(0, Filename.IndexOfAny(new[] { '-', '.' }));
}
}
public string PackageName { get; }

/// <inheritdoc />
public string GetReleaseNotes(string packageDirectory)
Expand Down Expand Up @@ -200,9 +196,7 @@ public static ReleaseEntry ParseReleaseEntry(string entry)
}

long size = Int64.Parse(m.Groups[3].Value);
bool isDelta = filenameIsDeltaFile(filename);

return new ReleaseEntry(m.Groups[1].Value, filename, size, isDelta, baseUrl, query, stagingPercentage);
return new ReleaseEntry(m.Groups[1].Value, filename, size, baseUrl, query, stagingPercentage);
}

/// <summary>
Expand Down Expand Up @@ -301,7 +295,7 @@ public static ReleaseEntry GenerateFromFile(Stream file, string filename, string
Contract.Requires(!String.IsNullOrEmpty(filename));

var hash = Utility.CalculateStreamSHA1(file);
return new ReleaseEntry(hash, filename, file.Length, filenameIsDeltaFile(filename), baseUrl);
return new ReleaseEntry(hash, filename, file.Length, baseUrl);
}

/// <summary>
Expand Down Expand Up @@ -362,10 +356,6 @@ static string stagingPercentageAsString(float percentage)
return String.Format("{0:F0}%", percentage * 100.0);
}

static bool filenameIsDeltaFile(string filename)
{
return filename.EndsWith("-delta.nupkg", StringComparison.InvariantCultureIgnoreCase);
}

/// <summary>
/// Given a list of releases and a specified release package, returns the release package
Expand All @@ -374,13 +364,41 @@ static bool filenameIsDeltaFile(string filename)
internal static ReleasePackage GetPreviousRelease(IEnumerable<ReleaseEntry> releaseEntries, IReleasePackage package, string targetDir)
{
if (releaseEntries == null || !releaseEntries.Any()) return null;

return releaseEntries
.Where(x => x.IsDelta == false)
.Where(x => x.Version < package.ToSemanticVersion())
.Where(x => x.Version < package.Version)
.OrderByDescending(x => x.Version)
.Select(x => new ReleasePackage(Path.Combine(targetDir, x.Filename), true))
.FirstOrDefault();
}

static readonly Regex _suffixRegex = new Regex(@"(-full|-delta)?\.nupkg$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
static readonly Regex _versionStartRegex = new Regex(@"(^|[^\d])(0|[1-9]\d*)\.(0|[1-9]\d*)($|[^\d])", RegexOptions.Compiled);

/// <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) ParseEntryFileName(string fileName)
{
if (!fileName.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
return (null, null, false);

bool delta = Path.GetFileNameWithoutExtension(fileName).EndsWith("-delta", StringComparison.OrdinalIgnoreCase);

var nameAndVer = _suffixRegex.Replace(fileName, "");

var match = _versionStartRegex.Match(nameAndVer);
if (!match.Success)
return (null, null, delta);

var verIdx = match.Index;
var name = nameAndVer.Substring(0, verIdx);
var version = nameAndVer.Substring(verIdx + 1);

var semVer = new SemanticVersion(version);
return (name, semVer, delta);
}
}
}
4 changes: 2 additions & 2 deletions src/Squirrel/SquirrelAwareApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static void HandleEvents(
if (args.Length != 2) return;

if (!lookup.ContainsKey(args[0])) return;
var version = args[1].ToSemanticVersion().Version;
var version = new SemanticVersion(args[1]).Version;

try {
lookup[args[0]](version);
Expand Down Expand Up @@ -115,7 +115,7 @@ public static void HandleEvents(
if (args.Length != 2) return;

if (!lookup.ContainsKey(args[0])) return;
var version = args[1].ToSemanticVersion();
var version = new SemanticVersion(args[1]);

try {
// CS: call dispose immediately means this instance of UpdateManager
Expand Down
13 changes: 6 additions & 7 deletions src/Squirrel/UpdateManager.ApplyReleases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,10 @@ public async Task FullUninstall()
if (!releases.Any())
return;

var currentRelease = releases.MaxBy(x => x.Name.ToSemanticVersion()).FirstOrDefault();
var (currentRelease, currentVersion) = releases.OrderByDescending(x => x.Version).FirstOrDefault();

this.Log().Info("Starting full uninstall");
if (currentRelease.Exists) {
var version = currentRelease.Name.ToSemanticVersion();

try {
var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName);

Expand All @@ -120,7 +118,7 @@ await squirrelAwareApps.ForEachAsync(async exe => {
cts.CancelAfter(10 * 1000);
try {
await Utility.InvokeProcessAsync(exe, new string[] { "--squirrel-uninstall", version.ToString() }, cts.Token).ConfigureAwait(false);
await Utility.InvokeProcessAsync(exe, new string[] { "--squirrel-uninstall", currentVersion.ToString() }, cts.Token).ConfigureAwait(false);
} catch (Exception ex) {
this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex);
}
Expand Down Expand Up @@ -674,14 +672,15 @@ internal async Task<List<ReleaseEntry>> updateLocalReleasesFile()
return await Task.Run(() => ReleaseEntry.BuildReleasesFile(Utility.PackageDirectoryForAppDir(rootAppDirectory))).ConfigureAwait(false);
}

IEnumerable<DirectoryInfo> getReleases()
IEnumerable<(DirectoryInfo Directory, SemanticVersion Version)> getReleases()
{
var rootDirectory = new DirectoryInfo(rootAppDirectory);

if (!rootDirectory.Exists) return Enumerable.Empty<DirectoryInfo>();
if (!rootDirectory.Exists) return Enumerable.Empty<(DirectoryInfo Directory, SemanticVersion Version)>();

return rootDirectory.GetDirectories()
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase));
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase))
.Select(x => (x, new SemanticVersion(x.Name.Substring(4))));
}

DirectoryInfo getDirectoryForRelease(SemanticVersion releaseVersion)
Expand Down
2 changes: 1 addition & 1 deletion src/Squirrel/UpdateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public SemanticVersion CurrentlyInstalledVersion(string executable = null)
.FirstOrDefault(x => x.StartsWith("app-", StringComparison.OrdinalIgnoreCase));

if (appDirName == null) return null;
return appDirName.ToSemanticVersion();
return new SemanticVersion(appDirName.Substring(4));
}

/// <inheritdoc/>
Expand Down

0 comments on commit 689b7ec

Please sign in to comment.