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

Improvements to replace argument #485

Merged
merged 7 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/submit.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The following arguments are available:
| Argument | Description |
|--------------|-------------|
| **-p, --prtitle** | The title of the pull request submitted to GitHub.
| **-r, --replace** | Boolean value for replacing an existing manifest from the Windows Package Manager repo. Optionally provide a version or else the latest version will be replaced. Default is false.
| **-t, --token** | GitHub personal access token used for direct submission to the Windows Package Manager repo. If no token is provided, tool will prompt for GitHub login credentials.

If you have provided your [GitHub token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) on the command line with the **submit** command and the device is registered with GitHub, **Winget-Create** will submit your PR to [Windows Package Manager repo](https://docs.microsoft.com/windows/package-manager/).
Expand Down
78 changes: 73 additions & 5 deletions src/WingetCreateCLI/Commands/SubmitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,29 @@ public static IEnumerable<Example> Examples
public string Path { get; set; }

/// <summary>
/// Gets or sets the GitHub token used to submit a pull request on behalf of the user.
/// Gets or sets the previous version to replace from the Windows Package Manager repository.
/// </summary>
[Option('t', "token", Required = false, HelpText = "GitHubToken_HelpText", ResourceType = typeof(Resources))]
public override string GitHubToken { get => base.GitHubToken; set => base.GitHubToken = value; }
[Value(1, MetaName = "ReplaceVersion", Required = false, HelpText = "ReplaceVersion_HelpText", ResourceType = typeof(Resources))]
public string ReplaceVersion { get; set; }

/// <summary>
/// Gets or sets the title for the pull request.
/// </summary>
[Option('p', "prtitle", Required = false, HelpText = "PullRequestTitle_HelpText", ResourceType = typeof(Resources))]
public override string PRTitle { get => base.PRTitle; set => base.PRTitle = value; }

/// <summary>
/// Gets or sets a value indicating whether or not to replace a previous version of the manifest with the update.
/// </summary>
[Option('r', "replace", Required = false, HelpText = "ReplacePrevious_HelpText", ResourceType = typeof(Resources))]
public bool Replace { get; set; }

/// <summary>
/// Gets or sets the GitHub token used to submit a pull request on behalf of the user.
/// </summary>
[Option('t', "token", Required = false, HelpText = "GitHubToken_HelpText", ResourceType = typeof(Resources))]
public override string GitHubToken { get => base.GitHubToken; set => base.GitHubToken = value; }

/// <summary>
/// Gets or sets the unbound arguments that exist after the first positional parameter.
/// </summary>
Expand Down Expand Up @@ -99,23 +111,79 @@ private async Task<bool> SubmitManifest()
{
string expandedPath = System.Environment.ExpandEnvironmentVariables(this.Path);

// TODO: Remove singleton support.
if (File.Exists(expandedPath) && ValidateManifest(expandedPath))
{
Manifests manifests = new Manifests();
manifests.SingletonManifest = Serialization.DeserializeFromPath<SingletonManifest>(expandedPath);
return await this.GitHubSubmitManifests(manifests, this.PRTitle);

if (this.Replace && !await this.ValidateReplaceArguments(manifests.SingletonManifest.PackageIdentifier, manifests.SingletonManifest.PackageVersion))
{
return false;
}

return await this.GitHubSubmitManifests(manifests, this.PRTitle, this.Replace, this.ReplaceVersion);
}
else if (Directory.Exists(expandedPath) && ValidateManifest(expandedPath))
{
List<string> manifestContents = Directory.GetFiles(expandedPath).Select(f => File.ReadAllText(f)).ToList();
Manifests manifests = Serialization.DeserializeManifestContents(manifestContents);
return await this.GitHubSubmitManifests(manifests, this.PRTitle);

if (this.Replace && !await this.ValidateReplaceArguments(manifests.VersionManifest.PackageIdentifier, manifests.VersionManifest.PackageVersion))
{
return false;
}

return await this.GitHubSubmitManifests(manifests, this.PRTitle, this.Replace, this.ReplaceVersion);
}
else
{
Logger.ErrorLocalized(nameof(Resources.Error_Prefix), Resources.PathDoesNotExist_Warning);
return false;
}
}

private async Task<bool> ValidateReplaceArguments(string packageId, string submitVersion)
{
string exactId;
try
{
exactId = await this.GitHubClient.FindPackageId(packageId);
}
catch (Octokit.RateLimitExceededException)
{
Logger.ErrorLocalized(nameof(Resources.RateLimitExceeded_Message));
return false;
}

if (string.IsNullOrEmpty(exactId))
{
Logger.ErrorLocalized(nameof(Resources.ReplacePackageIdDoesNotExist_Error), packageId);
return false;
}

if (!string.IsNullOrEmpty(this.ReplaceVersion))
{
// If submit version is same as replace version, it's a regular update.
if (submitVersion == this.ReplaceVersion)
{
Logger.ErrorLocalized(nameof(Resources.ReplaceVersionEqualsSubmitVersion_ErrorMessage));
return false;
}

// Check if the replace version exists in the repository.
try
{
await this.GitHubClient.GetManifestContentAsync(packageId, this.ReplaceVersion);
}
catch (Octokit.NotFoundException)
{
Logger.ErrorLocalized(nameof(Resources.VersionDoesNotExist_Error), this.ReplaceVersion, packageId);
return false;
}
}

return true;
}
}
}
10 changes: 9 additions & 1 deletion src/WingetCreateCLI/Commands/UpdateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ public override async Task<bool> Execute()
return false;
}

bool submitFlagMissing = !this.SubmitToGitHub && (!string.IsNullOrEmpty(this.PRTitle) || this.Replace);

if (submitFlagMissing)
{
Logger.WarnLocalized(nameof(Resources.SubmitFlagMissing_Warning));
Console.WriteLine();
}

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

string exactId;
Expand Down Expand Up @@ -172,7 +180,7 @@ public override async Task<bool> Execute()
}
catch (Octokit.NotFoundException)
{
Logger.ErrorLocalized(nameof(Resources.VersionDoesNotExist_Error), this.Version, this.Id);
Logger.ErrorLocalized(nameof(Resources.VersionDoesNotExist_Error), this.ReplaceVersion, this.Id);
return false;
}
}
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.

11 changes: 11 additions & 0 deletions src/WingetCreateCLI/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1237,4 +1237,15 @@
<data name="DownloadCommandProhibited_KeywordDescription" xml:space="preserve">
<value>Indicates whether the installer is prohibited from being downloaded for offline installation</value>
</data>
<data name="SubmitFlagMissing_Warning" xml:space="preserve">
<value>Submit arguments were provided. Did you forget to include the --submit, -s flag?</value>
<comment>'--submit, -s' refers to a command line switch argument</comment>
</data>
<data name="ReplacePackageIdDoesNotExist_Error" xml:space="preserve">
<value>Replace operation cannot be performed. Package identifier '{0}' does not exist in the Windows Package Manager repo.</value>
<comment>{0} - will be replaced with the ID of the package</comment>
</data>
<data name="ReplaceVersionEqualsSubmitVersion_ErrorMessage" xml:space="preserve">
<value>The replace version cannot be equal to the submit version.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,70 @@ public async Task UpdateFailsWithUnmatchedPackages()
Assert.That(result, Does.Contain(Resources.NewInstallerUrlMustMatchExisting_Message), "New installer must match error should be thrown");
}

/// <summary>
/// Verify that update command warns if submit arguments are provided without submit flag being set.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Test]
public async Task UpdateChecksMissingSubmitFlagWithReplace()
{
string packageId = "TestPublisher.TestPackageId";
string version = "1.2.3.4";

UpdateCommand command = new UpdateCommand
{
Id = packageId,
Version = version,
InstallerUrls = new[] { "https://fakedomain.com/fakeinstaller.exe" },
SubmitToGitHub = false,
Replace = true,
};

try
{
await command.Execute();
}
catch (Exception)
{
// Expected exception
}

string result = this.sw.ToString();
Assert.That(result, Does.Contain(Resources.SubmitFlagMissing_Warning), "Submit flag missing warning should be shown");
}

/// <summary>
/// Verify that update command warns if submit arguments are provided without submit flag being set.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Test]
public async Task UpdateChecksMissingSubmitFlagWithPRTitle()
{
string packageId = "TestPublisher.TestPackageId";
string version = "1.2.3.4";

UpdateCommand command = new UpdateCommand
{
Id = packageId,
Version = version,
InstallerUrls = new[] { "https://fakedomain.com/fakeinstaller.exe" },
SubmitToGitHub = false,
PRTitle = "Test PR Title",
};

try
{
await command.Execute();
}
catch (Exception)
{
// Expected exception
}

string result = this.sw.ToString();
Assert.That(result, Does.Contain(Resources.SubmitFlagMissing_Warning), "Submit flag missing warning should be shown");
}

/// <summary>
/// Since some installers are incorrectly labeled on the manifest, resort to using the installer URL to find matches.
/// This unit test uses a msi installer that is not an arm64 installer, but because the installer URL includes "arm64", it should find a match.
Expand Down
Loading