Files
velopack/test/Velopack.Tests/OldSquirrel/ReleaseEntry.cs
2024-02-02 12:26:31 +00:00

278 lines
10 KiB
C#

using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
namespace Velopack.Tests.OldSquirrel;
[DataContract]
public class ReleaseEntry
{
[DataMember] public string SHA1 { get; protected set; }
[DataMember] public string BaseUrl { get; protected set; }
[DataMember] public string Filename { get; protected set; }
[DataMember] public string Query { get; protected set; }
[DataMember] public long Filesize { get; protected set; }
[DataMember] public bool IsDelta { get; protected set; }
[DataMember] public float? StagingPercentage { get; protected set; }
protected ReleaseEntry(string sha1, string filename, long filesize, bool isDelta, 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;
}
[IgnoreDataMember]
public string EntryAsString {
get {
if (StagingPercentage != null) {
return String.Format("{0} {1}{2} {3} # {4}", SHA1, BaseUrl, Filename, Filesize, stagingPercentageAsString(StagingPercentage.Value));
} else {
return String.Format("{0} {1}{2} {3}", SHA1, BaseUrl, Filename, Filesize);
}
}
}
[IgnoreDataMember]
public SemanticVersion Version { get { return Filename.ToSemanticVersion(); } }
static readonly Regex packageNameRegex = new Regex(@"^([\w-]+)-\d+\..+\.nupkg$");
[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 GetReleaseNotes(string packageDirectory)
//{
// var zp = new ZipPackage(Path.Combine(packageDirectory, Filename));
// var t = zp.Id;
// if (String.IsNullOrWhiteSpace(zp.ReleaseNotes)) {
// throw new Exception(String.Format("Invalid 'ReleaseNotes' value in nuspec file at '{0}'", Path.Combine(packageDirectory, Filename)));
// }
// return zp.ReleaseNotes;
//}
//public Uri GetIconUrl(string packageDirectory)
//{
// var zp = new ZipPackage(Path.Combine(packageDirectory, Filename));
// return zp.IconUrl;
//}
static readonly Regex entryRegex = new Regex(@"^([0-9a-fA-F]{40})\s+(\S+)\s+(\d+)[\r]*$");
static readonly Regex commentRegex = new Regex(@"\s*#.*$");
static readonly Regex stagingRegex = new Regex(@"#\s+(\d{1,3})%$");
public static ReleaseEntry ParseReleaseEntry(string entry)
{
Contract.Requires(entry != null);
float? stagingPercentage = null;
var m = stagingRegex.Match(entry);
if (m != null && m.Success) {
stagingPercentage = Single.Parse(m.Groups[1].Value) / 100.0f;
}
entry = commentRegex.Replace(entry, "");
if (String.IsNullOrWhiteSpace(entry)) {
return null;
}
m = entryRegex.Match(entry);
if (!m.Success) {
throw new Exception("Invalid release entry: " + entry);
}
if (m.Groups.Count != 4) {
throw new Exception("Invalid release entry: " + entry);
}
string filename = m.Groups[2].Value;
// Split the base URL and the filename if an URI is provided,
// throws if a path is provided
string baseUrl = null;
string query = null;
if (Utility.IsHttpUrl(filename)) {
var uri = new Uri(filename);
var path = uri.LocalPath;
var authority = uri.GetLeftPart(UriPartial.Authority);
if (String.IsNullOrEmpty(path) || String.IsNullOrEmpty(authority)) {
throw new Exception("Invalid URL");
}
var indexOfLastPathSeparator = path.LastIndexOf("/") + 1;
baseUrl = authority + path.Substring(0, indexOfLastPathSeparator);
filename = path.Substring(indexOfLastPathSeparator);
if (!String.IsNullOrEmpty(uri.Query)) {
query = uri.Query;
}
}
if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) > -1) {
throw new Exception("Filename can either be an absolute HTTP[s] URL, *or* a file name");
}
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);
}
public bool IsStagingMatch(Guid? userId)
{
// A "Staging match" is when a user falls into the affirmative
// bucket - i.e. if the staging is at 10%, this user is the one out
// of ten case.
if (!StagingPercentage.HasValue) return true;
if (!userId.HasValue) return false;
uint val = BitConverter.ToUInt32(userId.Value.ToByteArray(), 12);
double percentage = ((double) val / (double) UInt32.MaxValue);
return percentage < StagingPercentage.Value;
}
public static IEnumerable<ReleaseEntry> ParseReleaseFile(string fileContents)
{
if (String.IsNullOrEmpty(fileContents)) {
return new ReleaseEntry[0];
}
//fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents);
var ret = fileContents.Split('\n')
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(ParseReleaseEntry)
.Where(x => x != null)
.ToArray();
return ret.Any(x => x == null) ? null : ret;
}
public static IEnumerable<ReleaseEntry> ParseReleaseFileAndApplyStaging(string fileContents, Guid? userToken)
{
if (String.IsNullOrEmpty(fileContents)) {
return new ReleaseEntry[0];
}
//fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents);
var ret = fileContents.Split('\n')
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(ParseReleaseEntry)
.Where(x => x != null && x.IsStagingMatch(userToken))
.ToArray();
return ret.Any(x => x == null) ? null : ret;
}
//public static void WriteReleaseFile(IEnumerable<ReleaseEntry> releaseEntries, Stream stream)
//{
// Contract.Requires(releaseEntries != null && releaseEntries.Any());
// Contract.Requires(stream != null);
// using (var sw = new StreamWriter(stream, Encoding.UTF8)) {
// sw.Write(String.Join("\n", releaseEntries
// .OrderBy(x => x.Version)
// .ThenByDescending(x => x.IsDelta)
// .Select(x => x.EntryAsString)));
// }
//}
//public static void WriteReleaseFile(IEnumerable<ReleaseEntry> releaseEntries, string path)
//{
// Contract.Requires(releaseEntries != null && releaseEntries.Any());
// Contract.Requires(!String.IsNullOrEmpty(path));
// using (var f = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None)) {
// WriteReleaseFile(releaseEntries, f);
// }
//}
//public static ReleaseEntry GenerateFromFile(Stream file, string filename, string baseUrl = null)
//{
// Contract.Requires(file != null && file.CanRead);
// Contract.Requires(!String.IsNullOrEmpty(filename));
// var hash = Utility.CalculateStreamSHA1(file);
// return new ReleaseEntry(hash, filename, file.Length, filenameIsDeltaFile(filename), baseUrl);
//}
//public static ReleaseEntry GenerateFromFile(string path, string baseUrl = null)
//{
// using (var inf = File.OpenRead(path)) {
// return GenerateFromFile(inf, Path.GetFileName(path), baseUrl);
// }
//}
//public static List<ReleaseEntry> BuildReleasesFile(string releasePackagesDir)
//{
// var packagesDir = new DirectoryInfo(releasePackagesDir);
// // Generate release entries for all of the local packages
// var entriesQueue = new ConcurrentQueue<ReleaseEntry>();
// Parallel.ForEach(packagesDir.GetFiles("*.nupkg"), x => {
// using (var file = x.OpenRead()) {
// entriesQueue.Enqueue(GenerateFromFile(file, x.Name));
// }
// });
// // Write the new RELEASES file to a temp file then move it into
// // place
// var entries = entriesQueue.ToList();
// var tempFile = default(string);
// Utility.WithTempFile(out tempFile, releasePackagesDir);
// try {
// using (var of = File.OpenWrite(tempFile)) {
// if (entries.Count > 0) WriteReleaseFile(entries, of);
// }
// var target = Path.Combine(packagesDir.FullName, "RELEASES");
// if (File.Exists(target)) {
// File.Delete(target);
// }
// File.Move(tempFile, target);
// } finally {
// if (File.Exists(tempFile)) Utility.DeleteFileHarder(tempFile, true);
// }
// return entries;
//}
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);
}
//public 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())
// .OrderByDescending(x => x.Version)
// .Select(x => new ReleasePackage(Path.Combine(targetDir, x.Filename), true))
// .FirstOrDefault();
//}
}