├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── deploy.yml │ └── keep-alive.yml ├── .gitignore ├── Directory.Build.props ├── Directory.Packages.props ├── Invoke-DownloadPackages.ps1 ├── LICENSE ├── NuGet.Config ├── NuGetTools.sln ├── README.md ├── global.json ├── latest-versions.json ├── spelling.dic ├── src ├── Knapcode.NuGetTools.Logic.Direct │ ├── AlignedVersionsDownloader.cs │ ├── ConsoleLogger.cs │ ├── Constants.cs │ ├── FrameworkEnumerator.cs │ ├── IAlignedVersionsDownloader.cs │ ├── IPackageRangeDownloader.cs │ ├── InMemorySettingSection.cs │ ├── InMemorySettings.cs │ ├── Knapcode.NuGetTools.Logic.Direct.csproj │ ├── MicrosoftLogger.cs │ ├── NuGetSettings.cs │ ├── PackageRangeDownloader.cs │ └── VersionedToolsFactory.cs ├── Knapcode.NuGetTools.Logic.NuGet2x │ ├── Framework2x.cs │ ├── FrameworkLogic2x.cs │ ├── Knapcode.NuGetTools.Logic.NuGet2x.csproj │ ├── NuGetLogic2x.cs │ ├── Version2x.cs │ ├── VersionLogic2x.cs │ ├── VersionRange2x.cs │ └── VersionRangeLogic2x.cs ├── Knapcode.NuGetTools.Logic.NuGet3x │ ├── Framework3x.cs │ ├── FrameworkLogic3x.cs │ ├── Knapcode.NuGetTools.Logic.NuGet3x.csproj │ ├── NuGetLogic3x.cs │ ├── Version3x.cs │ ├── VersionLogic3x.cs │ ├── VersionRange3x.cs │ └── VersionRangeLogic3x.cs ├── Knapcode.NuGetTools.Logic │ ├── DirectToolsFactory.cs │ ├── FrameworkEnumerationOptions.cs │ ├── FrameworkEnumeratorData.cs │ ├── FrameworkExpansionOptions.cs │ ├── FrameworkList.cs │ ├── FrameworkListItem.cs │ ├── FrameworkPrecedenceService.cs │ ├── IFrameworkEnumerator.cs │ ├── IFrameworkList.cs │ ├── IFrameworkPrecedenceService.cs │ ├── IToolsFactory.cs │ ├── IToolsService.cs │ ├── IVersionedService.cs │ ├── Knapcode.NuGetTools.Logic.csproj │ ├── Models │ │ ├── ComparisonResult.cs │ │ ├── Framework │ │ │ ├── FrameworkCompatibilityInput.cs │ │ │ ├── FrameworkCompatibilityOutput.cs │ │ │ ├── FrameworkPrecedenceInput.cs │ │ │ ├── FrameworkPrecedenceOutput.cs │ │ │ ├── GetNearestFrameworkInput.cs │ │ │ ├── GetNearestFrameworkOutput.cs │ │ │ ├── OutputFramework.cs │ │ │ ├── ParseFrameworkInput.cs │ │ │ └── ParseFrameworkOutput.cs │ │ ├── InputStatus.cs │ │ ├── Version │ │ │ ├── ParseVersionInput.cs │ │ │ ├── ParseVersionOutput.cs │ │ │ ├── SortVersionsInput.cs │ │ │ ├── SortVersionsOutput.cs │ │ │ ├── VersionComparisonInput.cs │ │ │ └── VersionComparisonOutput.cs │ │ └── VersionRange │ │ │ ├── FindBestVersionMatchInput.cs │ │ │ ├── FindBestVersionMatchOutput.cs │ │ │ ├── OutputVersion.cs │ │ │ ├── ParseVersionRangeInput.cs │ │ │ ├── ParseVersionRangeOutput.cs │ │ │ ├── VersionSatisfiesInput.cs │ │ │ └── VersionSatisfiesOutput.cs │ ├── NuGetRelease.cs │ ├── ToolsService.cs │ └── Wrappers │ │ ├── IFramework.cs │ │ ├── IFrameworkLogic.cs │ │ ├── INuGetLogic.cs │ │ ├── IVersion.cs │ │ ├── IVersionLogic.cs │ │ ├── IVersionRange.cs │ │ ├── IVersionRangeLogic.cs │ │ ├── NuGetAssembly.cs │ │ ├── NuGetPackage.cs │ │ └── VersionExtensions.cs ├── Knapcode.NuGetTools.PackageDownloader │ ├── Knapcode.NuGetTools.PackageDownloader.csproj │ ├── Program.cs │ └── Properties │ │ └── launchSettings.json ├── Knapcode.NuGetTools.Sandbox │ ├── Knapcode.NuGetTools.Sandbox.csproj │ └── Program.cs └── Knapcode.NuGetTools.Website │ ├── Configuration.cs │ ├── Controllers │ ├── FrameworksController.cs │ └── HomeController.cs │ ├── Extensions │ └── ExtensionMethods.cs │ ├── Knapcode.NuGetTools.Website.csproj │ ├── Models │ ├── SelectedVersionOutput.cs │ ├── SelectedVersionOutputT.cs │ └── VersionUrl.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── ServiceCollectionExtensions.cs │ ├── TagHelpers │ ├── ActionTagHelper.cs │ ├── ParseFrameworkTagHelper.cs │ ├── ParseVersionRangeTagHelper.cs │ └── ParseVersionTagHelper.cs │ ├── Telemetry │ └── TelemetryProcessor.cs │ ├── Views │ ├── Home │ │ ├── FindBestVersionMatch.cshtml │ │ ├── FrameworkCompatibility.cshtml │ │ ├── FrameworkPrecedence.cshtml │ │ ├── GetNearestFramework.cshtml │ │ ├── PackageMetadata.cshtml │ │ ├── ParseFramework.cshtml │ │ ├── ParseVersion.cshtml │ │ ├── ParseVersionRange.cshtml │ │ ├── SelectedVersionIndex.cshtml │ │ ├── SortVersions.cshtml │ │ ├── VersionComparison.cshtml │ │ └── VersionSatisfies.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.json │ ├── web.config │ └── wwwroot │ ├── _references.js │ ├── css │ └── site.css │ ├── favicon.ico │ ├── js │ └── site.js │ ├── lib │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ ├── bootstrap3-typeahead │ │ ├── bootstrap3-typeahead.js │ │ └── bootstrap3-typeahead.min.js │ ├── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ │ ├── jquery.js │ │ │ ├── jquery.min.js │ │ │ └── jquery.min.map │ └── typeahead.js │ │ ├── LICENSE │ │ └── dist │ │ ├── bloodhound.js │ │ └── bloodhound.min.js │ └── robots.txt └── test └── Knapcode.NuGetTools.Website.Test ├── IntegrationTest.cs ├── Knapcode.NuGetTools.Website.Test.csproj ├── Logic └── ToolsServiceTest.cs ├── TestServerFixture.cs └── xunit.runner.json /.editorconfig: -------------------------------------------------------------------------------- 1 | ; EditorConfig to support per-solution formatting. 2 | ; Use the EditorConfig VS add-in to make this work. 3 | ; http://editorconfig.org/ 4 | 5 | ; This is the default for the codeline. 6 | root = true 7 | 8 | [*] 9 | ; Don't use tabs for indentation. 10 | indent_style = space 11 | ; (Please don't specify an indent_size here; that has too many unintended consequences.) 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | # Spell checker configuration 17 | spelling_exclusion_path = spelling.dic 18 | 19 | ; Code files 20 | [*.{cs}] 21 | indent_size = 4 22 | 23 | ; All XML-based file formats 24 | [*.{config,csproj,nuspec,props,resx,ruleset,targets,vsct,vsixmanifest,xaml,xml,vsmanproj,swixproj}] 25 | indent_size = 2 26 | 27 | ; JSON files 28 | [*.json] 29 | indent_size = 2 30 | 31 | ; PowerShell scripts 32 | [*.{ps1}] 33 | indent_size = 4 34 | 35 | [*.{sh}] 36 | indent_size = 4 37 | 38 | ; Dotnet code style settings 39 | [*.{cs,vb}] 40 | ; Sort using and Import directives with System.* appearing first 41 | dotnet_sort_system_directives_first = true 42 | dotnet_separate_import_directive_groups = false 43 | 44 | ; IDE0003 Avoid "this." and "Me." if not necessary 45 | dotnet_style_qualification_for_field = false:warning 46 | dotnet_style_qualification_for_property = false:warning 47 | dotnet_style_qualification_for_method = false:warning 48 | dotnet_style_qualification_for_event = false:warning 49 | 50 | ; IDE0012 Use language keywords instead of framework type names for type references 51 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 52 | ; IDE0013 53 | dotnet_style_predefined_type_for_member_access = true:warning 54 | 55 | ; Suggest more modern language features when available 56 | dotnet_style_object_initializer = true:suggestion 57 | dotnet_style_collection_initializer = true:suggestion 58 | dotnet_style_explicit_tuple_names = true:suggestion 59 | dotnet_style_coalesce_expression = true:suggestion 60 | dotnet_style_null_propagation = true:suggestion 61 | 62 | ; CSharp code style settings 63 | [*.cs] 64 | ; IDE0007 'var' preferences 65 | csharp_style_var_for_built_in_types = true:none 66 | csharp_style_var_when_type_is_apparent = true:none 67 | csharp_style_var_elsewhere = false:none 68 | 69 | ; Prefer method-like constructs to have a block body 70 | csharp_style_expression_bodied_methods = false:none 71 | csharp_style_expression_bodied_constructors = false:none 72 | csharp_style_expression_bodied_operators = false:none 73 | 74 | ; Prefer property-like constructs to have an expression-body 75 | csharp_style_expression_bodied_properties = true:suggestion 76 | csharp_style_expression_bodied_indexers = true:suggestion 77 | csharp_style_expression_bodied_accessors = true:suggestion 78 | 79 | ; Suggest more modern language features when available 80 | csharp_style_pattern_matching_over_is_with_cast_check = true:none 81 | csharp_style_pattern_matching_over_as_with_null_check = true:none 82 | csharp_style_inlined_variable_declaration = true:none 83 | csharp_style_throw_expression = true:none 84 | csharp_style_conditional_delegate_call = true:suggestion 85 | 86 | ; Newline settings 87 | csharp_new_line_before_open_brace = all 88 | csharp_new_line_before_else = true 89 | csharp_new_line_before_catch = true 90 | csharp_new_line_before_finally = true 91 | csharp_new_line_before_members_in_object_initializers = true 92 | csharp_new_line_before_members_in_anonymous_types = true 93 | 94 | ; Naming styles 95 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 96 | dotnet_naming_style.camel_case_style.capitalization = camel_case 97 | 98 | ; Naming rule: async methods end in Async 99 | dotnet_naming_style.async_method_style.capitalization = pascal_case 100 | dotnet_naming_style.async_method_style.required_suffix = Async 101 | dotnet_naming_symbols.async_method_symbols.applicable_kinds = method 102 | dotnet_naming_symbols.async_method_symbols.required_modifiers = async 103 | dotnet_naming_rule.async_methods_rule.severity = suggestion 104 | dotnet_naming_rule.async_methods_rule.symbols = async_method_symbols 105 | dotnet_naming_rule.async_methods_rule.style = async_method_style 106 | 107 | ; Naming rule: Interfaces must be pascal-cased prefixed with I 108 | dotnet_naming_style.interface_style.capitalization = pascal_case 109 | dotnet_naming_style.interface_style.required_prefix = I 110 | dotnet_naming_symbols.interface_symbols.applicable_kinds = interface 111 | dotnet_naming_symbols.interface_symbols.applicable_accessibilities = * 112 | dotnet_naming_rule.interfaces_rule.severity = warning 113 | dotnet_naming_rule.interfaces_rule.symbols = interface_symbols 114 | dotnet_naming_rule.interfaces_rule.style = interface_style 115 | 116 | ; Naming rule: All methods and properties must be pascal-cased 117 | dotnet_naming_symbols.method_and_property_symbols.applicable_kinds = method,property,class,struct,enum:property,namespace 118 | dotnet_naming_symbols.method_and_property_symbols.applicable_accessibilities = * 119 | dotnet_naming_rule.methods_and_properties_rule.severity = warning 120 | dotnet_naming_rule.methods_and_properties_rule.symbols = method_and_property_symbols 121 | dotnet_naming_rule.methods_and_properties_rule.style = pascal_case_style 122 | 123 | ; Naming rule: Static fields must be pascal-cased 124 | dotnet_naming_symbols.static_member_symbols.applicable_kinds = field 125 | dotnet_naming_symbols.static_member_symbols.applicable_accessibilities = * 126 | dotnet_naming_symbols.static_member_symbols.required_modifiers = static 127 | dotnet_naming_symbols.const_member_symbols.applicable_kinds = field 128 | dotnet_naming_symbols.const_member_symbols.applicable_accessibilities = * 129 | dotnet_naming_symbols.const_member_symbols.required_modifiers = const 130 | dotnet_naming_rule.static_fields_rule.severity = warning 131 | dotnet_naming_rule.static_fields_rule.symbols = static_member_symbols 132 | dotnet_naming_rule.static_fields_rule.style = pascal_case_style 133 | 134 | ; Naming rule: Private members must be camel-cased and prefixed with underscore 135 | dotnet_naming_style.private_member_style.capitalization = camel_case 136 | dotnet_naming_style.private_member_style.required_prefix = _ 137 | dotnet_naming_symbols.private_field_symbols.applicable_kinds = field,event 138 | dotnet_naming_symbols.private_field_symbols.applicable_accessibilities = private,protected,internal 139 | dotnet_naming_rule.private_field_rule.severity = warning 140 | dotnet_naming_rule.private_field_rule.symbols = private_field_symbols 141 | dotnet_naming_rule.private_field_rule.style = private_member_style 142 | 143 | ; Diagnostics rule: Don't leave unnecessary suppressions 144 | dotnet_diagnostic.IDE0076.severity = warning 145 | dotnet_diagnostic.IDE0005.severity = warning 146 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | groups: 8 | actions-minor: 9 | update-types: 10 | - minor 11 | - patch 12 | 13 | - package-ecosystem: nuget 14 | directory: / 15 | schedule: 16 | interval: weekly 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | 3 | on: 4 | pull_request: 5 | push: 6 | workflow_dispatch: 7 | 8 | env: 9 | PRODUCTION_URL: https://nugettools.azurewebsites.net 10 | PACKAGES_DIR: ./src/Knapcode.NuGetTools.Website/packages 11 | PUBLISH_DIR: ./src/Knapcode.NuGetTools.Website/bin/publish 12 | ZIP_PATH: ./src/Knapcode.NuGetTools.Website/bin/website.zip 13 | 14 | jobs: 15 | build: 16 | runs-on: windows-latest 17 | defaults: 18 | run: 19 | shell: pwsh 20 | outputs: 21 | versions-changed: ${{ steps.check-versions.outputs.versions-changed }} 22 | 23 | steps: 24 | - name: Check out 25 | uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Set up .NET 30 | uses: actions/setup-dotnet@v4 31 | with: 32 | global-json-file: global.json 33 | 34 | - name: Restore dependencies 35 | run: dotnet restore 36 | 37 | - name: Build 38 | run: dotnet build --configuration Release --no-restore 39 | 40 | - name: Download NuGet.* packages 41 | run: ./Invoke-DownloadPackages.ps1 -PackagesDir $env:PACKAGES_DIR -Force 42 | 43 | - name: Test 44 | run: dotnet test --logger "console;verbosity=normal" --configuration Release --no-build 45 | 46 | - name: Publish 47 | run: dotnet publish ./src/Knapcode.NuGetTools.Website --output $env:PUBLISH_DIR --configuration Release --no-build 48 | 49 | - name: Zip publish directory 50 | run: Compress-Archive -Path $env:PUBLISH_DIR/* -DestinationPath $env:ZIP_PATH 51 | 52 | - name: Upload artifact for deployment job 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: website 56 | path: ${{ env.ZIP_PATH }} 57 | 58 | - name: Check for new versions 59 | id: check-versions 60 | run: | 61 | $report = dotnet run --project ./src/Knapcode.NuGetTools.PackageDownloader --configuration Release -- check-versions $env:PACKAGES_DIR $env:PRODUCTION_URL 62 | $report 63 | $notMatching = $report | ? { !$_.StartsWith("Matching") } 64 | Write-Output "versions-changed=$(($notMatching.Length -ne 0).ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT 65 | 66 | check-formatting: 67 | runs-on: windows-latest 68 | defaults: 69 | run: 70 | shell: pwsh 71 | outputs: 72 | versions-changed: ${{ steps.check-versions.outputs.versions-changed }} 73 | 74 | steps: 75 | - name: Check out 76 | uses: actions/checkout@v4 77 | with: 78 | fetch-depth: 0 79 | 80 | - name: Set up .NET 81 | uses: actions/setup-dotnet@v4 82 | with: 83 | global-json-file: global.json 84 | 85 | - name: Check formatting 86 | run: dotnet format --verify-no-changes -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: ["v[0-9]+.[0-9]+.[0-9]+"] 7 | workflow_dispatch: 8 | schedule: 9 | - cron: "0 0 * * *" 10 | 11 | concurrency: 12 | group: deploy 13 | cancel-in-progress: false 14 | 15 | env: 16 | PRODUCTION_URL: https://nugettools.azurewebsites.net 17 | PACKAGES_DIR: ./src/Knapcode.NuGetTools.Website/packages 18 | PUBLISH_DIR: ./src/Knapcode.NuGetTools.Website/bin/publish 19 | ZIP_PATH: ./src/Knapcode.NuGetTools.Website/bin/website.zip 20 | 21 | jobs: 22 | build: 23 | runs-on: windows-latest 24 | defaults: 25 | run: 26 | shell: pwsh 27 | outputs: 28 | versions-changed: ${{ steps.check-versions.outputs.versions-changed }} 29 | 30 | steps: 31 | - name: Check out 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | 36 | - name: Set up .NET 37 | uses: actions/setup-dotnet@v4 38 | with: 39 | global-json-file: global.json 40 | 41 | - name: Restore dependencies 42 | run: dotnet restore 43 | 44 | - name: Build 45 | run: dotnet build --configuration Release --no-restore 46 | 47 | - name: Download NuGet.* packages 48 | run: ./Invoke-DownloadPackages.ps1 -PackagesDir $env:PACKAGES_DIR -Force 49 | 50 | - name: Test 51 | run: dotnet test --logger "console;verbosity=normal" --configuration Release --no-build 52 | 53 | - name: Publish 54 | run: dotnet publish ./src/Knapcode.NuGetTools.Website --output $env:PUBLISH_DIR --configuration Release --no-build 55 | 56 | - name: Zip publish directory 57 | run: Compress-Archive -Path $env:PUBLISH_DIR/* -DestinationPath $env:ZIP_PATH 58 | 59 | - name: Upload artifact for deployment job 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: website 63 | path: ${{ env.ZIP_PATH }} 64 | 65 | - name: Check for new versions 66 | id: check-versions 67 | run: | 68 | $report = dotnet run --project ./src/Knapcode.NuGetTools.PackageDownloader --configuration Release -- check-versions $env:PACKAGES_DIR $env:PRODUCTION_URL 69 | $report 70 | $notMatching = $report | ? { !$_.StartsWith("Matching") } 71 | Write-Output "versions-changed=$(($notMatching.Length -ne 0).ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT 72 | 73 | deploy-to-dev: 74 | permissions: 75 | id-token: write 76 | contents: read 77 | runs-on: windows-latest 78 | defaults: 79 | run: 80 | shell: pwsh 81 | needs: build 82 | environment: 83 | name: DEV 84 | url: ${{ steps.deploy.outputs.webapp-url }} 85 | 86 | steps: 87 | - name: Download artifact 88 | uses: actions/download-artifact@v4 89 | with: 90 | name: website 91 | path: . 92 | 93 | - name: Log in to Azure 94 | uses: azure/login@v2 95 | with: 96 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 97 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 98 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 99 | 100 | - name: Deploy 101 | id: deploy 102 | uses: azure/webapps-deploy@v3 103 | with: 104 | app-name: NuGetToolsDev 105 | package: ./website.zip 106 | 107 | deploy-to-prod: 108 | permissions: 109 | id-token: write 110 | contents: read 111 | runs-on: windows-latest 112 | needs: build 113 | environment: 114 | name: PROD 115 | url: ${{ steps.set-environment-url.outputs.url }} 116 | 117 | env: 118 | RESOURCE_GROUP: Knapcode.NuGetTools 119 | WEBAPP_NAME: NuGetTools 120 | SLOT_NAME: staging 121 | 122 | steps: 123 | - name: Check out 124 | uses: actions/checkout@v4 125 | 126 | - name: Download artifact 127 | uses: actions/download-artifact@v4 128 | with: 129 | name: website 130 | path: . 131 | 132 | - name: Log in to Azure 133 | uses: azure/login@v2 134 | with: 135 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 136 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 137 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 138 | 139 | - name: Deploy 140 | id: deploy 141 | uses: azure/webapps-deploy@v3 142 | with: 143 | app-name: ${{ env.WEBAPP_NAME }} 144 | slot-name: ${{ env.SLOT_NAME }} 145 | package: ./website.zip 146 | 147 | - name: Copy packages for tests 148 | run: | 149 | Expand-Archive -Path ./website.zip -DestinationPath ./website 150 | Copy-Item ./website/packages ./src/Knapcode.NuGetTools.Website -Recurse 151 | 152 | - name: Run integration tests 153 | run: dotnet test --logger "console;verbosity=normal" --filter "FullyQualifiedName~Knapcode.NuGetTools.Website.Tests.IntegrationTest" 154 | env: 155 | NUGETTOOLS_BASE_URL: ${{ steps.deploy.outputs.webapp-url }} 156 | 157 | - name: Swap slots 158 | if: ${{ needs.build.outputs.versions-changed == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') }} 159 | id: swap-slots 160 | run: az webapp deployment slot swap -s $env:SLOT_NAME -n $env:WEBAPP_NAME -g $env:RESOURCE_GROUP 161 | 162 | - name: Set environment URL 163 | id: set-environment-url 164 | run: | 165 | if ("${{ steps.swap-slots.outcome }}" -eq "success") { 166 | Write-Output "url=$env:PRODUCTION_URL" >> $env:GITHUB_OUTPUT 167 | } else { 168 | Write-Output "url=${{ steps.deploy.outputs.webapp-url }}" >> $env:GITHUB_OUTPUT 169 | } 170 | -------------------------------------------------------------------------------- /.github/workflows/keep-alive.yml: -------------------------------------------------------------------------------- 1 | # Keep the GitHub Actions alive. If there is no activity for 60 days, GitHub Actions gets 2 | # disabled. So we add a commit every so often. 3 | name: Keep alive 4 | 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 1 * * *" 9 | workflow_run: 10 | workflows: [Deploy] 11 | types: [completed] 12 | 13 | env: 14 | PRODUCTION_URL: https://nugettools.azurewebsites.net 15 | 16 | jobs: 17 | keep-alive: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Check out 22 | uses: actions/checkout@v4 23 | 24 | - name: Write current versions 25 | shell: pwsh 26 | run: | 27 | $month = (Get-Date).ToUniversalTime().ToString('MMMM yyyy', [CultureInfo]::InvariantCulture) 28 | $versions = curl $env:PRODUCTION_URL/api/versions 29 | $data = $versions | jq --arg month "$month" ". | {month: `$month, versions: .}" 30 | $data | Out-File latest-versions.json -Encoding utf8 31 | 32 | - name: Commit and push if data has changed 33 | run: | 34 | git config user.name "Automated" 35 | git config user.email "actions@users.noreply.github.com" 36 | git add -A 37 | git commit -m "Update latest-versions.json" || exit 0 38 | git push 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # Publish profiles 255 | PublishProfiles 256 | 257 | # local .NET CLI installation 258 | cli 259 | 260 | # AssemblyInfo (automatically generated) 261 | **/Properties/AssemblyInfo.cs 262 | *Web Deploy* 263 | **/.config/* 264 | profile.arm.json 265 | package-versions.txt 266 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | v 5 | 6 | 7 | 8 | enable 9 | enable 10 | 11 | 12 | 13 | 14 | $([System.DateTime]::Today.ToString("O")) 15 | 16 | 17 | $([System.DateTime]::UtcNow.ToString("O")) 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6.10.0 4 | true 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Invoke-DownloadPackages.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string] $PackagesDir, 3 | [switch] $Force 4 | ) 5 | 6 | if (!$PackagesDir) { 7 | $PackagesDir = Join-Path $PSScriptRoot "src/Knapcode.NuGetTools.Website/packages" 8 | } 9 | 10 | $PackagesDir = [IO.Path]::GetFullPath($PackagesDir) 11 | 12 | Write-Host "Downloading NuGet packages for the website to $PackagesDir" 13 | 14 | if ($Force -and (Test-Path $PackagesDir)) { 15 | Write-Host "Deleting existing directory" 16 | Remove-Item -Force -Recurse $PackagesDir 17 | } 18 | 19 | $toolDir = Join-Path $PSScriptRoot "src/Knapcode.NuGetTools.PackageDownloader" 20 | & dotnet run --project $toolDir --configuration Release -- download $PackagesDir 21 | if ($LASTEXITCODE -ne 0) { 22 | throw "Package downloader failed with exit code $LASTEXITCODE." 23 | } 24 | 25 | Write-Host "Successfully downloaded NuGet packages" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Joel Verhagen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /NuGetTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33513.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C65B2136-1245-4F2D-950F-55187D51FEC0}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A4ECEBC3-81BD-4BE4-80E7-D4011B729DA2}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | Directory.Build.props = Directory.Build.props 12 | Directory.Packages.props = Directory.Packages.props 13 | global.json = global.json 14 | Invoke-DownloadPackages.ps1 = Invoke-DownloadPackages.ps1 15 | LICENSE = LICENSE 16 | NuGet.Config = NuGet.Config 17 | README.md = README.md 18 | EndProjectSection 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{85894F5E-EBB5-4CCD-B29A-3228D41313C8}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Website", "src\Knapcode.NuGetTools.Website\Knapcode.NuGetTools.Website.csproj", "{37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Website.Test", "test\Knapcode.NuGetTools.Website.Test\Knapcode.NuGetTools.Website.Test.csproj", "{076637CD-353D-484D-92C7-C36492F262A2}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Logic", "src\Knapcode.NuGetTools.Logic\Knapcode.NuGetTools.Logic.csproj", "{A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Sandbox", "src\Knapcode.NuGetTools.Sandbox\Knapcode.NuGetTools.Sandbox.csproj", "{07C73F45-3DF8-428F-AC91-58654E18BA24}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Logic.Direct", "src\Knapcode.NuGetTools.Logic.Direct\Knapcode.NuGetTools.Logic.Direct.csproj", "{D7F487E6-9A50-4F7B-A6AE-E323408136F4}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.PackageDownloader", "src\Knapcode.NuGetTools.PackageDownloader\Knapcode.NuGetTools.PackageDownloader.csproj", "{2EE6D448-3E19-432C-A25B-E21474D8D8E3}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Logic.NuGet3x", "src\Knapcode.NuGetTools.Logic.NuGet3x\Knapcode.NuGetTools.Logic.NuGet3x.csproj", "{EE80559E-3E56-472C-A3DA-49213721F938}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Knapcode.NuGetTools.Logic.NuGet2x", "src\Knapcode.NuGetTools.Logic.NuGet2x\Knapcode.NuGetTools.Logic.NuGet2x.csproj", "{F7C925D0-D3E8-470C-B095-AD4EA9103ED4}" 37 | EndProject 38 | Global 39 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 40 | Debug|Any CPU = Debug|Any CPU 41 | Debug|x64 = Debug|x64 42 | Release|Any CPU = Release|Any CPU 43 | Release|x64 = Release|x64 44 | EndGlobalSection 45 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 46 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Debug|x64.ActiveCfg = Debug|Any CPU 49 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Debug|x64.Build.0 = Debug|Any CPU 50 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Release|x64.ActiveCfg = Release|Any CPU 53 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33}.Release|x64.Build.0 = Release|Any CPU 54 | {076637CD-353D-484D-92C7-C36492F262A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {076637CD-353D-484D-92C7-C36492F262A2}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {076637CD-353D-484D-92C7-C36492F262A2}.Debug|x64.ActiveCfg = Debug|Any CPU 57 | {076637CD-353D-484D-92C7-C36492F262A2}.Debug|x64.Build.0 = Debug|Any CPU 58 | {076637CD-353D-484D-92C7-C36492F262A2}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {076637CD-353D-484D-92C7-C36492F262A2}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {076637CD-353D-484D-92C7-C36492F262A2}.Release|x64.ActiveCfg = Release|Any CPU 61 | {076637CD-353D-484D-92C7-C36492F262A2}.Release|x64.Build.0 = Release|Any CPU 62 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Debug|x64.ActiveCfg = Debug|Any CPU 65 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Debug|x64.Build.0 = Debug|Any CPU 66 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Release|x64.ActiveCfg = Release|Any CPU 69 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610}.Release|x64.Build.0 = Release|Any CPU 70 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Debug|x64.ActiveCfg = Debug|Any CPU 73 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Debug|x64.Build.0 = Debug|Any CPU 74 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Release|x64.ActiveCfg = Release|Any CPU 77 | {07C73F45-3DF8-428F-AC91-58654E18BA24}.Release|x64.Build.0 = Release|Any CPU 78 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 79 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 80 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Debug|x64.ActiveCfg = Debug|Any CPU 81 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Debug|x64.Build.0 = Debug|Any CPU 82 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 83 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Release|Any CPU.Build.0 = Release|Any CPU 84 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Release|x64.ActiveCfg = Release|Any CPU 85 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4}.Release|x64.Build.0 = Release|Any CPU 86 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 87 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Debug|Any CPU.Build.0 = Debug|Any CPU 88 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Debug|x64.ActiveCfg = Debug|Any CPU 89 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Debug|x64.Build.0 = Debug|Any CPU 90 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Release|Any CPU.Build.0 = Release|Any CPU 92 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Release|x64.ActiveCfg = Release|Any CPU 93 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3}.Release|x64.Build.0 = Release|Any CPU 94 | {EE80559E-3E56-472C-A3DA-49213721F938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 95 | {EE80559E-3E56-472C-A3DA-49213721F938}.Debug|Any CPU.Build.0 = Debug|Any CPU 96 | {EE80559E-3E56-472C-A3DA-49213721F938}.Debug|x64.ActiveCfg = Debug|Any CPU 97 | {EE80559E-3E56-472C-A3DA-49213721F938}.Debug|x64.Build.0 = Debug|Any CPU 98 | {EE80559E-3E56-472C-A3DA-49213721F938}.Release|Any CPU.ActiveCfg = Release|Any CPU 99 | {EE80559E-3E56-472C-A3DA-49213721F938}.Release|Any CPU.Build.0 = Release|Any CPU 100 | {EE80559E-3E56-472C-A3DA-49213721F938}.Release|x64.ActiveCfg = Release|Any CPU 101 | {EE80559E-3E56-472C-A3DA-49213721F938}.Release|x64.Build.0 = Release|Any CPU 102 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 103 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Debug|Any CPU.Build.0 = Debug|Any CPU 104 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Debug|x64.ActiveCfg = Debug|Any CPU 105 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Debug|x64.Build.0 = Debug|Any CPU 106 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Release|Any CPU.ActiveCfg = Release|Any CPU 107 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Release|Any CPU.Build.0 = Release|Any CPU 108 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Release|x64.ActiveCfg = Release|Any CPU 109 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4}.Release|x64.Build.0 = Release|Any CPU 110 | EndGlobalSection 111 | GlobalSection(SolutionProperties) = preSolution 112 | HideSolutionNode = FALSE 113 | EndGlobalSection 114 | GlobalSection(NestedProjects) = preSolution 115 | {37DA2902-5DC6-4F7C-A7FC-751A6FB83D33} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 116 | {076637CD-353D-484D-92C7-C36492F262A2} = {85894F5E-EBB5-4CCD-B29A-3228D41313C8} 117 | {A2B1EA5C-DA79-4894-BCB6-11FDBA2CD610} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 118 | {07C73F45-3DF8-428F-AC91-58654E18BA24} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 119 | {D7F487E6-9A50-4F7B-A6AE-E323408136F4} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 120 | {2EE6D448-3E19-432C-A25B-E21474D8D8E3} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 121 | {EE80559E-3E56-472C-A3DA-49213721F938} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 122 | {F7C925D0-D3E8-470C-B095-AD4EA9103ED4} = {C65B2136-1245-4F2D-950F-55187D51FEC0} 123 | EndGlobalSection 124 | GlobalSection(ExtensibilityGlobals) = postSolution 125 | SolutionGuid = {22DC91EB-7A5F-46AA-992C-9C28049612AD} 126 | EndGlobalSection 127 | EndGlobal 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NuGet Tools 2 | 3 | A website containing various tools to help understand NuGet. 4 | 5 | ## Live Website 6 | 7 | You can visit NuGet Tools at http://nugettools.azurewebsites.net/. 8 | 9 | ## Local development 10 | 11 | You should be able to open the solution file (`NuGetTools.sln`) in Visual Studio and launch the `Knapcode.NuGetTools.Website` project to start the website. This will allow you to use the web interface for the version of the NuGet client SDK (e.g. NuGet.Frameworks and NuGet.Versioning packages) that is used directly by the project. 12 | 13 | If you want to have additional NuGet client versions available, run the `Invoke-DownloadPackages.ps1` script to download all available versions of the NuGet client packages from NuGet.org. 14 | 15 | ## Supported features 16 | 17 | - Parse a NuGet framework 18 | - Parse a NuGet package version 19 | - Parse a NuGet package version range 20 | - Test NuGet framework compatibility 21 | - Compare two NuGet package versions 22 | - Test the "get nearest" NuGet framework algorithm 23 | - Test if a version satisfies a version range 24 | - Test the "get best version match" algorithm 25 | - Switch between different NuGet versions. 26 | 27 | ## Future 28 | 29 | I'd like to add the following features in the future: 30 | 31 | - Determine first available version dynamically -- it's hard coded today 32 | - Interacting with real NuGet packages (either uploaded or from a source) 33 | - Better copy-pasting so you can easily get a snippet to drop in an email or document 34 | - REST API 35 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /latest-versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "month": "June 2025", 3 | "versions": [ 4 | "6.14.0", 5 | "6.13.2", 6 | "6.13.1", 7 | "6.12.4", 8 | "6.12.1", 9 | "6.12.0", 10 | "6.11.1", 11 | "6.11.0", 12 | "6.11.0-preview.2", 13 | "6.10.2", 14 | "6.10.1", 15 | "6.10.0", 16 | "6.9.1", 17 | "6.8.1", 18 | "6.8.0", 19 | "6.7.1", 20 | "6.7.0", 21 | "6.6.2", 22 | "6.6.1", 23 | "6.6.0", 24 | "6.6.0-preview.3", 25 | "6.5.1", 26 | "6.5.0", 27 | "6.4.3", 28 | "6.4.2", 29 | "6.4.0", 30 | "6.3.4", 31 | "6.3.3", 32 | "6.3.1", 33 | "6.3.0", 34 | "6.2.4", 35 | "6.2.2", 36 | "6.2.1", 37 | "6.2.0", 38 | "6.1.0", 39 | "6.0.6", 40 | "6.0.5", 41 | "6.0.3-rc.1", 42 | "6.0.2", 43 | "6.0.0", 44 | "6.0.0-preview.4.243", 45 | "6.0.0-preview.3", 46 | "5.11.6", 47 | "5.11.5", 48 | "5.11.3", 49 | "5.11.2", 50 | "5.11.0", 51 | "5.10.0", 52 | "5.10.0-preview.2.7185", 53 | "5.9.3", 54 | "5.9.2", 55 | "5.9.1", 56 | "5.9.0", 57 | "5.9.0-preview.2", 58 | "5.8.1", 59 | "5.8.0", 60 | "5.8.0-preview.3.6823", 61 | "5.8.0-preview.2.6776", 62 | "5.8.0-preview.1", 63 | "5.7.3-rtm.5", 64 | "5.7.2", 65 | "5.7.1", 66 | "5.7.0", 67 | "5.7.0-rtm.6702", 68 | "5.7.0-preview.3.6653", 69 | "5.7.0-preview.2.6618", 70 | "5.7.0-preview.1.6592", 71 | "5.6.0", 72 | "5.6.0-preview.3.6558", 73 | "5.6.0-preview.2.6489", 74 | "5.6.0-preview.1.6483", 75 | "5.5.1", 76 | "5.5.0", 77 | "5.5.0-preview.2.6382", 78 | "5.5.0-preview.1.6319", 79 | "5.4.0", 80 | "5.3.1", 81 | "5.3.0", 82 | "5.3.0-rtm.6192", 83 | "5.2.1", 84 | "5.2.0", 85 | "5.1.0", 86 | "5.1.0-preview2.5965", 87 | "5.0.2", 88 | "5.0.0", 89 | "5.0.0-rtm.5867", 90 | "5.0.0-rtm.5856", 91 | "5.0.0-preview3.5800", 92 | "5.0.0-preview2.5782", 93 | "4.9.6", 94 | "4.9.5", 95 | "4.9.4", 96 | "4.9.3", 97 | "4.9.2", 98 | "4.9.2-rtm.5706", 99 | "4.9.1", 100 | "4.9.0-rtm.5658", 101 | "4.8.2", 102 | "4.8.0", 103 | "4.8.0-rtm.5362", 104 | "4.8.0-preview3.5278", 105 | "4.8.0-preview1.5156", 106 | "4.7.3", 107 | "4.7.2", 108 | "4.7.0", 109 | "4.7.0-rtm.5148", 110 | "4.7.0-rtm.5104", 111 | "4.7.0-preview4.5065", 112 | "4.7.0-preview1-4986", 113 | "4.6.4", 114 | "4.6.3", 115 | "4.6.2", 116 | "4.6.1", 117 | "4.6.0", 118 | "4.6.0-rtm-4918", 119 | "4.6.0-rtm-4825", 120 | "4.6.0-rtm-4791", 121 | "4.5.3", 122 | "4.5.2", 123 | "4.5.0", 124 | "4.5.0-rtm-4651", 125 | "4.4.3", 126 | "4.4.2", 127 | "4.4.0", 128 | "4.4.0-preview3-4475", 129 | "4.3.1", 130 | "4.3.0", 131 | "4.3.0-rtm-4324", 132 | "4.3.0-preview4", 133 | "4.3.0-preview3-4168", 134 | "4.3.0-beta1-2418", 135 | "4.2.0", 136 | "4.1.0", 137 | "4.0.0", 138 | "4.0.0-rtm-2283", 139 | "4.0.0-rtm-2265", 140 | "4.0.0-rc3", 141 | "4.0.0-rc2", 142 | "4.0.0-rc-2048", 143 | "3.5.0", 144 | "3.5.0-rc1-final", 145 | "3.5.0-beta2-1484", 146 | "3.5.0-beta-final", 147 | "3.4.4-rtm-final", 148 | "3.4.4-rc", 149 | "3.4.3", 150 | "3.3.0", 151 | "3.2.0", 152 | "2.14.0", 153 | "2.14.0-rtm-832", 154 | "2.13.0", 155 | "2.13.0-rc1-final", 156 | "2.12.0", 157 | "2.12.0-rtm-815", 158 | "2.11.1", 159 | "2.10.1", 160 | "2.9.0", 161 | "2.8.6", 162 | "2.8.5", 163 | "2.8.3", 164 | "2.8.3-alpha0001", 165 | "2.8.2", 166 | "2.8.2-beta", 167 | "2.8.1", 168 | "2.8.0", 169 | "2.7.2", 170 | "2.7.1", 171 | "2.7.0.1", 172 | "2.7.0", 173 | "2.7.0-alpha", 174 | "2.6.1", 175 | "2.6.0.1", 176 | "2.6.0", 177 | "2.6.0-alpha1", 178 | "2.5.1", 179 | "2.5.0" 180 | ] 181 | } 182 | -------------------------------------------------------------------------------- /spelling.dic: -------------------------------------------------------------------------------- 1 | knapcode 2 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/AlignedVersionsDownloader.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Common; 2 | using NuGet.Packaging.Core; 3 | using NuGet.Protocol.Core.Types; 4 | using NuGet.Versioning; 5 | 6 | namespace Knapcode.NuGetTools.Logic.Direct; 7 | 8 | public class AlignedVersionsDownloader : IAlignedVersionsDownloader 9 | { 10 | private readonly IPackageRangeDownloader _packageRangeDownloader; 11 | 12 | public AlignedVersionsDownloader(IPackageRangeDownloader packageRangeDownloader) 13 | { 14 | _packageRangeDownloader = packageRangeDownloader; 15 | } 16 | 17 | public async Task> GetDownloadedVersionsAsync( 18 | IEnumerable ids, 19 | SourceCacheContext sourceCacheContext, 20 | ILogger log, 21 | CancellationToken token) 22 | { 23 | return await GetAlignedVersionsAsync( 24 | ids, 25 | async id => 26 | { 27 | var identities = await _packageRangeDownloader.GetDownloadedVersionsAsync( 28 | id, 29 | sourceCacheContext, 30 | log, 31 | token); 32 | 33 | return identities.Select(x => x.Version); 34 | }); 35 | } 36 | 37 | public async Task> DownloadPackagesAsync( 38 | IEnumerable sources, 39 | IEnumerable ids, 40 | VersionRange versionRange, 41 | SourceCacheContext sourceCacheContext, 42 | ILogger log, 43 | CancellationToken token) 44 | { 45 | var versions = await GetAvailableVersionsAsync(sources, ids, log, token); 46 | 47 | var limitedVersions = versions 48 | .Where(x => versionRange.Satisfies(x)); 49 | 50 | var identities = ids 51 | .SelectMany(id => limitedVersions.Select(version => new PackageIdentity(id, version))); 52 | 53 | await _packageRangeDownloader.DownloadPackagesAsync( 54 | sources, 55 | identities, 56 | sourceCacheContext, 57 | log, 58 | token); 59 | 60 | return versions; 61 | } 62 | 63 | private async Task> GetAvailableVersionsAsync( 64 | IEnumerable sources, 65 | IEnumerable ids, 66 | ILogger log, 67 | CancellationToken token) 68 | { 69 | return await GetAlignedVersionsAsync( 70 | ids, 71 | async id => 72 | { 73 | var availableVersions = await _packageRangeDownloader.GetAvailableVersionsAsync( 74 | sources, 75 | id, 76 | log, 77 | token); 78 | 79 | return availableVersions.Select(x => x.Version); 80 | }); 81 | } 82 | 83 | private async Task> GetAlignedVersionsAsync( 84 | IEnumerable ids, 85 | Func>> getVersionsAsync) 86 | { 87 | var versions = new HashSet(); 88 | 89 | foreach (var id in ids) 90 | { 91 | var availableVersions = await getVersionsAsync(id); 92 | 93 | if (versions.Count == 0) 94 | { 95 | versions.UnionWith(availableVersions); 96 | } 97 | else 98 | { 99 | versions.IntersectWith(availableVersions); 100 | } 101 | } 102 | 103 | return versions; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/ConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Common; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Direct; 4 | 5 | public class ConsoleLogger : ILogger 6 | { 7 | public void Log(LogLevel level, string data) 8 | { 9 | if (level < LogLevel.Information) 10 | { 11 | return; 12 | } 13 | 14 | Console.WriteLine(data); 15 | } 16 | 17 | public void Log(ILogMessage message) 18 | { 19 | Log(message.Level, message.Message); 20 | } 21 | 22 | public Task LogAsync(LogLevel level, string data) 23 | { 24 | Log(level, data); 25 | return Task.CompletedTask; 26 | } 27 | 28 | public Task LogAsync(ILogMessage message) 29 | { 30 | Log(message); 31 | return Task.CompletedTask; 32 | } 33 | 34 | public void LogDebug(string data) 35 | { 36 | Log(LogLevel.Debug, data); 37 | } 38 | 39 | public void LogError(string data) 40 | { 41 | Log(LogLevel.Error, data); 42 | } 43 | 44 | public void LogErrorSummary(string data) 45 | { 46 | Log(LogLevel.Error, data); 47 | } 48 | 49 | public void LogInformation(string data) 50 | { 51 | Log(LogLevel.Information, data); 52 | } 53 | 54 | public void LogInformationSummary(string data) 55 | { 56 | Log(LogLevel.Information, data); 57 | } 58 | 59 | public void LogMinimal(string data) 60 | { 61 | Log(LogLevel.Minimal, data); 62 | } 63 | 64 | public void LogVerbose(string data) 65 | { 66 | Log(LogLevel.Verbose, data); 67 | } 68 | 69 | public void LogWarning(string data) 70 | { 71 | Log(LogLevel.Warning, data); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Direct; 2 | 3 | public static class Constants 4 | { 5 | public const string VersioningId = "NuGet.Versioning"; 6 | public const string FrameworksId = "NuGet.Frameworks"; 7 | public const string CoreId = "NuGet.Core"; 8 | 9 | public static IReadOnlyList PackageIds3x { get; } = new[] 10 | { 11 | VersioningId, 12 | FrameworksId 13 | }; 14 | 15 | public static IReadOnlyList PackageIds2x { get; } = new[] 16 | { 17 | CoreId 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/IAlignedVersionsDownloader.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Common; 2 | using NuGet.Protocol.Core.Types; 3 | using NuGet.Versioning; 4 | 5 | namespace Knapcode.NuGetTools.Logic.Direct; 6 | 7 | public interface IAlignedVersionsDownloader 8 | { 9 | Task> GetDownloadedVersionsAsync( 10 | IEnumerable ids, 11 | SourceCacheContext sourceCacheContext, 12 | ILogger log, 13 | CancellationToken token); 14 | 15 | Task> DownloadPackagesAsync( 16 | IEnumerable sources, 17 | IEnumerable ids, 18 | VersionRange versionRange, 19 | SourceCacheContext sourceCacheContext, 20 | ILogger log, 21 | CancellationToken token); 22 | } 23 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/IPackageRangeDownloader.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Common; 2 | using NuGet.Packaging.Core; 3 | using NuGet.Protocol.Core.Types; 4 | 5 | namespace Knapcode.NuGetTools.Logic.Direct; 6 | 7 | public interface IPackageRangeDownloader 8 | { 9 | Task> GetDownloadedVersionsAsync( 10 | string id, 11 | SourceCacheContext sourceCacheContext, 12 | ILogger log, 13 | CancellationToken token); 14 | 15 | Task DownloadPackagesAsync( 16 | IEnumerable sources, 17 | IEnumerable packageIdentities, 18 | SourceCacheContext sourceCacheContext, 19 | ILogger log, 20 | CancellationToken token); 21 | 22 | Task> GetAvailableVersionsAsync( 23 | IEnumerable sources, 24 | string id, 25 | ILogger log, 26 | CancellationToken token); 27 | } 28 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/InMemorySettingSection.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Configuration; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Direct; 4 | 5 | public class InMemorySettingSection : SettingSection 6 | { 7 | private readonly object _lock = new object(); 8 | 9 | public InMemorySettingSection( 10 | string name, 11 | IReadOnlyDictionary attributes, 12 | IEnumerable children) : base(name, attributes, children) 13 | { 14 | } 15 | 16 | public void AddItem(SettingItem item) 17 | { 18 | lock (_lock) 19 | { 20 | Children.Add(item); 21 | } 22 | } 23 | 24 | public override SettingBase Clone() => throw new NotImplementedException(); 25 | } 26 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/InMemorySettings.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Configuration; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Direct; 4 | 5 | public class InMemorySettings : ISettings 6 | { 7 | private readonly object _lock = new object(); 8 | private readonly Dictionary _settings = new Dictionary(); 9 | 10 | public string FileName => throw new NotImplementedException(); 11 | public IEnumerable Priority => throw new NotImplementedException(); 12 | public string Root => throw new NotImplementedException(); 13 | 14 | #pragma warning disable 0067 15 | public event EventHandler? SettingsChanged; 16 | #pragma warning restore 0067 17 | 18 | public bool DeleteSection(string section) => throw new NotImplementedException(); 19 | public bool DeleteValue(string section, string key) => throw new NotImplementedException(); 20 | public IList> GetNestedValues(string section, string subSection) => throw new NotImplementedException(); 21 | public string GetValue(string section, string key, bool isPath = false) => throw new NotImplementedException(); 22 | public void SetNestedValues(string section, string subSection, IList> values) => throw new NotImplementedException(); 23 | public void SetValue(string section, string key, string value) => throw new NotImplementedException(); 24 | public void Remove(string sectionName, SettingItem item) => throw new NotImplementedException(); 25 | public IList GetConfigFilePaths() => throw new NotImplementedException(); 26 | public IList GetConfigRoots() => throw new NotImplementedException(); 27 | 28 | public void AddOrUpdate(string sectionName, SettingItem item) 29 | { 30 | var section = GetInMemorySettingSection(sectionName); 31 | section.AddItem(item); 32 | } 33 | 34 | public SettingSection GetSection(string sectionName) 35 | { 36 | return GetInMemorySettingSection(sectionName); 37 | } 38 | 39 | private InMemorySettingSection GetInMemorySettingSection(string sectionName) 40 | { 41 | lock (_lock) 42 | { 43 | if (!_settings.TryGetValue(sectionName, out var section)) 44 | { 45 | section = new InMemorySettingSection( 46 | sectionName, 47 | new Dictionary(), 48 | new List()); 49 | _settings.Add(sectionName, section); 50 | } 51 | 52 | return section; 53 | } 54 | } 55 | 56 | public void SaveToDisk() 57 | { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/Knapcode.NuGetTools.Logic.Direct.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/MicrosoftLogger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Direct; 4 | 5 | public class MicrosoftLogger : NuGet.Common.ILogger 6 | { 7 | private readonly ILogger _logger; 8 | 9 | public MicrosoftLogger(ILogger logger) 10 | { 11 | _logger = logger; 12 | } 13 | 14 | public void Log(NuGet.Common.LogLevel level, string data) 15 | { 16 | switch (level) 17 | { 18 | case NuGet.Common.LogLevel.Debug: 19 | _logger.LogTrace(data); 20 | break; 21 | case NuGet.Common.LogLevel.Verbose: 22 | _logger.LogDebug(data); 23 | break; 24 | case NuGet.Common.LogLevel.Information: 25 | _logger.LogInformation(data); 26 | break; 27 | case NuGet.Common.LogLevel.Minimal: 28 | _logger.LogInformation(data); 29 | break; 30 | case NuGet.Common.LogLevel.Warning: 31 | _logger.LogWarning(data); 32 | break; 33 | case NuGet.Common.LogLevel.Error: 34 | _logger.LogError(data); 35 | break; 36 | default: 37 | _logger.LogInformation(data); 38 | break; 39 | } 40 | } 41 | 42 | public void Log(NuGet.Common.ILogMessage message) 43 | { 44 | Log(message.Level, message.Message); 45 | } 46 | 47 | public Task LogAsync(NuGet.Common.LogLevel level, string data) 48 | { 49 | Log(level, data); 50 | return Task.CompletedTask; 51 | } 52 | 53 | public Task LogAsync(NuGet.Common.ILogMessage message) 54 | { 55 | Log(message); 56 | return Task.CompletedTask; 57 | } 58 | 59 | public void LogDebug(string data) 60 | { 61 | _logger.LogTrace(data); 62 | } 63 | 64 | public void LogError(string data) 65 | { 66 | _logger.LogError(data); 67 | } 68 | 69 | public void LogErrorSummary(string data) 70 | { 71 | _logger.LogError(data); 72 | } 73 | 74 | public void LogInformation(string data) 75 | { 76 | _logger.LogInformation(data); 77 | } 78 | 79 | public void LogInformationSummary(string data) 80 | { 81 | _logger.LogInformation(data); 82 | } 83 | 84 | public void LogMinimal(string data) 85 | { 86 | _logger.LogInformation(data); 87 | } 88 | 89 | public void LogVerbose(string data) 90 | { 91 | _logger.LogDebug(data); 92 | } 93 | 94 | public void LogWarning(string data) 95 | { 96 | _logger.LogWarning(data); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/NuGetSettings.cs: -------------------------------------------------------------------------------- 1 | using NuGet.Configuration; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Direct; 4 | 5 | public class NuGetSettings 6 | { 7 | public NuGetSettings(ISettings settings) 8 | { 9 | Settings = settings; 10 | } 11 | 12 | public ISettings Settings { get; } 13 | 14 | public string GlobalPackagesFolder 15 | { 16 | get 17 | { 18 | return SettingsUtility.GetGlobalPackagesFolder(Settings); 19 | } 20 | 21 | set 22 | { 23 | var path = Path.GetFullPath(value); 24 | SettingsUtility.SetConfigValue(Settings, "globalPackagesFolder", path); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.Direct/PackageRangeDownloader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using NuGet.Common; 3 | using NuGet.PackageManagement; 4 | using NuGet.Packaging; 5 | using NuGet.Packaging.Core; 6 | using NuGet.Packaging.Signing; 7 | using NuGet.Protocol; 8 | using NuGet.Protocol.Core.Types; 9 | 10 | namespace Knapcode.NuGetTools.Logic.Direct; 11 | 12 | public class PackageRangeDownloader : IPackageRangeDownloader 13 | { 14 | private readonly NuGetSettings _nuGetSettings; 15 | 16 | private readonly ConcurrentDictionary _sourceRepositories 17 | = new ConcurrentDictionary(); 18 | 19 | private readonly ConcurrentDictionary> _packageMetadataResources 20 | = new ConcurrentDictionary>(); 21 | 22 | private readonly Lazy> _findPackageByIdResource; 23 | 24 | public PackageRangeDownloader(NuGetSettings nuGetSettings) 25 | { 26 | _nuGetSettings = nuGetSettings; 27 | _findPackageByIdResource = new Lazy>(GetFindPackageByIdResourceAsync); 28 | } 29 | 30 | public async Task> GetDownloadedVersionsAsync( 31 | string id, 32 | SourceCacheContext sourceCacheContext, 33 | ILogger log, 34 | CancellationToken token) 35 | { 36 | var findPackageByIdResource = await _findPackageByIdResource.Value; 37 | 38 | var versions = await findPackageByIdResource.GetAllVersionsAsync(id, sourceCacheContext, log, token); 39 | 40 | return versions.Select(x => new PackageIdentity(id, x)); 41 | } 42 | 43 | private async Task GetFindPackageByIdResourceAsync() 44 | { 45 | var sourceRepository = GetSourceRepository(_nuGetSettings.GlobalPackagesFolder); 46 | 47 | var findPackageByIdResource = await sourceRepository 48 | .GetResourceAsync(CancellationToken.None); 49 | 50 | return findPackageByIdResource; 51 | } 52 | 53 | public async Task> GetAvailableVersionsAsync( 54 | IEnumerable sources, 55 | string id, 56 | ILogger log, 57 | CancellationToken token) 58 | { 59 | var sourceRepositories = GetSourceRepositories(sources); 60 | 61 | var versionTasks = sourceRepositories 62 | .Select(x => GetAvailableVersionsAsync(x, id, log, token)); 63 | 64 | var versionSets = await Task.WhenAll(versionTasks); 65 | 66 | return versionSets 67 | .SelectMany(x => x) 68 | .OrderBy(x => x) 69 | .Distinct() 70 | .ToArray(); 71 | } 72 | 73 | public async Task DownloadPackagesAsync( 74 | IEnumerable sources, 75 | IEnumerable packageIdentities, 76 | SourceCacheContext sourceCacheContext, 77 | ILogger log, 78 | CancellationToken token) 79 | { 80 | var sourceRepositories = GetSourceRepositories(sources); 81 | 82 | var downloadTasks = packageIdentities 83 | .Select(x => DownloadPackageAsync(sourceRepositories, x, sourceCacheContext, log, token)); 84 | 85 | await Task.WhenAll(downloadTasks); 86 | } 87 | 88 | private List GetSourceRepositories(IEnumerable sources) 89 | { 90 | return sources.Select(GetSourceRepository).ToList(); 91 | } 92 | 93 | private SourceRepository GetSourceRepository(string source) 94 | { 95 | return _sourceRepositories.GetOrAdd( 96 | source, 97 | key => Repository.Factory.GetCoreV3(key)); 98 | } 99 | 100 | private async Task> GetAvailableVersionsAsync( 101 | SourceRepository sourceRepository, 102 | string id, 103 | ILogger log, 104 | CancellationToken token) 105 | { 106 | var metadataResource = await _packageMetadataResources.GetOrAdd( 107 | sourceRepository, 108 | key => key.GetResourceAsync(token)); 109 | 110 | using (var sourceCacheContext = new SourceCacheContext()) 111 | { 112 | var allMetadata = await metadataResource.GetMetadataAsync( 113 | id, 114 | includePrerelease: true, 115 | includeUnlisted: true, 116 | sourceCacheContext: sourceCacheContext, 117 | log: log, 118 | token: token); 119 | 120 | return allMetadata.Select(x => x.Identity); 121 | } 122 | } 123 | 124 | private async Task DownloadPackageAsync( 125 | List sourceRepositories, 126 | PackageIdentity identity, 127 | SourceCacheContext sourceCacheContext, 128 | ILogger log, 129 | CancellationToken token) 130 | { 131 | var resolver = new VersionFolderPathResolver(_nuGetSettings.GlobalPackagesFolder); 132 | var hashPath = resolver.GetHashPath(identity.Id, identity.Version); 133 | if (File.Exists(hashPath)) 134 | { 135 | log.LogInformation($"The package '{identity}' is already available."); 136 | return; 137 | } 138 | 139 | var packageDownloadContext = new PackageDownloadContext(sourceCacheContext); 140 | 141 | using var downloadResult = await PackageDownloader.GetDownloadResourceResultAsync( 142 | sourceRepositories, 143 | packageIdentity: identity, 144 | downloadContext: packageDownloadContext, 145 | globalPackagesFolder: _nuGetSettings.GlobalPackagesFolder, 146 | logger: log, 147 | token: token); 148 | 149 | if (downloadResult.Status != DownloadResourceResultStatus.Available) 150 | { 151 | throw new InvalidOperationException($"The package '{identity}' is not available."); 152 | } 153 | 154 | using var addResult = await GlobalPackagesFolderUtility.AddPackageAsync( 155 | downloadResult.PackageSource, 156 | identity, 157 | downloadResult.PackageStream, 158 | _nuGetSettings.GlobalPackagesFolder, 159 | packageDownloadContext.ParentId, 160 | ClientPolicyContext.GetClientPolicy(_nuGetSettings.Settings, log), 161 | log, 162 | token); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/Framework2x.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using Knapcode.NuGetTools.Logic.Wrappers; 3 | using NuGet; 4 | 5 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 6 | 7 | public class Framework2x : IFramework 8 | { 9 | public Framework2x(FrameworkName frameworkName) 10 | { 11 | FrameworkName = frameworkName; 12 | } 13 | 14 | public FrameworkName FrameworkName { get; } 15 | 16 | public string ShortFolderName => VersionUtility.GetShortFrameworkName(FrameworkName); 17 | public string DotNetFrameworkName => FrameworkName.ToString(); 18 | public string Identifier => FrameworkName.Identifier; 19 | public Version Version => FrameworkName.Version; 20 | public bool HasProfile => !string.IsNullOrEmpty(FrameworkName.Profile); 21 | public string Profile => FrameworkName.Profile; 22 | public bool IsPlatformAvailable => false; 23 | public bool HasPlatform => throw new NotSupportedException(); 24 | public string Platform => throw new NotSupportedException(); 25 | public Version PlatformVersion => throw new NotSupportedException(); 26 | public string ToStringResult => FrameworkName.ToString(); 27 | } 28 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/FrameworkLogic2x.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using Knapcode.NuGetTools.Logic.Wrappers; 3 | using NuGet; 4 | 5 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 6 | 7 | public class FrameworkLogic2x : IFrameworkLogic 8 | { 9 | public IFramework? GetNearest(IFramework project, IEnumerable package) 10 | { 11 | var prsList = new List(); 12 | var prsToFramework = new Dictionary(); 13 | foreach (var o in package) 14 | { 15 | var framework = (Framework2x)o; 16 | var prs = new PackageReferenceSet(framework.FrameworkName, Enumerable.Empty()); 17 | prsList.Add(prs); 18 | prsToFramework[prs] = framework; 19 | } 20 | 21 | var matched = VersionUtility.TryGetCompatibleItems(((Framework2x)project).FrameworkName, prsList, out var compatible); 22 | if (!matched) 23 | { 24 | return null; 25 | } 26 | 27 | var first = compatible.First(); 28 | return prsToFramework[first]; 29 | } 30 | 31 | public bool IsCompatible(IFramework project, IFramework package) 32 | { 33 | return VersionUtility.IsCompatible( 34 | ((Framework2x)project).FrameworkName, 35 | new FrameworkName[] { ((Framework2x)project).FrameworkName }); 36 | } 37 | 38 | public IFramework Parse(string input) 39 | { 40 | if (input.Contains(',')) 41 | { 42 | return new Framework2x(new FrameworkName(input)); 43 | } 44 | 45 | return new Framework2x(VersionUtility.ParseFrameworkName(input)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/Knapcode.NuGetTools.Logic.NuGet2x.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | NU1701 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/NuGetLogic2x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 5 | 6 | public class NuGetLogic2x : INuGetLogic 7 | { 8 | public NuGetLogic2x() 9 | { 10 | Framework = new FrameworkLogic2x(); 11 | Version = new VersionLogic2x(); 12 | VersionRange = new VersionRangeLogic2x(); 13 | AssemblyNames = new[] 14 | { 15 | typeof(VersionUtility).Assembly.FullName ?? throw new NotSupportedException(), 16 | }; 17 | } 18 | 19 | public IFrameworkLogic Framework { get; } 20 | public IVersionLogic Version { get; } 21 | public IVersionRangeLogic VersionRange { get; } 22 | public IReadOnlyList AssemblyNames { get; } 23 | } 24 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/Version2x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 5 | 6 | public class Version2x : IVersion 7 | { 8 | private static readonly bool StaticNormalizedStringAvailable = typeof(SemanticVersion) 9 | .GetMethod(nameof(SemanticVersion.ToNormalizedString)) is not null; 10 | private static readonly bool StaticFullStringAvailable = typeof(SemanticVersion) 11 | .GetMethod(nameof(SemanticVersion.ToFullString)) is not null; 12 | private static readonly bool StaticIsSemVer2Available = typeof(SemanticVersion) 13 | .GetMethod(nameof(SemanticVersion.IsSemVer2)) is not null; 14 | 15 | public Version2x(SemanticVersion version) 16 | { 17 | SemanticVersion = version; 18 | } 19 | 20 | public SemanticVersion SemanticVersion { get; } 21 | 22 | public int Revision => SemanticVersion.Version.Revision; 23 | public bool IsSemVer2 => SemanticVersion.IsSemVer2(); 24 | public bool IsPrerelease => !string.IsNullOrEmpty(SemanticVersion.SpecialVersion); 25 | public string NormalizedString => SemanticVersion.ToNormalizedString(); 26 | public bool NormalizedStringAvailable => StaticNormalizedStringAvailable; 27 | public string FullString => SemanticVersion.ToFullString(); 28 | public bool IsSemVer2Available => StaticIsSemVer2Available; 29 | public bool FullStringAvailable => StaticFullStringAvailable; 30 | public string ToStringResult => SemanticVersion.ToString(); 31 | } 32 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/VersionLogic2x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 5 | 6 | public class VersionLogic2x : IVersionLogic 7 | { 8 | public int Compare(IVersion versionA, IVersion versionB) 9 | { 10 | return ((Version2x)versionA).SemanticVersion.CompareTo(((Version2x)versionB).SemanticVersion); 11 | } 12 | 13 | public IVersion Parse(string input) 14 | { 15 | return new Version2x(SemanticVersion.Parse(input)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/VersionRange2x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 5 | 6 | public class VersionRange2x : IVersionRange 7 | { 8 | public VersionRange2x(IVersionSpec versionSpec) 9 | { 10 | VersionSpec = versionSpec; 11 | } 12 | 13 | public IVersionSpec VersionSpec { get; } 14 | 15 | public string NormalizedString => VersionSpec.ToString()!; 16 | public bool IsFloating => throw new NotSupportedException(); 17 | public string PrettyPrint => VersionUtility.PrettyPrint(VersionSpec); 18 | public bool HasLowerBound => VersionSpec.MinVersion is not null; 19 | public bool HasUpperBound => VersionSpec.MaxVersion is not null; 20 | public bool IsMinInclusive => VersionSpec.IsMinInclusive; 21 | public bool IsMaxInclusive => VersionSpec.IsMaxInclusive; 22 | public IVersion MinVersion => new Version2x(VersionSpec.MinVersion); 23 | public IVersion MaxVersion => new Version2x(VersionSpec.MaxVersion); 24 | public string LegacyShortString => throw new NotSupportedException(); 25 | public string LegacyString => throw new NotSupportedException(); 26 | public string OriginalString => throw new NotSupportedException(); 27 | public bool LegacyShortStringAvailable => false; 28 | public bool IsFloatingAvailable => false; 29 | public bool OriginalStringAvailable => false; 30 | public bool LegacyStringAvailable => false; 31 | } 32 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet2x/VersionRangeLogic2x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet2x; 5 | 6 | public class VersionRangeLogic2x : IVersionRangeLogic 7 | { 8 | public bool FindBestMatchAvailable => false; 9 | public bool IsBetterAvailable => false; 10 | 11 | public IVersion FindBestMatch(IVersionRange versionRange, IEnumerable versions) 12 | { 13 | throw new NotSupportedException(); 14 | } 15 | 16 | public bool IsBetter(IVersionRange versionRange, IVersion current, IVersion considering) 17 | { 18 | throw new NotSupportedException(); 19 | } 20 | 21 | public IVersionRange Parse(string input) 22 | { 23 | return new VersionRange2x(VersionUtility.ParseVersionSpec(input)); 24 | } 25 | 26 | public bool Satisfies(IVersionRange versionRange, IVersion version) 27 | { 28 | return ((VersionRange2x)versionRange).VersionSpec.Satisfies(((Version2x)version).SemanticVersion); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/Framework3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Frameworks; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class Framework3x : IFramework 7 | { 8 | private static readonly bool StaticIsPlatformAvailable = typeof(NuGetFramework) 9 | .GetProperty(nameof(NuGetFramework.HasPlatform)) is not null; 10 | 11 | public Framework3x(NuGetFramework framework) 12 | { 13 | NuGetFramework = framework; 14 | } 15 | 16 | public NuGetFramework NuGetFramework { get; } 17 | 18 | public string DotNetFrameworkName => NuGetFramework.DotNetFrameworkName; 19 | public string ShortFolderName => NuGetFramework.GetShortFolderName(); 20 | public string Identifier => NuGetFramework.Framework; 21 | public Version Version => NuGetFramework.Version; 22 | public bool HasProfile => NuGetFramework.HasProfile; 23 | public string Profile => NuGetFramework.Profile; 24 | public bool IsPlatformAvailable => StaticIsPlatformAvailable; 25 | public bool HasPlatform => NuGetFramework.HasPlatform; 26 | public string Platform => NuGetFramework.Platform; 27 | public Version PlatformVersion => NuGetFramework.PlatformVersion; 28 | public string ToStringResult => NuGetFramework.ToString(); 29 | } 30 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/FrameworkLogic3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Frameworks; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class FrameworkLogic3x : IFrameworkLogic 7 | { 8 | private readonly FrameworkReducer _reducer; 9 | private readonly IFrameworkCompatibilityProvider _compatibilityProvider; 10 | 11 | public FrameworkLogic3x() 12 | { 13 | _reducer = new FrameworkReducer(); 14 | _compatibilityProvider = DefaultCompatibilityProvider.Instance; 15 | } 16 | 17 | public IFramework? GetNearest(IFramework project, IEnumerable package) 18 | { 19 | var nearest = _reducer.GetNearest( 20 | ((Framework3x)project).NuGetFramework, 21 | package.Cast().Select(x => x.NuGetFramework)); 22 | 23 | if (nearest is null) 24 | { 25 | return null; 26 | } 27 | 28 | return package.First(x => ReferenceEquals(((Framework3x)x).NuGetFramework, nearest)); 29 | } 30 | 31 | public bool IsCompatible(IFramework project, IFramework package) 32 | { 33 | return _compatibilityProvider.IsCompatible( 34 | ((Framework3x)project).NuGetFramework, 35 | ((Framework3x)package).NuGetFramework); 36 | } 37 | 38 | public IFramework Parse(string input) 39 | { 40 | var nuGetFramework = NuGetFramework.Parse(input); 41 | return new Framework3x(nuGetFramework); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/Knapcode.NuGetTools.Logic.NuGet3x.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/NuGetLogic3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Frameworks; 3 | using NuGet.Versioning; 4 | 5 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 6 | 7 | public class NuGetLogic3x : INuGetLogic 8 | { 9 | public NuGetLogic3x() 10 | { 11 | Framework = new FrameworkLogic3x(); 12 | Version = new VersionLogic3x(); 13 | VersionRange = new VersionRangeLogic3x(); 14 | AssemblyNames = new[] 15 | { 16 | typeof(NuGetFramework).Assembly.FullName ?? throw new NotSupportedException(), 17 | typeof(NuGetVersion).Assembly.FullName ?? throw new NotSupportedException(), 18 | }; 19 | } 20 | 21 | public IFrameworkLogic Framework { get; } 22 | public IVersionLogic Version { get; } 23 | public IVersionRangeLogic VersionRange { get; } 24 | public IReadOnlyList AssemblyNames { get; } 25 | } 26 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/Version3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Versioning; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class Version3x : IVersion 7 | { 8 | private static readonly bool StaticFullStringAvailable = typeof(NuGetVersion) 9 | .GetMethod(nameof(NuGet.Versioning.NuGetVersion.ToFullString)) is not null; 10 | private static readonly bool StaticIsSemVer2Available = typeof(NuGetVersion) 11 | .GetMethod(nameof(NuGet.Versioning.NuGetVersion.IsSemVer2)) is not null; 12 | public Version3x(NuGetVersion version) 13 | { 14 | NuGetVersion = version; 15 | } 16 | 17 | public NuGetVersion NuGetVersion { get; } 18 | 19 | public string FullString => NuGetVersion.ToFullString(); 20 | public bool IsPrerelease => NuGetVersion.IsPrerelease; 21 | public bool IsSemVer2 => NuGetVersion.IsSemVer2; 22 | public string NormalizedString => NuGetVersion.ToNormalizedString(); 23 | public int Revision => NuGetVersion.Revision; 24 | public bool IsSemVer2Available => StaticIsSemVer2Available; 25 | public bool FullStringAvailable => StaticFullStringAvailable; 26 | public string ToStringResult => NuGetVersion.ToString(); 27 | public bool NormalizedStringAvailable => true; 28 | } 29 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/VersionLogic3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Versioning; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class VersionLogic3x : IVersionLogic 7 | { 8 | public int Compare(IVersion versionA, IVersion versionB) 9 | { 10 | return ((Version3x)versionA).NuGetVersion.CompareTo(((Version3x)versionB).NuGetVersion); 11 | } 12 | 13 | public IVersion Parse(string input) 14 | { 15 | var nuGetVersion = NuGetVersion.Parse(input); 16 | return new Version3x(nuGetVersion); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/VersionRange3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Versioning; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class VersionRange3x : IVersionRange 7 | { 8 | private static readonly bool StaticLegacyShortStringAvailable = typeof(VersionRange) 9 | .GetMethod(nameof(NuGet.Versioning.VersionRange.ToLegacyShortString)) is not null; 10 | 11 | public VersionRange3x(VersionRange versionRange) 12 | { 13 | VersionRange = versionRange; 14 | } 15 | 16 | public VersionRange VersionRange { get; } 17 | 18 | public bool HasLowerBound => VersionRange.HasLowerBound; 19 | public bool HasUpperBound => VersionRange.HasUpperBound; 20 | public bool IsFloating => VersionRange.IsFloating; 21 | public bool IsMaxInclusive => VersionRange.IsMaxInclusive; 22 | public bool IsMinInclusive => VersionRange.IsMinInclusive; 23 | public IVersion MaxVersion => new Version3x(VersionRange.MaxVersion ?? throw new NotSupportedException()); 24 | public IVersion MinVersion => new Version3x(VersionRange.MinVersion ?? throw new NotSupportedException()); 25 | public string NormalizedString => VersionRange.ToNormalizedString(); 26 | public string PrettyPrint => VersionRange.PrettyPrint(); 27 | public string LegacyShortString => VersionRange.ToLegacyShortString(); 28 | public string LegacyString => VersionRange.ToLegacyString(); 29 | public string OriginalString => VersionRange.OriginalString ?? throw new NotSupportedException(); 30 | public bool LegacyShortStringAvailable => StaticLegacyShortStringAvailable; 31 | public bool IsFloatingAvailable => true; 32 | public bool OriginalStringAvailable => true; 33 | public bool LegacyStringAvailable => true; 34 | } 35 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic.NuGet3x/VersionRangeLogic3x.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | using NuGet.Versioning; 3 | 4 | namespace Knapcode.NuGetTools.Logic.NuGet3x; 5 | 6 | public class VersionRangeLogic3x : IVersionRangeLogic 7 | { 8 | public bool FindBestMatchAvailable => true; 9 | public bool IsBetterAvailable => true; 10 | 11 | public IVersion? FindBestMatch(IVersionRange versionRange, IEnumerable versions) 12 | { 13 | var bestMatch = ((VersionRange3x)versionRange).VersionRange.FindBestMatch( 14 | versions.Cast().Select(x => x.NuGetVersion)); 15 | 16 | if (bestMatch == null) 17 | { 18 | return null; 19 | } 20 | 21 | return versions.First(x => ReferenceEquals(((Version3x)x).NuGetVersion, bestMatch)); 22 | } 23 | 24 | public bool IsBetter(IVersionRange versionRange, IVersion current, IVersion considering) 25 | { 26 | return ((VersionRange3x)versionRange).VersionRange.IsBetter(((Version3x)current).NuGetVersion, ((Version3x)considering).NuGetVersion); 27 | } 28 | 29 | public IVersionRange Parse(string input) 30 | { 31 | var nuGetVersionRange = VersionRange.Parse(input); 32 | return new VersionRange3x(nuGetVersionRange); 33 | } 34 | 35 | public bool Satisfies(IVersionRange versionRange, IVersion version) 36 | { 37 | return ((VersionRange3x)versionRange).VersionRange.Satisfies(((Version3x)version).NuGetVersion); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/DirectToolsFactory.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic; 4 | 5 | public class DirectToolsFactory : IToolsFactory 6 | { 7 | private readonly string _version; 8 | private readonly IToolsService _toolsService; 9 | private readonly IFrameworkPrecedenceService _frameworkPrecedenceService; 10 | private readonly IFrameworkList _frameworkList; 11 | 12 | public DirectToolsFactory( 13 | IToolsService toolsService, 14 | IFrameworkPrecedenceService frameworkPrecedenceService, 15 | IFrameworkList frameworkList) 16 | { 17 | _version = toolsService.Version; 18 | _toolsService = toolsService; 19 | _frameworkPrecedenceService = frameworkPrecedenceService; 20 | _frameworkList = frameworkList; 21 | } 22 | 23 | public IEnumerable GetAvailableVersions() 24 | { 25 | return new[] { _version }; 26 | } 27 | 28 | public IToolsService? GetService(string version) 29 | { 30 | if (version != _version) 31 | { 32 | return null; 33 | } 34 | 35 | return _toolsService; 36 | } 37 | 38 | public Task> GetAvailableVersionsAsync(CancellationToken token) 39 | { 40 | var versions = new[] { _version }; 41 | 42 | return Task.FromResult>(versions); 43 | } 44 | 45 | public Task GetServiceAsync(string version, CancellationToken token) 46 | { 47 | IToolsService? output = null; 48 | 49 | if (version == _version) 50 | { 51 | output = _toolsService; 52 | } 53 | 54 | return Task.FromResult(output); 55 | } 56 | 57 | public Task GetFrameworkPrecedenceServiceAsync(string version, CancellationToken token) 58 | { 59 | IFrameworkPrecedenceService? output = null; 60 | 61 | if (version == _version) 62 | { 63 | output = _frameworkPrecedenceService; 64 | } 65 | 66 | return Task.FromResult(output); 67 | } 68 | 69 | public Task GetFrameworkListAsync(CancellationToken token) 70 | { 71 | return Task.FromResult(_frameworkList); 72 | } 73 | 74 | public Task GetLatestVersionAsync(CancellationToken token) 75 | { 76 | return Task.FromResult(_version); 77 | } 78 | 79 | public Task?> GetPackagesAsync(string version, CancellationToken token) 80 | { 81 | IReadOnlyList? output = null; 82 | 83 | if (version == _version) 84 | { 85 | output = new List(); 86 | } 87 | 88 | return Task.FromResult(output); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkEnumerationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | [Flags] 4 | public enum FrameworkEnumerationOptions 5 | { 6 | None = 1 << 0, 7 | FrameworkNameProvider = 1 << 1, 8 | CommonFrameworks = 1 << 2, 9 | FrameworkMappings = 1 << 3, 10 | PortableFrameworkMappings = 1 << 4, 11 | SpecialFrameworks = 1 << 5, 12 | Hardcoded = 1 << 6, 13 | All = ~0 14 | } 15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkEnumeratorData.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | using Knapcode.NuGetTools.Logic.Wrappers; 4 | 5 | namespace Knapcode.NuGetTools.Logic; 6 | 7 | public class FrameworkEnumeratorData : IEquatable, IComparable 8 | { 9 | public FrameworkEnumeratorData(IFramework framework) 10 | { 11 | Identifier = framework.Identifier; 12 | Version = framework.Version; 13 | Profile = framework.Profile; 14 | Framework = framework; 15 | } 16 | 17 | public string Identifier { get; } 18 | public Version Version { get; } 19 | public string Profile { get; } 20 | public IFramework Framework { get; } 21 | 22 | public override string ToString() 23 | { 24 | if (string.IsNullOrEmpty(Profile)) 25 | { 26 | return $"{Identifier},Version=v{GetDisplayVersion(Version)}"; 27 | } 28 | else 29 | { 30 | return $"{Identifier},Version=v{GetDisplayVersion(Version)},Profile={Profile}"; 31 | } 32 | } 33 | 34 | public override bool Equals(object? obj) 35 | { 36 | return Equals(obj as FrameworkEnumeratorData); 37 | } 38 | 39 | public override int GetHashCode() 40 | { 41 | var hashCode = new HashCode(); 42 | hashCode.Add(Identifier, StringComparer.OrdinalIgnoreCase); 43 | hashCode.Add(Version); 44 | hashCode.Add(Profile, StringComparer.OrdinalIgnoreCase); 45 | return hashCode.ToHashCode(); 46 | } 47 | 48 | public bool Equals(FrameworkEnumeratorData? other) 49 | { 50 | if (other == null) 51 | { 52 | return false; 53 | } 54 | 55 | return StringComparer.OrdinalIgnoreCase.Equals(Identifier, other.Identifier) && 56 | Version == other.Version && 57 | StringComparer.OrdinalIgnoreCase.Equals(Profile, other.Profile); 58 | } 59 | 60 | public int CompareTo(FrameworkEnumeratorData? other) 61 | { 62 | if (other is null) 63 | { 64 | return 1; 65 | } 66 | 67 | var frameworkCompare = StringComparer.OrdinalIgnoreCase.Compare(Identifier, other.Identifier); 68 | if (frameworkCompare != 0) 69 | { 70 | return frameworkCompare; 71 | } 72 | 73 | var versionCompare = Version.CompareTo(other.Version); 74 | if (versionCompare != 0) 75 | { 76 | return frameworkCompare; 77 | } 78 | 79 | return Profile.CompareTo(other.Profile); 80 | } 81 | 82 | private static string GetDisplayVersion(Version version) 83 | { 84 | var sb = new StringBuilder(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor)); 85 | 86 | if (version.Build > 0 87 | || version.Revision > 0) 88 | { 89 | sb.AppendFormat(CultureInfo.InvariantCulture, ".{0}", version.Build); 90 | 91 | if (version.Revision > 0) 92 | { 93 | sb.AppendFormat(CultureInfo.InvariantCulture, ".{0}", version.Revision); 94 | } 95 | } 96 | 97 | return sb.ToString(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkExpansionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | [Flags] 4 | public enum FrameworkExpansionOptions 5 | { 6 | None = 1 << 0, 7 | RoundTripDotNetFrameworkName = 1 << 1, 8 | RoundTripShortFolderName = 1 << 2, 9 | FrameworkExpander = 1 << 3, 10 | All = ~0 11 | } 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkList.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public class FrameworkList : IFrameworkList 4 | { 5 | private readonly IFrameworkEnumerator _enumerator; 6 | private readonly Lazy> _items; 7 | private readonly Lazy> _dotNetFrameworkNames; 8 | private readonly Lazy> _shortFolderNames; 9 | private readonly Lazy> _identifiers; 10 | 11 | public FrameworkList(IFrameworkEnumerator enumerator) 12 | { 13 | _enumerator = enumerator; 14 | _items = new Lazy>(GetItems); 15 | _dotNetFrameworkNames = new Lazy>(GetDotNetFrameworkNames); 16 | _shortFolderNames = new Lazy>(GetShortFolderNames); 17 | _identifiers = new Lazy>(GetIdentifiers); 18 | } 19 | 20 | public IReadOnlyList DotNetFrameworkNames => _dotNetFrameworkNames.Value; 21 | 22 | public IReadOnlyList ShortFolderNames => _shortFolderNames.Value; 23 | 24 | public IReadOnlyList Identifiers => _identifiers.Value; 25 | 26 | private IReadOnlyList GetDotNetFrameworkNames() 27 | { 28 | return _items 29 | .Value 30 | .Select(x => x.DotNetFrameworkName) 31 | .Distinct(StringComparer.OrdinalIgnoreCase) 32 | .OrderBy(x => x, StringComparer.OrdinalIgnoreCase) 33 | .ToList(); 34 | } 35 | 36 | private IReadOnlyList GetShortFolderNames() 37 | { 38 | return _items 39 | .Value 40 | .Select(x => x.ShortFolderName) 41 | .Distinct(StringComparer.OrdinalIgnoreCase) 42 | .OrderBy(x => x, StringComparer.OrdinalIgnoreCase) 43 | .ToList(); 44 | } 45 | 46 | private IReadOnlyList GetIdentifiers() 47 | { 48 | return _items 49 | .Value 50 | .Select(x => x.Identifier) 51 | .Distinct(StringComparer.OrdinalIgnoreCase) 52 | .OrderBy(x => x, StringComparer.OrdinalIgnoreCase) 53 | .ToList(); 54 | } 55 | 56 | private IReadOnlyList GetItems() 57 | { 58 | return EnumerateItems().ToList(); 59 | } 60 | 61 | private IEnumerable EnumerateItems() 62 | { 63 | var enumerated = _enumerator.Enumerate(FrameworkEnumerationOptions.All); 64 | var expanded = _enumerator.Expand(enumerated, FrameworkExpansionOptions.All); 65 | 66 | foreach (var framework in expanded) 67 | { 68 | yield return new FrameworkListItem( 69 | framework.Identifier, 70 | framework.Version, 71 | framework.Profile, 72 | framework.Framework.DotNetFrameworkName, 73 | framework.Framework.ShortFolderName); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkListItem.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public class FrameworkListItem 4 | { 5 | public FrameworkListItem(string identifier, Version version, string profile, string dotNetFrameworkName, string shortFolderName) 6 | { 7 | Identifier = identifier; 8 | Version = version; 9 | Profile = profile; 10 | DotNetFrameworkName = dotNetFrameworkName; 11 | ShortFolderName = shortFolderName; 12 | } 13 | 14 | public string Identifier { get; } 15 | public Version Version { get; } 16 | public string Profile { get; } 17 | public string DotNetFrameworkName { get; } 18 | public string ShortFolderName { get; } 19 | } 20 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/FrameworkPrecedenceService.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Models; 2 | using Knapcode.NuGetTools.Logic.Models.Framework; 3 | using Knapcode.NuGetTools.Logic.Wrappers; 4 | 5 | namespace Knapcode.NuGetTools.Logic; 6 | 7 | public class FrameworkPrecedenceService : IFrameworkPrecedenceService 8 | { 9 | private readonly IFrameworkList _frameworkList; 10 | private readonly IFrameworkLogic _logic; 11 | private readonly bool _hasDuplicateKeyException; 12 | 13 | public FrameworkPrecedenceService(string version, IFrameworkList frameworkList, IFrameworkLogic logic) 14 | { 15 | Version = version; 16 | _frameworkList = frameworkList; 17 | _logic = logic; 18 | _hasDuplicateKeyException = version switch 19 | { 20 | "2.13.0-rc1-final" => true, 21 | "2.13.0" => true, 22 | "2.12.0" => true, 23 | _ => false, 24 | }; 25 | } 26 | 27 | public string Version { get; } 28 | 29 | public FrameworkPrecedenceOutput FrameworkPrecedence(FrameworkPrecedenceInput input) 30 | { 31 | var output = new FrameworkPrecedenceOutput 32 | { 33 | InputStatus = InputStatus.Missing, 34 | Input = input, 35 | Precedence = Array.Empty(), 36 | }; 37 | 38 | if (input != null && 39 | !string.IsNullOrWhiteSpace(input.Framework)) 40 | { 41 | try 42 | { 43 | output.Framework = _logic.Parse(input.Framework); 44 | output.InputStatus = InputStatus.Valid; 45 | } 46 | catch (Exception) 47 | { 48 | output.InputStatus = InputStatus.Invalid; 49 | } 50 | } 51 | 52 | if (output.Framework is not null) 53 | { 54 | output.Precedence = GetPrecedence(output, output.Framework); 55 | } 56 | 57 | return output; 58 | } 59 | 60 | private List GetPrecedence(FrameworkPrecedenceOutput output, IFramework framework) 61 | { 62 | // Get the initial set of candidates. 63 | var remainingCandidates = new HashSet( 64 | GetCandidates(output, framework), 65 | new FrameworkEqualityComparer()); 66 | 67 | // Perform "get nearest" on the remaining set to find the next in precedence. 68 | var precedence = new List(); 69 | var duplicateEncountered = false; 70 | while (remainingCandidates.Count > 0) 71 | { 72 | IFramework? nearest; 73 | const string duplicateMessage = "An item with the same key has already been added. Key: "; 74 | try 75 | { 76 | nearest = _logic.GetNearest(framework, remainingCandidates); 77 | } 78 | catch (ArgumentException ex) when (_hasDuplicateKeyException && !duplicateEncountered && ex.Message.StartsWith(duplicateMessage)) 79 | { 80 | remainingCandidates.RemoveWhere(x => x.ToStringResult == "DNXCore,Version=v5.0"); 81 | remainingCandidates.RemoveWhere(x => StringComparer.OrdinalIgnoreCase.Equals(x.Identifier, "Tizen")); 82 | remainingCandidates.RemoveWhere(x => StringComparer.OrdinalIgnoreCase.Equals(x.Identifier, ".NETCore")); 83 | duplicateEncountered = true; 84 | continue; 85 | } 86 | 87 | if (nearest is null) 88 | { 89 | break; 90 | } 91 | 92 | precedence.Add(nearest); 93 | remainingCandidates.Remove(nearest); 94 | } 95 | 96 | return precedence; 97 | } 98 | 99 | private IEnumerable GetCandidates(FrameworkPrecedenceOutput output, IFramework framework) 100 | { 101 | IEnumerable candidates = GetFrameworkList(); 102 | 103 | if (!output.Input.IncludeProfiles) 104 | { 105 | candidates = candidates.Where(x => string.IsNullOrEmpty(x.Profile) || IsPortable(x)); 106 | } 107 | 108 | if (output.Input.ExcludePortable) 109 | { 110 | candidates = candidates.Where(x => !IsPortable(x)); 111 | } 112 | 113 | var excludedIdentifiers = new HashSet(StringComparer.OrdinalIgnoreCase); 114 | if (!string.IsNullOrWhiteSpace(output.Input.ExcludedIdentifiers)) 115 | { 116 | var split = output 117 | .Input 118 | .ExcludedIdentifiers 119 | .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) 120 | .Select(x => x.Trim()) 121 | .Where(x => x.Length > 0) 122 | .ToList(); 123 | 124 | foreach (var identifier in split) 125 | { 126 | excludedIdentifiers.Add(identifier); 127 | } 128 | 129 | if (excludedIdentifiers.Any()) 130 | { 131 | candidates = candidates.Where(x => !excludedIdentifiers.Contains(x.Identifier)); 132 | } 133 | } 134 | 135 | // Narrow the list of frameworks down to those that are compatible. 136 | candidates = candidates.Where(x => _logic.IsCompatible(framework, x)); 137 | 138 | return candidates; 139 | } 140 | 141 | private static bool IsPortable(IFramework x) 142 | { 143 | return StringComparer.OrdinalIgnoreCase.Equals(".NETPortable", x.Identifier); 144 | } 145 | 146 | private List GetFrameworkList() 147 | { 148 | var frameworks = new List(); 149 | 150 | foreach (var item in _frameworkList.DotNetFrameworkNames) 151 | { 152 | try 153 | { 154 | frameworks.Add(_logic.Parse(item)); 155 | } 156 | catch 157 | { 158 | // Ignore frameworks that cannot be parsed 159 | } 160 | } 161 | 162 | return frameworks; 163 | } 164 | 165 | private class FrameworkEqualityComparer : IEqualityComparer 166 | { 167 | public bool Equals(IFramework? x, IFramework? y) 168 | { 169 | if (x is null && y is null) 170 | { 171 | return true; 172 | } 173 | 174 | if (x is null || y is null) 175 | { 176 | return false; 177 | } 178 | 179 | return StringComparer.OrdinalIgnoreCase.Equals(x.DotNetFrameworkName, y.DotNetFrameworkName); 180 | } 181 | 182 | public int GetHashCode(IFramework obj) 183 | { 184 | return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.DotNetFrameworkName); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IFrameworkEnumerator.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public interface IFrameworkEnumerator 4 | { 5 | IEnumerable Enumerate(FrameworkEnumerationOptions options); 6 | IEnumerable Expand(IEnumerable frameworks, FrameworkExpansionOptions options); 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IFrameworkList.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public interface IFrameworkList 4 | { 5 | IReadOnlyList DotNetFrameworkNames { get; } 6 | IReadOnlyList ShortFolderNames { get; } 7 | IReadOnlyList Identifiers { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IFrameworkPrecedenceService.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Models.Framework; 2 | 3 | namespace Knapcode.NuGetTools.Logic; 4 | 5 | public interface IFrameworkPrecedenceService : IVersionedService 6 | { 7 | FrameworkPrecedenceOutput FrameworkPrecedence(FrameworkPrecedenceInput input); 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IToolsFactory.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic; 4 | 5 | public interface IToolsFactory 6 | { 7 | /// 8 | /// Gets the available versions provided by this factory in descending order. That is, the highest version 9 | /// available will be the first item in the sequence. 10 | /// 11 | /// The cancellation token. 12 | /// The sequence of versions. 13 | Task> GetAvailableVersionsAsync(CancellationToken token); 14 | 15 | Task GetLatestVersionAsync(CancellationToken token); 16 | 17 | Task GetServiceAsync(string version, CancellationToken token); 18 | 19 | Task GetFrameworkPrecedenceServiceAsync(string version, CancellationToken token); 20 | 21 | Task GetFrameworkListAsync(CancellationToken token); 22 | 23 | Task?> GetPackagesAsync(string version, CancellationToken token); 24 | } 25 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IToolsService.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Models.Framework; 2 | using Knapcode.NuGetTools.Logic.Models.Version; 3 | using Knapcode.NuGetTools.Logic.Models.VersionRange; 4 | 5 | namespace Knapcode.NuGetTools.Logic; 6 | 7 | public interface IToolsService : IVersionedService 8 | { 9 | FrameworkCompatibilityOutput FrameworkCompatibility(FrameworkCompatibilityInput input); 10 | GetNearestFrameworkOutput GetNearestFramework(GetNearestFrameworkInput input); 11 | ParseFrameworkOutput ParseFramework(ParseFrameworkInput input); 12 | ParseVersionOutput ParseVersion(ParseVersionInput input); 13 | ParseVersionRangeOutput ParseVersionRange(ParseVersionRangeInput input); 14 | VersionComparisonOutput VersionComparison(VersionComparisonInput input); 15 | VersionSatisfiesOutput VersionSatisfies(VersionSatisfiesInput input); 16 | FindBestVersionMatchOutput FindBestVersionMatch(FindBestVersionMatchInput input); 17 | SortVersionsOutput SortVersions(SortVersionsInput input); 18 | } 19 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/IVersionedService.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public interface IVersionedService 4 | { 5 | string Version { get; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Knapcode.NuGetTools.Logic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/ComparisonResult.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models; 2 | 3 | public enum ComparisonResult 4 | { 5 | LessThan, 6 | Equal, 7 | GreaterThan 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/FrameworkCompatibilityInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 2 | 3 | public class FrameworkCompatibilityInput 4 | { 5 | public string? Project { get; set; } 6 | public string? Package { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/FrameworkCompatibilityOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 4 | 5 | public class FrameworkCompatibilityOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public FrameworkCompatibilityInput? Input { get; set; } 9 | public bool IsProjectValid { get; set; } 10 | public bool IsPackageValid { get; set; } 11 | public IFramework? Project { get; set; } 12 | public IFramework? Package { get; set; } 13 | public bool IsCompatible { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/FrameworkPrecedenceInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 2 | 3 | public class FrameworkPrecedenceInput 4 | { 5 | public bool IncludeProfiles { get; set; } 6 | public bool ExcludePortable { get; set; } 7 | public string? ExcludedIdentifiers { get; set; } 8 | public string? Framework { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/FrameworkPrecedenceOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 4 | 5 | public class FrameworkPrecedenceOutput 6 | { 7 | public required InputStatus InputStatus { get; set; } 8 | public required FrameworkPrecedenceInput Input { get; set; } 9 | public IFramework? Framework { get; set; } 10 | public required IReadOnlyList Precedence { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/GetNearestFrameworkInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 2 | 3 | public class GetNearestFrameworkInput 4 | { 5 | public string? Project { get; set; } 6 | public string? Package { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/GetNearestFrameworkOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 4 | 5 | public class GetNearestFrameworkOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public GetNearestFrameworkInput? Input { get; set; } 9 | public bool IsProjectValid { get; set; } 10 | public bool IsPackageValid { get; set; } 11 | public IFramework? Project { get; set; } 12 | public required IReadOnlyList Package { get; set; } 13 | public OutputFramework? Nearest { get; set; } 14 | public required IReadOnlyList Invalid { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/OutputFramework.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 4 | 5 | public class OutputFramework 6 | { 7 | public required string Input { get; set; } 8 | public required IFramework Framework { get; set; } 9 | public bool IsCompatible { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/ParseFrameworkInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 2 | 3 | public class ParseFrameworkInput 4 | { 5 | public string? Framework { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Framework/ParseFrameworkOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Framework; 4 | 5 | public class ParseFrameworkOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public ParseFrameworkInput? Input { get; set; } 9 | public IFramework? Framework { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/InputStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models; 2 | 3 | public enum InputStatus 4 | { 5 | Missing, 6 | Invalid, 7 | Valid 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/ParseVersionInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Version; 2 | 3 | public class ParseVersionInput 4 | { 5 | public string? Version { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/ParseVersionOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Version; 4 | 5 | public class ParseVersionOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public ParseVersionInput? Input { get; set; } 9 | public IVersion? Version { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/SortVersionsInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Version; 2 | 3 | public class SortVersionsInput 4 | { 5 | public string? Versions { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/SortVersionsOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Version; 4 | 5 | public class SortVersionsOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public SortVersionsInput? Input { get; set; } 9 | public required IReadOnlyList Versions { get; set; } 10 | public required IReadOnlyList Invalid { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/VersionComparisonInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.Version; 2 | 3 | public class VersionComparisonInput 4 | { 5 | public string? VersionA { get; set; } 6 | public string? VersionB { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/Version/VersionComparisonOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.Version; 4 | 5 | public class VersionComparisonOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public VersionComparisonInput? Input { get; set; } 9 | public bool IsVersionAValid { get; set; } 10 | public bool IsVersionBValid { get; set; } 11 | public IVersion? VersionA { get; set; } 12 | public IVersion? VersionB { get; set; } 13 | public ComparisonResult Result { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/FindBestVersionMatchInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 2 | 3 | public class FindBestVersionMatchInput 4 | { 5 | public string? VersionRange { get; set; } 6 | public string? Versions { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/FindBestVersionMatchOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 4 | 5 | public class FindBestVersionMatchOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public FindBestVersionMatchInput? Input { get; set; } 9 | public bool IsVersionRangeValid { get; set; } 10 | public bool IsVersionValid { get; set; } 11 | public IVersionRange? VersionRange { get; set; } 12 | public required IReadOnlyList Versions { get; set; } 13 | public OutputVersion? BestMatch { get; set; } 14 | public required IReadOnlyList Invalid { get; set; } 15 | public bool IsOperationSupported { get; set; } 16 | public bool AreVersionsSorted { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/OutputVersion.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 4 | 5 | public class OutputVersion 6 | { 7 | public required string Input { get; set; } 8 | public required IVersion Version { get; set; } 9 | public bool Satisfies { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/ParseVersionRangeInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 2 | 3 | public class ParseVersionRangeInput 4 | { 5 | public string? VersionRange { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/ParseVersionRangeOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 4 | 5 | public class ParseVersionRangeOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public ParseVersionRangeInput? Input { get; set; } 9 | public IVersionRange? VersionRange { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/VersionSatisfiesInput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 2 | 3 | public class VersionSatisfiesInput 4 | { 5 | public string? VersionRange { get; set; } 6 | public string? Version { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Models/VersionRange/VersionSatisfiesOutput.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | namespace Knapcode.NuGetTools.Logic.Models.VersionRange; 4 | 5 | public class VersionSatisfiesOutput 6 | { 7 | public InputStatus InputStatus { get; set; } 8 | public VersionSatisfiesInput? Input { get; set; } 9 | public bool IsVersionRangeValid { get; set; } 10 | public bool IsVersionValid { get; set; } 11 | public IVersionRange? VersionRange { get; set; } 12 | public IVersion? Version { get; set; } 13 | public bool Satisfies { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/NuGetRelease.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic; 2 | 3 | public enum NuGetRelease 4 | { 5 | Version2x, 6 | Version3x, 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IFramework.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IFramework 4 | { 5 | string ShortFolderName { get; } 6 | string DotNetFrameworkName { get; } 7 | string Identifier { get; } 8 | Version Version { get; } 9 | bool HasProfile { get; } 10 | string Profile { get; } 11 | bool IsPlatformAvailable { get; } 12 | bool HasPlatform { get; } 13 | string Platform { get; } 14 | Version PlatformVersion { get; } 15 | string ToStringResult { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IFrameworkLogic.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IFrameworkLogic 4 | { 5 | IFramework Parse(string input); 6 | bool IsCompatible(IFramework project, IFramework package); 7 | IFramework? GetNearest(IFramework project, IEnumerable package); 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/INuGetLogic.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface INuGetLogic 4 | { 5 | IFrameworkLogic Framework { get; } 6 | IVersionLogic Version { get; } 7 | IVersionRangeLogic VersionRange { get; } 8 | IReadOnlyList AssemblyNames { get; } 9 | } 10 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IVersion.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IVersion 4 | { 5 | int Revision { get; } 6 | bool IsSemVer2 { get; } 7 | bool IsPrerelease { get; } 8 | string NormalizedString { get; } 9 | bool NormalizedStringAvailable { get; } 10 | string FullString { get; } 11 | bool IsSemVer2Available { get; } 12 | bool FullStringAvailable { get; } 13 | string ToStringResult { get; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IVersionLogic.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IVersionLogic 4 | { 5 | IVersion Parse(string input); 6 | int Compare(IVersion versionA, IVersion versionB); 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IVersionRange.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IVersionRange 4 | { 5 | string NormalizedString { get; } 6 | bool IsFloating { get; } 7 | string PrettyPrint { get; } 8 | bool HasLowerBound { get; } 9 | bool HasUpperBound { get; } 10 | bool IsMinInclusive { get; } 11 | bool IsMaxInclusive { get; } 12 | IVersion MinVersion { get; } 13 | IVersion MaxVersion { get; } 14 | string LegacyShortString { get; } 15 | string LegacyString { get; } 16 | string OriginalString { get; } 17 | bool LegacyShortStringAvailable { get; } 18 | bool IsFloatingAvailable { get; } 19 | bool OriginalStringAvailable { get; } 20 | bool LegacyStringAvailable { get; } 21 | } 22 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/IVersionRangeLogic.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public interface IVersionRangeLogic 4 | { 5 | IVersionRange Parse(string input); 6 | bool Satisfies(IVersionRange versionRange, IVersion version); 7 | IVersion? FindBestMatch(IVersionRange versionRange, IEnumerable versions); 8 | bool IsBetter(IVersionRange versionRange, IVersion current, IVersion considering); 9 | bool FindBestMatchAvailable { get; } 10 | bool IsBetterAvailable { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/NuGetAssembly.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Reflection; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | 7 | namespace Knapcode.NuGetTools.Logic.Wrappers; 8 | 9 | public record NuGetAssembly( 10 | string RelativePath, 11 | string AssemblyName, 12 | string Sha256Hash, 13 | long FileSize, 14 | string CustomAttributes) 15 | { 16 | public static NuGetAssembly FromAssembly(string relativePath, Assembly assembly) 17 | { 18 | var assemblyName = assembly.FullName ?? throw new ArgumentException("No full name could be found."); 19 | 20 | string sha256Hash; 21 | long fileSize; 22 | using (var fileStream = new FileStream(assembly.Location, FileMode.Open, FileAccess.Read)) 23 | { 24 | sha256Hash = Convert.ToHexString(SHA256.HashData(fileStream)); 25 | fileSize = fileStream.Length; 26 | } 27 | 28 | var customAttributes = GetCustomAttributes(assembly); 29 | 30 | return new NuGetAssembly( 31 | relativePath, 32 | assemblyName, 33 | sha256Hash, 34 | fileSize, 35 | customAttributes); 36 | } 37 | 38 | private static string GetCustomAttributes(Assembly assembly) 39 | { 40 | var builder = new StringBuilder(); 41 | foreach (var attribute in assembly.CustomAttributes) 42 | { 43 | builder.Append("[assembly: "); 44 | 45 | var attributeName = attribute.AttributeType.Name; 46 | const string suffix = nameof(Attribute); 47 | if (attributeName.EndsWith(suffix, StringComparison.Ordinal)) 48 | { 49 | attributeName = attributeName.Substring(0, attributeName.Length - suffix.Length); 50 | } 51 | 52 | builder.Append(attributeName); 53 | builder.Append('('); 54 | 55 | var addedAttribute = false; 56 | 57 | foreach (var arg in attribute.ConstructorArguments) 58 | { 59 | if (addedAttribute) 60 | { 61 | builder.Append(", "); 62 | } 63 | else 64 | { 65 | addedAttribute = true; 66 | } 67 | 68 | AppendArgument(builder, arg); 69 | 70 | } 71 | 72 | foreach (var arg in attribute.NamedArguments) 73 | { 74 | if (addedAttribute) 75 | { 76 | builder.Append(", "); 77 | } 78 | else 79 | { 80 | addedAttribute = true; 81 | } 82 | 83 | builder.Append(arg.MemberName); 84 | builder.Append(" = "); 85 | AppendArgument(builder, arg.TypedValue); 86 | } 87 | 88 | builder.AppendLine(")]"); 89 | } 90 | 91 | return builder.ToString(); 92 | } 93 | 94 | private static void AppendArgument(StringBuilder builder, CustomAttributeTypedArgument arg) 95 | { 96 | switch (arg.Value) 97 | { 98 | case ReadOnlyCollection collectionValue: 99 | builder.AppendFormat("new[] { "); 100 | foreach (var innerArg in collectionValue) 101 | { 102 | AppendArgument(builder, innerArg); 103 | } 104 | builder.Append(" }"); 105 | break; 106 | default: 107 | var primitive = SymbolDisplay.FormatPrimitive(arg.Value!, quoteStrings: true, useHexadecimalNumbers: false); 108 | if (primitive is null) 109 | { 110 | throw new NotImplementedException($"Writing out attribute value of type {arg.ArgumentType.FullName} is not implemented yet."); 111 | } 112 | builder.Append(primitive); 113 | break; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/NuGetPackage.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public record NuGetPackage( 4 | string Id, 5 | string Version, 6 | IReadOnlyList Assemblies); 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Logic/Wrappers/VersionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Logic.Wrappers; 2 | 3 | public static class VersionExtensions 4 | { 5 | public static string GetPrettyString(this IVersion version) 6 | { 7 | if (version.FullStringAvailable) 8 | { 9 | return version.FullString; 10 | } 11 | 12 | if (version.NormalizedStringAvailable) 13 | { 14 | return version.NormalizedString; 15 | } 16 | 17 | return version.ToStringResult; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.PackageDownloader/Knapcode.NuGetTools.PackageDownloader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.PackageDownloader/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Knapcode.NuGetTools.PackageDownloader": { 4 | "commandName": "Project", 5 | "commandLineArgs": "download" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Sandbox/Knapcode.NuGetTools.Sandbox.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Sandbox/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Sandbox; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Reflection; 3 | using NuGet.Versioning; 4 | 5 | namespace Knapcode.NuGetTools.Website; 6 | 7 | public static class Configuration 8 | { 9 | static Configuration() 10 | { 11 | var assembly = typeof(Configuration).GetTypeInfo().Assembly!; 12 | 13 | AssemblyVersion = assembly 14 | .GetCustomAttribute()? 15 | .Version!; 16 | 17 | AssemblyFileVersion = assembly 18 | .GetCustomAttribute()? 19 | .Version!; 20 | 21 | AssemblyInformationalVersion = assembly 22 | .GetCustomAttribute()? 23 | .InformationalVersion!; 24 | 25 | if (SemanticVersion.TryParse(AssemblyInformationalVersion, out var version)) 26 | { 27 | AssemblyCommitHash = version.Metadata; 28 | var shortCommitHash = version.Metadata?.Length > 8 ? version.Metadata.Substring(0, 8) : version.Metadata; 29 | AssemblyInformationalVersion = new SemanticVersion(version.Major, version.Minor, version.Patch, version.ReleaseLabels, shortCommitHash).ToFullString(); 30 | } 31 | 32 | AssemblyBuildTimestamp = DateTimeOffset.Parse(assembly 33 | .GetCustomAttributes() 34 | .FirstOrDefault(x => x.Key == "BuildTimestamp")? 35 | .Value ?? "2001-01-01", CultureInfo.InvariantCulture); 36 | } 37 | 38 | public static string AssemblyVersion { get; private set; } 39 | public static string AssemblyFileVersion { get; private set; } 40 | public static string AssemblyInformationalVersion { get; private set; } 41 | public static string? AssemblyCommitHash { get; private set; } 42 | public static DateTimeOffset AssemblyBuildTimestamp { get; private set; } 43 | } 44 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Controllers/FrameworksController.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Knapcode.NuGetTools.Website; 5 | 6 | [Route("api/[controller]")] 7 | public class FrameworksController : Controller 8 | { 9 | private readonly IToolsFactory _toolsFactory; 10 | 11 | public FrameworksController(IToolsFactory toolsService) 12 | { 13 | _toolsFactory = toolsService; 14 | } 15 | 16 | [HttpGet("short-folder-names")] 17 | public async Task> GetShortFolderNames(CancellationToken token) 18 | { 19 | var frameworkList = await _toolsFactory.GetFrameworkListAsync(token); 20 | return frameworkList.ShortFolderNames; 21 | } 22 | 23 | [HttpGet("identifiers")] 24 | public async Task> GetIdentifiers(CancellationToken token) 25 | { 26 | var frameworkList = await _toolsFactory.GetFrameworkListAsync(token); 27 | return frameworkList.Identifiers; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Extensions/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Extensions.Hosting; 2 | 3 | public static class ExtensionMethods 4 | { 5 | public static bool IsAutomation(this IHostEnvironment env) 6 | { 7 | return env.IsEnvironment("Automation"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Knapcode.NuGetTools.Website.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | PreserveNewest 10 | 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Models/SelectedVersionOutput.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Website; 2 | 3 | public class SelectedVersionOutput 4 | { 5 | public required string CurrentVersion { get; set; } 6 | public required IEnumerable VersionUrls { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Models/SelectedVersionOutputT.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Website; 2 | 3 | public class SelectedVersionOutput : SelectedVersionOutput 4 | { 5 | public required T Output { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Models/VersionUrl.cs: -------------------------------------------------------------------------------- 1 | namespace Knapcode.NuGetTools.Website; 2 | 3 | public class VersionUrl 4 | { 5 | public required string Version { get; set; } 6 | public required string Url { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Program.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Website; 2 | using Microsoft.ApplicationInsights.Extensibility; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | builder.Services.AddControllersWithViews(); 7 | builder.Services.AddApplicationInsightsTelemetry(); 8 | builder.Services.AddNuGetTools(); 9 | builder.Services.AddSingleton(); 10 | 11 | var app = builder.Build(); 12 | 13 | if (!app.Environment.IsDevelopment() && !app.Environment.IsAutomation()) 14 | { 15 | app.UseExceptionHandler("/error"); 16 | app.UseHsts(); 17 | } 18 | 19 | app.UseStaticFiles(); 20 | 21 | app.MapControllerRoute( 22 | name: "default", 23 | pattern: "{controller=Home}/{action=Index}/{id?}"); 24 | 25 | app.Run(); 26 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:60353/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Knapcode.NuGetTools.Website": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "launchUrl": "http://localhost:5000/", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Knapcode.NuGetTools.Logic; 2 | using Knapcode.NuGetTools.Logic.Direct; 3 | using Knapcode.NuGetTools.Logic.NuGet3x; 4 | using Knapcode.NuGetTools.Logic.Wrappers; 5 | using NuGet.Common; 6 | using NuGet.Versioning; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection; 9 | 10 | public static class ServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddNuGetTools(this IServiceCollection services) 13 | { 14 | services.AddSingleton(serviceProvider => 15 | { 16 | var hostingEnvironment = serviceProvider.GetRequiredService(); 17 | 18 | var settings = new InMemorySettings(); 19 | var nuGetSettings = new NuGetSettings(settings); 20 | nuGetSettings.GlobalPackagesFolder = Path.Combine(hostingEnvironment.ContentRootPath, "packages"); 21 | 22 | return nuGetSettings; 23 | }); 24 | 25 | services.AddTransient(); 26 | services.AddTransient(); 27 | services.AddTransient(); 28 | services.AddTransient(); 29 | 30 | services.AddSingleton(); 31 | 32 | AddDirectToolsServices(services); 33 | 34 | services.AddSingleton(serviceProvider => 35 | { 36 | var versioned = GetVersionedToolsFactory(serviceProvider); 37 | if (versioned is not null) 38 | { 39 | return versioned; 40 | } 41 | 42 | return serviceProvider.GetRequiredService(); 43 | }); 44 | 45 | return services; 46 | } 47 | 48 | private static void AddDirectToolsServices(IServiceCollection services) 49 | { 50 | services.AddTransient(); 51 | services.AddTransient(); 52 | services.AddTransient(); 53 | services.AddTransient(); 54 | 55 | var clientVersion = NuGetVersion.Parse(ClientVersionUtility.GetNuGetAssemblyVersion()).ToNormalizedString(); 56 | 57 | services.AddTransient(serviceProvider => 58 | { 59 | return new ToolsService( 60 | clientVersion, 61 | serviceProvider.GetRequiredService()); 62 | }); 63 | 64 | services.AddTransient(serviceProvider => 65 | { 66 | return new FrameworkPrecedenceService( 67 | clientVersion, 68 | serviceProvider.GetRequiredService(), 69 | serviceProvider.GetRequiredService()); 70 | }); 71 | 72 | services.AddSingleton(); 73 | } 74 | 75 | private static VersionedToolsFactory? GetVersionedToolsFactory(IServiceProvider serviceProvider) 76 | { 77 | var settings = serviceProvider.GetRequiredService(); 78 | if (!Directory.Exists(settings.GlobalPackagesFolder) 79 | || !Directory.EnumerateDirectories(settings.GlobalPackagesFolder).Any()) 80 | { 81 | return null; 82 | } 83 | 84 | var factory = serviceProvider.GetRequiredService(); 85 | var versions = factory.GetAvailableVersionsAsync(CancellationToken.None).Result; 86 | if (!versions.Any()) 87 | { 88 | return null; 89 | } 90 | 91 | return factory; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/TagHelpers/ActionTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Rendering; 3 | using Microsoft.AspNetCore.Mvc.Routing; 4 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 5 | using Microsoft.AspNetCore.Razor.TagHelpers; 6 | 7 | namespace Knapcode.NuGetTools.Website; 8 | 9 | public abstract class ActionTagHelper : TagHelper 10 | { 11 | private readonly IUrlHelperFactory _urlHelperFactory; 12 | 13 | public ActionTagHelper(IUrlHelperFactory urlHelperFactory) 14 | { 15 | _urlHelperFactory = urlHelperFactory; 16 | } 17 | 18 | [ViewContext] 19 | public ViewContext? ViewContext { get; set; } 20 | 21 | public string? Value { get; set; } 22 | 23 | protected abstract string ActionName { get; } 24 | protected abstract RouteValueDictionary GetRouteValues(string value); 25 | 26 | public override void Process(TagHelperContext context, TagHelperOutput output) 27 | { 28 | // build the output 29 | output.TagName = "a"; 30 | 31 | if (Value is null || ViewContext is null) 32 | { 33 | return; 34 | } 35 | 36 | // get the action URL 37 | var urlHelper = _urlHelperFactory.GetUrlHelper(ViewContext); 38 | 39 | var routeValues = GetRouteValues(Value); 40 | 41 | const string nuGetVersionKey = "nuGetVersion"; 42 | if (!routeValues.ContainsKey(nuGetVersionKey) 43 | && ViewContext.RouteData.Values.TryGetValue(nuGetVersionKey, out var nuGetVersion)) 44 | { 45 | routeValues.Add(nuGetVersionKey, nuGetVersion); 46 | } 47 | 48 | var href = urlHelper.Action(ActionName, "Home", routeValues); 49 | 50 | output.Attributes.Add("href", href); 51 | 52 | if (output.TagMode != TagMode.StartTagAndEndTag) 53 | { 54 | output.TagMode = TagMode.StartTagAndEndTag; 55 | output.Content = new DefaultTagHelperContent().Append(Value); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/TagHelpers/ParseFrameworkTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Routing; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | 4 | namespace Knapcode.NuGetTools.Website; 5 | 6 | [HtmlTargetElement("parse-framework")] 7 | public class ParseFrameworkTagHelper : ActionTagHelper 8 | { 9 | private readonly IUrlHelperFactory _urlHelperFactory; 10 | 11 | public ParseFrameworkTagHelper(IUrlHelperFactory urlHelperFactory) : base(urlHelperFactory) 12 | { 13 | _urlHelperFactory = urlHelperFactory; 14 | } 15 | 16 | protected override string ActionName => nameof(HomeController.ParseFramework); 17 | 18 | protected override RouteValueDictionary GetRouteValues(string value) 19 | { 20 | return new RouteValueDictionary(new { framework = value }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/TagHelpers/ParseVersionRangeTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Routing; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | 4 | namespace Knapcode.NuGetTools.Website; 5 | 6 | [HtmlTargetElement("parse-version-range")] 7 | public class ParseVersionRangeTagHelper : ActionTagHelper 8 | { 9 | private readonly IUrlHelperFactory _urlHelperFactory; 10 | 11 | public ParseVersionRangeTagHelper(IUrlHelperFactory urlHelperFactory) : base(urlHelperFactory) 12 | { 13 | _urlHelperFactory = urlHelperFactory; 14 | } 15 | 16 | protected override string ActionName => nameof(HomeController.ParseVersionRange); 17 | 18 | protected override RouteValueDictionary GetRouteValues(string value) 19 | { 20 | return new RouteValueDictionary(new { versionRange = value }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/TagHelpers/ParseVersionTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Routing; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | 4 | namespace Knapcode.NuGetTools.Website; 5 | 6 | [HtmlTargetElement("parse-version")] 7 | public class ParseVersionTagHelper : ActionTagHelper 8 | { 9 | private readonly IUrlHelperFactory _urlHelperFactory; 10 | 11 | public ParseVersionTagHelper(IUrlHelperFactory urlHelperFactory) : base(urlHelperFactory) 12 | { 13 | _urlHelperFactory = urlHelperFactory; 14 | } 15 | 16 | protected override string ActionName => nameof(HomeController.ParseVersion); 17 | 18 | protected override RouteValueDictionary GetRouteValues(string value) 19 | { 20 | return new RouteValueDictionary(new { version = value }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Telemetry/TelemetryProcessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ApplicationInsights.Channel; 2 | using Microsoft.ApplicationInsights.DataContracts; 3 | using Microsoft.ApplicationInsights.Extensibility; 4 | 5 | namespace Knapcode.NuGetTools.Website; 6 | 7 | public class RequestSuccessInitializer : ITelemetryInitializer 8 | { 9 | private const string PropertyKey = "RequestSuccessInitializer"; 10 | private const string PropertyValue = "true"; 11 | 12 | private static readonly HashSet SuccessResponseCodes = new HashSet 13 | { 14 | 404 15 | }; 16 | 17 | public void Initialize(ITelemetry telemetry) 18 | { 19 | var requestTelemetry = telemetry as RequestTelemetry; 20 | if (requestTelemetry == null) 21 | { 22 | return; 23 | } 24 | 25 | int responseCode; 26 | if (!int.TryParse(requestTelemetry.ResponseCode, out responseCode)) 27 | { 28 | return; 29 | } 30 | 31 | if (SuccessResponseCodes.Contains(responseCode)) 32 | { 33 | requestTelemetry.Success = true; 34 | 35 | var itemTelemetry = (ISupportProperties)telemetry; 36 | itemTelemetry.Properties[PropertyKey] = PropertyValue; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/FindBestVersionMatch.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Find Best Version Match"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Find best version match

8 |

9 | The find best match operation is what NuGet uses to select one version of a package from many available versions based on a version specification (version range). 10 | Enter a version range and one or more versions. 11 |

12 | 13 |
14 |
15 | 16 | 18 |
19 |
20 | 21 | 22 |
23 | 24 |
25 | 26 |
27 | @if (output.InputStatus == InputStatus.Valid) 28 | { 29 | if (output.BestMatch != null) 30 | { 31 | 35 | } 36 | else if (output.IsOperationSupported) 37 | { 38 | 41 | } 42 | else 43 | { 44 | 47 | } 48 | 49 | 87 | } 88 | else if (output.InputStatus == InputStatus.Invalid) 89 | { 90 | if (!output.IsVersionRangeValid) 91 | { 92 | 95 | } 96 | 97 | if (!output.IsVersionValid) 98 | { 99 | 108 | } 109 | } 110 |
111 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/FrameworkCompatibility.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Framework Compatibility"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Check framework compatibility

8 |

9 | Enter the project and package frameworks below. Both short form (e.g. net45) and long form (e.g. .NETFramework,Version=v4.5) are supported. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 |
19 | 20 | 22 |
23 | 24 | 25 |
26 | 27 |
28 | @if (@output.InputStatus == InputStatus.Valid) 29 | { 30 | if (@output.IsCompatible) 31 | { 32 | 36 | } 37 | else 38 | { 39 | 43 | } 44 | } 45 | else if (@output.InputStatus == InputStatus.Invalid) 46 | { 47 | if (!@output.IsProjectValid) 48 | { 49 | 52 | } 53 | 54 | if (!@output.IsPackageValid) 55 | { 56 | 59 | } 60 | } 61 |
62 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/FrameworkPrecedence.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Framework Precedence"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Framework Precedence

8 |

9 | If multiple sets of assets are available in a package (under different frameworks), NuGet must choose between 10 | these frameworks. Enter a project framework to see the order of precedence of possible package frameworks. Note 11 | that this list is not exhaustive. 12 |

13 | 14 |
15 |
16 | 17 | 19 |
20 |
21 | 22 | 24 |
25 |
26 | 31 |
32 |
33 | 38 |
39 | 40 |
41 | 42 |
43 | @if (output.InputStatus == InputStatus.Valid) 44 | { 45 | if (!output.Precedence.Any()) 46 | { 47 | 51 | } 52 | else 53 | { 54 |

55 | The @output.Framework?.DotNetFrameworkName () 56 | project framework has the following package framework precedence list. 57 |

58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | @for (var i = 0; i < output.Precedence.Count; i++) 69 | { 70 | 71 | 72 | 73 | 74 | 75 | } 76 | 77 |
Order.NET Framework NameShort Folder Name
@(i + 1)@output.Precedence[i].DotNetFrameworkName@output.Precedence[i].ShortFolderName
78 | } 79 | } 80 | else if (@output.InputStatus == InputStatus.Invalid) 81 | { 82 | 85 | } 86 |
87 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/GetNearestFramework.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Get Nearest Framework"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Get Nearest Framework

8 |

9 | The get nearest framework operation is what NuGet uses to select one framework from many available framework assets in a package based on the project's framework. Enter one project framework and one or more package frameworks. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 |
19 | 20 | 22 |
23 | 24 |
25 | 26 |
27 | @if (@output.InputStatus == InputStatus.Valid) 28 | { 29 | if (@output.Nearest != null) 30 | { 31 | 36 | } 37 | else 38 | { 39 | 43 | } 44 | 45 | 75 | } 76 | else if (@output.InputStatus == InputStatus.Invalid) 77 | { 78 | if (!@output.IsProjectValid) 79 | { 80 | 83 | } 84 | 85 | if (!@output.IsPackageValid) 86 | { 87 | 96 | } 97 | } 98 |
99 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/PackageMetadata.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput> 2 | @{ 3 | ViewData["Title"] = "About this NuGet SDK version"; 4 | var output = Model.Output; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | The following packages are used by version @Model.CurrentVersion of NuGet and by this tool. 10 |

11 | 12 |
13 | @if (output.Count > 0) 14 | { 15 | foreach (var package in output) 16 | { 17 |

@package.Id @package.Version

18 |

19 | NuGet Gallery 20 | | 21 | NuGet Package Explorer (nuget.info) 22 | | 23 | FuGet 24 | | 25 | NuGet Trends 26 |

27 | foreach (var assembly in package.Assemblies) 28 | { 29 |

@(System.IO.Path.GetFileName(assembly.RelativePath))

30 |
31 |
Path
32 |
@assembly.RelativePath
33 | 34 |
Assembly name
35 |
@assembly.AssemblyName
36 | 37 |
SHA-256 hash
38 |
@assembly.Sha256Hash
39 | 40 |
File size
41 |
@((assembly.FileSize / (1024.0)).ToString("F2")) KB (@assembly.FileSize bytes)
42 |
43 | Custom attributes 44 |
@assembly.CustomAttributes
45 | } 46 | } 47 | } 48 |
49 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/ParseFramework.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Parse Framework"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Parse a framework

8 |

9 | Enter a NuGet framework to see how it parses. Both short form (e.g. net45) and long form (e.g. .NETFramework,Version=v4.5) are supported. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 | 19 |
20 | 21 |
22 | @if (@output.InputStatus == InputStatus.Valid) 23 | { 24 | 70 | } 71 | else if (@output.InputStatus == InputStatus.Invalid) 72 | { 73 | 76 | } 77 |
78 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/ParseVersion.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Parse Version"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Parse a version

8 |

9 | Enter a NuGet package version to see how it parses. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 | 19 |
20 | 21 |
22 | @if (@output.InputStatus == InputStatus.Valid) 23 | { 24 | 95 | } 96 | else if (@output.InputStatus == InputStatus.Invalid) 97 | { 98 | 101 | } 102 |
103 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/ParseVersionRange.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Parse Version Range"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Parse a version range

8 |

9 | Enter a NuGet package version range (version specification) to see how it parses. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 | 19 |
20 | 21 |
22 | @if (@output.InputStatus == InputStatus.Valid) 23 | { 24 | 104 | } 105 | else if (@output.InputStatus == InputStatus.Invalid) 106 | { 107 | 110 | } 111 |
112 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/SelectedVersionIndex.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "NuGet Tools"; 4 | } 5 | 6 |

NuGet Tools

7 |

8 | Current NuGet version: @Model.CurrentVersion 9 |

10 |

11 | NuGet loves parsing strings. These tools provide insight into how these strings are parsed. 12 |

13 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/SortVersions.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Sort Versions"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Sort versions

8 |

9 | Enter multiple NuGet package versions to sort them. 10 |

11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 |
21 | @if (output.InputStatus == InputStatus.Valid) 22 | { 23 | 34 | 35 | if (output.Invalid.Any()) 36 | { 37 | 48 | } 49 | } 50 | else if (output.InputStatus == InputStatus.Invalid) 51 | { 52 | 63 | } 64 |
-------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/VersionComparison.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Version Comparison"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Compare two versions

8 |

9 | Enter two NuGet package versions to compare them. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 |
19 | 20 | 22 |
23 | 24 | 25 |
26 | 27 |
28 | @if (@output.InputStatus == InputStatus.Valid) 29 | { 30 | 46 | } 47 | else if (@output.InputStatus == InputStatus.Invalid) 48 | { 49 | if (!@output.IsVersionAValid) 50 | { 51 | 54 | } 55 | 56 | if (!@output.IsVersionBValid) 57 | { 58 | 61 | } 62 | } 63 |
-------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Home/VersionSatisfies.cshtml: -------------------------------------------------------------------------------- 1 | @model SelectedVersionOutput 2 | @{ 3 | ViewData["Title"] = "Version Satisfies"; 4 | var output = Model.Output; 5 | } 6 | 7 |

Version satisfies a version range

8 |

9 | Enter a NuGet version range (version specification) and a NuGet package version to see if the version satisfies the version range. 10 |

11 | 12 |
13 |
14 | 15 | 17 |
18 |
19 | 20 | 22 |
23 | 24 |
25 | 26 |
27 | @if (@output.InputStatus == InputStatus.Valid) 28 | { 29 | @if (output.Satisfies) 30 | { 31 | 36 | } 37 | else 38 | { 39 | 44 | } 45 | } 46 | else if (@output.InputStatus == InputStatus.Invalid) 47 | { 48 | if (!@output.IsVersionRangeValid) 49 | { 50 | 53 | } 54 | 55 | if (!@output.IsVersionValid) 56 | { 57 | 60 | } 61 | } 62 |
-------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 |

Development Mode

9 |

10 | Swapping to Development environment will display more detailed information about the error that occurred. 11 |

12 |

13 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 14 |

15 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using Knapcode.NuGetTools.Logic.Models; 2 | @model SelectedVersionOutput 3 | 4 | 5 | 6 | 7 | 8 | @ViewData["Title"] - NuGet Tools - NuGet v@(Model.CurrentVersion) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | @Html.Raw(JavaScriptSnippet.FullScript) 22 | 23 | 24 | 81 |
82 | @RenderBody() 83 |
84 |
85 |

86 | Site by Joel Verhagen, 87 | version @(Configuration.AssemblyInformationalVersion) (commit). 88 | Last built on @Configuration.AssemblyBuildTimestamp.ToString("yyyy-MM-dd"). 89 | Visit the GitHub repository. 90 |

91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 106 | 110 | 111 | 112 | 113 | 114 | 115 | @RenderSection("scripts", required: false) 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Knapcode.NuGetTools.Logic.Models 2 | @using Knapcode.NuGetTools.Logic.Models.Framework 3 | @using Knapcode.NuGetTools.Logic.Models.Version 4 | @using Knapcode.NuGetTools.Logic.Models.VersionRange 5 | @using Knapcode.NuGetTools.Logic.Wrappers 6 | @using Knapcode.NuGetTools.Website 7 | @addTagHelper *, Knapcode.NuGetTools.Website 8 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 9 | @inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet 10 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Warning", 6 | "Microsoft": "Warning" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/_references.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | .version-select { 14 | margin: 10px; 15 | width: 155px; 16 | } 17 | 18 | .results { 19 | padding: 10px 0px; 20 | } 21 | 22 | ul, ol { 23 | margin: 5px 0px; 24 | } 25 | 26 | li { 27 | padding: 2px 0px; 28 | } 29 | 30 | .monospace { 31 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 32 | } 33 | 34 | .cmp-output { 35 | line-height: 2em; 36 | } 37 | 38 | .cmp-symbol { 39 | font-size: 2em; 40 | font-weight: bold; 41 | vertical-align: middle; 42 | padding: 0 5px 0 5px; 43 | } 44 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelverhagen/NuGetTools/d66010dc1e5492d7e658a011c9460f704feab51b/src/Knapcode.NuGetTools.Website/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Change the NuGet version when selection changes. 2 | $("#version-select").change(function (event) { 3 | var url = $(this).val(); 4 | if (url) { 5 | document.location = url; 6 | } 7 | }); 8 | 9 | // Enable autocomplete for framework short folder names. 10 | var frameworksShortFolderNames = new Bloodhound({ 11 | datumTokenizer: Bloodhound.tokenizers.whitespace, 12 | queryTokenizer: Bloodhound.tokenizers.whitespace, 13 | prefetch: '../api/frameworks/short-folder-names' 14 | }); 15 | 16 | frameworksShortFolderNames.initialize(); 17 | 18 | $(".framework-short-folder-name-typeahead").typeahead({ 19 | autoSelect: false, 20 | source: frameworksShortFolderNames.ttAdapter() 21 | }); 22 | 23 | // Enable autocomplete for framework identifiers. 24 | var frameworksIdentifiers = new Bloodhound({ 25 | datumTokenizer: Bloodhound.tokenizers.whitespace, 26 | queryTokenizer: Bloodhound.tokenizers.whitespace, 27 | prefetch: '../api/frameworks/identifiers' 28 | }); 29 | 30 | frameworksIdentifiers.initialize(); 31 | 32 | $(".framework-identifier-typeahead").typeahead({ 33 | autoSelect: false, 34 | source: frameworksIdentifiers.ttAdapter() 35 | }); 36 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2015 Twitter, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"} -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelverhagen/NuGetTools/d66010dc1e5492d7e658a011c9460f704feab51b/src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelverhagen/NuGetTools/d66010dc1e5492d7e658a011c9460f704feab51b/src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelverhagen/NuGetTools/d66010dc1e5492d7e658a011c9460f704feab51b/src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelverhagen/NuGetTools/d66010dc1e5492d7e658a011c9460f704feab51b/src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/bootstrap3-typeahead/bootstrap3-typeahead.min.js: -------------------------------------------------------------------------------- 1 | (function(root,factory){"use strict";if(typeof module!=="undefined"&&module.exports){module.exports=factory(require("jquery"))}else if(typeof define==="function"&&define.amd){define(["jquery"],function($){return factory($)})}else{factory(root.jQuery)}})(this,function($){"use strict";var Typeahead=function(element,options){this.$element=$(element);this.options=$.extend({},$.fn.typeahead.defaults,options);this.matcher=this.options.matcher||this.matcher;this.sorter=this.options.sorter||this.sorter;this.select=this.options.select||this.select;this.autoSelect=typeof this.options.autoSelect=="boolean"?this.options.autoSelect:true;this.highlighter=this.options.highlighter||this.highlighter;this.render=this.options.render||this.render;this.updater=this.options.updater||this.updater;this.displayText=this.options.displayText||this.displayText;this.source=this.options.source;this.delay=this.options.delay;this.$menu=$(this.options.menu);this.$appendTo=this.options.appendTo?$(this.options.appendTo):null;this.fitToElement=typeof this.options.fitToElement=="boolean"?this.options.fitToElement:false;this.shown=false;this.listen();this.showHintOnFocus=typeof this.options.showHintOnFocus=="boolean"||this.options.showHintOnFocus==="all"?this.options.showHintOnFocus:false;this.afterSelect=this.options.afterSelect;this.addItem=false;this.value=this.$element.val()||this.$element.text()};Typeahead.prototype={constructor:Typeahead,select:function(){var val=this.$menu.find(".active").data("value");this.$element.data("active",val);if(this.autoSelect||val){var newVal=this.updater(val);if(!newVal){newVal=""}this.$element.val(this.displayText(newVal)||newVal).text(this.displayText(newVal)||newVal).change();this.afterSelect(newVal)}return this.hide()},updater:function(item){return item},setSource:function(source){this.source=source},show:function(){var pos=$.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});var scrollHeight=typeof this.options.scrollHeight=="function"?this.options.scrollHeight.call():this.options.scrollHeight;var element;if(this.shown){element=this.$menu}else if(this.$appendTo){element=this.$menu.appendTo(this.$appendTo);this.hasSameParent=this.$appendTo.is(this.$element.parent())}else{element=this.$menu.insertAfter(this.$element);this.hasSameParent=true}if(!this.hasSameParent){element.css("position","fixed");var offset=this.$element.offset();pos.top=offset.top;pos.left=offset.left}var dropup=$(element).parent().hasClass("dropup");var newTop=dropup?"auto":pos.top+pos.height+scrollHeight;var right=$(element).hasClass("dropdown-menu-right");var newLeft=right?"auto":pos.left;element.css({top:newTop,left:newLeft}).show();if(this.options.fitToElement===true){element.css("width",this.$element.outerWidth()+"px")}this.shown=true;return this},hide:function(){this.$menu.hide();this.shown=false;return this},lookup:function(query){var items;if(typeof query!="undefined"&&query!==null){this.query=query}else{this.query=this.$element.val()||this.$element.text()||""}if(this.query.length0){this.$element.data("active",items[0])}else{this.$element.data("active",null)}if(this.options.addItem){items.push(this.options.addItem)}if(this.options.items=="all"){return this.render(items).show()}else{return this.render(items.slice(0,this.options.items)).show()}},matcher:function(item){var it=this.displayText(item);return~it.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(items){var beginswith=[];var caseSensitive=[];var caseInsensitive=[];var item;while(item=items.shift()){var it=this.displayText(item);if(!it.toLowerCase().indexOf(this.query.toLowerCase()))beginswith.push(item);else if(~it.indexOf(this.query))caseSensitive.push(item);else caseInsensitive.push(item)}return beginswith.concat(caseSensitive,caseInsensitive)},highlighter:function(item){var html=$("
");var query=this.query;var i=item.toLowerCase().indexOf(query.toLowerCase());var len=query.length;var leftPart;var middlePart;var rightPart;var strong;if(len===0){return html.text(item).html()}while(i>-1){leftPart=item.substr(0,i);middlePart=item.substr(i,len);rightPart=item.substr(i+len);strong=$("").text(middlePart);html.append(document.createTextNode(leftPart)).append(strong);item=rightPart;i=item.toLowerCase().indexOf(query.toLowerCase())}return html.append(document.createTextNode(item)).html()},render:function(items){var that=this;var self=this;var activeFound=false;var data=[];var _category=that.options.separator;$.each(items,function(key,value){if(key>0&&value[_category]!==items[key-1][_category]){data.push({__type:"divider"})}if(value[_category]&&(key===0||value[_category]!==items[key-1][_category])){data.push({__type:"category",name:value[_category]})}data.push(value)});items=$(data).map(function(i,item){if((item.__type||false)=="category"){return $(that.options.headerHtml).text(item.name)[0]}if((item.__type||false)=="divider"){return $(that.options.headerDivider)[0]}var text=self.displayText(item);i=$(that.options.item).data("value",item);i.find("a").html(that.highlighter(text,item));if(text==self.$element.val()){i.addClass("active");self.$element.data("active",item);activeFound=true}return i[0]});if(this.autoSelect&&!activeFound){items.filter(":not(.dropdown-header)").first().addClass("active");this.$element.data("active",items.first().data("value"))}this.$menu.html(items);return this},displayText:function(item){return typeof item!=="undefined"&&typeof item.name!="undefined"&&item.name||item},next:function(event){var active=this.$menu.find(".active").removeClass("active");var next=active.next();if(!next.length){next=$(this.$menu.find("li")[0])}next.addClass("active")},prev:function(event){var active=this.$menu.find(".active").removeClass("active");var prev=active.prev();if(!prev.length){prev=this.$menu.find("li").last()}prev.addClass("active")},listen:function(){this.$element.on("focus",$.proxy(this.focus,this)).on("blur",$.proxy(this.blur,this)).on("keypress",$.proxy(this.keypress,this)).on("input",$.proxy(this.input,this)).on("keyup",$.proxy(this.keyup,this));if(this.eventSupported("keydown")){this.$element.on("keydown",$.proxy(this.keydown,this))}this.$menu.on("click",$.proxy(this.click,this)).on("mouseenter","li",$.proxy(this.mouseenter,this)).on("mouseleave","li",$.proxy(this.mouseleave,this)).on("mousedown",$.proxy(this.mousedown,this))},destroy:function(){this.$element.data("typeahead",null);this.$element.data("active",null);this.$element.off("focus").off("blur").off("keypress").off("input").off("keyup");if(this.eventSupported("keydown")){this.$element.off("keydown")}this.$menu.remove();this.destroyed=true},eventSupported:function(eventName){var isSupported=eventName in this.$element;if(!isSupported){this.$element.setAttribute(eventName,"return;");isSupported=typeof this.$element[eventName]==="function"}return isSupported},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:if(e.shiftKey)return;e.preventDefault();this.prev();break;case 40:if(e.shiftKey)return;e.preventDefault();this.next();break}},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27]);if(!this.shown&&e.keyCode==40){this.lookup()}else{this.move(e)}},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},input:function(e){var currentValue=this.$element.val()||this.$element.text();if(this.value!==currentValue){this.value=currentValue;this.lookup()}},keyup:function(e){if(this.destroyed){return}switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break}},focus:function(e){if(!this.focused){this.focused=true;if(this.options.showHintOnFocus&&this.skipShowHintOnFocus!==true){if(this.options.showHintOnFocus==="all"){this.lookup("")}else{this.lookup()}}}if(this.skipShowHintOnFocus){this.skipShowHintOnFocus=false}},blur:function(e){if(!this.mousedover&&!this.mouseddown&&this.shown){this.hide();this.focused=false}else if(this.mouseddown){this.skipShowHintOnFocus=true;this.$element.focus();this.mouseddown=false}},click:function(e){e.preventDefault();this.skipShowHintOnFocus=true;this.select();this.$element.focus();this.hide()},mouseenter:function(e){this.mousedover=true;this.$menu.find(".active").removeClass("active");$(e.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=false;if(!this.focused&&this.shown)this.hide()},mousedown:function(e){this.mouseddown=true;this.$menu.one("mouseup",function(e){this.mouseddown=false}.bind(this))}};var old=$.fn.typeahead;$.fn.typeahead=function(option){var arg=arguments;if(typeof option=="string"&&option=="getActive"){return this.data("active")}return this.each(function(){var $this=$(this);var data=$this.data("typeahead");var options=typeof option=="object"&&option;if(!data)$this.data("typeahead",data=new Typeahead(this,options));if(typeof option=="string"&&data[option]){if(arg.length>1){data[option].apply(data,Array.prototype.slice.call(arg,1))}else{data[option]()}}})};$.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1,scrollHeight:0,autoSelect:true,afterSelect:$.noop,addItem:false,delay:0,separator:"category",headerHtml:'',headerDivider:''};$.fn.typeahead.Constructor=Typeahead;$.fn.typeahead.noConflict=function(){$.fn.typeahead=old;return this};$(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var $this=$(this);if($this.data("typeahead"))return;$this.typeahead($this.data())})}); 2 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/lib/typeahead.js/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2014 Twitter, Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Knapcode.NuGetTools.Website/wwwroot/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /test/Knapcode.NuGetTools.Website.Test/Knapcode.NuGetTools.Website.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/Knapcode.NuGetTools.Website.Test/TestServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using AngleSharp.Dom; 3 | using AngleSharp.Html.Dom; 4 | using AngleSharp.Html.Parser; 5 | using Knapcode.NuGetTools.Logic; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Mvc.Testing; 8 | using Microsoft.AspNetCore.TestHost; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using NuGet.Versioning; 11 | 12 | namespace Knapcode.NuGetTools.Website.Tests; 13 | 14 | public class TestServerFixture : IDisposable 15 | { 16 | public static Uri? BaseAddress { get; set; } 17 | 18 | private readonly HtmlParser _htmlParser; 19 | 20 | public TestServerFixture() 21 | { 22 | Factory = new WebApplicationFactory() 23 | .WithWebHostBuilder(builder => builder 24 | .UseWebRoot(GetWebRoot()) 25 | .UseContentRoot(GetContentRoot()) 26 | .UseEnvironment("Automation")); 27 | Server = Factory.Server; 28 | 29 | if (BaseAddress == null) 30 | { 31 | Client = Server.CreateClient(); 32 | } 33 | else 34 | { 35 | var handler = new HttpClientHandler 36 | { 37 | AllowAutoRedirect = false, 38 | }; 39 | Client = new HttpClient(handler) 40 | { 41 | BaseAddress = BaseAddress, 42 | }; 43 | } 44 | 45 | _htmlParser = new HtmlParser(); 46 | } 47 | 48 | internal WebApplicationFactory Factory { get; } 49 | public TestServer Server { get; } 50 | public HttpClient Client { get; } 51 | 52 | private static string GetWebRoot() 53 | { 54 | return Path.Combine(GetContentRoot(), "wwwroot"); 55 | } 56 | 57 | private static string GetContentRoot() 58 | { 59 | return Enumerable 60 | .Empty() 61 | .Concat(new[] 62 | { 63 | Path.GetDirectoryName(typeof(IntegrationTest).Assembly.Location), 64 | Directory.GetCurrentDirectory(), 65 | }) 66 | .Where(x => x is not null) 67 | .Select(x => GetContentRoot(x!)) 68 | .First(x => x is not null)!; 69 | } 70 | 71 | private static string? GetContentRoot(string startingDirectory) 72 | { 73 | string? currentDirectory = startingDirectory; 74 | while (currentDirectory != null && !Directory 75 | .GetFiles(currentDirectory) 76 | .Select(p => Path.GetFileName(p)) 77 | .Contains("NuGetTools.sln")) 78 | { 79 | currentDirectory = Path.GetDirectoryName(currentDirectory); 80 | } 81 | 82 | if (currentDirectory is null) 83 | { 84 | return null; 85 | } 86 | 87 | return Path.Combine(currentDirectory, "src", "Knapcode.NuGetTools.Website"); 88 | } 89 | 90 | public async Task> GetAvailableVersionsAsync() 91 | { 92 | var toolsFactory = Factory.Services.GetRequiredService(); 93 | var versionStrings = await toolsFactory.GetAvailableVersionsAsync(CancellationToken.None); 94 | return versionStrings 95 | .Select(x => new NuGetVersion(x)) 96 | .ToList(); 97 | } 98 | 99 | public async Task GetDocumentAsync(HttpResponseMessage response) 100 | { 101 | using (var stream = await response.Content.ReadAsStreamAsync()) 102 | { 103 | return await _htmlParser.ParseDocumentAsync(stream); 104 | } 105 | } 106 | 107 | public async Task GetFlattenedTextAsync(HttpResponseMessage response) 108 | { 109 | var document = await GetDocumentAsync(response); 110 | var textContent = document.DocumentElement.Text(); 111 | return Regex.Replace(textContent, @"\s+", " ", RegexOptions.Multiline); 112 | } 113 | 114 | public void Dispose() 115 | { 116 | Client.Dispose(); 117 | Server.Dispose(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /test/Knapcode.NuGetTools.Website.Test/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "shadowCopy": false 3 | } --------------------------------------------------------------------------------