diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 094d07b875..cfee50a122 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: key: ${{ runner.os }}-tools-${{ hashFiles('recipe.cake') }} - name: Build with .Net Framework shell: powershell - run: ./build.ps1 --verbosity=diagnostic --target=CI --testExecutionType=unit --shouldRunOpenCover=false + run: ./build.ps1 --verbosity=diagnostic --target=CI --testExecutionType=unit --shouldRunOpenCover=false --shouldBuildMsi=true - name: Upload Windows build results uses: actions/upload-artifact@v3 # Always upload build results @@ -67,6 +67,7 @@ jobs: code_drop\Packages\NuGet\*.nupkg code_drop\Packages\Chocolatey\*.nupkg code_drop\MsBuild.log + code_drop\MSIs\en-US\chocolatey-*.msi # - uses: coverallsapp/github-action@master # with: # github-token: ${{ secrets.GITHUB_TOKEN }} @@ -140,4 +141,4 @@ jobs: # uses: coverallsapp/github-action@master # with: # github-token: ${{ secrets.GITHUB_TOKEN }} - # parallel-finished: true \ No newline at end of file + # parallel-finished: true diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 8f52dbb633..5f1e28d8ae 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -65,7 +65,7 @@ object Chocolatey : BuildType({ script { name = "Call Cake" scriptContent = """ - build.official.bat --verbosity=diagnostic --target=CI --testExecutionType=unit --shouldRunOpenCover=false + build.official.bat --verbosity=diagnostic --target=CI --testExecutionType=unit --shouldRunOpenCover=false --shouldBuildMsi=true """.trimIndent() } } diff --git a/recipe.cake b/recipe.cake index feca7ac13c..4e52b636ac 100644 --- a/recipe.cake +++ b/recipe.cake @@ -167,6 +167,7 @@ Task("Prepare-Chocolatey-Packages") .IsDependeeOf("Create-Chocolatey-Packages") .IsDependeeOf("Verify-PowerShellScripts") .IsDependeeOf("Sign-Assemblies") + .IsDependentOn("Copy-Nuspec-Folders") .WithCriteria(() => BuildParameters.BuildAgentOperatingSystem == PlatformFamily.Windows, "Skipping because not running on Windows") .WithCriteria(() => BuildParameters.ShouldRunChocolatey, "Skipping because execution of Chocolatey has been disabled") .Does(() => @@ -363,7 +364,6 @@ Task("Prepare-NuGet-Packages") Task("Prepare-MSI") .WithCriteria(() => BuildParameters.ShouldBuildMsi, "Skipping because creation of MSI has been disabled") - .WithCriteria(() => BuildParameters.IsTagged, "Skipping because build is not tagged") .IsDependeeOf("Build-MSI") .Does(() => { @@ -378,9 +378,6 @@ Task("Prepare-MSI") } }); -BuildParameters.Tasks.BuildMsiTask - .WithCriteria(() => BuildParameters.IsTagged, "Skipping because build is not tagged"); - Task("Create-TarGz-Packages") .IsDependentOn("Build") .IsDependeeOf("Package") @@ -438,7 +435,7 @@ BuildParameters.SetParameters(context: Context, getMsisToSign: getMsisToSign, getILMergeConfigs: getILMergeConfigs, preferDotNetGlobalToolUsage: !IsRunningOnWindows(), - shouldBuildMsi: true, + shouldBuildMsi: false, msiUsedWithinNupkg: false, shouldAuthenticodeSignMsis: true, shouldRunNuGet: IsRunningOnWindows(), diff --git a/src/chocolatey.tests.integration/chocolatey.tests.integration.csproj b/src/chocolatey.tests.integration/chocolatey.tests.integration.csproj index d5f8a0615f..b25c37cdd8 100644 --- a/src/chocolatey.tests.integration/chocolatey.tests.integration.csproj +++ b/src/chocolatey.tests.integration/chocolatey.tests.integration.csproj @@ -485,6 +485,7 @@ + diff --git a/src/chocolatey.tests.integration/infrastructure.app/nuget/NugetListSpecs.cs b/src/chocolatey.tests.integration/infrastructure.app/nuget/NugetListSpecs.cs new file mode 100644 index 0000000000..bd6eb5e599 --- /dev/null +++ b/src/chocolatey.tests.integration/infrastructure.app/nuget/NugetListSpecs.cs @@ -0,0 +1,1053 @@ +// Copyright © 2017 - 2024 Chocolatey Software, Inc +// Copyright © 2011 - 2017 RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using chocolatey.infrastructure.app.configuration; +using chocolatey.infrastructure.app.nuget; +using chocolatey.infrastructure.filesystem; +using FluentAssertions; +using Moq; +using NuGet.Common; +using NuGet.Protocol.Core.Types; +using WireMock.FluentAssertions; +using WireMock.Matchers; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +namespace chocolatey.tests.integration.infrastructure.app.services +{ + public class NugetListSpecs + { + public abstract class NugetListSpecsBase : TinySpec + { + protected ChocolateyConfiguration Configuration = new ChocolateyConfiguration(); + protected readonly ILogger Logger = new ChocolateyNugetLogger(); + protected readonly Mock FileSystem = new Mock(); + protected Lazy MockServer; + protected string HttpCacheLocation = Path.Combine( + Environment.CurrentDirectory, + Guid.NewGuid().ToString()); + + public override void Context() + { + Configuration.CacheExpirationInMinutes = 0; + Configuration.CacheLocation = HttpCacheLocation; + MockServer = new Lazy(() => WireMockServer.Start()); + } + + public override void AfterObservations() + { + if (MockServer.IsValueCreated) + { + MockServer.Value.Stop(); + MockServer.Value.Dispose(); + } + + if (Directory.Exists(HttpCacheLocation)) + { + Directory.Delete(HttpCacheLocation, recursive: true); + } + + base.AfterObservations(); + } + + protected void AddMockServerV2ApiUrl(ChocolateyConfiguration config) + { + config.Sources = $"{config.Sources};${MockServer.Value.Url}/api/v2/".TrimStart(';'); + } + + protected void AddMockServerV3ApiUrl(ChocolateyConfiguration config) + { + config.Sources = $"{config.Sources};${MockServer.Value.Url}/v3/index.json".TrimStart(';'); + } + + protected void AddSimpleResponse(string path, string body, string destination = BodyDestinationFormat.Json, Encoding encoding = null) + { + if (encoding is null) + { + encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + } + + var matcher = path.Contains('*') + ? (IStringMatcher)new WildcardMatcher(path) + : new ExactMatcher(path); + + MockServer.Value.Given(Request.Create().WithPath(matcher).UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody(body, destination, encoding)); + } + + protected void AddSimpleResponse(string path, object body, Encoding encoding = null) + { + if (encoding is null) + { + encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + } + + var matcher = path.Contains('*') + ? (IStringMatcher)new WildcardMatcher(path) + : new ExactMatcher(path); + + MockServer.Value.Given(Request.Create().WithPath(matcher).UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBodyAsJson(body, encoding, indented: false)); + } + + protected void AddV3OnlyIndexResponse(string path, string host = null) + { + if (string.IsNullOrEmpty(host)) + { + host = MockServer.Value.Url; + } + + AddSimpleResponse(path, @"{ + ""version"": ""3.0.0"", + ""resources"": [ + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService/3.0.0-rc"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService/3.0.0-beta"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService/3.0.0-rc"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService/3.0.0-beta"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl/3.0.0-rc"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl/3.0.0-beta"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations-gz/"", + ""@type"": ""RegistrationsBaseUrl/3.4.0"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations-gz/"", + ""@type"": ""RegistrationsBaseUrl/3.6.0"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/flatcontainer"", + ""@type"": ""PackageBaseAddress/3.0.0"", + ""comment"": ""Base URL of where NuGet packages are stored, in the format https://api.nuget.org/v3-flatcontainer/{id-lower}/{version-lower}/{id-lower}.{version-lower}.nupkg"" + }, + { + ""@id"": """ + host + @"/feeds/internal-choco/{id}/{version}"", + ""@type"": ""PackageDetailsUriTemplate/5.1.0"", + ""comment"": ""URI template used by NuGet Client to construct details URL for packages"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/{id-lower}/index.json"", + ""@type"": ""PackageDisplayMetadataUriTemplate/3.0.0-rc"", + ""comment"": ""URI template used by NuGet Client to construct display metadata for Packages using ID"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/{id-lower}/{version-lower}.json"", + ""@type"": ""PackageVersionDisplayMetadataUriTemplate/3.0.0-rc"", + ""comment"": ""URI template used by NuGet Client to construct display metadata for Packages using ID, Version"" + } + ] + }"); + } + + protected void AddV3IndexResponse(string path, string host = null) + { + if (string.IsNullOrEmpty(host)) + { + host = MockServer.Value.Url; + } + + AddSimpleResponse(path, @"{ + ""version"": ""3.0.0"", + ""resources"": [ + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService/3.0.0-rc"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/search"", + ""@type"": ""SearchQueryService/3.0.0-beta"", + ""comment"": ""Query endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService/3.0.0-rc"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/autocomplete"", + ""@type"": ""SearchAutocompleteService/3.0.0-beta"", + ""comment"": ""Autocomplete endpoint of NuGet Search service"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl/3.0.0-rc"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/"", + ""@type"": ""RegistrationsBaseUrl/3.0.0-beta"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations-gz/"", + ""@type"": ""RegistrationsBaseUrl/3.4.0"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations-gz/"", + ""@type"": ""RegistrationsBaseUrl/3.6.0"", + ""comment"": ""Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/flatcontainer"", + ""@type"": ""PackageBaseAddress/3.0.0"", + ""comment"": ""Base URL of where NuGet packages are stored, in the format https://api.nuget.org/v3-flatcontainer/{id-lower}/{version-lower}/{id-lower}.{version-lower}.nupkg"" + }, + { + ""@id"": """ + host + @"/feeds/internal-choco/{id}/{version}"", + ""@type"": ""PackageDetailsUriTemplate/5.1.0"", + ""comment"": ""URI template used by NuGet Client to construct details URL for packages"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/{id-lower}/index.json"", + ""@type"": ""PackageDisplayMetadataUriTemplate/3.0.0-rc"", + ""comment"": ""URI template used by NuGet Client to construct display metadata for Packages using ID"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/v3/registrations/{id-lower}/{version-lower}.json"", + ""@type"": ""PackageVersionDisplayMetadataUriTemplate/3.0.0-rc"", + ""comment"": ""URI template used by NuGet Client to construct display metadata for Packages using ID, Version"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/"", + ""@type"": ""LegacyGallery"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/"", + ""@type"": ""LegacyGallery/2.0.0"" + }, + { + ""@id"": """ + host + @"/nuget/internal-choco/package"", + ""@type"": ""PackagePublish/2.0.0"" + } + ] +} +"); + + AddV2MetadataResponse("/nuget/internal-choco/"); + + } + + protected void AddV2MetadataResponse(string path) + { + AddSimpleResponse(path, @" + + + Default + + + Packages + + + +", destination: BodyDestinationFormat.SameAsSource); + + AddSimpleResponse(path + "$metadata", @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +", destination: BodyDestinationFormat.SameAsSource); + } + } + + public class When_Searching_For_A_Package_With_Version_On_A_V3_Only_Feed : NugetListSpecsBase + { + private List _result; + + public override void Context() + { + base.Context(); + + AddV3OnlyIndexResponse("/endpoints/test-jsons/content/index.json"); + MockServer.Value.Given(Request.Create() + .WithPath(new ExactMatcher("/nuget/internal-choco/v3/search")) + .UsingMethod("GET") + .WithParam("q", new ExactMatcher("7zip")) + .WithParam("skip", new ExactMatcher("0")) + .WithParam("take", new ExactMatcher("30")) + .WithParam("semVerLevel", new ExactMatcher("2.0.0"))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody(@"{ + ""totalHits"": 2, + ""data"": [ + { + ""id"": ""7zip"", + ""version"": ""23.1.0"", + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""versions"": [ + { + ""version"": ""22.1"", + ""downloads"": 1 + }, + { + ""version"": ""23.1.0"", + ""downloads"": 0 + } + ], + ""authors"": ""Igor Pavlov"", + ""packageTypes"": [ + { + ""name"": ""Dependency"" + } + ], + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""owners"": [ + ""chocolatey-community"", + ""Rob Reynolds"" + ], + ""projectUrl"": ""http://www.7-zip.org/"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations/7zip/index.json"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""foss"" + ], + ""title"": ""7-Zip"", + ""totalDownloads"": 1 + }, + { + ""id"": ""7zip.install"", + ""version"": ""23.1.0"", + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""versions"": [ + { + ""version"": ""22.1.0"", + ""downloads"": 1 + }, + { + ""version"": ""23.1.0"", + ""downloads"": 0 + } + ], + ""authors"": ""Igor Pavlov"", + ""packageTypes"": [ + { + ""name"": ""Dependency"" + } + ], + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""owners"": [ + ""chocolatey-community"", + ""Rob Reynolds"" + ], + ""projectUrl"": ""http://www.7-zip.org/"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations/7zip.install/index.json"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""cross-platform"", + ""cli"", + ""foss"" + ], + ""title"": ""7-Zip (Install)"", + ""totalDownloads"": 1 + } + ] + }", destination: BodyDestinationFormat.Json)); + AddSimpleResponse("/nuget/internal-choco/v3/registrations-gz/7zip/index.json", @"{ + ""count"": 1, + ""items"": [ + { + ""count"": 2, + ""items"": [ + { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip/22.1.json"", + ""@type"": ""Package"", + ""catalogEntry"": { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/catalog/7zip/22.1.json"", + ""@type"": ""PackageDetails"", + ""authors"": ""Igor Pavlov"", + ""dependencyGroups"": [ + { + ""dependencies"": [ + { + ""id"": ""7zip.install"", + ""range"": ""[22.1]"" + } + ] + } + ], + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""id"": ""7zip"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""projectUrl"": ""http://www.7-zip.org/"", + ""published"": ""2023-05-08T15:25:36.157Z"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""foss"" + ], + ""title"": ""7-Zip"", + ""version"": ""22.1"" + }, + ""packageContent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/flatcontainer/7zip/22.1/7zip.22.1.nupkg"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip/index.json"" + }, + { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip/23.1.0.json"", + ""@type"": ""Package"", + ""catalogEntry"": { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/catalog/7zip/23.1.0.json"", + ""@type"": ""PackageDetails"", + ""authors"": ""Igor Pavlov"", + ""dependencyGroups"": [ + { + ""dependencies"": [ + { + ""id"": ""7zip.install"", + ""range"": ""[23.1.0]"" + } + ] + } + ], + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""id"": ""7zip"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""projectUrl"": ""http://www.7-zip.org/"", + ""published"": ""2024-02-06T14:48:14.26Z"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""foss"" + ], + ""title"": ""7-Zip"", + ""version"": ""23.1.0"" + }, + ""packageContent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/flatcontainer/7zip/23.1.0/7zip.23.1.0.nupkg"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip/index.json"" + } + ], + ""parent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip/index.json"", + ""lower"": ""22.1"", + ""upper"": ""23.1.0"" + } + ] + }"); + AddSimpleResponse("/nuget/internal-choco/v3/registrations-gz/7zip.install/index.json", @"{ + ""count"": 1, + ""items"": [ + { + ""count"": 2, + ""items"": [ + { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip.install/22.1.0.json"", + ""@type"": ""Package"", + ""catalogEntry"": { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/catalog/7zip.install/22.1.0.json"", + ""@type"": ""PackageDetails"", + ""authors"": ""Igor Pavlov"", + ""dependencyGroups"": [ + { + ""dependencies"": [ + { + ""id"": ""chocolatey-core.extension"", + ""range"": ""1.3.3"" + } + ] + } + ], + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""id"": ""7zip.install"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""projectUrl"": ""http://www.7-zip.org/"", + ""published"": ""2023-05-08T15:25:39.903Z"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""cross-platform"", + ""cli"", + ""foss"" + ], + ""title"": ""7-Zip (Install)"", + ""version"": ""22.1.0"" + }, + ""packageContent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/flatcontainer/7zip.install/22.1.0/7zip.install.22.1.0.nupkg"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip.install/index.json"" + }, + { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip.install/23.1.0.json"", + ""@type"": ""Package"", + ""catalogEntry"": { + ""@id"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/catalog/7zip.install/23.1.0.json"", + ""@type"": ""PackageDetails"", + ""authors"": ""Igor Pavlov"", + ""dependencyGroups"": [ + { + ""dependencies"": [ + { + ""id"": ""chocolatey-core.extension"", + ""range"": ""1.3.3"" + } + ] + } + ], + ""description"": ""7-Zip is a file archiver with a high compression ratio.\n\n## Features\n\n- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression\n- Supported formats:\n- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM\n- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z.\n- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip\n- Strong AES-256 encryption in 7z and ZIP formats\n- Self-extracting capability for 7z format\n- Integration with Windows Shell\n- Powerful File Manager\n- Powerful command line version\n- Plugin for FAR Manager\n- Localizations for 87 languages\n\n## Notes\n- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it.\n- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.**"", + ""iconUrl"": ""https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg"", + ""id"": ""7zip.install"", + ""licenseUrl"": ""http://www.7-zip.org/license.txt"", + ""projectUrl"": ""http://www.7-zip.org/"", + ""published"": ""2024-02-06T14:48:19.44Z"", + ""summary"": ""7-Zip is a file archiver with a high compression ratio."", + ""tags"": [ + ""7zip"", + ""zip"", + ""archiver"", + ""admin"", + ""cross-platform"", + ""cli"", + ""foss"" + ], + ""title"": ""7-Zip (Install)"", + ""version"": ""23.1.0"" + }, + ""packageContent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/flatcontainer/7zip.install/23.1.0/7zip.install.23.1.0.nupkg"", + ""registration"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip.install/index.json"" + } + ], + ""parent"": """ + MockServer.Value.Url + @"/nuget/internal-choco/v3/registrations-gz/7zip.install/index.json"", + ""lower"": ""22.1.0"", + ""upper"": ""23.1.0"" + } + ] + }"); + + Configuration.Sources = $"{MockServer.Value.Url}/endpoints/test-jsons/content/index.json"; + Configuration.Input = "7zip"; + Configuration.Version = "22.1.0"; + Configuration.SourceCommand.Username = "kim"; + Configuration.SourceCommand.Password = "P@ssword123"; + } + + public override void Because() + { + _result = NugetList.GetPackages(Configuration, Logger, FileSystem.Object).ToList(); + } + + [Fact] + public void Should_Have_Found_Two_Packages() + { + _result.Should().HaveCount(2); + } + + [InlineData("7zip", "22.1.0")] + [InlineData("7zip.install", "22.1.0")] + public void Should_Contain_Expected_Package(string id, string version) + { + _result.Should() + .ContainSingle(c => c.Identity.Id == id && c.Identity.Version.ToNormalizedString() == version); + } + + [InlineData("/endpoints/test-jsons/content/index.json")] + [InlineData("/nuget/internal-choco/v3/search?q=7zip&skip=0&take=30&prerelease=false&semVerLevel=2.0.0")] + [InlineData("/nuget/internal-choco/v3/registrations-gz/7zip/index.json")] + [InlineData("/nuget/internal-choco/v3/registrations-gz/7zip.install/index.json")] + public void Should_Have_Called_Expected_Paths(string path) + { + MockServer.Value.Should() + .HaveReceivedACall() + .AtUrl(MockServer.Value.Url + path) + .And.UsingMethod("GET"); + } + } + + public class When_Searching_For_A_Package_With_Version_On_A_Combined_Feed : NugetListSpecsBase + { + private List _result; + + public override void Context() + { + base.Context(); + + AddV3IndexResponse("/nuget/internal-choco/v3/index.json"); + MockServer.Value.Given(Request.Create() + .WithPath(new ExactMatcher("/nuget/internal-choco/Search()")) + .UsingGet() + .WithParam("$orderby", + new ExactMatcher("Id"), + new ExactMatcher("Version desc")) + .WithParam("searchTerm", new ExactMatcher("'7zip'")) + .WithParam("$skip", new ExactMatcher("0")) + .WithParam("$top", new ExactMatcher("30")) + .WithParam("semVerLevel", new ExactMatcher("2.0.0"))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody(@" + + Packages + " + MockServer.Value.Url + @"/nuget/internal-choco/Search()/ + 2024-11-19T13:38:35Z + + + " + MockServer.Value.Url + @"/nuget/internal-choco/Packages(Id='7zip',Version='23.1.0') + 7zip + 7-Zip is a file archiver with a high compression ratio. + 2024-02-06T14:48:14Z + + Igor Pavlov + + + + + + + 23.1.0 + 7-Zip + false + 7-Zip is a file archiver with a high compression ratio. + +## Features +- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression +- Supported formats: +- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM +- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z. +- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip +- Strong AES-256 encryption in 7z and ZIP formats +- Self-extracting capability for 7z format +- Integration with Windows Shell +- Powerful File Manager +- Powerful command line version +- Plugin for FAR Manager +- Localizations for 87 languages + +## Notes +- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it. +- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.** + http://www.7-zip.org/history.txt + 7-Zip is a file archiver with a high compression ratio. + http://www.7-zip.org/ + + https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg + + http://www.7-zip.org/license.txt + + 7zip zip archiver admin foss + 7zip.install:[23.1.0] + true + 2024-02-06T14:48:14.2600000Z + 2024-02-06T14:48:14.2600000Z + 3608 + hsYlJkAOzwVQ8+/hVjxaVkV6obzPflj9p3GJVX1B5KOGfKCOMZf0r/GuLYCFeNFXdQG0Og3zXAv6Sl5K+S54HQ== + true + true + true + false + false + 23.1.0 + true + SHA512 + false + false + 0 + 0 + + + + " + MockServer.Value.Url + @"/nuget/internal-choco/Packages(Id='7zip',Version='22.1') + 7zip + 7-Zip is a file archiver with a high compression ratio. + 2023-05-08T15:25:36Z + + Igor Pavlov + + + + + + + 22.1 + 7-Zip + false + 7-Zip is a file archiver with a high compression ratio. + +## Features +- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression +- Supported formats: +- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM +- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z. +- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip +- Strong AES-256 encryption in 7z and ZIP formats +- Self-extracting capability for 7z format +- Integration with Windows Shell +- Powerful File Manager +- Powerful command line version +- Plugin for FAR Manager +- Localizations for 87 languages + +## Notes +- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it. +- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.** + http://www.7-zip.org/history.txt + 7-Zip is a file archiver with a high compression ratio. + http://www.7-zip.org/ + + https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg + + http://www.7-zip.org/license.txt + + 7zip zip archiver admin foss + 7zip.install:[22.1] + true + 2023-05-08T15:25:36.1570000Z + 2023-05-08T15:25:36.1570000Z + 5112 + NX/wvBxlO66YVGIAnop8TkSxcIptt7on/33AfkudbP+u9cjgrsxV+YAZxK5yMD0hx8O0BJjjU3MVbv+70EO6lw== + false + false + true + false + false + 22.1 + true + SHA512 + false + false + 1 + 1 + + + + " + MockServer.Value.Url + @"/nuget/internal-choco/Packages(Id='7zip.install',Version='23.1.0') + 7zip.install + 7-Zip is a file archiver with a high compression ratio. + 2024-02-06T14:48:19Z + + Igor Pavlov + + + + + + + 23.1.0 + 7-Zip (Install) + false + 7-Zip is a file archiver with a high compression ratio. + +## Features + +- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression +- Supported formats: +- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM +- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z. +- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip +- Strong AES-256 encryption in 7z and ZIP formats +- Self-extracting capability for 7z format +- Integration with Windows Shell +- Powerful File Manager +- Powerful command line version +- Plugin for FAR Manager +- Localizations for 87 languages + +## Notes +- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it. +- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.** + [Software Changelog](http://www.7-zip.org/history.txt) +[Package Changelog](https://github.com/chocolatey-community/chocolatey-coreteampackages/blob/master/automatic/7zip.install/Changelog.md) + 7-Zip is a file archiver with a high compression ratio. + http://www.7-zip.org/ + + https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg + + http://www.7-zip.org/license.txt + + 7zip zip archiver admin cross-platform cli foss + chocolatey-core.extension:1.3.3 + true + 2024-02-06T14:48:19.4400000Z + 2024-02-06T14:48:19.4400000Z + 2867604 + fYqj9nY0NqGqYuyuov5AbdZ0JHC0hBwnNiBOPRNBOo1sFCOibU3ZYlBc1aleh6I3T0gCagUti9wQLl0bfAFyJg== + true + true + true + false + false + 23.1.0 + true + SHA512 + false + false + 0 + 0 + + + + " + MockServer.Value.Url + @"/nuget/internal-choco/Packages(Id='7zip.install',Version='22.1.0') + 7zip.install + 7-Zip is a file archiver with a high compression ratio. + 2023-05-08T15:25:39Z + + Igor Pavlov + + + + + + + 22.1.0 + 7-Zip (Install) + false + 7-Zip is a file archiver with a high compression ratio. + +## Features + +- High compression ratio in [7z format](http://www.7-zip.org/7z.html) with **LZMA** and **LZMA2** compression +- Supported formats: +- Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM +- Unpacking only: AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR and Z. +- For ZIP and GZIP formats, **7-Zip** provides a compression ratio that is 2-10 % better than the ratio provided by PKZip and WinZip +- Strong AES-256 encryption in 7z and ZIP formats +- Self-extracting capability for 7z format +- Integration with Windows Shell +- Powerful File Manager +- Powerful command line version +- Plugin for FAR Manager +- Localizations for 87 languages + +## Notes +- The installer for 7-Zip is known to close the Explorer process. This means you may lose current work. If it doesn't automatically restart explorer, type `explorer` on the command shell to restart it. +- **If the package is out of date please check [Version History](#versionhistory) for the latest submitted version. If you have a question, please ask it in [Chocolatey Community Package Discussions](https://github.com/chocolatey-community/chocolatey-packages/discussions) or raise an issue on the [Chocolatey Community Packages Repository](https://github.com/chocolatey-community/chocolatey-packages/issues) if you have problems with the package. Disqus comments will generally not be responded to.** + [Software Changelog](http://www.7-zip.org/history.txt) +[Package Changelog](https://github.com/chocolatey-community/chocolatey-coreteampackages/blob/master/automatic/7zip.install/Changelog.md) + 7-Zip is a file archiver with a high compression ratio. + http://www.7-zip.org/ + + https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-packages@68b91a851cee97e55c748521aa6da6211dd37c98/icons/7zip.svg + + http://www.7-zip.org/license.txt + + 7zip zip archiver admin cross-platform cli foss + chocolatey-core.extension:1.3.3 + true + 2023-05-08T15:25:39.9030000Z + 2023-05-08T15:25:39.9030000Z + 2842805 + KMGAfQdcR3Vmk8fun0XoOfH2ySi5Et18mhgvEHVpWKFVBERnxDyBvqmdl2zCdiScjgvrWD+0KWZdUZGxGGuEqg== + false + false + true + false + false + 22.1.0 + true + SHA512 + false + false + 1 + 1 + + +", destination: BodyDestinationFormat.SameAsSource)); + + Configuration.Sources = $"{MockServer.Value.Url}/nuget/internal-choco/v3/index.json"; + Configuration.Input = "7zip"; + Configuration.Version = "22.1.0"; + Configuration.SourceCommand.Username = "kim"; + Configuration.SourceCommand.Password = "P@ssword123"; + } + + public override void Because() + { + _result = NugetList.GetPackages(Configuration, Logger, FileSystem.Object).ToList(); + } + + [Fact] + public void Should_Have_Found_Two_Packages() + { + _result.Should().HaveCount(2); + } + + [InlineData("7zip", "22.1.0")] + [InlineData("7zip.install", "22.1.0")] + public void Should_Contain_Expected_Package(string id, string version) + { + _result.Should() + .ContainSingle(c => c.Identity.Id == id && c.Identity.Version.ToNormalizedString() == version); + } + + [InlineData("/nuget/internal-choco/v3/index.json")] + [InlineData("/nuget/internal-choco/")] + [InlineData("/nuget/internal-choco/$metadata")] + [InlineData("/nuget/internal-choco/Search()?$orderby=Id,Version desc&searchTerm='7zip'&targetFramework=''&includePrerelease=false&$skip=0&$top=30&semVerLevel=2.0.0")] + public void Should_Have_Called_Expected_Paths(string path) + { + MockServer.Value.Should() + .HaveReceivedACall() + .AtUrl(MockServer.Value.Url + path) + .And.UsingMethod("GET"); + } + } + } +} diff --git a/src/chocolatey.tests/chocolatey.tests.csproj b/src/chocolatey.tests/chocolatey.tests.csproj index c075719642..97e4b7d4f0 100644 --- a/src/chocolatey.tests/chocolatey.tests.csproj +++ b/src/chocolatey.tests/chocolatey.tests.csproj @@ -186,6 +186,7 @@ + diff --git a/src/chocolatey.tests/infrastructure.app/nuget/ChocolateyNugetCredentialProviderSpecs.cs b/src/chocolatey.tests/infrastructure.app/nuget/ChocolateyNugetCredentialProviderSpecs.cs new file mode 100644 index 0000000000..7805c03cfb --- /dev/null +++ b/src/chocolatey.tests/infrastructure.app/nuget/ChocolateyNugetCredentialProviderSpecs.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using chocolatey.infrastructure.app.configuration; +using chocolatey.infrastructure.app.nuget; +using FluentAssertions; +using Moq; +using NuGet.Configuration; +using NUnit.Framework; + +namespace chocolatey.tests.infrastructure.app.nuget +{ + public class ChocolateyNugetCredentialProviderSpecs + { + public abstract class ChocolateyNugetCredentialProviderSpecsBase : TinySpec + { + protected ChocolateyConfiguration Configuration; + protected ChocolateyNugetCredentialProvider Provider; + + protected const string Username = "user"; + protected const string Password = "totally_secure_password!!!"; + protected const string TargetSourceName = "testsource"; + protected const string TargetSourceUrl = "https://testserver.org/repository/test-repository"; + protected Uri TargetSourceUri = new Uri(TargetSourceUrl); + + protected NetworkCredential Result; + + private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); + protected CancellationToken CancellationToken + { + get + { + return _tokenSource.Token; + } + } + + public override void Context() + { + Configuration = new ChocolateyConfiguration(); + Provider = new ChocolateyNugetCredentialProvider(Configuration); + + Configuration.Information.IsInteractive = false; + Configuration.MachineSources = new List + { + new MachineSourceConfiguration + { + AllowSelfService = true, + VisibleToAdminsOnly = false, + EncryptedPassword = NugetEncryptionUtility.EncryptString("otherPassword"), + Username = "otherUserName", + Key = "https://someotherplace.com/repository/things/", + Name = "not-this-one", + Priority = 1, + }, + new MachineSourceConfiguration + { + AllowSelfService = true, + VisibleToAdminsOnly = false, + EncryptedPassword = NugetEncryptionUtility.EncryptString(Password), + Username = Username, + Key = TargetSourceUrl, + Name = TargetSourceName, + Priority = 1, + }, + }; + } + + public override void Because() + { + With().Wait(); + } + + public virtual async Task With() + { + var result = await Provider.GetAsync(TargetSourceUri, proxy: null, CredentialRequestType.Unauthorized, message: string.Empty, isRetry: false, nonInteractive: true, CancellationToken); + Result = result.Credentials as NetworkCredential; + } + } + + public class When_Using_Explicit_Credentials_And_Source_Param : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = Configuration.ExplicitSources = TargetSourceUrl; + Configuration.SourceCommand.Username = "user"; + Configuration.SourceCommand.Password = "totally_secure_password!!!"; + } + + [Fact] + public void Should_Create_Credentials() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be("user"); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be("totally_secure_password!!!"); + } + } + + public class When_A_Source_Name_Is_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = TargetSourceUrl; + Configuration.ExplicitSources = TargetSourceName; + } + + [Fact] + public void Should_Find_The_Saved_Source_And_Returns_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + public class When_A_Source_Url_Matching_A_Saved_Source_Is_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = Configuration.ExplicitSources = TargetSourceUrl; + } + + [Fact] + public void Should_Find_The_Saved_Source_And_Return_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + public class Looks_Up_Source_Url_When_Name_And_Credentials_Is_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = TargetSourceUrl; + Configuration.ExplicitSources = TargetSourceName; + + Configuration.SourceCommand.Username = "user"; + Configuration.SourceCommand.Password = "totally_secure_password!!!"; + } + + [Fact] + public void Should_Create_And_Return_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + public class When_No_Matching_Source_Is_Found_By_Url : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = Configuration.ExplicitSources = "https://unknownurl.com/api/v2/"; + } + + public override async Task With() + { + var result = await Provider.GetAsync(new Uri("https://unknownurl.com/api/v2/"), proxy: null, CredentialRequestType.Unauthorized, message: string.Empty, isRetry: false, nonInteractive: true, CancellationToken); + Result = result.Credentials as NetworkCredential; + } + + [Fact] + public void Should_Return_The_Default_Network_Credential() + { + Result.Should().Be(CredentialCache.DefaultNetworkCredentials); + } + } + + public class When_Multiple_Named_Sources_Are_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = Configuration.MachineSources.Select(s => s.Key).Join(";"); + Configuration.ExplicitSources = Configuration.MachineSources.Select(s => s.Name).Join(";"); + } + + [Fact] + public void Should_Find_The_Correct_Saved_Source_For_The_Target_Uri_And_Return_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + public class When_Multiple_Source_Urls_Are_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = Configuration.ExplicitSources = $"https://unknownurl.com/api/v2/;{TargetSourceUrl}"; + } + + [Fact] + public void Should_Find_The_Saved_Source_And_Return_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + public class When_A_Mix_Of_Named_And_Url_Sources_Are_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + public override void Context() + { + base.Context(); + Configuration.Sources = $"https://unknownurl.com/api/v2/;{TargetSourceUrl}"; + Configuration.ExplicitSources = $"https://unknownurl.com/api/v2/;{TargetSourceName}"; + } + + [Fact] + public void Should_Find_The_Saved_Source_And_Return_The_Credential() + { + Result.Should().NotBeNull(); + } + + [Fact] + public void Should_Provide_The_Correct_Username() + { + Result.UserName.Should().Be(Username); + } + + [Fact] + public void Should_Provide_The_Correct_Password() + { + Result.Password.Should().Be(Password); + } + } + + // This is a regression test for issue #3565 + public class When_A_Url_Matching_The_Hostname_Only_Of_A_Saved_Source_Is_Provided : ChocolateyNugetCredentialProviderSpecsBase + { + private Uri _otherRepoUri; + public override void Context() + { + base.Context(); + _otherRepoUri = new Uri(TargetSourceUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped) + "/other_path/repository/"); + Configuration.Sources = Configuration.ExplicitSources = _otherRepoUri.AbsoluteUri; + } + + public override async Task With() + { + var result = await Provider.GetAsync(new Uri("https://unknownurl.com/api/v2/"), proxy: null, CredentialRequestType.Unauthorized, message: string.Empty, isRetry: false, nonInteractive: true, CancellationToken); + Result = result.Credentials as NetworkCredential; + } + + [Fact] + public void Should_Return_The_Default_Network_Credential() + { + Result.Should().Be(CredentialCache.DefaultNetworkCredentials); + } + } + } +} diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs index ea4b44c90d..d7ee4e97e0 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs @@ -38,7 +38,7 @@ public override void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConf .Add( "s=|source=", "Source - Source location for install. Can use special 'windowsfeatures', 'ruby', 'cygwin', or 'python' sources. Defaults to configured sources.", - option => configuration.Sources = option.UnquoteSafe()) + option => configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe()) .Add( "l|lo|localonly|local-only", "LocalOnly - Only search against local machine items.", diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs index 45bf7cf207..2ca1c0c5ad 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs @@ -53,7 +53,7 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi optionSet .Add("s=|source=", "Source - The source to find the package(s) to install. Special sources include: ruby, cygwin, windowsfeatures, and python. To specify more than one source, pass it with a semi-colon separating the values (e.g. \"'source1;source2'\"). Defaults to default feeds.", - option => configuration.Sources = option.UnquoteSafe()) + option => configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe()) .Add("version=", "Version - A specific version to install. Defaults to unspecified.", option => configuration.Version = option.UnquoteSafe()) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index ff65fa62b6..a7cd594683 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -85,7 +85,7 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi "Source - Name of alternative source to use, for example 'windowsfeatures', 'ruby', 'cygwin', or 'python'.", option => { - configuration.Sources = option.UnquoteSafe(); + configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe(); configuration.ListCommand.ExplicitSource = true; }) .Add("idonly|id-only", @@ -252,7 +252,7 @@ public virtual void Run(ChocolateyConfiguration config) { // Would have liked to have done this in the Validate method, but can't, since the SourceType // hasn't yet been set, since the sources have not yet been parsed. - if (config.ListCommand.ExplicitSource && config.SourceType == SourceTypes.Normal) + if (!string.IsNullOrWhiteSpace(config.ExplicitSources) && config.SourceType == SourceTypes.Normal) { throw new ApplicationException("When using the '--source' option with the 'choco list' command, only a named alternative source can be provided."); } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs index aff0f25614..6c6b46f426 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs @@ -40,7 +40,7 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi optionSet .Add("s=|source=", "Source - The source to find the package(s) to install. Special sources include: ruby, cygwin, windowsfeatures, and python. To specify more than one source, pass it with a semi-colon separating the values (e.g. \"'source1;source2'\"). Defaults to default feeds.", - option => configuration.Sources = option.UnquoteSafe()) + option => configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe()) .Add("u=|user=", "User - used with authenticated feeds. Defaults to empty.", option => configuration.SourceCommand.Username = option.UnquoteSafe()) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySearchCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySearchCommand.cs index 2bf9d85959..34acb867ec 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateySearchCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateySearchCommand.cs @@ -43,7 +43,7 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi optionSet .Add("s=|source=", "Source - Source location for install. Can use special 'windowsfeatures', 'ruby', 'cygwin', or 'python' sources. Defaults to sources.", - option => configuration.Sources = option.UnquoteSafe()) + option => configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe()) .Add("idonly|id-only", "Id Only - Only return Package Ids in the list results.", option => configuration.ListCommand.IdOnly = option != null) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs index 03569df162..04206f592f 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs @@ -53,7 +53,7 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi optionSet .Add("s=|source=", "Source - The source to find the package(s) to install. Special sources include: ruby, cygwin, windowsfeatures, and python. To specify more than one source, pass it with a semi-colon separating the values (e.g. \"'source1;source2'\"). Defaults to default feeds.", - option => configuration.Sources = option.UnquoteSafe()) + option => configuration.Sources = configuration.ExplicitSources = option.UnquoteSafe()) .Add("version=", "Version - A specific version to install. Defaults to unspecified.", option => configuration.Version = option.UnquoteSafe()) diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 1c4ea87af6..9954353c49 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -262,6 +262,12 @@ private void AppendOutput(StringBuilder propertyValues, string append) /// public string Sources { get; set; } + /// + /// One or more source locations set by comamnd line only. Semi-colon delimited. + /// Do not set this anywhere other than parsing CLI arguments for commands. + /// + public string ExplicitSources { get; set; } + public string SourceType { get; set; } public bool IncludeConfiguredSources { get; set; } @@ -597,6 +603,7 @@ public ListCommandConfiguration() public bool NotBroken { get; set; } public bool IncludeVersionOverrides { get; set; } public bool ExplicitPageSize { get; set; } + [Obsolete("This property is deprecated and will be removed in v3. Check if the top-level ExplicitSources property is set instead.")] public bool ExplicitSource { get; set; } } diff --git a/src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs b/src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs index 2c7e4ee692..ac17ac30a5 100644 --- a/src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs +++ b/src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs @@ -47,7 +47,7 @@ public ChocolateyNugetCredentialProvider(ChocolateyConfiguration config) public Task GetAsync(Uri uri, IWebProxy proxy, CredentialRequestType credentialType, string message, bool isRetry, bool nonInteractive, CancellationToken cancellationToken) { - if (uri == null) + if (uri is null) { throw new ArgumentNullException("uri"); } @@ -86,59 +86,60 @@ public Task GetAsync(Uri uri, IWebProxy proxy, CredentialReq } // credentials were not explicit - // discover based on closest match in sources - var candidateSources = _config.MachineSources.Where( - s => - { - var sourceUrl = s.Key.TrimEnd('/'); - - try - { - var sourceUri = new Uri(sourceUrl); - return sourceUri.Host.IsEqualTo(uri.Host) - && !string.IsNullOrWhiteSpace(s.Username) - && !string.IsNullOrWhiteSpace(s.EncryptedPassword); - } - catch (Exception) - { - this.Log().Error("Source '{0}' is not a valid Uri".FormatWith(sourceUrl)); - } - - return false; - }).ToList(); - + // find matching source(s) in sources list + var trimmedTargetUri = new Uri(uri.AbsoluteUri.TrimEnd('/')); MachineSourceConfiguration source = null; + // If the user has specified --source with a *named* source and not a URL, try to find the matching one + // with the correct URL for this credential request. + // Lower case all of the explicitly named sources so that we can use .Contains to compare them. + var namedExplicitSources = _config.ExplicitSources?.ToLower().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Where(s => !Uri.IsWellFormedUriString(s, UriKind.Absolute)) + .ToList(); - if (candidateSources.Count == 1) + if (namedExplicitSources?.Count > 0) { - // only one match, use it - source = candidateSources.FirstOrDefault(); + // Instead of using Uri.Equals(), we're using Uri.Compare() on the HttpRequestUrl components as this allows + // us to ignore the case of everything. + source = _config.MachineSources + .Where(s => namedExplicitSources.Contains(s.Name.ToLower()) + && Uri.TryCreate(s.Key.TrimEnd('/'), UriKind.Absolute, out var trimmedSourceUri) + && Uri.Compare(trimmedSourceUri, trimmedTargetUri, UriComponents.HttpRequestUrl, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0) + .FirstOrDefault(); } - else if (candidateSources.Count > 1) + + if (source is null) { - // find the source that is the closest match - foreach (var candidateSource in candidateSources.OrEmpty()) + // Could not find a valid source by name, or the source(s) specified were all URLs. + // Try to look up the target URL in the saved machine sources to attempt to match credentials. + // + // Note: This behaviour remains as removing it would be a breaking change, but we may want + // to remove this in a future version, as specifying an explicit URL should potentially + // not go looking in the configuration file for saved credentials anyway. + // See GitHub Issue: https://github.com/chocolatey/choco/issues/3573 + var candidateSources = _config.MachineSources + .Where(s => !string.IsNullOrWhiteSpace(s.Username) + && !string.IsNullOrWhiteSpace(s.EncryptedPassword) + && Uri.TryCreate(s.Key.TrimEnd('/'), UriKind.Absolute, out var trimmedSourceUri) + && Uri.Compare(trimmedSourceUri, trimmedTargetUri, UriComponents.HttpRequestUrl, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0) + .ToList(); + + if (candidateSources.Count == 1) { - var candidateRegEx = new Regex(Regex.Escape(candidateSource.Key.TrimEnd('/')), RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - if (candidateRegEx.IsMatch(uri.OriginalString.TrimEnd('/'))) - { - this.Log().Debug("Source selected will be '{0}'".FormatWith(candidateSource.Key.TrimEnd('/'))); - source = candidateSource; - break; - } + // only one match, use it + source = candidateSources.First(); } - - if (source == null && !isRetry) + else if (candidateSources.Count > 1 && !isRetry) { - // use the first source. If it fails, fall back to grabbing credentials from the user + // Use the credentials from the first found source, unless it's a retry (creds already tried and failed) + // use the first source. If it fails, fall back to grabbing credentials from the user. var candidateSource = candidateSources.First(); this.Log().Debug("Evaluated {0} candidate sources but was unable to find a match, using {1}".FormatWith(candidateSources.Count, candidateSource.Key.TrimEnd('/'))); source = candidateSource; } } - if (source == null) + if (source is null) { ICredentials credential = CredentialCache.DefaultNetworkCredentials; @@ -146,7 +147,6 @@ public Task GetAsync(Uri uri, IWebProxy proxy, CredentialReq { this.Log().Debug("This is a retry attempt. Asking user for credentials for '{0}'".FormatWith(uri.OriginalString)); credential = GetUserCredentials(uri, proxy, credentialType); - } return Task.FromResult(new CredentialResponse(credential)); diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index 595c403120..52d5c2d132 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -203,6 +203,27 @@ private async static Task> SearchPackagesAsyn } } } + else if (version != null) + { + // We need to look up any packages that do not have a matching version number. + + foreach (var package in latestResults) + { + if (package.Identity.Version != version) + { + var result = FindPackage(package.Identity.Id, configuration, nugetLogger, (SourceCacheContext)cacheContext, new[] { repositoryResources }, version); + + if (result != null) + { + results.Add(result); + } + } + else + { + results.Add(package); + } + } + } else { results.AddRange(latestResults); diff --git a/tests/helpers/common/Chocolatey/Disable-ChocolateySource.ps1 b/tests/helpers/common/Chocolatey/Disable-ChocolateySource.ps1 index 523839f5c9..58cb264f53 100644 --- a/tests/helpers/common/Chocolatey/Disable-ChocolateySource.ps1 +++ b/tests/helpers/common/Chocolatey/Disable-ChocolateySource.ps1 @@ -8,10 +8,8 @@ function Disable-ChocolateySource { [Parameter()] [switch]$All ) - # Significantly weird behaviour with piping this source list by property name. - $CurrentSources = (Invoke-Choco source list -r).Lines | ConvertFrom-ChocolateyOutput -Command SourceList | Where-Object { - $_.Name -like $Name - } + + $CurrentSources = Get-ChocolateySource -Name $Name foreach ($Source in $CurrentSources) { $null = Invoke-Choco source disable --name $Source.Name } diff --git a/tests/helpers/common/Chocolatey/Enable-ChocolateySource.ps1 b/tests/helpers/common/Chocolatey/Enable-ChocolateySource.ps1 index 6a44cd184e..6f6a2f1abd 100644 --- a/tests/helpers/common/Chocolatey/Enable-ChocolateySource.ps1 +++ b/tests/helpers/common/Chocolatey/Enable-ChocolateySource.ps1 @@ -9,9 +9,7 @@ function Enable-ChocolateySource { [switch]$All ) # Significantly weird behaviour with piping this source list by property name. - $CurrentSources = (Invoke-Choco source list -r).Lines | ConvertFrom-ChocolateyOutput -Command SourceList | Where-Object { - $_.Name -like $Name - } + $CurrentSources = Get-ChocolateySource -Name $Name foreach ($Source in $CurrentSources) { $null = Invoke-Choco source enable --name $Source.Name } diff --git a/tests/helpers/common/Chocolatey/Get-ChocolateySource.ps1 b/tests/helpers/common/Chocolatey/Get-ChocolateySource.ps1 new file mode 100644 index 0000000000..7b097500a9 --- /dev/null +++ b/tests/helpers/common/Chocolatey/Get-ChocolateySource.ps1 @@ -0,0 +1,11 @@ +function Get-ChocolateySource { + [CmdletBinding()] + param( + [Parameter()] + [string]$Name = "*" + ) + # Significantly weird behaviour with piping this source list by property name. + (Invoke-Choco source list -r).Lines | ConvertFrom-ChocolateyOutput -Command SourceList | Where-Object { + $_.Name -like $Name + } +} diff --git a/tests/pester-tests/commands/choco-push.Tests.ps1 b/tests/pester-tests/commands/choco-push.Tests.ps1 index 63a2a002f6..8525bd9706 100644 --- a/tests/pester-tests/commands/choco-push.Tests.ps1 +++ b/tests/pester-tests/commands/choco-push.Tests.ps1 @@ -152,6 +152,9 @@ Describe 'choco push nuget <_> repository' -Tag Chocolatey, PushCommand -Skip:($ if ($UseConfig) { $null = Invoke-Choco apikey add --source $RepositoryToUse$RepositoryEndpoint --api-key $ApiKey + # Add the Nuget source so that the push doesn't prompt for credentials. + # See https://github.com/chocolatey/choco/issues/2026#issuecomment-2423828013 + $null = Invoke-Choco source add --name temporary-nuget --source $RepositoryToUse$RepositoryEndpoint --user $env:NUGET_SOURCE_USERNAME --password $env:NUGET_SOURCE_PASSWORD # Ensure the key is null (should always be, but scoping can be wonky) $KeyParameter = $null } else { diff --git a/tests/pester-tests/features/ArgumentsDecryption.Tests.ps1 b/tests/pester-tests/features/ArgumentsDecryption.Tests.ps1 index d4fcca8654..95d7c8ed77 100644 --- a/tests/pester-tests/features/ArgumentsDecryption.Tests.ps1 +++ b/tests/pester-tests/features/ArgumentsDecryption.Tests.ps1 @@ -97,6 +97,8 @@ Invoke-Choco install upgradepackage --version 1.0.0 $argumentsFile = Join-Path $env:ChocolateyInstall ".chocolatey/upgradepackage.1.0.0/.arguments" $FileContents | Set-Content -Path $argumentsFile -Encoding utf8 -Force + # Remove the `download` directory so the download command doesn't fail the second test. + Remove-Item -Path $PWD/download -Recurse -Force -ErrorAction SilentlyContinue $Output = Invoke-Choco $Command @Parameters --debug } diff --git a/tests/pester-tests/features/CredentialProvider.Tests.ps1 b/tests/pester-tests/features/CredentialProvider.Tests.ps1 new file mode 100644 index 0000000000..5ab3887fc6 --- /dev/null +++ b/tests/pester-tests/features/CredentialProvider.Tests.ps1 @@ -0,0 +1,70 @@ +# These tests are to ensure that credentials from one configured and enabled source are not +# picked up and used when a URL is matching based on the hostname. These tests use an authenticated +# source without explicitly providing a username/password. It is expected that Chocolatey will prompt for +# the username and password. +Describe 'Ensuring credentials do not bleed from configured sources' -Tag CredentialProvider -ForEach @( + # Info and outdated are returning 0 in all test cases we've thrown at them. + # Suspect the only way either of these commands actually return non-zero is in a scenario where + # something goes catastrophically wrong outside of the actual command calls. + @{ + Command = 'info' + ExitCode = 0 + } + @{ + Command = 'outdated' + ExitCode = 0 + } + @{ + Command = 'install' + ExitCode = 1 + } + @{ + Command = 'search' + ExitCode = 1 + } + @{ + Command = 'upgrade' + ExitCode = 1 + } + @{ + Command = 'download' + ExitCode = 1 + } +) { + BeforeDiscovery { + $HasLicensedExtension = Test-PackageIsEqualOrHigher -PackageName 'chocolatey.extension' -Version '6.0.0' + } + + BeforeAll { + Initialize-ChocolateyTestInstall + Disable-ChocolateySource -All + Enable-ChocolateySource -Name 'hermes' + $SetupSource = Get-ChocolateySource -Name 'hermes-setup' + Remove-Item download -force -recurse + } + + # Skip the download command if chocolatey.extension is not installed. + Context 'Command ()' -Skip:($Command -eq 'download' -and -not $HasLicensedExtension) { + BeforeAll { + # The package used ultimately doesn't matter as we don't expect to find it. + # Picking a package that should be found if the behaviour changes. + $PackageUnderTest = 'chocolatey-compatibility.extension' + Restore-ChocolateyInstallSnapshot + # Chocolatey will prompt for credentials, we need to force something in there, and this will do that. + $Output = 'n' | Invoke-Choco $Command $PackageUnderTest --confirm --source="'$($SetupSource.Url)'" + } + + AfterAll { + Remove-ChocolateyInstallSnapshot + } + + It 'Exits Correctly ()' { + $Output.ExitCode | Should -Be $ExitCode -Because $Output.String + } + + It 'Outputs error message' { + $FilteredOutput = $Output.Lines -match "Failed to fetch results from V2 feed at '$($SetupSource.Url.Trim('/'))" + $FilteredOutput.Count | Should -BeGreaterOrEqual 1 -Because $Output.String + } + } +} diff --git a/tests/pester-tests/features/UserAgent.Tests.ps1 b/tests/pester-tests/features/UserAgent.Tests.ps1 index 1b3f433741..543f188aa5 100644 --- a/tests/pester-tests/features/UserAgent.Tests.ps1 +++ b/tests/pester-tests/features/UserAgent.Tests.ps1 @@ -72,10 +72,17 @@ Describe "Chocolatey User Agent" -Tag Chocolatey, UserAgent { $userAgent = $matches['UserAgent'] - $userAgent -match 'Chocolatey Command Line/(?[a-z0-9.-]+) ([a-z ]+/([a-z0-9.-]+) )?\((?[^,)]+)(?:, (?[^)]+))?\) via NuGet Client' | + $userAgent -match 'Chocolatey Command Line/(?[a-z0-9.-]+) ([a-z ]+/(?[a-z0-9.-]+) )?\((?[^,)]+)(?:, (?[^)]+))?\) via NuGet Client' | Should -BeTrue -Because "the user agent string should contain the choco.exe version, the licensed extension version if any, and any parent processes. $logLine" $matches['Version'] | Should -Be $ChocolateyVersion -Because "the user agent string should contain the currently running Chocolatey version. $logLine" + + if (Test-PackageIsEqualOrHigher -PackageName 'chocolatey.extension' -Version '6.3.0-alpha') { + # We are not asserting the Licensed Extension version here as the Chocolatey package version often + # mismatches the assembly version. + $matches['LicensedVersion'] | Should -Not -BeNullOrEmpty -Because "Chocolatey Licensed Extension is installed and should be in the user agent. $logLine" + } + $filteredProcesses = @($Processes | Where-Object { $_ -notin $ExcludedProcesses }) if ($filteredProcesses.Count -gt 1) {