Compare commits

...

107 Commits

Author SHA1 Message Date
ba848a34bb Update configuration files 2023-05-24 09:54:37 +00:00
aa983c550d Merge pull request #428 from akv-platform/add-latest-patch-syntax
Add latest patch syntax
2023-05-24 08:41:30 +02:00
b891376106 Merge branch 'main' into add-latest-patch-syntax 2023-05-22 12:29:35 +02:00
b05a3f26b3 Fix review points, rebuild solution 2023-05-22 12:27:33 +02:00
5fdecd2063 Increase amount of retries for Dotnet installation scripts tests (#427)
* Increase amount of retries for Dotnet installation scripts tests

* Format: Increase amount of retries for Dotnet installation scripts tests
2023-05-19 08:50:54 +02:00
38b49fb717 Fix informational and debug messages 2023-05-18 12:39:22 +02:00
3cf3e230c1 Fix informational message 2023-05-18 12:01:55 +02:00
83a1653fa3 Update regular expresiion for isLatestPatchSyntax() 2023-05-18 11:46:40 +02:00
898aa0ce4d Fix typo 2023-05-18 11:16:22 +02:00
2f028bc044 update unit and e2e tests 2023-05-18 11:11:51 +02:00
21cf89aa73 Remove commented lines of code 2023-05-16 15:13:18 +02:00
fefaa59d2e update logic of outputting dotnet-version 2023-05-16 14:58:18 +02:00
e8501859aa Update unit tests 2023-05-15 14:24:28 +02:00
426d75d071 Merge branch 'main' into add-latest-patch-syntax 2023-05-15 14:13:18 +02:00
0f534f5829 Refactor and update unit-tests (#418)
* Update unit-tests and dotnet-install scripts
2023-05-15 14:09:29 +02:00
fbdbede901 Rebuild solution 2023-05-15 11:49:01 +02:00
0bc43909e0 Update mechanic of outputting installed dotnet version 2023-05-15 11:45:07 +02:00
c5a57b219c Update unit-tests 2023-05-05 10:44:54 +02:00
6adeb768ce Update docs 2023-05-05 10:43:09 +02:00
f425be78f5 Merge branch 'main' of https://github.com/actions/setup-dotnet
into add-latest-patch-syntax
2023-04-20 14:47:10 +02:00
fc8786b149 Update e2e tests and e2e test infrastructure (#414) 2023-04-20 14:42:27 +02:00
7d08dc7593 Add e2e test 2023-04-19 16:48:00 +02:00
e0a32d6459 Merge branch 'update-e2e-tests' into add-latest-patch-syntax 2023-04-19 16:43:50 +02:00
255362be61 Silent dotnet new globaljson command 2023-04-19 16:32:38 +02:00
50b46b3b1d Update verify-dotnet.ps1 2023-04-19 16:24:27 +02:00
e8ac21d503 Fix typos 2023-04-19 15:41:28 +02:00
a79ce57e6b Update contribution documentation 2023-04-19 13:43:18 +02:00
180a15970f Update e2e test infrastructure 2023-04-19 13:40:44 +02:00
b72f430d36 Update e2e tests 2023-04-13 17:28:59 +02:00
559e47b01b Fix review points 2023-04-13 10:33:52 +02:00
7358a44590 Update unit tests 2023-04-12 16:44:51 +02:00
34c30d0e81 Refactor logic 2023-04-12 15:44:03 +02:00
5f570676c2 Add unit and e2e tests 2023-04-11 15:53:11 +02:00
aa34a3ceaa Fix typo 2023-04-11 13:44:35 +02:00
12f70884d7 Fix typo 2023-04-11 13:23:50 +02:00
0318091611 Update resolveVersionInput() 2023-04-11 13:20:34 +02:00
f199d27aa1 Update solution 2023-04-10 16:58:35 +02:00
660c25a321 Rebuild action 2023-04-10 16:24:56 +02:00
4f6b2f576a Change the logic to support A.B.Cxx 2023-04-10 16:23:29 +02:00
920b830bd1 Fix glob bug in package.json scripts section (#401) 2023-04-03 12:48:02 +02:00
abdd14ee80 Update configuration files (#398)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-03-15 00:33:04 +01:00
1d9f0dad5b Accept absolute paths for 'global-json-file' input (#396) 2023-03-13 12:34:56 +01:00
2699274f6e Merge pull request #395 from akv-platform/reusable-config
Turn on automatic update of ESLint and Prettier configuration files
2023-03-10 10:56:01 +01:00
ca579e0fb2 Add update-config-files.yml 2023-03-09 13:47:42 +01:00
c82240598b Add and configure ESLint and update configuration for Prettier (#391)
* Apply ESLint config and update Prettier

* Update dependencies and rebuild

* Update docs
2023-03-09 14:43:05 +02:00
926f442022 Update workflow badges (#377) 2023-01-31 10:29:16 +02:00
c41fd15071 Merge pull request #375 from akv-platform/fix-error-message
Make throwing errors in setup-dotnet more informative
2023-01-30 14:21:47 +01:00
0c8652569e Fix error message 2023-01-26 10:35:10 +01:00
3cf27f13bb Update release-new-action-version.yml (#368) 2023-01-12 13:51:42 +01:00
ae8edb8fff Bump json5 from 2.1.3 to 2.2.3 (#365)
Bumps [json5](https://github.com/json5/json5) from 2.1.3 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.1.3...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-06 17:20:11 +01:00
82b2b40816 Merge pull request #364 from actions/update-team-setup-actions-team
Update CODEOWNERS
2022-12-27 09:27:52 +01:00
fe74f1cb7d Update CODEOWNERS 2022-12-26 09:40:40 +01:00
e94d154672 Merge pull request #362 from actions/update-codeowners
Update CODEOWNERS
2022-12-23 08:22:22 +01:00
fe67d2f8db update team 2022-12-22 23:59:20 +01:00
023f7252a0 Update CODEOWNERS 2022-12-22 23:56:54 +01:00
3f3ad54c0c Merge pull request #358 from akv-platform/apply-reusable-workflows
Update action to use reusable workflows
2022-12-22 10:50:01 +02:00
e1d35446fe Merge pull request #359 from akv-platform/documentation-clarification
Update README.md to clarify installation process
2022-12-21 16:17:31 +02:00
11aaa65761 Fix review points 2022-12-20 16:24:40 +01:00
13abe4777e Update step id naming 2022-12-20 15:31:56 +01:00
aefe5b483f Update README.md 2022-12-20 15:03:30 +01:00
047f06d086 Update README.md 2022-12-20 14:58:50 +01:00
014f8c8384 Add part about temporary global.json files 2022-12-20 12:30:05 +01:00
80c862dc38 Update README.md 2022-12-20 11:45:22 +01:00
e21107efbd Update README.md 2022-12-20 11:41:39 +01:00
d8f1ab14a7 Add link to global.json documentation ti README.md 2022-12-20 11:38:26 +01:00
f05a62b1cc Update README.md to clarify installation process 2022-12-20 11:36:11 +01:00
338d4e3bbf Update workflows to use reusable-workflows repo 2022-12-19 12:38:18 +01:00
228eec3014 Add links to reusable workflows 2022-12-15 16:24:18 +01:00
c483e03222 Update workflows 2022-12-15 16:16:00 +01:00
a35f420124 Update to use public version of reusable workflows 2022-12-14 13:20:26 +01:00
4214866121 Update workflows 2022-12-14 12:24:48 +01:00
0681939502 Update workflow.yml 2022-12-14 11:56:17 +01:00
ba8a1f9c02 Update action to use reusable workflows 2022-12-14 11:41:18 +01:00
09d024bd02 Merge pull request #348 from JamesMGreene/patch-1
Update to latest `actions/publish-action`
2022-11-23 11:20:14 +02:00
8ae4c080ce Update to latest actions/publish-action
To avoid Actions core deprecation messages.

https://github.com/actions/publish-action/releases/tag/v0.2.1
2022-11-22 10:50:00 -06:00
c5d2f92cae Bump minimatch from 3.0.4 to 3.1.2 (#347) 2022-11-18 15:49:29 +01:00
069c35efca Merge pull request #344 from teo-tsirpanis/patch-1
Notify if `global.json` was not found in the root directory.
2022-11-17 10:03:58 +01:00
6a2cd88ae8 Address PR feedback; update the installers and the Squid image. 2022-11-15 18:00:14 +02:00
9984a6fa87 Warn if the action ends up not installing any .NET version. 2022-11-14 19:58:22 +02:00
607fce577a Update the link to the .Net Core releases index file (#337) 2022-10-24 14:21:32 +02:00
501b34e8f6 Merge pull request #336 from e-korolevskii/refactor-nuget-cfg-parse
Refactor nuget cfg parse
2022-10-24 09:42:37 +02:00
b827fcce4d build 2022-10-13 16:35:12 +02:00
239baf3c5b Merge branch 'main' into refactor-nuget-cfg-parse 2022-10-13 16:34:35 +02:00
4d4a70f4a5 Add logic for processing DOTNET_INSTALL_DIR environment variable (#332) 2022-10-10 14:27:29 +02:00
70c3f4d098 Merge pull request #331 from rentziass/rentziass/update-actions-core
Update @actions/core to 1.10.0
2022-10-07 13:50:06 +02:00
9260643816 Update @actions/core to 1.10.0 2022-10-06 12:05:06 +01:00
6d6c7c9313 build 2022-09-30 09:33:54 +02:00
e753bbf2ff remove build 2022-09-30 09:33:03 +02:00
629365b26e Merge branch 'main' into refactor-nuget-cfg-parse 2022-09-30 09:28:48 +02:00
942a0bea39 format 2022-09-30 02:37:51 +02:00
26db2473e9 fix empty first line 2022-09-30 02:23:20 +02:00
467621733d add packageSourceCredentials 2022-09-30 02:19:09 +02:00
bdd38d13dc build 2022-09-30 02:05:09 +02:00
1d2f8f9eaf add debug log 2022-09-30 02:04:34 +02:00
354d280fa3 refactor using of XMLBuilder 2022-09-30 01:54:05 +02:00
0ff311b0d0 add nested dep license 2022-09-29 18:43:45 +02:00
bc65ba63d3 update license 2022-09-29 18:30:09 +02:00
90642fa8c5 update parser to v4 2022-09-29 18:23:42 +02:00
cf081e76a2 merge main & build 2022-09-28 15:30:56 +02:00
4ca3c96b49 Merge branch 'main' into refactor-nuget-cfg-parse 2022-09-28 09:30:07 +02:00
cc76dfab99 format 2022-09-21 15:14:01 +03:00
1f2c90bb99 Merge branch 'main' into refactor-nuget-cfg-parse 2022-09-19 20:18:40 +02:00
251997c37d updated installers 2022-09-19 20:18:24 +02:00
5d7bc0454b update installers 2022-08-31 16:01:51 +02:00
792e988dae build 2022-08-31 15:43:30 +02:00
e5034212c9 refactoring 2022-08-31 15:41:59 +02:00
0997db20d4 refactoring 2022-08-31 15:41:46 +02:00
47 changed files with 6140 additions and 8205 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!src/

51
.eslintrc.js Normal file
View File

@ -0,0 +1,51 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-plugin-jest/recommended',
'eslint-config-prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
rules: {
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
'yoda': 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}],
'node/no-extraneous-import': 'error'
},
overrides: [
{
files: ['**/*{test,spec}.ts'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off',
}
}
],
env: {
node: true,
es6: true,
'jest/globals': true
}
};

3
.gitattributes vendored
View File

@ -1 +1,2 @@
.licenses/** -diff linguist-generated=true * text=auto eol=lf
.licenses/** -diff linguist-generated=true

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @actions/virtual-environments-owners * @actions/setup-actions-team

17
.github/workflows/basic-validation.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Basic validation
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- main
- releases/*
paths-ignore:
- '**.md'
jobs:
call-basic-validation:
name: Basic validation
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main

View File

@ -1,8 +1,3 @@
# `dist/index.js` is a special file in Actions.
# When you reference an action with `uses:` in a workflow,
# `index.js` is the code that will run.
# For our project, we generate this file through a build process from other source files.
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
name: Check dist/ name: Check dist/
on: on:
@ -17,36 +12,6 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
check-dist: call-check-dist:
runs-on: ubuntu-latest name: Check dist/
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
steps:
- uses: actions/checkout@v3
- name: Set Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16.x
cache: npm
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Rebuild the dist/ directory
run: npm run build
- name: Compare the expected and actual dist/ directories
run: |
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff
exit 1
fi
id: diff
# If index.js was different than expected, upload the expected version as an artifact
- uses: actions/upload-artifact@v3
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
path: dist/

View File

@ -1,70 +1,14 @@
# For most projects, this workflow file will not need changing; you simply need name: CodeQL analysis
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on: on:
push: push:
branches: [ main ] branches: [main]
pull_request: pull_request:
# The branches below must be a subset of the branches above branches: [main]
branches: [ main ]
schedule: schedule:
- cron: '23 19 * * 0' - cron: '0 3 * * 0'
jobs: jobs:
analyze: call-codeQL-analysis:
name: Analyze name: CodeQL analysis
runs-on: ubuntu-latest uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -1,4 +1,4 @@
name: Main workflow name: e2e tests
on: on:
pull_request: pull_request:
@ -12,28 +12,6 @@ on:
- '**.md' - '**.md'
jobs: jobs:
build:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16.x
cache: npm
- run: npm ci --ignore-scripts
- run: npm run build
- run: npm run format-check
- run: npm test
- name: Verify no unstaged changes
if: runner.os != 'windows'
run: __tests__/verify-no-unstaged-changes.sh
test-setup-multiple-versions: test-setup-multiple-versions:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
strategy: strategy:
@ -46,7 +24,7 @@ jobs:
- name: Clear toolcache - name: Clear toolcache
shell: pwsh shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }} run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 2.2.402 and 3.1.404 - name: Setup dotnet 2.2.402, 3.1.404 and 3.0.x
uses: ./ uses: ./
with: with:
dotnet-version: | dotnet-version: |
@ -55,7 +33,7 @@ jobs:
3.0.x 3.0.x
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 2.2.402 3.1.404 '3.0' run: __tests__/verify-dotnet.ps1 -Patterns "^2.2.402$", "^3.1.404$", "^3.0"
test-setup-full-version: test-setup-full-version:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -82,13 +60,9 @@ jobs:
source-url: https://api.nuget.org/v3/index.json source-url: https://api.nuget.org/v3/index.json
env: env:
NUGET_AUTH_TOKEN: NOTATOKEN NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify nuget config file
shell: pwsh
run: |
if (-Not (Test-Path "../nuget.config")) { throw "nuget file not generated correctly" }
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1.201 2.2.402 run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$", "^2.2.402$" -CheckNugetConfig
test-setup-without-patch-version: test-setup-without-patch-version:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -113,7 +87,7 @@ jobs:
dotnet-version: '2.2' dotnet-version: '2.2'
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1 2.2 run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2"
test-setup-prerelease-version: test-setup-prerelease-version:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -127,17 +101,13 @@ jobs:
- name: Clear toolcache - name: Clear toolcache
shell: pwsh shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }} run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet '2.2'
uses: ./
with:
dotnet-version: '2.2'
- name: Setup dotnet '3.1.100-preview1-014459' - name: Setup dotnet '3.1.100-preview1-014459'
uses: ./ uses: ./
with: with:
dotnet-version: '3.1.100-preview1-014459' dotnet-version: '3.1.100-preview1-014459'
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1.100-preview1-014459 run: __tests__/verify-dotnet.ps1 -Patterns "3.1.100-preview1-014459"
test-setup-latest-patch-version: test-setup-latest-patch-version:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -161,7 +131,28 @@ jobs:
dotnet-version: 2.2.X dotnet-version: 2.2.X
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 '2.2' '3.1' run: __tests__/verify-dotnet.ps1 -Patterns "^2.2", "^3.1"
test-ABCxx-syntax:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.4xx
uses: ./
with:
dotnet-version: '6.0.4xx'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^6\.0\.4\d{2}"
test-setup-with-wildcard: test-setup-with-wildcard:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -185,7 +176,7 @@ jobs:
dotnet-version: 2.2.* dotnet-version: 2.2.*
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1 2.2 run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2"
test-setup-global-json-specified-and-version: test-setup-global-json-specified-and-version:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
@ -203,7 +194,7 @@ jobs:
shell: bash shell: bash
run: | run: |
mkdir subdirectory mkdir subdirectory
echo '{"sdk":{"version": "2.2","rollForward": "latestFeature"}}' > ./subdirectory/global.json echo '{"sdk":{"version": "2.2.207","rollForward": "latestFeature"}}' > ./subdirectory/global.json
- name: Setup dotnet - name: Setup dotnet
uses: ./ uses: ./
with: with:
@ -211,95 +202,117 @@ jobs:
global-json-file: ./subdirectory/global.json global-json-file: ./subdirectory/global.json
- name: Verify dotnet - name: Verify dotnet
shell: pwsh shell: pwsh
run: __tests__/verify-dotnet.ps1 2.2 3.1 run: __tests__/verify-dotnet.ps1 -Patterns "^2.2", "^3.1"
test-setup-global-json-only:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Write global.json
shell: bash
run: |
mkdir subdirectory
echo '{"sdk":{"version": "2.2.207","rollForward": "latestFeature"}}' > ./subdirectory/global.json
- name: Setup dotnet
uses: ./
with:
global-json-file: ./subdirectory/global.json
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^2.2"
test-setup-with-dotnet-quality: test-setup-with-dotnet-quality:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest] operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Clear toolcache - name: Clear toolcache
shell: pwsh shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }} run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 7.0 with preview quality - name: Setup dotnet 7.0 with preview quality
uses: ./ uses: ./
with: with:
dotnet-version: "7.0" dotnet-version: '7.0'
dotnet-quality: "preview" dotnet-quality: 'preview'
- name: Verify preview version - name: Verify dotnet
shell: pwsh shell: pwsh
run: | run: __tests__/verify-dotnet.ps1 -Patterns "^7\.0\.\d+-"
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version.Contains("preview") -or $version.Contains("rc"))) { throw "Unexpected version" }
test-dotnet-version-output-during-single-version-installation: test-dotnet-version-output-during-single-version-installation:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest] operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Clear toolcache - name: Clear toolcache
shell: pwsh shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }} run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.401 - name: Setup dotnet 6.0.401
uses: ./ uses: ./
id: step1 id: step1
with: with:
dotnet-version: "6.0.401" dotnet-version: '6.0.401'
- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" }
- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" }
test-dotnet-version-output-during-multiple-version-installation: test-dotnet-version-output-during-multiple-version-installation:
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.operating-system }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest] operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Clear toolcache - name: Clear toolcache
shell: pwsh shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }} run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12
uses: ./
id: step2
with:
dotnet-version: |
7.0.100-rc.1.22431.12
6.0.401
5.0.408
- name: Verify value of the dotnet-version output - name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12
shell: pwsh uses: ./
run: | id: step2
$version = "7.0.100-rc.1.22431.12" with:
if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" } dotnet-version: |
7.0.100-rc.1.22431.12
6.0.401
5.0.408
- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = "7.0.100-rc.1.22431.12"
if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" }
test-proxy: test-proxy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: mcr.microsoft.com/dotnet/core/runtime-deps:3.0-bionic image: ubuntu:latest
options: --dns 127.0.0.1 options: --dns 127.0.0.1
services: services:
squid-proxy: squid-proxy:
image: datadog/squid:latest image: ubuntu/squid:latest
ports: ports:
- 3128:3128 - 3128:3128
env: env:
@ -308,21 +321,29 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Clear tool cache - name: Install Powershell
run: rm -rf "/usr/share/dotnet"
- name: Install curl
run: | run: |
apt update apt-get update
apt -y install curl apt-get install -y wget apt-transport-https software-properties-common
- name: Setup dotnet 3.1.201 wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
apt-get update
apt-get install -y powershell
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0
uses: ./ uses: ./
with: with:
dotnet-version: 3.1.201 dotnet-version: 6.0
source-url: https://api.nuget.org/v3/index.json source-url: https://api.nuget.org/v3/index.json
env: env:
NUGET_AUTH_TOKEN: NOTATOKEN NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet - name: Verify dotnet
run: __tests__/verify-dotnet.sh 3.1.201 shell: pwsh
run: |
__tests__/verify-dotnet.ps1 -Patterns "^6.0" -CheckNugetConfig
test-bypass-proxy: test-bypass-proxy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -332,8 +353,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Clear tool cache - name: Clear toolcache
run: rm -rf "/usr/share/dotnet" shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 3.1.201 - name: Setup dotnet 3.1.201
uses: ./ uses: ./
with: with:
@ -342,4 +364,5 @@ jobs:
env: env:
NUGET_AUTH_TOKEN: NOTATOKEN NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet - name: Verify dotnet
run: __tests__/verify-dotnet.sh 3.1.201 shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$" -CheckNugetConfig

View File

@ -10,16 +10,6 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
test: call-licensed:
runs-on: ubuntu-latest name: Licensed
name: Check licenses uses: actions/reusable-workflows/.github/workflows/licensed.yml@main
steps:
- uses: actions/checkout@v3
- run: npm ci --ignore-scripts
- name: Install licensed
run: |
cd $RUNNER_TEMP
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.4.4/licensed-3.4.4-linux-x64.tar.gz
sudo tar -xzf licensed.tar.gz
sudo mv licensed /usr/local/bin/licensed
- run: licensed status

View File

@ -1,4 +1,5 @@
name: Release new action version name: Release new action version
on: on:
release: release:
types: [released] types: [released]
@ -20,9 +21,9 @@ jobs:
name: releaseNewActionVersion name: releaseNewActionVersion
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Update the ${{ env.TAG_NAME }} tag - name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag id: update-major-tag
uses: actions/publish-action@v0.2.0 uses: actions/publish-action@v0.2.2
with: with:
source-tag: ${{ env.TAG_NAME }} source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }} slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -18,7 +18,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest] operating-system: [ubuntu-latest, windows-latest, macOS-latest]
dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0'] dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0', '6.0', '7.0', '8.0']
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -29,9 +29,7 @@ jobs:
uses: ./ uses: ./
with: with:
dotnet-version: ${{ matrix.dotnet-version }} dotnet-version: ${{ matrix.dotnet-version }}
- name: Check installed version - name: Verify installed version
shell: pwsh shell: pwsh
run: | run: |
$version = & dotnet --version __tests__/verify-dotnet.ps1 -Patterns "^${{ matrix.dotnet-version }}"
Write-Host "Installed version: $version"
if (-not $version.StartsWith("${{ matrix.dotnet-version }}")) { throw "Unexpected version" }

View File

@ -0,0 +1,11 @@
name: Update configuration files
on:
schedule:
- cron: '0 3 * * 0'
workflow_dispatch:
jobs:
call-update-configuration-files:
name: Update configuration files
uses: actions/reusable-workflows/.github/workflows/update-config-files.yml@main

4
.gitignore vendored
View File

@ -3,8 +3,8 @@ global.json
lib/ lib/
node_modules/ node_modules/
__tests__/runner/* __tests__/runner/*
__tests__/sample-csproj/bin/ __tests__/e2e-test-csproj/bin/
__tests__/sample-csproj/obj/ __tests__/e2e-test-csproj/obj/
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore # Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs # Logs

View File

@ -2,3 +2,4 @@
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
npm run format npm run format
npm run lint:fix

View File

@ -3,3 +3,4 @@
# Tests are not run at push time since they can take 2-4 minutes to complete # Tests are not run at push time since they can take 2-4 minutes to complete
npm run format-check npm run format-check
npm run lint

View File

@ -1,6 +1,6 @@
--- ---
name: "@actions/core" name: "@actions/core"
version: 1.9.1 version: 1.10.0
type: npm type: npm
summary: Actions core lib summary: Actions core lib
homepage: https://github.com/actions/toolkit/tree/main/packages/core homepage: https://github.com/actions/toolkit/tree/main/packages/core

View File

@ -1,27 +1,11 @@
--- ---
name: fast-xml-parser name: fast-xml-parser
version: 3.17.4 version: 4.0.10
type: npm type: npm
summary: Validate XML or Parse XML to JS/JSON very fast without C/C++ based libraries summary: Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++ based libraries and no callback.
homepage: https://github.com/NaturalIntelligence/fast-xml-parser#readme homepage: https://github.com/NaturalIntelligence/fast-xml-parser#readme
license: mit license: mit
licenses: licenses:
- sources: LICENSE - sources: LICENSE
text: "MIT License\n\nCopyright (c) 2017 Amit Kumar Gupta\n\nPermission is hereby text: "MIT License\n\nCopyright (c) 2017 Amit Kumar Gupta\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
granted, free of charge, to any person obtaining a copy\nof this software and
associated documentation files (the \"Software\"), to deal\nin the Software without
restriction, including without limitation the rights\nto use, copy, modify, merge,
publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit
persons to whom the Software is\nfurnished to do so, subject to the following
conditions:\n\nIf you use this library in a public repository then you give us
the right to mention your company name and logo in user's list without further
permission required, but you can request them to be taken down within 30 days.
\n\nThe above copyright notice and this permission notice shall be included in
all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED
\"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE
AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
notices: [] notices: []

11
.licenses/npm/strnum.dep.yml generated Normal file
View File

@ -0,0 +1,11 @@
---
name: strnum
version: 1.0.5
type: npm
summary: Parse string into Number based on configuration
homepage: https://github.com/NaturalIntelligence/strnum
license: mit
licenses:
- sources: LICENSE
text: "MIT License\n\nCopyright (c) 2021 Natural Intelligence\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
notices: []

View File

@ -1,24 +0,0 @@
---
name: xmlbuilder
version: 13.0.2
type: npm
summary: An XML builder for node.js
homepage: http://github.com/oozcitak/xmlbuilder-js
license: mit
licenses:
- sources: LICENSE
text: "The MIT License (MIT)\r\n\r\nCopyright (c) 2013 Ozgur Ozcitak\r\n\r\nPermission
is hereby granted, free of charge, to any person obtaining a copy\r\nof this software
and associated documentation files (the \"Software\"), to deal\r\nin the Software
without restriction, including without limitation the rights\r\nto use, copy,
modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software,
and to permit persons to whom the Software is\r\nfurnished to do so, subject to
the following conditions:\r\n\r\nThe above copyright notice and this permission
notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\nTHE
SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR
A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\nTHE SOFTWARE.\r\n"
notices: []

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!.github/
!src/

11
.prettierrc.js Normal file
View File

@ -0,0 +1,11 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
trailingComma: 'none',
bracketSpacing: false,
arrowParens: 'avoid'
};

View File

@ -1,11 +0,0 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid",
"parser": "typescript"
}

View File

@ -1,6 +1,7 @@
# setup-dotnet # setup-dotnet
[![GitHub Actions Status](https://github.com/actions/setup-dotnet/workflows/Main%20workflow/badge.svg)](https://github.com/actions/setup-dotnet) [![Basic validation](https://github.com/actions/setup-dotnet/actions/workflows/basic-validation.yml/badge.svg?branch=main)](https://github.com/actions/setup-dotnet/actions/workflows/basic-validation.yml)
[![e2e tests](https://github.com/actions/setup-dotnet/actions/workflows/e2e-tests.yml/badge.svg?branch=main)](https://github.com/actions/setup-dotnet/actions/workflows/e2e-tests.yml)
This action sets up a [.NET CLI](https://github.com/dotnet/sdk) environment for use in actions by: This action sets up a [.NET CLI](https://github.com/dotnet/sdk) environment for use in actions by:
@ -10,8 +11,8 @@ This action sets up a [.NET CLI](https://github.com/dotnet/sdk) environment for
> **Note**: GitHub hosted runners have some versions of the .NET SDK > **Note**: GitHub hosted runners have some versions of the .NET SDK
preinstalled. Installed versions are subject to change. Please refer to the preinstalled. Installed versions are subject to change. Please refer to the
documentation documentation:
[software installed on github hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software) [Software installed on github hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software)
for .NET SDK versions that are currently available. for .NET SDK versions that are currently available.
## Usage ## Usage
@ -27,6 +28,7 @@ steps:
dotnet-version: '3.1.x' dotnet-version: '3.1.x'
- run: dotnet build <my project> - run: dotnet build <my project>
``` ```
> **Warning**: Unless a concrete version is specified in the [`global.json`](https://learn.microsoft.com/en-us/dotnet/core/tools/global-json) file, **_the latest .NET version installed on the runner (including preinstalled versions) will be used [by default](https://learn.microsoft.com/en-us/dotnet/core/versions/selection#the-sdk-uses-the-latest-installed-version)_**. Please refer to the [documentation](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software) for the currently preinstalled .NET SDK versions.
**Multiple version installation**: **Multiple version installation**:
```yml ```yml
@ -40,8 +42,6 @@ steps:
5.0.x 5.0.x
- run: dotnet build <my project> - run: dotnet build <my project>
``` ```
> **Note**: In case multiple versions are installed, the latest .NET version will be used by default unless another version is specified in the `global.json` file.
## Supported version syntax ## Supported version syntax
The `dotnet-version` input supports following syntax: The `dotnet-version` input supports following syntax:
@ -49,12 +49,13 @@ The `dotnet-version` input supports following syntax:
- **A.B.C** (e.g 6.0.400, 7.0.100-preview.7.22377.5) - installs exact version of .NET SDK - **A.B.C** (e.g 6.0.400, 7.0.100-preview.7.22377.5) - installs exact version of .NET SDK
- **A.B** or **A.B.x** (e.g. 3.1, 3.1.x) - installs the latest patch version of .NET SDK on the channel `3.1`, including prerelease versions (preview, rc) - **A.B** or **A.B.x** (e.g. 3.1, 3.1.x) - installs the latest patch version of .NET SDK on the channel `3.1`, including prerelease versions (preview, rc)
- **A** or **A.x** (e.g. 3, 3.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc) - **A** or **A.x** (e.g. 3, 3.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc)
- **A.B.Cxx** (e.g. 6.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc).
## Using the `dotnet-quality` input ## Using the `dotnet-quality` input
This input sets up the action to install the latest build of the specified quality in the channel. The possible values of `dotnet-quality` are: **daily**, **signed**, **validated**, **preview**, **ga**. This input sets up the action to install the latest build of the specified quality in the channel. The possible values of `dotnet-quality` are: **daily**, **signed**, **validated**, **preview**, **ga**.
> **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A' and 'A.x' formats where the major version is higher than 5. In other cases, `dotnet-quality` input will be ignored. > **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A', 'A.x' and 'A.B.Cxx' formats where the major version is higher than 5. In other cases, `dotnet-quality` input will be ignored.
```yml ```yml
steps: steps:
@ -97,7 +98,31 @@ jobs:
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: ${{ matrix.dotnet }} dotnet-version: ${{ matrix.dotnet }}
- run: dotnet build <my project> - name: Execute dotnet
run: dotnet build <my project>
```
>**Note**: Unless a concrete version is specified in the [`global.json`](https://learn.microsoft.com/en-us/dotnet/core/tools/global-json) file, the latest .NET version installed on the runner (including preinstalled versions) will be used [by default](https://learn.microsoft.com/en-us/dotnet/core/versions/selection#the-sdk-uses-the-latest-installed-version). To control this behavior you may want to use temporary `global.json` files:
**Matrix testing with temporary global.json creation**
```yml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: [ '2.1.x', '3.1.x', '5.0.x' ]
name: Dotnet ${{ matrix.dotnet }} sample
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
id: stepid
with:
dotnet-version: ${{ matrix.dotnet }}
- name: Create temporary global.json
run: echo '{"sdk":{"version": "${{ steps.stepid.outputs.dotnet-version }}"}}' > ./global.json
- name: Execute dotnet
run: dotnet build <my project>
``` ```
## Setting up authentication for nuget feeds ## Setting up authentication for nuget feeds
@ -155,10 +180,10 @@ In case of a single version installation, the `dotnet-version` output contains t
```yaml ```yaml
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
id: cp310 id: stepid
with: with:
dotnet-version: 3.1.422 dotnet-version: 3.1.422
- run: echo '${{ steps.cp310.outputs.dotnet-version }}' # outputs 3.1.422 - run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 3.1.422
``` ```
**Multiple version installation** **Multiple version installation**
@ -167,12 +192,12 @@ In case of a multiple version installation, the `dotnet-version` output contains
```yaml ```yaml
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
id: cp310 id: stepid
with: with:
dotnet-version: | dotnet-version: |
3.1.422 3.1.422
5.0.408 5.0.408
- run: echo '${{ steps.cp310.outputs.dotnet-version }}' # outputs 5.0.408 - run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 5.0.408
``` ```
**Installation from global.json** **Installation from global.json**
@ -180,13 +205,13 @@ When the `dotnet-version` input is used along with the `global-json-file` input,
```yaml ```yaml
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
id: cp310 id: stepid
with: with:
dotnet-version: | dotnet-version: |
3.1.422 3.1.422
5.0.408 5.0.408
global-json-file: "./global.json" # contains version 2.2.207 global-json-file: "./global.json" # contains version 2.2.207
- run: echo '${{ steps.cp310.outputs.dotnet-version }}' # outputs 2.2.207 - run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 2.2.207
``` ```
## Environment variables ## Environment variables
@ -195,11 +220,18 @@ Some environment variables may be necessary for your particular case or to impro
| **Env.variable** | **Description** | **Default value** | | **Env.variable** | **Description** | **Default value** |
| ----------- | ----------- | ----------- | | ----------- | ----------- | ----------- |
| DOTNET_INSTALL_DIR |Specifies a directory where .NET SDKs should be installed by the action|*isn't set*| | DOTNET_INSTALL_DIR |Specifies a directory where .NET SDKs should be installed by the action.|*default value for each OS* |
| DOTNET_NOLOGO |Removes logo and telemetry message from first run of dotnet cli|*false*| | DOTNET_NOLOGO |Removes logo and telemetry message from first run of dotnet cli|*false*|
| DOTNET_CLI_TELEMETRY_OPTOUT |Opt-out of telemetry being sent to Microsoft|*false*| | DOTNET_CLI_TELEMETRY_OPTOUT |Opt-out of telemetry being sent to Microsoft|*false*|
| DOTNET_MULTILEVEL_LOOKUP |Configures whether the global install location is used as a fall-back|*true*| | DOTNET_MULTILEVEL_LOOKUP |Configures whether the global install location is used as a fall-back|*true*|
The default value of the `DOTNET_INSTALL_DIR` environment variable depends on the operation system which is used on a runner:
| **Operation system** | **Default value** |
| ----------- | ----------- |
| **Windows** | `C:\Program Files\dotnet` |
| **Ubuntu** | `/usr/share/dotnet` |
| **macOS** | `/Users/runner/.dotnet` |
**Example usage**: **Example usage**:
```yml ```yml
build: build:

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`authutil tests Existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -15,7 +15,7 @@ exports[`authutil tests Existing config not in repo root, sets up a partial NuGe
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -30,7 +30,7 @@ exports[`authutil tests Existing config w/ Azure Artifacts source and NuGet.org,
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -45,7 +45,7 @@ exports[`authutil tests Existing config w/ GPR source and NuGet.org, sets up a p
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -63,7 +63,7 @@ exports[`authutil tests Existing config w/ no GPR sources, sets up a full NuGet.
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -81,7 +81,7 @@ exports[`authutil tests Existing config w/ no sources, sets up a full NuGet.conf
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -96,7 +96,7 @@ exports[`authutil tests Existing config w/ only Azure Artifacts source, sets up
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -111,7 +111,7 @@ exports[`authutil tests Existing config w/ only GPR source, sets up a partial Nu
</configuration>" </configuration>"
`; `;
exports[`authutil tests Existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = ` exports[`authutil tests existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -130,7 +130,7 @@ exports[`authutil tests Existing config w/ two GPR sources, sets up a partial Nu
</configuration>" </configuration>"
`; `;
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = ` exports[`authutil tests no existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -148,7 +148,7 @@ exports[`authutil tests No existing config, sets up a full NuGet.config with URL
</configuration>" </configuration>"
`; `;
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and token for other source 1`] = ` exports[`authutil tests no existing config, sets up a full NuGet.config with URL and token for other source 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>
@ -166,7 +166,7 @@ exports[`authutil tests No existing config, sets up a full NuGet.config with URL
</configuration>" </configuration>"
`; `;
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = ` exports[`authutil tests no existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?> "<?xml version=\\"1.0\\"?>
<configuration> <configuration>
<config> <config>

View File

@ -1,340 +1,336 @@
import * as io from '@actions/io'; import * as io from '@actions/io';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
const fakeSourcesDirForTesting = path.join( const fakeSourcesDirForTesting = path.join(
__dirname, __dirname,
'runner', 'runner',
path.join( path.join(Math.random().toString(36).substring(7)),
Math.random() 's'
.toString(36) );
.substring(7)
), const invalidNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>`;
's'
); const emptyNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
<configuration>
const invalidNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?>`; </configuration>`;
const emptyNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> const nugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
</configuration>`; <packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
const nugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> </packageSources>
<configuration> </configuration>`;
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> const gprnugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" />
const gprnugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<configuration> </packageSources>
<packageSources> </configuration>`;
<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" /> const gprNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" />
const gprNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> </packageSources>
<configuration> </configuration>`;
<packageSources>
<add key="GPR" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> const twogprNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="GPR-GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" />
const twogprNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> <add key="GPR-Actions" value="https://nuget.pkg.github.com/actions/index.json" protocolVersion="3" />
<configuration> </packageSources>
<packageSources> </configuration>`;
<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" /> const spaceNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="GPR GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" />
const spaceNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> </packageSources>
<configuration> </configuration>`;
<packageSources>
<add key="GPR GitHub" value="https://nuget.pkg.github.com/OwnerName/index.json" protocolVersion="3" /> const azureartifactsNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" />
const azureartifactsNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> </packageSources>
<configuration> </configuration>`;
<packageSources>
<add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" /> const azureartifactsnugetorgNuGetConfig = `<?xml version="1.0" encoding="utf-8"?>
</packageSources> <configuration>
</configuration>`; <packageSources>
<add key="AzureArtifacts" value="https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json" protocolVersion="3" />
const azureartifactsnugetorgNuGetConfig: string = `<?xml version="1.0" encoding="utf-8"?> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<configuration> </packageSources>
<packageSources> </configuration>`;
<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" /> // 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.
</packageSources> const nugetConfigFile = path.join(fakeSourcesDirForTesting, '../nuget.config');
</configuration>`;
process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo';
// 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. import * as auth from '../src/authutil';
const nugetConfigFile = path.join(fakeSourcesDirForTesting, '../nuget.config');
describe('authutil tests', () => {
process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo'; beforeEach(async () => {
import * as auth from '../src/authutil'; await io.rmRF(fakeSourcesDirForTesting);
await io.mkdirP(fakeSourcesDirForTesting);
describe('authutil tests', () => { }, 30000);
beforeEach(async () => {
await io.rmRF(fakeSourcesDirForTesting); afterAll(async () => {
await io.mkdirP(fakeSourcesDirForTesting); await io.rmRF(fakeSourcesDirForTesting);
}, 30000); }, 30000);
afterAll(async () => { beforeEach(() => {
await io.rmRF(fakeSourcesDirForTesting); if (fs.existsSync(nugetConfigFile)) {
}, 30000); fs.unlinkSync(nugetConfigFile);
}
beforeEach(() => { process.env['INPUT_OWNER'] = '';
if (fs.existsSync(nugetConfigFile)) { process.env['NUGET_AUTH_TOKEN'] = '';
fs.unlinkSync(nugetConfigFile); });
}
process.env['INPUT_OWNER'] = ''; it('no existing config, sets up a full NuGet.config with URL and user/PAT for GPR', async () => {
process.env['NUGET_AUTH_TOKEN'] = ''; process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); auth.configAuthentication(
'https://nuget.pkg.github.com/OwnerName/index.json',
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'; fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('no existing config, auth token environment variable not provided, throws', async () => {
).toMatchSnapshot(); let thrown = false;
}); try {
auth.configAuthentication(
it('No existing config, auth token environment variable not provided, throws', async () => { 'https://nuget.pkg.github.com/OwnerName/index.json',
let thrown = false; '',
try { fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', } catch {
'', thrown = true;
fakeSourcesDirForTesting }
); expect(thrown).toBe(true);
} catch { });
thrown = true;
} it('no existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR', async () => {
expect(thrown).toBe(true); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); process.env['INPUT_OWNER'] = 'otherorg';
auth.configAuthentication(
it('No existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR', async () => { 'https://nuget.pkg.github.com/otherorg/index.json',
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; '',
process.env['INPUT_OWNER'] = 'otherorg'; fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/otherorg/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config (invalid), tries to parse an invalid NuGet.config and throws', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config (invalid), tries to parse an invalid NuGet.config and throws', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, invalidNuGetConfig);
fakeSourcesDirForTesting, let thrown = false;
'nuget.config' try {
); auth.configAuthentication(
fs.writeFileSync(inputNuGetConfigPath, invalidNuGetConfig); 'https://nuget.pkg.github.com/OwnerName/index.json',
let thrown = false; '',
try { fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', } catch {
'', thrown = true;
fakeSourcesDirForTesting }
); expect(thrown).toBe(true);
} catch { });
thrown = true;
} it('existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => {
expect(thrown).toBe(true); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, emptyNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://nuget.pkg.github.com/OwnerName/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, emptyNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, nugetorgNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://nuget.pkg.github.com/OwnerName/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, nugetorgNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://nuget.pkg.github.com/OwnerName/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, gprnugetorgNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://nuget.pkg.github.com/OwnerName/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, gprnugetorgNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, twogprNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://nuget.pkg.github.com',
); '',
fs.writeFileSync(inputNuGetConfigPath, twogprNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ spaces in key, throws for now', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ spaces in key, throws for now', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, spaceNuGetConfig);
fakeSourcesDirForTesting, let thrown = false;
'nuget.config' try {
); auth.configAuthentication(
fs.writeFileSync(inputNuGetConfigPath, spaceNuGetConfig); 'https://nuget.pkg.github.com/OwnerName/index.json',
let thrown = false; '',
try { fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', } catch {
'', thrown = true;
fakeSourcesDirForTesting }
); expect(thrown).toBe(true);
} catch { });
thrown = true;
} it('existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR', async () => {
expect(thrown).toBe(true); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigDirectory: string = path.join(
fakeSourcesDirForTesting,
it('Existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR', async () => { 'subfolder'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigDirectory: string = path.join( const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting, inputNuGetConfigDirectory,
'subfolder' 'nuget.config'
); );
const inputNuGetConfigPath: string = path.join( fs.mkdirSync(inputNuGetConfigDirectory, {recursive: true});
inputNuGetConfigDirectory, fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig);
'nuget.config' auth.configAuthentication(
); 'https://nuget.pkg.github.com/OwnerName/index.json',
fs.mkdirSync(inputNuGetConfigDirectory, {recursive: true}); 'subfolder/nuget.config',
fs.writeFileSync(inputNuGetConfigPath, gprNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://nuget.pkg.github.com/OwnerName/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'subfolder/nuget.config', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, azureartifactsNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, azureartifactsNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); const inputNuGetConfigPath: string = path.join(
fakeSourcesDirForTesting,
it('Existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR', async () => { 'nuget.config'
process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN'; );
const inputNuGetConfigPath: string = path.join( fs.writeFileSync(inputNuGetConfigPath, azureartifactsnugetorgNuGetConfig);
fakeSourcesDirForTesting, auth.configAuthentication(
'nuget.config' 'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json',
); '',
fs.writeFileSync(inputNuGetConfigPath, azureartifactsnugetorgNuGetConfig); fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect(
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'}) it('no existing config, sets up a full NuGet.config with URL and token for other source', async () => {
).toMatchSnapshot(); process.env['NUGET_AUTH_TOKEN'] = 'TEST_FAKE_AUTH_TOKEN';
}); auth.configAuthentication(
'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json',
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'; fakeSourcesDirForTesting
await auth.configAuthentication( );
'https://pkgs.dev.azure.com/amullans/_packaging/GitHubBuilds/nuget/v3/index.json', expect(fs.existsSync(nugetConfigFile)).toBe(true);
'', expect(
fakeSourcesDirForTesting fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
); ).toMatchSnapshot();
expect(fs.existsSync(nugetConfigFile)).toBe(true); });
expect( });
fs.readFileSync(nugetConfigFile, {encoding: 'utf8'})
).toMatchSnapshot();
});
});

View File

@ -1,21 +1,45 @@
import cscFile from '../.github/csc.json';
describe('csc tests', () => { describe('csc tests', () => {
it('Valid regular expression', async () => { test('regular expression in csc.json is valid', async () => {
var cscFile = require('../.github/csc.json'); const regexPattern = cscFile['problemMatcher'][0]['pattern'][0]['regexp'];
var regex = cscFile['problemMatcher'][0]['pattern'][0]['regexp']; const regexResultsMap = cscFile['problemMatcher'][0]['pattern'][0];
console.log(regex); const regex = new RegExp(regexPattern);
var re = new RegExp(regex);
// Ideally we would verify that this const stringsToMatch = [
var stringsToMatch = [
'Program.cs(10,79): error CS1002: ; expected [/Users/zacharyeisinger/Documents/repo/setup-dotnet/__tests__/sample-broken-csproj/sample.csproj]', 'Program.cs(10,79): error CS1002: ; expected [/Users/zacharyeisinger/Documents/repo/setup-dotnet/__tests__/sample-broken-csproj/sample.csproj]',
"S:\\Msbuild\\src\\Build\\Evaluation\\ExpressionShredder.cs(33,7): error CS1003: Syntax error, ',' expected [S:\\msbuild\\src\\Build\\Microsoft.Build.csproj > Properties:prop]" "S:\\Msbuild\\src\\Build\\Evaluation\\ExpressionShredder.cs(33,7): error CS1003: Syntax error, ',' expected [S:\\msbuild\\src\\Build\\Microsoft.Build.csproj > Properties:prop]"
]; ];
// Expected results are calculated according to the csc matcher located in csc.json file
const expectedResults = [
{
file: 'Program.cs',
line: '10',
severity: 'error',
code: 'CS1002',
message: '; expected',
fromPath:
'/Users/zacharyeisinger/Documents/repo/setup-dotnet/__tests__/sample-broken-csproj/sample.csproj'
},
{
file: 'S:\\Msbuild\\src\\Build\\Evaluation\\ExpressionShredder.cs',
line: '33',
severity: 'error',
code: 'CS1003',
message: "Syntax error, ',' expected",
fromPath:
'S:\\msbuild\\src\\Build\\Microsoft.Build.csproj > Properties:prop'
}
];
stringsToMatch.forEach(string => { stringsToMatch.map((string, index) => {
var matchStr = string.match(re); const matchedResultsArray = string.match(regex);
console.log(matchStr); for (const propName in expectedResults[index]) {
expect(matchStr).toEqual(expect.anything()); const propertyIndex = regexResultsMap[propName];
const expectedPropValue = expectedResults[index][propName];
const matchedPropValue = matchedResultsArray![propertyIndex];
expect(matchedPropValue).toEqual(expectedPropValue);
}
}); });
}, 10000); }, 10000);
}); });

View File

@ -0,0 +1,18 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace test_csproj
{
[TestClass]
public class Test
{
[TestMethod]
public void TestMethod()
{
Console.WriteLine("TestMethod");
int calculatedResult = 1000 / 25;
int expectedResult = 40;
Assert.AreEqual(calculatedResult, expectedResult);
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TEST_TARGET_FRAMEWORK)</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<!-- These packages will be downloaded over the network for testing proxy settings -->
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" />
<PackageReference Include="MSTest.TestAdapter" Version="1.1.18" />
<PackageReference Include="MSTest.TestFramework" Version="1.1.18" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,65 @@
import path from 'path';
import fs from 'fs';
import * as hc from '@actions/http-client';
const HTTP_CLIENT_OPTIONS = {allowRetries: true, maxRetries: 10} as const;
const TEST_TIMEOUT = 30000;
describe('Dotnet installation scripts tests', () => {
it(
'Uses an up to date bash download script',
async () => {
const httpCallbackClient = new hc.HttpClient(
'setup-dotnet-test',
[],
HTTP_CLIENT_OPTIONS
);
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.sh'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.sh')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
},
TEST_TIMEOUT
);
it(
'Uses an up to date powershell download script',
async () => {
const httpCallbackClient = new hc.HttpClient(
'setup-dotnet-test',
[],
HTTP_CLIENT_OPTIONS
);
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.ps1'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
},
TEST_TIMEOUT
);
});
function normalizeFileContents(contents: string): string {
return contents
.trim()
.replace(new RegExp('\r\n', 'g'), '\n')
.replace(new RegExp('\r', 'g'), '\n');
}

View File

@ -1,290 +1,443 @@
import * as io from '@actions/io';
import * as os from 'os';
import fs from 'fs';
import path from 'path';
import each from 'jest-each'; import each from 'jest-each';
import * as hc from '@actions/http-client'; import semver from 'semver';
import * as exec from '@actions/exec';
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as installer from '../src/installer'; import * as installer from '../src/installer';
import {QualityOptions} from '../src/setup-dotnet';
import {IS_WINDOWS} from '../src/utils'; import {IS_WINDOWS} from '../src/utils';
import {IS_LINUX} from '../src/utils'; import {QualityOptions} from '../src/setup-dotnet';
let toolDir: string; describe('installer tests', () => {
const env = process.env;
if (IS_WINDOWS) { beforeEach(() => {
toolDir = path.join(process.env['PROGRAMFILES'] + '', 'dotnet'); jest.resetModules();
} else if (IS_LINUX) { process.env = {...env};
toolDir = '/usr/share/dotnet'; });
} else {
toolDir = path.join(process.env['HOME'] + '', '.dotnet');
}
const tempDir = path.join(__dirname, 'runner', 'temp');
process.env['RUNNER_TOOL_CACHE'] = toolDir; describe('DotnetCoreInstaller tests', () => {
process.env['RUNNER_TEMP'] = tempDir; const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
const warningSpy = jest.spyOn(core, 'warning');
const whichSpy = jest.spyOn(io, 'which');
const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying');
describe('DotnetCoreInstaller tests', () => { describe('installDotnet() tests', () => {
beforeAll(async () => { whichSpy.mockImplementation(() => Promise.resolve('PathToShell'));
process.env.RUNNER_TOOL_CACHE = toolDir;
process.env.DOTNET_INSTALL_DIR = toolDir;
process.env.RUNNER_TEMP = tempDir;
process.env.DOTNET_ROOT = '';
try {
await io.rmRF(`${toolDir}/*`);
await io.rmRF(`${tempDir}/*`);
} catch (err) {
console.log(
`Failed to remove test directories, check the error message:${os.EOL}`,
err.message
);
}
}, 30000);
afterEach(async () => { it('should throw the error in case of non-zero exit code of the installation script. The error message should contain logs.', async () => {
try { const inputVersion = '3.1.100';
await io.rmRF(`${toolDir}/*`); const inputQuality = '' as QualityOptions;
await io.rmRF(`${tempDir}/*`); const errorMessage = 'fictitious error message!';
} catch (err) { getExecOutputSpy.mockImplementation(() => {
console.log( return Promise.resolve({
`Failed to remove test directories, check the error message:${os.EOL}`, exitCode: 1,
err.message stdout: '',
); stderr: errorMessage
} });
}, 30000); });
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await expect(dotnetInstaller.installDotnet()).rejects.toThrow(
`Failed to install dotnet, exit code: 1. ${errorMessage}`
);
});
it('Aquires multiple versions of dotnet', async () => { it('should return version of .NET SDK after installation complete', async () => {
const versions = ['2.2.207', '3.1.120']; const inputVersion = '3.1.100';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
for (const version of versions) { const dotnetInstaller = new installer.DotnetCoreInstaller(
await getDotnet(version); inputVersion,
} inputQuality
expect(fs.existsSync(path.join(toolDir, 'sdk', '2.2.207'))).toBe(true); );
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.120'))).toBe(true); const installedVersion = await dotnetInstaller.installDotnet();
if (IS_WINDOWS) { expect(installedVersion).toBe(inputVersion);
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); });
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
expect(process.env.DOTNET_ROOT).toBeDefined; it(`should supply 'version' argument to the installation script if supplied version is in A.B.C syntax`, async () => {
expect(process.env.PATH).toBeDefined; const inputVersion = '6.0.300';
expect(process.env.DOTNET_ROOT).toBe(toolDir); const inputQuality = '' as QualityOptions;
expect(process.env.PATH?.startsWith(toolDir)).toBe(true); const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
}, 600000);
it('Acquires version of dotnet if no matching version is installed', async () => { getExecOutputSpy.mockImplementation(() => {
await getDotnet('3.1.201'); return Promise.resolve({
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true); exitCode: 0,
if (IS_WINDOWS) { stdout: `${stdout}`,
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true); stderr: ''
} else { });
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true); });
} maxSatisfyingSpy.mockImplementation(() => inputVersion);
expect(process.env.DOTNET_ROOT).toBeDefined; const dotnetInstaller = new installer.DotnetCoreInstaller(
expect(process.env.PATH).toBeDefined; inputVersion,
expect(process.env.DOTNET_ROOT).toBe(toolDir); inputQuality
expect(process.env.PATH?.startsWith(toolDir)).toBe(true); );
}, 600000); //This needs some time to download on "slower" internet connections
it('Acquires generic version of dotnet if no matching version is installed', async () => { await dotnetInstaller.installDotnet();
await getDotnet('3.1');
var directory = fs
.readdirSync(path.join(toolDir, 'sdk'))
.filter(fn => fn.startsWith('3.1.'));
expect(directory.length > 0).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
expect(process.env.DOTNET_ROOT).toBeDefined; const scriptArguments = (
expect(process.env.PATH).toBeDefined; getExecOutputSpy.mock.calls[0][1] as string[]
expect(process.env.DOTNET_ROOT).toBe(toolDir); ).join(' ');
expect(process.env.PATH?.startsWith(toolDir)).toBe(true); const expectedArgument = IS_WINDOWS
}, 600000); //This needs some time to download on "slower" internet connections ? `-Version ${inputVersion}`
: `--version ${inputVersion}`;
it('Returns string with installed SDK version', async () => { expect(scriptArguments).toContain(expectedArgument);
const version = '3.1.120'; });
let installedVersion: string;
installedVersion = await getDotnet(version); it(`should warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => {
const inputVersion = '6.0.300';
const inputQuality = 'ga' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
expect(installedVersion).toBe('3.1.120'); const dotnetInstaller = new installer.DotnetCoreInstaller(
}, 600000); inputVersion,
inputQuality
);
it('Throws if no location contains correct dotnet version', async () => { await dotnetInstaller.installDotnet();
await expect(async () => {
await getDotnet('1000.0.0');
}).rejects.toThrow();
}, 30000);
it('Uses an up to date bash download script', async () => { expect(warningSpy).toHaveBeenCalledWith(
const httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], { `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${inputVersion}. 'dotnet-quality' input is ignored.`
allowRetries: true, );
maxRetries: 3 });
});
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.sh'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.sh')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
}, 30000);
it('Uses an up to date powershell download script', async () => { it(`should warn if the 'quality' input is set and version isn't in A.B.C syntax but major tag is lower then 6`, async () => {
var httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], { const inputVersion = '3.1';
allowRetries: true, const inputQuality = 'ga' as QualityOptions;
maxRetries: 3 const stdout = `Fictitious dotnet version 3.1.100 is installed`;
});
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.ps1'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
}, 30000);
});
describe('DotnetVersionResolver tests', () => { getExecOutputSpy.mockImplementation(() => {
each([ return Promise.resolve({
'3.1', exitCode: 0,
'3.x', stdout: `${stdout}`,
'3.1.x', stderr: ''
'3.1.*', });
'3.1.X', });
'3.1.2', maxSatisfyingSpy.mockImplementation(() => inputVersion);
'3.1.0-preview1'
]).test(
"if valid version: '%s' is supplied, it should return version object with some value",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject = await dotnetVersionResolver.createDotNetVersion();
expect(!!versionObject.value).toBeTruthy; const dotnetInstaller = new installer.DotnetCoreInstaller(
} inputVersion,
); inputQuality
);
each([ await dotnetInstaller.installDotnet();
'.',
'..', expect(warningSpy).toHaveBeenCalledWith(
' . ', `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${inputVersion}. 'dotnet-quality' input is ignored.`
'. ', );
' .', });
' . . ',
' .. ', each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test(
' . ', `should supply 'quality' argument to the installation script if quality input is set and version (%s) is not in A.B.C syntax`,
'-1.-1', async inputVersion => {
'-1', const inputQuality = 'ga' as QualityOptions;
'-1.-1.-1', const exitCode = 0;
'..3', const stdout = `Fictitious dotnet version 6.0.0 is installed`;
'1..3', getExecOutputSpy.mockImplementation(() => {
'1..', return Promise.resolve({
'.2.3', exitCode: exitCode,
'.2.x', stdout: `${stdout}`,
'*.', stderr: ''
'1.2.', });
'1.2.-abc', });
'a.b', maxSatisfyingSpy.mockImplementation(() => inputVersion);
'a.b.c',
'a.b.c-preview', const dotnetInstaller = new installer.DotnetCoreInstaller(
' 0 . 1 . 2 ', inputVersion,
'invalid' inputQuality
]).test( );
"if invalid version: '%s' is supplied, it should throw",
async version => { await dotnetInstaller.installDotnet();
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Quality ${inputQuality}`
: `--quality ${inputQuality}`;
expect(scriptArguments).toContain(expectedArgument);
}
); );
await expect( each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test(
async () => await dotnetVersionResolver.createDotNetVersion() `should supply 'channel' argument to the installation script if version (%s) isn't in A.B.C syntax`,
).rejects.toThrow(); async inputVersion => {
} const inputQuality = '' as QualityOptions;
); const exitCode = 0;
const stdout = `Fictitious dotnet version 6.0.0 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: exitCode,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
each(['3.1', '3.1.x', '3.1.*', '3.1.X']).test( const dotnetInstaller = new installer.DotnetCoreInstaller(
"if version: '%s' that can be resolved to 'channel' option is supplied, it should set quality flag to 'true' and type to 'channel' in version object", inputVersion,
async version => { inputQuality
const dotnetVersionResolver = new installer.DotnetVersionResolver( );
version
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Channel 6.0`
: `--channel 6.0`;
expect(scriptArguments).toContain(expectedArgument);
}
); );
const versionObject = await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('channel')).toBeTruthy;
expect(versionObject.qualityFlag).toBeTruthy;
}
);
each(['3.1.2', '3.1.0-preview1']).test(
"if version: '%s' that can be resolved to 'version' option is supplied, it should set quality flag to 'false' and type to 'version' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject = await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('version')).toBeTruthy;
expect(versionObject.qualityFlag).toBeFalsy;
}
);
each(['3.1.2', '3.1']).test(
'it should create proper line arguments for powershell/bash installation scripts',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject = await dotnetVersionResolver.createDotNetVersion();
const windowsRegEx = new RegExp(/^-[VC]/);
const nonWindowsRegEx = new RegExp(/^--[vc]/);
if (IS_WINDOWS) { if (IS_WINDOWS) {
expect(windowsRegEx.test(versionObject.type)).toBeTruthy; it(`should supply '-ProxyAddress' argument to the installation script if env.variable 'https_proxy' is set`, async () => {
expect(nonWindowsRegEx.test(versionObject.type)).toBeFalsy; process.env['https_proxy'] = 'https://proxy.com';
} else { const inputVersion = '6.0.100';
expect(nonWindowsRegEx.test(versionObject.type)).toBeTruthy; const inputQuality = '' as QualityOptions;
expect(windowsRegEx.test(versionObject.type)).toBeFalsy; const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(scriptArguments).toContain(
`-ProxyAddress ${process.env['https_proxy']}`
);
});
it(`should supply '-ProxyBypassList' argument to the installation script if env.variable 'no_proxy' is set`, async () => {
process.env['no_proxy'] = 'first.url,second.url';
const inputVersion = '6.0.100';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version 6.0.0 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(scriptArguments).toContain(
`-ProxyBypassList ${process.env['no_proxy']}`
);
});
} }
} });
);
describe('addToPath() tests', () => {
it(`should export DOTNET_ROOT env.var with value from DOTNET_INSTALL_DIR env.var`, async () => {
process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir';
installer.DotnetCoreInstaller.addToPath();
const dotnet_root = process.env['DOTNET_ROOT'];
expect(dotnet_root).toBe(process.env['DOTNET_INSTALL_DIR']);
});
it(`should export value from DOTNET_INSTALL_DIR env.var to the PATH`, async () => {
process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir';
installer.DotnetCoreInstaller.addToPath();
const path = process.env['PATH'];
expect(path).toContain(process.env['DOTNET_INSTALL_DIR']);
});
});
});
describe('DotnetVersionResolver tests', () => {
describe('createDotNetVersion() tests', () => {
each([
'3.1',
'3.x',
'3.1.x',
'3.1.*',
'3.1.X',
'3.1.2',
'3.1.0-preview1',
'6.0.2xx'
]).test(
'if valid version is supplied (%s), it should return version object with some value',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(!!versionObject.value).toBe(true);
}
);
each([
'.',
'..',
' . ',
'. ',
' .',
' . . ',
' .. ',
' . ',
'-1.-1',
'-1',
'-1.-1.-1',
'..3',
'1..3',
'1..',
'.2.3',
'.2.x',
'*.',
'1.2.',
'1.2.-abc',
'a.b',
'a.b.c',
'a.b.c-preview',
' 0 . 1 . 2 ',
'invalid'
]).test(
'if invalid version is supplied (%s), it should throw',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
await expect(
async () => await dotnetVersionResolver.createDotNetVersion()
).rejects.toThrow();
}
);
each(['3', '3.1', '3.1.x', '3.1.*', '3.1.X', '6.0.2xx']).test(
"if version that can be resolved to 'channel' option is supplied (%s), it should set type to 'channel' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('channel')).toBe(
true
);
}
);
each(['6.0', '6.0.x', '6.0.*', '6.0.X', '6.0.2xx']).test(
"if version that can be resolved to 'channel' option is supplied and its major tag is >= 6 (%s), it should set type to 'channel' and qualityFlag to 'true' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('channel')).toBe(
true
);
expect(versionObject.qualityFlag).toBe(true);
}
);
each(['3.1.2', '3.1.0-preview1']).test(
"if version that can be resolved to 'version' option is supplied (%s), it should set quality flag to 'false' and type to 'version' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('version')).toBe(
true
);
expect(versionObject.qualityFlag).toBe(false);
}
);
each(['3.1.2', '3.1']).test(
'it should create proper line arguments for powershell/bash installation scripts',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
const windowsRegEx = new RegExp(/^-(Version|Channel)/);
const nonWindowsRegEx = new RegExp(/^--(version|channel)/);
if (IS_WINDOWS) {
expect(windowsRegEx.test(versionObject.type)).toBe(true);
expect(nonWindowsRegEx.test(versionObject.type)).toBe(false);
} else {
expect(nonWindowsRegEx.test(versionObject.type)).toBe(true);
expect(windowsRegEx.test(versionObject.type)).toBe(false);
}
}
);
it(`should throw if dotnet-version is supplied in A.B.Cxx syntax with major tag lower that 5`, async () => {
const version = '3.0.1xx';
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
await expect(
async () => await dotnetVersionResolver.createDotNetVersion()
).rejects.toThrow(
`'dotnet-version' was supplied in invalid format: ${version}! The A.B.Cxx syntax is available since the .NET 5.0 release.`
);
});
});
});
}); });
function normalizeFileContents(contents: string): string {
return contents
.trim()
.replace(new RegExp('\r\n', 'g'), '\n')
.replace(new RegExp('\r', 'g'), '\n');
}
async function getDotnet(
version: string,
quality: string = ''
): Promise<string> {
const dotnetInstaller = new installer.DotnetCoreInstaller(
version,
quality as QualityOptions
);
const installedVersion = await dotnetInstaller.installDotnet();
installer.DotnetCoreInstaller.addToPath();
return installedVersion;
}

View File

@ -1,15 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace sample_csproj
{
[TestClass]
public class Program
{
[TestMethod]
public void TestMethod1()
{
Console.WriteLine("Hello, World!");
}
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netcoreapp3.0;netcoreapp2.2</TargetFrameworks>
<RootNamespace>sample_csproj</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<!-- These packages will be downloaded over the network for testing proxy settings -->
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
</Project>

View File

@ -1,98 +1,173 @@
import * as io from '@actions/io';
import * as core from '@actions/core'; import * as core from '@actions/core';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import semver from 'semver';
import path from 'path'; import * as auth from '../src/authutil';
import * as setup from '../src/setup-dotnet'; import * as setup from '../src/setup-dotnet';
import {IS_WINDOWS} from '../src/utils'; import {DotnetCoreInstaller} from '../src/installer';
import {IS_LINUX} from '../src/utils';
let toolDir: string;
if (IS_WINDOWS) {
toolDir = path.join(process.env['PROGRAMFILES'] + '', 'dotnet');
} else if (IS_LINUX) {
toolDir = '/usr/share/dotnet';
} else {
toolDir = path.join(process.env['HOME'] + '', '.dotnet');
}
const tempDir = path.join(__dirname, 'runner', 'temp2');
describe('setup-dotnet tests', () => { describe('setup-dotnet tests', () => {
let getInputSpy = jest.spyOn(core, 'getInput'); const inputs = {} as any;
let getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput');
let setOutputSpy = jest.spyOn(core, 'setOutput');
let inputs = {} as any; const getInputSpy = jest.spyOn(core, 'getInput');
const getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput');
const setFailedSpy = jest.spyOn(core, 'setFailed');
const warningSpy = jest.spyOn(core, 'warning');
const debugSpy = jest.spyOn(core, 'debug');
const infoSpy = jest.spyOn(core, 'info');
const setOutputSpy = jest.spyOn(core, 'setOutput');
beforeAll(async () => { const existsSyncSpy = jest.spyOn(fs, 'existsSync');
process.env.RUNNER_TOOL_CACHE = toolDir;
process.env.DOTNET_INSTALL_DIR = toolDir;
process.env.RUNNER_TEMP = tempDir;
try {
await io.rmRF(`${toolDir}/*`);
await io.rmRF(`${tempDir}/*`);
} catch (err) {
console.log(err.message);
console.log('Failed to remove test directories');
}
}, 30000);
afterEach(async () => { const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying');
try {
await io.rmRF(path.join(process.cwd(), 'global.json'));
await io.rmRF(`${toolDir}/*`);
await io.rmRF(`${tempDir}/*`);
} catch (err) {
console.log(err.message);
console.log('Failed to remove test directories');
}
}, 30000);
it('Acquires version of dotnet from global.json if no matching version is installed', async () => { const installDotnetSpy = jest.spyOn(
const globalJsonPath = path.join(process.cwd(), 'global.json'); DotnetCoreInstaller.prototype,
const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.1.201"${os.EOL}}${os.EOL}}`; 'installDotnet'
if (!fs.existsSync(globalJsonPath)) { );
fs.writeFileSync(globalJsonPath, jsonContents); const addToPathSpy = jest.spyOn(DotnetCoreInstaller, 'addToPath');
}
await setup.run();
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true); const configAuthenticationSpy = jest.spyOn(auth, 'configAuthentication');
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
}, 400000);
it("Sets output with the latest installed by action version if global.json file isn't specified", async () => { describe('run() tests', () => {
inputs['dotnet-version'] = ['3.1.201', '6.0.401']; beforeEach(() => {
getMultilineInputSpy.mockImplementation(input => inputs[input as string]);
getInputSpy.mockImplementation(input => inputs[input as string]);
});
getMultilineInputSpy.mockImplementation(input => inputs[input]); afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
await setup.run(); it('should fail the action if global-json-file input is present, but the file does not exist in the file system', async () => {
inputs['global-json-file'] = 'fictitious.json';
inputs['dotnet-version'] = [];
expect(setOutputSpy).toBeCalledWith('dotnet-version', '6.0.401'); const expectedErrorMessage = `The specified global.json file '${inputs['global-json-file']}' does not exist`;
}, 400000);
it("Sets output with the version specified in global.json, if it's present", async () => { await setup.run();
const globalJsonPath = path.join(process.cwd(), 'global.json'); expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.0.103"${os.EOL}}${os.EOL}}`; });
if (!fs.existsSync(globalJsonPath)) {
fs.writeFileSync(globalJsonPath, jsonContents);
}
inputs['dotnet-version'] = ['3.1.201', '6.0.401']; test(`if 'dotnet-version' and 'global-json-file' inputs aren't present, should log into debug output, try to find global.json in the repo root, fail and log message into info output`, async () => {
inputs['global-json-file'] = './global.json'; inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
getMultilineInputSpy.mockImplementation(input => inputs[input]); maxSatisfyingSpy.mockImplementation(() => null);
setOutputSpy.mockImplementation(() => {});
getInputSpy.mockImplementation(input => inputs[input]); const expectedDebugMessage =
'No version found, trying to find version from global.json';
const expectedInfoMessage = `The global.json wasn't found in the root directory. No .NET version will be installed.`;
await setup.run(); await setup.run();
expect(setOutputSpy).toBeCalledWith('dotnet-version', '3.0.103'); expect(debugSpy).toHaveBeenCalledWith(expectedDebugMessage);
}, 400000); expect(existsSyncSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(expectedInfoMessage);
});
it('should fail the action if quality is supplied but its value is not supported', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0'];
inputs['dotnet-quality'] = 'fictitiousQuality';
const expectedErrorMessage = `Value '${inputs['dotnet-quality']}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`;
await setup.run();
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
});
it('should call installDotnet() multiple times if dotnet-version multiline input is provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0', '7.0'];
inputs['dotnet-quality'] = '';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
await setup.run();
expect(installDotnetSpy).toHaveBeenCalledTimes(2);
});
it('should call addToPath() after installation complete', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0', '7.0'];
inputs['dotnet-quality'] = '';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(addToPathSpy).toHaveBeenCalledTimes(1);
});
it('should call auth.configAuthentication() if source-url input is provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
inputs['dotnet-quality'] = '';
inputs['source-url'] = 'fictitious.source.url';
configAuthenticationSpy.mockImplementation(() => {});
await setup.run();
expect(configAuthenticationSpy).toHaveBeenCalledWith(
inputs['source-url'],
undefined
);
});
it('should call auth.configAuthentication() with proper parameters if source-url and config-file inputs are provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
inputs['dotnet-quality'] = '';
inputs['source-url'] = 'fictitious.source.url';
inputs['config-file'] = 'fictitious.path';
configAuthenticationSpy.mockImplementation(() => {});
setOutputSpy.mockImplementation(() => {});
await setup.run();
expect(configAuthenticationSpy).toHaveBeenCalledWith(
inputs['source-url'],
inputs['config-file']
);
});
it('should call setOutput() after installation complete successfully', async () => {
inputs['dotnet-version'] = ['6.0.300'];
installDotnetSpy.mockImplementation(() =>
Promise.resolve(`${inputs['dotnet-version']}`)
);
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(setOutputSpy).toHaveBeenCalledTimes(1);
});
it(`shouldn't call setOutput() if parsing dotnet-installer logs failed`, async () => {
inputs['dotnet-version'] = ['6.0.300'];
const warningMessage = `Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.`;
installDotnetSpy.mockImplementation(() => Promise.resolve(null));
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(warningSpy).toHaveBeenCalledWith(warningMessage);
expect(setOutputSpy).not.toHaveBeenCalled();
});
it(`shouldn't call setOutput() if actions didn't install .NET`, async () => {
inputs['dotnet-version'] = [];
const warningMessage = `The 'dotnet-version' output will not be set.`;
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(infoSpy).toHaveBeenCalledWith(warningMessage);
expect(setOutputSpy).not.toHaveBeenCalled();
});
});
}); });

View File

@ -1,73 +1,117 @@
if (!$args[0]) <#
{ .DESCRIPTION
throw "Must supply dotnet version argument" Verifies that installed on the machine .NET SDK versions match the input patterns.
Optionally checks that the nuget.config file is generated correctly.
.PARAMETER Patterns
Specifies the regular expression patterns that should be matched with the installed
on the machine .NET SDK versions. The number of patterns should be equal to the number
of installed .NET versions.
.PARAMETER CheckNugetConfig
Switches the check for the existence of the nuget.config file.
.EXAMPLE
PS> .\verify-dotnet.ps1 -Paterns "^3.1.200$", "^6.0" -CheckNugetConfig
#>
param(
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$true)]
[string[]]$Patterns,
[switch]$CheckNugetConfig
)
$PatternsList = [System.Collections.ArrayList]($Patterns)
if ($CheckNugetConfig.IsPresent -and !(Test-Path "../nuget.config")) {
throw "The nuget.config file is not generated correctly."
} }
Write-Host "These patterns were supplied to the script: $($PatternsList -join ', ')."
$dotnet = Get-Command dotnet | Select-Object -First 1 | ForEach-Object { $_.Path } $dotnet = Get-Command dotnet | Select-Object -First 1 | ForEach-Object { $_.Path }
Write-Host "Found '$dotnet'" Write-Host "Found: '$dotnet'"
if($args.count -eq 1) # SDKs are listed on multiple lines with the path afterwards in square brackets
$Versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() }
Write-Host "Found installed versions: $($Versions -join ', ')."
$InstalledVersionCount = $Versions.Count
foreach($version in $Versions)
{ {
$version = & $dotnet --version | Out-String | ForEach-Object { $_.Trim() } foreach($pattern in $PatternsList)
Write-Host "Version $version"
if (-not ($version.StartsWith($args[0].ToString())))
{ {
Write-Host "PATH='$env:PATH'" if ($version -match $pattern)
throw "Unexpected version" {
} $PatternsList.Remove($pattern)
} $InstalledVersionCount--
break
if ($args[1])
{
# SDKs are listed on multiple lines with the path afterwards in square brackets
$versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() }
Write-Host "Installed versions: $versions"
$InstalledVersionCount = 0
foreach($arg in $args){
foreach ($version in $versions)
{
if ($version.StartsWith($arg.ToString()))
{
$InstalledVersionCount++
}
} }
}
if ( $InstalledVersionCount -ne $args.Count)
{
Write-Host "PATH='$env:PATH'"
throw "Unexpected version"
} }
} }
Write-Host "Building sample csproj" if ( $InstalledVersionCount -ne 0)
& $dotnet build __tests__/sample-csproj/ --no-cache
if ($LASTEXITCODE -ne 0)
{ {
throw "Unexpected exit code $LASTEXITCODE" throw "An unexpected version of Dotnet is found on the machine, please check the correctness of the -Patterns input."
} }
Write-Host "Testing compiled app" $workingDir = Get-Location
$sample_output = "$(dotnet test __tests__/sample-csproj/ --no-build)" $testProjectDir = "./__tests__/e2e-test-csproj"
Write-Host "Sample output: $sample_output" Write-Host "Changing directory to the $testProjectDir"
# For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once Set-Location $testProjectDir
if ($args[1])
$targetFrameworkVersionMap = @{
"1.0" = "netcoreapp1.0";
"1.1" = "netcoreapp1.1";
"2.0" = "netcoreapp2.0";
"2.1" = "netcoreapp2.1";
"2.2" = "netcoreapp2.2";
"3.0" = "netcoreapp3.0";
"3.1" = "netcoreapp3.1";
"5.0" = "net5.0";
"6.0" = "net6.0";
"7.0" = "net7.0";
"8.0" = "net8.0";
}
foreach ($version in $Versions)
{ {
if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*") # Creating temporary global.json file inside e2e-test-csproj dir and setting exact version of .NET inside allows to override default behavior of .NET and run build and tests on that exact version.
Write-Host "Creating temporary global.json file for $version .NET version."
& $dotnet new globaljson --sdk-version $version --force | Out-Null
if (!(Test-Path "./global.json"))
{ {
throw "Unexpected output" throw "An error occured while creating the global.json file. Exit code: $LASTEXITCODE"
} }
} Write-Host "The global.json file for the version $version is created. Currently used .NET version is: $(& $dotnet --version)."
if ($args[2])
{ # Environment variable TEST_TARGET_FRAMEWORK is used inside the test.csproj file to target required framework version
if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*Test Run Successful.*") $version -match "^(?<key>\d+\.\d+)" | Out-Null
if (!($targetFrameworkVersionMap.ContainsKey($Matches.key)))
{ {
throw "Unexpected output" throw "The map with the framework targets doesn't contain a target name for the version $version."
} }
} Write-Host "Setting the TEST_TARGET_FRAMEWORK environment variable to $($targetFrameworkVersionMap[$Matches.key])"
else [Environment]::SetEnvironmentVariable('TEST_TARGET_FRAMEWORK', $($targetFrameworkVersionMap[$Matches.key]))
{
if ($sample_output -notlike "*Test Run Successful.*") Write-Host "Building test C# project with $version .NET version."
& $dotnet build --no-cache
if ($LASTEXITCODE -ne 0)
{ {
throw "Unexpected output" throw "Building process is not successful, exit code: $LASTEXITCODE"
} }
Write-Host "Testing compiled C# project with $version .NET version."
& $dotnet test --no-build
if ($LASTEXITCODE -ne 0)
{
throw "Testing process is not successful, exit code: $LASTEXITCODE"
}
Write-Host "Tests are completed successfully!"
Write-Host "Removing temporary global.json file."
Remove-Item ./global.json
} }
Set-Location $workingDir

View File

@ -1,44 +0,0 @@
if [ -z "$1" ]; then
echo "Must supply dotnet version argument"
exit 1
fi
if [ ! -f "../nuget.config" ]; then
echo "nuget file not generated correctly"
exit 1
fi
dotnet_version="$(dotnet --version)"
echo "Found dotnet version '$dotnet_version'"
if [ -z "$(echo $dotnet_version | grep $1)" ]; then
echo "Unexpected version"
exit 1
fi
if [ -n "$2" ]; then
dotnet_version="$(dotnet --list-sdks)"
echo "Found dotnet version '$dotnet_version'"
if [ -z "$(echo $dotnet_version | grep $2)" ]; then
echo "Unexpected version"
exit 1
fi
fi
echo "Building sample csproj"
dotnet build __tests__/sample-csproj/ --no-cache || exit 1
echo "Testing compiled app"
sample_output=$(dotnet test __tests__/sample-csproj/ --no-build)
echo "Sample output: $sample_output"
# For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once
if [ -n "$2" ]; then
if [ -z "$(echo $sample_output | grep "Test Run Successful.*Test Run Successful.")" ]; then
echo "Unexpected output"
exit 1
fi
else
if [ -z "$(echo $sample_output | grep "Test Run Successful.")" ]; then
echo "Unexpected output"
exit 1
fi
fi

View File

@ -1,7 +0,0 @@
#!/bin/bash
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff
exit 1
fi

View File

@ -6,7 +6,7 @@ branding:
color: green color: green
inputs: inputs:
dotnet-version: dotnet-version:
description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x' description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x, 6.0.2xx'
dotnet-quality: dotnet-quality:
description: 'Optional quality of the build. The possible values are: daily, signed, validated, preview, ga.' description: 'Optional quality of the build. The possible values are: daily, signed, validated, preview, ga.'
global-json-file: global-json-file:

7937
dist/index.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -61,12 +61,13 @@ Pull requests are the easiest way to contribute changes to git repos at GitHub.
- To implement new features or fix bugs, you need to make changes to the `.ts` files, which are located in the `src` folder - To implement new features or fix bugs, you need to make changes to the `.ts` files, which are located in the `src` folder
- To comply with the code style, **you need to run the `format` script** - To comply with the code style, **you need to run the `format` script**
- To lint the code, **you need to run the `lint:fix` script**
- To transpile source code to `javascript` we use [NCC](https://github.com/vercel/ncc). **It is very important to run the `build` script after making changes**, otherwise your changes will not get into the final `javascript` build - To transpile source code to `javascript` we use [NCC](https://github.com/vercel/ncc). **It is very important to run the `build` script after making changes**, otherwise your changes will not get into the final `javascript` build
**Learn more about how to implement tests:** **Learn more about how to implement tests:**
Adding or changing tests is an integral part of making a change to the code. Adding or changing tests is an integral part of making a change to the code.
Unit tests are in the `__tests__` folder, and end-to-end tests are in the `workflows` folder, particularly in the [workflow.yml](https://github.com/actions/setup-dotnet/blob/main/.github/workflows/workflow.yml). Unit tests are in the `__tests__` folder, and end-to-end tests are in the `workflows` folder, particularly in the [e2e-tests.yml](https://github.com/actions/setup-dotnet/blob/main/.github/workflows/e2e-tests.yml).
- The contributor can add various types of tests (like unit tests or end-to-end tests), which, in his opinion, will be necessary and sufficient for testing new or changed functionality - The contributor can add various types of tests (like unit tests or end-to-end tests), which, in his opinion, will be necessary and sufficient for testing new or changed functionality
- Tests should cover a successful execution, as well as some edge cases and possible errors - Tests should cover a successful execution, as well as some edge cases and possible errors

View File

@ -9,20 +9,27 @@
.DESCRIPTION .DESCRIPTION
Installs dotnet cli. If dotnet installation already exists in the given directory Installs dotnet cli. If dotnet installation already exists in the given directory
it will update it only if the requested version differs from the one already installed. it will update it only if the requested version differs from the one already installed.
Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:
- The SDK needs to be installed without user interaction and without admin rights.
- The SDK installation doesn't need to persist across multiple CI runs.
To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.
.PARAMETER Channel .PARAMETER Channel
Default: LTS Default: LTS
Download from the Channel specified. Possible values: Download from the Channel specified. Possible values:
- Current - most current release - STS - the most recent Standard Term Support release
- LTS - most current supported release - LTS - the most recent Long Term Support release
- 2-part version in a format A.B - represents a specific release - 2-part version in a format A.B - represents a specific release
examples: 2.0, 1.0 examples: 2.0, 1.0
- 3-part version in a format A.B.Cxx - represents a specific SDK release - 3-part version in a format A.B.Cxx - represents a specific SDK release
examples: 5.0.1xx, 5.0.2xx examples: 5.0.1xx, 5.0.2xx
Supported since 5.0 release Supported since 5.0 release
Warning: Value "Current" is deprecated for the Channel parameter. Use "STS" instead.
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used. Note: The version parameter overrides the channel parameter when any version other than 'latest' is used.
.PARAMETER Quality .PARAMETER Quality
Download the latest build of specified quality in the channel. The possible values are: daily, signed, validated, preview, GA. Download the latest build of specified quality in the channel. The possible values are: daily, signed, validated, preview, GA.
Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used. Works only in combination with channel. Not applicable for STS and LTS channels and will be ignored if those channels are used.
For SDK use channel in A.B.Cxx format: using quality together with channel in A.B format is not supported. For SDK use channel in A.B.Cxx format: using quality together with channel in A.B format is not supported.
Supported since 5.0 release. Supported since 5.0 release.
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality. Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality.
@ -163,6 +170,12 @@ function Say-Verbose($str) {
} }
} }
function Measure-Action($name, $block) {
$time = Measure-Command $block
$totalSeconds = $time.TotalSeconds
Say-Verbose "⏱ Action '$name' took $totalSeconds seconds"
}
function Say-Invocation($Invocation) { function Say-Invocation($Invocation) {
$command = $Invocation.MyCommand; $command = $Invocation.MyCommand;
$args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ") $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
@ -277,13 +290,18 @@ function Get-NormalizedChannel([string]$Channel) {
return "" return ""
} }
if ($Channel.Contains("Current")) {
Say-Warning 'Value "Current" is deprecated for -Channel option. Use "STS" instead.'
}
if ($Channel.StartsWith('release/')) { if ($Channel.StartsWith('release/')) {
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, such as "-Channel 5.0 -Quality Daily."' 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, such as "-Channel 5.0 -Quality Daily."'
} }
switch ($Channel) { switch ($Channel) {
{ $_ -eq "lts" } { return "LTS" } { $_ -eq "lts" } { return "LTS" }
{ $_ -eq "current" } { return "current" } { $_ -eq "sts" } { return "STS" }
{ $_ -eq "current" } { return "STS" }
default { return $Channel.ToLowerInvariant() } default { return $Channel.ToLowerInvariant() }
} }
} }
@ -925,10 +943,10 @@ function PrintDryRunOutput($Invocation, $DownloadLinks)
function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) { function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
#quality is not supported for LTS or current channel #quality is not supported for LTS or STS channel
if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "current") -contains $Channel)) { if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "STS") -contains $Channel)) {
$Quality = "" $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."
} }
Say-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'." Say-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'."
@ -1098,10 +1116,10 @@ function Prepare-Install-Directory {
} }
} }
Say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" Say-Verbose "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-Verbose "- 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-Verbose "- The SDK installation doesn't need to persist across multiple CI runs."
Say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n" Say-Verbose "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n"
if ($SharedRuntime -and (-not $Runtime)) { if ($SharedRuntime -and (-not $Runtime)) {
$Runtime = "dotnet" $Runtime = "dotnet"
@ -1109,14 +1127,16 @@ if ($SharedRuntime -and (-not $Runtime)) {
$OverrideNonVersionedFiles = !$SkipNonVersionedFiles $OverrideNonVersionedFiles = !$SkipNonVersionedFiles
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture Measure-Action "Product discovery" {
$NormalizedQuality = Get-NormalizedQuality $Quality $script:CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
Say-Verbose "Normalized quality: '$NormalizedQuality'" $script:NormalizedQuality = Get-NormalizedQuality $Quality
$NormalizedChannel = Get-NormalizedChannel $Channel Say-Verbose "Normalized quality: '$NormalizedQuality'"
Say-Verbose "Normalized channel: '$NormalizedChannel'" $script:NormalizedChannel = Get-NormalizedChannel $Channel
$NormalizedProduct = Get-NormalizedProduct $Runtime Say-Verbose "Normalized channel: '$NormalizedChannel'"
Say-Verbose "Normalized product: '$NormalizedProduct'" $script:NormalizedProduct = Get-NormalizedProduct $Runtime
$FeedCredential = ValidateFeedCredential $FeedCredential Say-Verbose "Normalized product: '$NormalizedProduct'"
$script:FeedCredential = ValidateFeedCredential $FeedCredential
}
$InstallRoot = Resolve-Installation-Path $InstallDir $InstallRoot = Resolve-Installation-Path $InstallDir
Say-Verbose "InstallRoot: $InstallRoot" Say-Verbose "InstallRoot: $InstallRoot"
@ -1127,7 +1147,7 @@ $feeds = Get-Feeds-To-Use
$DownloadLinks = @() $DownloadLinks = @()
if ($Version.ToLowerInvariant() -ne "latest" -and -not [string]::IsNullOrEmpty($Quality)) { if ($Version.ToLowerInvariant() -ne "latest" -and -not [string]::IsNullOrEmpty($Quality)) {
throw "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." throw "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."
} }
# aka.ms links can only be used if the user did not request a specific version via the command line or a global.json file. # aka.ms links can only be used if the user did not request a specific version via the command line or a global.json file.
@ -1194,7 +1214,7 @@ if ($DryRun) {
return return
} }
Prepare-Install-Directory Measure-Action "Installation directory preparation" { Prepare-Install-Directory }
$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
Say-Verbose "Zip path: $ZipPath" Say-Verbose "Zip path: $ZipPath"
@ -1208,7 +1228,7 @@ foreach ($link in $DownloadLinks)
Say-Verbose "Downloading `"$($link.type)`" link $($link.downloadLink)" Say-Verbose "Downloading `"$($link.type)`" link $($link.downloadLink)"
try { try {
DownloadFile -Source $link.downloadLink -OutPath $ZipPath Measure-Action "Package download" { DownloadFile -Source $link.downloadLink -OutPath $ZipPath }
Say-Verbose "Download succeeded." Say-Verbose "Download succeeded."
$DownloadSucceeded = $true $DownloadSucceeded = $true
$DownloadedLink = $link $DownloadedLink = $link
@ -1245,7 +1265,7 @@ if (-not $DownloadSucceeded) {
} }
Say "Extracting the archive." Say "Extracting the archive."
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot Measure-Action "Package extraction" { Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot }
# Check if the SDK version is installed; if not, fail the installation. # Check if the SDK version is installed; if not, fail the installation.
$isAssetInstalled = $false $isAssetInstalled = $false
@ -1271,224 +1291,224 @@ if (!$isAssetInstalled) {
SafeRemoveFile -Path $ZipPath SafeRemoveFile -Path $ZipPath
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot Measure-Action "Setting up shell environment" { Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot }
Say "Note that the script does not resolve dependencies during installation." Say "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/windows#dependencies" Say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install/windows#dependencies"
Say "Installed version is $($DownloadedLink.effectiveVersion)" Say "Installed version is $($DownloadedLink.effectiveVersion)"
Say "Installation finished" Say "Installation finished"
# SIG # Begin signature block # SIG # Begin signature block
# MIInnQYJKoZIhvcNAQcCoIInjjCCJ4oCAQExDzANBglghkgBZQMEAgEFADB5Bgor # MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBPbD3vCI+sY1o7 # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBhfTi3SRn7+vyy
# t+9GwL7gEDtWJk/5Ypegl3ITSKy+X6CCDYEwggX/MIID56ADAgECAhMzAAACzI61 # uCXKPjhiawegWZ493EcaOEycbgkZcKCCDXYwggX0MIID3KADAgECAhMzAAACy7d1
# lqa90clOAAAAAALMMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAxWhcNMjMwNTExMjA0NjAxWjB0MQsw # bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCiTbHs68bADvNud97NzcdP0zh0mRr4VpDv68KobjQFybVAuVgiINf9aG2zQtWK # AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA
# No6+2X2Ix65KGcBXuZyEi0oBUAAGnIe5O5q/Y0Ij0WwDyMWaVad2Te4r1Eic3HWH # wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4
# UfiiNjF0ETHKg3qa7DCyUqwsR9q5SaXuHlYCwM+m59Nl3jKnYnKLLfzhl13wImV9 # 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5
# DF8N76ANkRyK6BYoc9I6hHF2MCTQYWbQ4fXgzKhgzj4zeabWgfu+ZJCiFLkogvc0 # RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN
# RVb0x3DtyxMbl/3e45Eu+sn/x6EVwbJZVvtQYcmdGF1yAYht+JnNmWwAxL8MgHMz # lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X
# xEcoY1Q1JtstiY3+u3ulGMvhAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUiLhHjTKWzIqVIp+sM2rOHH11rfQw # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDcwNTI5MB8GA1UdIwQYMBaAFEhu # MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAeA8D # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ
# sOAHS53MTIHYu8bbXrO6yQtRD6JfyMWeXaLu3Nc8PDnFc1efYq/F3MGx/aiwNbcs # ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf
# J2MU7BKNWTP5JQVBA2GNIeR3mScXqnOsv1XqXPvZeISDVWLaBQzceItdIwgo6B13 # zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh
# vxlkkSYMvB0Dr3Yw7/W9U4Wk5K/RDOnIGvmKqKi3AwyxlV1mpefy729FKaWT7edB # 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4
# d3I4+hldMY8sdfDPjWRtJzjMjXZs41OUOwtHccPazjjC7KndzvZHx/0VWL8n0NT/ # EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j
# 404vftnXKifMZkS4p2sB3oK+6kCcsyWsgS/3eYGw1Fe4MOnin1RhgrW1rHPODJTG # 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck
# AUOmW4wc3Q6KKr2zve7sMDZe9tfylonPwhk971rX8qGw6LkrGFv31IJeJSe/aUbG # 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd
# dUDPkbrABbVvPElgoj5eP3REqx5jdfkQw7tOdWkhn0jDUh2uQen9Atj3RkJyHuR0 # jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N
# GUsJVMWFJdkIO/gFwzoOGlHNsmxvpANV86/1qgb1oZXdrURpzJp53MsDaBY/pxOc # mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1
# J0Cvg6uWs3kQWgKk5aBzvsX95BzdItHTpVMtVPW4q41XEvbFmUP1n6oL5rdNdrTM # pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB
# j/HXMRk1KCksax1Vxo3qv+13cCsZAaQNaIAvt5LvkshZkDZIP//0Hnq7NnWeYR3z # fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To
# 4oFiw9N2n3bb9baQWuWPswG0Dq9YT9kb+Cs4qIIwggd6MIIFYqADAgECAgphDpDS # /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZcjCCGW4CAQEwgZUwfjELMAkG # /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAsyOtZamvdHJTgAAAAACzDAN # Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgNQFZgkyG # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFmuaTXYQ37AFvsEol24fdW+
# luNzcU2g8/R/8PaAnIpTnmBnw3/0HJQjl9wwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # nRqHcc1fr+VQVdqhXc/vMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BgkqhkiG9w0BAQEFAASCAQABpLusOOxklzXjvllIe1AgDCgkYd0BN4cT3yQ8uULV # BQAEggEAjY5XW5Ly7TJ1OTbeIR98xU+2dmtw7L71ws+ICnQCGhj2xJDUK+5yrTfO
# e+OnVgGOLnPcffCSGZ/SQMgJndoRMBSBd0jH5JxSkSuLXJpEWs1nl4QUg93FxYLr # 8C98l/P4ynFi33Dl8z2YElqUCuqEXbiCzz06lIL4NuibC5DV/X80ZmICR/NYd2v1
# pMdFepMsN733h5JuZGcTFf7P23IOxYaVEC+mKLbkOxIJaxgDQYSgliSg9X2hwLJ2 # ww7IH+7dpsHAowBBindCYpVwQ3Ea3kDWgsjPAinAysFFushSOnNWFvrF6vi2smrs
# frCUV4b3ZWL0R495LhGpo65B7Ik/OOeHXWcs8d7vOnE/ObPHFv3fn1QTrq+KvbhA # smbrAAhEhSfLd1Pxxdw73hQ0YjM/D3F3opaybMQ0blpHhOaqtbiyYzvk0doIzBEc
# TWEmL3P9P0Jn7k6gJjrTOxpgcDenr0IE5X63oe7y32LgLlJbr1OjKQUUCPVQ16d9 # trSH4NDIc3yLNj5VbjSczpexE+hyQNY4xCtwco4bVtXhONUihv08AIKR8+sIaI7A
# bgkhRp0gghdSAbDKjQuEAQ+e3GTeoNWnzxPlQfMLP0droYIW/DCCFvgGCisGAQQB # mM/SWrrwGYSSSxydKqDei7biKG4jDqGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC
# gjcDAwExghboMIIW5AYJKoZIhvcNAQcCoIIW1TCCFtECAQMxDzANBglghkgBZQME # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# MDEwDQYJYIZIAWUDBAIBBQAEIGXZSvVWqDs0wjW8JLHjKJ41lgzXHpdGqPCyYSvk # AwQCAQUABCB6Hzt2gUb/WZK8fvVnOocriE4rYr6mscZi3gZnBCpiigIGZBr2iMZU
# gIH0AgZi1XtjQncYEzIwMjIwODAzMTI1NjA1Ljc2OFowBIACAfSggdCkgc0wgcox # GBMyMDIzMDMzMTE1MjEwNi41MTZaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# RVNOOjEyQkMtRTNBRS03NEVCMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # OjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# cCBTZXJ2aWNloIIRUzCCBwwwggT0oAMCAQICEzMAAAGhAYVVmblUXYoAAQAAAaEw # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAGybkADf26plJIAAQAAAbIwDQYJ
# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw
# MjExMjAyMTkwNTI0WhcNMjMwMjI4MTkwNTI0WjCByjELMAkGA1UEBhMCVVMxEzAR # OTIwMjAyMjAxWhcNMjMxMjE0MjAyMjAxWjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTJCQy1FM0FFLTc0 # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowODQyLTRC
# RUIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G # RTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDayTxe5WukkrYxxVuHLYW9BEWCD9kk # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqiZTIde/lQ4rC+Bml5f/Wu
# jnnHsOKwGddIPbZlLY+l5ovLDNf+BEMQKAZQI3DX91l1yCDuP9X7tOPC48ZRGXA/ # q/xKTxrfbG23HofmQ+qZAN4GyO73PF3y9OAfpt7Qf2jcldWOGUB+HzBuwllYyP3f
# bf9ql0FK5438gIl7cV528XeEOFwc/A+UbIUfW296Omg8Z62xaQv3jrG4U/priArF # x4MY8zvuAuB37FvoytnNC2DKnVrVlHOVcGUL9CnmhDNMA2/nskjIf2IoiG9J0qLY
# /er1UA1HNuIGUyqjlygiSPwK2NnFApi1JD+Uef5c47kh7pW1Kj7RnchpFeY9MekP # r8duvHdQJ9Li2Pq9guySb9mvUL60ogslCO9gkh6FiEDwMrwUr8Wja6jFpUTny8tg
# QRia7cEaUYU4sqCiJVdDJpefLvPT9EdthlQx75ldx+AwZf2a9T7uQRSBh8tpxPdI # 0N0cnCN2w4fKkp5qZcbUYFYicLSb/6A7pHCtX6xnjqwhmJoib3vkKJyVxbuFLRhV
# DDkKiWMwjKTrAY09A3I/jidqPuc8PvX+sqxqyZEN2h4GA0Edjmk64nkIukAK18K5 # XxH95b0LHeNhifn3jvo2j+/4QV10jEpXVW+iC9BsTtR69xvTjU51ZgP7BR4YDEWq
# nALDLO9SMTxpAwQIHRDtZeTClvAPCEoy1vtPD7f+eqHqStuu+XCkfRjXEpX9+h9f # 7JsylSOv5B5THTDXRf184URzFhTyb8OZQKY7mqMh7c8J8w1sEM4XDUF2UZNy829N
# rsB0/BgD5CBf3ELLAa8TefMfHZWEJRTPNrbXMKizSrUSkVv/3HP/ZsJpwaz5My2R # VCzG2tfdEXZaHxF8RmxpQYBxyhZwY1rotuIS+gfN2eq+hkAT3ipGn8/KmDwDtzAb
# byc3Ah9bT76eBJkyfT5FN9v/KQ0HnxhRMs6HHhTmNx+LztYci+vHf0D3QH1eCjZW # nfuXjApgeZqwgcYJ8pDJ+y/xU6ouzJz1Bve5TTihkiA7wQsQe6R60Zk9dPdNzw0M
# ZRjp1mOyxpPU2mDMG6gelvJse1JzRADo7YIok/J3Ccbm8MbBbm85iogFltFHecHF # K5niRzuQZAt4GI96FhjhlUWcUZOCkv/JXM/OGu/rgSplYwdmPLzzfDtXyuy/GCU5
# EFwrsDGBFnNYHMhcbarQNA+gY2e2l9fAkX3MjI7Uklkoz74/P6KIqe5jcd9FPCbb # I4l08g6iifXypMgoYkkceOAAz4vx1x0BOnZWfI3fSwqNUvoN7ncTT+MB4Vpvf1QB
# SbYH9OLsteeYOQIDAQABo4IBNjCCATIwHQYDVR0OBBYEFBa/IDLbY475VQyKiZSw # ppjBAQUuvui6eCG0MCVNAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUmfIngFzZEZlP
# 47l0/cypMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY # kjDOVluBSDDaanEwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD
# MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG
# BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# hvcNAQELBQADggIBACDDIxElfXlG5YKcKrLPSS+f3JWZprwKEiASvivaHTBRlXtA # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBANxHtu3FzIabaDbW
# s+TkadcsEei+9w5vmF5tCUzTH4c0nCI7bZxnsL+S6XsiOs3Z1V4WX+IwoXUJ4zLv # qswdKBlAhKXRCN+5CSMiv2TYa4i2QuWIm+99piwAhDhADfbqor1zyLi95Y6GQnvI
# s0+mT4vjGDtYfKQ/bsmJKar2c99m/fHv1Wm2CTcyaePvi86Jh3UyLjdRILWbtzs4 # WUgdeC7oL1ZtZye92zYK+EIfwYZmhS+CH4infAzUvscHZF3wlrJUfPUIDGVP0lCY
# oImFMwwKbzHdPopxrBhgi+C1YZshosWLlgzyuxjUl+qNg1m52MJmf11loI7D9HJo # Vse9mguvG0dqkY4ayQPEHOvJubgZZaOdg/N8dInd6fGeOc+0DoGzB+LieObJ2Q0A
# aQzd+rf928Y8rvULmg2h/G50o+D0UJ1Fa/cJJaHfB3sfKw9X6GrtXYGjmM3+g+Ah # tEt3XN3iX8Cp6+dZTX8xwE/LvhRwPpb/+nKshO7TVuvenwdTwqB/LT6CNPaElwFe
# aVsfupKXNtOFu5tnLKvAH5OIjEDYV1YKmlXuBuhbYassygPFMmNgG2Ank3drEcDc # KxKrqRTPMbHeg+i+KnBLfwmhEXsMg2s1QX7JIxfvT96md0eiMjiMEO22LbOzmLMN
# ZhCXXqpRszNo1F6Gu5JCpQZXbOJM9Ue5PlJKtmImAYIGsw+pnHy/r5ggSYOp4g5Z # d3LINowAnRBAJtX+3/e390B9sMGMHp+a1V+hgs62AopBl0p/00li30DN5wEQ5If3
# 1oU9GhVCM3V0T9adee6OUXBk1rE4dZc/UsPlj0qoiljL+lN1A5gkmmz7k5tIObVG # 5Zk7b/T6pEx6rJUDYCti7zCbikjKTanBnOc99zGMlej5X+fC/k5ExUCrOs3/VzGR
# B7dJdz8J0FwXRE5qYu1AdvauVbZwGQkL1x8aK/svjEQW0NUyJ29znDHiXl5vLoRT # CZt5LvVQSdWqq/QMzTEmim4sbzASK9imEkjNtZZyvC1CsUcD1voFktld4mKMjE+u
# jjFpshUBi2+IY+mNqbLmj24j5eT+bjDlE3HmNtLPpLcMDYqZ1H+6U6YmaiNmac2j # DEV3IddD+DrRk94nVzNPSuZXewfVOnXHSeqG7xM3V7fl2aL4v1OhL2+JwO1Tx3B0
# RXDAaeEE/uoDMt2dArfJP7M+MDv3zzNNTINeuNEtDVgm9zwfgIUCXnDZuVtiMIIH # irO1O9qbNdJk355bntd1RSVKgM22KFBHnoL7Js7pRhBiaKmVTQGoOb+j1Qa7q+ci
# cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # xGo48Vh9k35BDsJS/DLoXFSPDl4mMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ
# iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw
# OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh
# C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx
# WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc
# UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc
# nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo
# veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi
# YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9
# fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH
# GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X
# KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE
# R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/
# eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3
# FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd
# BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE
# AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB
# Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud
# HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By
# b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO
# MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy
# dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC
# AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk
# bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng
# ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3
# lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC
# gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6
# MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU
# BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh
# VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+
# fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp
# NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI
# qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAsowggIzAgEBMIH4
# oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw
# IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoxMkJDLUUzQUUtNzRFQjElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAG3F2jO4LEMVLwgKG
# XdYMN4FBgOCggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOaUaAIwIhgPMjAyMjA4MDMxMTIwMzRaGA8yMDIyMDgw
# NDExMjAzNFowczA5BgorBgEEAYRZCgQBMSswKTAKAgUA5pRoAgIBADAGAgEAAgEI
# MAcCAQACAhIDMAoCBQDmlbmCAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQB
# hFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEA
# bqvBFqohycksQZhSEJZTiCeQ6hwWlYWRXL1PerCFbLmK+4vgr57BkwFsu5KzBE2z
# i1eHNrssK4BcBLYyIhDIjMSqqtrvclrB6SSDag1WcxZrz42xatvyhKXZd52a5R5Q
# xJw66cvwkDa4UmEtVOnbkaOPyyAql72D9w/XLHY0nmUxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAaEBhVWZuVRdigABAAAB
# oTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCCGB/0CqUv9yvdxWNnaciRCHPCM4WmcYpKBUNiR1Xg+
# yjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIOsIVPE6gYJoIIKOhHIF7UlJ
# Cswl4IJPISvOKInfjtCEMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh
# IDIwMTACEzMAAAGhAYVVmblUXYoAAQAAAaEwIgQgy8rtxrFwV1rcLxTDP8W6Af+y # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1
# Za2AaaMVTQY3E9A2eHMwDQYJKoZIhvcNAQELBQAEggIAEJIvjnhqXrrsyJWHHG9i # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# gKBBM3d51KglP0nJ0dY1zp7uUIBTQ53LVONE1SFiqbw6akydYum6iTaI7tvFWJRW # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# dx5Fq56gt+QY2YO0nsn3zH3ulyUUkhHuMsx5N/pQT6tsEMu6tWCuWucf44JQHlyY # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB
# x8/C+S5QoA7DId3ugccCFpZWUMb76QWReDtalDz3XY/gNSBT2DTJ8WT78WREcYKu # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK
# aBO52cUXKKLtr5ZoPcdEElB/TPuctcC6Hh0+J4Y6PCNwOVPodpEmjMSV0tAN8tZp # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg
# T3cyf9YPnwXNdDiaikZlPSO0pXCM5+KjrBm5hnj6+J8qMc+Qc91UMh2J96kZWVmm # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp
# PsWE7YA4DlWrIWn2mdGLtTJP4sOlqRjigP9rdBFo0oG9c1ySKw3rN7zpGTDnFdkS # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d
# vxeCoLWx48BJ4bE3Siwx6cwrYScmIgyobLb1Ztu5FEmFUn8maX5oo8IY9kPsODOG # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9
# 3y8hPoLOOj2lRdslV9bdjtrbnqeY5Nq/oKuftbX8iD2MYFgWOqeufw3TcQiSz2uF # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR
# tKGolAePdRf1S7c81CC89g7tcwy1TILR9M2JdWOwosAtpFXxX6Vc1OGiRwPAyXBL # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu
# dvDqiTx5zb9k87hfJvwix/oXfo4fNCNdE/i/VbmsAJjcxd+eEBbJ9Oc+oPqC/5zq # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO
# pPtLXUsVfUWX58dPRnYeMAg= # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb
# oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6
# bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t
# AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW
# BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2
# VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu
# bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q
# XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6
# U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt
# I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis
# 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp
# kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0
# sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e
# W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ
# sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7
# Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0
# dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ
# tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow
# ODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAjhJ+EeySRfn2KCNsjn9cF9AUSTqggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOfRUdUwIhgPMjAyMzAzMzEyMDM0MjlaGA8yMDIzMDQwMTIwMzQyOVowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA59FR1QIBADAHAgEAAgIKJDAHAgEAAgIRLzAKAgUA
# 59KjVQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJlOESCa/uRR1x6GunE8
# K/WgHWTpSE31EITDOfTMvDcF4ptngCS5aOc4gfzmhNNehWfP6EOrgoSQzJYZ4YCh
# fYbHNMk56f18sq8t7y2hgR7KixcEo/4HVzeSdaOclHNc4Gn7kCGpMvpT3Xz9Lzc7
# UKWDZ0zkNKnbS8TZLNueVQwfMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAGybkADf26plJIAAQAAAbIwDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQgXhJRuHCXk3arJvifIY3DBe9Ce9EmlP1y6U4XkgL31DkwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCBTeM485+E+t4PEVieUoFKX7PVyLo/nzu+htJPCG04+
# NTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABsm5A
# A39uqZSSAAEAAAGyMCIEIGGWlnNnYHrB5HguWG0/nJd/WvSrCogze+QCpenu3IM5
# MA0GCSqGSIb3DQEBCwUABIICADVOLTuNxeEnBOfZpb7Nv4uf91W/Ho5i99zenDSJ
# x5QHVs+bKXmgc3a7/SSsliAT3zygHc7cH4zARbCZePLTivByKmeG08Ka35eyR+FK
# awSNrI/X+eVIC6nw/egCwviBC1NAG8jHGkuScbHeiiGajvS6lp3ORML7UexMuE4w
# 9SEumoghljCLZMwCSvw+3WxhQoBEZroR8u+PID2RdD0vi85FjKPWcZZijVLqHeFi
# TnuFqwRCLTV0MV+dDCbjwXneIqV+AVlnqb9iDMr3ZhISlRcy9XJNpY5vQBj/wqUW
# vefrmpdz0LNkdtXYThPkyl3mha2KsoQi5SA9zSjlAjFgY3ppmXvi3Frbfqk+iL+f
# l/Qc4+B71jG4t28lTWKteJiHqo+6AUXK2rlAl0d74yvhO6N8lMMtXhdJc8JABYn1
# v2/KKZn5RvPFF8QP7Ac1saIe1+gUFNcsYOLaMm/xl8E6kefWwZnm5Rhm606g1AC/
# N5Wo08aAs0ymTPH91dEbmOURXLbA3vCyG7kbfgnhCs/j7oQHWaFDzEYuXDIA4ICT
# dxPUTltbq3OWdp0PAS8JSEKPQFaOoQEnPa4adrXWxMvOmel8IGqJiQ+BPOaLQG64
# Qu2tMkH/5szb1fsEnCe8SJmy5ESF+kmpnLBtJ17Y9o+9nJHF5ddFmvzy+LUaIqDN
# cOfH
# SIG # End signature block # SIG # End signature block

View File

@ -451,6 +451,10 @@ get_normalized_channel() {
local channel="$(to_lowercase "$1")" 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 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.'; 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 fi
@ -461,6 +465,14 @@ get_normalized_channel() {
echo "LTS" echo "LTS"
return 0 return 0
;; ;;
sts)
echo "STS"
return 0
;;
current)
echo "STS"
return 0
;;
*) *)
echo "$channel" echo "$channel"
return 0 return 0
@ -1127,10 +1139,11 @@ downloadwget() {
get_download_link_from_aka_ms() { get_download_link_from_aka_ms() {
eval $invocation eval $invocation
#quality is not supported for LTS or current channel #quality is not supported for LTS or STS channel
if [[ ! -z "$normalized_quality" && ("$normalized_channel" == "LTS" || "$normalized_channel" == "current") ]]; then #STS maps to current
if [[ ! -z "$normalized_quality" && ("$normalized_channel" == "LTS" || "$normalized_channel" == "STS") ]]; then
normalized_quality="" 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 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'." 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'."
@ -1239,7 +1252,7 @@ generate_akams_links() {
normalized_version="$(to_lowercase "$version")" normalized_version="$(to_lowercase "$version")"
if [[ "$normalized_version" != "latest" ]] && [ -n "$normalized_quality" ]; then 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 return 1
fi fi
@ -1604,18 +1617,23 @@ do
echo " $script_name -h|-?|--help" echo " $script_name -h|-?|--help"
echo "" echo ""
echo "$script_name is a simple command line interface for obtaining dotnet cli." 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 ""
echo "Options:" echo "Options:"
echo " -c,--channel <CHANNEL> Download from the channel specified, Defaults to \`$channel\`." echo " -c,--channel <CHANNEL> Download from the channel specified, Defaults to \`$channel\`."
echo " -Channel" echo " -Channel"
echo " Possible values:" echo " Possible values:"
echo " - Current - most current release" echo " - STS - the most recent Standard Term Support release"
echo " - LTS - most current supported release" echo " - LTS - the most recent Long Term Support release"
echo " - 2-part version in a format A.B - represents a specific release" echo " - 2-part version in a format A.B - represents a specific release"
echo " examples: 2.0; 1.0" echo " examples: 2.0; 1.0"
echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release" echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release"
echo " examples: 5.0.1xx, 5.0.2xx." echo " examples: 5.0.1xx, 5.0.2xx."
echo " Supported since 5.0 release" 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 " 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 " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
echo " -Version" echo " -Version"
@ -1626,7 +1644,7 @@ do
echo " -q,--quality <quality> Download the latest build of specified quality in the channel." echo " -q,--quality <quality> Download the latest build of specified quality in the channel."
echo " -Quality" echo " -Quality"
echo " The possible values are: daily, signed, validated, preview, GA." 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 " 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 " 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." echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality."
@ -1680,10 +1698,10 @@ do
shift shift
done done
say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" say_verbose "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_verbose "- 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_verbose "- 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 "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 if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then
message="Provide credentials via --feed-credential parameter." message="Provide credentials via --feed-credential parameter."
@ -1716,5 +1734,5 @@ else
fi fi
say "Note that the script does not resolve dependencies during installation." 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." say "Installation finished successfully."

2255
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,15 @@
{ {
"name": "setup-dotnet", "name": "setup-dotnet",
"version": "3.0.1", "version": "3.0.2",
"private": true, "private": true,
"description": "setup dotnet action", "description": "setup dotnet action",
"main": "lib/setup-dotnet.js", "main": "lib/setup-dotnet.js",
"scripts": { "scripts": {
"build": "tsc && ncc build", "build": "tsc && ncc build",
"format": "prettier --write **/*.ts", "format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"",
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"",
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix",
"prepare": "husky install", "prepare": "husky install",
"test": "jest --coverage --config ./jest.config.js", "test": "jest --coverage --config ./jest.config.js",
"update-installers": "nwget https://dot.net/v1/dotnet-install.ps1 -O externals/install-dotnet.ps1 && nwget https://dot.net/v1/dotnet-install.sh -O externals/install-dotnet.sh" "update-installers": "nwget https://dot.net/v1/dotnet-install.ps1 -O externals/install-dotnet.ps1 && nwget https://dot.net/v1/dotnet-install.sh -O externals/install-dotnet.sh"
@ -24,26 +26,30 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1", "@actions/core": "^1.10.0",
"@actions/exec": "^1.0.4", "@actions/exec": "^1.0.4",
"@actions/github": "^1.1.0", "@actions/github": "^1.1.0",
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"@actions/io": "^1.0.2", "@actions/io": "^1.0.2",
"fast-xml-parser": "^3.15.1", "fast-xml-parser": "^4.0.10",
"semver": "^6.3.0", "semver": "^6.3.0"
"xmlbuilder": "^13.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.0.2", "@types/jest": "^27.0.2",
"@types/node": "^16.11.25", "@types/node": "^16.11.25",
"@types/semver": "^6.2.2", "@types/semver": "^6.2.2",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@vercel/ncc": "^0.33.4", "@vercel/ncc": "^0.33.4",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-jest": "^27.2.1",
"husky": "^8.0.1", "husky": "^8.0.1",
"jest": "^27.2.5", "jest": "^27.2.5",
"jest-circus": "^27.2.5", "jest-circus": "^27.2.5",
"prettier": "^1.19.1", "prettier": "^2.8.4",
"ts-jest": "^27.0.5", "ts-jest": "^27.0.5",
"typescript": "^3.9.7", "typescript": "^4.8.4",
"wget-improved": "^3.2.1" "wget-improved": "^3.2.1"
}, },
"jest": { "jest": {

View File

@ -1,154 +1,196 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import * as xmlbuilder from 'xmlbuilder'; import {XMLParser, XMLBuilder} from 'fast-xml-parser';
import * as xmlParser from 'fast-xml-parser';
export function configAuthentication(
export function configAuthentication( feedUrl: string,
feedUrl: string, existingFileLocation = '',
existingFileLocation: string = '', processRoot: string = process.cwd()
processRoot: string = process.cwd() ) {
) { const existingNuGetConfig: string = path.resolve(
const existingNuGetConfig: string = path.resolve( processRoot,
processRoot, existingFileLocation === ''
existingFileLocation === '' ? getExistingNugetConfig(processRoot)
? getExistingNugetConfig(processRoot) : existingFileLocation
: existingFileLocation );
);
const tempNuGetConfig: string = path.resolve(
const tempNuGetConfig: string = path.resolve( processRoot,
processRoot, '../',
'../', 'nuget.config'
'nuget.config' );
);
writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig);
writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig); }
}
function isValidKey(key: string): boolean {
function isValidKey(key: string): boolean { return /^[\w\-.]+$/i.test(key);
return /^[\w\-\.]+$/i.test(key); }
}
function getExistingNugetConfig(processRoot: string) {
function getExistingNugetConfig(processRoot: string) { const defaultConfigName = 'nuget.config';
const defaultConfigName = 'nuget.config'; const configFileNames = fs
const configFileNames = fs .readdirSync(processRoot)
.readdirSync(processRoot) .filter(filename => filename.toLowerCase() === defaultConfigName);
.filter(filename => filename.toLowerCase() === defaultConfigName); if (configFileNames.length) {
if (configFileNames.length) { return configFileNames[0];
return configFileNames[0]; }
} return defaultConfigName;
return defaultConfigName; }
}
function writeFeedToFile(
function writeFeedToFile( feedUrl: string,
feedUrl: string, existingFileLocation: string,
existingFileLocation: string, tempFileLocation: string
tempFileLocation: string ) {
) { core.info(
core.info( `dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}`
`dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}` );
); const sourceKeys: string[] = [];
let xml: xmlbuilder.XMLElement; let owner: string = core.getInput('owner');
let sourceKeys: string[] = []; const sourceUrl: string = feedUrl;
let owner: string = core.getInput('owner'); if (!owner) {
let sourceUrl: string = feedUrl; owner = github.context.repo.owner;
if (!owner) { }
owner = github.context.repo.owner;
} if (!process.env.NUGET_AUTH_TOKEN) {
throw new Error(
if (!process.env.NUGET_AUTH_TOKEN) { '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}}'
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
if (fs.existsSync(existingFileLocation)) { const curContents: string = fs.readFileSync(existingFileLocation, 'utf8');
// get key from existing NuGet.config so NuGet/dotnet can match credentials
const curContents: string = fs.readFileSync(existingFileLocation, 'utf8'); const parserOptions = {
const json = xmlParser.parse(curContents, {ignoreAttributes: false}); ignoreAttributes: false
};
if (typeof json.configuration === 'undefined') { const parser = new XMLParser(parserOptions);
throw new Error(`The provided NuGet.config seems invalid.`); const json = parser.parse(curContents);
}
if (typeof json.configuration.packageSources != 'undefined') { if (typeof json.configuration === 'undefined') {
if (typeof json.configuration.packageSources.add != 'undefined') { throw new Error(`The provided NuGet.config seems invalid.`);
// file has at least one <add> }
if (typeof json.configuration.packageSources.add[0] === 'undefined') { if (json.configuration?.packageSources?.add) {
// file has only one <add> const packageSources = json.configuration.packageSources.add;
if (
json.configuration.packageSources.add['@_value'] if (Array.isArray(packageSources)) {
.toLowerCase() packageSources.forEach(source => {
.includes(feedUrl.toLowerCase()) const value = source['@_value'];
) { core.debug(`source '${value}'`);
const key = json.configuration.packageSources.add['@_key']; if (value.toLowerCase().includes(feedUrl.toLowerCase())) {
sourceKeys.push(key); const key = source['@_key'];
core.debug(`Found a URL with key ${key}`); sourceKeys.push(key);
} core.debug(`Found a URL with key ${key}`);
} else { }
// file has 2+ <add> });
for ( } else {
let i = 0; if (
i < json.configuration.packageSources.add.length; packageSources['@_value']
i++ .toLowerCase()
) { .includes(feedUrl.toLowerCase())
const source = json.configuration.packageSources.add[i]; ) {
const value = source['@_value']; const key = packageSources['@_key'];
core.debug(`source '${value}'`); sourceKeys.push(key);
if (value.toLowerCase().includes(feedUrl.toLowerCase())) { core.debug(`Found a URL with key ${key}`);
const key = source['@_key']; }
sourceKeys.push(key); }
core.debug(`Found a URL with key ${key}`); }
} }
}
} const xmlSource: any[] = [
} {
} '?xml': [
} {
'#text': ''
xml = xmlbuilder }
.create('configuration') ],
.ele('config') ':@': {
.ele('add', {key: 'defaultPushSource', value: sourceUrl}) '@_version': '1.0'
.up() }
.up(); },
{
if (!sourceKeys.length) { configuration: [
let keystring = 'Source'; {
xml = xml config: [
.ele('packageSources') {
.ele('add', {key: keystring, value: sourceUrl}) add: [],
.up() ':@': {
.up(); '@_key': 'defaultPushSource',
sourceKeys.push(keystring); '@_value': sourceUrl
} }
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." ];
);
} if (!sourceKeys.length) {
const keystring = 'Source';
xml = xml
.ele(key) xmlSource[1].configuration.push({
.ele('add', {key: 'Username', value: owner}) packageSources: [
.up() {
.ele('add', { add: [],
key: 'ClearTextPassword', ':@': {
value: process.env.NUGET_AUTH_TOKEN '@_key': keystring,
}) '@_value': sourceUrl
.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 sourceKeys.push(keystring);
// process.platform == 'win32' }
// ? '%NUGET_AUTH_TOKEN%'
// : '$NUGET_AUTH_TOKEN' const packageSourceCredentials: any[] = [];
sourceKeys.forEach(key => {
const output = xml.end({pretty: true}); if (!isValidKey(key)) {
fs.writeFileSync(tempFileLocation, output); throw new Error(
} "Source name can contain letters, numbers, and '-', '_', '.' symbols only. Please, fix source name in NuGet.config and try again."
);
}
packageSourceCredentials.push({
[key]: [
{
add: [],
':@': {
'@_key': 'Username',
'@_value': owner
}
},
{
add: [],
':@': {
'@_key': 'ClearTextPassword',
'@_value': process.env.NUGET_AUTH_TOKEN
}
}
]
});
});
xmlSource[1].configuration.push({
packageSourceCredentials
});
const xmlBuilderOptions = {
format: true,
ignoreAttributes: false,
preserveOrder: true,
allowBooleanAttributes: true,
suppressBooleanAttributes: true,
suppressEmptyNode: true
};
const builder = new XMLBuilder(xmlBuilderOptions);
const output = builder.build(xmlSource).trim();
fs.writeFileSync(tempFileLocation, output);
}

View File

@ -1,272 +1,278 @@
// Load tempDirectory before it gets wiped by tool-cache // Load tempDirectory before it gets wiped by tool-cache
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as hc from '@actions/http-client'; import * as hc from '@actions/http-client';
import {chmodSync} from 'fs'; import {chmodSync} from 'fs';
import {readdir} from 'fs/promises'; import path from 'path';
import path from 'path'; import os from 'os';
import semver from 'semver'; import semver from 'semver';
import {IS_LINUX, IS_WINDOWS} from './utils'; import {IS_LINUX, IS_WINDOWS} from './utils';
import {QualityOptions} from './setup-dotnet'; import {QualityOptions} from './setup-dotnet';
export interface DotnetVersion { export interface DotnetVersion {
type: string; type: string;
value: string; value: string;
qualityFlag: boolean; qualityFlag: boolean;
} }
export class DotnetVersionResolver { const QUALITY_INPUT_MINIMAL_MAJOR_TAG = 6;
private inputVersion: string; const LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG = 5;
private resolvedArgument: DotnetVersion; export class DotnetVersionResolver {
private inputVersion: string;
constructor(version: string) { private resolvedArgument: DotnetVersion;
this.inputVersion = version.trim();
this.resolvedArgument = {type: '', value: '', qualityFlag: false}; constructor(version: string) {
} this.inputVersion = version.trim();
this.resolvedArgument = {type: '', value: '', qualityFlag: false};
private async resolveVersionInput(): Promise<void> { }
if (!semver.validRange(this.inputVersion)) {
throw new Error( private async resolveVersionInput(): Promise<void> {
`'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x` if (!semver.validRange(this.inputVersion) && !this.isLatestPatchSyntax()) {
); throw new Error(
} `The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx`
if (semver.valid(this.inputVersion)) { );
this.resolvedArgument.type = 'version'; }
this.resolvedArgument.value = this.inputVersion; if (semver.valid(this.inputVersion)) {
} else { this.createVersionArgument();
const [major, minor] = this.inputVersion.split('.'); } else {
await this.createChannelArgument();
if (this.isNumericTag(major)) { }
this.resolvedArgument.type = 'channel'; }
if (this.isNumericTag(minor)) {
this.resolvedArgument.value = `${major}.${minor}`; private isNumericTag(versionTag): boolean {
} else { return /^\d+$/.test(versionTag);
const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { }
allowRetries: true,
maxRetries: 3 private isLatestPatchSyntax() {
}); const majorTag = this.inputVersion.match(
this.resolvedArgument.value = await this.getLatestVersion( /^(?<majorTag>\d+)\.\d+\.\d{1}x{2}$/
httpClient, )?.groups?.majorTag;
[major, minor] if (
); majorTag &&
} parseInt(majorTag) < LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG
} ) {
this.resolvedArgument.qualityFlag = +major >= 6 ? true : false; throw new Error(
} `The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! The A.B.Cxx syntax is available since the .NET 5.0 release.`
} );
}
private isNumericTag(versionTag): boolean { return majorTag ? true : false;
return /^\d+$/.test(versionTag); }
}
private createVersionArgument() {
public async createDotNetVersion(): Promise<{ this.resolvedArgument.type = 'version';
type: string; this.resolvedArgument.value = this.inputVersion;
value: string; }
qualityFlag: boolean;
}> { private async createChannelArgument() {
await this.resolveVersionInput(); this.resolvedArgument.type = 'channel';
if (!this.resolvedArgument.type) { const [major, minor] = this.inputVersion.split('.');
return this.resolvedArgument; if (this.isLatestPatchSyntax()) {
} this.resolvedArgument.value = this.inputVersion;
if (IS_WINDOWS) { } else if (this.isNumericTag(major) && this.isNumericTag(minor)) {
this.resolvedArgument.type = this.resolvedArgument.value = `${major}.${minor}`;
this.resolvedArgument.type === 'channel' ? '-Channel' : '-Version'; } else if (this.isNumericTag(major)) {
} else { this.resolvedArgument.value = await this.getLatestByMajorTag(major);
this.resolvedArgument.type = } else {
this.resolvedArgument.type === 'channel' ? '--channel' : '--version'; // If "dotnet-version" is specified as *, x or X resolve latest version of .NET explicitly from LTS channel. The version argument will default to "latest" by install-dotnet script.
} this.resolvedArgument.value = 'LTS';
return this.resolvedArgument; }
} this.resolvedArgument.qualityFlag =
parseInt(major) >= QUALITY_INPUT_MINIMAL_MAJOR_TAG ? true : false;
private async getLatestVersion( }
httpClient: hc.HttpClient,
versionParts: string[] public async createDotNetVersion(): Promise<DotnetVersion> {
): Promise<string> { await this.resolveVersionInput();
const response = await httpClient.getJson<any>( if (!this.resolvedArgument.type) {
DotnetVersionResolver.DotNetCoreIndexUrl return this.resolvedArgument;
); }
const result = response.result || {}; if (IS_WINDOWS) {
let releasesInfo: any[] = result['releases-index']; this.resolvedArgument.type =
this.resolvedArgument.type === 'channel' ? '-Channel' : '-Version';
let releaseInfo = releasesInfo.find(info => { } else {
let sdkParts: string[] = info['channel-version'].split('.'); this.resolvedArgument.type =
return sdkParts[0] === versionParts[0]; this.resolvedArgument.type === 'channel' ? '--channel' : '--version';
}); }
return this.resolvedArgument;
if (!releaseInfo) { }
throw new Error(
`Could not find info for version ${versionParts.join('.')} at ${ private async getLatestByMajorTag(majorTag: string): Promise<string> {
DotnetVersionResolver.DotNetCoreIndexUrl const httpClient = new hc.HttpClient('actions/setup-dotnet', [], {
}` allowRetries: true,
); maxRetries: 3
} });
const response = await httpClient.getJson<any>(
return releaseInfo['channel-version']; DotnetVersionResolver.DotNetCoreIndexUrl
} );
const result = response.result || {};
static DotNetCoreIndexUrl: string = const releasesInfo: any[] = result['releases-index'];
'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json';
} const releaseInfo = releasesInfo.find(info => {
const sdkParts: string[] = info['channel-version'].split('.');
export class DotnetCoreInstaller { return sdkParts[0] === majorTag;
private version: string; });
private quality: QualityOptions;
private static readonly installationDirectoryWindows = path.join( if (!releaseInfo) {
process.env['PROGRAMFILES'] + '', throw new Error(
'dotnet' `Could not find info for version with major tag: "${majorTag}" at ${DotnetVersionResolver.DotNetCoreIndexUrl}`
); );
private static readonly installationDirectoryLinux = '/usr/share/dotnet'; }
private static readonly installationDirectoryMac = path.join(
process.env['HOME'] + '', return releaseInfo['channel-version'];
'.dotnet' }
);
static DotNetCoreIndexUrl =
static addToPath() { 'https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json';
if (process.env['DOTNET_INSTALL_DIR']) { }
core.addPath(process.env['DOTNET_INSTALL_DIR']);
core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']); export class DotnetCoreInstaller {
} else { private version: string;
if (IS_WINDOWS) { private quality: QualityOptions;
core.addPath(DotnetCoreInstaller.installationDirectoryWindows);
core.exportVariable( static {
'DOTNET_ROOT', const installationDirectoryWindows = path.join(
DotnetCoreInstaller.installationDirectoryWindows process.env['PROGRAMFILES'] + '',
); 'dotnet'
} else if (IS_LINUX) { );
core.addPath(DotnetCoreInstaller.installationDirectoryLinux); const installationDirectoryLinux = '/usr/share/dotnet';
core.exportVariable( const installationDirectoryMac = path.join(
'DOTNET_ROOT', process.env['HOME'] + '',
DotnetCoreInstaller.installationDirectoryLinux '.dotnet'
); );
} else { const dotnetInstallDir: string | undefined =
// This is the default set in install-dotnet.sh process.env['DOTNET_INSTALL_DIR'];
core.addPath(DotnetCoreInstaller.installationDirectoryMac); if (dotnetInstallDir) {
core.exportVariable( process.env['DOTNET_INSTALL_DIR'] =
'DOTNET_ROOT', this.convertInstallPathToAbsolute(dotnetInstallDir);
DotnetCoreInstaller.installationDirectoryMac } else {
); if (IS_WINDOWS) {
} process.env['DOTNET_INSTALL_DIR'] = installationDirectoryWindows;
} } else {
} process.env['DOTNET_INSTALL_DIR'] = IS_LINUX
? installationDirectoryLinux
constructor(version: string, quality: QualityOptions) { : installationDirectoryMac;
this.version = version; }
this.quality = quality; }
} }
private setQuality( constructor(version: string, quality: QualityOptions) {
dotnetVersion: DotnetVersion, this.version = version;
scriptArguments: string[] this.quality = quality;
): void { }
const option = IS_WINDOWS ? '-Quality' : '--quality';
if (dotnetVersion.qualityFlag) { private static convertInstallPathToAbsolute(installDir: string): string {
scriptArguments.push(option, this.quality); let transformedPath;
} else { if (path.isAbsolute(installDir)) {
core.warning( transformedPath = installDir;
`'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A and A.x formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.` } else {
); transformedPath = installDir.startsWith('~')
} ? path.join(os.homedir(), installDir.slice(1))
} : (transformedPath = path.join(process.cwd(), installDir));
}
public async installDotnet(): Promise<string> { return path.normalize(transformedPath);
const windowsDefaultOptions = [ }
'-NoLogo',
'-Sta', static addToPath() {
'-NoProfile', core.addPath(process.env['DOTNET_INSTALL_DIR']!);
'-NonInteractive', core.exportVariable('DOTNET_ROOT', process.env['DOTNET_INSTALL_DIR']);
'-ExecutionPolicy', }
'Unrestricted',
'-Command' private setQuality(
]; dotnetVersion: DotnetVersion,
const scriptName = IS_WINDOWS ? 'install-dotnet.ps1' : 'install-dotnet.sh'; scriptArguments: string[]
const escapedScript = path ): void {
.join(__dirname, '..', 'externals', scriptName) const option = IS_WINDOWS ? '-Quality' : '--quality';
.replace(/'/g, "''"); if (dotnetVersion.qualityFlag) {
let scriptArguments: string[]; scriptArguments.push(option, this.quality);
let scriptPath = ''; } else {
core.warning(
const versionResolver = new DotnetVersionResolver(this.version); `The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${this.version}. 'dotnet-quality' input is ignored.`
const dotnetVersion = await versionResolver.createDotNetVersion(); );
}
if (IS_WINDOWS) { }
scriptArguments = ['&', `'${escapedScript}'`];
public async installDotnet(): Promise<string | null> {
if (dotnetVersion.type) { const windowsDefaultOptions = [
scriptArguments.push(dotnetVersion.type, dotnetVersion.value); '-NoLogo',
} '-Sta',
'-NoProfile',
if (this.quality) { '-NonInteractive',
this.setQuality(dotnetVersion, scriptArguments); '-ExecutionPolicy',
} 'Unrestricted',
'-Command'
if (process.env['https_proxy'] != null) { ];
scriptArguments.push(`-ProxyAddress ${process.env['https_proxy']}`); const scriptName = IS_WINDOWS ? 'install-dotnet.ps1' : 'install-dotnet.sh';
} const escapedScript = path
// This is not currently an option .join(__dirname, '..', 'externals', scriptName)
if (process.env['no_proxy'] != null) { .replace(/'/g, "''");
scriptArguments.push(`-ProxyBypassList ${process.env['no_proxy']}`); let scriptArguments: string[];
} let scriptPath = '';
if (!process.env['DOTNET_INSTALL_DIR']) { const versionResolver = new DotnetVersionResolver(this.version);
process.env['DOTNET_INSTALL_DIR'] = const dotnetVersion = await versionResolver.createDotNetVersion();
DotnetCoreInstaller.installationDirectoryWindows;
} if (IS_WINDOWS) {
scriptArguments = ['&', `'${escapedScript}'`];
scriptPath =
(await io.which('pwsh', false)) || (await io.which('powershell', true)); if (dotnetVersion.type) {
scriptArguments = windowsDefaultOptions.concat(scriptArguments); scriptArguments.push(dotnetVersion.type, dotnetVersion.value);
} else { }
chmodSync(escapedScript, '777');
scriptPath = await io.which(escapedScript, true); if (this.quality) {
scriptArguments = []; this.setQuality(dotnetVersion, scriptArguments);
}
if (dotnetVersion.type) {
scriptArguments.push(dotnetVersion.type, dotnetVersion.value); if (process.env['https_proxy'] != null) {
} scriptArguments.push(`-ProxyAddress ${process.env['https_proxy']}`);
}
if (this.quality) { // This is not currently an option
this.setQuality(dotnetVersion, scriptArguments); if (process.env['no_proxy'] != null) {
} scriptArguments.push(`-ProxyBypassList ${process.env['no_proxy']}`);
}
if (!process.env['DOTNET_INSTALL_DIR']) {
process.env['DOTNET_INSTALL_DIR'] = IS_LINUX scriptPath =
? DotnetCoreInstaller.installationDirectoryLinux (await io.which('pwsh', false)) || (await io.which('powershell', true));
: DotnetCoreInstaller.installationDirectoryMac; scriptArguments = windowsDefaultOptions.concat(scriptArguments);
} } else {
} chmodSync(escapedScript, '777');
// process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used scriptPath = await io.which(escapedScript, true);
const getExecOutputOptions = { scriptArguments = [];
ignoreReturnCode: true,
env: process.env as {string: string} if (dotnetVersion.type) {
}; scriptArguments.push(dotnetVersion.type, dotnetVersion.value);
const {exitCode, stdout} = await exec.getExecOutput( }
`"${scriptPath}"`,
scriptArguments, if (this.quality) {
getExecOutputOptions this.setQuality(dotnetVersion, scriptArguments);
); }
if (exitCode) { }
throw new Error(`Failed to install dotnet ${exitCode}. ${stdout}`); // process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used
} const getExecOutputOptions = {
ignoreReturnCode: true,
return this.outputDotnetVersion( env: process.env as {string: string}
dotnetVersion.value, };
process.env['DOTNET_INSTALL_DIR'] const {exitCode, stdout, stderr} = await exec.getExecOutput(
); `"${scriptPath}"`,
} scriptArguments,
getExecOutputOptions
private async outputDotnetVersion( );
version, if (exitCode) {
installationPath throw new Error(
): Promise<string> { `Failed to install dotnet, exit code: ${exitCode}. ${stderr}`
let versionsOnRunner: string[] = await readdir( );
path.join(installationPath.replace(/'/g, ''), 'sdk') }
);
return this.parseInstalledVersion(stdout);
let installedVersion = semver.maxSatisfying(versionsOnRunner, version, { }
includePrerelease: true
})!; private parseInstalledVersion(stdout: string): string | null {
const regex = /(?<version>\d+\.\d+\.\d+[a-z0-9._-]*)/gm;
return installedVersion; const matchedResult = regex.exec(stdout);
}
} if (!matchedResult) {
core.warning(`Failed to parse installed by the script version of .NET`);
return null;
}
return matchedResult.groups!.version;
}
}

View File

@ -13,7 +13,7 @@ const qualityOptions = [
'ga' 'ga'
] as const; ] as const;
export type QualityOptions = typeof qualityOptions[number]; export type QualityOptions = (typeof qualityOptions)[number];
export async function run() { export async function run() {
try { try {
@ -27,11 +27,11 @@ export async function run() {
// Proxy, auth, (etc) are still set up, even if no version is identified // Proxy, auth, (etc) are still set up, even if no version is identified
// //
const versions = core.getMultilineInput('dotnet-version'); const versions = core.getMultilineInput('dotnet-version');
const installedDotnetVersions: string[] = []; const installedDotnetVersions: (string | null)[] = [];
const globalJsonFileInput = core.getInput('global-json-file'); const globalJsonFileInput = core.getInput('global-json-file');
if (globalJsonFileInput) { if (globalJsonFileInput) {
const globalJsonPath = path.join(process.cwd(), globalJsonFileInput); const globalJsonPath = path.resolve(process.cwd(), globalJsonFileInput);
if (!fs.existsSync(globalJsonPath)) { if (!fs.existsSync(globalJsonPath)) {
throw new Error( throw new Error(
`The specified global.json file '${globalJsonFileInput}' does not exist` `The specified global.json file '${globalJsonFileInput}' does not exist`
@ -46,6 +46,10 @@ export async function run() {
const globalJsonPath = path.join(process.cwd(), 'global.json'); const globalJsonPath = path.join(process.cwd(), 'global.json');
if (fs.existsSync(globalJsonPath)) { if (fs.existsSync(globalJsonPath)) {
versions.push(getVersionFromGlobalJson(globalJsonPath)); versions.push(getVersionFromGlobalJson(globalJsonPath));
} else {
core.info(
`The global.json wasn't found in the root directory. No .NET version will be installed.`
);
} }
} }
@ -54,7 +58,7 @@ export async function run() {
if (quality && !qualityOptions.includes(quality)) { if (quality && !qualityOptions.includes(quality)) {
throw new Error( throw new Error(
`${quality} is not a supported value for 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.` `Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`
); );
} }
@ -74,19 +78,7 @@ export async function run() {
auth.configAuthentication(sourceUrl, configFile); auth.configAuthentication(sourceUrl, configFile);
} }
const comparisonRange: string = globalJsonFileInput outputInstalledVersion(installedDotnetVersions, globalJsonFileInput);
? versions[versions.length - 1]!
: '*';
const versionToOutput = semver.maxSatisfying(
installedDotnetVersions,
comparisonRange,
{
includePrerelease: true
}
);
core.setOutput('dotnet-version', versionToOutput);
const matchersPath = path.join(__dirname, '..', '.github'); const matchersPath = path.join(__dirname, '..', '.github');
core.info(`##[add-matcher]${path.join(matchersPath, 'csc.json')}`); core.info(`##[add-matcher]${path.join(matchersPath, 'csc.json')}`);
@ -96,7 +88,7 @@ export async function run() {
} }
function getVersionFromGlobalJson(globalJsonPath: string): string { function getVersionFromGlobalJson(globalJsonPath: string): string {
let version: string = ''; let version = '';
const globalJson = JSON.parse( const globalJson = JSON.parse(
// .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649 // .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim() fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim()
@ -112,4 +104,37 @@ function getVersionFromGlobalJson(globalJsonPath: string): string {
return version; return version;
} }
function outputInstalledVersion(
installedVersions: (string | null)[],
globalJsonFileInput: string
): void {
if (!installedVersions.length) {
core.info(`The 'dotnet-version' output will not be set.`);
return;
}
if (installedVersions.includes(null)) {
core.warning(
`Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.`
);
return;
}
if (globalJsonFileInput) {
const versionToOutput = installedVersions.at(-1); // .NET SDK version parsed from the global.json file is installed last
core.setOutput('dotnet-version', versionToOutput);
return;
}
const versionToOutput = semver.maxSatisfying(
installedVersions as string[],
'*',
{
includePrerelease: true
}
);
core.setOutput('dotnet-version', versionToOutput);
}
run(); run();

View File

@ -35,6 +35,7 @@
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */ /* Additional Checks */
"useUnknownInCatchVariables": false, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
@ -48,7 +49,8 @@
// "typeRoots": [], /* List of folders to include type definitions from. */ // "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */ // "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"resolveJsonModule": true, /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */