Compare commits

..

8 Commits
v3.4.0 ... v2

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/
on:
@@ -12,6 +17,36 @@ on:
workflow_dispatch:
jobs:
call-check-dist:
name: Check dist/
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
check-dist:
runs-on: ubuntu-latest
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:
push:
branches: [main]
branches: [ main ]
pull_request:
branches: [main]
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '0 3 * * 0'
- cron: '23 19 * * 0'
jobs:
call-codeQL-analysis:
name: CodeQL analysis
uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main
analyze:
name: Analyze
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:
jobs:
call-licensed:
name: Licensed
uses: actions/reusable-workflows/.github/workflows/licensed.yml@main
test:
runs-on: ubuntu-latest
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
on:
release:
types: [released]
@@ -21,9 +20,9 @@ jobs:
name: releaseNewActionVersion
runs-on: ubuntu-latest
steps:
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag
uses: actions/publish-action@v0.2.2
with:
source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag
uses: actions/publish-action@v0.1.0
with:
source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -18,7 +18,7 @@ jobs:
fail-fast: false
matrix:
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:
- name: Checkout
uses: actions/checkout@v3
@@ -29,7 +29,9 @@ jobs:
uses: ./
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Verify installed version
- name: Check installed version
shell: pwsh
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/
node_modules/
__tests__/runner/*
__tests__/e2e-test-csproj/bin/
__tests__/e2e-test-csproj/obj/
__tests__/sample-csproj/bin/
__tests__/sample-csproj/obj/
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs

View File

@@ -2,4 +2,3 @@
. "$(dirname -- "$0")/_/husky.sh"
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
npm run format-check
npm run lint

View File

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

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
[![Basic validation](https://github.com/actions/setup-dotnet/actions/workflows/basic-validation.yml/badge.svg?branch=main)](https://github.com/actions/setup-dotnet/actions/workflows/basic-validation.yml)
[![e2e tests](https://github.com/actions/setup-dotnet/actions/workflows/e2e-tests.yml/badge.svg?branch=main)](https://github.com/actions/setup-dotnet/actions/workflows/e2e-tests.yml)
[![GitHub Actions Status](https://github.com/actions/setup-dotnet/workflows/Main%20workflow/badge.svg)](https://github.com/actions/setup-dotnet)
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
- 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
documentation:
[Software installed on github hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software)
documentation
[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.
## Usage
# Usage
See [action.yml](action.yml)
**Basic**:
Basic:
```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v2
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>
```
> **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
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v2
with:
dotnet-version: |
3.1.x
5.0.x
- run: dotnet build <my project>
```
## Supported version syntax
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.
Preview version:
```yml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v2
with:
dotnet-version: '6.0.x'
dotnet-quality: 'preview'
include-prerelease: true
- run: dotnet build <my project>
```
## 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.
global.json in a subdirectory:
```yml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v2
with:
global-json-file: csharp/global.json
- run: dotnet build <my project>
working-directory: csharp
```
## Caching NuGet Packages
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.
Matrix Testing:
```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:
build:
runs-on: ubuntu-latest
@@ -152,44 +74,38 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v2
with:
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**
```yml
Side by Side Testing:
```yaml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: [ '2.1.x', '3.1.x', '5.0.x' ]
name: Dotnet ${{ matrix.dotnet }} sample
name: Dotnet Side by Side testing sample
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
id: stepid
uses: actions/setup-dotnet@v2
with:
dotnet-version: ${{ matrix.dotnet }}
- name: Create temporary global.json
run: echo '{"sdk":{"version": "${{ steps.stepid.outputs.dotnet-version }}"}}' > ./global.json
- name: Execute dotnet
run: dotnet build <my project>
dotnet-version: |
2.1.x
3.1.x
- run: dotnet build <my project>
- run: dotnet test <my project>
```
## Setting up authentication for nuget feeds
### Github Package Registry (GPR)
```yml
Authentication for nuget feeds:
```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
# Authenticates packages to push to GPR
- uses: actions/setup-dotnet@v2
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
env:
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
@@ -198,22 +114,19 @@ steps:
run: dotnet pack --configuration Release <my project>
- name: Publish the package to GPR
run: dotnet nuget push <my project>/bin/Release/*.nupkg
```
### Azure Artifacts
```yml
- uses: actions/setup-dotnet@v3
# Authenticates packages to push to Azure Artifacts
- uses: actions/setup-dotnet@v2
with:
source-url: https://pkgs.dev.azure.com/<your-organization>/_packaging/<your-feed-name>/nuget/v3/index.json
env:
NUGET_AUTH_TOKEN: ${{secrets.AZURE_DEVOPS_PAT}} # Note, create a secret with this name in Settings
- name: Publish the package to Azure Artifacts
run: dotnet nuget push <my project>/bin/Release/*.nupkg
```
### nuget.org
```yml
- uses: actions/setup-dotnet@v3
# Authenticates packages to push to nuget.org.
# 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@v2
with:
dotnet-version: 3.1.x
- name: Publish the package to nuget.org
@@ -221,97 +134,32 @@ steps:
env:
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
## 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
## Environment Variables to use with dotnet
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_INSTALL_DIR |Specifies a directory where .NET SDKs should be installed by the action.|*default value for each OS* |
| DOTNET_NOLOGO |Removes logo and telemetry message from first run of dotnet cli|*false*|
| DOTNET_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* |
- 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_MULTILEVEL_LOOKUP - configures whether the global install location is used as a fall-back (default: true)
The default values of the `DOTNET_INSTALL_DIR` and `NUGET_PACKAGES` environment variables depend on the operation system which is used on a runner:
| **Operation system** | `DOTNET_INSTALL_DIR` | `NUGET_PACKAGES` |
| ----------- | ----------- | ----------- |
| **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
Example usage:
```yaml
build:
runs-on: ubuntu-latest
env:
DOTNET_INSTALL_DIR: "path/to/directory"
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@main
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v2
with:
dotnet-version: '3.1.x'
cache: true
dotnet-version: '3.1.x' # SDK Version to use.
```
## 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
exports[`authutil tests existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config not in repo root, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -15,7 +15,7 @@ exports[`authutil tests existing config not in repo root, sets up a partial NuGe
</configuration>"
`;
exports[`authutil tests existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ Azure Artifacts source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -30,7 +30,7 @@ exports[`authutil tests existing config w/ Azure Artifacts source and NuGet.org,
</configuration>"
`;
exports[`authutil tests existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ GPR source and NuGet.org, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -45,7 +45,7 @@ exports[`authutil tests existing config w/ GPR source and NuGet.org, sets up a p
</configuration>"
`;
exports[`authutil tests existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ no GPR sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -63,7 +63,7 @@ exports[`authutil tests existing config w/ no GPR sources, sets up a full NuGet.
</configuration>"
`;
exports[`authutil tests existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ no sources, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -81,7 +81,7 @@ exports[`authutil tests existing config w/ no sources, sets up a full NuGet.conf
</configuration>"
`;
exports[`authutil tests existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ only Azure Artifacts source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -96,7 +96,7 @@ exports[`authutil tests existing config w/ only Azure Artifacts source, sets up
</configuration>"
`;
exports[`authutil tests existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ only GPR source, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -111,7 +111,7 @@ exports[`authutil tests existing config w/ only GPR source, sets up a partial Nu
</configuration>"
`;
exports[`authutil tests existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = `
exports[`authutil tests Existing config w/ two GPR sources, sets up a partial NuGet.config user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -130,7 +130,7 @@ exports[`authutil tests existing config w/ two GPR sources, sets up a partial Nu
</configuration>"
`;
exports[`authutil tests no existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = `
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and other owner/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -148,7 +148,7 @@ exports[`authutil tests no existing config, sets up a full NuGet.config with URL
</configuration>"
`;
exports[`authutil tests no existing config, sets up a full NuGet.config with URL and token for other source 1`] = `
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and token for other source 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>
@@ -166,7 +166,7 @@ exports[`authutil tests no existing config, sets up a full NuGet.config with URL
</configuration>"
`;
exports[`authutil tests no existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
exports[`authutil tests No existing config, sets up a full NuGet.config with URL and user/PAT for GPR 1`] = `
"<?xml version=\\"1.0\\"?>
<configuration>
<config>

View File

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

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', () => {
test('regular expression in csc.json is valid', async () => {
const regexPattern = cscFile['problemMatcher'][0]['pattern'][0]['regexp'];
const regexResultsMap = cscFile['problemMatcher'][0]['pattern'][0];
it('Valid regular expression', async () => {
var cscFile = require('../.github/csc.json');
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]',
"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) => {
const matchedResultsArray = string.match(regex);
for (const propName in expectedResults[index]) {
const propertyIndex = regexResultsMap[propName];
const expectedPropValue = expectedResults[index][propName];
const matchedPropValue = matchedResultsArray![propertyIndex];
expect(matchedPropValue).toEqual(expectedPropValue);
}
stringsToMatch.forEach(string => {
var matchStr = string.match(re);
console.log(matchStr);
expect(matchStr).toEqual(expect.anything());
});
}, 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 semver from 'semver';
import fs from 'fs';
import fspromises from 'fs/promises';
import * as exec from '@actions/exec';
import * as core from '@actions/core';
import * as io from '@actions/io';
import io = require('@actions/io');
import fs = require('fs');
import os = require('os');
import path = require('path');
import hc = require('@actions/http-client');
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 {IS_WINDOWS} from '../src/utils';
import {QualityOptions} from '../src/setup-dotnet';
const IS_WINDOWS = process.platform === 'win32';
describe('installer tests', () => {
const env = process.env;
beforeEach(() => {
jest.resetModules();
process.env = {...env};
beforeAll(async () => {
process.env.RUNNER_TOOL_CACHE = toolDir;
process.env.DOTNET_INSTALL_DIR = toolDir;
process.env.RUNNER_TEMP = tempDir;
process.env.DOTNET_ROOT = '';
await io.rmRF(toolDir);
await io.rmRF(tempDir);
});
describe('DotnetCoreInstaller tests', () => {
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
const warningSpy = jest.spyOn(core, 'warning');
const whichSpy = jest.spyOn(io, 'which');
const maxSatisfyingSpy = jest.spyOn(semver, 'maxSatisfying');
const chmodSyncSpy = jest.spyOn(fs, 'chmodSync');
const readdirSpy = jest.spyOn(fspromises, 'readdir');
afterAll(async () => {
try {
await io.rmRF(toolDir);
await io.rmRF(tempDir);
} catch {
console.log('Failed to remove test directories');
}
}, 30000);
describe('installDotnet() tests', () => {
beforeAll(() => {
whichSpy.mockImplementation(() => Promise.resolve('PathToShell'));
chmodSyncSpy.mockImplementation(() => {});
readdirSpy.mockImplementation(() => Promise.resolve([]));
});
it('Aquires multiple versions of dotnet', async () => {
const versions = ['2.2.207', '3.1.120'];
afterAll(() => {
jest.resetAllMocks();
});
for (const version of versions) {
await getDotnet(version);
}
expect(fs.existsSync(path.join(toolDir, 'sdk', '2.2.207'))).toBe(true);
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.120'))).toBe(true);
it('should throw the error in case of non-zero exit code of the installation script. The error message should contain logs.', async () => {
const inputVersion = '3.1.100';
const inputQuality = '' as QualityOptions;
const errorMessage = 'fictitious error message!';
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 1,
stdout: '',
stderr: errorMessage
});
});
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);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await expect(dotnetInstaller.installDotnet()).rejects.toThrow(
`Failed to install dotnet, exit code: 1. ${errorMessage}`
);
});
it('Acquires version of dotnet if no matching version is installed', async () => {
await getDotnet('3.1.201');
expect(fs.existsSync(path.join(toolDir, 'sdk', '3.1.201'))).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
it('should return version of .NET SDK after installation complete', async () => {
const inputVersion = '3.1.100';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
expect(process.env.DOTNET_ROOT).toBeDefined;
expect(process.env.PATH).toBeDefined;
expect(process.env.DOTNET_ROOT).toBe(toolDir);
expect(process.env.PATH?.startsWith(toolDir)).toBe(true);
}, 600000); //This needs some time to download on "slower" internet connections
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
const installedVersion = await dotnetInstaller.installDotnet();
it('Acquires generic version of dotnet if no matching version is installed', async () => {
await getDotnet('3.1');
var directory = fs
.readdirSync(path.join(toolDir, 'sdk'))
.filter(fn => fn.startsWith('3.1.'));
expect(directory.length > 0).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(toolDir, 'dotnet.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
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 () => {
const inputVersion = '6.0.300';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
it('Throws if no location contains correct dotnet version', async () => {
let thrown = false;
try {
await getDotnet('1000.0.0');
} catch {
thrown = true;
}
expect(thrown).toBe(true);
}, 30000);
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(' ');
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']}`
);
});
}
it('Uses an up to date bash download script', async () => {
const httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], {
allowRetries: true,
maxRetries: 3
});
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.sh'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.sh')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
}, 30000);
describe('addToPath() tests', () => {
it(`should export DOTNET_ROOT env.var with value from DOTNET_INSTALL_DIR env.var`, async () => {
process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir';
installer.DotnetCoreInstaller.addToPath();
const dotnet_root = process.env['DOTNET_ROOT'];
expect(dotnet_root).toBe(process.env['DOTNET_INSTALL_DIR']);
});
it(`should export value from DOTNET_INSTALL_DIR env.var to the PATH`, async () => {
process.env['DOTNET_INSTALL_DIR'] = 'fictitious/dotnet/install/dir';
installer.DotnetCoreInstaller.addToPath();
const path = process.env['PATH'];
expect(path).toContain(process.env['DOTNET_INSTALL_DIR']);
});
it('Uses an up to date powershell download script', async () => {
var httpCallbackClient = new hc.HttpClient('setup-dotnet-test', [], {
allowRetries: true,
maxRetries: 3
});
});
describe('DotnetVersionResolver tests', () => {
describe('createDotNetVersion() tests', () => {
each([
'3.1',
'3.x',
'3.1.x',
'3.1.*',
'3.1.X',
'3.1.2',
'3.1.0-preview1',
'6.0.2xx'
]).test(
'if valid version is supplied (%s), it should return version object with some value',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(!!versionObject.value).toBe(true);
}
);
each([
'.',
'..',
' . ',
'. ',
' .',
' . . ',
' .. ',
' . ',
'-1.-1',
'-1',
'-1.-1.-1',
'..3',
'1..3',
'1..',
'.2.3',
'.2.x',
'*.',
'1.2.',
'1.2.-abc',
'a.b',
'a.b.c',
'a.b.c-preview',
' 0 . 1 . 2 ',
'invalid'
]).test(
'if invalid version is supplied (%s), it should throw',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
await expect(
async () => await dotnetVersionResolver.createDotNetVersion()
).rejects.toThrow();
}
);
each(['3', '3.1', '3.1.x', '3.1.*', '3.1.X', '6.0.2xx']).test(
"if version that can be resolved to 'channel' option is supplied (%s), it should set type to 'channel' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('channel')).toBe(
true
);
}
);
each(['6.0', '6.0.x', '6.0.*', '6.0.X', '6.0.2xx']).test(
"if version that can be resolved to 'channel' option is supplied and its major tag is >= 6 (%s), it should set type to 'channel' and qualityFlag to 'true' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('channel')).toBe(
true
);
expect(versionObject.qualityFlag).toBe(true);
}
);
each(['3.1.2', '3.1.0-preview1']).test(
"if version that can be resolved to 'version' option is supplied (%s), it should set quality flag to 'false' and type to 'version' in version object",
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
expect(versionObject.type.toLowerCase().includes('version')).toBe(
true
);
expect(versionObject.qualityFlag).toBe(false);
}
);
each(['3.1.2', '3.1']).test(
'it should create proper line arguments for powershell/bash installation scripts',
async version => {
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
const versionObject =
await dotnetVersionResolver.createDotNetVersion();
const windowsRegEx = new RegExp(/^-(Version|Channel)/);
const nonWindowsRegEx = new RegExp(/^--(version|channel)/);
if (IS_WINDOWS) {
expect(windowsRegEx.test(versionObject.type)).toBe(true);
expect(nonWindowsRegEx.test(versionObject.type)).toBe(false);
} else {
expect(nonWindowsRegEx.test(versionObject.type)).toBe(true);
expect(windowsRegEx.test(versionObject.type)).toBe(false);
}
}
);
it(`should throw if dotnet-version is supplied in A.B.Cxx syntax with major tag lower that 5`, async () => {
const version = '3.0.1xx';
const dotnetVersionResolver = new installer.DotnetVersionResolver(
version
);
await expect(
async () => await dotnetVersionResolver.createDotNetVersion()
).rejects.toThrow(
`'dotnet-version' was supplied in invalid format: ${version}! The A.B.Cxx syntax is available since the .NET 5.0 release.`
);
});
});
});
const response: hc.HttpClientResponse = await httpCallbackClient.get(
'https://dot.net/v1/dotnet-install.ps1'
);
expect(response.message.statusCode).toBe(200);
const upToDateContents: string = await response.readBody();
const currentContents: string = fs
.readFileSync(
path.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
)
.toString();
expect(normalizeFileContents(currentContents)).toBe(
normalizeFileContents(upToDateContents)
);
}, 30000);
});
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 fs from 'fs';
import semver from 'semver';
import * as auth from '../src/authutil';
import io = require('@actions/io');
import fs = require('fs');
import os = require('os');
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 {DotnetCoreInstaller} from '../src/installer';
import * as cacheUtils from '../src/cache-utils';
import * as cacheRestore from '../src/cache-restore';
import * as dotnetInstaller from '../src/installer';
const IS_WINDOWS = process.platform === 'win32';
describe('setup-dotnet tests', () => {
const inputs = {} as any;
const getInputSpy = jest.spyOn(core, 'getInput');
const getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput');
const getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
const setFailedSpy = jest.spyOn(core, 'setFailed');
const warningSpy = jest.spyOn(core, 'warning');
const debugSpy = jest.spyOn(core, 'debug');
const infoSpy = jest.spyOn(core, 'info');
const setOutputSpy = jest.spyOn(core, 'setOutput');
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();
});
beforeAll(async () => {
process.env.RUNNER_TOOL_CACHE = toolDir;
process.env.DOTNET_INSTALL_DIR = toolDir;
process.env.RUNNER_TEMP = tempDir;
process.env['INPUT_INCLUDE-PRERELEASE'] = 'false';
await io.rmRF(toolDir);
await io.rmRF(tempDir);
});
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 @@
<#
.DESCRIPTION
Verifies that installed on the machine .NET SDK versions match the input patterns.
Optionally checks that the nuget.config file is generated correctly.
.PARAMETER Patterns
Specifies the regular expression patterns that should be matched with the installed
on the machine .NET SDK versions. The number of patterns should be equal to the number
of installed .NET versions.
.PARAMETER CheckNugetConfig
Switches the check for the existence of the nuget.config file.
.EXAMPLE
PS> .\verify-dotnet.ps1 -Paterns "^3.1.200$", "^6.0" -CheckNugetConfig
#>
param(
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$true)]
[string[]]$Patterns,
[switch]$CheckNugetConfig
)
$PatternsList = [System.Collections.ArrayList]($Patterns)
if ($CheckNugetConfig.IsPresent -and !(Test-Path "../nuget.config")) {
throw "The nuget.config file is not generated correctly."
if (!$args[0])
{
throw "Must supply dotnet version argument"
}
Write-Host "These patterns were supplied to the script: $($PatternsList -join ', ')."
$dotnet = Get-Command dotnet | Select-Object -First 1 | ForEach-Object { $_.Path }
Write-Host "Found: '$dotnet'"
Write-Host "Found '$dotnet'"
# SDKs are listed on multiple lines with the path afterwards in square brackets
$Versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() }
Write-Host "Found installed versions: $($Versions -join ', ')."
$InstalledVersionCount = $Versions.Count
foreach($version in $Versions)
if($args.count -eq 1)
{
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)
{
$PatternsList.Remove($pattern)
$InstalledVersionCount--
break
Write-Host "PATH='$env:PATH'"
throw "Unexpected version"
}
}
if ($args[1])
{
# SDKs are listed on multiple lines with the path afterwards in square brackets
$versions = & $dotnet --list-sdks | ForEach-Object { $_.SubString(0, $_.IndexOf('[')).Trim() }
Write-Host "Installed versions: $versions"
$InstalledVersionCount = 0
foreach($arg in $args){
foreach ($version in $versions)
{
if ($version.StartsWith($arg.ToString()))
{
$InstalledVersionCount++
}
}
}
if ( $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
$testProjectDir = "./__tests__/e2e-test-csproj"
Write-Host "Changing directory to the $testProjectDir"
Set-Location $testProjectDir
$targetFrameworkVersionMap = @{
"1.0" = "netcoreapp1.0";
"1.1" = "netcoreapp1.1";
"2.0" = "netcoreapp2.0";
"2.1" = "netcoreapp2.1";
"2.2" = "netcoreapp2.2";
"3.0" = "netcoreapp3.0";
"3.1" = "netcoreapp3.1";
"5.0" = "net5.0";
"6.0" = "net6.0";
"7.0" = "net7.0";
"8.0" = "net8.0";
}
foreach ($version in $Versions)
Write-Host "Testing compiled app"
$sample_output = "$(dotnet test __tests__/sample-csproj/ --no-build)"
Write-Host "Sample output: $sample_output"
# For Side-by-Side installs we want to run the tests twice, for a single install the tests will run once
if ($args[1])
{
# Creating temporary global.json file inside e2e-test-csproj dir and setting exact version of .NET inside allows to override default behavior of .NET and run build and tests on that exact version.
Write-Host "Creating temporary global.json file for $version .NET version."
& $dotnet new globaljson --sdk-version $version --force | Out-Null
if (!(Test-Path "./global.json"))
if ($sample_output -notlike "*Test Run Successful.*Test Run Successful.*")
{
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