Skip to content

Commit

Permalink
Address latest feedback.
Browse files Browse the repository at this point in the history
  • Loading branch information
peters committed Jun 16, 2020
1 parent fe86d60 commit e3cb47a
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 127 deletions.
103 changes: 40 additions & 63 deletions src/GprTool/NuGetUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,15 @@ namespace GprTool
{
public class PackageFile
{
public string Filename { get; set; }
public string Owner { get; set; }
public string RepositoryName { get; set; }
public string RepositoryUrl { get; set; }
public bool ShouldRewriteNuspec { get; set; }
public bool IsNuspecRewritten { get; set; }

public string FilenameWithoutGprPrefixAndPath
{
get
{
var gprIndex = !IsNuspecRewritten ? -1 : Filename.LastIndexOf("_gpr", StringComparison.OrdinalIgnoreCase);
if (gprIndex == -1)
{
return Filename;
}

// Support case sensitive filename extensions (e.g. test_gpr.NuPkg -> test.NuPkg)

var filenameUntilGpr = Filename.Substring(0, gprIndex);
var filenameExcludingGpr = Filename.Substring(gprIndex + 4);

return Path.GetFileName(filenameUntilGpr + filenameExcludingGpr);
}
}
public string Filename { get; set; }
public string FilenameAbsolutePath { get; set; }
public string FilenameWithoutGprPrefix { get; set; }
}

public class NuGetUtilities
Expand Down Expand Up @@ -86,32 +71,20 @@ public static bool BuildOwnerAndRepositoryFromUrl(PackageFile packageFile, strin
return true;
}

public static bool TryReadPackageFileMetadata(PackageFile packageFile)
public static bool BuildOwnerAndRepositoryFromUrlFromNupkg(PackageFile packageFile)
{
if (!File.Exists(packageFile.Filename))
{
return false;
}

Manifest manifest;

try
{
manifest = ReadNupkgManifest(packageFile.Filename);
}
catch (Exception)
{
return false;
}

var manifest = ReadNupkgManifest(packageFile.FilenameAbsolutePath);

return BuildOwnerAndRepositoryFromUrl(packageFile, manifest.Metadata.Repository?.Url);
}

public static PackageFile BuildPackageFile(string filename, string repositoryUrl)
{
var packageFile = new PackageFile
{
Filename = filename
Filename = Path.GetFileName(filename),
FilenameWithoutGprPrefix = Path.GetFileName(filename),
FilenameAbsolutePath = Path.GetFullPath(filename)
};

BuildOwnerAndRepositoryFromUrl(packageFile, repositoryUrl);
Expand All @@ -126,35 +99,29 @@ public static Manifest ReadNupkgManifest(string nupkgPath)
return Manifest.ReadFrom(packageArchiveReader.GetNuspec(), false);
}

public static bool ShouldRewriteNupkg(string nupkgPath, string repositoryUrl, NuGetVersion nuGetVersion = null)
public static bool ShouldRewriteNupkg(PackageFile packageFile, NuGetVersion nuGetVersion = null)
{
if (nupkgPath == null) throw new ArgumentNullException(nameof(nupkgPath));
if (packageFile == null) throw new ArgumentNullException(nameof(packageFile));

var manifest = ReadNupkgManifest(nupkgPath);
var manifest = ReadNupkgManifest(packageFile.FilenameAbsolutePath);

if (nuGetVersion != null && !nuGetVersion.Equals(manifest.Metadata.Version))
{
return true;
}

return !string.Equals(repositoryUrl, manifest.Metadata.Repository?.Url, StringComparison.OrdinalIgnoreCase);
return !string.Equals(packageFile.RepositoryUrl, manifest.Metadata.Repository?.Url, StringComparison.OrdinalIgnoreCase);
}

public static string RewriteNupkg(string nupkgPath, string repositoryUrl, NuGetVersion nuGetVersion = null)
public static void RewriteNupkg(PackageFile packageFile, NuGetVersion nuGetVersion = null)
{
if (nupkgPath == null) throw new ArgumentNullException(nameof(nupkgPath));
if (repositoryUrl == null) throw new ArgumentNullException(nameof(repositoryUrl));

var randomDirectoryId = Guid.NewGuid().ToString("N");
var nupkgFilename = Path.GetFileName(nupkgPath);
var nupkgFilenameWithoutExt = Path.GetFileNameWithoutExtension(nupkgFilename);
var nupkgWorkingDirectoryAbsolutePath = Path.GetDirectoryName(nupkgPath);
var workingDirectory = Path.Combine(nupkgWorkingDirectoryAbsolutePath, $"{nupkgFilenameWithoutExt}_{randomDirectoryId}");

using var tmpDirectory = new DisposableDirectory(workingDirectory);
using var packageArchiveReader = new PackageArchiveReader(nupkgPath.ReadSharedToStream(), false);
using var nuspecMemoryStream = new MemoryStream();

if (packageFile == null) throw new ArgumentNullException(nameof(packageFile));

var randomId = Guid.NewGuid().ToString("N");

using var packageArchiveReader = new PackageArchiveReader(
packageFile.FilenameAbsolutePath.ReadSharedToStream(), false);

var nuspecXDocument = packageArchiveReader.NuspecReader.Xml;
var packageXElement = nuspecXDocument.Single("package");
var metadataXElement = packageXElement.Single("metadata");
Expand All @@ -174,34 +141,44 @@ public static string RewriteNupkg(string nupkgPath, string repositoryUrl, NuGetV
if (repositoryXElement == null)
{
repositoryXElement = new XElement("repository");
repositoryXElement.SetAttributeValue("url", repositoryUrl);
repositoryXElement.SetAttributeValue("url", packageFile.RepositoryUrl);
repositoryXElement.SetAttributeValue("type", "git");
metadataXElement.Add(repositoryXElement);
}
else
{
repositoryXElement.SetAttributeValue("url", repositoryUrl);
repositoryXElement.SetAttributeValue("url", packageFile.RepositoryUrl);
repositoryXElement.SetAttributeValue("type", "git");
}

using var nuspecMemoryStream = new MemoryStream();
nuspecXDocument.Save(nuspecMemoryStream);
nuspecMemoryStream.Seek(0, SeekOrigin.Begin);

ZipFile.ExtractToDirectory(nupkgPath, tmpDirectory.WorkingDirectory, true);
var packageFileWorkingDirectoryAbsolutePath = Path.GetDirectoryName(packageFile.FilenameAbsolutePath);
var packageFileRewriteWorkingDirectory = Path.Combine(packageFileWorkingDirectoryAbsolutePath,
$"{packageId}.{nuGetVersion}_{randomId}");

using var tmpDirectory = new DisposableDirectory(packageFileRewriteWorkingDirectory);

ZipFile.ExtractToDirectory(packageFile.FilenameAbsolutePath, tmpDirectory.WorkingDirectory);

var nuspecDstFilename = Path.Combine(tmpDirectory.WorkingDirectory, $"{packageId}.nuspec");
File.WriteAllBytes(nuspecDstFilename, nuspecMemoryStream.ToArray());

using var outputStream = new MemoryStream();

var packageBuilder = new PackageBuilder(nuspecMemoryStream, tmpDirectory.WorkingDirectory, propertyProvider => throw new NotImplementedException());
var packageBuilder = new PackageBuilder(nuspecMemoryStream, tmpDirectory.WorkingDirectory,
propertyProvider => throw new NotImplementedException());
packageBuilder.Save(outputStream);

var nupkgDstFilenameAbsolutePath = Path.Combine(nupkgWorkingDirectoryAbsolutePath, $"{packageId}.{nuGetVersion}_gpr.nupkg");

File.WriteAllBytes(nupkgDstFilenameAbsolutePath, outputStream.ToArray());
packageFile.FilenameAbsolutePath = Path.Combine(packageFileWorkingDirectoryAbsolutePath,
$"{packageId}.{nuGetVersion}_{randomId}_gpr.nupkg");
packageFile.Filename = Path.GetFileName(packageFile.FilenameAbsolutePath);
packageFile.FilenameWithoutGprPrefix = $"{packageId}.{nuGetVersion}.nupkg";
packageFile.IsNuspecRewritten = true;

return nupkgDstFilenameAbsolutePath;
File.WriteAllBytes(packageFile.FilenameAbsolutePath, outputStream.ToArray());
}

public static string FindTokenInNuGetConfig(Action<string> warning = null)
Expand Down
80 changes: 52 additions & 28 deletions src/GprTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading;
using DotNet.Globbing;
using McMaster.Extensions.CommandLineUtils;
using NuGet.Packaging;
using NuGet.Versioning;
using RestSharp;
using RestSharp.Authenticators;
Expand Down Expand Up @@ -333,9 +334,10 @@ static IAsyncPolicy<IRestResponse> BuildRetryAsyncPolicy(int retryNumber, int re

var retryPolicy = Policy
// http://restsharp.org/usage/exceptions.html
.HandleResult<IRestResponse>(x => !cancellationToken.IsCancellationRequested
.HandleResult<IRestResponse>(x => !cancellationToken.IsCancellationRequested
&& x.StatusCode != HttpStatusCode.Unauthorized
&& x.StatusCode != HttpStatusCode.Conflict
&& x.StatusCode != HttpStatusCode.Conflict
&& x.StatusCode != HttpStatusCode.BadRequest
&& x.StatusCode != HttpStatusCode.OK)
.WaitAndRetryAsync(retryNumber, retryAttempt => TimeSpan.FromSeconds(retrySleepSeconds));

Expand All @@ -351,7 +353,7 @@ protected override async Task OnExecute(CommandLineApplication app)
var isGlobPattern = glob.IsGlobPattern();

NuGetVersion nuGetVersion = null;
if (RepositoryUrl != null && Version != null && !NuGetVersion.TryParse(Version, out nuGetVersion))
if (Version != null && !NuGetVersion.TryParse(Version, out nuGetVersion))
{
Console.WriteLine($"Invalid version: {Version}");
return;
Expand Down Expand Up @@ -383,15 +385,15 @@ protected override async Task OnExecute(CommandLineApplication app)

foreach (var packageFile in packageFiles)
{
if (!File.Exists(packageFile.Filename))
if (!File.Exists(packageFile.FilenameAbsolutePath))
{
Console.WriteLine($"Package file was not found: {packageFile}");
return;
}

if (RepositoryUrl == null)
{
NuGetUtilities.TryReadPackageFileMetadata(packageFile);
NuGetUtilities.BuildOwnerAndRepositoryFromUrlFromNupkg(packageFile);
}
else
{
Expand All @@ -404,19 +406,13 @@ protected override async Task OnExecute(CommandLineApplication app)
{
Console.WriteLine(
$"Project is missing a valid <RepositoryUrl /> XML element value: {packageFile.RepositoryUrl}. " +
$"Package filename: {packageFile.Filename} " +
$"Package filename: {packageFile.FilenameAbsolutePath} " +
"Please use --repository option to set a valid upstream GitHub repository. " +
"Additional details are available at: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#repositoryurl");
return;
}

if (!NuGetUtilities.ShouldRewriteNupkg(packageFile.Filename, packageFile.RepositoryUrl, nuGetVersion))
{
continue;
}

packageFile.Filename = NuGetUtilities.RewriteNupkg(packageFile.Filename, packageFile.RepositoryUrl, nuGetVersion);
packageFile.IsNuspecRewritten = true;
packageFile.ShouldRewriteNuspec = NuGetUtilities.ShouldRewriteNupkg(packageFile, nuGetVersion);
}

const string user = "GprTool";
Expand All @@ -437,51 +433,79 @@ protected override async Task OnExecute(CommandLineApplication app)
{
try
{
await UploadPackageAsync();
await concurrencySemaphore.WaitAsync(CancellationToken);

NuGetVersion packageVersion;
if (packageFile.ShouldRewriteNuspec)
{
NuGetUtilities.RewriteNupkg(packageFile, nuGetVersion);

var manifest = NuGetUtilities.ReadNupkgManifest(packageFile.FilenameAbsolutePath);
packageVersion = manifest.Metadata.Version;
}
else
{
var manifest = NuGetUtilities.ReadNupkgManifest(packageFile.FilenameAbsolutePath);
packageVersion = manifest.Metadata.Version;
}

await using var packageStream = packageFile.FilenameAbsolutePath.ReadSharedToStream();

Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: " +
$"Repository url: {packageFile.RepositoryUrl}. " +
$"Version: {packageVersion}. " +
$"Size: {packageStream.Length} bytes. ");


return await retryPolicy.ExecuteAsync(() => UploadPackageAsync(packageStream));
}
finally
{
concurrencySemaphore.Dispose();
concurrencySemaphore.Release();
}
});

async Task UploadPackageAsync()
async Task<IRestResponse> UploadPackageAsync(MemoryStream packageStream)
{
await concurrencySemaphore.WaitAsync();
if (packageStream == null) throw new ArgumentNullException(nameof(packageStream));

var request = new RestRequest(Method.PUT);

await using var packageStream = packageFile.Filename.ReadSharedToStream();
request.AddFile("package", packageStream.ToArray(), packageFile.FilenameWithoutGprPrefixAndPath);

var client = WithRestClient($"https://nuget.pkg.github.com/{packageFile.Owner}/", x =>
{
x.Authenticator = new HttpBasicAuthenticator(user, token);
});

Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefixAndPath}]: Uploading package.");
packageStream.Seek(0, SeekOrigin.Begin);

request.AddFile("package", packageStream.CopyTo, packageFile.FilenameWithoutGprPrefix, packageStream.Length);

Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: Uploading package.");

var response = await retryPolicy.ExecuteAsync(() => client.ExecuteAsync(request, CancellationToken));
var response = await client.ExecuteAsync(request, CancellationToken);

if (response.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefixAndPath}]: {response.Content}");
return;
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: {response.Content}");
goto done;
}

var nugetWarning = response.Headers.FirstOrDefault(h =>
h.Name.Equals("X-Nuget-Warning", StringComparison.OrdinalIgnoreCase));
if (nugetWarning != null)
{
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefixAndPath}]: {nugetWarning.Value}");
return;
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: {nugetWarning.Value}");
goto done;
}

Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefixAndPath}]: {response.StatusDescription}");
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: {response.StatusDescription}");
foreach (var header in response.Headers)
{
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefixAndPath}]: {header.Name}: {header.Value}");
Console.WriteLine($"[{packageFile.FilenameWithoutGprPrefix}]: {header.Name}: {header.Value}");
}

done:
return response;
}
});

Expand Down
Loading

0 comments on commit e3cb47a

Please sign in to comment.