Compare commits

..

8 Commits

Author SHA1 Message Date
Zachary Taylor
8e6bd2905e Merge pull request #576 from actions/remove-cdn-fallback-v2
`v2` - Remove `dotnetcli.blob.core.windows.net` storage account fallback logic and update install scripts
2025-02-10 10:11:28 -05:00
Zachary Taylor
603ff3ab1c Update workflow.yml to remove outdated URLs 2025-01-30 16:15:18 -05:00
Zachary Taylor
d284574c05 Update workflow.yml 2025-01-29 16:49:25 -05:00
Zachary Taylor
bac33b41f7 add updated install scripts 2025-01-29 21:37:00 +00:00
Zachary Taylor
f56a6a6861 fix ci by adding unstaged index.js changes 2025-01-02 02:23:32 +00:00
Zachary Taylor
fb81433816 Remove extra spacing 2025-01-02 01:03:07 +00:00
Zachary Taylor
9798ae12d7 Remove CDN fallback logic (v2) 2025-01-02 00:39:19 +00:00
HarithaVattikuti
aab9aab748 V2 - Use new .NET CDN URLs and update to latest install scripts (#568)
* new cdn url changes

* Fix Workflow

* Fix CI failures for ubuntu-latest

* Fix upload-artifact@v4

* Fix proxy test issue

* Fix Proxy env issue

* Fix globalization invariant issue

* Fix libssl issue

* Install libssl1.1 to fix CI failures

* Add fallback logic

* Fix CI failures dist

* Update installer.ts

* Update installer.ts

* Fix proxy test failure

* Update signed version

---------

Co-authored-by: Priya Gupta <147705955+priyagupta108@users.noreply.github.com>
2024-12-26 15:00:58 -06:00
116 changed files with 28789 additions and 200679 deletions

View File

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

View File

@@ -1,51 +0,0 @@
// 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,2 +1 @@
* text=auto eol=lf .licenses/** -diff linguist-generated=true
.licenses/** -diff linguist-generated=true

2
.github/CODEOWNERS vendored
View File

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

View File

@@ -1,17 +0,0 @@
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,3 +1,8 @@
# `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:
@@ -12,6 +17,36 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
call-check-dist: check-dist:
name: Check dist/ runs-on: ubuntu-latest
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@v4
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
path: dist/

View File

@@ -1,14 +1,70 @@
name: CodeQL analysis # For most projects, this workflow file will not need changing; you simply need
# 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:
branches: [main] # The branches below must be a subset of the branches above
branches: [ main ]
schedule: schedule:
- cron: '0 3 * * 0' - cron: '23 19 * * 0'
jobs: jobs:
call-codeQL-analysis: analyze:
name: CodeQL analysis name: Analyze
uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main runs-on: ubuntu-latest
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,428 +0,0 @@
name: e2e tests
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- main
- releases/*
paths-ignore:
- '**.md'
jobs:
test-setup-multiple-versions:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 2.2.402, 3.1.404 and 3.0.x
uses: ./
with:
dotnet-version: |
2.2.402
3.1.404
3.0.x
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^2.2.402$", "^3.1.404$", "^3.0"
test-setup-full-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macos-13]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
# Side-by-side install of 2.2 and 3.1 used for the test project
- name: Setup dotnet 2.2.402
uses: ./
with:
dotnet-version: 2.2.402
- name: Setup dotnet 3.1.201
uses: ./
with:
dotnet-version: 3.1.201
# We are including this variable to force the generation of the nuget config file to verify that it is created in the correct place
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$", "^2.2.402$" -CheckNugetConfig
test-setup-without-patch-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
# 2.0, 3.0, 5.0 needs to be in single quotes to interpret as a string instead of as an integer
- name: Setup dotnet '3.1'
uses: ./
with:
dotnet-version: '3.1'
- name: Setup dotnet '2.2'
uses: ./
with:
dotnet-version: '2.2'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2"
test-setup-prerelease-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macos-13]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet '3.1.100-preview1-014459'
uses: ./
with:
dotnet-version: '3.1.100-preview1-014459'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "3.1.100-preview1-014459"
test-setup-latest-patch-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 3.1.x
uses: ./
with:
dotnet-version: 3.1.x
- name: Setup dotnet 2.2.X
uses: ./
with:
dotnet-version: 2.2.X
- name: Verify dotnet
shell: pwsh
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-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.4xx
uses: ./
with:
dotnet-version: '6.0.4xx'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^6\.0\.4\d{2}"
test-setup-with-wildcard:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 3.1.*
uses: ./
with:
dotnet-version: 3.1.*
- name: Setup dotnet 2.2.*
uses: ./
with:
dotnet-version: 2.2.*
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1", "^2.2"
test-setup-global-json-specified-and-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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:
dotnet-version: 3.1
global-json-file: ./subdirectory/global.json
- name: Verify dotnet
shell: pwsh
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-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Write global.json
shell: bash
run: |
mkdir subdirectory
echo '{"sdk":{"version": "2.2.207","rollForward": "latestFeature"}}' > ./subdirectory/global.json
- name: Setup dotnet
uses: ./
with:
global-json-file: ./subdirectory/global.json
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^2.2"
test-setup-with-dotnet-quality:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 7.0 with preview quality
uses: ./
with:
dotnet-version: '7.0'
dotnet-quality: 'preview'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^7\.0\.\d+-"
test-setup-with-cache:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macos-latest]
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Copy NuGet lock file to root
shell: bash
run: cp ./__tests__/e2e-test-csproj/packages.lock.json ./packages.lock.json
- name: Setup .NET Core 3.1
id: setup-dotnet
uses: ./
with:
dotnet-version: 3.1
cache: true
- name: Verify Cache
if: steps.setup-dotnet.outputs.cache-hit == 'true'
shell: bash
run: if [[ -e ${NUGET_PACKAGES} ]]; then exit 0; else exit 1; fi
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1"
test-setup-with-cache-dependency-path:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macos-latest]
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup .NET Core 3.1
id: setup-dotnet
uses: ./
with:
dotnet-version: 3.1
cache: true
cache-dependency-path: './__tests__/e2e-test-csproj/packages.lock.json'
- name: Verify Cache
if: steps.setup-dotnet.outputs.cache-hit == 'true'
shell: bash
run: if [[ -e ${NUGET_PACKAGES} ]]; then exit 0; else exit 1; fi
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1"
test-dotnet-version-output-during-single-version-installation:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.401
uses: ./
id: step1
with:
dotnet-version: '6.0.401'
- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" }
test-dotnet-version-output-during-multiple-version-installation:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12
uses: ./
id: step2
with:
dotnet-version: |
7.0.100-rc.1.22431.12
6.0.401
5.0.408
- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = "7.0.100-rc.1.22431.12"
if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" }
test-proxy:
runs-on: ubuntu-22.04
container:
image: ubuntu:latest
options: --dns 127.0.0.1
services:
squid-proxy:
image: ubuntu/squid:latest
ports:
- 3128:3128
env:
https_proxy: http://squid-proxy:3128
http_proxy: http://squid-proxy:3128
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Powershell
run: |
apt-get update
apt-get install -y wget apt-transport-https software-properties-common
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
apt-get update
apt-get install -y powershell
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 6.0
uses: ./
with:
dotnet-version: 6.0
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet
shell: pwsh
run: |
__tests__/verify-dotnet.ps1 -Patterns "^6.0" -CheckNugetConfig
test-bypass-proxy:
runs-on: ubuntu-22.04
env:
https_proxy: http://no-such-proxy:3128
no_proxy: github.com,download.visualstudio.microsoft.com,api.nuget.org,builds.dotnet.microsoft.com,ci.dot.net
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
- name: Setup dotnet 3.1.201
uses: ./
with:
dotnet-version: 3.1.201
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 -Patterns "^3.1.201$" -CheckNugetConfig

View File

@@ -10,6 +10,16 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
call-licensed: test:
name: Licensed runs-on: ubuntu-latest
uses: actions/reusable-workflows/.github/workflows/licensed.yml@main name: Check licenses
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,5 +1,4 @@
name: Release new action version name: Release new action version
on: on:
release: release:
types: [released] types: [released]
@@ -21,9 +20,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.2 uses: actions/publish-action@v0.1.0
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-22.04, windows-latest, macOS-latest] operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0', '6.0', '7.0', '8.0'] dotnet-version: ['2.1', '2.2', '3.0', '3.1', '5.0']
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -29,7 +29,9 @@ jobs:
uses: ./ uses: ./
with: with:
dotnet-version: ${{ matrix.dotnet-version }} dotnet-version: ${{ matrix.dotnet-version }}
- name: Verify installed version - name: Check installed version
shell: pwsh shell: pwsh
run: | run: |
__tests__/verify-dotnet.ps1 -Patterns "^${{ matrix.dotnet-version }}" $version = & dotnet --version
Write-Host "Installed version: $version"
if (-not $version.StartsWith("${{ matrix.dotnet-version }}")) { throw "Unexpected version" }

View File

@@ -1,11 +0,0 @@
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

244
.github/workflows/workflow.yml vendored Normal file
View File

@@ -0,0 +1,244 @@
name: Main workflow
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- main
- releases/*
paths-ignore:
- '**.md'
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:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 2.2.402 and 3.1.404
uses: ./
with:
dotnet-version: |
2.2.402
3.1.404
3.0.x
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 2.2.402 3.1.404 '3.0'
test-setup-full-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macos-13]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
# Side-by-side install of 2.2 and 3.1 used for the test project
- name: Setup dotnet 2.2.402
uses: ./
with:
dotnet-version: 2.2.402
- name: Setup dotnet 3.1.201
uses: ./
with:
dotnet-version: 3.1.201
# We are including this veriable to force the generation of the nuget config file to verify that it is created in the correct place
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify nuget config file
shell: pwsh
run: |
if (-Not (Test-Path "../nuget.config")) { throw "nuget file not generated correctly" }
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1.201 2.2.402
test-setup-without-patch-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
# 2.0, 3.0, 5.0 needs to be in single quotes to interpret as a string instead of as an integer
- name: Setup dotnet '3.1'
uses: ./
with:
dotnet-version: '3.1'
- name: Setup dotnet '2.2'
uses: ./
with:
dotnet-version: '2.2'
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1 2.2
test-setup-latest-patch-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 3.1.x
uses: ./
with:
dotnet-version: 3.1.x
- name: Setup dotnet 2.2.x
uses: ./
with:
dotnet-version: 2.2.x
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1 2.2
test-setup-with-wildcard:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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 3.1.*
uses: ./
with:
dotnet-version: 3.1.*
- name: Setup dotnet 2.2.*
uses: ./
with:
dotnet-version: 2.2.*
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 3.1 2.2
test-setup-global-json-specified-and-version:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-22.04, 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","rollForward": "latestFeature"}}' > ./subdirectory/global.json
- name: Setup dotnet
uses: ./
with:
dotnet-version: 3.1
global-json-file: ./subdirectory/global.json
- name: Verify dotnet
shell: pwsh
run: __tests__/verify-dotnet.ps1 2.2 3.1
test-proxy:
runs-on: ubuntu-20.04
container:
image: ubuntu:20.04
options: --dns 127.0.0.1
services:
squid-proxy:
image: ubuntu/squid:latest
ports:
- 3128:3128
env:
https_proxy: http://squid-proxy:3128
http_proxy: http://squid-proxy:3128
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: true
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear tool cache
run: rm -rf "/usr/share/dotnet"
- name: Install curl
run: |
apt update
apt -y install curl libssl1.1 libssl-dev
- name: Setup dotnet 3.1.201
uses: ./
with:
dotnet-version: 3.1.201
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet
run: __tests__/verify-dotnet.sh 3.1.201
test-bypass-proxy:
runs-on: ubuntu-22.04
env:
https_proxy: http://no-such-proxy:3128
no_proxy: github.com,download.visualstudio.microsoft.com,api.nuget.org,builds.dotnet.microsoft.com,ci.dot.net
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear tool cache
run: rm -rf "/usr/share/dotnet"
- name: Setup dotnet 3.1.201
uses: ./
with:
dotnet-version: 3.1.201
source-url: https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: NOTATOKEN
- name: Verify dotnet
run: __tests__/verify-dotnet.sh 3.1.201

4
.gitignore vendored
View File

@@ -3,8 +3,8 @@ global.json
lib/ lib/
node_modules/ node_modules/
__tests__/runner/* __tests__/runner/*
__tests__/e2e-test-csproj/bin/ __tests__/sample-csproj/bin/
__tests__/e2e-test-csproj/obj/ __tests__/sample-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,4 +2,3 @@
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
npm run format npm run format
npm run lint:fix

View File

@@ -3,4 +3,3 @@
# 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

@@ -3,7 +3,6 @@ sources:
allowed: allowed:
- apache-2.0 - apache-2.0
- 0bsd
- bsd-2-clause - bsd-2-clause
- bsd-3-clause - bsd-3-clause
- isc - isc
@@ -12,5 +11,4 @@ allowed:
- unlicense - unlicense
reviewed: reviewed:
npm: npm:
- sax # ISC + MIT

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -1,11 +0,0 @@
// 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'
};

11
.prettierrc.json Normal file
View File

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

252
README.md
View File

@@ -1,7 +1,6 @@
# setup-dotnet # 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) [![GitHub Actions Status](https://github.com/actions/setup-dotnet/workflows/Main%20workflow/badge.svg)](https://github.com/actions/setup-dotnet)
[![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:
@@ -9,139 +8,62 @@ This action sets up a [.NET CLI](https://github.com/dotnet/sdk) environment for
- registering problem matchers for error output - registering problem matchers for error output
- setting up authentication to private package sources like GitHub Packages - setting up authentication to private package sources like GitHub Packages
> **Note**: GitHub hosted runners have some versions of the .NET SDK Please 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
See [action.yml](action.yml) See [action.yml](action.yml)
**Basic**: Basic:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v2
with: with:
dotnet-version: '3.1.x' dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel
- 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 versions:
> 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.
**Multiple version installation**:
```yml ```yml
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v2
with: with:
dotnet-version: | dotnet-version: |
3.1.x 3.1.x
5.0.x 5.0.x
- run: dotnet build <my project> - run: dotnet build <my project>
``` ```
## Supported version syntax Preview version:
The `dotnet-version` input supports following syntax:
- **A.B.C** (e.g 6.0.400, 7.0.100-preview.7.22377.5) - installs exact version of .NET SDK
- **A.B** or **A.B.x** (e.g. 3.1, 3.1.x) - installs the latest patch version of .NET SDK on the channel `3.1`, including prerelease versions (preview, rc)
- **A** or **A.x** (e.g. 3, 3.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc)
- **A.B.Cxx** (e.g. 6.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc).
## Using the `dotnet-quality` input
This input sets up the action to install the latest build of the specified quality in the channel. The possible values of `dotnet-quality` are: **daily**, **signed**, **validated**, **preview**, **ga**.
> **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A', '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:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v2
with: with:
dotnet-version: '6.0.x' dotnet-version: '6.0.x'
dotnet-quality: 'preview' include-prerelease: true
- run: dotnet build <my project> - run: dotnet build <my project>
``` ```
global.json in a subdirectory:
## Using the `global-json-file` input
`setup-dotnet` action can read .NET SDK version from a `global.json` file. Input `global-json-file` is used for specifying the path to the `global.json`. If the file that was supplied to `global-json-file` input doesn't exist, the action will fail with error.
>**Note**: In case both `dotnet-version` and `global-json-file` inputs are used, versions from both inputs will be installed.
```yml ```yml
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v2
with: with:
global-json-file: csharp/global.json global-json-file: csharp/global.json
- run: dotnet build <my project> - run: dotnet build <my project>
working-directory: csharp working-directory: csharp
``` ```
## Caching NuGet Packages Matrix Testing:
The action has a built-in functionality for caching and restoring dependencies. It uses [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under the hood for caching global packages data but requires less configuration settings. The `cache` input is optional, and caching is turned off by default.
The action searches for [NuGet Lock files](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies) (`packages.lock.json`) in the repository root, calculates their hash and uses it as a part of the cache key. If lock file does not exist, this action throws error. Use `cache-dependency-path` for cases when multiple dependency files are used, or they are located in different subdirectories.
> **Warning**: Caching NuGet packages is available since .NET SDK 2.1.500 and 2.2.100 as the NuGet lock file [is available](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies) only for NuGet 4.9 and above.
```yaml ```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.x
cache: true
- run: dotnet restore --locked-mode
```
> **Note**: This action will only restore `global-packages` folder, so you will probably get the [NU1403](https://learn.microsoft.com/nuget/reference/errors-and-warnings/nu1403) error when running `dotnet restore`.
> To avoid this, you can use [`DisableImplicitNuGetFallbackFolder`](https://github.com/dotnet/reproducible-builds/blob/abfe986832aa28597d3340b92469d1a702013d23/Documentation/Reproducible-MSBuild/Techniques/DisableImplicitNuGetFallbackFolder.md) option.
```xml
<PropertyGroup>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
</PropertyGroup>
```
### Reduce caching size
> **Note**: Use [`NUGET_PACKAGES`](https://learn.microsoft.com/nuget/reference/cli-reference/cli-ref-environment-variables) environment variable if available. Some action runners already has huge libraries. (ex. Xamarin)
```yaml
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.x
cache: true
- run: dotnet restore --locked-mode
```
### Caching NuGet packages in monorepos
```yaml
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.x
cache: true
cache-dependency-path: subdir/packages.lock.json
- run: dotnet restore --locked-mode
```
## Matrix Testing
Using `setup-dotnet` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of .NET SDK:
```yml
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -152,44 +74,38 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v2
with: with:
dotnet-version: ${{ matrix.dotnet }} dotnet-version: ${{ matrix.dotnet }}
- name: Execute dotnet - run: dotnet build <my project>
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** Side by Side Testing:
```yml ```yaml
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: name: Dotnet Side by Side testing sample
matrix:
dotnet: [ '2.1.x', '3.1.x', '5.0.x' ]
name: Dotnet ${{ matrix.dotnet }} sample
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v2
id: stepid
with: with:
dotnet-version: ${{ matrix.dotnet }} dotnet-version: |
- name: Create temporary global.json 2.1.x
run: echo '{"sdk":{"version": "${{ steps.stepid.outputs.dotnet-version }}"}}' > ./global.json 3.1.x
- name: Execute dotnet - run: dotnet build <my project>
run: dotnet build <my project> - run: dotnet test <my project>
``` ```
## Setting up authentication for nuget feeds
### Github Package Registry (GPR) Authentication for nuget feeds:
```yml ```yaml
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 # Authenticates packages to push to GPR
- uses: actions/setup-dotnet@v2
with: with:
dotnet-version: '3.1.x' dotnet-version: '3.1.x' # SDK Version to use.
source-url: https://nuget.pkg.github.com/<owner>/index.json source-url: https://nuget.pkg.github.com/<owner>/index.json
env: env:
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
@@ -198,22 +114,19 @@ steps:
run: dotnet pack --configuration Release <my project> run: dotnet pack --configuration Release <my project>
- name: Publish the package to GPR - name: Publish the package to GPR
run: dotnet nuget push <my project>/bin/Release/*.nupkg run: dotnet nuget push <my project>/bin/Release/*.nupkg
```
### Azure Artifacts # Authenticates packages to push to Azure Artifacts
```yml - uses: actions/setup-dotnet@v2
- uses: actions/setup-dotnet@v3
with: with:
source-url: https://pkgs.dev.azure.com/<your-organization>/_packaging/<your-feed-name>/nuget/v3/index.json source-url: https://pkgs.dev.azure.com/<your-organization>/_packaging/<your-feed-name>/nuget/v3/index.json
env: env:
NUGET_AUTH_TOKEN: ${{secrets.AZURE_DEVOPS_PAT}} # Note, create a secret with this name in Settings NUGET_AUTH_TOKEN: ${{secrets.AZURE_DEVOPS_PAT}} # Note, create a secret with this name in Settings
- name: Publish the package to Azure Artifacts - name: Publish the package to Azure Artifacts
run: dotnet nuget push <my project>/bin/Release/*.nupkg run: dotnet nuget push <my project>/bin/Release/*.nupkg
```
### nuget.org # Authenticates packages to push to nuget.org.
```yml # It's only the way to push a package to nuget.org feed for macOS/Linux machines due to API key config store limitations.
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v2
with: with:
dotnet-version: 3.1.x dotnet-version: 3.1.x
- name: Publish the package to nuget.org - name: Publish the package to nuget.org
@@ -221,97 +134,32 @@ steps:
env: env:
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }} NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }}
``` ```
> **Note**: It's the only way to push a package to nuget.org feed for macOS/Linux machines due to API key config store limitations.
# Outputs and environment variables ## Environment Variables to use with dotnet
## Outputs
### `dotnet-version`
Using the **dotnet-version** output it's possible to get the installed by the action .NET SDK version.
**Single version installation**
In case of a single version installation, the `dotnet-version` output contains the version that is installed by the action.
```yaml
- uses: actions/setup-dotnet@v3
id: stepid
with:
dotnet-version: 3.1.422
- run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 3.1.422
```
**Multiple version installation**
In case of a multiple version installation, the `dotnet-version` output contains the latest version that is installed by the action.
```yaml
- uses: actions/setup-dotnet@v3
id: stepid
with:
dotnet-version: |
3.1.422
5.0.408
- run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 5.0.408
```
**Installation from global.json**
When the `dotnet-version` input is used along with the `global-json-file` input, the `dotnet-version` output contains the version resolved from the `global.json`.
```yaml
- uses: actions/setup-dotnet@v3
id: stepid
with:
dotnet-version: |
3.1.422
5.0.408
global-json-file: "./global.json" # contains version 2.2.207
- run: echo '${{ steps.stepid.outputs.dotnet-version }}' # outputs 2.2.207
```
### `cache-hit`
A boolean value to indicate an exact match was found for the cache key (follows [actions/cache](https://github.com/actions/cache#outputs))
## Environment variables
Some environment variables may be necessary for your particular case or to improve logging. Some examples are listed below, but the full list with complete details can be found here: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables Some environment variables may be necessary for your particular case or to improve logging. Some examples are listed below, but the full list with complete details can be found here: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables
| **Env.variable** | **Description** | **Default value** | - DOTNET_NOLOGO - removes logo and telemetry message from first run of dotnet cli (default: false)
| ----------- | ----------- | ----------- | - DOTNET_CLI_TELEMETRY_OPTOUT - opt-out of telemetry being sent to Microsoft (default: false)
| DOTNET_INSTALL_DIR |Specifies a directory where .NET SDKs should be installed by the action.|*default value for each OS* | - DOTNET_MULTILEVEL_LOOKUP - configures whether the global install location is used as a fall-back (default: true)
| 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_MULTILEVEL_LOOKUP |Configures whether the global install location is used as a fall-back|*true*|
| NUGET_PACKAGES |Configures a path to the [NuGet `global-packages` folder](https://learn.microsoft.com/nuget/consume-packages/managing-the-global-packages-and-cache-folders)|*default value for each OS* |
The default values of the `DOTNET_INSTALL_DIR` and `NUGET_PACKAGES` environment variables depend on the operation system which is used on a runner: Example usage:
| **Operation system** | `DOTNET_INSTALL_DIR` | `NUGET_PACKAGES` | ```yaml
| ----------- | ----------- | ----------- |
| **Windows** | `C:\Program Files\dotnet` | `%userprofile%\.nuget\packages` |
| **Ubuntu** | `/usr/share/dotnet` | `~/.nuget/packages` |
| **macOS** | `/Users/runner/.dotnet` | `~/.nuget/packages` |
**Example usage of environment variable**:
```yml
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
DOTNET_INSTALL_DIR: "path/to/directory" DOTNET_NOLOGO: true
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v2
with: with:
dotnet-version: '3.1.x' dotnet-version: '3.1.x' # SDK Version to use.
cache: true
``` ```
## License # License
The scripts and documentation in this project are released under the [MIT License](LICENSE) The scripts and documentation in this project are released under the [MIT License](LICENSE)
## Contributions # Contributions
Contributions are welcome! See [Contributor's Guide](docs/contributors.md) Contributions are welcome! See [Contributor's Guide](docs/contributors.md)

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

View File

@@ -1,101 +0,0 @@
import {readdir} from 'node:fs/promises';
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import * as glob from '@actions/glob';
import {restoreCache} from '../src/cache-restore';
import {getNuGetFolderPath} from '../src/cache-utils';
import {lockFilePatterns} from '../src/constants';
jest.mock('node:fs/promises');
jest.mock('@actions/cache');
jest.mock('@actions/core');
jest.mock('@actions/glob');
jest.mock('../src/cache-utils');
describe('cache-restore tests', () => {
describe.each(lockFilePatterns)('restoreCache("%s")', lockFilePattern => {
/** Store original process.env.GITHUB_WORKSPACE */
let githubWorkspace: string | undefined;
beforeAll(() => {
githubWorkspace = process.env.GITHUB_WORKSPACE;
jest.mocked(getNuGetFolderPath).mockResolvedValue({
'global-packages': 'global-packages',
'http-cache': 'http-cache',
temp: 'temp',
'plugins-cache': 'plugins-cache'
});
});
beforeEach(() => {
process.env.GITHUB_WORKSPACE = './';
jest.mocked(glob.hashFiles).mockClear();
jest.mocked(core.saveState).mockClear();
jest.mocked(core.setOutput).mockClear();
jest.mocked(cache.restoreCache).mockClear();
});
afterEach(() => (process.env.GITHUB_WORKSPACE = githubWorkspace));
it('throws error when lock file is not found', async () => {
jest.mocked(glob.hashFiles).mockResolvedValue('');
await expect(restoreCache(lockFilePattern)).rejects.toThrow();
expect(jest.mocked(core.saveState)).not.toHaveBeenCalled();
expect(jest.mocked(core.setOutput)).not.toHaveBeenCalled();
expect(jest.mocked(cache.restoreCache)).not.toHaveBeenCalled();
});
it('does not call core.saveState("CACHE_RESULT") when cache.restoreCache() returns falsy', async () => {
jest.mocked(glob.hashFiles).mockResolvedValue('hash');
jest.mocked(cache.restoreCache).mockResolvedValue(undefined);
await restoreCache(lockFilePattern);
const expectedKey = `dotnet-cache-${process.env.RUNNER_OS}-hash`;
expect(jest.mocked(core.saveState)).toHaveBeenCalledWith(
'CACHE_KEY',
expectedKey
);
expect(jest.mocked(core.saveState)).not.toHaveBeenCalledWith(
'CACHE_RESULT',
expectedKey
);
expect(jest.mocked(core.setOutput)).toHaveBeenCalledWith(
'cache-hit',
false
);
});
it('calls core.saveState("CACHE_RESULT") when cache.restoreCache() returns key', async () => {
const expectedKey = `dotnet-cache-${process.env.RUNNER_OS}-hash`;
jest.mocked(glob.hashFiles).mockResolvedValue('hash');
jest.mocked(cache.restoreCache).mockResolvedValue(expectedKey);
await restoreCache(lockFilePattern);
expect(jest.mocked(core.saveState)).toHaveBeenCalledWith(
'CACHE_KEY',
expectedKey
);
expect(jest.mocked(core.saveState)).toHaveBeenCalledWith(
'CACHE_RESULT',
expectedKey
);
expect(jest.mocked(core.setOutput)).toHaveBeenCalledWith(
'cache-hit',
true
);
});
it('calls glob.hashFiles("/packages.lock.json") if cacheDependencyPath is falsy', async () => {
const expectedKey = `dotnet-cache-${process.env.RUNNER_OS}-hash`;
jest.mocked(glob.hashFiles).mockResolvedValue('hash');
jest.mocked(cache.restoreCache).mockResolvedValue(expectedKey);
jest.mocked(readdir).mockResolvedValue([lockFilePattern] as any);
await restoreCache('');
expect(jest.mocked(glob.hashFiles)).not.toHaveBeenCalledWith('');
expect(jest.mocked(glob.hashFiles)).toHaveBeenCalledWith(lockFilePattern);
});
});
});

View File

@@ -1,87 +0,0 @@
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import fs from 'node:fs';
import {run} from '../src/cache-save';
import {getNuGetFolderPath} from '../src/cache-utils';
jest.mock('@actions/cache');
jest.mock('@actions/core');
jest.mock('node:fs');
jest.mock('../src/cache-utils');
describe('cache-save tests', () => {
beforeAll(() => {
jest.mocked(getNuGetFolderPath).mockResolvedValue({
'global-packages': 'global-packages',
'http-cache': 'http-cache',
temp: 'temp',
'plugins-cache': 'plugins-cache'
});
});
beforeEach(() => {
jest.mocked(core.setFailed).mockClear();
jest.mocked(core.getState).mockClear();
jest.mocked(core.setOutput).mockClear();
jest.mocked(cache.saveCache).mockClear();
jest.mocked(fs.existsSync).mockClear();
});
it('does not save cache when inputs:cache === false', async () => {
jest.mocked(core.getBooleanInput).mockReturnValue(false);
await run();
expect(jest.mocked(core.setFailed)).not.toHaveBeenCalled();
expect(jest.mocked(core.getState)).not.toHaveBeenCalled();
expect(jest.mocked(fs.existsSync)).not.toHaveBeenCalled();
expect(jest.mocked(cache.saveCache)).not.toHaveBeenCalled();
});
it('does not save cache when core.getState("CACHE_KEY") returns ""', async () => {
jest.mocked(core.getBooleanInput).mockReturnValue(true);
jest.mocked(core.getState).mockReturnValue('');
await run();
expect(jest.mocked(core.setFailed)).not.toHaveBeenCalled();
expect(jest.mocked(core.getState)).toHaveBeenCalledTimes(2);
expect(jest.mocked(fs.existsSync)).not.toHaveBeenCalled();
expect(jest.mocked(cache.saveCache)).not.toHaveBeenCalled();
});
it('throws Error when cachePath not exists', async () => {
jest.mocked(core.getBooleanInput).mockReturnValue(true);
jest.mocked(core.getState).mockReturnValue('cache-key');
jest.mocked(fs.existsSync).mockReturnValue(false);
await run();
expect(jest.mocked(core.setFailed)).toHaveBeenCalled();
expect(jest.mocked(core.getState)).toHaveBeenCalledTimes(2);
expect(jest.mocked(cache.saveCache)).not.toHaveBeenCalled();
});
it('does not save cache when state.CACHE_KEY === state.CACHE_RESULT', async () => {
jest.mocked(core.getBooleanInput).mockReturnValue(true);
jest.mocked(core.getState).mockReturnValue('cache-key');
jest.mocked(fs.existsSync).mockReturnValue(true);
await run();
expect(jest.mocked(core.setFailed)).not.toHaveBeenCalled();
expect(jest.mocked(core.getState)).toHaveBeenCalledTimes(2);
expect(jest.mocked(cache.saveCache)).not.toHaveBeenCalled();
});
it('saves cache when state.CACHE_KEY !== state.CACHE_RESULT', async () => {
jest.mocked(core.getBooleanInput).mockReturnValue(true);
jest.mocked(core.getState).mockImplementation(s => s);
jest.mocked(fs.existsSync).mockReturnValue(true);
await run();
expect(jest.mocked(core.setFailed)).not.toHaveBeenCalled();
expect(jest.mocked(core.getState)).toHaveBeenCalledTimes(2);
expect(jest.mocked(cache.saveCache)).toHaveBeenCalled();
});
});

View File

@@ -1,122 +0,0 @@
import * as cache from '@actions/cache';
import * as exec from '@actions/exec';
import {getNuGetFolderPath, isCacheFeatureAvailable} from '../src/cache-utils';
jest.mock('@actions/cache');
jest.mock('@actions/core');
jest.mock('@actions/exec');
describe('cache-utils tests', () => {
describe('getNuGetFolderPath()', () => {
it.each([
[
`
http-cache: /home/codespace/.local/share/NuGet/v3-cache
global-packages: /var/nuget
temp: /tmp/NuGetScratch
plugins-cache: /home/codespace/.local/share/NuGet/plugins-cache
`,
{
'http-cache': '/home/codespace/.local/share/NuGet/v3-cache',
'global-packages': '/var/nuget',
temp: '/tmp/NuGetScratch',
'plugins-cache': '/home/codespace/.local/share/NuGet/plugins-cache'
}
],
[
`
http-cache: /home/codespace/.local/share/NuGet/v3-cache
global-packages: /var/nuget
temp: /tmp/NuGetScratch
plugins-cache: /home/codespace/.local/share/NuGet/plugins-cache
`,
{
'http-cache': '/home/codespace/.local/share/NuGet/v3-cache',
'global-packages': '/var/nuget',
temp: '/tmp/NuGetScratch',
'plugins-cache': '/home/codespace/.local/share/NuGet/plugins-cache'
}
],
[
`
http-cache: C:\\Users\\user\\AppData\\Local\\NuGet\\v3-cache
global-packages: C:\\Users\\user\\.nuget\\packages\\
temp: C:\\Users\\user\\AppData\\Local\\Temp\\NuGetScratch
plugins-cache: C:\\Users\\user\\AppData\\Local\\NuGet\\plugins-cache
`,
{
'http-cache': 'C:\\Users\\user\\AppData\\Local\\NuGet\\v3-cache',
'global-packages': 'C:\\Users\\user\\.nuget\\packages\\',
temp: 'C:\\Users\\user\\AppData\\Local\\Temp\\NuGetScratch',
'plugins-cache':
'C:\\Users\\user\\AppData\\Local\\NuGet\\plugins-cache'
}
],
[
`
http-cache: C:\\Users\\user\\AppData\\Local\\NuGet\\v3-cache
global-packages: C:\\Users\\user\\.nuget\\packages\\
temp: C:\\Users\\user\\AppData\\Local\\Temp\\NuGetScratch
plugins-cache: C:\\Users\\user\\AppData\\Local\\NuGet\\plugins-cache
`,
{
'http-cache': 'C:\\Users\\user\\AppData\\Local\\NuGet\\v3-cache',
'global-packages': 'C:\\Users\\user\\.nuget\\packages\\',
temp: 'C:\\Users\\user\\AppData\\Local\\Temp\\NuGetScratch',
'plugins-cache':
'C:\\Users\\user\\AppData\\Local\\NuGet\\plugins-cache'
}
]
])('(stdout: "%s") returns %p', async (stdout, expected) => {
jest
.mocked(exec.getExecOutput)
.mockResolvedValue({stdout, stderr: '', exitCode: 0});
const pathes = await getNuGetFolderPath();
expect(pathes).toStrictEqual(expected);
});
it.each([
`
error: An invalid local resource name was provided. Provide one of the following values: http-cache, temp, global-packages, all.
Usage: dotnet nuget locals [arguments] [options]
Arguments:
Cache Location(s) Specifies the cache location(s) to list or clear.
<all | http-cache | global-packages | temp>
Options:
-h|--help Show help information
--force-english-output Forces the application to run using an invariant, English-based culture.
-c|--clear Clear the selected local resources or cache location(s).
-l|--list List the selected local resources or cache location(s).
`,
'bash: dotnet: command not found',
''
])('(stderr: "%s", exitCode: 1) throws Error', async stderr => {
jest
.mocked(exec.getExecOutput)
.mockResolvedValue({stdout: '', stderr, exitCode: 1});
await expect(getNuGetFolderPath()).rejects.toThrow();
});
});
describe.each(['', 'https://github.com/', 'https://example.com/'])(
'isCacheFeatureAvailable()',
url => {
// Save & Restore env
let serverUrlEnv: string | undefined;
beforeAll(() => (serverUrlEnv = process.env['GITHUB_SERVER_URL']));
beforeEach(() => (process.env['GITHUB_SERVER_URL'] = url));
afterEach(() => (process.env['GITHUB_SERVER_URL'] = serverUrlEnv));
it('returns true when cache.isFeatureAvailable() === true', () => {
jest.mocked(cache.isFeatureAvailable).mockReturnValue(true);
expect(isCacheFeatureAvailable()).toBe(true);
});
it('returns false when cache.isFeatureAvailable() === false', () => {
jest.mocked(cache.isFeatureAvailable).mockReturnValue(false);
expect(isCacheFeatureAvailable()).toBe(false);
});
}
);
});

View File

@@ -1,45 +1,23 @@
import cscFile from '../.github/csc.json'; import fs = require('fs');
describe('csc tests', () => { describe('csc tests', () => {
test('regular expression in csc.json is valid', async () => { it('Valid regular expression', async () => {
const regexPattern = cscFile['problemMatcher'][0]['pattern'][0]['regexp']; var cscFile = require('../.github/csc.json');
const regexResultsMap = cscFile['problemMatcher'][0]['pattern'][0]; var regex = cscFile['problemMatcher'][0]['pattern'][0]['regexp'];
const regex = new RegExp(regexPattern); console.log(regex);
var re = new RegExp(regex);
const stringsToMatch = [ // Ideally we would verify that this
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.map((string, index) => { stringsToMatch.forEach(string => {
const matchedResultsArray = string.match(regex); var matchStr = string.match(re);
for (const propName in expectedResults[index]) { console.log(matchStr);
const propertyIndex = regexResultsMap[propName]; expect(matchStr).toEqual(expect.anything());
const expectedPropValue = expectedResults[index][propName];
const matchedPropValue = matchedResultsArray![propertyIndex];
expect(matchedPropValue).toEqual(expectedPropValue);
}
}); });
}, 10000); }, 10000);
}); });

View File

@@ -1,18 +0,0 @@
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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TEST_TARGET_FRAMEWORK)</TargetFramework>
<IsPackable>false</IsPackable>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
</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

@@ -1,65 +0,0 @@
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,457 +1,151 @@
import each from 'jest-each'; import io = require('@actions/io');
import semver from 'semver'; import fs = require('fs');
import fs from 'fs'; import os = require('os');
import fspromises from 'fs/promises'; import path = require('path');
import * as exec from '@actions/exec'; import hc = require('@actions/http-client');
import * as core from '@actions/core';
import * as io from '@actions/io'; const toolDir = path.join(__dirname, 'runner', 'tools');
const tempDir = path.join(__dirname, 'runner', 'temp');
process.env['RUNNER_TOOL_CACHE'] = toolDir;
process.env['RUNNER_TEMP'] = tempDir;
import * as installer from '../src/installer'; import * as installer from '../src/installer';
import {IS_WINDOWS} from '../src/utils'; const IS_WINDOWS = process.platform === 'win32';
import {QualityOptions} from '../src/setup-dotnet';
describe('installer tests', () => { describe('installer tests', () => {
const env = process.env; beforeAll(async () => {
process.env.RUNNER_TOOL_CACHE = toolDir;
beforeEach(() => { process.env.DOTNET_INSTALL_DIR = toolDir;
jest.resetModules(); process.env.RUNNER_TEMP = tempDir;
process.env = {...env}; process.env.DOTNET_ROOT = '';
await io.rmRF(toolDir);
await io.rmRF(tempDir);
}); });
describe('DotnetCoreInstaller tests', () => { afterAll(async () => {
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); try {
const warningSpy = jest.spyOn(core, 'warning'); await io.rmRF(toolDir);
const whichSpy = jest.spyOn(io, 'which'); await io.rmRF(tempDir);
const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying'); } catch {
const chmodSyncSpy = jest.spyOn(fs, 'chmodSync'); console.log('Failed to remove test directories');
const readdirSpy = jest.spyOn(fspromises, 'readdir'); }
}, 30000);
describe('installDotnet() tests', () => { it('Aquires multiple versions of dotnet', async () => {
beforeAll(() => { const versions = ['2.2.207', '3.1.120'];
whichSpy.mockImplementation(() => Promise.resolve('PathToShell'));
chmodSyncSpy.mockImplementation(() => {});
readdirSpy.mockImplementation(() => Promise.resolve([]));
});
afterAll(() => { for (const version of versions) {
jest.resetAllMocks(); await getDotnet(version);
}); }
expect(fs.existsSync(path.join(toolDir, 'sdk', '2.2.207'))).toBe(true);
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.120'))).toBe(true);
it('should throw the error in case of non-zero exit code of the installation script. The error message should contain logs.', async () => { if (IS_WINDOWS) {
const inputVersion = '3.1.100'; expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
const inputQuality = '' as QualityOptions; } else {
const errorMessage = 'fictitious error message!'; expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
getExecOutputSpy.mockImplementation(() => { expect(process.env.DOTNET_ROOT).toBeDefined;
return Promise.resolve({ expect(process.env.PATH).toBeDefined;
exitCode: 1, expect(process.env.DOTNET_ROOT).toBe(toolDir);
stdout: '', expect(process.env.PATH?.startsWith(toolDir)).toBe(true);
stderr: errorMessage }, 600000);
});
});
const dotnetInstaller = new installer.DotnetCoreInstaller( it('Acquires version of dotnet if no matching version is installed', async () => {
inputVersion, await getDotnet('3.1.201');
inputQuality expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true);
); if (IS_WINDOWS) {
await expect(dotnetInstaller.installDotnet()).rejects.toThrow( expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
`Failed to install dotnet, exit code: 1. ${errorMessage}` } else {
); expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}); }
it('should return version of .NET SDK after installation complete', async () => { expect(process.env.DOTNET_ROOT).toBeDefined;
const inputVersion = '3.1.100'; expect(process.env.PATH).toBeDefined;
const inputQuality = '' as QualityOptions; expect(process.env.DOTNET_ROOT).toBe(toolDir);
const stdout = `Fictitious dotnet version ${inputVersion} is installed`; expect(process.env.PATH?.startsWith(toolDir)).toBe(true);
getExecOutputSpy.mockImplementation(() => { }, 600000); //This needs some time to download on "slower" internet connections
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller( it('Acquires generic version of dotnet if no matching version is installed', async () => {
inputVersion, await getDotnet('3.1');
inputQuality var directory = fs
); .readdirSync(path.join(toolDir, 'sdk'))
const installedVersion = await dotnetInstaller.installDotnet(); .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(installedVersion).toBe(inputVersion); expect(process.env.DOTNET_ROOT).toBeDefined;
}); expect(process.env.PATH).toBeDefined;
expect(process.env.DOTNET_ROOT).toBe(toolDir);
expect(process.env.PATH?.startsWith(toolDir)).toBe(true);
}, 600000); //This needs some time to download on "slower" internet connections
it(`should supply 'version' argument to the installation script if supplied version is in A.B.C syntax`, async () => { it('Throws if no location contains correct dotnet version', async () => {
const inputVersion = '6.0.300'; let thrown = false;
const inputQuality = '' as QualityOptions; try {
const stdout = `Fictitious dotnet version ${inputVersion} is installed`; await getDotnet('1000.0.0');
} catch {
thrown = true;
}
expect(thrown).toBe(true);
}, 30000);
getExecOutputSpy.mockImplementation(() => { it('Uses an up to date bash download script', async () => {
return Promise.resolve({ const httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], {
exitCode: 0, allowRetries: true,
stdout: `${stdout}`, maxRetries: 3
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Version ${inputVersion}`
: `--version ${inputVersion}`;
expect(scriptArguments).toContain(expectedArgument);
});
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);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
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.`
);
});
it(`should warn if the 'quality' input is set and version isn't in A.B.C syntax but major tag is lower then 6`, async () => {
const inputVersion = '3.1';
const inputQuality = 'ga' as QualityOptions;
const stdout = `Fictitious dotnet version 3.1.100 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
expect(warningSpy).toHaveBeenCalledWith(
`The 'dotnet-quality' input can be used only with .NET SDK version in A.B, A.B.x, A, A.x and A.B.Cxx formats where the major tag is higher than 5. You specified: ${inputVersion}. 'dotnet-quality' input is ignored.`
);
});
each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test(
`should supply 'quality' argument to the installation script if quality input is set and version (%s) is not in A.B.C syntax`,
async inputVersion => {
const inputQuality = 'ga' as QualityOptions;
const exitCode = 0;
const stdout = `Fictitious dotnet version 6.0.0 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: exitCode,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Quality ${inputQuality}`
: `--quality ${inputQuality}`;
expect(scriptArguments).toContain(expectedArgument);
}
);
each(['6', '6.0', '6.0.x', '6.0.*', '6.0.X']).test(
`should supply 'channel' argument to the installation script if version (%s) isn't in A.B.C syntax`,
async inputVersion => {
const inputQuality = '' as QualityOptions;
const exitCode = 0;
const stdout = `Fictitious dotnet version 6.0.0 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: exitCode,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Channel 6.0`
: `--channel 6.0`;
expect(scriptArguments).toContain(expectedArgument);
}
);
if (IS_WINDOWS) {
it(`should supply '-ProxyAddress' argument to the installation script if env.variable 'https_proxy' is set`, async () => {
process.env['https_proxy'] = 'https://proxy.com';
const inputVersion = '6.0.100';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(scriptArguments).toContain(
`-ProxyAddress ${process.env['https_proxy']}`
);
});
it(`should supply '-ProxyBypassList' argument to the installation script if env.variable 'no_proxy' is set`, async () => {
process.env['no_proxy'] = 'first.url,second.url';
const inputVersion = '6.0.100';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version 6.0.0 is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(scriptArguments).toContain(
`-ProxyBypassList ${process.env['no_proxy']}`
);
});
}
}); });
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);
describe('addToPath() tests', () => { it('Uses an up to date powershell download script', async () => {
it(`should export DOTNET_ROOT env.var with value from DOTNET_INSTALL_DIR env.var`, async () => { var httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], {
process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir'; allowRetries: true,
installer.DotnetCoreInstaller.addToPath(); maxRetries: 3
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']);
});
}); });
}); const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.ps1'
describe('DotnetVersionResolver tests', () => { );
describe('createDotNetVersion() tests', () => { expect(response.message.statusCode).toBe(200);
each([ const upToDateContents: string = await response.readBody();
'3.1', const currentContents: string = fs
'3.x', .readFileSync(
'3.1.x', path.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
'3.1.*', )
'3.1.X', .toString();
'3.1.2', expect(normalizeFileContents(currentContents)).toBe(
'3.1.0-preview1', normalizeFileContents(upToDateContents)
'6.0.2xx' );
]).test( }, 30000);
'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): Promise<void> {
const dotnetInstaller = new installer.DotnetCoreInstaller(version);
await dotnetInstaller.installDotnet();
installer.DotnetCoreInstaller.addToPath();
}

View File

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

View File

@@ -0,0 +1,18 @@
<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,230 +1,71 @@
import * as core from '@actions/core'; import io = require('@actions/io');
import fs from 'fs'; import fs = require('fs');
import semver from 'semver'; import os = require('os');
import * as auth from '../src/authutil'; import path = require('path');
const toolDir = path.join(__dirname, 'runner', 'tools2');
const tempDir = path.join(__dirname, 'runner', 'temp2');
import * as setup from '../src/setup-dotnet'; import * as setup from '../src/setup-dotnet';
import {DotnetCoreInstaller} from '../src/installer'; import * as dotnetInstaller from '../src/installer';
import * as cacheUtils from '../src/cache-utils';
import * as cacheRestore from '../src/cache-restore'; const IS_WINDOWS = process.platform === 'win32';
describe('setup-dotnet tests', () => { describe('setup-dotnet tests', () => {
const inputs = {} as any; beforeAll(async () => {
process.env.RUNNER_TOOL_CACHE = toolDir;
const getInputSpy = jest.spyOn(core, 'getInput'); process.env.DOTNET_INSTALL_DIR = toolDir;
const getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput'); process.env.RUNNER_TEMP = tempDir;
const getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput'); process.env['INPUT_INCLUDE-PRERELEASE'] = 'false';
const setFailedSpy = jest.spyOn(core, 'setFailed'); await io.rmRF(toolDir);
const warningSpy = jest.spyOn(core, 'warning'); await io.rmRF(tempDir);
const debugSpy = jest.spyOn(core, 'debug');
const infoSpy = jest.spyOn(core, 'info');
const setOutputSpy = jest.spyOn(core, 'setOutput');
const existsSyncSpy = jest.spyOn(fs, 'existsSync');
const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying');
const installDotnetSpy = jest.spyOn(
DotnetCoreInstaller.prototype,
'installDotnet'
);
const addToPathSpy = jest.spyOn(DotnetCoreInstaller, 'addToPath');
const isCacheFeatureAvailableSpy = jest.spyOn(
cacheUtils,
'isCacheFeatureAvailable'
);
const restoreCacheSpy = jest.spyOn(cacheRestore, 'restoreCache');
const configAuthenticationSpy = jest.spyOn(auth, 'configAuthentication');
describe('run() tests', () => {
beforeEach(() => {
getMultilineInputSpy.mockImplementation(input => inputs[input as string]);
getInputSpy.mockImplementation(input => inputs[input as string]);
getBooleanInputSpy.mockImplementation(input => inputs[input as string]);
});
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
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'] = [];
const expectedErrorMessage = `The specified global.json file '${inputs['global-json-file']}' does not exist`;
await setup.run();
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
});
test(`if 'dotnet-version' and 'global-json-file' inputs aren't present, should log into debug output, try to find global.json in the repo root, fail and log message into info output`, async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
maxSatisfyingSpy.mockImplementation(() => null);
setOutputSpy.mockImplementation(() => {});
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();
expect(debugSpy).toHaveBeenCalledWith(expectedDebugMessage);
expect(existsSyncSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(expectedInfoMessage);
});
it('should fail the action if quality is supplied but its value is not supported', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0'];
inputs['dotnet-quality'] = 'fictitiousQuality';
const expectedErrorMessage = `Value '${inputs['dotnet-quality']}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`;
await setup.run();
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
});
it('should call installDotnet() multiple times if dotnet-version multiline input is provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0', '7.0'];
inputs['dotnet-quality'] = '';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
await setup.run();
expect(installDotnetSpy).toHaveBeenCalledTimes(2);
});
it('should call addToPath() after installation complete', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = ['6.0', '7.0'];
inputs['dotnet-quality'] = '';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(addToPathSpy).toHaveBeenCalledTimes(1);
});
it('should call auth.configAuthentication() if source-url input is provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
inputs['dotnet-quality'] = '';
inputs['source-url'] = 'fictitious.source.url';
configAuthenticationSpy.mockImplementation(() => {});
await setup.run();
expect(configAuthenticationSpy).toHaveBeenCalledWith(
inputs['source-url'],
undefined
);
});
it('should call auth.configAuthentication() with proper parameters if source-url and config-file inputs are provided', async () => {
inputs['global-json-file'] = '';
inputs['dotnet-version'] = [];
inputs['dotnet-quality'] = '';
inputs['source-url'] = 'fictitious.source.url';
inputs['config-file'] = 'fictitious.path';
configAuthenticationSpy.mockImplementation(() => {});
setOutputSpy.mockImplementation(() => {});
await setup.run();
expect(configAuthenticationSpy).toHaveBeenCalledWith(
inputs['source-url'],
inputs['config-file']
);
});
it('should call setOutput() after installation complete successfully', async () => {
inputs['dotnet-version'] = ['6.0.300'];
installDotnetSpy.mockImplementation(() =>
Promise.resolve(`${inputs['dotnet-version']}`)
);
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(setOutputSpy).toHaveBeenCalledTimes(1);
});
it(`shouldn't call setOutput() if parsing dotnet-installer logs failed`, async () => {
inputs['dotnet-version'] = ['6.0.300'];
const warningMessage = `Failed to output the installed version of .NET. The 'dotnet-version' output will not be set.`;
installDotnetSpy.mockImplementation(() => Promise.resolve(null));
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(warningSpy).toHaveBeenCalledWith(warningMessage);
expect(setOutputSpy).not.toHaveBeenCalled();
});
it(`shouldn't call setOutput() if actions didn't install .NET`, async () => {
inputs['dotnet-version'] = [];
const warningMessage = `The 'dotnet-version' output will not be set.`;
addToPathSpy.mockImplementation(() => {});
await setup.run();
expect(infoSpy).toHaveBeenCalledWith(warningMessage);
expect(setOutputSpy).not.toHaveBeenCalled();
});
it(`should get 'cache-dependency-path' and call restoreCache() if input cache is set to true and cache feature is available`, async () => {
inputs['dotnet-version'] = ['6.0.300'];
inputs['dotnet-quality'] = '';
inputs['cache'] = true;
inputs['cache-dependency-path'] = 'fictitious.package.lock.json';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
addToPathSpy.mockImplementation(() => {});
isCacheFeatureAvailableSpy.mockImplementation(() => true);
restoreCacheSpy.mockImplementation(() => Promise.resolve());
await setup.run();
expect(isCacheFeatureAvailableSpy).toHaveBeenCalledTimes(1);
expect(restoreCacheSpy).toHaveBeenCalledWith(
inputs['cache-dependency-path']
);
});
it(`shouldn't call restoreCache() if input cache isn't set to true`, async () => {
inputs['dotnet-version'] = ['6.0.300'];
inputs['dotnet-quality'] = '';
inputs['cache'] = false;
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
addToPathSpy.mockImplementation(() => {});
isCacheFeatureAvailableSpy.mockImplementation(() => true);
restoreCacheSpy.mockImplementation(() => Promise.resolve());
await setup.run();
expect(restoreCacheSpy).not.toHaveBeenCalled();
});
it(`shouldn't call restoreCache() if cache feature isn't available`, async () => {
inputs['dotnet-version'] = ['6.0.300'];
inputs['dotnet-quality'] = '';
inputs['cache'] = true;
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
addToPathSpy.mockImplementation(() => {});
isCacheFeatureAvailableSpy.mockImplementation(() => false);
restoreCacheSpy.mockImplementation(() => Promise.resolve());
await setup.run();
expect(restoreCacheSpy).not.toHaveBeenCalled();
});
}); });
afterEach(async () => {
try {
await io.rmRF(path.join(process.cwd(), 'global.json'));
await io.rmRF(toolDir);
await io.rmRF(tempDir);
} catch {
console.log('Failed to remove test directories');
}
}, 30000);
it('Acquires version of dotnet from global.json if no matching version is installed', async () => {
const globalJsonPath = path.join(process.cwd(), 'global.json');
const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.1.201"${os.EOL}}${os.EOL}}`;
if (!fs.existsSync(globalJsonPath)) {
fs.writeFileSync(globalJsonPath, jsonContents);
}
await setup.run();
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
}, 400000);
it('Acquires version of dotnet from global.json with rollForward option, install the latest patch', async () => {
const globalJsonPath = path.join(process.cwd(), 'global.json');
const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version":"3.1.201",${os.EOL}"rollForward":"latestFeature"${os.EOL}}${os.EOL}}`;
if (!fs.existsSync(globalJsonPath)) {
fs.writeFileSync(globalJsonPath, jsonContents);
}
const version = '3.1';
const installer = new dotnetInstaller.DotnetCoreInstaller(version);
const patchVersion = await installer.resolveVersion(
new dotnetInstaller.DotNetVersionInfo(version)
);
await setup.run();
expect(fs.existsSync(path.join(toolDir, 'sdk', patchVersion))).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
}, 400000);
}); });

View File

@@ -1,117 +1,73 @@
<# if (!$args[0])
.DESCRIPTION {
Verifies that installed on the machine .NET SDK versions match the input patterns. throw "Must supply dotnet version argument"
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'"
# SDKs are listed on multiple lines with the path afterwards in square brackets if($args.count -eq 1)
$Versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() }
Write-Host "Found installed versions: $($Versions -join ', ')."
$InstalledVersionCount = $Versions.Count
foreach($version in $Versions)
{ {
foreach($pattern in $PatternsList) $version = & $dotnet --version | Out-String | ForEach-Object { $_.Trim() }
Write-Host "Version $version"
if (-not ($version.StartsWith($args[0].ToString())))
{ {
if ($version -match $pattern) Write-Host "PATH='$env:PATH'"
{ 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"
} }
} }
if ( $InstalledVersionCount -ne 0) Write-Host "Building sample csproj"
& $dotnet build __tests__/sample-csproj/ --no-cache
if ($LASTEXITCODE -ne 0)
{ {
throw "An unexpected version of Dotnet is found on the machine, please check the correctness of the -Patterns input." throw "Unexpected exit code $LASTEXITCODE"
} }
$workingDir = Get-Location Write-Host "Testing compiled app"
$testProjectDir = "./__tests__/e2e-test-csproj" $sample_output = "$(dotnet test __tests__/sample-csproj/ --no-build)"
Write-Host "Changing directory to the $testProjectDir" Write-Host "Sample output: $sample_output"
Set-Location $testProjectDir # For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once
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)
{ {
# 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. if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*")
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 "An error occured while creating the global.json file. Exit code: $LASTEXITCODE" throw "Unexpected output"
}
}
if ($args[2])
{
if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*Test Run Successful.*")
{
throw "Unexpected output"
}
}
else
{
if ($sample_output -notlike "*Test Run Successful.*")
{
throw "Unexpected output"
} }
Write-Host "The global.json file for the version $version is created. Currently used .NET version is: $(& $dotnet --version)."
# Environment variable TEST_TARGET_FRAMEWORK is used inside the test.csproj file to target required framework version
$version -match "^(?<key>\d+\.\d+)" | Out-Null
if (!($targetFrameworkVersionMap.ContainsKey($Matches.key)))
{
throw "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])"
[Environment]::SetEnvironmentVariable('TEST_TARGET_FRAMEWORK', $($targetFrameworkVersionMap[$Matches.key]))
Write-Host "Building test C# project with $version .NET version."
& $dotnet build --no-cache
if ($LASTEXITCODE -ne 0)
{
throw "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

44
__tests__/verify-dotnet.sh Executable file
View File

@@ -0,0 +1,44 @@
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

@@ -0,0 +1,17 @@
#!/bin/bash
if [[ "$(git status --porcelain)" != "" ]]; then
echo ----------------------------------------
echo git status
echo ----------------------------------------
git status
echo ----------------------------------------
echo git diff
echo ----------------------------------------
git diff
echo ----------------------------------------
echo Troubleshooting
echo ----------------------------------------
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run pre-checkin"
exit 1
fi

View File

@@ -0,0 +1,91 @@
import each from 'jest-each';
import * as installer from '../src/installer';
describe('version tests', () => {
each(['3.1.999', '3.1.101-preview.3']).test(
"Exact version '%s' should be the same",
vers => {
let versInfo = new installer.DotNetVersionInfo(vers);
expect(versInfo.isExactVersion()).toBe(true);
expect(versInfo.version()).toBe(vers);
}
);
each([
['3.x', '3.x'],
['3.*', '3.*'],
['3.1.x', '3.1'],
['1.1.*', '1.1'],
['2.0', '2.0']
]).test("Generic version '%s' should be '%s'", (vers, resVers) => {
let versInfo = new installer.DotNetVersionInfo(vers);
expect(versInfo.isExactVersion()).toBe(false);
expect(versInfo.version()).toBe(resVers);
});
each([
'',
'.',
'..',
' . ',
'. ',
' .',
' . . ',
' .. ',
' . ',
'-1.-1',
'-1',
'-1.-1.-1',
'..3',
'1..3',
'1..',
'.2.3',
'.2.x',
'1',
'*.*.1',
'*.1',
'*.',
'1.2.',
'1.2.-abc',
'a.b',
'a.b.c',
'a.b.c-preview',
' 0 . 1 . 2 '
]).test("Malformed version '%s' should throw", vers => {
expect(() => new installer.DotNetVersionInfo(vers)).toThrow();
});
each([
['3.1.x', '3.1.'],
['3.1.*', '3.1.'],
['3.1', '3.1.'],
['5.0.0-preview.6', '5.0.0-preview.6'],
['3.1.201', '3.1.201']
]).test(
"Resolving version '%s' as '%s'",
async (input, expectedVersion) => {
const dotnetInstaller = new installer.DotnetCoreInstaller(input);
let versInfo = await dotnetInstaller.resolveVersion(
new installer.DotNetVersionInfo(input)
);
console.log(versInfo);
expect(versInfo.startsWith(expectedVersion));
},
100000
);
it('Resolving a nonexistent generic version fails', async () => {
const dotnetInstaller = new installer.DotnetCoreInstaller('999.1.x');
try {
await dotnetInstaller.resolveVersion(
new installer.DotNetVersionInfo('999.1.x')
);
fail();
} catch {
expect(true);
}
}, 100000);
});

Some files were not shown because too many files have changed in this diff Show More