Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle bad credential exception and block update submissions with no changes #100

Merged
merged 11 commits into from
Jul 3, 2021
8 changes: 7 additions & 1 deletion src/WingetCreateCLI/Commands/BaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public async Task<bool> SetAndCheckGitHubToken(bool cacheToken = false)

return true;
}
catch
catch (Exception e)
{
if (token == cachedToken)
{
Expand All @@ -158,6 +158,12 @@ public async Task<bool> SetAndCheckGitHubToken(bool cacheToken = false)
GitHubOAuth.DeleteTokenCache();
return await this.SetAndCheckGitHubToken();
}
else if (e is AuthorizationException)
{
Logger.ErrorLocalized(nameof(Resources.Error_Prefix), e.Message);
Logger.ErrorLocalized(nameof(Resources.InvalidTokenError_Message));
return false;
}
else
{
throw;
Expand Down
27 changes: 16 additions & 11 deletions src/WingetCreateCLI/Commands/NewCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,23 @@ public override async Task<bool> Execute()

string manifestDirectoryPath = SaveManifestDirToLocalPath(manifests, this.OutputDir);

if (!ValidateManifest(manifestDirectoryPath) ||
!Prompt.Confirm(Resources.ConfirmGitHubSubmitManifest_Message) ||
!await this.SetAndCheckGitHubToken())
{
bool isManifestValid = ValidateManifest(manifestDirectoryPath);

if (isManifestValid && Prompt.Confirm(Resources.ConfirmGitHubSubmitManifest_Message))
{
if (await this.SetAndCheckGitHubToken())
{
return commandEvent.IsSuccessful = await this.GitHubSubmitManifests(manifests, this.GitHubToken);
}

return false;
}
else
{
Console.WriteLine();
Logger.WarnLocalized(nameof(Resources.SkippingPullRequest_Message));
return false;
Logger.WarnLocalized(nameof(Resources.SkippingPullRequest_Message));
return commandEvent.IsSuccessful = isManifestValid;
}

return commandEvent.IsSuccessful = await this.GitHubSubmitManifests(
manifests,
this.GitHubToken);
}
finally
{
Expand Down Expand Up @@ -307,7 +312,7 @@ private async Task<bool> PromptPackageIdentifierAndCheckDuplicates(Manifests man
VersionManifest versionManifest = manifests.VersionManifest;
versionManifest.PackageIdentifier = PromptProperty(versionManifest, versionManifest.PackageIdentifier, nameof(versionManifest.PackageIdentifier));

GitHub client = new GitHub(this.GitHubToken, this.WingetRepoOwner, this.WingetRepo);
GitHub client = new GitHub(null, this.WingetRepoOwner, this.WingetRepo);

string exactMatch = await client.FindPackageId(versionManifest.PackageIdentifier);
if (!string.IsNullOrEmpty(exactMatch))
Expand Down
2 changes: 1 addition & 1 deletion src/WingetCreateCLI/Commands/SubmitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private async Task<bool> SubmitManifest()
{
List<string> manifestContents = Directory.GetFiles(this.Path).Select(f => File.ReadAllText(f)).ToList();

Serialization.DeserializeManifestContents(manifestContents, manifests);
manifests = Serialization.DeserializeManifestContents(manifestContents);
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
return await this.GitHubSubmitManifests(manifests, this.GitHubToken);
}
else
Expand Down
47 changes: 38 additions & 9 deletions src/WingetCreateCLI/Commands/UpdateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,18 @@ public override async Task<bool> Execute()

try
{
GitHub client = new GitHub(this.GitHubToken, this.WingetRepoOwner, this.WingetRepo);
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved

if (!string.IsNullOrEmpty(this.GitHubToken))
{
if (!await this.SetAndCheckGitHubToken())
{
return false;
}
}

Logger.DebugLocalized(nameof(Resources.RetrievingManifest_Message), this.Id);

GitHub client = new GitHub(this.GitHubToken, this.WingetRepoOwner, this.WingetRepo);
string exactId = await client.FindPackageId(this.Id);

if (!string.IsNullOrEmpty(exactId))
Expand Down Expand Up @@ -133,9 +142,7 @@ public override async Task<bool> Execute()
/// <returns>Manifests object representing the updates manifest content, or null if the update failed.</returns>
public async Task<Manifests> DeserializeExistingManifestsAndUpdate(List<string> latestManifestContent)
{
Manifests manifests = new Manifests();

Serialization.DeserializeManifestContents(latestManifestContent, manifests);
Manifests manifests = Serialization.DeserializeManifestContents(latestManifestContent);

if (manifests.SingletonManifest != null)
{
Expand Down Expand Up @@ -192,27 +199,36 @@ public async Task<Manifests> DeserializeExistingManifestsAndUpdate(List<string>
/// <returns>Boolean representing whether the manifest was updated successfully or not.</returns>
public async Task<bool> ExecuteManifestUpdate(List<string> latestManifestContent, CommandExecutedEvent commandEvent)
{
Manifests manifests = await this.DeserializeExistingManifestsAndUpdate(latestManifestContent);
if (manifests == null)
Manifests originalManifests = Serialization.DeserializeManifestContents(latestManifestContent);
Manifests updatedManifests = await this.DeserializeExistingManifestsAndUpdate(latestManifestContent);

if (updatedManifests == null)
{
return false;
}

DisplayManifestPreview(manifests);
DisplayManifestPreview(updatedManifests);

if (string.IsNullOrEmpty(this.OutputDir))
{
this.OutputDir = Directory.GetCurrentDirectory();
}

string manifestDirectoryPath = SaveManifestDirToLocalPath(manifests, this.OutputDir);
string manifestDirectoryPath = SaveManifestDirToLocalPath(updatedManifests, this.OutputDir);

if (ValidateManifest(manifestDirectoryPath))
{
if (this.SubmitToGitHub)
{
if (!this.VerifyUpdatedInstallerHash(originalManifests.InstallerManifest, updatedManifests.InstallerManifest))
{
Logger.ErrorLocalized(nameof(Resources.NoChangeDetectedInUpdatedManifest_Message));
Logger.ErrorLocalized(nameof(Resources.CompareUpdatedManifestWithExisting_Message));
return false;
}

return await this.SetAndCheckGitHubToken()
? (commandEvent.IsSuccessful = await this.GitHubSubmitManifests(manifests, this.GitHubToken))
? (commandEvent.IsSuccessful = await this.GitHubSubmitManifests(updatedManifests, this.GitHubToken))
: false;
}

Expand Down Expand Up @@ -260,5 +276,18 @@ private static void UpdatePropertyForLocaleManifests(string propertyName, string
manifest.GetType().GetProperty(propertyName).SetValue(manifest, value);
}
}

/// <summary>
/// Compares the hashes of the original and updated manifests and returns true if the new manifest has an updated SHA256 hash.
/// </summary>
/// <param name="oldManifest">The original installer manifest object model.</param>
/// <param name="newManifest">The updated installer manifest object model.</param>
/// <returns>A boolean value indicating whether the updated manifest has new changes compared to the original manifest.</returns>
private bool VerifyUpdatedInstallerHash(InstallerManifest oldManifest, InstallerManifest newManifest)
{
var oldHashes = oldManifest.Installers.Select(i => i.InstallerSha256).Distinct();
var newHashes = newManifest.Installers.Select(i => i.InstallerSha256).Distinct();
return newHashes.Except(oldHashes).Any();
}
}
}
20 changes: 15 additions & 5 deletions src/WingetCreateCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Microsoft.WingetCreateCLI
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
Expand Down Expand Up @@ -35,13 +37,15 @@ private static async Task<int> Main(string[] args)
Logger.Trace($"Command line args: {arguments}");

Parser myParser = new Parser(config => config.HelpWriter = null);

var parserResult = myParser.ParseArguments<NewCommand, UpdateCommand, SubmitCommand, TokenCommand, SettingsCommand>(args);
BaseCommand command = parserResult.MapResult(c => c as BaseCommand, err => null);

var types = GetVerbs();
var parserResult = myParser.ParseArguments(args, types);
BaseCommand command = parserResult.MapResult(c => c as BaseCommand, err => null);

if (command == null)
{
DisplayHelp(parserResult as NotParsed<object>);
return 1;
return args.Any() ? 1 : 0;
}

try
Expand All @@ -61,7 +65,13 @@ private static async Task<int> Main(string[] args)
Logger.Error(ex.ToString());
return 1;
}
}
}

private static Type[] GetVerbs()
{
return Assembly.GetExecutingAssembly().GetTypes()
.Where(types => types.GetCustomAttribute<VerbAttribute>() != null).ToArray();
}

private static void DisplayHelp(NotParsed<object> result)
{
Expand Down
27 changes: 27 additions & 0 deletions src/WingetCreateCLI/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/WingetCreateCLI/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -587,4 +587,13 @@
<data name="UnexpectedErrorLoadSettings_Message" xml:space="preserve">
<value>Unexpected error while loading settings. Please verify your settings by running the settings command.</value>
</data>
<data name="CompareUpdatedManifestWithExisting_Message" xml:space="preserve">
<value>Please ensure that the updated manifest you are submitting is different from the existing package.</value>
</data>
<data name="InvalidTokenError_Message" xml:space="preserve">
<value>Invalid token provided, please generate a new GitHub token and try again.</value>
</data>
<data name="NoChangeDetectedInUpdatedManifest_Message" xml:space="preserve">
<value>Submitting a manifest without any updated changes is not allowed. </value>
</data>
</root>
8 changes: 6 additions & 2 deletions src/WingetCreateCore/Common/Serialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@ public static string ConvertYamlToJson(string yaml)
/// Deserializes a list of manifest strings into their appropriate object models.
/// </summary>
/// <param name="manifestContents">List of manifest string contents.</param>
/// <param name="manifests">Wrapper object for manifest object models.</param>
public static void DeserializeManifestContents(IEnumerable<string> manifestContents, Manifests manifests)
/// <returns>Manifest object model.</returns>
public static Manifests DeserializeManifestContents(IEnumerable<string> manifestContents)
{
Manifests manifests = new Manifests();

foreach (string content in manifestContents)
{
string trimmedContent = RemoveBom(content);
Expand Down Expand Up @@ -171,6 +173,8 @@ public static void DeserializeManifestContents(IEnumerable<string> manifestConte
manifests.InstallerManifest = Serialization.DeserializeFromString<InstallerManifest>(trimmedContent);
}
}

return manifests;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ public async Task UpdateCommandTest()
string manifestDir = Utils.GetAppManifestDirPath(TestConstants.TestPackageIdentifier, version);
var updatedManifestContents = Directory.GetFiles(Path.Combine(this.tempPath, manifestDir)).Select(f => File.ReadAllText(f));
Assert.IsTrue(updatedManifestContents.Any(), "Updated manifests were not created successfully");
var manifestsToValidate = new Manifests();
Serialization.DeserializeManifestContents(updatedManifestContents, manifestsToValidate);
Manifests manifestsToValidate = Serialization.DeserializeManifestContents(updatedManifestContents);
Assert.AreEqual(version, manifestsToValidate.VersionManifest.PackageVersion, $"Failed to update version of {TestConstants.TestPackageIdentifier}");
}

Expand Down Expand Up @@ -98,8 +97,7 @@ public async Task UpdateAndVerifyUpdatedProperties()
string version = "1.2.3.4";
UpdateCommand command = GetUpdateCommand(TestConstants.TestMsiPackageIdentifier, version, this.tempPath);
List<string> initialManifestContent = GetInitialManifestContent(TestConstants.TestMsiManifest);
var initialManifests = new Manifests();
Serialization.DeserializeManifestContents(initialManifestContent, initialManifests);
Manifests initialManifests = Serialization.DeserializeManifestContents(initialManifestContent);
var initialInstaller = initialManifests.SingletonManifest.Installers.First();

var updatedManifests = await command.DeserializeExistingManifestsAndUpdate(initialManifestContent);
Expand Down