You've already forked setup-dotnet
							
							
				mirror of
				https://github.com/actions/setup-dotnet.git
				synced 2025-10-31 07:16:21 +07:00 
			
		
		
		
	Compare commits
	
		
			51 Commits
		
	
	
		
			update-tea
			...
			tool-confi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ba848a34bb | |||
| aa983c550d | |||
| b891376106 | |||
| b05a3f26b3 | |||
| 5fdecd2063 | |||
| 38b49fb717 | |||
| 3cf3e230c1 | |||
| 83a1653fa3 | |||
| 898aa0ce4d | |||
| 2f028bc044 | |||
| 21cf89aa73 | |||
| fefaa59d2e | |||
| e8501859aa | |||
| 426d75d071 | |||
| 0f534f5829 | |||
| fbdbede901 | |||
| 0bc43909e0 | |||
| c5a57b219c | |||
| 6adeb768ce | |||
| f425be78f5 | |||
| fc8786b149 | |||
| 7d08dc7593 | |||
| e0a32d6459 | |||
| 255362be61 | |||
| 50b46b3b1d | |||
| e8ac21d503 | |||
| a79ce57e6b | |||
| 180a15970f | |||
| b72f430d36 | |||
| 559e47b01b | |||
| 7358a44590 | |||
| 34c30d0e81 | |||
| 5f570676c2 | |||
| aa34a3ceaa | |||
| 12f70884d7 | |||
| 0318091611 | |||
| f199d27aa1 | |||
| 660c25a321 | |||
| 4f6b2f576a | |||
| 920b830bd1 | |||
| abdd14ee80 | |||
| 1d9f0dad5b | |||
| 2699274f6e | |||
| ca579e0fb2 | |||
| c82240598b | |||
| 926f442022 | |||
| c41fd15071 | |||
| 0c8652569e | |||
| 3cf27f13bb | |||
| ae8edb8fff | |||
| 82b2b40816 | 
							
								
								
									
										6
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| # Ignore list | ||||
| /* | ||||
|  | ||||
| # Do not ignore these folders: | ||||
| !__tests__/ | ||||
| !src/ | ||||
							
								
								
									
										51
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| // This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update. | ||||
| module.exports = { | ||||
|   extends: [ | ||||
|     'eslint:recommended', | ||||
|     'plugin:@typescript-eslint/recommended', | ||||
|     'plugin:eslint-plugin-jest/recommended', | ||||
|     'eslint-config-prettier' | ||||
|   ], | ||||
|   parser: '@typescript-eslint/parser', | ||||
|   plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'], | ||||
|   rules: { | ||||
|     '@typescript-eslint/no-require-imports': 'error', | ||||
|     '@typescript-eslint/no-non-null-assertion': 'off', | ||||
|     '@typescript-eslint/no-explicit-any': 'off', | ||||
|     '@typescript-eslint/no-empty-function': 'off', | ||||
|     '@typescript-eslint/ban-ts-comment': [ | ||||
|       'error', | ||||
|       { | ||||
|         'ts-ignore': 'allow-with-description' | ||||
|       } | ||||
|     ], | ||||
|     'no-console': 'error', | ||||
|     'yoda': 'error', | ||||
|     'prefer-const': [ | ||||
|       'error', | ||||
|       { | ||||
|         destructuring: 'all' | ||||
|       } | ||||
|     ], | ||||
|     'no-control-regex': 'off', | ||||
|     'no-constant-condition': ['error', {checkLoops: false}], | ||||
|     'node/no-extraneous-import': 'error' | ||||
|   }, | ||||
|   overrides: [ | ||||
|     { | ||||
|       files: ['**/*{test,spec}.ts'], | ||||
|       rules: { | ||||
|         '@typescript-eslint/no-unused-vars': 'off', | ||||
|         'jest/no-standalone-expect': 'off', | ||||
|         'jest/no-conditional-expect': 'off', | ||||
|         'no-console': 'off', | ||||
|  | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   env: { | ||||
|     node: true, | ||||
|     es6: true, | ||||
|     'jest/globals': true | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @ -1 +1,2 @@ | ||||
| * text=auto eol=lf | ||||
| .licenses/** -diff linguist-generated=true | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/basic-validation.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/basic-validation.yml
									
									
									
									
										vendored
									
									
								
							| @ -14,4 +14,4 @@ on: | ||||
| jobs: | ||||
|   call-basic-validation: | ||||
|     name: Basic validation | ||||
|     uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main | ||||
|     uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							| @ -14,4 +14,4 @@ on: | ||||
| jobs: | ||||
|   call-check-dist: | ||||
|     name: Check dist/ | ||||
|     uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main | ||||
|     uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main | ||||
|  | ||||
							
								
								
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @ -2,13 +2,13 @@ name: CodeQL analysis | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: [ main ] | ||||
|     branches: [main] | ||||
|   pull_request: | ||||
|     branches: [ main ] | ||||
|     branches: [main] | ||||
|   schedule: | ||||
|     - cron: '0 3 * * 0' | ||||
|  | ||||
| jobs: | ||||
|   call-codeQL-analysis: | ||||
|     name: CodeQL analysis  | ||||
|     uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main | ||||
|     name: CodeQL analysis | ||||
|     uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main | ||||
|  | ||||
							
								
								
									
										249
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										249
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							| @ -24,7 +24,7 @@ jobs: | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|       - name: Setup dotnet 2.2.402 and 3.1.404 | ||||
|       - name: Setup dotnet 2.2.402, 3.1.404 and 3.0.x | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: | | ||||
| @ -33,7 +33,7 @@ jobs: | ||||
|             3.0.x | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 2.2.402 3.1.404 '3.0' | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^2.2.402$", "^3.1.404$", "^3.0" | ||||
|  | ||||
|   test-setup-full-version: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -60,13 +60,9 @@ jobs: | ||||
|           source-url: https://api.nuget.org/v3/index.json | ||||
|         env: | ||||
|           NUGET_AUTH_TOKEN: NOTATOKEN | ||||
|       - name: Verify nuget config file | ||||
|         shell: pwsh | ||||
|         run: | | ||||
|           if (-Not (Test-Path "../nuget.config")) { throw "nuget file not generated correctly" } | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 3.1.201 2.2.402 | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$", "^2.2.402$" -CheckNugetConfig | ||||
|  | ||||
|   test-setup-without-patch-version: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -91,7 +87,7 @@ jobs: | ||||
|           dotnet-version: '2.2' | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 3.1 2.2 | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2" | ||||
|  | ||||
|   test-setup-prerelease-version: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -105,17 +101,13 @@ jobs: | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|       - name: Setup dotnet '2.2' | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: '2.2' | ||||
|       - name: Setup dotnet '3.1.100-preview1-014459' | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: '3.1.100-preview1-014459' | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 3.1.100-preview1-014459 | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "3.1.100-preview1-014459" | ||||
|  | ||||
|   test-setup-latest-patch-version: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -139,7 +131,28 @@ jobs: | ||||
|           dotnet-version: 2.2.X | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 '2.2' '3.1' | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^2.2", "^3.1" | ||||
|  | ||||
|   test-ABCxx-syntax: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|  | ||||
|       - name: Setup dotnet 6.0.4xx | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: '6.0.4xx' | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^6\.0\.4\d{2}" | ||||
|  | ||||
|   test-setup-with-wildcard: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -163,7 +176,7 @@ jobs: | ||||
|           dotnet-version: 2.2.* | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 3.1 2.2 | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2" | ||||
|  | ||||
|   test-setup-global-json-specified-and-version: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
| @ -181,7 +194,7 @@ jobs: | ||||
|         shell: bash | ||||
|         run: | | ||||
|           mkdir subdirectory | ||||
|           echo '{"sdk":{"version": "2.2","rollForward": "latestFeature"}}' > ./subdirectory/global.json | ||||
|           echo '{"sdk":{"version": "2.2.207","rollForward": "latestFeature"}}' > ./subdirectory/global.json | ||||
|       - name: Setup dotnet | ||||
|         uses: ./ | ||||
|         with: | ||||
| @ -189,91 +202,113 @@ jobs: | ||||
|           global-json-file: ./subdirectory/global.json | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 2.2 3.1 | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^2.2", "^3.1" | ||||
|  | ||||
|   test-setup-global-json-only: | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|       - name: Write global.json | ||||
|         shell: bash | ||||
|         run: | | ||||
|           mkdir subdirectory | ||||
|           echo '{"sdk":{"version": "2.2.207","rollForward": "latestFeature"}}' > ./subdirectory/global.json | ||||
|       - name: Setup dotnet | ||||
|         uses: ./ | ||||
|         with: | ||||
|           global-json-file: ./subdirectory/global.json | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^2.2" | ||||
|  | ||||
|   test-setup-with-dotnet-quality: | ||||
|       runs-on: ${{ matrix.operating-system }} | ||||
|       strategy: | ||||
|         fail-fast: false | ||||
|         matrix: | ||||
|           operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|       steps: | ||||
|         - name: Checkout | ||||
|           uses: actions/checkout@v3 | ||||
|         - name: Clear toolcache | ||||
|           shell: pwsh | ||||
|           run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|                 | ||||
|         - name: Setup dotnet 7.0 with preview quality | ||||
|           uses: ./ | ||||
|           with: | ||||
|             dotnet-version: "7.0" | ||||
|             dotnet-quality: "preview" | ||||
|         - name: Verify preview version | ||||
|           shell: pwsh | ||||
|           run: | | ||||
|             $version = & dotnet --version | ||||
|             Write-Host "Installed version: $version" | ||||
|             if (-not ($version.Contains("preview") -or $version.Contains("rc"))) { throw "Unexpected version" } | ||||
|    | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|  | ||||
|       - name: Setup dotnet 7.0 with preview quality | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: '7.0' | ||||
|           dotnet-quality: 'preview' | ||||
|       - name: Verify dotnet | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^7\.0\.\d+-" | ||||
|  | ||||
|   test-dotnet-version-output-during-single-version-installation: | ||||
|       runs-on: ${{ matrix.operating-system }} | ||||
|       strategy: | ||||
|         fail-fast: false | ||||
|         matrix: | ||||
|           operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|       steps: | ||||
|         - name: Checkout | ||||
|           uses: actions/checkout@v3 | ||||
|         - name: Clear toolcache | ||||
|           shell: pwsh | ||||
|           run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|                 | ||||
|         - name: Setup dotnet 6.0.401 | ||||
|           uses: ./ | ||||
|           id: step1 | ||||
|           with: | ||||
|             dotnet-version: "6.0.401" | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|  | ||||
|       - name: Setup dotnet 6.0.401 | ||||
|         uses: ./ | ||||
|         id: step1 | ||||
|         with: | ||||
|           dotnet-version: '6.0.401' | ||||
|  | ||||
|       - name: Verify value of the dotnet-version output | ||||
|         shell: pwsh | ||||
|         run: | | ||||
|           $version = & dotnet --version | ||||
|           Write-Host "Installed version: $version" | ||||
|           if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" } | ||||
|  | ||||
|         - name: Verify value of the dotnet-version output | ||||
|           shell: pwsh | ||||
|           run: | | ||||
|             $version = & dotnet --version | ||||
|             Write-Host "Installed version: $version" | ||||
|             if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" } | ||||
|    | ||||
|   test-dotnet-version-output-during-multiple-version-installation: | ||||
|       runs-on: ${{ matrix.operating-system }} | ||||
|       strategy: | ||||
|         fail-fast: false | ||||
|         matrix: | ||||
|           operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|       steps: | ||||
|         - name: Checkout | ||||
|           uses: actions/checkout@v3 | ||||
|         - name: Clear toolcache | ||||
|           shell: pwsh | ||||
|           run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|                 | ||||
|         - name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12 | ||||
|           uses: ./ | ||||
|           id: step2 | ||||
|           with: | ||||
|             dotnet-version: | | ||||
|               7.0.100-rc.1.22431.12 | ||||
|               6.0.401 | ||||
|               5.0.408 | ||||
|     runs-on: ${{ matrix.operating-system }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|  | ||||
|         - name: Verify value of the dotnet-version output | ||||
|           shell: pwsh | ||||
|           run: | | ||||
|             $version = "7.0.100-rc.1.22431.12" | ||||
|             if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" } | ||||
|       - name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12 | ||||
|         uses: ./ | ||||
|         id: step2 | ||||
|         with: | ||||
|           dotnet-version: | | ||||
|             7.0.100-rc.1.22431.12 | ||||
|             6.0.401 | ||||
|             5.0.408 | ||||
|  | ||||
|       - name: Verify value of the dotnet-version output | ||||
|         shell: pwsh | ||||
|         run: | | ||||
|           $version = "7.0.100-rc.1.22431.12" | ||||
|           if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" } | ||||
|  | ||||
|   test-proxy: | ||||
|     runs-on: ubuntu-latest | ||||
|     container: | ||||
|       image: mcr.microsoft.com/dotnet/core/runtime-deps:3.0-bionic | ||||
|       image: ubuntu:latest | ||||
|       options: --dns 127.0.0.1 | ||||
|     services: | ||||
|       squid-proxy: | ||||
| @ -286,21 +321,29 @@ jobs: | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear tool cache | ||||
|         run: rm -rf "/usr/share/dotnet" | ||||
|       - name: Install curl | ||||
|       - name: Install Powershell | ||||
|         run: | | ||||
|           apt update | ||||
|           apt -y install curl | ||||
|       - name: Setup dotnet 3.1.201 | ||||
|           apt-get update | ||||
|           apt-get install -y wget apt-transport-https software-properties-common | ||||
|           wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb" | ||||
|           dpkg -i packages-microsoft-prod.deb | ||||
|           rm packages-microsoft-prod.deb | ||||
|           apt-get update | ||||
|           apt-get install -y powershell | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|       - name: Setup dotnet 6.0 | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: 3.1.201 | ||||
|           dotnet-version: 6.0 | ||||
|           source-url: https://api.nuget.org/v3/index.json | ||||
|         env: | ||||
|           NUGET_AUTH_TOKEN: NOTATOKEN | ||||
|       - name: Verify dotnet | ||||
|         run: __tests__/verify-dotnet.sh 3.1.201 | ||||
|         shell: pwsh | ||||
|         run: | | ||||
|           __tests__/verify-dotnet.ps1 -Patterns "^6.0" -CheckNugetConfig | ||||
|  | ||||
|   test-bypass-proxy: | ||||
|     runs-on: ubuntu-latest | ||||
| @ -310,8 +353,9 @@ jobs: | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Clear tool cache | ||||
|         run: rm -rf "/usr/share/dotnet" | ||||
|       - name: Clear toolcache | ||||
|         shell: pwsh | ||||
|         run: __tests__/clear-toolcache.ps1 ${{ runner.os }} | ||||
|       - name: Setup dotnet 3.1.201 | ||||
|         uses: ./ | ||||
|         with: | ||||
| @ -320,4 +364,5 @@ jobs: | ||||
|         env: | ||||
|           NUGET_AUTH_TOKEN: NOTATOKEN | ||||
|       - name: Verify dotnet | ||||
|         run: __tests__/verify-dotnet.sh 3.1.201 | ||||
|         shell: pwsh | ||||
|         run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$" -CheckNugetConfig | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/licensed.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/licensed.yml
									
									
									
									
										vendored
									
									
								
							| @ -12,4 +12,4 @@ on: | ||||
| jobs: | ||||
|   call-licensed: | ||||
|     name: Licensed | ||||
|     uses: actions/reusable-workflows/.github/workflows/licensed.yml@main | ||||
|     uses: actions/reusable-workflows/.github/workflows/licensed.yml@main | ||||
|  | ||||
							
								
								
									
										12
									
								
								.github/workflows/release-new-action-version.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/release-new-action-version.yml
									
									
									
									
										vendored
									
									
								
							| @ -21,9 +21,9 @@ jobs: | ||||
|       name: releaseNewActionVersion | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - name: Update the ${{ env.TAG_NAME }} tag | ||||
|       id: update-major-tag | ||||
|       uses: actions/publish-action@v0.2.1 | ||||
|       with: | ||||
|         source-tag: ${{ env.TAG_NAME }} | ||||
|         slack-webhook: ${{ secrets.SLACK_WEBHOOK }} | ||||
|       - name: Update the ${{ env.TAG_NAME }} tag | ||||
|         id: update-major-tag | ||||
|         uses: actions/publish-action@v0.2.2 | ||||
|         with: | ||||
|           source-tag: ${{ env.TAG_NAME }} | ||||
|           slack-webhook: ${{ secrets.SLACK_WEBHOOK }} | ||||
|  | ||||
							
								
								
									
										8
									
								
								.github/workflows/test-dotnet.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/test-dotnet.yml
									
									
									
									
										vendored
									
									
								
							| @ -18,7 +18,7 @@ jobs: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0'] | ||||
|         dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0', '6.0', '7.0', '8.0'] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -29,9 +29,7 @@ jobs: | ||||
|         uses: ./ | ||||
|         with: | ||||
|           dotnet-version: ${{ matrix.dotnet-version }} | ||||
|       - name: Check installed version | ||||
|       - name: Verify installed version | ||||
|         shell: pwsh | ||||
|         run: | | ||||
|           $version = & dotnet --version | ||||
|           Write-Host "Installed version: $version" | ||||
|           if (-not $version.StartsWith("${{ matrix.dotnet-version }}")) { throw "Unexpected version" } | ||||
|           __tests__/verify-dotnet.ps1 -Patterns "^${{ matrix.dotnet-version }}" | ||||
|  | ||||
							
								
								
									
										11
									
								
								.github/workflows/update-config-files.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/update-config-files.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| name: Update configuration files | ||||
|  | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: '0 3 * * 0' | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   call-update-configuration-files: | ||||
|     name: Update configuration files | ||||
|     uses: actions/reusable-workflows/.github/workflows/update-config-files.yml@main | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -3,8 +3,8 @@ global.json | ||||
| lib/ | ||||
| node_modules/ | ||||
| __tests__/runner/* | ||||
| __tests__/sample-csproj/bin/ | ||||
| __tests__/sample-csproj/obj/ | ||||
| __tests__/e2e-test-csproj/bin/ | ||||
| __tests__/e2e-test-csproj/obj/ | ||||
|  | ||||
| # Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore | ||||
| # Logs | ||||
|  | ||||
| @ -2,3 +2,4 @@ | ||||
| . "$(dirname -- "$0")/_/husky.sh" | ||||
|  | ||||
| npm run format | ||||
| npm run lint:fix | ||||
|  | ||||
| @ -3,3 +3,4 @@ | ||||
|  | ||||
| # Tests are not run at push time since they can take 2-4 minutes to complete | ||||
| npm run format-check | ||||
| npm run lint | ||||
|  | ||||
							
								
								
									
										7
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| # Ignore list | ||||
| /* | ||||
|  | ||||
| # Do not ignore these folders: | ||||
| !__tests__/ | ||||
| !.github/ | ||||
| !src/ | ||||
							
								
								
									
										11
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| // This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update. | ||||
| module.exports = { | ||||
|   printWidth: 80, | ||||
|   tabWidth: 2, | ||||
|   useTabs: false, | ||||
|   semi: true, | ||||
|   singleQuote: true, | ||||
|   trailingComma: 'none', | ||||
|   bracketSpacing: false, | ||||
|   arrowParens: 'avoid' | ||||
| }; | ||||
| @ -1,12 +0,0 @@ | ||||
| { | ||||
|     "printWidth": 80, | ||||
|     "tabWidth": 2, | ||||
|     "useTabs": false, | ||||
|     "semi": true, | ||||
|     "singleQuote": true, | ||||
|     "trailingComma": "none", | ||||
|     "bracketSpacing": false, | ||||
|     "arrowParens": "avoid", | ||||
|     "parser": "typescript", | ||||
|     "endOfLine": "auto" | ||||
|   } | ||||
| @ -1,6 +1,7 @@ | ||||
| # setup-dotnet | ||||
|  | ||||
| [](https://github.com/actions/setup-dotnet) | ||||
| [](https://github.com/actions/setup-dotnet/actions/workflows/basic-validation.yml) | ||||
| [](https://github.com/actions/setup-dotnet/actions/workflows/e2e-tests.yml) | ||||
|  | ||||
| This action sets up a [.NET CLI](https://github.com/dotnet/sdk) environment for use in actions by: | ||||
|  | ||||
| @ -48,12 +49,13 @@ The `dotnet-version` input supports following syntax: | ||||
| - **A.B.C** (e.g 6.0.400, 7.0.100-preview.7.22377.5) - installs exact version of .NET SDK | ||||
| - **A.B** or **A.B.x** (e.g. 3.1, 3.1.x) - installs the latest patch version of .NET SDK on the channel `3.1`, including prerelease versions (preview, rc) | ||||
| - **A** or **A.x** (e.g. 3, 3.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc) | ||||
| - **A.B.Cxx** (e.g. 6.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc).  | ||||
|  | ||||
|  | ||||
| ## Using the `dotnet-quality` input | ||||
| This input sets up the action to install the latest build of the specified quality in the channel. The possible values of `dotnet-quality` are: **daily**, **signed**, **validated**, **preview**, **ga**. | ||||
|  | ||||
| > **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A' and 'A.x' formats where the major version is higher than 5. In other cases, `dotnet-quality` input will be ignored. | ||||
| > **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A', 'A.x' and 'A.B.Cxx' formats where the major version is higher than 5. In other cases, `dotnet-quality` input will be ignored. | ||||
|  | ||||
| ```yml | ||||
| steps: | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||
|  | ||||
| exports[`authutil tests Existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -15,7 +15,7 @@ exports[`authutil tests Existing config not in repo root, sets up a partial NuGe | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -30,7 +30,7 @@ exports[`authutil tests Existing config w/ Azure Artifacts source and NuGet.org, | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -45,7 +45,7 @@ exports[`authutil tests Existing config w/ GPR source and NuGet.org, sets up a p | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -63,7 +63,7 @@ exports[`authutil tests Existing config w/ no GPR sources, sets up a full NuGet. | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -81,7 +81,7 @@ exports[`authutil tests Existing config w/ no sources, sets up a full NuGet.conf | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -96,7 +96,7 @@ exports[`authutil tests Existing config w/ only Azure Artifacts source, sets up | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -111,7 +111,7 @@ exports[`authutil tests Existing config w/ only GPR source, sets up a partial Nu | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests Existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -130,7 +130,7 @@ exports[`authutil tests Existing config w/ two GPR sources, sets up a partial Nu | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests No existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests no existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -148,7 +148,7 @@ exports[`authutil tests No existing config, sets up a full NuGet.config with URL | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests No existing config, sets up a full NuGet.config with URL and token for other source 1`] = ` | ||||
| exports[`authutil tests no existing config, sets up a full NuGet.config with URL and token for other source 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
| @ -166,7 +166,7 @@ exports[`authutil tests No existing config, sets up a full NuGet.config with URL | ||||
| </configuration>" | ||||
| `; | ||||
|  | ||||
| exports[`authutil tests No existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| exports[`authutil tests no existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` | ||||
| "<?xml version=\\"1.0\\"?> | ||||
| <configuration> | ||||
|   <config> | ||||
|  | ||||
| @ -1,336 +1,336 @@ | ||||
| import * as io from '@actions/io'; | ||||
| import fs from 'fs'; | ||||
| import path from 'path'; | ||||
|  | ||||
| const fakeSourcesDirForTesting = path.join( | ||||
|   __dirname, | ||||
|   'runner', | ||||
|   path.join(Math.random().toString(36).substring(7)), | ||||
|   's' | ||||
| ); | ||||
|  | ||||
| const invalidNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?>`; | ||||
|  | ||||
| const emptyNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
| </configuration>`; | ||||
|  | ||||
| const nugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const gprnugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const gprNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const twogprNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR-GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|     <add key="GPR-Actions" value="https://nuget.pkg.github.com/actions/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const spaceNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const azureartifactsNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const azureartifactsnugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" /> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| // We want a NuGet.config one level above the sources directory, so it doesn't trample a user's NuGet.config but is still picked up by NuGet/dotnet. | ||||
| const nugetConfigFile = path.join(fakeSourcesDirForTesting, '../nuget.config'); | ||||
|  | ||||
| process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo'; | ||||
| import * as auth from '../src/authutil'; | ||||
|  | ||||
| describe('authutil tests', () => { | ||||
|   beforeEach(async () => { | ||||
|     await io.rmRF(fakeSourcesDirForTesting); | ||||
|     await io.mkdirP(fakeSourcesDirForTesting); | ||||
|   }, 30000); | ||||
|  | ||||
|   afterAll(async () => { | ||||
|     await io.rmRF(fakeSourcesDirForTesting); | ||||
|   }, 30000); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     if (fs.existsSync(nugetConfigFile)) { | ||||
|       fs.unlinkSync(nugetConfigFile); | ||||
|     } | ||||
|     process.env['INPUT_OWNER'] = ''; | ||||
|     process.env['NUGET_AUTH_TOKEN'] = ''; | ||||
|   }); | ||||
|  | ||||
|   it('No existing config, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('No existing config, auth token environment variable not provided, throws', async () => { | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       await auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('No existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     process.env['INPUT_OWNER'] = 'otherorg'; | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/otherorg/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config (invalid), tries to parse an invalid NuGet.config and throws', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, invalidNuGetConfig); | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       await auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, emptyNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, nugetorgNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprnugetorgNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, twogprNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ spaces in key, throws for now', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, spaceNuGetConfig); | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       await auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigDirectory: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'subfolder' | ||||
|     ); | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       inputNuGetConfigDirectory, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.mkdirSync(inputNuGetConfigDirectory, {recursive: true}); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       'subfolder/nuget.config', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, azureartifactsNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('Existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, azureartifactsnugetorgNuGetConfig); | ||||
|     await auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('No existing config, sets up a full NuGet.config with URL and token for other source', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     await auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
| }); | ||||
| import * as io from '@actions/io'; | ||||
| import fs from 'fs'; | ||||
| import path from 'path'; | ||||
|  | ||||
| const fakeSourcesDirForTesting = path.join( | ||||
|   __dirname, | ||||
|   'runner', | ||||
|   path.join(Math.random().toString(36).substring(7)), | ||||
|   's' | ||||
| ); | ||||
|  | ||||
| const invalidNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>`; | ||||
|  | ||||
| const emptyNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
| </configuration>`; | ||||
|  | ||||
| const nugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const gprnugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const gprNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const twogprNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR-GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|     <add key="GPR-Actions" value="https://nuget.pkg.github.com/actions/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const spaceNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="GPR GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const azureartifactsNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| const azureartifactsnugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?> | ||||
| <configuration> | ||||
|   <packageSources> | ||||
|     <add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" /> | ||||
|     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||||
|   </packageSources> | ||||
| </configuration>`; | ||||
|  | ||||
| // We want a NuGet.config one level above the sources directory, so it doesn't trample a user's NuGet.config but is still picked up by NuGet/dotnet. | ||||
| const nugetConfigFile = path.join(fakeSourcesDirForTesting, '../nuget.config'); | ||||
|  | ||||
| process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo'; | ||||
| import * as auth from '../src/authutil'; | ||||
|  | ||||
| describe('authutil tests', () => { | ||||
|   beforeEach(async () => { | ||||
|     await io.rmRF(fakeSourcesDirForTesting); | ||||
|     await io.mkdirP(fakeSourcesDirForTesting); | ||||
|   }, 30000); | ||||
|  | ||||
|   afterAll(async () => { | ||||
|     await io.rmRF(fakeSourcesDirForTesting); | ||||
|   }, 30000); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     if (fs.existsSync(nugetConfigFile)) { | ||||
|       fs.unlinkSync(nugetConfigFile); | ||||
|     } | ||||
|     process.env['INPUT_OWNER'] = ''; | ||||
|     process.env['NUGET_AUTH_TOKEN'] = ''; | ||||
|   }); | ||||
|  | ||||
|   it('no existing config, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('no existing config, auth token environment variable not provided, throws', async () => { | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('no existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     process.env['INPUT_OWNER'] = 'otherorg'; | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/otherorg/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config (invalid), tries to parse an invalid NuGet.config and throws', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, invalidNuGetConfig); | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, emptyNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, nugetorgNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprnugetorgNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, twogprNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ spaces in key, throws for now', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, spaceNuGetConfig); | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       auth.configAuthentication( | ||||
|         'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|         '', | ||||
|         fakeSourcesDirForTesting | ||||
|       ); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigDirectory: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'subfolder' | ||||
|     ); | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       inputNuGetConfigDirectory, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.mkdirSync(inputNuGetConfigDirectory, {recursive: true}); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://nuget.pkg.github.com/OwnerName/index.json', | ||||
|       'subfolder/nuget.config', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, azureartifactsNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     const inputNuGetConfigPath: string = path.join( | ||||
|       fakeSourcesDirForTesting, | ||||
|       'nuget.config' | ||||
|     ); | ||||
|     fs.writeFileSync(inputNuGetConfigPath, azureartifactsnugetorgNuGetConfig); | ||||
|     auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
|  | ||||
|   it('no existing config, sets up a full NuGet.config with URL and token for other source', async () => { | ||||
|     process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; | ||||
|     auth.configAuthentication( | ||||
|       'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', | ||||
|       '', | ||||
|       fakeSourcesDirForTesting | ||||
|     ); | ||||
|     expect(fs.existsSync(nugetConfigFile)).toBe(true); | ||||
|     expect( | ||||
|       fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) | ||||
|     ).toMatchSnapshot(); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -1,21 +1,45 @@ | ||||
| import cscFile from '../.github/csc.json'; | ||||
| describe('csc tests', () => { | ||||
|   it('Valid regular expression', async () => { | ||||
|     var cscFile = require('../.github/csc.json'); | ||||
|     var regex = cscFile['problemMatcher'][0]['pattern'][0]['regexp']; | ||||
|   test('regular expression in csc.json is valid', async () => { | ||||
|     const regexPattern = cscFile['problemMatcher'][0]['pattern'][0]['regexp']; | ||||
|     const regexResultsMap = cscFile['problemMatcher'][0]['pattern'][0]; | ||||
|  | ||||
|     console.log(regex); | ||||
|     var re = new RegExp(regex); | ||||
|     const regex = new RegExp(regexPattern); | ||||
|  | ||||
|     // Ideally we would verify that this | ||||
|     var stringsToMatch = [ | ||||
|     const stringsToMatch = [ | ||||
|       'Program.cs(10,79): error CS1002: ; expected [/Users/zacharyeisinger/Documents/repo/setup-dotnet/__tests__/sample-broken-csproj/sample.csproj]', | ||||
|       "S:\\Msbuild\\src\\Build\\Evaluation\\ExpressionShredder.cs(33,7): error CS1003: Syntax error, ',' expected [S:\\msbuild\\src\\Build\\Microsoft.Build.csproj > Properties:prop]" | ||||
|     ]; | ||||
|     // Expected results are calculated according to the csc matcher located in csc.json file | ||||
|     const expectedResults = [ | ||||
|       { | ||||
|         file: 'Program.cs', | ||||
|         line: '10', | ||||
|         severity: 'error', | ||||
|         code: 'CS1002', | ||||
|         message: '; expected', | ||||
|         fromPath: | ||||
|           '/Users/zacharyeisinger/Documents/repo/setup-dotnet/__tests__/sample-broken-csproj/sample.csproj' | ||||
|       }, | ||||
|       { | ||||
|         file: 'S:\\Msbuild\\src\\Build\\Evaluation\\ExpressionShredder.cs', | ||||
|         line: '33', | ||||
|         severity: 'error', | ||||
|         code: 'CS1003', | ||||
|         message: "Syntax error, ',' expected", | ||||
|         fromPath: | ||||
|           'S:\\msbuild\\src\\Build\\Microsoft.Build.csproj > Properties:prop' | ||||
|       } | ||||
|     ]; | ||||
|  | ||||
|     stringsToMatch.forEach(string => { | ||||
|       var matchStr = string.match(re); | ||||
|       console.log(matchStr); | ||||
|       expect(matchStr).toEqual(expect.anything()); | ||||
|     stringsToMatch.map((string, index) => { | ||||
|       const matchedResultsArray = string.match(regex); | ||||
|       for (const propName in expectedResults[index]) { | ||||
|         const propertyIndex = regexResultsMap[propName]; | ||||
|         const expectedPropValue = expectedResults[index][propName]; | ||||
|         const matchedPropValue = matchedResultsArray![propertyIndex]; | ||||
|         expect(matchedPropValue).toEqual(expectedPropValue); | ||||
|       } | ||||
|     }); | ||||
|   }, 10000); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										18
									
								
								__tests__/e2e-test-csproj/Test.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								__tests__/e2e-test-csproj/Test.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using System; | ||||
|  | ||||
| namespace test_csproj | ||||
| { | ||||
|     [TestClass] | ||||
|     public class Test | ||||
|     { | ||||
|         [TestMethod] | ||||
|         public void TestMethod() | ||||
|         {    | ||||
|             Console.WriteLine("TestMethod"); | ||||
|             int calculatedResult = 1000 / 25; | ||||
|             int expectedResult = 40; | ||||
|             Assert.AreEqual(calculatedResult, expectedResult); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								__tests__/e2e-test-csproj/test.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								__tests__/e2e-test-csproj/test.csproj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>$(TEST_TARGET_FRAMEWORK)</TargetFramework> | ||||
|     <IsPackable>false</IsPackable> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <!-- These packages will be downloaded over the network for testing proxy settings --> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" /> | ||||
|     <PackageReference Include="MSTest.TestAdapter" Version="1.1.18" /> | ||||
|     <PackageReference Include="MSTest.TestFramework" Version="1.1.18" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										65
									
								
								__tests__/installation-scripts.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								__tests__/installation-scripts.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| import path from 'path'; | ||||
| import fs from 'fs'; | ||||
| import * as hc from '@actions/http-client'; | ||||
|  | ||||
| const HTTP_CLIENT_OPTIONS = {allowRetries: true, maxRetries: 10} as const; | ||||
| const TEST_TIMEOUT = 30000; | ||||
|  | ||||
| describe('Dotnet installation scripts tests', () => { | ||||
|   it( | ||||
|     'Uses an up to date bash download script', | ||||
|     async () => { | ||||
|       const httpCallbackClient = new hc.HttpClient( | ||||
|         'setup-dotnet-test', | ||||
|         [], | ||||
|         HTTP_CLIENT_OPTIONS | ||||
|       ); | ||||
|       const response: hc.HttpClientResponse = await httpCallbackClient.get( | ||||
|         'https://dot.net/v1/dotnet-install.sh' | ||||
|       ); | ||||
|       expect(response.message.statusCode).toBe(200); | ||||
|       const upToDateContents: string = await response.readBody(); | ||||
|       const currentContents: string = fs | ||||
|         .readFileSync( | ||||
|           path.join(__dirname, '..', 'externals', 'install-dotnet.sh') | ||||
|         ) | ||||
|         .toString(); | ||||
|       expect(normalizeFileContents(currentContents)).toBe( | ||||
|         normalizeFileContents(upToDateContents) | ||||
|       ); | ||||
|     }, | ||||
|     TEST_TIMEOUT | ||||
|   ); | ||||
|  | ||||
|   it( | ||||
|     'Uses an up to date powershell download script', | ||||
|     async () => { | ||||
|       const httpCallbackClient = new hc.HttpClient( | ||||
|         'setup-dotnet-test', | ||||
|         [], | ||||
|         HTTP_CLIENT_OPTIONS | ||||
|       ); | ||||
|       const response: hc.HttpClientResponse = await httpCallbackClient.get( | ||||
|         'https://dot.net/v1/dotnet-install.ps1' | ||||
|       ); | ||||
|       expect(response.message.statusCode).toBe(200); | ||||
|       const upToDateContents: string = await response.readBody(); | ||||
|       const currentContents: string = fs | ||||
|         .readFileSync( | ||||
|           path.join(__dirname, '..', 'externals', 'install-dotnet.ps1') | ||||
|         ) | ||||
|         .toString(); | ||||
|       expect(normalizeFileContents(currentContents)).toBe( | ||||
|         normalizeFileContents(upToDateContents) | ||||
|       ); | ||||
|     }, | ||||
|     TEST_TIMEOUT | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| function normalizeFileContents(contents: string): string { | ||||
|   return contents | ||||
|     .trim() | ||||
|     .replace(new RegExp('\r\n', 'g'), '\n') | ||||
|     .replace(new RegExp('\r', 'g'), '\n'); | ||||
| } | ||||
| @ -1,290 +1,443 @@ | ||||
| import * as io from '@actions/io'; | ||||
| import * as os from 'os'; | ||||
| import fs from 'fs'; | ||||
| import path from 'path'; | ||||
| import each from 'jest-each'; | ||||
| import * as hc from '@actions/http-client'; | ||||
| import semver from 'semver'; | ||||
| import * as exec from '@actions/exec'; | ||||
| import * as core from '@actions/core'; | ||||
| import * as io from '@actions/io'; | ||||
| import * as installer from '../src/installer'; | ||||
| import {QualityOptions} from '../src/setup-dotnet'; | ||||
|  | ||||
| import {IS_WINDOWS} from '../src/utils'; | ||||
| import {IS_LINUX} from '../src/utils'; | ||||
| import {QualityOptions} from '../src/setup-dotnet'; | ||||
|  | ||||
| let toolDir: string; | ||||
| describe('installer tests', () => { | ||||
|   const env = process.env; | ||||
|  | ||||
| if (IS_WINDOWS) { | ||||
|   toolDir = path.join(process.env['PROGRAMFILES'] + '', 'dotnet'); | ||||
| } else if (IS_LINUX) { | ||||
|   toolDir = '/usr/share/dotnet'; | ||||
| } else { | ||||
|   toolDir = path.join(process.env['HOME'] + '', '.dotnet'); | ||||
| } | ||||
| const tempDir = path.join(__dirname, 'runner', 'temp'); | ||||
|   beforeEach(() => { | ||||
|     jest.resetModules(); | ||||
|     process.env = {...env}; | ||||
|   }); | ||||
|  | ||||
| process.env['RUNNER_TOOL_CACHE'] = toolDir; | ||||
| process.env['RUNNER_TEMP'] = tempDir; | ||||
|   describe('DotnetCoreInstaller tests', () => { | ||||
|     const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); | ||||
|     const warningSpy = jest.spyOn(core, 'warning'); | ||||
|     const whichSpy = jest.spyOn(io, 'which'); | ||||
|     const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying'); | ||||
|  | ||||
| describe('DotnetCoreInstaller tests', () => { | ||||
|   beforeAll(async () => { | ||||
|     process.env.RUNNER_TOOL_CACHE = toolDir; | ||||
|     process.env.DOTNET_INSTALL_DIR = toolDir; | ||||
|     process.env.RUNNER_TEMP = tempDir; | ||||
|     process.env.DOTNET_ROOT = ''; | ||||
|     try { | ||||
|       await io.rmRF(`${toolDir}/*`); | ||||
|       await io.rmRF(`${tempDir}/*`); | ||||
|     } catch (err) { | ||||
|       console.log( | ||||
|         `Failed to remove test directories, check the error message:${os.EOL}`, | ||||
|         err.message | ||||
|       ); | ||||
|     } | ||||
|   }, 30000); | ||||
|     describe('installDotnet() tests', () => { | ||||
|       whichSpy.mockImplementation(() => Promise.resolve('PathToShell')); | ||||
|  | ||||
|   afterEach(async () => { | ||||
|     try { | ||||
|       await io.rmRF(`${toolDir}/*`); | ||||
|       await io.rmRF(`${tempDir}/*`); | ||||
|     } catch (err) { | ||||
|       console.log( | ||||
|         `Failed to remove test directories, check the error message:${os.EOL}`, | ||||
|         err.message | ||||
|       ); | ||||
|     } | ||||
|   }, 30000); | ||||
|       it('should throw the error in case of non-zero exit code of the installation script. The error message should contain logs.', async () => { | ||||
|         const inputVersion = '3.1.100'; | ||||
|         const inputQuality = '' as QualityOptions; | ||||
|         const errorMessage = 'fictitious error message!'; | ||||
|         getExecOutputSpy.mockImplementation(() => { | ||||
|           return Promise.resolve({ | ||||
|             exitCode: 1, | ||||
|             stdout: '', | ||||
|             stderr: errorMessage | ||||
|           }); | ||||
|         }); | ||||
|         const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|           inputVersion, | ||||
|           inputQuality | ||||
|         ); | ||||
|         await expect(dotnetInstaller.installDotnet()).rejects.toThrow( | ||||
|           `Failed to install dotnet, exit code: 1. ${errorMessage}` | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|   it('Aquires multiple versions of dotnet', async () => { | ||||
|     const versions = ['2.2.207', '3.1.120']; | ||||
|       it('should return version of .NET SDK after installation complete', async () => { | ||||
|         const inputVersion = '3.1.100'; | ||||
|         const inputQuality = '' as QualityOptions; | ||||
|         const stdout = `Fictitious dotnet version ${inputVersion} is installed`; | ||||
|         getExecOutputSpy.mockImplementation(() => { | ||||
|           return Promise.resolve({ | ||||
|             exitCode: 0, | ||||
|             stdout: `${stdout}`, | ||||
|             stderr: '' | ||||
|           }); | ||||
|         }); | ||||
|         maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|     for (const version of versions) { | ||||
|       await getDotnet(version); | ||||
|     } | ||||
|     expect(fs.existsSync(path.join(toolDir, 'sdk', '2.2.207'))).toBe(true); | ||||
|     expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.120'))).toBe(true); | ||||
|         const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|           inputVersion, | ||||
|           inputQuality | ||||
|         ); | ||||
|         const installedVersion = await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|     if (IS_WINDOWS) { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); | ||||
|     } else { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true); | ||||
|     } | ||||
|         expect(installedVersion).toBe(inputVersion); | ||||
|       }); | ||||
|  | ||||
|     expect(process.env.DOTNET_ROOT).toBeDefined; | ||||
|     expect(process.env.PATH).toBeDefined; | ||||
|     expect(process.env.DOTNET_ROOT).toBe(toolDir); | ||||
|     expect(process.env.PATH?.startsWith(toolDir)).toBe(true); | ||||
|   }, 600000); | ||||
|       it(`should supply 'version' argument to the installation script if supplied version is in A.B.C syntax`, async () => { | ||||
|         const inputVersion = '6.0.300'; | ||||
|         const inputQuality = '' as QualityOptions; | ||||
|         const stdout = `Fictitious dotnet version ${inputVersion} is installed`; | ||||
|  | ||||
|   it('Acquires version of dotnet if no matching version is installed', async () => { | ||||
|     await getDotnet('3.1.201'); | ||||
|     expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true); | ||||
|     if (IS_WINDOWS) { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); | ||||
|     } else { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true); | ||||
|     } | ||||
|         getExecOutputSpy.mockImplementation(() => { | ||||
|           return Promise.resolve({ | ||||
|             exitCode: 0, | ||||
|             stdout: `${stdout}`, | ||||
|             stderr: '' | ||||
|           }); | ||||
|         }); | ||||
|         maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|     expect(process.env.DOTNET_ROOT).toBeDefined; | ||||
|     expect(process.env.PATH).toBeDefined; | ||||
|     expect(process.env.DOTNET_ROOT).toBe(toolDir); | ||||
|     expect(process.env.PATH?.startsWith(toolDir)).toBe(true); | ||||
|   }, 600000); //This needs some time to download on "slower" internet connections | ||||
|         const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|           inputVersion, | ||||
|           inputQuality | ||||
|         ); | ||||
|  | ||||
|   it('Acquires generic version of dotnet if no matching version is installed', async () => { | ||||
|     await getDotnet('3.1'); | ||||
|     var directory = fs | ||||
|       .readdirSync(path.join(toolDir, 'sdk')) | ||||
|       .filter(fn => fn.startsWith('3.1.')); | ||||
|     expect(directory.length > 0).toBe(true); | ||||
|     if (IS_WINDOWS) { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); | ||||
|     } else { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true); | ||||
|     } | ||||
|         await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|     expect(process.env.DOTNET_ROOT).toBeDefined; | ||||
|     expect(process.env.PATH).toBeDefined; | ||||
|     expect(process.env.DOTNET_ROOT).toBe(toolDir); | ||||
|     expect(process.env.PATH?.startsWith(toolDir)).toBe(true); | ||||
|   }, 600000); //This needs some time to download on "slower" internet connections | ||||
|         const scriptArguments = ( | ||||
|           getExecOutputSpy.mock.calls[0][1] as string[] | ||||
|         ).join(' '); | ||||
|         const expectedArgument = IS_WINDOWS | ||||
|           ? `-Version ${inputVersion}` | ||||
|           : `--version ${inputVersion}`; | ||||
|  | ||||
|   it('Returns string with installed SDK version', async () => { | ||||
|     const version = '3.1.120'; | ||||
|     let installedVersion: string; | ||||
|         expect(scriptArguments).toContain(expectedArgument); | ||||
|       }); | ||||
|  | ||||
|     installedVersion = await getDotnet(version); | ||||
|       it(`should warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => { | ||||
|         const inputVersion = '6.0.300'; | ||||
|         const inputQuality = 'ga' as QualityOptions; | ||||
|         const stdout = `Fictitious dotnet version ${inputVersion} is installed`; | ||||
|         getExecOutputSpy.mockImplementation(() => { | ||||
|           return Promise.resolve({ | ||||
|             exitCode: 0, | ||||
|             stdout: `${stdout}`, | ||||
|             stderr: '' | ||||
|           }); | ||||
|         }); | ||||
|         maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|     expect(installedVersion).toBe('3.1.120'); | ||||
|   }, 600000); | ||||
|         const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|           inputVersion, | ||||
|           inputQuality | ||||
|         ); | ||||
|  | ||||
|   it('Throws if no location contains correct dotnet version', async () => { | ||||
|     await expect(async () => { | ||||
|       await getDotnet('1000.0.0'); | ||||
|     }).rejects.toThrow(); | ||||
|   }, 30000); | ||||
|         await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|   it('Uses an up to date bash download script', async () => { | ||||
|     const httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], { | ||||
|       allowRetries: true, | ||||
|       maxRetries: 3 | ||||
|     }); | ||||
|     const response: hc.HttpClientResponse = await httpCallbackClient.get( | ||||
|       'https://dot.net/v1/dotnet-install.sh' | ||||
|     ); | ||||
|     expect(response.message.statusCode).toBe(200); | ||||
|     const upToDateContents: string = await response.readBody(); | ||||
|     const currentContents: string = fs | ||||
|       .readFileSync( | ||||
|         path.join(__dirname, '..', 'externals', 'install-dotnet.sh') | ||||
|       ) | ||||
|       .toString(); | ||||
|     expect(normalizeFileContents(currentContents)).toBe( | ||||
|       normalizeFileContents(upToDateContents) | ||||
|     ); | ||||
|   }, 30000); | ||||
|         expect(warningSpy).toHaveBeenCalledWith( | ||||
|           `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${inputVersion}. 'dotnet-quality' input is ignored.` | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|   it('Uses an up to date powershell download script', async () => { | ||||
|     var httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], { | ||||
|       allowRetries: true, | ||||
|       maxRetries: 3 | ||||
|     }); | ||||
|     const response: hc.HttpClientResponse = await httpCallbackClient.get( | ||||
|       'https://dot.net/v1/dotnet-install.ps1' | ||||
|     ); | ||||
|     expect(response.message.statusCode).toBe(200); | ||||
|     const upToDateContents: string = await response.readBody(); | ||||
|     const currentContents: string = fs | ||||
|       .readFileSync( | ||||
|         path.join(__dirname, '..', 'externals', 'install-dotnet.ps1') | ||||
|       ) | ||||
|       .toString(); | ||||
|     expect(normalizeFileContents(currentContents)).toBe( | ||||
|       normalizeFileContents(upToDateContents) | ||||
|     ); | ||||
|   }, 30000); | ||||
| }); | ||||
|       it(`should warn if the 'quality' input is set and version isn't in A.B.C syntax but major tag is lower then 6`, async () => { | ||||
|         const inputVersion = '3.1'; | ||||
|         const inputQuality = 'ga' as QualityOptions; | ||||
|         const stdout = `Fictitious dotnet version 3.1.100 is installed`; | ||||
|  | ||||
| describe('DotnetVersionResolver tests', () => { | ||||
|   each([ | ||||
|     '3.1', | ||||
|     '3.x', | ||||
|     '3.1.x', | ||||
|     '3.1.*', | ||||
|     '3.1.X', | ||||
|     '3.1.2', | ||||
|     '3.1.0-preview1' | ||||
|   ]).test( | ||||
|     "if valid version: '%s' is supplied, it should return version object with some value", | ||||
|     async version => { | ||||
|       const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|         version | ||||
|       ); | ||||
|       const versionObject = await dotnetVersionResolver.createDotNetVersion(); | ||||
|         getExecOutputSpy.mockImplementation(() => { | ||||
|           return Promise.resolve({ | ||||
|             exitCode: 0, | ||||
|             stdout: `${stdout}`, | ||||
|             stderr: '' | ||||
|           }); | ||||
|         }); | ||||
|         maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|       expect(!!versionObject.value).toBeTruthy; | ||||
|     } | ||||
|   ); | ||||
|         const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|           inputVersion, | ||||
|           inputQuality | ||||
|         ); | ||||
|  | ||||
|   each([ | ||||
|     '.', | ||||
|     '..', | ||||
|     ' . ', | ||||
|     '. ', | ||||
|     ' .', | ||||
|     ' . . ', | ||||
|     ' .. ', | ||||
|     ' .  ', | ||||
|     '-1.-1', | ||||
|     '-1', | ||||
|     '-1.-1.-1', | ||||
|     '..3', | ||||
|     '1..3', | ||||
|     '1..', | ||||
|     '.2.3', | ||||
|     '.2.x', | ||||
|     '*.', | ||||
|     '1.2.', | ||||
|     '1.2.-abc', | ||||
|     'a.b', | ||||
|     'a.b.c', | ||||
|     'a.b.c-preview', | ||||
|     ' 0 . 1 . 2 ', | ||||
|     'invalid' | ||||
|   ]).test( | ||||
|     "if invalid version: '%s' is supplied, it should throw", | ||||
|     async version => { | ||||
|       const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|         version | ||||
|         await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|         expect(warningSpy).toHaveBeenCalledWith( | ||||
|           `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${inputVersion}. 'dotnet-quality' input is ignored.` | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test( | ||||
|         `should supply 'quality' argument to the installation script if quality input is set and version (%s) is not in A.B.C syntax`, | ||||
|         async inputVersion => { | ||||
|           const inputQuality = 'ga' as QualityOptions; | ||||
|           const exitCode = 0; | ||||
|           const stdout = `Fictitious dotnet version 6.0.0 is installed`; | ||||
|           getExecOutputSpy.mockImplementation(() => { | ||||
|             return Promise.resolve({ | ||||
|               exitCode: exitCode, | ||||
|               stdout: `${stdout}`, | ||||
|               stderr: '' | ||||
|             }); | ||||
|           }); | ||||
|           maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|           const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|             inputVersion, | ||||
|             inputQuality | ||||
|           ); | ||||
|  | ||||
|           await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|           const scriptArguments = ( | ||||
|             getExecOutputSpy.mock.calls[0][1] as string[] | ||||
|           ).join(' '); | ||||
|           const expectedArgument = IS_WINDOWS | ||||
|             ? `-Quality ${inputQuality}` | ||||
|             : `--quality ${inputQuality}`; | ||||
|  | ||||
|           expect(scriptArguments).toContain(expectedArgument); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       await expect( | ||||
|         async () => await dotnetVersionResolver.createDotNetVersion() | ||||
|       ).rejects.toThrow(); | ||||
|     } | ||||
|   ); | ||||
|       each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test( | ||||
|         `should supply 'channel' argument to the installation script if version (%s) isn't in A.B.C syntax`, | ||||
|         async inputVersion => { | ||||
|           const inputQuality = '' as QualityOptions; | ||||
|           const exitCode = 0; | ||||
|           const stdout = `Fictitious dotnet version 6.0.0 is installed`; | ||||
|           getExecOutputSpy.mockImplementation(() => { | ||||
|             return Promise.resolve({ | ||||
|               exitCode: exitCode, | ||||
|               stdout: `${stdout}`, | ||||
|               stderr: '' | ||||
|             }); | ||||
|           }); | ||||
|           maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|   each(['3.1', '3.1.x', '3.1.*', '3.1.X']).test( | ||||
|     "if version: '%s' that can be resolved to 'channel' option is supplied, it should set quality flag to 'true' and type to 'channel' in version object", | ||||
|     async version => { | ||||
|       const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|         version | ||||
|           const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|             inputVersion, | ||||
|             inputQuality | ||||
|           ); | ||||
|  | ||||
|           await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|           const scriptArguments = ( | ||||
|             getExecOutputSpy.mock.calls[0][1] as string[] | ||||
|           ).join(' '); | ||||
|           const expectedArgument = IS_WINDOWS | ||||
|             ? `-Channel 6.0` | ||||
|             : `--channel 6.0`; | ||||
|  | ||||
|           expect(scriptArguments).toContain(expectedArgument); | ||||
|         } | ||||
|       ); | ||||
|       const versionObject = await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|       expect(versionObject.type.toLowerCase().includes('channel')).toBeTruthy; | ||||
|       expect(versionObject.qualityFlag).toBeTruthy; | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   each(['3.1.2', '3.1.0-preview1']).test( | ||||
|     "if version: '%s' that can be resolved to 'version' option is supplied, it should set quality flag to 'false' and type to 'version' in version object", | ||||
|     async version => { | ||||
|       const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|         version | ||||
|       ); | ||||
|       const versionObject = await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|       expect(versionObject.type.toLowerCase().includes('version')).toBeTruthy; | ||||
|       expect(versionObject.qualityFlag).toBeFalsy; | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   each(['3.1.2', '3.1']).test( | ||||
|     'it should create proper line arguments for powershell/bash installation scripts', | ||||
|     async version => { | ||||
|       const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|         version | ||||
|       ); | ||||
|       const versionObject = await dotnetVersionResolver.createDotNetVersion(); | ||||
|       const windowsRegEx = new RegExp(/^-[VC]/); | ||||
|       const nonWindowsRegEx = new RegExp(/^--[vc]/); | ||||
|  | ||||
|       if (IS_WINDOWS) { | ||||
|         expect(windowsRegEx.test(versionObject.type)).toBeTruthy; | ||||
|         expect(nonWindowsRegEx.test(versionObject.type)).toBeFalsy; | ||||
|       } else { | ||||
|         expect(nonWindowsRegEx.test(versionObject.type)).toBeTruthy; | ||||
|         expect(windowsRegEx.test(versionObject.type)).toBeFalsy; | ||||
|         it(`should supply '-ProxyAddress' argument to the installation script if env.variable 'https_proxy' is set`, async () => { | ||||
|           process.env['https_proxy'] = 'https://proxy.com'; | ||||
|           const inputVersion = '6.0.100'; | ||||
|           const inputQuality = '' as QualityOptions; | ||||
|           const stdout = `Fictitious dotnet version ${inputVersion} is installed`; | ||||
|  | ||||
|           getExecOutputSpy.mockImplementation(() => { | ||||
|             return Promise.resolve({ | ||||
|               exitCode: 0, | ||||
|               stdout: `${stdout}`, | ||||
|               stderr: '' | ||||
|             }); | ||||
|           }); | ||||
|           maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|           const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|             inputVersion, | ||||
|             inputQuality | ||||
|           ); | ||||
|  | ||||
|           await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|           const scriptArguments = ( | ||||
|             getExecOutputSpy.mock.calls[0][1] as string[] | ||||
|           ).join(' '); | ||||
|  | ||||
|           expect(scriptArguments).toContain( | ||||
|             `-ProxyAddress ${process.env['https_proxy']}` | ||||
|           ); | ||||
|         }); | ||||
|  | ||||
|         it(`should supply '-ProxyBypassList' argument to the installation script if env.variable 'no_proxy' is set`, async () => { | ||||
|           process.env['no_proxy'] = 'first.url,second.url'; | ||||
|           const inputVersion = '6.0.100'; | ||||
|           const inputQuality = '' as QualityOptions; | ||||
|           const stdout = `Fictitious dotnet version 6.0.0 is installed`; | ||||
|  | ||||
|           getExecOutputSpy.mockImplementation(() => { | ||||
|             return Promise.resolve({ | ||||
|               exitCode: 0, | ||||
|               stdout: `${stdout}`, | ||||
|               stderr: '' | ||||
|             }); | ||||
|           }); | ||||
|           maxSatisfyingSpy.mockImplementation(() => inputVersion); | ||||
|  | ||||
|           const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|             inputVersion, | ||||
|             inputQuality | ||||
|           ); | ||||
|  | ||||
|           await dotnetInstaller.installDotnet(); | ||||
|  | ||||
|           const scriptArguments = ( | ||||
|             getExecOutputSpy.mock.calls[0][1] as string[] | ||||
|           ).join(' '); | ||||
|  | ||||
|           expect(scriptArguments).toContain( | ||||
|             `-ProxyBypassList ${process.env['no_proxy']}` | ||||
|           ); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   ); | ||||
|     }); | ||||
|  | ||||
|     describe('addToPath() tests', () => { | ||||
|       it(`should export DOTNET_ROOT env.var with value from DOTNET_INSTALL_DIR env.var`, async () => { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir'; | ||||
|         installer.DotnetCoreInstaller.addToPath(); | ||||
|         const dotnet_root = process.env['DOTNET_ROOT']; | ||||
|         expect(dotnet_root).toBe(process.env['DOTNET_INSTALL_DIR']); | ||||
|       }); | ||||
|  | ||||
|       it(`should export value from DOTNET_INSTALL_DIR env.var to the PATH`, async () => { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir'; | ||||
|         installer.DotnetCoreInstaller.addToPath(); | ||||
|         const path = process.env['PATH']; | ||||
|         expect(path).toContain(process.env['DOTNET_INSTALL_DIR']); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('DotnetVersionResolver tests', () => { | ||||
|     describe('createDotNetVersion() tests', () => { | ||||
|       each([ | ||||
|         '3.1', | ||||
|         '3.x', | ||||
|         '3.1.x', | ||||
|         '3.1.*', | ||||
|         '3.1.X', | ||||
|         '3.1.2', | ||||
|         '3.1.0-preview1', | ||||
|         '6.0.2xx' | ||||
|       ]).test( | ||||
|         'if valid version is supplied (%s), it should return version object with some value', | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|           const versionObject = | ||||
|             await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|           expect(!!versionObject.value).toBe(true); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       each([ | ||||
|         '.', | ||||
|         '..', | ||||
|         ' . ', | ||||
|         '. ', | ||||
|         ' .', | ||||
|         ' . . ', | ||||
|         ' .. ', | ||||
|         ' .  ', | ||||
|         '-1.-1', | ||||
|         '-1', | ||||
|         '-1.-1.-1', | ||||
|         '..3', | ||||
|         '1..3', | ||||
|         '1..', | ||||
|         '.2.3', | ||||
|         '.2.x', | ||||
|         '*.', | ||||
|         '1.2.', | ||||
|         '1.2.-abc', | ||||
|         'a.b', | ||||
|         'a.b.c', | ||||
|         'a.b.c-preview', | ||||
|         ' 0 . 1 . 2 ', | ||||
|         'invalid' | ||||
|       ]).test( | ||||
|         'if invalid version is supplied (%s), it should throw', | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|  | ||||
|           await expect( | ||||
|             async () => await dotnetVersionResolver.createDotNetVersion() | ||||
|           ).rejects.toThrow(); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       each(['3', '3.1', '3.1.x', '3.1.*', '3.1.X', '6.0.2xx']).test( | ||||
|         "if version that can be resolved to 'channel' option is supplied (%s), it should set type to 'channel' in version object", | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|           const versionObject = | ||||
|             await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|           expect(versionObject.type.toLowerCase().includes('channel')).toBe( | ||||
|             true | ||||
|           ); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       each(['6.0', '6.0.x', '6.0.*', '6.0.X', '6.0.2xx']).test( | ||||
|         "if version that can be resolved to 'channel' option is supplied and its major tag is >= 6 (%s), it should set type to 'channel' and qualityFlag to 'true' in version object", | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|           const versionObject = | ||||
|             await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|           expect(versionObject.type.toLowerCase().includes('channel')).toBe( | ||||
|             true | ||||
|           ); | ||||
|           expect(versionObject.qualityFlag).toBe(true); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       each(['3.1.2', '3.1.0-preview1']).test( | ||||
|         "if version that can be resolved to 'version' option is supplied (%s), it should set quality flag to 'false' and type to 'version' in version object", | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|           const versionObject = | ||||
|             await dotnetVersionResolver.createDotNetVersion(); | ||||
|  | ||||
|           expect(versionObject.type.toLowerCase().includes('version')).toBe( | ||||
|             true | ||||
|           ); | ||||
|           expect(versionObject.qualityFlag).toBe(false); | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       each(['3.1.2', '3.1']).test( | ||||
|         'it should create proper line arguments for powershell/bash installation scripts', | ||||
|         async version => { | ||||
|           const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|             version | ||||
|           ); | ||||
|           const versionObject = | ||||
|             await dotnetVersionResolver.createDotNetVersion(); | ||||
|           const windowsRegEx = new RegExp(/^-(Version|Channel)/); | ||||
|           const nonWindowsRegEx = new RegExp(/^--(version|channel)/); | ||||
|  | ||||
|           if (IS_WINDOWS) { | ||||
|             expect(windowsRegEx.test(versionObject.type)).toBe(true); | ||||
|             expect(nonWindowsRegEx.test(versionObject.type)).toBe(false); | ||||
|           } else { | ||||
|             expect(nonWindowsRegEx.test(versionObject.type)).toBe(true); | ||||
|             expect(windowsRegEx.test(versionObject.type)).toBe(false); | ||||
|           } | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       it(`should throw if dotnet-version is supplied in A.B.Cxx syntax with major tag lower that 5`, async () => { | ||||
|         const version = '3.0.1xx'; | ||||
|         const dotnetVersionResolver = new installer.DotnetVersionResolver( | ||||
|           version | ||||
|         ); | ||||
|         await expect( | ||||
|           async () => await dotnetVersionResolver.createDotNetVersion() | ||||
|         ).rejects.toThrow( | ||||
|           `'dotnet-version' was supplied in invalid format: ${version}! The A.B.Cxx syntax is available since the .NET 5.0 release.` | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| function normalizeFileContents(contents: string): string { | ||||
|   return contents | ||||
|     .trim() | ||||
|     .replace(new RegExp('\r\n', 'g'), '\n') | ||||
|     .replace(new RegExp('\r', 'g'), '\n'); | ||||
| } | ||||
|  | ||||
| async function getDotnet( | ||||
|   version: string, | ||||
|   quality: string = '' | ||||
| ): Promise<string> { | ||||
|   const dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|     version, | ||||
|     quality as QualityOptions | ||||
|   ); | ||||
|   const installedVersion = await dotnetInstaller.installDotnet(); | ||||
|   installer.DotnetCoreInstaller.addToPath(); | ||||
|   return installedVersion; | ||||
| } | ||||
|  | ||||
| @ -1,15 +0,0 @@ | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using System; | ||||
|  | ||||
| namespace sample_csproj | ||||
| { | ||||
|     [TestClass] | ||||
|     public class Program | ||||
|     { | ||||
|         [TestMethod] | ||||
|         public void TestMethod1() | ||||
|         { | ||||
|             Console.WriteLine("Hello, World!"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFrameworks>netcoreapp3.1;netcoreapp3.0;netcoreapp2.2</TargetFrameworks> | ||||
|     <RootNamespace>sample_csproj</RootNamespace> | ||||
|  | ||||
|     <IsPackable>false</IsPackable> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <!-- These packages will be downloaded over the network for testing proxy settings --> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | ||||
|     <PackageReference Include="MSTest.TestAdapter" Version="2.1.0" /> | ||||
|     <PackageReference Include="MSTest.TestFramework" Version="2.1.0" /> | ||||
|     <PackageReference Include="coverlet.collector" Version="1.2.0" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @ -1,98 +1,173 @@ | ||||
| import * as io from '@actions/io'; | ||||
| import * as core from '@actions/core'; | ||||
| import fs from 'fs'; | ||||
| import os from 'os'; | ||||
| import path from 'path'; | ||||
| import semver from 'semver'; | ||||
| import * as auth from '../src/authutil'; | ||||
|  | ||||
| import * as setup from '../src/setup-dotnet'; | ||||
| import {IS_WINDOWS} from '../src/utils'; | ||||
| import {IS_LINUX} from '../src/utils'; | ||||
|  | ||||
| let toolDir: string; | ||||
|  | ||||
| if (IS_WINDOWS) { | ||||
|   toolDir = path.join(process.env['PROGRAMFILES'] + '', 'dotnet'); | ||||
| } else if (IS_LINUX) { | ||||
|   toolDir = '/usr/share/dotnet'; | ||||
| } else { | ||||
|   toolDir = path.join(process.env['HOME'] + '', '.dotnet'); | ||||
| } | ||||
|  | ||||
| const tempDir = path.join(__dirname, 'runner', 'temp2'); | ||||
| import {DotnetCoreInstaller} from '../src/installer'; | ||||
|  | ||||
| describe('setup-dotnet tests', () => { | ||||
|   let getInputSpy = jest.spyOn(core, 'getInput'); | ||||
|   let getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput'); | ||||
|   let setOutputSpy = jest.spyOn(core, 'setOutput'); | ||||
|   const inputs = {} as any; | ||||
|  | ||||
|   let inputs = {} as any; | ||||
|   const getInputSpy = jest.spyOn(core, 'getInput'); | ||||
|   const getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput'); | ||||
|   const setFailedSpy = jest.spyOn(core, 'setFailed'); | ||||
|   const warningSpy = jest.spyOn(core, 'warning'); | ||||
|   const debugSpy = jest.spyOn(core, 'debug'); | ||||
|   const infoSpy = jest.spyOn(core, 'info'); | ||||
|   const setOutputSpy = jest.spyOn(core, 'setOutput'); | ||||
|  | ||||
|   beforeAll(async () => { | ||||
|     process.env.RUNNER_TOOL_CACHE = toolDir; | ||||
|     process.env.DOTNET_INSTALL_DIR = toolDir; | ||||
|     process.env.RUNNER_TEMP = tempDir; | ||||
|     try { | ||||
|       await io.rmRF(`${toolDir}/*`); | ||||
|       await io.rmRF(`${tempDir}/*`); | ||||
|     } catch (err) { | ||||
|       console.log(err.message); | ||||
|       console.log('Failed to remove test directories'); | ||||
|     } | ||||
|   }, 30000); | ||||
|   const existsSyncSpy = jest.spyOn(fs, 'existsSync'); | ||||
|  | ||||
|   afterEach(async () => { | ||||
|     try { | ||||
|       await io.rmRF(path.join(process.cwd(), 'global.json')); | ||||
|       await io.rmRF(`${toolDir}/*`); | ||||
|       await io.rmRF(`${tempDir}/*`); | ||||
|     } catch (err) { | ||||
|       console.log(err.message); | ||||
|       console.log('Failed to remove test directories'); | ||||
|     } | ||||
|   }, 30000); | ||||
|   const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying'); | ||||
|  | ||||
|   it('Acquires version of dotnet from global.json if no matching version is installed', async () => { | ||||
|     const globalJsonPath = path.join(process.cwd(), 'global.json'); | ||||
|     const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.1.201"${os.EOL}}${os.EOL}}`; | ||||
|     if (!fs.existsSync(globalJsonPath)) { | ||||
|       fs.writeFileSync(globalJsonPath, jsonContents); | ||||
|     } | ||||
|     await setup.run(); | ||||
|   const installDotnetSpy = jest.spyOn( | ||||
|     DotnetCoreInstaller.prototype, | ||||
|     'installDotnet' | ||||
|   ); | ||||
|   const addToPathSpy = jest.spyOn(DotnetCoreInstaller, 'addToPath'); | ||||
|  | ||||
|     expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true); | ||||
|     if (IS_WINDOWS) { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); | ||||
|     } else { | ||||
|       expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true); | ||||
|     } | ||||
|   }, 400000); | ||||
|   const configAuthenticationSpy = jest.spyOn(auth, 'configAuthentication'); | ||||
|  | ||||
|   it("Sets output with the latest installed by action version if global.json file isn't specified", async () => { | ||||
|     inputs['dotnet-version'] = ['3.1.201', '6.0.401']; | ||||
|   describe('run() tests', () => { | ||||
|     beforeEach(() => { | ||||
|       getMultilineInputSpy.mockImplementation(input => inputs[input as string]); | ||||
|       getInputSpy.mockImplementation(input => inputs[input as string]); | ||||
|     }); | ||||
|  | ||||
|     getMultilineInputSpy.mockImplementation(input => inputs[input]); | ||||
|     afterEach(() => { | ||||
|       jest.clearAllMocks(); | ||||
|       jest.resetAllMocks(); | ||||
|     }); | ||||
|  | ||||
|     await setup.run(); | ||||
|     it('should fail the action if global-json-file input is present, but the file does not exist in the file system', async () => { | ||||
|       inputs['global-json-file'] = 'fictitious.json'; | ||||
|       inputs['dotnet-version'] = []; | ||||
|  | ||||
|     expect(setOutputSpy).toBeCalledWith('dotnet-version', '6.0.401'); | ||||
|   }, 400000); | ||||
|       const expectedErrorMessage = `The specified global.json file '${inputs['global-json-file']}' does not exist`; | ||||
|  | ||||
|   it("Sets output with the version specified in global.json, if it's present", async () => { | ||||
|     const globalJsonPath = path.join(process.cwd(), 'global.json'); | ||||
|     const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.0.103"${os.EOL}}${os.EOL}}`; | ||||
|     if (!fs.existsSync(globalJsonPath)) { | ||||
|       fs.writeFileSync(globalJsonPath, jsonContents); | ||||
|     } | ||||
|       await setup.run(); | ||||
|       expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage); | ||||
|     }); | ||||
|  | ||||
|     inputs['dotnet-version'] = ['3.1.201', '6.0.401']; | ||||
|     inputs['global-json-file'] = './global.json'; | ||||
|     test(`if 'dotnet-version' and 'global-json-file' inputs aren't present, should log into debug output, try to find global.json in the repo root, fail and log message into info output`, async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = []; | ||||
|  | ||||
|     getMultilineInputSpy.mockImplementation(input => inputs[input]); | ||||
|       maxSatisfyingSpy.mockImplementation(() => null); | ||||
|       setOutputSpy.mockImplementation(() => {}); | ||||
|  | ||||
|     getInputSpy.mockImplementation(input => inputs[input]); | ||||
|       const expectedDebugMessage = | ||||
|         'No version found, trying to find version from global.json'; | ||||
|       const expectedInfoMessage = `The global.json wasn't found in the root directory. No .NET version will be installed.`; | ||||
|  | ||||
|     await setup.run(); | ||||
|       await setup.run(); | ||||
|  | ||||
|     expect(setOutputSpy).toBeCalledWith('dotnet-version', '3.0.103'); | ||||
|   }, 400000); | ||||
|       expect(debugSpy).toHaveBeenCalledWith(expectedDebugMessage); | ||||
|       expect(existsSyncSpy).toHaveBeenCalled(); | ||||
|       expect(infoSpy).toHaveBeenCalledWith(expectedInfoMessage); | ||||
|     }); | ||||
|  | ||||
|     it('should fail the action if quality is supplied but its value is not supported', async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = ['6.0']; | ||||
|       inputs['dotnet-quality'] = 'fictitiousQuality'; | ||||
|  | ||||
|       const expectedErrorMessage = `Value '${inputs['dotnet-quality']}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`; | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage); | ||||
|     }); | ||||
|  | ||||
|     it('should call installDotnet() multiple times if dotnet-version multiline input is provided', async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = ['6.0', '7.0']; | ||||
|       inputs['dotnet-quality'] = ''; | ||||
|  | ||||
|       installDotnetSpy.mockImplementation(() => Promise.resolve('')); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(installDotnetSpy).toHaveBeenCalledTimes(2); | ||||
|     }); | ||||
|  | ||||
|     it('should call addToPath() after installation complete', async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = ['6.0', '7.0']; | ||||
|       inputs['dotnet-quality'] = ''; | ||||
|  | ||||
|       installDotnetSpy.mockImplementation(() => Promise.resolve('')); | ||||
|       addToPathSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(addToPathSpy).toHaveBeenCalledTimes(1); | ||||
|     }); | ||||
|  | ||||
|     it('should call auth.configAuthentication() if source-url input is provided', async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = []; | ||||
|       inputs['dotnet-quality'] = ''; | ||||
|       inputs['source-url'] = 'fictitious.source.url'; | ||||
|  | ||||
|       configAuthenticationSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(configAuthenticationSpy).toHaveBeenCalledWith( | ||||
|         inputs['source-url'], | ||||
|         undefined | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should call auth.configAuthentication() with proper parameters if source-url and config-file inputs are provided', async () => { | ||||
|       inputs['global-json-file'] = ''; | ||||
|       inputs['dotnet-version'] = []; | ||||
|       inputs['dotnet-quality'] = ''; | ||||
|       inputs['source-url'] = 'fictitious.source.url'; | ||||
|       inputs['config-file'] = 'fictitious.path'; | ||||
|  | ||||
|       configAuthenticationSpy.mockImplementation(() => {}); | ||||
|       setOutputSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(configAuthenticationSpy).toHaveBeenCalledWith( | ||||
|         inputs['source-url'], | ||||
|         inputs['config-file'] | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should call setOutput() after installation complete successfully', async () => { | ||||
|       inputs['dotnet-version'] = ['6.0.300']; | ||||
|  | ||||
|       installDotnetSpy.mockImplementation(() => | ||||
|         Promise.resolve(`${inputs['dotnet-version']}`) | ||||
|       ); | ||||
|       addToPathSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(setOutputSpy).toHaveBeenCalledTimes(1); | ||||
|     }); | ||||
|  | ||||
|     it(`shouldn't call setOutput() if parsing dotnet-installer logs failed`, async () => { | ||||
|       inputs['dotnet-version'] = ['6.0.300']; | ||||
|       const warningMessage = `Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.`; | ||||
|  | ||||
|       installDotnetSpy.mockImplementation(() => Promise.resolve(null)); | ||||
|       addToPathSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|       expect(warningSpy).toHaveBeenCalledWith(warningMessage); | ||||
|       expect(setOutputSpy).not.toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it(`shouldn't call setOutput() if actions didn't install .NET`, async () => { | ||||
|       inputs['dotnet-version'] = []; | ||||
|       const warningMessage = `The 'dotnet-version' output will not be set.`; | ||||
|  | ||||
|       addToPathSpy.mockImplementation(() => {}); | ||||
|  | ||||
|       await setup.run(); | ||||
|  | ||||
|       expect(infoSpy).toHaveBeenCalledWith(warningMessage); | ||||
|       expect(setOutputSpy).not.toHaveBeenCalled(); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -1,73 +1,117 @@ | ||||
| if (!$args[0]) | ||||
| { | ||||
|   throw "Must supply dotnet version argument" | ||||
| <# | ||||
|   .DESCRIPTION | ||||
|   Verifies that installed on the machine .NET SDK versions match the input patterns. | ||||
|   Optionally checks that the nuget.config file is generated correctly. | ||||
|  | ||||
|   .PARAMETER Patterns | ||||
|   Specifies the regular expression patterns that should be matched with the installed | ||||
|   on the machine .NET SDK versions. The number of patterns should be equal to the number | ||||
|   of installed .NET versions.  | ||||
|  | ||||
|   .PARAMETER CheckNugetConfig | ||||
|   Switches the check for the existence of the nuget.config file. | ||||
|  | ||||
|   .EXAMPLE | ||||
|   PS> .\verify-dotnet.ps1 -Paterns "^3.1.200$", "^6.0" -CheckNugetConfig | ||||
| #> | ||||
|  | ||||
| param( | ||||
|   [ValidateNotNullOrEmpty()] | ||||
|   [Parameter(Mandatory=$true)] | ||||
|   [string[]]$Patterns, | ||||
|   [switch]$CheckNugetConfig | ||||
| ) | ||||
|  | ||||
| $PatternsList = [System.Collections.ArrayList]($Patterns) | ||||
|  | ||||
| if ($CheckNugetConfig.IsPresent -and !(Test-Path "../nuget.config")) { | ||||
|   throw "The nuget.config file is not generated correctly." | ||||
| } | ||||
|  | ||||
| Write-Host "These patterns were supplied to the script: $($PatternsList -join ', ')." | ||||
| $dotnet = Get-Command dotnet | Select-Object -First 1 | ForEach-Object { $_.Path } | ||||
| Write-Host "Found '$dotnet'" | ||||
| Write-Host "Found: '$dotnet'" | ||||
|  | ||||
| if($args.count -eq 1) | ||||
| # SDKs are listed on multiple lines with the path afterwards in square brackets | ||||
| $Versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() } | ||||
| Write-Host "Found installed versions: $($Versions -join ', ')." | ||||
| $InstalledVersionCount = $Versions.Count | ||||
|  | ||||
| foreach($version in $Versions) | ||||
| { | ||||
|   $version = & $dotnet --version | Out-String | ForEach-Object { $_.Trim() } | ||||
|   Write-Host "Version $version" | ||||
|   if (-not ($version.StartsWith($args[0].ToString()))) | ||||
|   foreach($pattern in $PatternsList)  | ||||
|   { | ||||
|     Write-Host "PATH='$env:PATH'" | ||||
|     throw "Unexpected version" | ||||
|   } | ||||
| } | ||||
|  | ||||
| if ($args[1]) | ||||
| { | ||||
|   # SDKs are listed on multiple lines with the path afterwards in square brackets | ||||
|   $versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() } | ||||
|   Write-Host "Installed versions: $versions" | ||||
|   $InstalledVersionCount = 0 | ||||
|   foreach($arg in $args){ | ||||
|     foreach ($version in $versions) | ||||
|     { | ||||
|       if ($version.StartsWith($arg.ToString()))  | ||||
|       { | ||||
|         $InstalledVersionCount++ | ||||
|       } | ||||
|     if ($version -match $pattern) | ||||
|     {  | ||||
|       $PatternsList.Remove($pattern) | ||||
|       $InstalledVersionCount-- | ||||
|       break | ||||
|     } | ||||
|    } | ||||
|   if ( $InstalledVersionCount -ne $args.Count) | ||||
|   { | ||||
|     Write-Host "PATH='$env:PATH'" | ||||
|     throw "Unexpected version" | ||||
|   } | ||||
| } | ||||
|  | ||||
| Write-Host "Building sample csproj" | ||||
| & $dotnet build __tests__/sample-csproj/ --no-cache | ||||
| if ($LASTEXITCODE -ne 0) | ||||
| if ( $InstalledVersionCount -ne 0) | ||||
| { | ||||
|   throw "Unexpected exit code $LASTEXITCODE" | ||||
|   throw "An unexpected version of Dotnet is found on the machine, please check the correctness of the -Patterns input." | ||||
| } | ||||
|  | ||||
| Write-Host "Testing compiled app" | ||||
| $sample_output = "$(dotnet test __tests__/sample-csproj/ --no-build)" | ||||
| Write-Host "Sample output: $sample_output" | ||||
| # For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once | ||||
| if ($args[1]) | ||||
| $workingDir = Get-Location | ||||
| $testProjectDir = "./__tests__/e2e-test-csproj" | ||||
| Write-Host "Changing directory to the $testProjectDir" | ||||
| Set-Location $testProjectDir | ||||
|  | ||||
| $targetFrameworkVersionMap = @{ | ||||
|   "1.0" = "netcoreapp1.0"; | ||||
|   "1.1" = "netcoreapp1.1"; | ||||
|   "2.0" = "netcoreapp2.0"; | ||||
|   "2.1" = "netcoreapp2.1"; | ||||
|   "2.2" = "netcoreapp2.2"; | ||||
|   "3.0" = "netcoreapp3.0"; | ||||
|   "3.1" = "netcoreapp3.1"; | ||||
|   "5.0" = "net5.0"; | ||||
|   "6.0" = "net6.0"; | ||||
|   "7.0" = "net7.0"; | ||||
|   "8.0" = "net8.0"; | ||||
|  } | ||||
|  | ||||
| foreach ($version in $Versions) | ||||
| { | ||||
|   if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*") | ||||
|   # Creating temporary global.json file inside e2e-test-csproj dir and setting exact version of .NET inside allows to override default behavior of .NET and run build and tests on that exact version.  | ||||
|   Write-Host "Creating temporary global.json file for $version .NET version." | ||||
|   & $dotnet new globaljson --sdk-version $version --force | Out-Null | ||||
|   if (!(Test-Path "./global.json")) | ||||
|   { | ||||
|     throw "Unexpected output" | ||||
|     throw "An error occured while creating the global.json file. Exit code: $LASTEXITCODE" | ||||
|   } | ||||
| } | ||||
| if ($args[2]) | ||||
| { | ||||
|   if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*Test Run Successful.*") | ||||
|   Write-Host "The global.json file for the version $version is created. Currently used .NET version is: $(& $dotnet --version)." | ||||
|  | ||||
|   # Environment variable TEST_TARGET_FRAMEWORK is used inside the test.csproj file to target required framework version | ||||
|   $version -match "^(?<key>\d+\.\d+)" | Out-Null | ||||
|   if (!($targetFrameworkVersionMap.ContainsKey($Matches.key))) | ||||
|   { | ||||
|     throw "Unexpected output" | ||||
|     throw "The map with the framework targets doesn't contain a target name for the version $version." | ||||
|   } | ||||
| } | ||||
| else | ||||
| { | ||||
|   if ($sample_output -notlike "*Test Run Successful.*") | ||||
|   Write-Host "Setting the TEST_TARGET_FRAMEWORK environment variable to $($targetFrameworkVersionMap[$Matches.key])" | ||||
|   [Environment]::SetEnvironmentVariable('TEST_TARGET_FRAMEWORK', $($targetFrameworkVersionMap[$Matches.key])) | ||||
|  | ||||
|   Write-Host "Building test C# project with $version .NET version." | ||||
|   & $dotnet build --no-cache | ||||
|   if ($LASTEXITCODE -ne 0) | ||||
|   { | ||||
|     throw "Unexpected output" | ||||
|     throw "Building process is not successful, exit code: $LASTEXITCODE" | ||||
|   } | ||||
|  | ||||
|   Write-Host "Testing compiled C# project with $version .NET version." | ||||
|   & $dotnet test --no-build | ||||
|   if ($LASTEXITCODE -ne 0) | ||||
|   { | ||||
|     throw "Testing process is not successful, exit code: $LASTEXITCODE" | ||||
|   } | ||||
|  | ||||
|   Write-Host "Tests are completed successfully!" | ||||
|  | ||||
|   Write-Host "Removing temporary global.json file." | ||||
|   Remove-Item ./global.json | ||||
| } | ||||
|  | ||||
| Set-Location $workingDir | ||||
| @ -1,44 +0,0 @@ | ||||
| if [ -z "$1" ]; then | ||||
|   echo "Must supply dotnet version argument" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| if [ ! -f "../nuget.config" ]; then | ||||
|   echo "nuget file not generated correctly" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| dotnet_version="$(dotnet --version)" | ||||
| echo "Found dotnet version '$dotnet_version'" | ||||
| if [ -z "$(echo $dotnet_version | grep $1)" ]; then | ||||
|   echo "Unexpected version" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| if [ -n "$2" ]; then | ||||
|   dotnet_version="$(dotnet --list-sdks)" | ||||
|   echo "Found dotnet version '$dotnet_version'" | ||||
|   if [ -z "$(echo $dotnet_version | grep $2)" ]; then | ||||
|     echo "Unexpected version" | ||||
|     exit 1 | ||||
|   fi | ||||
| fi | ||||
|  | ||||
| echo "Building sample csproj" | ||||
| dotnet build __tests__/sample-csproj/ --no-cache || exit 1 | ||||
|  | ||||
| echo "Testing compiled app" | ||||
| sample_output=$(dotnet test __tests__/sample-csproj/ --no-build) | ||||
| echo "Sample output: $sample_output" | ||||
| # For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once | ||||
| if [ -n "$2" ]; then | ||||
|   if [ -z "$(echo $sample_output | grep "Test Run Successful.*Test Run Successful.")" ]; then | ||||
|     echo "Unexpected output" | ||||
|     exit 1 | ||||
|   fi | ||||
| else | ||||
|   if [ -z "$(echo $sample_output | grep "Test Run Successful.")" ]; then | ||||
|     echo "Unexpected output" | ||||
|     exit 1 | ||||
|   fi | ||||
| fi | ||||
| @ -6,7 +6,7 @@ branding: | ||||
|   color: green | ||||
| inputs: | ||||
|   dotnet-version: | ||||
|     description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x' | ||||
|     description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x, 6.0.2xx' | ||||
|   dotnet-quality: | ||||
|     description: 'Optional quality of the build. The possible values are: daily, signed, validated, preview, ga.' | ||||
|   global-json-file: | ||||
|  | ||||
							
								
								
									
										150
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										150
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -45,7 +45,7 @@ function configAuthentication(feedUrl, existingFileLocation = '', processRoot = | ||||
| } | ||||
| exports.configAuthentication = configAuthentication; | ||||
| function isValidKey(key) { | ||||
|     return /^[\w\-\.]+$/i.test(key); | ||||
|     return /^[\w\-.]+$/i.test(key); | ||||
| } | ||||
| function getExistingNugetConfig(processRoot) { | ||||
|     const defaultConfigName = 'nuget.config'; | ||||
| @ -60,9 +60,9 @@ function getExistingNugetConfig(processRoot) { | ||||
| function writeFeedToFile(feedUrl, existingFileLocation, tempFileLocation) { | ||||
|     var _a, _b; | ||||
|     core.info(`dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}`); | ||||
|     let sourceKeys = []; | ||||
|     const sourceKeys = []; | ||||
|     let owner = core.getInput('owner'); | ||||
|     let sourceUrl = feedUrl; | ||||
|     const sourceUrl = feedUrl; | ||||
|     if (!owner) { | ||||
|         owner = github.context.repo.owner; | ||||
|     } | ||||
| @ -132,7 +132,7 @@ function writeFeedToFile(feedUrl, existingFileLocation, tempFileLocation) { | ||||
|         } | ||||
|     ]; | ||||
|     if (!sourceKeys.length) { | ||||
|         let keystring = 'Source'; | ||||
|         const keystring = 'Source'; | ||||
|         xmlSource[1].configuration.push({ | ||||
|             packageSources: [ | ||||
|                 { | ||||
| @ -238,11 +238,12 @@ const exec = __importStar(__nccwpck_require__(1514)); | ||||
| const io = __importStar(__nccwpck_require__(7436)); | ||||
| const hc = __importStar(__nccwpck_require__(6255)); | ||||
| const fs_1 = __nccwpck_require__(7147); | ||||
| const promises_1 = __nccwpck_require__(3292); | ||||
| const path_1 = __importDefault(__nccwpck_require__(1017)); | ||||
| const os_1 = __importDefault(__nccwpck_require__(2037)); | ||||
| const semver_1 = __importDefault(__nccwpck_require__(5911)); | ||||
| const utils_1 = __nccwpck_require__(918); | ||||
| const QUALITY_INPUT_MINIMAL_MAJOR_TAG = 6; | ||||
| const LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG = 5; | ||||
| class DotnetVersionResolver { | ||||
|     constructor(version) { | ||||
|         this.inputVersion = version.trim(); | ||||
| @ -250,35 +251,54 @@ class DotnetVersionResolver { | ||||
|     } | ||||
|     resolveVersionInput() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if (!semver_1.default.validRange(this.inputVersion)) { | ||||
|                 throw new Error(`'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x`); | ||||
|             if (!semver_1.default.validRange(this.inputVersion) && !this.isLatestPatchSyntax()) { | ||||
|                 throw new Error(`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx`); | ||||
|             } | ||||
|             if (semver_1.default.valid(this.inputVersion)) { | ||||
|                 this.resolvedArgument.type = 'version'; | ||||
|                 this.resolvedArgument.value = this.inputVersion; | ||||
|                 this.createVersionArgument(); | ||||
|             } | ||||
|             else { | ||||
|                 const [major, minor] = this.inputVersion.split('.'); | ||||
|                 if (this.isNumericTag(major)) { | ||||
|                     this.resolvedArgument.type = 'channel'; | ||||
|                     if (this.isNumericTag(minor)) { | ||||
|                         this.resolvedArgument.value = `${major}.${minor}`; | ||||
|                     } | ||||
|                     else { | ||||
|                         const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|                             allowRetries: true, | ||||
|                             maxRetries: 3 | ||||
|                         }); | ||||
|                         this.resolvedArgument.value = yield this.getLatestVersion(httpClient, [major, minor]); | ||||
|                     } | ||||
|                 } | ||||
|                 this.resolvedArgument.qualityFlag = +major >= 6 ? true : false; | ||||
|                 yield this.createChannelArgument(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     isNumericTag(versionTag) { | ||||
|         return /^\d+$/.test(versionTag); | ||||
|     } | ||||
|     isLatestPatchSyntax() { | ||||
|         var _b, _c; | ||||
|         const majorTag = (_c = (_b = this.inputVersion.match(/^(?<majorTag>\d+)\.\d+\.\d{1}x{2}$/)) === null || _b === void 0 ? void 0 : _b.groups) === null || _c === void 0 ? void 0 : _c.majorTag; | ||||
|         if (majorTag && | ||||
|             parseInt(majorTag) < LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG) { | ||||
|             throw new Error(`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! The A.B.Cxx syntax is available since the .NET 5.0 release.`); | ||||
|         } | ||||
|         return majorTag ? true : false; | ||||
|     } | ||||
|     createVersionArgument() { | ||||
|         this.resolvedArgument.type = 'version'; | ||||
|         this.resolvedArgument.value = this.inputVersion; | ||||
|     } | ||||
|     createChannelArgument() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             this.resolvedArgument.type = 'channel'; | ||||
|             const [major, minor] = this.inputVersion.split('.'); | ||||
|             if (this.isLatestPatchSyntax()) { | ||||
|                 this.resolvedArgument.value = this.inputVersion; | ||||
|             } | ||||
|             else if (this.isNumericTag(major) && this.isNumericTag(minor)) { | ||||
|                 this.resolvedArgument.value = `${major}.${minor}`; | ||||
|             } | ||||
|             else if (this.isNumericTag(major)) { | ||||
|                 this.resolvedArgument.value = yield this.getLatestByMajorTag(major); | ||||
|             } | ||||
|             else { | ||||
|                 // If "dotnet-version" is specified as *, x or X resolve latest version of .NET explicitly from LTS channel. The version argument will default to "latest" by install-dotnet script. | ||||
|                 this.resolvedArgument.value = 'LTS'; | ||||
|             } | ||||
|             this.resolvedArgument.qualityFlag = | ||||
|                 parseInt(major) >= QUALITY_INPUT_MINIMAL_MAJOR_TAG ? true : false; | ||||
|         }); | ||||
|     } | ||||
|     createDotNetVersion() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             yield this.resolveVersionInput(); | ||||
| @ -296,17 +316,21 @@ class DotnetVersionResolver { | ||||
|             return this.resolvedArgument; | ||||
|         }); | ||||
|     } | ||||
|     getLatestVersion(httpClient, versionParts) { | ||||
|     getLatestByMajorTag(majorTag) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|                 allowRetries: true, | ||||
|                 maxRetries: 3 | ||||
|             }); | ||||
|             const response = yield httpClient.getJson(DotnetVersionResolver.DotNetCoreIndexUrl); | ||||
|             const result = response.result || {}; | ||||
|             let releasesInfo = result['releases-index']; | ||||
|             let releaseInfo = releasesInfo.find(info => { | ||||
|                 let sdkParts = info['channel-version'].split('.'); | ||||
|                 return sdkParts[0] === versionParts[0]; | ||||
|             const releasesInfo = result['releases-index']; | ||||
|             const releaseInfo = releasesInfo.find(info => { | ||||
|                 const sdkParts = info['channel-version'].split('.'); | ||||
|                 return sdkParts[0] === majorTag; | ||||
|             }); | ||||
|             if (!releaseInfo) { | ||||
|                 throw new Error(`Could not find info for version ${versionParts.join('.')} at ${DotnetVersionResolver.DotNetCoreIndexUrl}`); | ||||
|                 throw new Error(`Could not find info for version with major tag: "${majorTag}" at ${DotnetVersionResolver.DotNetCoreIndexUrl}`); | ||||
|             } | ||||
|             return releaseInfo['channel-version']; | ||||
|         }); | ||||
| @ -341,7 +365,7 @@ class DotnetCoreInstaller { | ||||
|             scriptArguments.push(option, this.quality); | ||||
|         } | ||||
|         else { | ||||
|             core.warning(`'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A and A.x formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.`); | ||||
|             core.warning(`The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.`); | ||||
|         } | ||||
|     } | ||||
|     installDotnet() { | ||||
| @ -398,22 +422,21 @@ class DotnetCoreInstaller { | ||||
|                 ignoreReturnCode: true, | ||||
|                 env: process.env | ||||
|             }; | ||||
|             const { exitCode, stdout } = yield exec.getExecOutput(`"${scriptPath}"`, scriptArguments, getExecOutputOptions); | ||||
|             const { exitCode, stdout, stderr } = yield exec.getExecOutput(`"${scriptPath}"`, scriptArguments, getExecOutputOptions); | ||||
|             if (exitCode) { | ||||
|                 throw new Error(`Failed to install dotnet ${exitCode}. ${stdout}`); | ||||
|                 throw new Error(`Failed to install dotnet, exit code: ${exitCode}. ${stderr}`); | ||||
|             } | ||||
|             return this.outputDotnetVersion(dotnetVersion.value); | ||||
|             return this.parseInstalledVersion(stdout); | ||||
|         }); | ||||
|     } | ||||
|     outputDotnetVersion(version) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const installationPath = process.env['DOTNET_INSTALL_DIR']; | ||||
|             let versionsOnRunner = yield (0, promises_1.readdir)(path_1.default.join(installationPath.replace(/'/g, ''), 'sdk')); | ||||
|             let installedVersion = semver_1.default.maxSatisfying(versionsOnRunner, version, { | ||||
|                 includePrerelease: true | ||||
|             }); | ||||
|             return installedVersion; | ||||
|         }); | ||||
|     parseInstalledVersion(stdout) { | ||||
|         const regex = /(?<version>\d+\.\d+\.\d+[a-z0-9._-]*)/gm; | ||||
|         const matchedResult = regex.exec(stdout); | ||||
|         if (!matchedResult) { | ||||
|             core.warning(`Failed to parse installed by the script version of .NET`); | ||||
|             return null; | ||||
|         } | ||||
|         return matchedResult.groups.version; | ||||
|     } | ||||
| } | ||||
| exports.DotnetCoreInstaller = DotnetCoreInstaller; | ||||
| @ -513,7 +536,7 @@ function run() { | ||||
|             const installedDotnetVersions = []; | ||||
|             const globalJsonFileInput = core.getInput('global-json-file'); | ||||
|             if (globalJsonFileInput) { | ||||
|                 const globalJsonPath = path_1.default.join(process.cwd(), globalJsonFileInput); | ||||
|                 const globalJsonPath = path_1.default.resolve(process.cwd(), globalJsonFileInput); | ||||
|                 if (!fs.existsSync(globalJsonPath)) { | ||||
|                     throw new Error(`The specified global.json file '${globalJsonFileInput}' does not exist`); | ||||
|                 } | ||||
| @ -527,13 +550,13 @@ function run() { | ||||
|                     versions.push(getVersionFromGlobalJson(globalJsonPath)); | ||||
|                 } | ||||
|                 else { | ||||
|                     core.info(`global.json wasn't found in the root directory. No .NET version will be installed.`); | ||||
|                     core.info(`The global.json wasn't found in the root directory. No .NET version will be installed.`); | ||||
|                 } | ||||
|             } | ||||
|             if (versions.length) { | ||||
|                 const quality = core.getInput('dotnet-quality'); | ||||
|                 if (quality && !qualityOptions.includes(quality)) { | ||||
|                     throw new Error(`${quality} is not a supported value for 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`); | ||||
|                     throw new Error(`Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`); | ||||
|                 } | ||||
|                 let dotnetInstaller; | ||||
|                 const uniqueVersions = new Set(versions); | ||||
| @ -549,13 +572,7 @@ function run() { | ||||
|             if (sourceUrl) { | ||||
|                 auth.configAuthentication(sourceUrl, configFile); | ||||
|             } | ||||
|             const comparisonRange = globalJsonFileInput | ||||
|                 ? versions[versions.length - 1] | ||||
|                 : '*'; | ||||
|             const versionToOutput = semver_1.default.maxSatisfying(installedDotnetVersions, comparisonRange, { | ||||
|                 includePrerelease: true | ||||
|             }); | ||||
|             core.setOutput('dotnet-version', versionToOutput); | ||||
|             outputInstalledVersion(installedDotnetVersions, globalJsonFileInput); | ||||
|             const matchersPath = path_1.default.join(__dirname, '..', '.github'); | ||||
|             core.info(`##[add-matcher]${path_1.default.join(matchersPath, 'csc.json')}`); | ||||
|         } | ||||
| @ -580,6 +597,25 @@ function getVersionFromGlobalJson(globalJsonPath) { | ||||
|     } | ||||
|     return version; | ||||
| } | ||||
| function outputInstalledVersion(installedVersions, globalJsonFileInput) { | ||||
|     if (!installedVersions.length) { | ||||
|         core.info(`The 'dotnet-version' output will not be set.`); | ||||
|         return; | ||||
|     } | ||||
|     if (installedVersions.includes(null)) { | ||||
|         core.warning(`Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.`); | ||||
|         return; | ||||
|     } | ||||
|     if (globalJsonFileInput) { | ||||
|         const versionToOutput = installedVersions.at(-1); // .NET SDK version parsed from the global.json file is installed last | ||||
|         core.setOutput('dotnet-version', versionToOutput); | ||||
|         return; | ||||
|     } | ||||
|     const versionToOutput = semver_1.default.maxSatisfying(installedVersions, '*', { | ||||
|         includePrerelease: true | ||||
|     }); | ||||
|     core.setOutput('dotnet-version', versionToOutput); | ||||
| } | ||||
| run(); | ||||
|  | ||||
|  | ||||
| @ -21084,14 +21120,6 @@ module.exports = require("fs"); | ||||
|  | ||||
| /***/ }), | ||||
|  | ||||
| /***/ 3292: | ||||
| /***/ ((module) => { | ||||
|  | ||||
| "use strict"; | ||||
| module.exports = require("fs/promises"); | ||||
|  | ||||
| /***/ }), | ||||
|  | ||||
| /***/ 3685: | ||||
| /***/ ((module) => { | ||||
|  | ||||
|  | ||||
| @ -61,12 +61,13 @@ Pull requests are the easiest way to contribute changes to git repos at GitHub. | ||||
|  | ||||
| - To implement new features or fix bugs, you need to make changes to the `.ts` files, which are located in the `src` folder | ||||
| - To comply with the code style, **you need to run the `format` script** | ||||
| - To lint the code, **you need to run the `lint:fix` script** | ||||
| - To transpile source code to `javascript` we use [NCC](https://github.com/vercel/ncc). **It is very important to run the `build` script after making changes**, otherwise your changes will not get into the final `javascript` build | ||||
|  | ||||
| **Learn more about how to implement tests:** | ||||
|  | ||||
| Adding or changing tests is an integral part of making a change to the code.  | ||||
| Unit tests are in the `__tests__` folder, and end-to-end tests are in the `workflows` folder, particularly in the [workflow.yml](https://github.com/actions/setup-dotnet/blob/main/.github/workflows/workflow.yml). | ||||
| Unit tests are in the `__tests__` folder, and end-to-end tests are in the `workflows` folder, particularly in the [e2e-tests.yml](https://github.com/actions/setup-dotnet/blob/main/.github/workflows/e2e-tests.yml). | ||||
|  | ||||
| - The contributor can add various types of tests (like unit tests or end-to-end tests), which, in his opinion, will be necessary and sufficient for testing new or changed functionality | ||||
| - Tests should cover a successful execution, as well as some edge cases and possible errors | ||||
|  | ||||
							
								
								
									
										459
									
								
								externals/install-dotnet.ps1
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										459
									
								
								externals/install-dotnet.ps1
									
									
									
									
										vendored
									
									
								
							| @ -9,6 +9,12 @@ | ||||
| .DESCRIPTION | ||||
|     Installs dotnet cli. If dotnet installation already exists in the given directory | ||||
|     it will update it only if the requested version differs from the one already installed. | ||||
|  | ||||
|     Note that the intended use of this script is for Continuous Integration (CI) scenarios, where: | ||||
|     - The SDK needs to be installed without user interaction and without admin rights. | ||||
|     - The SDK installation doesn't need to persist across multiple CI runs. | ||||
|     To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer. | ||||
|  | ||||
| .PARAMETER Channel | ||||
|     Default: LTS | ||||
|     Download from the Channel specified. Possible values: | ||||
| @ -164,6 +170,12 @@ function Say-Verbose($str) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function Measure-Action($name, $block) { | ||||
|     $time = Measure-Command $block | ||||
|     $totalSeconds = $time.TotalSeconds | ||||
|     Say-Verbose "⏱ Action '$name' took $totalSeconds seconds" | ||||
| } | ||||
|  | ||||
| function Say-Invocation($Invocation) { | ||||
|     $command = $Invocation.MyCommand; | ||||
|     $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ") | ||||
| @ -1104,10 +1116,10 @@ function Prepare-Install-Directory { | ||||
|     } | ||||
| } | ||||
|  | ||||
| Say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" | ||||
| Say "- The SDK needs to be installed without user interaction and without admin rights." | ||||
| Say "- The SDK installation doesn't need to persist across multiple CI runs." | ||||
| Say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n" | ||||
| Say-Verbose "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" | ||||
| Say-Verbose "- The SDK needs to be installed without user interaction and without admin rights." | ||||
| Say-Verbose "- The SDK installation doesn't need to persist across multiple CI runs." | ||||
| Say-Verbose "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n" | ||||
|  | ||||
| if ($SharedRuntime -and (-not $Runtime)) { | ||||
|     $Runtime = "dotnet" | ||||
| @ -1115,14 +1127,16 @@ if ($SharedRuntime -and (-not $Runtime)) { | ||||
|  | ||||
| $OverrideNonVersionedFiles = !$SkipNonVersionedFiles | ||||
|  | ||||
| $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture | ||||
| $NormalizedQuality = Get-NormalizedQuality $Quality | ||||
| Say-Verbose "Normalized quality: '$NormalizedQuality'" | ||||
| $NormalizedChannel = Get-NormalizedChannel $Channel | ||||
| Say-Verbose "Normalized channel: '$NormalizedChannel'" | ||||
| $NormalizedProduct = Get-NormalizedProduct $Runtime | ||||
| Say-Verbose "Normalized product: '$NormalizedProduct'" | ||||
| $FeedCredential = ValidateFeedCredential $FeedCredential | ||||
| Measure-Action "Product discovery" { | ||||
|     $script:CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture | ||||
|     $script:NormalizedQuality = Get-NormalizedQuality $Quality | ||||
|     Say-Verbose "Normalized quality: '$NormalizedQuality'" | ||||
|     $script:NormalizedChannel = Get-NormalizedChannel $Channel | ||||
|     Say-Verbose "Normalized channel: '$NormalizedChannel'" | ||||
|     $script:NormalizedProduct = Get-NormalizedProduct $Runtime | ||||
|     Say-Verbose "Normalized product: '$NormalizedProduct'" | ||||
|     $script:FeedCredential = ValidateFeedCredential $FeedCredential | ||||
| } | ||||
|  | ||||
| $InstallRoot = Resolve-Installation-Path $InstallDir | ||||
| Say-Verbose "InstallRoot: $InstallRoot" | ||||
| @ -1200,7 +1214,7 @@ if ($DryRun) { | ||||
|     return | ||||
| } | ||||
|  | ||||
| Prepare-Install-Directory | ||||
| Measure-Action "Installation directory preparation" { Prepare-Install-Directory } | ||||
|  | ||||
| $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) | ||||
| Say-Verbose "Zip path: $ZipPath" | ||||
| @ -1214,7 +1228,7 @@ foreach ($link in $DownloadLinks) | ||||
|     Say-Verbose "Downloading `"$($link.type)`" link $($link.downloadLink)" | ||||
|  | ||||
|     try { | ||||
|         DownloadFile -Source $link.downloadLink -OutPath $ZipPath | ||||
|         Measure-Action "Package download" { DownloadFile -Source $link.downloadLink -OutPath $ZipPath } | ||||
|         Say-Verbose "Download succeeded." | ||||
|         $DownloadSucceeded = $true | ||||
|         $DownloadedLink = $link | ||||
| @ -1251,7 +1265,7 @@ if (-not $DownloadSucceeded) { | ||||
| } | ||||
|  | ||||
| Say "Extracting the archive." | ||||
| Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot | ||||
| Measure-Action "Package extraction" { Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot } | ||||
|  | ||||
| #  Check if the SDK version is installed; if not, fail the installation. | ||||
| $isAssetInstalled = $false | ||||
| @ -1277,225 +1291,224 @@ if (!$isAssetInstalled) { | ||||
|  | ||||
| SafeRemoveFile -Path $ZipPath | ||||
|  | ||||
| Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot | ||||
| Measure-Action "Setting up shell environment" { Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot } | ||||
|  | ||||
| Say "Note that the script does not resolve dependencies during installation." | ||||
| Say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install/windows#dependencies" | ||||
| Say "Installed version is $($DownloadedLink.effectiveVersion)" | ||||
| Say "Installation finished" | ||||
|  | ||||
| # SIG # Begin signature block | ||||
| # MIInzgYJKoZIhvcNAQcCoIInvzCCJ7sCAQExDzANBglghkgBZQMEAgEFADB5Bgor | ||||
| # MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor | ||||
| # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG | ||||
| # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB7pzZ0nuEMd30h | ||||
| # n1EcAYUQN+1clltqaLf9611TDrw/laCCDYUwggYDMIID66ADAgECAhMzAAACzfNk | ||||
| # v/jUTF1RAAAAAALNMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD | ||||
| # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBhfTi3SRn7+vyy | ||||
| # uCXKPjhiawegWZ493EcaOEycbgkZcKCCDXYwggX0MIID3KADAgECAhMzAAACy7d1 | ||||
| # OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD | ||||
| # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy | ||||
| # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p | ||||
| # bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAyWhcNMjMwNTExMjA0NjAyWjB0MQsw | ||||
| # bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw | ||||
| # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u | ||||
| # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy | ||||
| # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB | ||||
| # AQDrIzsY62MmKrzergm7Ucnu+DuSHdgzRZVCIGi9CalFrhwtiK+3FIDzlOYbs/zz | ||||
| # HwuLC3hir55wVgHoaC4liQwQ60wVyR17EZPa4BQ28C5ARlxqftdp3H8RrXWbVyvQ | ||||
| # aUnBQVZM73XDyGV1oUPZGHGWtgdqtBUd60VjnFPICSf8pnFiit6hvSxH5IVWI0iO | ||||
| # nfqdXYoPWUtVUMmVqW1yBX0NtbQlSHIU6hlPvo9/uqKvkjFUFA2LbC9AWQbJmH+1 | ||||
| # uM0l4nDSKfCqccvdI5l3zjEk9yUSUmh1IQhDFn+5SL2JmnCF0jZEZ4f5HE7ykDP+ | ||||
| # oiA3Q+fhKCseg+0aEHi+DRPZAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE | ||||
| # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU0WymH4CP7s1+yQktEwbcLQuR9Zww | ||||
| # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh | ||||
| # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ3MDUzMDAfBgNVHSMEGDAW | ||||
| # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v | ||||
| # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw | ||||
| # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov | ||||
| # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx | ||||
| # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB | ||||
| # AE7LSuuNObCBWYuttxJAgilXJ92GpyV/fTiyXHZ/9LbzXs/MfKnPwRydlmA2ak0r | ||||
| # GWLDFh89zAWHFI8t9JLwpd/VRoVE3+WyzTIskdbBnHbf1yjo/+0tpHlnroFJdcDS | ||||
| # MIsH+T7z3ClY+6WnjSTetpg1Y/pLOLXZpZjYeXQiFwo9G5lzUcSd8YVQNPQAGICl | ||||
| # 2JRSaCNlzAdIFCF5PNKoXbJtEqDcPZ8oDrM9KdO7TqUE5VqeBe6DggY1sZYnQD+/ | ||||
| # LWlz5D0wCriNgGQ/TWWexMwwnEqlIwfkIcNFxo0QND/6Ya9DTAUykk2SKGSPt0kL | ||||
| # tHxNEn2GJvcNtfohVY/b0tuyF05eXE3cdtYZbeGoU1xQixPZAlTdtLmeFNly82uB | ||||
| # VbybAZ4Ut18F//UrugVQ9UUdK1uYmc+2SdRQQCccKwXGOuYgZ1ULW2u5PyfWxzo4 | ||||
| # BR++53OB/tZXQpz4OkgBZeqs9YaYLFfKRlQHVtmQghFHzB5v/WFonxDVlvPxy2go | ||||
| # a0u9Z+ZlIpvooZRvm6OtXxdAjMBcWBAsnBRr/Oj5s356EDdf2l/sLwLFYE61t+ME | ||||
| # iNYdy0pXL6gN3DxTVf2qjJxXFkFfjjTisndudHsguEMk8mEtnvwo9fOSKT6oRHhM | ||||
| # 9sZ4HTg/TTMjUljmN3mBYWAWI5ExdC1inuog0xrKmOWVMIIHejCCBWKgAwIBAgIK | ||||
| # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV | ||||
| # AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA | ||||
| # wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4 | ||||
| # 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5 | ||||
| # RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN | ||||
| # lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X | ||||
| # a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE | ||||
| # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w | ||||
| # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW | ||||
| # MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci | ||||
| # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j | ||||
| # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG | ||||
| # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu | ||||
| # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 | ||||
| # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ | ||||
| # ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf | ||||
| # zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh | ||||
| # 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4 | ||||
| # EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j | ||||
| # 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck | ||||
| # 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd | ||||
| # jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N | ||||
| # mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1 | ||||
| # pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB | ||||
| # fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To | ||||
| # /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq | ||||
| # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x | ||||
| # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv | ||||
| # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 | ||||
| # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG | ||||
| # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG | ||||
| # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg | ||||
| # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC | ||||
| # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 | ||||
| # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr | ||||
| # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg | ||||
| # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy | ||||
| # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 | ||||
| # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh | ||||
| # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k | ||||
| # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB | ||||
| # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn | ||||
| # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 | ||||
| # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w | ||||
| # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o | ||||
| # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD | ||||
| # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa | ||||
| # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny | ||||
| # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG | ||||
| # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t | ||||
| # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV | ||||
| # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 | ||||
| # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG | ||||
| # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl | ||||
| # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb | ||||
| # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l | ||||
| # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 | ||||
| # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 | ||||
| # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 | ||||
| # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam | ||||
| # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa | ||||
| # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah | ||||
| # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA | ||||
| # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt | ||||
| # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr | ||||
| # /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw | ||||
| # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN | ||||
| # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp | ||||
| # Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB | ||||
| # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO | ||||
| # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFmuaTXYQ37AFvsEol24fdW+ | ||||
| # nRqHcc1fr+VQVdqhXc/vMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A | ||||
| # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB | ||||
| # BQAEggEAjY5XW5Ly7TJ1OTbeIR98xU+2dmtw7L71ws+ICnQCGhj2xJDUK+5yrTfO | ||||
| # 8C98l/P4ynFi33Dl8z2YElqUCuqEXbiCzz06lIL4NuibC5DV/X80ZmICR/NYd2v1 | ||||
| # ww7IH+7dpsHAowBBindCYpVwQ3Ea3kDWgsjPAinAysFFushSOnNWFvrF6vi2smrs | ||||
| # smbrAAhEhSfLd1Pxxdw73hQ0YjM/D3F3opaybMQ0blpHhOaqtbiyYzvk0doIzBEc | ||||
| # trSH4NDIc3yLNj5VbjSczpexE+hyQNY4xCtwco4bVtXhONUihv08AIKR8+sIaI7A | ||||
| # mM/SWrrwGYSSSxydKqDei7biKG4jDqGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC | ||||
| # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq | ||||
| # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl | ||||
| # AwQCAQUABCB6Hzt2gUb/WZK8fvVnOocriE4rYr6mscZi3gZnBCpiigIGZBr2iMZU | ||||
| # GBMyMDIzMDMzMTE1MjEwNi41MTZaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV | ||||
| # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE | ||||
| # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl | ||||
| # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO | ||||
| # OjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT | ||||
| # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAGybkADf26plJIAAQAAAbIwDQYJ | ||||
| # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x | ||||
| # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv | ||||
| # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw | ||||
| # OTIwMjAyMjAxWhcNMjMxMjE0MjAyMjAxWjCB0jELMAkGA1UEBhMCVVMxEzARBgNV | ||||
| # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv | ||||
| # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm | ||||
| # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw | ||||
| # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE | ||||
| # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD | ||||
| # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG | ||||
| # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la | ||||
| # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc | ||||
| # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D | ||||
| # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ | ||||
| # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk | ||||
| # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 | ||||
| # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd | ||||
| # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL | ||||
| # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd | ||||
| # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 | ||||
| # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS | ||||
| # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI | ||||
| # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL | ||||
| # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD | ||||
| # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv | ||||
| # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf | ||||
| # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 | ||||
| # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf | ||||
| # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF | ||||
| # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h | ||||
| # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA | ||||
| # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn | ||||
| # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 | ||||
| # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b | ||||
| # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ | ||||
| # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy | ||||
| # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp | ||||
| # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi | ||||
| # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb | ||||
| # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS | ||||
| # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL | ||||
| # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX | ||||
| # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4x | ||||
| # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt | ||||
| # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p | ||||
| # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAALN82S/+NRMXVEAAAAA | ||||
| # As0wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw | ||||
| # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINK7 | ||||
| # cJe0KVfbcXchjID30U/cUg7pWAQUa3+n8JuhjLCLMEIGCisGAQQBgjcCAQwxNDAy | ||||
| # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j | ||||
| # b20wDQYJKoZIhvcNAQEBBQAEggEAODLxcflOtjpIXXIhbYyQ0wFeBx0NrmoMU/Ri | ||||
| # e7CRrAieAbG4iLJzs4DhUov5iuMHY6AAbLWAH54QlSkd4XNp6POsE7lSzN9yjlVw | ||||
| # V/e0XCaYeXIbtd75hGd5P7wAhM4m2ViDI4IRHyQtjysW0U0F6YiqNlFm7Fzo5Si6 | ||||
| # l2XxvuEDSdyJcEN70wHQajx6bKfnI/oMY59iGjDXvDP/6cQV9NI0gPHFTwPKA7vg | ||||
| # PySyVFEG7dQMoEwAWy9GHbcS//RulgUwBhWcrtUP411XLSO6is2VTknwbdglc9HZ | ||||
| # zViuS4C1ujHlPrlMzm8Y5iGVIQCna5w2NU/kGsSK5+dMkovomKGCFykwghclBgor | ||||
| # BgEEAYI3AwMBMYIXFTCCFxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZI | ||||
| # AWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGE | ||||
| # WQoDATAxMA0GCWCGSAFlAwQCAQUABCDRz6ce9oWlH6+o0BtjmAjtvEMN1hfhIA5v | ||||
| # +wTZHvB4XgIGY2PeyIloGBMyMDIyMTExMDE1MDUxNi43MzRaMASAAgH0oIHYpIHV | ||||
| # MIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH | ||||
| # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL | ||||
| # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsT | ||||
| # HVRoYWxlcyBUU1MgRVNOOkEyNDAtNEI4Mi0xMzBFMSUwIwYDVQQDExxNaWNyb3Nv | ||||
| # ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAG4CNTB | ||||
| # uHngUUkAAQAAAbgwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV | ||||
| # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv | ||||
| # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg | ||||
| # UENBIDIwMTAwHhcNMjIwOTIwMjAyMjE2WhcNMjMxMjE0MjAyMjE2WjCB0jELMAkG | ||||
| # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx | ||||
| # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z | ||||
| # b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg | ||||
| # VFNTIEVTTjpBMjQwLTRCODItMTMwRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt | ||||
| # U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJwb | ||||
| # sfwRHERn5C95QPGn37tJ5vOiY9aWjeIDxpgaXaYGiqsw0G0cvCK3YulrqemEf2Ck | ||||
| # GSdcOJAF++EqhOSqrO13nGcjqw6hFNnsGwKANyzddwnOO0jz1lfBIIu77TbfNvna | ||||
| # WbwSRu0DTGHA7n7PR0MYJ9bC/HopStpbFf606LKcTWnwaUuEdAhx6FAqg1rkgugi | ||||
| # uuaaxKyxRkdjFZLKFXEXL9p01PtwS0fG6vZiRVnEKgeal2TeLvdAIqapBwltPYif | ||||
| # gqnp7Z4VJMcPo0TWmRNVFOcHRNwWHehN9xg6ugIGXPo7hMpWrPgg4moHO2epc0T3 | ||||
| # 6rgm9hlDrl28bG5TakmV7NJ98kbF5lgtlrowT6ecwEVtuLd4a0gzYqhanW7zaFZn | ||||
| # Dft5yMexy59ifETdzpwArj2nJAyIsiq1PY3XPm2mUMLlACksqelHKfWihK/Fehw/ | ||||
| # mziovBVwkkr/G0F19OWgR+MBUKifwpOyQiLAxrqvVnfCY4QjJCZiHIuS15HCQ/TI | ||||
| # t/Qj4x1WvRa1UqjnmpLu4/yBYWZsdvZoq8SXI7iOs7muecAJeEkYlM6iOkMighzE | ||||
| # hjQK9ThPpoAtluXbL7qIHGrfFlHmX/4soc7jj1j8uB31U34gJlB2XphjMaT+E+O9 | ||||
| # SImk/6GRV9Sm8C88Fnmm2VdwMluCNAUzPFjfvHx3AgMBAAGjggFJMIIBRTAdBgNV | ||||
| # HQ4EFgQUxP1HJTeFwzNYo1njfucXuUfQaW4wHwYDVR0jBBgwFoAUn6cVXQBeYl2D | ||||
| # 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv | ||||
| # ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy | ||||
| # MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov | ||||
| # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l | ||||
| # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB | ||||
| # Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQAD | ||||
| # ggIBAJ9uk8miwpMoKw3D996piEzbegAGxkABHYn2vP2hbqnkS9U97s/6QlyZOhGF | ||||
| # sVudaiLeRZZTsaG5hR0oCuBINZ/lelo5xzHc+mBOpBXpxSaW1hqoxaCLsVH1EBtz | ||||
| # 7in25Hjy+ejuBcilH6EZ0ZtNxmWGIQz8R0AuS0Tj4VgJXHIlXP9dVOiyGo9Velrk | ||||
| # +FGx/BC+iEuCaKd/IsypHPiCUCh52DGc91s2S7ldQx1H4CljOAtanDfbvSejASWL | ||||
| # o/s3w0XMAbDurWNns0XidAF2RnL1PaxoOyz9VYakNGK4F3/uJRZnVgbsCYuwNX1B | ||||
| # mSwM1ZbPSnggNSGTZx/FQ20Jj/ulrK0ryAbvNbNb4kkaS4a767ifCqvUOFLlUT8P | ||||
| # N43hhldxI6yHPMOWItJpEHIZBiTNKblBsYbIrghb1Ym9tfSsLa5ZJDzVZNndRfhU | ||||
| # qJOyXF+CVm9OtVmFDG9kIwM6QAX8Q0if721z4VOzZNvD8ktg1lI+XjXgXDJVs3h4 | ||||
| # 7sMu9GXSYzky+7dtgmc3iRPkda3YVRdmPJtNFN0NLybcssE7vhFCij75eDGQBFq0 | ||||
| # A4KVG6uBdr6UTWwE0VKHxBz2BpGvn7BCs+5yxnF+HV6CUickDqqPi/II7Zssd9Eb | ||||
| # P9uzj4luldXDAPrWGtdGq+wK0odlGNVuCMxsL3hn8+KiO9UiMIIHcTCCBVmgAwIB | ||||
| # AgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE | ||||
| # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc | ||||
| # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0 | ||||
| # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1 | ||||
| # WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu | ||||
| # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv | ||||
| # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC | ||||
| # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O | ||||
| # 1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZn | ||||
| # hUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t | ||||
| # 1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxq | ||||
| # D89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmP | ||||
| # frVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSW | ||||
| # rAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv | ||||
| # 231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zb | ||||
| # r17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYcten | ||||
| # IPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQc | ||||
| # xWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17a | ||||
| # j54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQAB | ||||
| # MCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQU | ||||
| # n6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEw | ||||
| # QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E | ||||
| # b2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQB | ||||
| # gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/ | ||||
| # MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ | ||||
| # oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p | ||||
| # Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB | ||||
| # BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v | ||||
| # Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3h | ||||
| # LB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x | ||||
| # 5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74p | ||||
| # y27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1A | ||||
| # oL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbC | ||||
| # HcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB | ||||
| # 9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNt | ||||
| # yo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3 | ||||
| # rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcV | ||||
| # v7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A24 | ||||
| # 5oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lw | ||||
| # Y1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB | ||||
| # 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl | ||||
| # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk | ||||
| # TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U | ||||
| # aGFsZXMgVFNTIEVTTjpBMjQwLTRCODItMTMwRTElMCMGA1UEAxMcTWljcm9zb2Z0 | ||||
| # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAcGteVqFx/IbTKXHL | ||||
| # euXCPRPMD7uggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu | ||||
| # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv | ||||
| # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN | ||||
| # BgkqhkiG9w0BAQUFAAIFAOcW7qowIhgPMjAyMjExMTAxMTI5NDZaGA8yMDIyMTEx | ||||
| # MTExMjk0NlowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA5xbuqgIBADAHAgEAAgIE | ||||
| # qTAHAgEAAgIRVjAKAgUA5xhAKgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE | ||||
| # AYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GB | ||||
| # AGsU2HTQg158bHX+QngoY7NVfCbGRaLjQOi8geKi26qQWAxll9QLFg4+epiG2nZB | ||||
| # eQvhxeNmIzounhWfJ+gfhFMi8aBT5z4dLK9iBtmpG1Y14RmSS4andiUlS6bVNVNe | ||||
| # WGObqHijMVeMOphiTaAfzR6zSASDaG0CfVm9bNBOnZZsMYIEDTCCBAkCAQEwgZMw | ||||
| # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl | ||||
| # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd | ||||
| # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAG4CNTBuHngUUkAAQAA | ||||
| # AbgwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRAB | ||||
| # BDAvBgkqhkiG9w0BCQQxIgQg578XwPrBwneU95xu1sHFncHeCC0UPQ7QK7PvSSby | ||||
| # VpwwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCAo69Y4oHA7Q4pS+Y1NsBfr | ||||
| # pIYTeWsPeGTami0X0PD7HzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI | ||||
| # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv | ||||
| # ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD | ||||
| # QSAyMDEwAhMzAAABuAjUwbh54FFJAAEAAAG4MCIEIJVlMK4mQfdJaAZx7Unfka/I | ||||
| # D4Wbw5edh/SR7TTptzRqMA0GCSqGSIb3DQEBCwUABIICAA1QlR3ywR7e+jqZ++NC | ||||
| # xsIwREiwVS70CEkbH8XpPRS0mFS0SHcCfpwymGfdep3D0CWk0PIfMhXq0SD97iBI | ||||
| # rOLdHglVBkMYTjGEBHyBzv/LevAZUuzoc5aqyIF4Ywa5KS4PGbMSuRK5CKAojOzH | ||||
| # A/vp2/KYuADmf9kOOgOfDVicyfoqZ+3W+QaUI/k0KKV4dPLF55+y18C+Td6sR60Y | ||||
| # AkcvGZObuj/OkREhdTJ1qJ2E/4RKG8gtGY1DfluLon7+UvS/ciWDWrJnHMmkxM11 | ||||
| # cYuRIvLArIdq/YS2bcSnY6AVO2zYjj7gCqDN9GuCurstUKC5uxVl3VNxntC0u3Le | ||||
| # BoI/R5uMYlTXodW8ukLNL6zHrQ4wI4udgW77KJref+3E1PEpZBRMxwose7Vt8lDc | ||||
| # sW1vdM+eZzUXRLhDR8a0Nai7+PaNoukoGf4pvwsu8Mkeji5a0hWtU9lUVRv6nzue | ||||
| # 3L2olhsbiHhAET7N6Rj0kzEhbUgfVUJrGvNlWOfN7MDr+OpArGXMPLtovbKTLtXF | ||||
| # v/GrJo9wQuyqUmY6KQSRDZgOw1CcoZpJcy40HG/aOlJwk03N13OZD5H3KfHwEphR | ||||
| # YnbGwGq9zUId5druSr5s40Yyl3idAkqmI5SXAm9v/gRq2X9vMU0a7KqXet9wO62F | ||||
| # TqxV+7Qp48Vw6hW1g+Q7oWoc | ||||
| # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl | ||||
| # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowODQyLTRC | ||||
| # RTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC | ||||
| # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqiZTIde/lQ4rC+Bml5f/Wu | ||||
| # q/xKTxrfbG23HofmQ+qZAN4GyO73PF3y9OAfpt7Qf2jcldWOGUB+HzBuwllYyP3f | ||||
| # x4MY8zvuAuB37FvoytnNC2DKnVrVlHOVcGUL9CnmhDNMA2/nskjIf2IoiG9J0qLY | ||||
| # r8duvHdQJ9Li2Pq9guySb9mvUL60ogslCO9gkh6FiEDwMrwUr8Wja6jFpUTny8tg | ||||
| # 0N0cnCN2w4fKkp5qZcbUYFYicLSb/6A7pHCtX6xnjqwhmJoib3vkKJyVxbuFLRhV | ||||
| # XxH95b0LHeNhifn3jvo2j+/4QV10jEpXVW+iC9BsTtR69xvTjU51ZgP7BR4YDEWq | ||||
| # 7JsylSOv5B5THTDXRf184URzFhTyb8OZQKY7mqMh7c8J8w1sEM4XDUF2UZNy829N | ||||
| # VCzG2tfdEXZaHxF8RmxpQYBxyhZwY1rotuIS+gfN2eq+hkAT3ipGn8/KmDwDtzAb | ||||
| # nfuXjApgeZqwgcYJ8pDJ+y/xU6ouzJz1Bve5TTihkiA7wQsQe6R60Zk9dPdNzw0M | ||||
| # K5niRzuQZAt4GI96FhjhlUWcUZOCkv/JXM/OGu/rgSplYwdmPLzzfDtXyuy/GCU5 | ||||
| # I4l08g6iifXypMgoYkkceOAAz4vx1x0BOnZWfI3fSwqNUvoN7ncTT+MB4Vpvf1QB | ||||
| # ppjBAQUuvui6eCG0MCVNAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUmfIngFzZEZlP | ||||
| # kjDOVluBSDDaanEwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD | ||||
| # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j | ||||
| # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG | ||||
| # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu | ||||
| # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw | ||||
| # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD | ||||
| # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBANxHtu3FzIabaDbW | ||||
| # qswdKBlAhKXRCN+5CSMiv2TYa4i2QuWIm+99piwAhDhADfbqor1zyLi95Y6GQnvI | ||||
| # WUgdeC7oL1ZtZye92zYK+EIfwYZmhS+CH4infAzUvscHZF3wlrJUfPUIDGVP0lCY | ||||
| # Vse9mguvG0dqkY4ayQPEHOvJubgZZaOdg/N8dInd6fGeOc+0DoGzB+LieObJ2Q0A | ||||
| # tEt3XN3iX8Cp6+dZTX8xwE/LvhRwPpb/+nKshO7TVuvenwdTwqB/LT6CNPaElwFe | ||||
| # KxKrqRTPMbHeg+i+KnBLfwmhEXsMg2s1QX7JIxfvT96md0eiMjiMEO22LbOzmLMN | ||||
| # d3LINowAnRBAJtX+3/e390B9sMGMHp+a1V+hgs62AopBl0p/00li30DN5wEQ5If3 | ||||
| # 5Zk7b/T6pEx6rJUDYCti7zCbikjKTanBnOc99zGMlej5X+fC/k5ExUCrOs3/VzGR | ||||
| # CZt5LvVQSdWqq/QMzTEmim4sbzASK9imEkjNtZZyvC1CsUcD1voFktld4mKMjE+u | ||||
| # DEV3IddD+DrRk94nVzNPSuZXewfVOnXHSeqG7xM3V7fl2aL4v1OhL2+JwO1Tx3B0 | ||||
| # irO1O9qbNdJk355bntd1RSVKgM22KFBHnoL7Js7pRhBiaKmVTQGoOb+j1Qa7q+ci | ||||
| # xGo48Vh9k35BDsJS/DLoXFSPDl4mMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ | ||||
| # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT | ||||
| # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m | ||||
| # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh | ||||
| # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 | ||||
| # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH | ||||
| # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD | ||||
| # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB | ||||
| # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK | ||||
| # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg | ||||
| # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp | ||||
| # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d | ||||
| # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 | ||||
| # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR | ||||
| # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu | ||||
| # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO | ||||
| # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb | ||||
| # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 | ||||
| # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t | ||||
| # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW | ||||
| # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb | ||||
| # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz | ||||
| # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku | ||||
| # aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA | ||||
| # QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2 | ||||
| # VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu | ||||
| # bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw | ||||
| # LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93 | ||||
| # d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt | ||||
| # MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q | ||||
| # XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6 | ||||
| # U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt | ||||
| # I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis | ||||
| # 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp | ||||
| # kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0 | ||||
| # sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e | ||||
| # W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ | ||||
| # sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7 | ||||
| # Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0 | ||||
| # dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ | ||||
| # tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx | ||||
| # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT | ||||
| # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh | ||||
| # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow | ||||
| # ODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy | ||||
| # dmljZaIjCgEBMAcGBSsOAwIaAxUAjhJ+EeySRfn2KCNsjn9cF9AUSTqggYMwgYCk | ||||
| # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH | ||||
| # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD | ||||
| # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF | ||||
| # AOfRUdUwIhgPMjAyMzAzMzEyMDM0MjlaGA8yMDIzMDQwMTIwMzQyOVowdDA6Bgor | ||||
| # BgEEAYRZCgQBMSwwKjAKAgUA59FR1QIBADAHAgEAAgIKJDAHAgEAAgIRLzAKAgUA | ||||
| # 59KjVQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID | ||||
| # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJlOESCa/uRR1x6GunE8 | ||||
| # K/WgHWTpSE31EITDOfTMvDcF4ptngCS5aOc4gfzmhNNehWfP6EOrgoSQzJYZ4YCh | ||||
| # fYbHNMk56f18sq8t7y2hgR7KixcEo/4HVzeSdaOclHNc4Gn7kCGpMvpT3Xz9Lzc7 | ||||
| # UKWDZ0zkNKnbS8TZLNueVQwfMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx | ||||
| # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT | ||||
| # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt | ||||
| # U3RhbXAgUENBIDIwMTACEzMAAAGybkADf26plJIAAQAAAbIwDQYJYIZIAWUDBAIB | ||||
| # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx | ||||
| # IgQgXhJRuHCXk3arJvifIY3DBe9Ce9EmlP1y6U4XkgL31DkwgfoGCyqGSIb3DQEJ | ||||
| # EAIvMYHqMIHnMIHkMIG9BCBTeM485+E+t4PEVieUoFKX7PVyLo/nzu+htJPCG04+ | ||||
| # NTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw | ||||
| # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x | ||||
| # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABsm5A | ||||
| # A39uqZSSAAEAAAGyMCIEIGGWlnNnYHrB5HguWG0/nJd/WvSrCogze+QCpenu3IM5 | ||||
| # MA0GCSqGSIb3DQEBCwUABIICADVOLTuNxeEnBOfZpb7Nv4uf91W/Ho5i99zenDSJ | ||||
| # x5QHVs+bKXmgc3a7/SSsliAT3zygHc7cH4zARbCZePLTivByKmeG08Ka35eyR+FK | ||||
| # awSNrI/X+eVIC6nw/egCwviBC1NAG8jHGkuScbHeiiGajvS6lp3ORML7UexMuE4w | ||||
| # 9SEumoghljCLZMwCSvw+3WxhQoBEZroR8u+PID2RdD0vi85FjKPWcZZijVLqHeFi | ||||
| # TnuFqwRCLTV0MV+dDCbjwXneIqV+AVlnqb9iDMr3ZhISlRcy9XJNpY5vQBj/wqUW | ||||
| # vefrmpdz0LNkdtXYThPkyl3mha2KsoQi5SA9zSjlAjFgY3ppmXvi3Frbfqk+iL+f | ||||
| # l/Qc4+B71jG4t28lTWKteJiHqo+6AUXK2rlAl0d74yvhO6N8lMMtXhdJc8JABYn1 | ||||
| # v2/KKZn5RvPFF8QP7Ac1saIe1+gUFNcsYOLaMm/xl8E6kefWwZnm5Rhm606g1AC/ | ||||
| # N5Wo08aAs0ymTPH91dEbmOURXLbA3vCyG7kbfgnhCs/j7oQHWaFDzEYuXDIA4ICT | ||||
| # dxPUTltbq3OWdp0PAS8JSEKPQFaOoQEnPa4adrXWxMvOmel8IGqJiQ+BPOaLQG64 | ||||
| # Qu2tMkH/5szb1fsEnCe8SJmy5ESF+kmpnLBtJ17Y9o+9nJHF5ddFmvzy+LUaIqDN | ||||
| # cOfH | ||||
| # SIG # End signature block | ||||
|  | ||||
							
								
								
									
										14
									
								
								externals/install-dotnet.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								externals/install-dotnet.sh
									
									
									
									
										vendored
									
									
								
							| @ -1617,6 +1617,10 @@ do | ||||
|             echo "       $script_name -h|-?|--help" | ||||
|             echo "" | ||||
|             echo "$script_name is a simple command line interface for obtaining dotnet cli." | ||||
|             echo "    Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" | ||||
|             echo "    - The SDK needs to be installed without user interaction and without admin rights." | ||||
|             echo "    - The SDK installation doesn't need to persist across multiple CI runs." | ||||
|             echo "    To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer." | ||||
|             echo "" | ||||
|             echo "Options:" | ||||
|             echo "  -c,--channel <CHANNEL>         Download from the channel specified, Defaults to \`$channel\`." | ||||
| @ -1694,10 +1698,10 @@ do | ||||
|     shift | ||||
| done | ||||
|  | ||||
| say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" | ||||
| say "- The SDK needs to be installed without user interaction and without admin rights." | ||||
| say "- The SDK installation doesn't need to persist across multiple CI runs." | ||||
| say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n" | ||||
| say_verbose "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" | ||||
| say_verbose "- The SDK needs to be installed without user interaction and without admin rights." | ||||
| say_verbose "- The SDK installation doesn't need to persist across multiple CI runs." | ||||
| say_verbose "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n" | ||||
|  | ||||
| if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then | ||||
|     message="Provide credentials via --feed-credential parameter." | ||||
| @ -1731,4 +1735,4 @@ fi | ||||
|  | ||||
| say "Note that the script does not resolve dependencies during installation." | ||||
| say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section." | ||||
| say "Installation finished successfully." | ||||
| say "Installation finished successfully." | ||||
							
								
								
									
										2157
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2157
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @ -6,9 +6,10 @@ | ||||
|   "main": "lib/setup-dotnet.js", | ||||
|   "scripts": { | ||||
|     "build": "tsc && ncc build", | ||||
|     "format": "prettier --write **/*.ts", | ||||
|     "format-check": "prettier --check **/*.ts", | ||||
|     "lint": "echo \"Fake command that does nothing. It is used in reusable workflows\"", | ||||
|     "format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"", | ||||
|     "format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"", | ||||
|     "lint": "eslint --config ./.eslintrc.js \"**/*.ts\"", | ||||
|     "lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix", | ||||
|     "prepare": "husky install", | ||||
|     "test": "jest --coverage --config ./jest.config.js", | ||||
|     "update-installers": "nwget https://dot.net/v1/dotnet-install.ps1 -O externals/install-dotnet.ps1 && nwget https://dot.net/v1/dotnet-install.sh -O externals/install-dotnet.sh" | ||||
| @ -37,11 +38,16 @@ | ||||
|     "@types/jest": "^27.0.2", | ||||
|     "@types/node": "^16.11.25", | ||||
|     "@types/semver": "^6.2.2", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.54.0", | ||||
|     "@typescript-eslint/parser": "^5.54.0", | ||||
|     "@vercel/ncc": "^0.33.4", | ||||
|     "eslint": "^8.35.0", | ||||
|     "eslint-config-prettier": "^8.6.0", | ||||
|     "eslint-plugin-jest": "^27.2.1", | ||||
|     "husky": "^8.0.1", | ||||
|     "jest": "^27.2.5", | ||||
|     "jest-circus": "^27.2.5", | ||||
|     "prettier": "^2.7.1", | ||||
|     "prettier": "^2.8.4", | ||||
|     "ts-jest": "^27.0.5", | ||||
|     "typescript": "^4.8.4", | ||||
|     "wget-improved": "^3.2.1" | ||||
|  | ||||
							
								
								
									
										392
									
								
								src/authutil.ts
									
									
									
									
									
								
							
							
						
						
									
										392
									
								
								src/authutil.ts
									
									
									
									
									
								
							| @ -1,196 +1,196 @@ | ||||
| import * as fs from 'fs'; | ||||
| import * as path from 'path'; | ||||
| import * as core from '@actions/core'; | ||||
| import * as github from '@actions/github'; | ||||
| import {XMLParser, XMLBuilder} from 'fast-xml-parser'; | ||||
|  | ||||
| export function configAuthentication( | ||||
|   feedUrl: string, | ||||
|   existingFileLocation: string = '', | ||||
|   processRoot: string = process.cwd() | ||||
| ) { | ||||
|   const existingNuGetConfig: string = path.resolve( | ||||
|     processRoot, | ||||
|     existingFileLocation === '' | ||||
|       ? getExistingNugetConfig(processRoot) | ||||
|       : existingFileLocation | ||||
|   ); | ||||
|  | ||||
|   const tempNuGetConfig: string = path.resolve( | ||||
|     processRoot, | ||||
|     '../', | ||||
|     'nuget.config' | ||||
|   ); | ||||
|  | ||||
|   writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig); | ||||
| } | ||||
|  | ||||
| function isValidKey(key: string): boolean { | ||||
|   return /^[\w\-\.]+$/i.test(key); | ||||
| } | ||||
|  | ||||
| function getExistingNugetConfig(processRoot: string) { | ||||
|   const defaultConfigName = 'nuget.config'; | ||||
|   const configFileNames = fs | ||||
|     .readdirSync(processRoot) | ||||
|     .filter(filename => filename.toLowerCase() === defaultConfigName); | ||||
|   if (configFileNames.length) { | ||||
|     return configFileNames[0]; | ||||
|   } | ||||
|   return defaultConfigName; | ||||
| } | ||||
|  | ||||
| function writeFeedToFile( | ||||
|   feedUrl: string, | ||||
|   existingFileLocation: string, | ||||
|   tempFileLocation: string | ||||
| ) { | ||||
|   core.info( | ||||
|     `dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}` | ||||
|   ); | ||||
|   let sourceKeys: string[] = []; | ||||
|   let owner: string = core.getInput('owner'); | ||||
|   let sourceUrl: string = feedUrl; | ||||
|   if (!owner) { | ||||
|     owner = github.context.repo.owner; | ||||
|   } | ||||
|  | ||||
|   if (!process.env.NUGET_AUTH_TOKEN) { | ||||
|     throw new Error( | ||||
|       'The NUGET_AUTH_TOKEN environment variable was not provided. In this step, add the following: \r\nenv:\r\n  NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (fs.existsSync(existingFileLocation)) { | ||||
|     // get key from existing NuGet.config so NuGet/dotnet can match credentials | ||||
|     const curContents: string = fs.readFileSync(existingFileLocation, 'utf8'); | ||||
|  | ||||
|     const parserOptions = { | ||||
|       ignoreAttributes: false | ||||
|     }; | ||||
|     const parser = new XMLParser(parserOptions); | ||||
|     const json = parser.parse(curContents); | ||||
|  | ||||
|     if (typeof json.configuration === 'undefined') { | ||||
|       throw new Error(`The provided NuGet.config seems invalid.`); | ||||
|     } | ||||
|     if (json.configuration?.packageSources?.add) { | ||||
|       const packageSources = json.configuration.packageSources.add; | ||||
|  | ||||
|       if (Array.isArray(packageSources)) { | ||||
|         packageSources.forEach(source => { | ||||
|           const value = source['@_value']; | ||||
|           core.debug(`source '${value}'`); | ||||
|           if (value.toLowerCase().includes(feedUrl.toLowerCase())) { | ||||
|             const key = source['@_key']; | ||||
|             sourceKeys.push(key); | ||||
|             core.debug(`Found a URL with key ${key}`); | ||||
|           } | ||||
|         }); | ||||
|       } else { | ||||
|         if ( | ||||
|           packageSources['@_value'] | ||||
|             .toLowerCase() | ||||
|             .includes(feedUrl.toLowerCase()) | ||||
|         ) { | ||||
|           const key = packageSources['@_key']; | ||||
|           sourceKeys.push(key); | ||||
|           core.debug(`Found a URL with key ${key}`); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const xmlSource: any[] = [ | ||||
|     { | ||||
|       '?xml': [ | ||||
|         { | ||||
|           '#text': '' | ||||
|         } | ||||
|       ], | ||||
|       ':@': { | ||||
|         '@_version': '1.0' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       configuration: [ | ||||
|         { | ||||
|           config: [ | ||||
|             { | ||||
|               add: [], | ||||
|               ':@': { | ||||
|                 '@_key': 'defaultPushSource', | ||||
|                 '@_value': sourceUrl | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ]; | ||||
|  | ||||
|   if (!sourceKeys.length) { | ||||
|     let keystring = 'Source'; | ||||
|  | ||||
|     xmlSource[1].configuration.push({ | ||||
|       packageSources: [ | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': keystring, | ||||
|             '@_value': sourceUrl | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }); | ||||
|  | ||||
|     sourceKeys.push(keystring); | ||||
|   } | ||||
|  | ||||
|   const packageSourceCredentials: any[] = []; | ||||
|   sourceKeys.forEach(key => { | ||||
|     if (!isValidKey(key)) { | ||||
|       throw new Error( | ||||
|         "Source name can contain letters, numbers, and '-', '_', '.' symbols only. Please, fix source name in NuGet.config and try again." | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     packageSourceCredentials.push({ | ||||
|       [key]: [ | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': 'Username', | ||||
|             '@_value': owner | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': 'ClearTextPassword', | ||||
|             '@_value': process.env.NUGET_AUTH_TOKEN | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   xmlSource[1].configuration.push({ | ||||
|     packageSourceCredentials | ||||
|   }); | ||||
|  | ||||
|   const xmlBuilderOptions = { | ||||
|     format: true, | ||||
|     ignoreAttributes: false, | ||||
|     preserveOrder: true, | ||||
|     allowBooleanAttributes: true, | ||||
|     suppressBooleanAttributes: true, | ||||
|     suppressEmptyNode: true | ||||
|   }; | ||||
|  | ||||
|   const builder = new XMLBuilder(xmlBuilderOptions); | ||||
|  | ||||
|   const output = builder.build(xmlSource).trim(); | ||||
|  | ||||
|   fs.writeFileSync(tempFileLocation, output); | ||||
| } | ||||
| import * as fs from 'fs'; | ||||
| import * as path from 'path'; | ||||
| import * as core from '@actions/core'; | ||||
| import * as github from '@actions/github'; | ||||
| import {XMLParser, XMLBuilder} from 'fast-xml-parser'; | ||||
|  | ||||
| export function configAuthentication( | ||||
|   feedUrl: string, | ||||
|   existingFileLocation = '', | ||||
|   processRoot: string = process.cwd() | ||||
| ) { | ||||
|   const existingNuGetConfig: string = path.resolve( | ||||
|     processRoot, | ||||
|     existingFileLocation === '' | ||||
|       ? getExistingNugetConfig(processRoot) | ||||
|       : existingFileLocation | ||||
|   ); | ||||
|  | ||||
|   const tempNuGetConfig: string = path.resolve( | ||||
|     processRoot, | ||||
|     '../', | ||||
|     'nuget.config' | ||||
|   ); | ||||
|  | ||||
|   writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig); | ||||
| } | ||||
|  | ||||
| function isValidKey(key: string): boolean { | ||||
|   return /^[\w\-.]+$/i.test(key); | ||||
| } | ||||
|  | ||||
| function getExistingNugetConfig(processRoot: string) { | ||||
|   const defaultConfigName = 'nuget.config'; | ||||
|   const configFileNames = fs | ||||
|     .readdirSync(processRoot) | ||||
|     .filter(filename => filename.toLowerCase() === defaultConfigName); | ||||
|   if (configFileNames.length) { | ||||
|     return configFileNames[0]; | ||||
|   } | ||||
|   return defaultConfigName; | ||||
| } | ||||
|  | ||||
| function writeFeedToFile( | ||||
|   feedUrl: string, | ||||
|   existingFileLocation: string, | ||||
|   tempFileLocation: string | ||||
| ) { | ||||
|   core.info( | ||||
|     `dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}` | ||||
|   ); | ||||
|   const sourceKeys: string[] = []; | ||||
|   let owner: string = core.getInput('owner'); | ||||
|   const sourceUrl: string = feedUrl; | ||||
|   if (!owner) { | ||||
|     owner = github.context.repo.owner; | ||||
|   } | ||||
|  | ||||
|   if (!process.env.NUGET_AUTH_TOKEN) { | ||||
|     throw new Error( | ||||
|       'The NUGET_AUTH_TOKEN environment variable was not provided. In this step, add the following: \r\nenv:\r\n  NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (fs.existsSync(existingFileLocation)) { | ||||
|     // get key from existing NuGet.config so NuGet/dotnet can match credentials | ||||
|     const curContents: string = fs.readFileSync(existingFileLocation, 'utf8'); | ||||
|  | ||||
|     const parserOptions = { | ||||
|       ignoreAttributes: false | ||||
|     }; | ||||
|     const parser = new XMLParser(parserOptions); | ||||
|     const json = parser.parse(curContents); | ||||
|  | ||||
|     if (typeof json.configuration === 'undefined') { | ||||
|       throw new Error(`The provided NuGet.config seems invalid.`); | ||||
|     } | ||||
|     if (json.configuration?.packageSources?.add) { | ||||
|       const packageSources = json.configuration.packageSources.add; | ||||
|  | ||||
|       if (Array.isArray(packageSources)) { | ||||
|         packageSources.forEach(source => { | ||||
|           const value = source['@_value']; | ||||
|           core.debug(`source '${value}'`); | ||||
|           if (value.toLowerCase().includes(feedUrl.toLowerCase())) { | ||||
|             const key = source['@_key']; | ||||
|             sourceKeys.push(key); | ||||
|             core.debug(`Found a URL with key ${key}`); | ||||
|           } | ||||
|         }); | ||||
|       } else { | ||||
|         if ( | ||||
|           packageSources['@_value'] | ||||
|             .toLowerCase() | ||||
|             .includes(feedUrl.toLowerCase()) | ||||
|         ) { | ||||
|           const key = packageSources['@_key']; | ||||
|           sourceKeys.push(key); | ||||
|           core.debug(`Found a URL with key ${key}`); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const xmlSource: any[] = [ | ||||
|     { | ||||
|       '?xml': [ | ||||
|         { | ||||
|           '#text': '' | ||||
|         } | ||||
|       ], | ||||
|       ':@': { | ||||
|         '@_version': '1.0' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       configuration: [ | ||||
|         { | ||||
|           config: [ | ||||
|             { | ||||
|               add: [], | ||||
|               ':@': { | ||||
|                 '@_key': 'defaultPushSource', | ||||
|                 '@_value': sourceUrl | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ]; | ||||
|  | ||||
|   if (!sourceKeys.length) { | ||||
|     const keystring = 'Source'; | ||||
|  | ||||
|     xmlSource[1].configuration.push({ | ||||
|       packageSources: [ | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': keystring, | ||||
|             '@_value': sourceUrl | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }); | ||||
|  | ||||
|     sourceKeys.push(keystring); | ||||
|   } | ||||
|  | ||||
|   const packageSourceCredentials: any[] = []; | ||||
|   sourceKeys.forEach(key => { | ||||
|     if (!isValidKey(key)) { | ||||
|       throw new Error( | ||||
|         "Source name can contain letters, numbers, and '-', '_', '.' symbols only. Please, fix source name in NuGet.config and try again." | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     packageSourceCredentials.push({ | ||||
|       [key]: [ | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': 'Username', | ||||
|             '@_value': owner | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           add: [], | ||||
|           ':@': { | ||||
|             '@_key': 'ClearTextPassword', | ||||
|             '@_value': process.env.NUGET_AUTH_TOKEN | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   xmlSource[1].configuration.push({ | ||||
|     packageSourceCredentials | ||||
|   }); | ||||
|  | ||||
|   const xmlBuilderOptions = { | ||||
|     format: true, | ||||
|     ignoreAttributes: false, | ||||
|     preserveOrder: true, | ||||
|     allowBooleanAttributes: true, | ||||
|     suppressBooleanAttributes: true, | ||||
|     suppressEmptyNode: true | ||||
|   }; | ||||
|  | ||||
|   const builder = new XMLBuilder(xmlBuilderOptions); | ||||
|  | ||||
|   const output = builder.build(xmlSource).trim(); | ||||
|  | ||||
|   fs.writeFileSync(tempFileLocation, output); | ||||
| } | ||||
|  | ||||
							
								
								
									
										541
									
								
								src/installer.ts
									
									
									
									
									
								
							
							
						
						
									
										541
									
								
								src/installer.ts
									
									
									
									
									
								
							| @ -1,263 +1,278 @@ | ||||
| // Load tempDirectory before it gets wiped by tool-cache | ||||
| import * as core from '@actions/core'; | ||||
| import * as exec from '@actions/exec'; | ||||
| import * as io from '@actions/io'; | ||||
| import * as hc from '@actions/http-client'; | ||||
| import {chmodSync} from 'fs'; | ||||
| import {readdir} from 'fs/promises'; | ||||
| import path from 'path'; | ||||
| import os from 'os'; | ||||
| import semver from 'semver'; | ||||
| import {IS_LINUX, IS_WINDOWS} from './utils'; | ||||
| import {QualityOptions} from './setup-dotnet'; | ||||
|  | ||||
| export interface DotnetVersion { | ||||
|   type: string; | ||||
|   value: string; | ||||
|   qualityFlag: boolean; | ||||
| } | ||||
|  | ||||
| export class DotnetVersionResolver { | ||||
|   private inputVersion: string; | ||||
|   private resolvedArgument: DotnetVersion; | ||||
|  | ||||
|   constructor(version: string) { | ||||
|     this.inputVersion = version.trim(); | ||||
|     this.resolvedArgument = {type: '', value: '', qualityFlag: false}; | ||||
|   } | ||||
|  | ||||
|   private async resolveVersionInput(): Promise<void> { | ||||
|     if (!semver.validRange(this.inputVersion)) { | ||||
|       throw new Error( | ||||
|         `'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x` | ||||
|       ); | ||||
|     } | ||||
|     if (semver.valid(this.inputVersion)) { | ||||
|       this.resolvedArgument.type = 'version'; | ||||
|       this.resolvedArgument.value = this.inputVersion; | ||||
|     } else { | ||||
|       const [major, minor] = this.inputVersion.split('.'); | ||||
|  | ||||
|       if (this.isNumericTag(major)) { | ||||
|         this.resolvedArgument.type = 'channel'; | ||||
|         if (this.isNumericTag(minor)) { | ||||
|           this.resolvedArgument.value = `${major}.${minor}`; | ||||
|         } else { | ||||
|           const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|             allowRetries: true, | ||||
|             maxRetries: 3 | ||||
|           }); | ||||
|           this.resolvedArgument.value = await this.getLatestVersion( | ||||
|             httpClient, | ||||
|             [major, minor] | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|       this.resolvedArgument.qualityFlag = +major >= 6 ? true : false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private isNumericTag(versionTag): boolean { | ||||
|     return /^\d+$/.test(versionTag); | ||||
|   } | ||||
|  | ||||
|   public async createDotNetVersion(): Promise<{ | ||||
|     type: string; | ||||
|     value: string; | ||||
|     qualityFlag: boolean; | ||||
|   }> { | ||||
|     await this.resolveVersionInput(); | ||||
|     if (!this.resolvedArgument.type) { | ||||
|       return this.resolvedArgument; | ||||
|     } | ||||
|     if (IS_WINDOWS) { | ||||
|       this.resolvedArgument.type = | ||||
|         this.resolvedArgument.type === 'channel' ? '-Channel' : '-Version'; | ||||
|     } else { | ||||
|       this.resolvedArgument.type = | ||||
|         this.resolvedArgument.type === 'channel' ? '--channel' : '--version'; | ||||
|     } | ||||
|     return this.resolvedArgument; | ||||
|   } | ||||
|  | ||||
|   private async getLatestVersion( | ||||
|     httpClient: hc.HttpClient, | ||||
|     versionParts: string[] | ||||
|   ): Promise<string> { | ||||
|     const response = await httpClient.getJson<any>( | ||||
|       DotnetVersionResolver.DotNetCoreIndexUrl | ||||
|     ); | ||||
|     const result = response.result || {}; | ||||
|     let releasesInfo: any[] = result['releases-index']; | ||||
|  | ||||
|     let releaseInfo = releasesInfo.find(info => { | ||||
|       let sdkParts: string[] = info['channel-version'].split('.'); | ||||
|       return sdkParts[0] === versionParts[0]; | ||||
|     }); | ||||
|  | ||||
|     if (!releaseInfo) { | ||||
|       throw new Error( | ||||
|         `Could not find info for version ${versionParts.join('.')} at ${ | ||||
|           DotnetVersionResolver.DotNetCoreIndexUrl | ||||
|         }` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return releaseInfo['channel-version']; | ||||
|   } | ||||
|  | ||||
|   static DotNetCoreIndexUrl: string = | ||||
|     'https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json'; | ||||
| } | ||||
|  | ||||
| export class DotnetCoreInstaller { | ||||
|   private version: string; | ||||
|   private quality: QualityOptions; | ||||
|  | ||||
|   static { | ||||
|     const installationDirectoryWindows = path.join( | ||||
|       process.env['PROGRAMFILES'] + '', | ||||
|       'dotnet' | ||||
|     ); | ||||
|     const installationDirectoryLinux = '/usr/share/dotnet'; | ||||
|     const installationDirectoryMac = path.join( | ||||
|       process.env['HOME'] + '', | ||||
|       '.dotnet' | ||||
|     ); | ||||
|     const dotnetInstallDir: string | undefined = | ||||
|       process.env['DOTNET_INSTALL_DIR']; | ||||
|     if (dotnetInstallDir) { | ||||
|       process.env['DOTNET_INSTALL_DIR'] = | ||||
|         this.convertInstallPathToAbsolute(dotnetInstallDir); | ||||
|     } else { | ||||
|       if (IS_WINDOWS) { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = installationDirectoryWindows; | ||||
|       } else { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = IS_LINUX | ||||
|           ? installationDirectoryLinux | ||||
|           : installationDirectoryMac; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   constructor(version: string, quality: QualityOptions) { | ||||
|     this.version = version; | ||||
|     this.quality = quality; | ||||
|   } | ||||
|  | ||||
|   private static convertInstallPathToAbsolute(installDir: string): string { | ||||
|     let transformedPath; | ||||
|     if (path.isAbsolute(installDir)) { | ||||
|       transformedPath = installDir; | ||||
|     } else { | ||||
|       transformedPath = installDir.startsWith('~') | ||||
|         ? path.join(os.homedir(), installDir.slice(1)) | ||||
|         : (transformedPath = path.join(process.cwd(), installDir)); | ||||
|     } | ||||
|     return path.normalize(transformedPath); | ||||
|   } | ||||
|  | ||||
|   static addToPath() { | ||||
|     core.addPath(process.env['DOTNET_INSTALL_DIR']!); | ||||
|     core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']); | ||||
|   } | ||||
|  | ||||
|   private setQuality( | ||||
|     dotnetVersion: DotnetVersion, | ||||
|     scriptArguments: string[] | ||||
|   ): void { | ||||
|     const option = IS_WINDOWS ? '-Quality' : '--quality'; | ||||
|     if (dotnetVersion.qualityFlag) { | ||||
|       scriptArguments.push(option, this.quality); | ||||
|     } else { | ||||
|       core.warning( | ||||
|         `'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A and A.x formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.` | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public async installDotnet(): Promise<string> { | ||||
|     const windowsDefaultOptions = [ | ||||
|       '-NoLogo', | ||||
|       '-Sta', | ||||
|       '-NoProfile', | ||||
|       '-NonInteractive', | ||||
|       '-ExecutionPolicy', | ||||
|       'Unrestricted', | ||||
|       '-Command' | ||||
|     ]; | ||||
|     const scriptName = IS_WINDOWS ? 'install-dotnet.ps1' : 'install-dotnet.sh'; | ||||
|     const escapedScript = path | ||||
|       .join(__dirname, '..', 'externals', scriptName) | ||||
|       .replace(/'/g, "''"); | ||||
|     let scriptArguments: string[]; | ||||
|     let scriptPath = ''; | ||||
|  | ||||
|     const versionResolver = new DotnetVersionResolver(this.version); | ||||
|     const dotnetVersion = await versionResolver.createDotNetVersion(); | ||||
|  | ||||
|     if (IS_WINDOWS) { | ||||
|       scriptArguments = ['&', `'${escapedScript}'`]; | ||||
|  | ||||
|       if (dotnetVersion.type) { | ||||
|         scriptArguments.push(dotnetVersion.type, dotnetVersion.value); | ||||
|       } | ||||
|  | ||||
|       if (this.quality) { | ||||
|         this.setQuality(dotnetVersion, scriptArguments); | ||||
|       } | ||||
|  | ||||
|       if (process.env['https_proxy'] != null) { | ||||
|         scriptArguments.push(`-ProxyAddress ${process.env['https_proxy']}`); | ||||
|       } | ||||
|       // This is not currently an option | ||||
|       if (process.env['no_proxy'] != null) { | ||||
|         scriptArguments.push(`-ProxyBypassList ${process.env['no_proxy']}`); | ||||
|       } | ||||
|  | ||||
|       scriptPath = | ||||
|         (await io.which('pwsh', false)) || (await io.which('powershell', true)); | ||||
|       scriptArguments = windowsDefaultOptions.concat(scriptArguments); | ||||
|     } else { | ||||
|       chmodSync(escapedScript, '777'); | ||||
|       scriptPath = await io.which(escapedScript, true); | ||||
|       scriptArguments = []; | ||||
|  | ||||
|       if (dotnetVersion.type) { | ||||
|         scriptArguments.push(dotnetVersion.type, dotnetVersion.value); | ||||
|       } | ||||
|  | ||||
|       if (this.quality) { | ||||
|         this.setQuality(dotnetVersion, scriptArguments); | ||||
|       } | ||||
|     } | ||||
|     // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|     const getExecOutputOptions = { | ||||
|       ignoreReturnCode: true, | ||||
|       env: process.env as {string: string} | ||||
|     }; | ||||
|     const {exitCode, stdout} = await exec.getExecOutput( | ||||
|       `"${scriptPath}"`, | ||||
|       scriptArguments, | ||||
|       getExecOutputOptions | ||||
|     ); | ||||
|     if (exitCode) { | ||||
|       throw new Error(`Failed to install dotnet ${exitCode}. ${stdout}`); | ||||
|     } | ||||
|  | ||||
|     return this.outputDotnetVersion(dotnetVersion.value); | ||||
|   } | ||||
|  | ||||
|   private async outputDotnetVersion(version): Promise<string> { | ||||
|     const installationPath = process.env['DOTNET_INSTALL_DIR']!; | ||||
|     let versionsOnRunner: string[] = await readdir( | ||||
|       path.join(installationPath.replace(/'/g, ''), 'sdk') | ||||
|     ); | ||||
|  | ||||
|     let installedVersion = semver.maxSatisfying(versionsOnRunner, version, { | ||||
|       includePrerelease: true | ||||
|     })!; | ||||
|  | ||||
|     return installedVersion; | ||||
|   } | ||||
| } | ||||
| // Load tempDirectory before it gets wiped by tool-cache | ||||
| import * as core from '@actions/core'; | ||||
| import * as exec from '@actions/exec'; | ||||
| import * as io from '@actions/io'; | ||||
| import * as hc from '@actions/http-client'; | ||||
| import {chmodSync} from 'fs'; | ||||
| import path from 'path'; | ||||
| import os from 'os'; | ||||
| import semver from 'semver'; | ||||
| import {IS_LINUX, IS_WINDOWS} from './utils'; | ||||
| import {QualityOptions} from './setup-dotnet'; | ||||
|  | ||||
| export interface DotnetVersion { | ||||
|   type: string; | ||||
|   value: string; | ||||
|   qualityFlag: boolean; | ||||
| } | ||||
|  | ||||
| const QUALITY_INPUT_MINIMAL_MAJOR_TAG = 6; | ||||
| const LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG = 5; | ||||
| export class DotnetVersionResolver { | ||||
|   private inputVersion: string; | ||||
|   private resolvedArgument: DotnetVersion; | ||||
|  | ||||
|   constructor(version: string) { | ||||
|     this.inputVersion = version.trim(); | ||||
|     this.resolvedArgument = {type: '', value: '', qualityFlag: false}; | ||||
|   } | ||||
|  | ||||
|   private async resolveVersionInput(): Promise<void> { | ||||
|     if (!semver.validRange(this.inputVersion) && !this.isLatestPatchSyntax()) { | ||||
|       throw new Error( | ||||
|         `The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx` | ||||
|       ); | ||||
|     } | ||||
|     if (semver.valid(this.inputVersion)) { | ||||
|       this.createVersionArgument(); | ||||
|     } else { | ||||
|       await this.createChannelArgument(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private isNumericTag(versionTag): boolean { | ||||
|     return /^\d+$/.test(versionTag); | ||||
|   } | ||||
|  | ||||
|   private isLatestPatchSyntax() { | ||||
|     const majorTag = this.inputVersion.match( | ||||
|       /^(?<majorTag>\d+)\.\d+\.\d{1}x{2}$/ | ||||
|     )?.groups?.majorTag; | ||||
|     if ( | ||||
|       majorTag && | ||||
|       parseInt(majorTag) < LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG | ||||
|     ) { | ||||
|       throw new Error( | ||||
|         `The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! The A.B.Cxx syntax is available since the .NET 5.0 release.` | ||||
|       ); | ||||
|     } | ||||
|     return majorTag ? true : false; | ||||
|   } | ||||
|  | ||||
|   private createVersionArgument() { | ||||
|     this.resolvedArgument.type = 'version'; | ||||
|     this.resolvedArgument.value = this.inputVersion; | ||||
|   } | ||||
|  | ||||
|   private async createChannelArgument() { | ||||
|     this.resolvedArgument.type = 'channel'; | ||||
|     const [major, minor] = this.inputVersion.split('.'); | ||||
|     if (this.isLatestPatchSyntax()) { | ||||
|       this.resolvedArgument.value = this.inputVersion; | ||||
|     } else if (this.isNumericTag(major) && this.isNumericTag(minor)) { | ||||
|       this.resolvedArgument.value = `${major}.${minor}`; | ||||
|     } else if (this.isNumericTag(major)) { | ||||
|       this.resolvedArgument.value = await this.getLatestByMajorTag(major); | ||||
|     } else { | ||||
|       // If "dotnet-version" is specified as *, x or X resolve latest version of .NET explicitly from LTS channel. The version argument will default to "latest" by install-dotnet script. | ||||
|       this.resolvedArgument.value = 'LTS'; | ||||
|     } | ||||
|     this.resolvedArgument.qualityFlag = | ||||
|       parseInt(major) >= QUALITY_INPUT_MINIMAL_MAJOR_TAG ? true : false; | ||||
|   } | ||||
|  | ||||
|   public async createDotNetVersion(): Promise<DotnetVersion> { | ||||
|     await this.resolveVersionInput(); | ||||
|     if (!this.resolvedArgument.type) { | ||||
|       return this.resolvedArgument; | ||||
|     } | ||||
|     if (IS_WINDOWS) { | ||||
|       this.resolvedArgument.type = | ||||
|         this.resolvedArgument.type === 'channel' ? '-Channel' : '-Version'; | ||||
|     } else { | ||||
|       this.resolvedArgument.type = | ||||
|         this.resolvedArgument.type === 'channel' ? '--channel' : '--version'; | ||||
|     } | ||||
|     return this.resolvedArgument; | ||||
|   } | ||||
|  | ||||
|   private async getLatestByMajorTag(majorTag: string): Promise<string> { | ||||
|     const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|       allowRetries: true, | ||||
|       maxRetries: 3 | ||||
|     }); | ||||
|     const response = await httpClient.getJson<any>( | ||||
|       DotnetVersionResolver.DotNetCoreIndexUrl | ||||
|     ); | ||||
|     const result = response.result || {}; | ||||
|     const releasesInfo: any[] = result['releases-index']; | ||||
|  | ||||
|     const releaseInfo = releasesInfo.find(info => { | ||||
|       const sdkParts: string[] = info['channel-version'].split('.'); | ||||
|       return sdkParts[0] === majorTag; | ||||
|     }); | ||||
|  | ||||
|     if (!releaseInfo) { | ||||
|       throw new Error( | ||||
|         `Could not find info for version with major tag: "${majorTag}" at ${DotnetVersionResolver.DotNetCoreIndexUrl}` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return releaseInfo['channel-version']; | ||||
|   } | ||||
|  | ||||
|   static DotNetCoreIndexUrl = | ||||
|     'https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json'; | ||||
| } | ||||
|  | ||||
| export class DotnetCoreInstaller { | ||||
|   private version: string; | ||||
|   private quality: QualityOptions; | ||||
|  | ||||
|   static { | ||||
|     const installationDirectoryWindows = path.join( | ||||
|       process.env['PROGRAMFILES'] + '', | ||||
|       'dotnet' | ||||
|     ); | ||||
|     const installationDirectoryLinux = '/usr/share/dotnet'; | ||||
|     const installationDirectoryMac = path.join( | ||||
|       process.env['HOME'] + '', | ||||
|       '.dotnet' | ||||
|     ); | ||||
|     const dotnetInstallDir: string | undefined = | ||||
|       process.env['DOTNET_INSTALL_DIR']; | ||||
|     if (dotnetInstallDir) { | ||||
|       process.env['DOTNET_INSTALL_DIR'] = | ||||
|         this.convertInstallPathToAbsolute(dotnetInstallDir); | ||||
|     } else { | ||||
|       if (IS_WINDOWS) { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = installationDirectoryWindows; | ||||
|       } else { | ||||
|         process.env['DOTNET_INSTALL_DIR'] = IS_LINUX | ||||
|           ? installationDirectoryLinux | ||||
|           : installationDirectoryMac; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   constructor(version: string, quality: QualityOptions) { | ||||
|     this.version = version; | ||||
|     this.quality = quality; | ||||
|   } | ||||
|  | ||||
|   private static convertInstallPathToAbsolute(installDir: string): string { | ||||
|     let transformedPath; | ||||
|     if (path.isAbsolute(installDir)) { | ||||
|       transformedPath = installDir; | ||||
|     } else { | ||||
|       transformedPath = installDir.startsWith('~') | ||||
|         ? path.join(os.homedir(), installDir.slice(1)) | ||||
|         : (transformedPath = path.join(process.cwd(), installDir)); | ||||
|     } | ||||
|     return path.normalize(transformedPath); | ||||
|   } | ||||
|  | ||||
|   static addToPath() { | ||||
|     core.addPath(process.env['DOTNET_INSTALL_DIR']!); | ||||
|     core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']); | ||||
|   } | ||||
|  | ||||
|   private setQuality( | ||||
|     dotnetVersion: DotnetVersion, | ||||
|     scriptArguments: string[] | ||||
|   ): void { | ||||
|     const option = IS_WINDOWS ? '-Quality' : '--quality'; | ||||
|     if (dotnetVersion.qualityFlag) { | ||||
|       scriptArguments.push(option, this.quality); | ||||
|     } else { | ||||
|       core.warning( | ||||
|         `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.` | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public async installDotnet(): Promise<string | null> { | ||||
|     const windowsDefaultOptions = [ | ||||
|       '-NoLogo', | ||||
|       '-Sta', | ||||
|       '-NoProfile', | ||||
|       '-NonInteractive', | ||||
|       '-ExecutionPolicy', | ||||
|       'Unrestricted', | ||||
|       '-Command' | ||||
|     ]; | ||||
|     const scriptName = IS_WINDOWS ? 'install-dotnet.ps1' : 'install-dotnet.sh'; | ||||
|     const escapedScript = path | ||||
|       .join(__dirname, '..', 'externals', scriptName) | ||||
|       .replace(/'/g, "''"); | ||||
|     let scriptArguments: string[]; | ||||
|     let scriptPath = ''; | ||||
|  | ||||
|     const versionResolver = new DotnetVersionResolver(this.version); | ||||
|     const dotnetVersion = await versionResolver.createDotNetVersion(); | ||||
|  | ||||
|     if (IS_WINDOWS) { | ||||
|       scriptArguments = ['&', `'${escapedScript}'`]; | ||||
|  | ||||
|       if (dotnetVersion.type) { | ||||
|         scriptArguments.push(dotnetVersion.type, dotnetVersion.value); | ||||
|       } | ||||
|  | ||||
|       if (this.quality) { | ||||
|         this.setQuality(dotnetVersion, scriptArguments); | ||||
|       } | ||||
|  | ||||
|       if (process.env['https_proxy'] != null) { | ||||
|         scriptArguments.push(`-ProxyAddress ${process.env['https_proxy']}`); | ||||
|       } | ||||
|       // This is not currently an option | ||||
|       if (process.env['no_proxy'] != null) { | ||||
|         scriptArguments.push(`-ProxyBypassList ${process.env['no_proxy']}`); | ||||
|       } | ||||
|  | ||||
|       scriptPath = | ||||
|         (await io.which('pwsh', false)) || (await io.which('powershell', true)); | ||||
|       scriptArguments = windowsDefaultOptions.concat(scriptArguments); | ||||
|     } else { | ||||
|       chmodSync(escapedScript, '777'); | ||||
|       scriptPath = await io.which(escapedScript, true); | ||||
|       scriptArguments = []; | ||||
|  | ||||
|       if (dotnetVersion.type) { | ||||
|         scriptArguments.push(dotnetVersion.type, dotnetVersion.value); | ||||
|       } | ||||
|  | ||||
|       if (this.quality) { | ||||
|         this.setQuality(dotnetVersion, scriptArguments); | ||||
|       } | ||||
|     } | ||||
|     // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|     const getExecOutputOptions = { | ||||
|       ignoreReturnCode: true, | ||||
|       env: process.env as {string: string} | ||||
|     }; | ||||
|     const {exitCode, stdout, stderr} = await exec.getExecOutput( | ||||
|       `"${scriptPath}"`, | ||||
|       scriptArguments, | ||||
|       getExecOutputOptions | ||||
|     ); | ||||
|     if (exitCode) { | ||||
|       throw new Error( | ||||
|         `Failed to install dotnet, exit code: ${exitCode}. ${stderr}` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return this.parseInstalledVersion(stdout); | ||||
|   } | ||||
|  | ||||
|   private parseInstalledVersion(stdout: string): string | null { | ||||
|     const regex = /(?<version>\d+\.\d+\.\d+[a-z0-9._-]*)/gm; | ||||
|     const matchedResult = regex.exec(stdout); | ||||
|  | ||||
|     if (!matchedResult) { | ||||
|       core.warning(`Failed to parse installed by the script version of .NET`); | ||||
|       return null; | ||||
|     } | ||||
|     return matchedResult.groups!.version; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ const qualityOptions = [ | ||||
|   'ga' | ||||
| ] as const; | ||||
|  | ||||
| export type QualityOptions = typeof qualityOptions[number]; | ||||
| export type QualityOptions = (typeof qualityOptions)[number]; | ||||
|  | ||||
| export async function run() { | ||||
|   try { | ||||
| @ -27,11 +27,11 @@ export async function run() { | ||||
|     // Proxy, auth, (etc) are still set up, even if no version is identified | ||||
|     // | ||||
|     const versions = core.getMultilineInput('dotnet-version'); | ||||
|     const installedDotnetVersions: string[] = []; | ||||
|     const installedDotnetVersions: (string | null)[] = []; | ||||
|  | ||||
|     const globalJsonFileInput = core.getInput('global-json-file'); | ||||
|     if (globalJsonFileInput) { | ||||
|       const globalJsonPath = path.join(process.cwd(), globalJsonFileInput); | ||||
|       const globalJsonPath = path.resolve(process.cwd(), globalJsonFileInput); | ||||
|       if (!fs.existsSync(globalJsonPath)) { | ||||
|         throw new Error( | ||||
|           `The specified global.json file '${globalJsonFileInput}' does not exist` | ||||
| @ -48,7 +48,7 @@ export async function run() { | ||||
|         versions.push(getVersionFromGlobalJson(globalJsonPath)); | ||||
|       } else { | ||||
|         core.info( | ||||
|           `global.json wasn't found in the root directory. No .NET version will be installed.` | ||||
|           `The global.json wasn't found in the root directory. No .NET version will be installed.` | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| @ -58,7 +58,7 @@ export async function run() { | ||||
|  | ||||
|       if (quality && !qualityOptions.includes(quality)) { | ||||
|         throw new Error( | ||||
|           `${quality} is not a supported value for 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.` | ||||
|           `Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.` | ||||
|         ); | ||||
|       } | ||||
|  | ||||
| @ -78,19 +78,7 @@ export async function run() { | ||||
|       auth.configAuthentication(sourceUrl, configFile); | ||||
|     } | ||||
|  | ||||
|     const comparisonRange: string = globalJsonFileInput | ||||
|       ? versions[versions.length - 1]! | ||||
|       : '*'; | ||||
|  | ||||
|     const versionToOutput = semver.maxSatisfying( | ||||
|       installedDotnetVersions, | ||||
|       comparisonRange, | ||||
|       { | ||||
|         includePrerelease: true | ||||
|       } | ||||
|     ); | ||||
|  | ||||
|     core.setOutput('dotnet-version', versionToOutput); | ||||
|     outputInstalledVersion(installedDotnetVersions, globalJsonFileInput); | ||||
|  | ||||
|     const matchersPath = path.join(__dirname, '..', '.github'); | ||||
|     core.info(`##[add-matcher]${path.join(matchersPath, 'csc.json')}`); | ||||
| @ -100,7 +88,7 @@ export async function run() { | ||||
| } | ||||
|  | ||||
| function getVersionFromGlobalJson(globalJsonPath: string): string { | ||||
|   let version: string = ''; | ||||
|   let version = ''; | ||||
|   const globalJson = JSON.parse( | ||||
|     // .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649 | ||||
|     fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim() | ||||
| @ -116,4 +104,37 @@ function getVersionFromGlobalJson(globalJsonPath: string): string { | ||||
|   return version; | ||||
| } | ||||
|  | ||||
| function outputInstalledVersion( | ||||
|   installedVersions: (string | null)[], | ||||
|   globalJsonFileInput: string | ||||
| ): void { | ||||
|   if (!installedVersions.length) { | ||||
|     core.info(`The 'dotnet-version' output will not be set.`); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (installedVersions.includes(null)) { | ||||
|     core.warning( | ||||
|       `Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.` | ||||
|     ); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (globalJsonFileInput) { | ||||
|     const versionToOutput = installedVersions.at(-1); // .NET SDK version parsed from the global.json file is installed last | ||||
|     core.setOutput('dotnet-version', versionToOutput); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const versionToOutput = semver.maxSatisfying( | ||||
|     installedVersions as string[], | ||||
|     '*', | ||||
|     { | ||||
|       includePrerelease: true | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   core.setOutput('dotnet-version', versionToOutput); | ||||
| } | ||||
|  | ||||
| run(); | ||||
|  | ||||
| @ -49,7 +49,8 @@ | ||||
|     // "typeRoots": [],                       /* List of folders to include type definitions from. */ | ||||
|     // "types": [],                           /* Type declaration files to be included in compilation. */ | ||||
|     // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||||
|     "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||||
|     "esModuleInterop": true,                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||||
|     "resolveJsonModule": true,                /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */ | ||||
|     // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */ | ||||
|     // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */ | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	![github-actions[bot]](/assets/img/avatar_default.png)