You've already forked setup-dotnet
							
							
				mirror of
				https://github.com/actions/setup-dotnet.git
				synced 2025-10-31 15:26:22 +07:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			update-cod
			...
			remove-fal
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8bcd8d8b49 | |||
| 0b32034241 | |||
| aab9aab748 | 
							
								
								
									
										2
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							| @ -45,7 +45,7 @@ jobs: | ||||
|         id: diff | ||||
|  | ||||
|       # If index.js was different than expected, upload the expected version as an artifact | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|       - uses: actions/upload-artifact@v4 | ||||
|         if: ${{ failure() && steps.diff.conclusion == 'failure' }} | ||||
|         with: | ||||
|           name: dist | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/test-dotnet.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test-dotnet.yml
									
									
									
									
										vendored
									
									
								
							| @ -17,7 +17,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|         dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0'] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|  | ||||
							
								
								
									
										24
									
								
								.github/workflows/workflow.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/workflow.yml
									
									
									
									
										vendored
									
									
								
							| @ -39,7 +39,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -62,7 +62,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macos-13] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -95,7 +95,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -120,7 +120,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -144,7 +144,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -168,7 +168,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         operating-system: [ubuntu-latest, windows-latest, macOS-latest] | ||||
|         operating-system: [ubuntu-22.04, windows-latest, macOS-latest] | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -190,18 +190,20 @@ jobs: | ||||
|         run: __tests__/verify-dotnet.ps1 2.2 3.1 | ||||
|  | ||||
|   test-proxy: | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-20.04 | ||||
|     container: | ||||
|       image: mcr.microsoft.com/dotnet/core/runtime-deps:3.0-bionic | ||||
|       image: ubuntu:20.04 | ||||
|       options: --dns 127.0.0.1 | ||||
|     services: | ||||
|       squid-proxy: | ||||
|         image: datadog/squid:latest | ||||
|         image: ubuntu/squid:latest | ||||
|         ports: | ||||
|           - 3128:3128 | ||||
|     env: | ||||
|       https_proxy: http://squid-proxy:3128 | ||||
|       http_proxy: http://squid-proxy:3128 | ||||
|       DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: true | ||||
|  | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v3 | ||||
| @ -210,7 +212,7 @@ jobs: | ||||
|       - name: Install curl | ||||
|         run: | | ||||
|           apt update | ||||
|           apt -y install curl | ||||
|           apt -y install curl libssl1.1 libssl-dev | ||||
|       - name: Setup dotnet 3.1.201 | ||||
|         uses: ./ | ||||
|         with: | ||||
| @ -222,7 +224,7 @@ jobs: | ||||
|         run: __tests__/verify-dotnet.sh 3.1.201 | ||||
|  | ||||
|   test-bypass-proxy: | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-22.04 | ||||
|     env: | ||||
|       https_proxy: http://no-such-proxy:3128 | ||||
|       no_proxy: github.com,dotnetcli.blob.core.windows.net,download.visualstudio.microsoft.com,api.nuget.org,dotnetcli.azureedge.net | ||||
|  | ||||
| @ -1,340 +1,336 @@ | ||||
| import io = require('@actions/io'); | ||||
| import fs = require('fs'); | ||||
| import path = require('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 io = require('@actions/io'); | ||||
| import fs = require('fs'); | ||||
| import path = require('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(); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										2604
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2604
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3113
									
								
								externals/install-dotnet.ps1
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3113
									
								
								externals/install-dotnet.ps1
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										315
									
								
								externals/install-dotnet.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										315
									
								
								externals/install-dotnet.sh
									
									
									
									
										vendored
									
									
								
							| @ -298,11 +298,20 @@ get_machine_architecture() { | ||||
|     if command -v uname > /dev/null; then | ||||
|         CPUName=$(uname -m) | ||||
|         case $CPUName in | ||||
|         armv1*|armv2*|armv3*|armv4*|armv5*|armv6*) | ||||
|             echo "armv6-or-below" | ||||
|             return 0 | ||||
|             ;; | ||||
|         armv*l) | ||||
|             echo "arm" | ||||
|             return 0 | ||||
|             ;; | ||||
|         aarch64|arm64) | ||||
|             if [ "$(getconf LONG_BIT)" -lt 64 ]; then | ||||
|                 # This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS) | ||||
|                 echo "arm" | ||||
|                 return 0 | ||||
|             fi | ||||
|             echo "arm64" | ||||
|             return 0 | ||||
|             ;; | ||||
| @ -310,6 +319,22 @@ get_machine_architecture() { | ||||
|             echo "s390x" | ||||
|             return 0 | ||||
|             ;; | ||||
|         ppc64le) | ||||
|             echo "ppc64le" | ||||
|             return 0 | ||||
|             ;; | ||||
|         loongarch64) | ||||
|             echo "loongarch64" | ||||
|             return 0 | ||||
|             ;; | ||||
|         riscv64) | ||||
|             echo "riscv64" | ||||
|             return 0 | ||||
|             ;; | ||||
|         powerpc|ppc) | ||||
|             echo "ppc" | ||||
|             return 0 | ||||
|             ;; | ||||
|         esac | ||||
|     fi | ||||
|  | ||||
| @ -326,7 +351,13 @@ get_normalized_architecture_from_architecture() { | ||||
|     local architecture="$(to_lowercase "$1")" | ||||
|  | ||||
|     if [[ $architecture == \<auto\> ]]; then | ||||
|         echo "$(get_machine_architecture)" | ||||
|         machine_architecture="$(get_machine_architecture)" | ||||
|         if [[ "$machine_architecture" == "armv6-or-below" ]]; then | ||||
|             say_err "Architecture \`$machine_architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" | ||||
|             return 1 | ||||
|         fi | ||||
|  | ||||
|         echo $machine_architecture | ||||
|         return 0 | ||||
|     fi | ||||
|  | ||||
| @ -347,6 +378,14 @@ get_normalized_architecture_from_architecture() { | ||||
|             echo "s390x" | ||||
|             return 0 | ||||
|             ;; | ||||
|         ppc64le) | ||||
|             echo "ppc64le" | ||||
|             return 0 | ||||
|             ;; | ||||
|         loongarch64) | ||||
|             echo "loongarch64" | ||||
|             return 0 | ||||
|             ;; | ||||
|     esac | ||||
|  | ||||
|     say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" | ||||
| @ -384,11 +423,17 @@ get_normalized_architecture_for_specific_sdk_version() { | ||||
| # args: | ||||
| # version or channel - $1 | ||||
| is_arm64_supported() { | ||||
|     #any channel or version that starts with the specified versions | ||||
|     case "$1" in | ||||
|         ( "1"* | "2"* | "3"*  | "4"* | "5"*)  | ||||
|             echo false | ||||
|             return 0 | ||||
|     # Extract the major version by splitting on the dot | ||||
|     major_version="${1%%.*}" | ||||
|  | ||||
|     # Check if the major version is a valid number and less than 6 | ||||
|     case "$major_version" in | ||||
|         [0-9]*)   | ||||
|             if [ "$major_version" -lt 6 ]; then | ||||
|                 echo false | ||||
|                 return 0 | ||||
|             fi | ||||
|             ;; | ||||
|     esac | ||||
|  | ||||
|     echo true | ||||
| @ -407,8 +452,13 @@ get_normalized_os() { | ||||
|                 echo "$osname" | ||||
|                 return 0 | ||||
|                 ;; | ||||
|             macos) | ||||
|                 osname='osx' | ||||
|                 echo "$osname" | ||||
|                 return 0 | ||||
|                 ;; | ||||
|             *) | ||||
|                 say_err "'$user_defined_os' is not a supported value for --os option, supported values are: osx, linux, linux-musl, freebsd, rhel.6. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." | ||||
|                 say_err "'$user_defined_os' is not a supported value for --os option, supported values are: osx, macos, linux, linux-musl, freebsd, rhel.6. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." | ||||
|                 return 1 | ||||
|                 ;; | ||||
|         esac | ||||
| @ -451,6 +501,10 @@ get_normalized_channel() { | ||||
|  | ||||
|     local channel="$(to_lowercase "$1")" | ||||
|  | ||||
|     if [[ $channel == current ]]; then | ||||
|         say_warning 'Value "Current" is deprecated for -Channel option. Use "STS" instead.' | ||||
|     fi | ||||
|  | ||||
|     if [[ $channel == release/* ]]; then | ||||
|         say_warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead.'; | ||||
|     fi | ||||
| @ -461,6 +515,14 @@ get_normalized_channel() { | ||||
|                 echo "LTS" | ||||
|                 return 0 | ||||
|                 ;; | ||||
|             sts) | ||||
|                 echo "STS" | ||||
|                 return 0 | ||||
|                 ;; | ||||
|             current) | ||||
|                 echo "STS" | ||||
|                 return 0 | ||||
|                 ;; | ||||
|             *) | ||||
|                 echo "$channel" | ||||
|                 return 0 | ||||
| @ -526,6 +588,40 @@ is_dotnet_package_installed() { | ||||
|     fi | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # downloaded file - $1 | ||||
| # remote_file_size - $2 | ||||
| validate_remote_local_file_sizes()  | ||||
| { | ||||
|     eval $invocation | ||||
|  | ||||
|     local downloaded_file="$1" | ||||
|     local remote_file_size="$2" | ||||
|     local file_size='' | ||||
|  | ||||
|     if [[ "$OSTYPE" == "linux-gnu"* ]]; then | ||||
|         file_size="$(stat -c '%s' "$downloaded_file")" | ||||
|     elif [[ "$OSTYPE" == "darwin"* ]]; then | ||||
|         # hardcode in order to avoid conflicts with GNU stat | ||||
|         file_size="$(/usr/bin/stat -f '%z' "$downloaded_file")" | ||||
|     fi   | ||||
|      | ||||
|     if [ -n "$file_size" ]; then | ||||
|         say "Downloaded file size is $file_size bytes." | ||||
|  | ||||
|         if [ -n "$remote_file_size" ] && [ -n "$file_size" ]; then | ||||
|             if [ "$remote_file_size" -ne "$file_size" ]; then | ||||
|                 say "The remote and local file sizes are not equal. The remote file size is $remote_file_size bytes and the local size is $file_size bytes. The local package may be corrupted." | ||||
|             else | ||||
|                 say "The remote and local file sizes are equal." | ||||
|             fi | ||||
|         fi | ||||
|          | ||||
|     else | ||||
|         say "Either downloaded or local package size can not be measured. One of them may be corrupted."       | ||||
|     fi  | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # azure_feed - $1 | ||||
| # channel - $2 | ||||
| @ -860,6 +956,37 @@ get_absolute_path() { | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # override - $1 (boolean, true or false) | ||||
| get_cp_options() { | ||||
|     eval $invocation | ||||
|  | ||||
|     local override="$1" | ||||
|     local override_switch="" | ||||
|  | ||||
|     if [ "$override" = false ]; then | ||||
|         override_switch="-n" | ||||
|  | ||||
|         # create temporary files to check if 'cp -u' is supported | ||||
|         tmp_dir="$(mktemp -d)" | ||||
|         tmp_file="$tmp_dir/testfile" | ||||
|         tmp_file2="$tmp_dir/testfile2" | ||||
|  | ||||
|         touch "$tmp_file" | ||||
|  | ||||
|         # use -u instead of -n if it's available | ||||
|         if cp -u "$tmp_file" "$tmp_file2" 2>/dev/null; then | ||||
|             override_switch="-u" | ||||
|         fi | ||||
|  | ||||
|         # clean up | ||||
|         rm -f "$tmp_file" "$tmp_file2" | ||||
|         rm -rf "$tmp_dir" | ||||
|     fi | ||||
|  | ||||
|     echo "$override_switch" | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # input_files - stdin | ||||
| # root_path - $1 | ||||
| @ -871,15 +998,7 @@ copy_files_or_dirs_from_list() { | ||||
|     local root_path="$(remove_trailing_slash "$1")" | ||||
|     local out_path="$(remove_trailing_slash "$2")" | ||||
|     local override="$3" | ||||
|     local osname="$(get_current_os_name)" | ||||
|     local override_switch=$( | ||||
|         if [ "$override" = false ]; then | ||||
|             if [ "$osname" = "linux-musl" ]; then | ||||
|                 printf -- "-u"; | ||||
|             else | ||||
|                 printf -- "-n"; | ||||
|             fi | ||||
|         fi) | ||||
|     local override_switch="$(get_cp_options "$override")" | ||||
|  | ||||
|     cat | uniq | while read -r file_path; do | ||||
|         local path="$(remove_beginning_slash "${file_path#$root_path}")" | ||||
| @ -894,14 +1013,39 @@ copy_files_or_dirs_from_list() { | ||||
|     done | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # zip_uri - $1 | ||||
| get_remote_file_size() { | ||||
|     local zip_uri="$1" | ||||
|  | ||||
|     if machine_has "curl"; then | ||||
|         file_size=$(curl -sI  "$zip_uri" | grep -i content-length | awk '{ num = $2 + 0; print num }') | ||||
|     elif machine_has "wget"; then | ||||
|         file_size=$(wget --spider --server-response -O /dev/null "$zip_uri" 2>&1 | grep -i 'Content-Length:' | awk '{ num = $2 + 0; print num }') | ||||
|     else | ||||
|         say "Neither curl nor wget is available on this system." | ||||
|         return | ||||
|     fi | ||||
|  | ||||
|     if [ -n "$file_size" ]; then | ||||
|         say "Remote file $zip_uri size is $file_size bytes." | ||||
|         echo "$file_size" | ||||
|     else | ||||
|         say_verbose "Content-Length header was not extracted for $zip_uri." | ||||
|         echo "" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| # args: | ||||
| # zip_path - $1 | ||||
| # out_path - $2 | ||||
| # remote_file_size - $3 | ||||
| extract_dotnet_package() { | ||||
|     eval $invocation | ||||
|  | ||||
|     local zip_path="$1" | ||||
|     local out_path="$2" | ||||
|     local remote_file_size="$3" | ||||
|  | ||||
|     local temp_out_path="$(mktemp -d "$temporary_file_template")" | ||||
|  | ||||
| @ -911,9 +1055,13 @@ extract_dotnet_package() { | ||||
|     local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/' | ||||
|     find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false | ||||
|     find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files" | ||||
|  | ||||
|      | ||||
|     validate_remote_local_file_sizes "$zip_path" "$remote_file_size" | ||||
|      | ||||
|     rm -rf "$temp_out_path" | ||||
|     rm -f "$zip_path" && say_verbose "Temporary zip file $zip_path was removed" | ||||
|     if [ -z ${keep_zip+x} ]; then | ||||
|         rm -f "$zip_path" && say_verbose "Temporary archive file $zip_path was removed" | ||||
|     fi | ||||
|  | ||||
|     if [ "$failed" = true ]; then | ||||
|         say_err "Extraction failed" | ||||
| @ -1124,13 +1272,69 @@ downloadwget() { | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| extract_stem() { | ||||
|     local url="$1" | ||||
|     # extract the protocol | ||||
|     proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')" | ||||
|     # remove the protocol | ||||
|     url="${1/$proto/}" | ||||
|     # extract the path (if any) - since we know all of our feeds have a first path segment, we can skip the first one. otherwise we'd use -f2- to get the full path | ||||
|     full_path="$(echo $url | grep / | cut -d/ -f2-)" | ||||
|     path="$(echo $full_path | cut -d/ -f2-)" | ||||
|     echo $path | ||||
| } | ||||
|  | ||||
| check_url_exists() { | ||||
|     eval $invocation | ||||
|     local url="$1" | ||||
|  | ||||
|     local code="" | ||||
|     if machine_has "curl" | ||||
|     then | ||||
|         code=$(curl --head -o /dev/null -w "%{http_code}" -s --fail "$url"); | ||||
|     elif machine_has "wget" | ||||
|     then | ||||
|         # get the http response, grab the status code | ||||
|         server_response=$(wget -qO- --method=HEAD --server-response "$url" 2>&1) | ||||
|         code=$(echo "$server_response" | grep "HTTP/" | awk '{print $2}') | ||||
|     fi | ||||
|     if [ $code = "200" ]; then | ||||
|         return 0 | ||||
|     else | ||||
|         return 1 | ||||
|     fi | ||||
| } | ||||
|  | ||||
| sanitize_redirect_url() { | ||||
|     eval $invocation | ||||
|  | ||||
|     local url_stem | ||||
|     url_stem=$(extract_stem "$1") | ||||
|     say_verbose "Checking configured feeds for the asset at ${yellow:-}$url_stem${normal:-}" | ||||
|  | ||||
|     for feed in "${feeds[@]}" | ||||
|     do | ||||
|         local trial_url="$feed/$url_stem" | ||||
|         say_verbose "Checking ${yellow:-}$trial_url${normal:-}" | ||||
|         if check_url_exists "$trial_url"; then | ||||
|             say_verbose "Found a match at ${yellow:-}$trial_url${normal:-}" | ||||
|             echo "$trial_url" | ||||
|             return 0 | ||||
|         else | ||||
|             say_verbose "No match at ${yellow:-}$trial_url${normal:-}" | ||||
|         fi | ||||
|     done | ||||
|     return 1 | ||||
| } | ||||
|  | ||||
| get_download_link_from_aka_ms() { | ||||
|     eval $invocation | ||||
|  | ||||
|     #quality is not supported for LTS or current channel | ||||
|     if [[ ! -z "$normalized_quality"  && ("$normalized_channel" == "LTS" || "$normalized_channel" == "current") ]]; then | ||||
|     #quality is not supported for LTS or STS channel | ||||
|     #STS maps to current | ||||
|     if [[ ! -z "$normalized_quality"  && ("$normalized_channel" == "LTS" || "$normalized_channel" == "STS") ]]; then | ||||
|         normalized_quality="" | ||||
|         say_warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored." | ||||
|         say_warning "Specifying quality for STS or LTS channel is not supported, the quality will be ignored." | ||||
|     fi | ||||
|  | ||||
|     say_verbose "Retrieving primary payload URL from aka.ms for channel: '$normalized_channel', quality: '$normalized_quality', product: '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."  | ||||
| @ -1159,6 +1363,12 @@ get_download_link_from_aka_ms() { | ||||
|     http_codes=$( echo "$response" | awk '$1 ~ /^HTTP/ {print $2}' ) | ||||
|     # They all need to be 301, otherwise some links are broken (except for the last, which is not a redirect but 200 or 404). | ||||
|     broken_redirects=$( echo "$http_codes" | sed '$d' | grep -v '301' ) | ||||
|     # The response may end without final code 2xx/4xx/5xx somehow, e.g. network restrictions on www.bing.com causes redirecting to bing.com fails with connection refused. | ||||
|     # In this case it should not exclude the last. | ||||
|     last_http_code=$(  echo "$http_codes" | tail -n 1 ) | ||||
|     if ! [[ $last_http_code =~ ^(2|4|5)[0-9][0-9]$ ]]; then | ||||
|         broken_redirects=$( echo "$http_codes" | grep -v '301' ) | ||||
|     fi | ||||
|  | ||||
|     # All HTTP codes are 301 (Moved Permanently), the redirect link exists. | ||||
|     if [[ -z "$broken_redirects" ]]; then | ||||
| @ -1169,6 +1379,11 @@ get_download_link_from_aka_ms() { | ||||
|             return 1 | ||||
|         fi | ||||
|  | ||||
|         sanitized_redirect_url=$(sanitize_redirect_url "$aka_ms_download_link") | ||||
|         if [[ -n "$sanitized_redirect_url" ]]; then | ||||
|             aka_ms_download_link="$sanitized_redirect_url" | ||||
|         fi | ||||
|  | ||||
|         say_verbose "The redirect location retrieved: '$aka_ms_download_link'." | ||||
|         return 0 | ||||
|     else | ||||
| @ -1180,7 +1395,9 @@ get_download_link_from_aka_ms() { | ||||
| get_feeds_to_use() | ||||
| { | ||||
|     feeds=( | ||||
|     "https://builds.dotnet.microsoft.com/dotnet" | ||||
|     "https://dotnetcli.azureedge.net/dotnet" | ||||
|     "https://ci.dot.net/public" | ||||
|     "https://dotnetbuilds.azureedge.net/public" | ||||
|     ) | ||||
|  | ||||
| @ -1239,7 +1456,7 @@ generate_akams_links() { | ||||
|  | ||||
|     normalized_version="$(to_lowercase "$version")" | ||||
|     if [[ "$normalized_version" != "latest" ]] && [ -n "$normalized_quality" ]; then | ||||
|         say_err "Quality and Version options are not allowed to be specified simultaneously. See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#options for details." | ||||
|         say_err "Quality and Version options are not allowed to be specified simultaneously. See https://learn.microsoft.com/dotnet/core/tools/dotnet-install-script#options for details." | ||||
|         return 1 | ||||
|     fi | ||||
|  | ||||
| @ -1406,10 +1623,11 @@ install_dotnet() { | ||||
|     eval $invocation | ||||
|     local download_failed=false | ||||
|     local download_completed=false | ||||
|     local remote_file_size=0 | ||||
|  | ||||
|     mkdir -p "$install_root" | ||||
|     zip_path="$(mktemp "$temporary_file_template")" | ||||
|     say_verbose "Zip path: $zip_path" | ||||
|     zip_path="${zip_path:-$(mktemp "$temporary_file_template")}" | ||||
|     say_verbose "Archive path: $zip_path" | ||||
|  | ||||
|     for link_index in "${!download_links[@]}" | ||||
|     do | ||||
| @ -1433,7 +1651,7 @@ install_dotnet() { | ||||
|                 say "Failed to download $link_type link '$download_link': $download_error_msg" | ||||
|                 ;; | ||||
|             esac | ||||
|             rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed" | ||||
|             rm -f "$zip_path" 2>&1 && say_verbose "Temporary archive file $zip_path was removed" | ||||
|         else | ||||
|             download_completed=true | ||||
|             break | ||||
| @ -1446,8 +1664,10 @@ install_dotnet() { | ||||
|         return 1 | ||||
|     fi | ||||
|  | ||||
|     say "Extracting zip from $download_link" | ||||
|     extract_dotnet_package "$zip_path" "$install_root" || return 1 | ||||
|     remote_file_size="$(get_remote_file_size "$download_link")" | ||||
|  | ||||
|     say "Extracting archive from $download_link" | ||||
|     extract_dotnet_package "$zip_path" "$install_root" "$remote_file_size" || return 1 | ||||
|  | ||||
|     #  Check if the SDK version is installed; if not, fail the installation. | ||||
|     # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. | ||||
| @ -1597,25 +1817,42 @@ do | ||||
|             override_non_versioned_files=false | ||||
|             non_dynamic_parameters+=" $name" | ||||
|             ;; | ||||
|         --keep-zip|-[Kk]eep[Zz]ip) | ||||
|             keep_zip=true | ||||
|             non_dynamic_parameters+=" $name" | ||||
|             ;; | ||||
|         --zip-path|-[Zz]ip[Pp]ath) | ||||
|             shift | ||||
|             zip_path="$1" | ||||
|             ;; | ||||
|         -?|--?|-h|--help|-[Hh]elp) | ||||
|             script_name="$(basename "$0")" | ||||
|             script_name="dotnet-install.sh" | ||||
|             echo ".NET Tools Installer" | ||||
|             echo "Usage: $script_name [-c|--channel <CHANNEL>] [-v|--version <VERSION>] [-p|--prefix <DESTINATION>]" | ||||
|             echo "Usage:" | ||||
|             echo "       # Install a .NET SDK of a given Quality from a given Channel" | ||||
|             echo "       $script_name [-c|--channel <CHANNEL>] [-q|--quality <QUALITY>]" | ||||
|             echo "       # Install a .NET SDK of a specific public version" | ||||
|             echo "       $script_name [-v|--version <VERSION>]" | ||||
|             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\`." | ||||
|             echo "      -Channel" | ||||
|             echo "          Possible values:" | ||||
|             echo "          - Current - most current release" | ||||
|             echo "          - LTS - most current supported release" | ||||
|             echo "          - STS - the most recent Standard Term Support release" | ||||
|             echo "          - LTS - the most recent Long Term Support release" | ||||
|             echo "          - 2-part version in a format A.B - represents a specific release" | ||||
|             echo "              examples: 2.0; 1.0" | ||||
|             echo "          - 3-part version in a format A.B.Cxx - represents a specific SDK release" | ||||
|             echo "              examples: 5.0.1xx, 5.0.2xx." | ||||
|             echo "              Supported since 5.0 release" | ||||
|             echo "          Warning: Value 'Current' is deprecated for the Channel parameter. Use 'STS' instead." | ||||
|             echo "          Note: The version parameter overrides the channel parameter when any version other than 'latest' is used." | ||||
|             echo "  -v,--version <VERSION>         Use specific VERSION, Defaults to \`$version\`." | ||||
|             echo "      -Version" | ||||
| @ -1626,7 +1863,7 @@ do | ||||
|             echo "  -q,--quality <quality>         Download the latest build of specified quality in the channel." | ||||
|             echo "      -Quality" | ||||
|             echo "          The possible values are: daily, signed, validated, preview, GA." | ||||
|             echo "          Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used."  | ||||
|             echo "          Works only in combination with channel. Not applicable for STS and LTS channels and will be ignored if those channels are used."  | ||||
|             echo "          For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported."  | ||||
|             echo "          Supported since 5.0 release."  | ||||
|             echo "          Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality." | ||||
| @ -1637,7 +1874,7 @@ do | ||||
|             echo "      -InstallDir" | ||||
|             echo "  --architecture <ARCHITECTURE>      Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`." | ||||
|             echo "      --arch,-Architecture,-Arch" | ||||
|             echo "          Possible values: x64, arm, arm64 and s390x" | ||||
|             echo "          Possible values: x64, arm, arm64, s390x, ppc64le and loongarch64" | ||||
|             echo "  --os <system>                    Specifies operating system to be used when selecting the installer." | ||||
|             echo "          Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6." | ||||
|             echo "          In case any other value is provided, the platform will be determined by the script based on machine configuration." | ||||
| @ -1662,6 +1899,8 @@ do | ||||
|             echo "  --no-cdn,-NoCdn                    Disable downloading from the Azure CDN, and use the uncached feed directly." | ||||
|             echo "  --jsonfile <JSONFILE>              Determines the SDK version from a user specified global.json file." | ||||
|             echo "                                     Note: global.json must have a value for 'SDK:Version'" | ||||
|             echo "  --keep-zip,-KeepZip                If set, downloaded file is kept." | ||||
|             echo "  --zip-path, -ZipPath               If set, downloaded file is stored at the specified path." | ||||
|             echo "  -?,--?,-h,--help,-Help             Shows this help message" | ||||
|             echo "" | ||||
|             echo "Install Location:" | ||||
| @ -1680,10 +1919,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." | ||||
| @ -1716,5 +1955,5 @@ else | ||||
| fi | ||||
|  | ||||
| say "Note that the script does not resolve dependencies during installation." | ||||
| say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section." | ||||
| 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." | ||||
|  | ||||
							
								
								
									
										310
									
								
								src/authutil.ts
									
									
									
									
									
								
							
							
						
						
									
										310
									
								
								src/authutil.ts
									
									
									
									
									
								
							| @ -1,155 +1,155 @@ | ||||
| import * as fs from 'fs'; | ||||
| import * as path from 'path'; | ||||
| import * as core from '@actions/core'; | ||||
| import * as github from '@actions/github'; | ||||
| import * as xmlbuilder from 'xmlbuilder'; | ||||
| import * as xmlParser from 'fast-xml-parser'; | ||||
| import {ProcessEnvOptions} from 'child_process'; | ||||
|  | ||||
| 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 | ||||
| ) { | ||||
|   console.log( | ||||
|     `dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}` | ||||
|   ); | ||||
|   let xml: xmlbuilder.XMLElement; | ||||
|   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 || 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'); | ||||
|     var json = xmlParser.parse(curContents, {ignoreAttributes: false}); | ||||
|  | ||||
|     if (typeof json.configuration == 'undefined') { | ||||
|       throw new Error(`The provided NuGet.config seems invalid.`); | ||||
|     } | ||||
|     if (typeof json.configuration.packageSources != 'undefined') { | ||||
|       if (typeof json.configuration.packageSources.add != 'undefined') { | ||||
|         // file has at least one <add> | ||||
|         if (typeof json.configuration.packageSources.add[0] == 'undefined') { | ||||
|           // file has only one <add> | ||||
|           if ( | ||||
|             json.configuration.packageSources.add['@_value'] | ||||
|               .toLowerCase() | ||||
|               .includes(feedUrl.toLowerCase()) | ||||
|           ) { | ||||
|             let key = json.configuration.packageSources.add['@_key']; | ||||
|             sourceKeys.push(key); | ||||
|             core.debug(`Found a URL with key ${key}`); | ||||
|           } | ||||
|         } else { | ||||
|           // file has 2+ <add> | ||||
|           for ( | ||||
|             let i = 0; | ||||
|             i < json.configuration.packageSources.add.length; | ||||
|             i++ | ||||
|           ) { | ||||
|             const source = json.configuration.packageSources.add[i]; | ||||
|             const value = source['@_value']; | ||||
|             core.debug(`source '${value}'`); | ||||
|             if (value.toLowerCase().includes(feedUrl.toLowerCase())) { | ||||
|               let key = source['@_key']; | ||||
|               sourceKeys.push(key); | ||||
|               core.debug(`Found a URL with key ${key}`); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   xml = xmlbuilder | ||||
|     .create('configuration') | ||||
|     .ele('config') | ||||
|     .ele('add', {key: 'defaultPushSource', value: sourceUrl}) | ||||
|     .up() | ||||
|     .up(); | ||||
|  | ||||
|   if (sourceKeys.length == 0) { | ||||
|     let keystring = 'Source'; | ||||
|     xml = xml | ||||
|       .ele('packageSources') | ||||
|       .ele('add', {key: keystring, value: sourceUrl}) | ||||
|       .up() | ||||
|       .up(); | ||||
|     sourceKeys.push(keystring); | ||||
|   } | ||||
|   xml = xml.ele('packageSourceCredentials'); | ||||
|  | ||||
|   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." | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     xml = xml | ||||
|       .ele(key) | ||||
|       .ele('add', {key: 'Username', value: owner}) | ||||
|       .up() | ||||
|       .ele('add', { | ||||
|         key: 'ClearTextPassword', | ||||
|         value: process.env.NUGET_AUTH_TOKEN | ||||
|       }) | ||||
|       .up() | ||||
|       .up(); | ||||
|   }); | ||||
|  | ||||
|   // If NuGet fixes itself such that on Linux it can look for environment variables in the config file (it doesn't seem to work today), | ||||
|   // use this for the value above | ||||
|   //           process.platform == 'win32' | ||||
|   //             ? '%NUGET_AUTH_TOKEN%' | ||||
|   //             : '$NUGET_AUTH_TOKEN' | ||||
|  | ||||
|   var output = xml.end({pretty: true}); | ||||
|   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 * as xmlbuilder from 'xmlbuilder'; | ||||
| import * as xmlParser from 'fast-xml-parser'; | ||||
| import {ProcessEnvOptions} from 'child_process'; | ||||
|  | ||||
| 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 | ||||
| ) { | ||||
|   console.log( | ||||
|     `dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}` | ||||
|   ); | ||||
|   let xml: xmlbuilder.XMLElement; | ||||
|   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 || 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'); | ||||
|     var json = xmlParser.parse(curContents, {ignoreAttributes: false}); | ||||
|  | ||||
|     if (typeof json.configuration == 'undefined') { | ||||
|       throw new Error(`The provided NuGet.config seems invalid.`); | ||||
|     } | ||||
|     if (typeof json.configuration.packageSources != 'undefined') { | ||||
|       if (typeof json.configuration.packageSources.add != 'undefined') { | ||||
|         // file has at least one <add> | ||||
|         if (typeof json.configuration.packageSources.add[0] == 'undefined') { | ||||
|           // file has only one <add> | ||||
|           if ( | ||||
|             json.configuration.packageSources.add['@_value'] | ||||
|               .toLowerCase() | ||||
|               .includes(feedUrl.toLowerCase()) | ||||
|           ) { | ||||
|             let key = json.configuration.packageSources.add['@_key']; | ||||
|             sourceKeys.push(key); | ||||
|             core.debug(`Found a URL with key ${key}`); | ||||
|           } | ||||
|         } else { | ||||
|           // file has 2+ <add> | ||||
|           for ( | ||||
|             let i = 0; | ||||
|             i < json.configuration.packageSources.add.length; | ||||
|             i++ | ||||
|           ) { | ||||
|             const source = json.configuration.packageSources.add[i]; | ||||
|             const value = source['@_value']; | ||||
|             core.debug(`source '${value}'`); | ||||
|             if (value.toLowerCase().includes(feedUrl.toLowerCase())) { | ||||
|               let key = source['@_key']; | ||||
|               sourceKeys.push(key); | ||||
|               core.debug(`Found a URL with key ${key}`); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   xml = xmlbuilder | ||||
|     .create('configuration') | ||||
|     .ele('config') | ||||
|     .ele('add', {key: 'defaultPushSource', value: sourceUrl}) | ||||
|     .up() | ||||
|     .up(); | ||||
|  | ||||
|   if (sourceKeys.length == 0) { | ||||
|     let keystring = 'Source'; | ||||
|     xml = xml | ||||
|       .ele('packageSources') | ||||
|       .ele('add', {key: keystring, value: sourceUrl}) | ||||
|       .up() | ||||
|       .up(); | ||||
|     sourceKeys.push(keystring); | ||||
|   } | ||||
|   xml = xml.ele('packageSourceCredentials'); | ||||
|  | ||||
|   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." | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     xml = xml | ||||
|       .ele(key) | ||||
|       .ele('add', {key: 'Username', value: owner}) | ||||
|       .up() | ||||
|       .ele('add', { | ||||
|         key: 'ClearTextPassword', | ||||
|         value: process.env.NUGET_AUTH_TOKEN | ||||
|       }) | ||||
|       .up() | ||||
|       .up(); | ||||
|   }); | ||||
|  | ||||
|   // If NuGet fixes itself such that on Linux it can look for environment variables in the config file (it doesn't seem to work today), | ||||
|   // use this for the value above | ||||
|   //           process.platform == 'win32' | ||||
|   //             ? '%NUGET_AUTH_TOKEN%' | ||||
|   //             : '$NUGET_AUTH_TOKEN' | ||||
|  | ||||
|   var output = xml.end({pretty: true}); | ||||
|   fs.writeFileSync(tempFileLocation, output); | ||||
| } | ||||
|  | ||||
							
								
								
									
										609
									
								
								src/installer.ts
									
									
									
									
									
								
							
							
						
						
									
										609
									
								
								src/installer.ts
									
									
									
									
									
								
							| @ -1,304 +1,305 @@ | ||||
| // 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 hc = require('@actions/http-client'); | ||||
| import {chmodSync} from 'fs'; | ||||
| import * as path from 'path'; | ||||
| import {ExecOptions} from '@actions/exec/lib/interfaces'; | ||||
| import * as semver from 'semver'; | ||||
|  | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
|  | ||||
| /** | ||||
|  * Represents the inputted version information | ||||
|  */ | ||||
| export class DotNetVersionInfo { | ||||
|   public inputVersion: string; | ||||
|   private fullversion: string; | ||||
|   private isExactVersionSet: boolean = false; | ||||
|  | ||||
|   constructor(version: string) { | ||||
|     this.inputVersion = version; | ||||
|  | ||||
|     // Check for exact match | ||||
|     if (semver.valid(semver.clean(version) || '') != null) { | ||||
|       this.fullversion = semver.clean(version) as string; | ||||
|       this.isExactVersionSet = true; | ||||
|  | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const parts: string[] = version.split('.'); | ||||
|  | ||||
|     if (parts.length < 2 || parts.length > 3) this.throwInvalidVersionFormat(); | ||||
|  | ||||
|     if (parts.length == 3 && parts[2] !== 'x' && parts[2] !== '*') { | ||||
|       this.throwInvalidVersionFormat(); | ||||
|     } | ||||
|  | ||||
|     const major = this.getVersionNumberOrThrow(parts[0]); | ||||
|     const minor = ['x', '*'].includes(parts[1]) | ||||
|       ? parts[1] | ||||
|       : this.getVersionNumberOrThrow(parts[1]); | ||||
|  | ||||
|     this.fullversion = major + '.' + minor; | ||||
|   } | ||||
|  | ||||
|   private getVersionNumberOrThrow(input: string): number { | ||||
|     try { | ||||
|       if (!input || input.trim() === '') this.throwInvalidVersionFormat(); | ||||
|  | ||||
|       let number = Number(input); | ||||
|  | ||||
|       if (Number.isNaN(number) || number < 0) this.throwInvalidVersionFormat(); | ||||
|  | ||||
|       return number; | ||||
|     } catch { | ||||
|       this.throwInvalidVersionFormat(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private throwInvalidVersionFormat() { | ||||
|     throw new Error( | ||||
|       'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * If true exacatly one version should be resolved | ||||
|    */ | ||||
|   public isExactVersion(): boolean { | ||||
|     return this.isExactVersionSet; | ||||
|   } | ||||
|  | ||||
|   public version(): string { | ||||
|     return this.fullversion; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class DotnetCoreInstaller { | ||||
|   constructor(version: string, includePrerelease: boolean = false) { | ||||
|     this.version = version; | ||||
|     this.includePrerelease = includePrerelease; | ||||
|   } | ||||
|  | ||||
|   public async installDotnet() { | ||||
|     let output = ''; | ||||
|     let resultCode = 0; | ||||
|  | ||||
|     let calculatedVersion = await this.resolveVersion( | ||||
|       new DotNetVersionInfo(this.version) | ||||
|     ); | ||||
|  | ||||
|     var envVariables: {[key: string]: string} = {}; | ||||
|     for (let key in process.env) { | ||||
|       if (process.env[key]) { | ||||
|         let value: any = process.env[key]; | ||||
|         envVariables[key] = value; | ||||
|       } | ||||
|     } | ||||
|     if (IS_WINDOWS) { | ||||
|       let escapedScript = path | ||||
|         .join(__dirname, '..', 'externals', 'install-dotnet.ps1') | ||||
|         .replace(/'/g, "''"); | ||||
|       let command = `& '${escapedScript}'`; | ||||
|       if (calculatedVersion) { | ||||
|         command += ` -Version ${calculatedVersion}`; | ||||
|       } | ||||
|       if (process.env['https_proxy'] != null) { | ||||
|         command += ` -ProxyAddress ${process.env['https_proxy']}`; | ||||
|       } | ||||
|       // This is not currently an option | ||||
|       if (process.env['no_proxy'] != null) { | ||||
|         command += ` -ProxyBypassList ${process.env['no_proxy']}`; | ||||
|       } | ||||
|  | ||||
|       // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|       const powershellPath = | ||||
|         (await io.which('pwsh', false)) || (await io.which('powershell', true)); | ||||
|  | ||||
|       var options: ExecOptions = { | ||||
|         listeners: { | ||||
|           stdout: (data: Buffer) => { | ||||
|             output += data.toString(); | ||||
|           } | ||||
|         }, | ||||
|         env: envVariables | ||||
|       }; | ||||
|  | ||||
|       resultCode = await exec.exec( | ||||
|         `"${powershellPath}"`, | ||||
|         [ | ||||
|           '-NoLogo', | ||||
|           '-Sta', | ||||
|           '-NoProfile', | ||||
|           '-NonInteractive', | ||||
|           '-ExecutionPolicy', | ||||
|           'Unrestricted', | ||||
|           '-Command', | ||||
|           command | ||||
|         ], | ||||
|         options | ||||
|       ); | ||||
|     } else { | ||||
|       let escapedScript = path | ||||
|         .join(__dirname, '..', 'externals', 'install-dotnet.sh') | ||||
|         .replace(/'/g, "''"); | ||||
|       chmodSync(escapedScript, '777'); | ||||
|  | ||||
|       const scriptPath = await io.which(escapedScript, true); | ||||
|  | ||||
|       let scriptArguments: string[] = []; | ||||
|       if (calculatedVersion) { | ||||
|         scriptArguments.push('--version', calculatedVersion); | ||||
|       } | ||||
|  | ||||
|       // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|       resultCode = await exec.exec(`"${scriptPath}"`, scriptArguments, { | ||||
|         listeners: { | ||||
|           stdout: (data: Buffer) => { | ||||
|             output += data.toString(); | ||||
|           } | ||||
|         }, | ||||
|         env: envVariables | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     if (resultCode != 0) { | ||||
|       throw new Error(`Failed to install dotnet ${resultCode}. ${output}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static addToPath() { | ||||
|     if (process.env['DOTNET_INSTALL_DIR']) { | ||||
|       core.addPath(process.env['DOTNET_INSTALL_DIR']); | ||||
|       core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']); | ||||
|     } else { | ||||
|       if (IS_WINDOWS) { | ||||
|         // This is the default set in install-dotnet.ps1 | ||||
|         core.addPath( | ||||
|           path.join(process.env['LocalAppData'] + '', 'Microsoft', 'dotnet') | ||||
|         ); | ||||
|         core.exportVariable( | ||||
|           'DOTNET_ROOT', | ||||
|           path.join(process.env['LocalAppData'] + '', 'Microsoft', 'dotnet') | ||||
|         ); | ||||
|       } else { | ||||
|         // This is the default set in install-dotnet.sh | ||||
|         core.addPath(path.join(process.env['HOME'] + '', '.dotnet')); | ||||
|         core.exportVariable( | ||||
|           'DOTNET_ROOT', | ||||
|           path.join(process.env['HOME'] + '', '.dotnet') | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     console.log(process.env['PATH']); | ||||
|   } | ||||
|  | ||||
|   // versionInfo - versionInfo of the SDK/Runtime | ||||
|   async resolveVersion(versionInfo: DotNetVersionInfo): Promise<string> { | ||||
|     if (versionInfo.isExactVersion()) { | ||||
|       return versionInfo.version(); | ||||
|     } | ||||
|  | ||||
|     const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|       allowRetries: true, | ||||
|       maxRetries: 3 | ||||
|     }); | ||||
|  | ||||
|     const releasesJsonUrl: string = await this.getReleasesJsonUrl( | ||||
|       httpClient, | ||||
|       versionInfo.version().split('.') | ||||
|     ); | ||||
|  | ||||
|     const releasesResponse = await httpClient.getJson<any>(releasesJsonUrl); | ||||
|     const releasesResult = releasesResponse.result || {}; | ||||
|     let releasesInfo: any[] = releasesResult['releases']; | ||||
|     releasesInfo = releasesInfo.filter((releaseInfo: any) => { | ||||
|       return ( | ||||
|         semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version(), { | ||||
|           includePrerelease: this.includePrerelease | ||||
|         }) || | ||||
|         semver.satisfies( | ||||
|           releaseInfo['sdk']['version-display'], | ||||
|           versionInfo.version(), | ||||
|           { | ||||
|             includePrerelease: this.includePrerelease | ||||
|           } | ||||
|         ) | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     // Exclude versions that are newer than the latest if using not exact | ||||
|     let latestSdk: string = releasesResult['latest-sdk']; | ||||
|  | ||||
|     releasesInfo = releasesInfo.filter((releaseInfo: any) => | ||||
|       semver.lte(releaseInfo['sdk']['version'], latestSdk, { | ||||
|         includePrerelease: this.includePrerelease | ||||
|       }) | ||||
|     ); | ||||
|  | ||||
|     // Sort for latest version | ||||
|     releasesInfo = releasesInfo.sort((a, b) => | ||||
|       semver.rcompare(a['sdk']['version'], b['sdk']['version'], { | ||||
|         includePrerelease: this.includePrerelease | ||||
|       }) | ||||
|     ); | ||||
|  | ||||
|     if (releasesInfo.length == 0) { | ||||
|       throw new Error( | ||||
|         `Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     let release = releasesInfo[0]; | ||||
|     return release['sdk']['version']; | ||||
|   } | ||||
|  | ||||
|   private async getReleasesJsonUrl( | ||||
|     httpClient: hc.HttpClient, | ||||
|     versionParts: string[] | ||||
|   ): Promise<string> { | ||||
|     const response = await httpClient.getJson<any>(DotNetCoreIndexUrl); | ||||
|     const result = response.result || {}; | ||||
|     let releasesInfo: any[] = result['releases-index']; | ||||
|  | ||||
|     releasesInfo = releasesInfo.filter((info: any) => { | ||||
|       // channel-version is the first 2 elements of the version (e.g. 2.1), filter out versions that don't match 2.1.x. | ||||
|       const sdkParts: string[] = info['channel-version'].split('.'); | ||||
|       if ( | ||||
|         versionParts.length >= 2 && | ||||
|         !(versionParts[1] == 'x' || versionParts[1] == '*') | ||||
|       ) { | ||||
|         return versionParts[0] == sdkParts[0] && versionParts[1] == sdkParts[1]; | ||||
|       } | ||||
|       return versionParts[0] == sdkParts[0]; | ||||
|     }); | ||||
|  | ||||
|     if (releasesInfo.length === 0) { | ||||
|       throw new Error( | ||||
|         `Could not find info for version ${versionParts.join( | ||||
|           '.' | ||||
|         )} at ${DotNetCoreIndexUrl}` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     const releaseInfo = releasesInfo[0]; | ||||
|     if (releaseInfo['support-phase'] === 'eol') { | ||||
|       core.warning( | ||||
|         `${releaseInfo['product']} ${releaseInfo['channel-version']} is no longer supported and will not receive security updates in the future. Please refer to https://aka.ms/dotnet-core-support for more information about the .NET support policy.` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return releaseInfo['releases.json']; | ||||
|   } | ||||
|  | ||||
|   private version: string; | ||||
|   private includePrerelease: boolean; | ||||
| } | ||||
|  | ||||
| const DotNetCoreIndexUrl: string = | ||||
|   'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json'; | ||||
| // 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 hc = require('@actions/http-client'); | ||||
| import {chmodSync} from 'fs'; | ||||
| import * as path from 'path'; | ||||
| import {ExecOptions} from '@actions/exec/lib/interfaces'; | ||||
| import * as semver from 'semver'; | ||||
|  | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
|  | ||||
| /** | ||||
|  * Represents the inputted version information | ||||
|  */ | ||||
| export class DotNetVersionInfo { | ||||
|   public inputVersion: string; | ||||
|   private fullversion: string; | ||||
|   private isExactVersionSet: boolean = false; | ||||
|  | ||||
|   constructor(version: string) { | ||||
|     this.inputVersion = version; | ||||
|  | ||||
|     // Check for exact match | ||||
|     if (semver.valid(semver.clean(version) || '') != null) { | ||||
|       this.fullversion = semver.clean(version) as string; | ||||
|       this.isExactVersionSet = true; | ||||
|  | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const parts: string[] = version.split('.'); | ||||
|  | ||||
|     if (parts.length < 2 || parts.length > 3) this.throwInvalidVersionFormat(); | ||||
|  | ||||
|     if (parts.length == 3 && parts[2] !== 'x' && parts[2] !== '*') { | ||||
|       this.throwInvalidVersionFormat(); | ||||
|     } | ||||
|  | ||||
|     const major = this.getVersionNumberOrThrow(parts[0]); | ||||
|     const minor = ['x', '*'].includes(parts[1]) | ||||
|       ? parts[1] | ||||
|       : this.getVersionNumberOrThrow(parts[1]); | ||||
|  | ||||
|     this.fullversion = major + '.' + minor; | ||||
|   } | ||||
|  | ||||
|   private getVersionNumberOrThrow(input: string): number { | ||||
|     try { | ||||
|       if (!input || input.trim() === '') this.throwInvalidVersionFormat(); | ||||
|  | ||||
|       let number = Number(input); | ||||
|  | ||||
|       if (Number.isNaN(number) || number < 0) this.throwInvalidVersionFormat(); | ||||
|  | ||||
|       return number; | ||||
|     } catch { | ||||
|       this.throwInvalidVersionFormat(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private throwInvalidVersionFormat() { | ||||
|     throw new Error( | ||||
|       'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * If true exacatly one version should be resolved | ||||
|    */ | ||||
|   public isExactVersion(): boolean { | ||||
|     return this.isExactVersionSet; | ||||
|   } | ||||
|  | ||||
|   public version(): string { | ||||
|     return this.fullversion; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class DotnetCoreInstaller { | ||||
|   constructor(version: string, includePrerelease: boolean = false) { | ||||
|     this.version = version; | ||||
|     this.includePrerelease = includePrerelease; | ||||
|   } | ||||
|  | ||||
|   public async installDotnet() { | ||||
|     let output = ''; | ||||
|     let resultCode = 0; | ||||
|  | ||||
|     let calculatedVersion = await this.resolveVersion( | ||||
|       new DotNetVersionInfo(this.version) | ||||
|     ); | ||||
|  | ||||
|     var envVariables: {[key: string]: string} = {}; | ||||
|     for (let key in process.env) { | ||||
|       if (process.env[key]) { | ||||
|         let value: any = process.env[key]; | ||||
|         envVariables[key] = value; | ||||
|       } | ||||
|     } | ||||
|     if (IS_WINDOWS) { | ||||
|       let escapedScript = path | ||||
|         .join(__dirname, '..', 'externals', 'install-dotnet.ps1') | ||||
|         .replace(/'/g, "''"); | ||||
|       let command = `& '${escapedScript}'`; | ||||
|       if (calculatedVersion) { | ||||
|         command += ` -Version ${calculatedVersion}`; | ||||
|       } | ||||
|       if (process.env['https_proxy'] != null) { | ||||
|         command += ` -ProxyAddress ${process.env['https_proxy']}`; | ||||
|       } | ||||
|       // This is not currently an option | ||||
|       if (process.env['no_proxy'] != null) { | ||||
|         command += ` -ProxyBypassList ${process.env['no_proxy']}`; | ||||
|       } | ||||
|  | ||||
|       // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|       const powershellPath = | ||||
|         (await io.which('pwsh', false)) || (await io.which('powershell', true)); | ||||
|  | ||||
|       var options: ExecOptions = { | ||||
|         listeners: { | ||||
|           stdout: (data: Buffer) => { | ||||
|             output += data.toString(); | ||||
|           } | ||||
|         }, | ||||
|         env: envVariables | ||||
|       }; | ||||
|  | ||||
|       resultCode = await exec.exec( | ||||
|         `"${powershellPath}"`, | ||||
|         [ | ||||
|           '-NoLogo', | ||||
|           '-Sta', | ||||
|           '-NoProfile', | ||||
|           '-NonInteractive', | ||||
|           '-ExecutionPolicy', | ||||
|           'Unrestricted', | ||||
|           '-Command', | ||||
|           command | ||||
|         ], | ||||
|         options | ||||
|       ); | ||||
|     } else { | ||||
|       let escapedScript = path | ||||
|         .join(__dirname, '..', 'externals', 'install-dotnet.sh') | ||||
|         .replace(/'/g, "''"); | ||||
|       chmodSync(escapedScript, '777'); | ||||
|  | ||||
|       const scriptPath = await io.which(escapedScript, true); | ||||
|  | ||||
|       let scriptArguments: string[] = []; | ||||
|       if (calculatedVersion) { | ||||
|         scriptArguments.push('--version', calculatedVersion); | ||||
|       } | ||||
|  | ||||
|       // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used | ||||
|       resultCode = await exec.exec(`"${scriptPath}"`, scriptArguments, { | ||||
|         listeners: { | ||||
|           stdout: (data: Buffer) => { | ||||
|             output += data.toString(); | ||||
|           } | ||||
|         }, | ||||
|         env: envVariables | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     if (resultCode != 0) { | ||||
|       throw new Error(`Failed to install dotnet ${resultCode}. ${output}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static addToPath() { | ||||
|     if (process.env['DOTNET_INSTALL_DIR']) { | ||||
|       core.addPath(process.env['DOTNET_INSTALL_DIR']); | ||||
|       core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']); | ||||
|     } else { | ||||
|       if (IS_WINDOWS) { | ||||
|         // This is the default set in install-dotnet.ps1 | ||||
|         core.addPath( | ||||
|           path.join(process.env['LocalAppData'] + '', 'Microsoft', 'dotnet') | ||||
|         ); | ||||
|         core.exportVariable( | ||||
|           'DOTNET_ROOT', | ||||
|           path.join(process.env['LocalAppData'] + '', 'Microsoft', 'dotnet') | ||||
|         ); | ||||
|       } else { | ||||
|         // This is the default set in install-dotnet.sh | ||||
|         core.addPath(path.join(process.env['HOME'] + '', '.dotnet')); | ||||
|         core.exportVariable( | ||||
|           'DOTNET_ROOT', | ||||
|           path.join(process.env['HOME'] + '', '.dotnet') | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     console.log(process.env['PATH']); | ||||
|   } | ||||
|  | ||||
|   // versionInfo - versionInfo of the SDK/Runtime | ||||
|   async resolveVersion(versionInfo: DotNetVersionInfo): Promise<string> { | ||||
|     if (versionInfo.isExactVersion()) { | ||||
|       return versionInfo.version(); | ||||
|     } | ||||
|  | ||||
|     const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { | ||||
|       allowRetries: true, | ||||
|       maxRetries: 3 | ||||
|     }); | ||||
|  | ||||
|     const releasesJsonUrl: string = await this.getReleasesJsonUrl( | ||||
|       httpClient, | ||||
|       versionInfo.version().split('.') | ||||
|     ); | ||||
|  | ||||
|     const releasesResponse = await httpClient.getJson<any>(releasesJsonUrl); | ||||
|     const releasesResult = releasesResponse.result || {}; | ||||
|     let releasesInfo: any[] = releasesResult['releases']; | ||||
|     releasesInfo = releasesInfo.filter((releaseInfo: any) => { | ||||
|       return ( | ||||
|         semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version(), { | ||||
|           includePrerelease: this.includePrerelease | ||||
|         }) || | ||||
|         semver.satisfies( | ||||
|           releaseInfo['sdk']['version-display'], | ||||
|           versionInfo.version(), | ||||
|           { | ||||
|             includePrerelease: this.includePrerelease | ||||
|           } | ||||
|         ) | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     // Exclude versions that are newer than the latest if using not exact | ||||
|     let latestSdk: string = releasesResult['latest-sdk']; | ||||
|  | ||||
|     releasesInfo = releasesInfo.filter((releaseInfo: any) => | ||||
|       semver.lte(releaseInfo['sdk']['version'], latestSdk, { | ||||
|         includePrerelease: this.includePrerelease | ||||
|       }) | ||||
|     ); | ||||
|  | ||||
|     // Sort for latest version | ||||
|     releasesInfo = releasesInfo.sort((a, b) => | ||||
|       semver.rcompare(a['sdk']['version'], b['sdk']['version'], { | ||||
|         includePrerelease: this.includePrerelease | ||||
|       }) | ||||
|     ); | ||||
|  | ||||
|     if (releasesInfo.length == 0) { | ||||
|       throw new Error( | ||||
|         `Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     let release = releasesInfo[0]; | ||||
|     return release['sdk']['version']; | ||||
|   } | ||||
|  | ||||
|   private async getReleasesJsonUrl( | ||||
|     httpClient: hc.HttpClient, | ||||
|     versionParts: string[] | ||||
|   ): Promise<string> { | ||||
|     const response = await httpClient.getJson<any>(DotNetCoreIndexUrl); | ||||
|  | ||||
|     const result = response.result || {}; | ||||
|     let releasesInfo: any[] = result['releases-index']; | ||||
|  | ||||
|     releasesInfo = releasesInfo.filter((info: any) => { | ||||
|       // channel-version is the first 2 elements of the version (e.g. 2.1), filter out versions that don't match 2.1.x. | ||||
|       const sdkParts: string[] = info['channel-version'].split('.'); | ||||
|       if ( | ||||
|         versionParts.length >= 2 && | ||||
|         !(versionParts[1] == 'x' || versionParts[1] == '*') | ||||
|       ) { | ||||
|         return versionParts[0] == sdkParts[0] && versionParts[1] == sdkParts[1]; | ||||
|       } | ||||
|       return versionParts[0] == sdkParts[0]; | ||||
|     }); | ||||
|  | ||||
|     if (releasesInfo.length === 0) { | ||||
|       throw new Error( | ||||
|         `Could not find info for version ${versionParts.join( | ||||
|           '.' | ||||
|         )} at ${DotNetCoreIndexUrl}` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     const releaseInfo = releasesInfo[0]; | ||||
|     if (releaseInfo['support-phase'] === 'eol') { | ||||
|       core.warning( | ||||
|         `${releaseInfo['product']} ${releaseInfo['channel-version']} is no longer supported and will not receive security updates in the future. Please refer to https://aka.ms/dotnet-core-support for more information about the .NET support policy.` | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return releaseInfo['releases.json']; | ||||
|   } | ||||
|  | ||||
|   private version: string; | ||||
|   private includePrerelease: boolean; | ||||
| } | ||||
|  | ||||
| const DotNetCoreIndexUrl: string = | ||||
|   'https://builds.dotnet.microsoft.com/dotnet/release-metadata/releases-index.json'; | ||||
|  | ||||
| @ -38,9 +38,8 @@ export async function run() { | ||||
|     } | ||||
|  | ||||
|     if (versions.length) { | ||||
|       const includePrerelease: boolean = core.getBooleanInput( | ||||
|         'include-prerelease' | ||||
|       ); | ||||
|       const includePrerelease: boolean = | ||||
|         core.getBooleanInput('include-prerelease'); | ||||
|       let dotnetInstaller!: installer.DotnetCoreInstaller; | ||||
|       for (const version of new Set<string>(versions)) { | ||||
|         dotnetInstaller = new installer.DotnetCoreInstaller( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	