Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to write resolved version of SDK into the output variable #324

Merged
merged 22 commits into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/workflow.yml
Expand Up @@ -237,6 +237,60 @@ jobs:
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version.Contains("preview") -or $version.Contains("rc"))) { throw "Unexpected version" }

test-dotnet-version-output-during-single-version-installation:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}

- name: Setup dotnet 6.0.401
uses: ./
id: step1
with:
dotnet-version: "6.0.401"

- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = & dotnet --version
Write-Host "Installed version: $version"
if (-not ($version -eq '${{steps.step1.outputs.dotnet-version}}')) { throw "Unexpected output value" }

test-dotnet-version-output-during-multiple-version-installation:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Clear toolcache
shell: pwsh
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}

- name: Setup dotnet 6.0.401, 5.0.408, 7.0.100-rc.1.22431.12
uses: ./
id: step2
with:
dotnet-version: |
7.0.100-rc.1.22431.12
6.0.401
5.0.408

- name: Verify value of the dotnet-version output
shell: pwsh
run: |
$version = "7.0.100-rc.1.22431.12"
if (-not ($version -eq '${{steps.step2.outputs.dotnet-version}}')) { throw "Unexpected output value" }

test-proxy:
runs-on: ubuntu-latest
Expand Down
35 changes: 35 additions & 0 deletions README.md
Expand Up @@ -141,6 +141,41 @@ steps:
```
> **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 **dotnet-version** output it's possible to get the installed by action .NET SDK version.

**Single version installation**

In case of a single version installation, `dotnet-version` contains the version that is installed by the action.

```yaml
- uses: actions/setup-dotnet@v3
id: cp310
with:
dotnet-version: 3.1.422
- run: echo '${{ steps.cp310.outputs.dotnet-version }}' # outputs 3.1.422
```

**Multiple version installation**

In case of a multiple version installation, `dotnet-version` contains the latest version that is installed by the action.

```yaml
- uses: actions/setup-dotnet@v3
id: cp310
with:
dotnet-version: |
3.1.422
5.0.408
- run: echo '${{ steps.cp310.outputs.dotnet-version }}' # outputs 5.0.408
```


## Environment variables

Some environment variables may be necessary for your particular case or to improve logging. Some examples are listed below, but the full list with complete details can be found here: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables
Expand Down
17 changes: 15 additions & 2 deletions __tests__/installer.test.ts
Expand Up @@ -107,6 +107,15 @@ describe('DotnetCoreInstaller tests', () => {
expect(process.env.PATH?.startsWith(toolDir)).toBe(true);
}, 600000); //This needs some time to download on "slower" internet connections

it('Returns string with installed SDK version', async () => {
const version = '3.1.120';
let installedVersion: string;

installedVersion = await getDotnet(version);

expect(installedVersion).toBe('3.1.120');
}, 600000);

it('Throws if no location contains correct dotnet version', async () => {
await expect(async () => {
await getDotnet('1000.0.0');
Expand Down Expand Up @@ -267,11 +276,15 @@ function normalizeFileContents(contents: string): string {
.replace(new RegExp('\r', 'g'), '\n');
}

async function getDotnet(version: string, quality: string = ''): Promise<void> {
async function getDotnet(
version: string,
quality: string = ''
): Promise<string> {
const dotnetInstaller = new installer.DotnetCoreInstaller(
version,
quality as QualityOptions
);
await dotnetInstaller.installDotnet();
const installedVersion = await dotnetInstaller.installDotnet();
installer.DotnetCoreInstaller.addToPath();
return installedVersion;
}
36 changes: 36 additions & 0 deletions __tests__/setup-dotnet.test.ts
@@ -1,4 +1,5 @@
import * as io from '@actions/io';
import * as core from '@actions/core';
import fs from 'fs';
import os from 'os';
import path from 'path';
Expand All @@ -20,6 +21,12 @@ if (IS_WINDOWS) {
const tempDir = path.join(__dirname, 'runner', 'temp2');

describe('setup-dotnet tests', () => {
let getInputSpy = jest.spyOn(core, 'getInput');
let getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput');
let setOutputSpy = jest.spyOn(core, 'setOutput');

let inputs = {} as any;

beforeAll(async () => {
process.env.RUNNER_TOOL_CACHE = toolDir;
process.env.DOTNET_INSTALL_DIR = toolDir;
Expand Down Expand Up @@ -59,4 +66,33 @@ describe('setup-dotnet tests', () => {
expect(fs.existsSync(path.join(toolDir, 'dotnet'))).toBe(true);
}
}, 400000);

it("Sets output with the latest installed by action version if global.json file isn't specified", async () => {
inputs['dotnet-version'] = ['3.1.201', '6.0.401'];

getMultilineInputSpy.mockImplementation(input => inputs[input]);

await setup.run();

expect(setOutputSpy).toBeCalledWith('dotnet-version', '6.0.401');
}, 400000);

it("Sets output with the version specified in global.json, if it's present", async () => {
const globalJsonPath = path.join(process.cwd(), 'global.json');
const jsonContents = `{${os.EOL}"sdk": {${os.EOL}"version": "3.0.103"${os.EOL}}${os.EOL}}`;
if (!fs.existsSync(globalJsonPath)) {
fs.writeFileSync(globalJsonPath, jsonContents);
}

inputs['dotnet-version'] = ['3.1.201', '6.0.401'];
inputs['global-json-file'] = './global.json';

getMultilineInputSpy.mockImplementation(input => inputs[input]);

getInputSpy.mockImplementation(input => inputs[input]);

await setup.run();

expect(setOutputSpy).toBeCalledWith('dotnet-version', '3.0.103');
}, 400000);
});
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -17,6 +17,9 @@ inputs:
description: 'Optional OWNER for using packages from GitHub Package Registry organizations/users other than the current repository''s owner. Only used if a GPR URL is also provided in source-url'
config-file:
description: 'Optional NuGet.config location, if your NuGet.config isn''t located in the root of the repo.'
outputs:
dotnet-version:
description: 'Contains the installed by action .NET SDK version for reuse.'
runs:
using: 'node16'
main: 'dist/index.js'
29 changes: 28 additions & 1 deletion dist/index.js
Expand Up @@ -356,8 +356,25 @@ class DotnetCoreInstaller {
if (exitCode) {
throw new Error(`Failed to install dotnet ${exitCode}. ${stdout}`);
}
return this.outputDotnetVersion(stdout);
});
}
outputDotnetVersion(logs) {
let resolvedVersion = '';
const installedByScriptPattern = /Installed version is (?<version>\d+\.\d+\.\d.*)$/m;
const preinstalledOnRunnerPattern = /.NET Core SDK with version '(?<version>\d+\.\d+\.\d.*)'/m;
const regExpressions = [
installedByScriptPattern,
preinstalledOnRunnerPattern
];
for (let regExp of regExpressions) {
if (regExp.test(logs)) {
resolvedVersion = logs.match(regExp).groups.version;
break;
}
}
return resolvedVersion;
}
}
exports.DotnetCoreInstaller = DotnetCoreInstaller;
DotnetCoreInstaller.installationDirectoryWindows = path_1.default.join(process.env['PROGRAMFILES'] + '', 'dotnet');
Expand Down Expand Up @@ -408,6 +425,7 @@ const core = __importStar(__nccwpck_require__(2186));
const installer_1 = __nccwpck_require__(1480);
const fs = __importStar(__nccwpck_require__(7147));
const path_1 = __importDefault(__nccwpck_require__(1017));
const semver_1 = __importDefault(__nccwpck_require__(5911));
const auth = __importStar(__nccwpck_require__(8527));
const qualityOptions = [
'daily',
Expand All @@ -429,6 +447,7 @@ function run() {
// Proxy, auth, (etc) are still set up, even if no version is identified
//
const versions = core.getMultilineInput('dotnet-version');
const installedDotnetVersions = [];
const globalJsonFileInput = core.getInput('global-json-file');
if (globalJsonFileInput) {
const globalJsonPath = path_1.default.join(process.cwd(), globalJsonFileInput);
Expand All @@ -454,7 +473,8 @@ function run() {
const uniqueVersions = new Set(versions);
for (const version of uniqueVersions) {
dotnetInstaller = new installer_1.DotnetCoreInstaller(version, quality);
yield dotnetInstaller.installDotnet();
const installedVersion = yield dotnetInstaller.installDotnet();
installedDotnetVersions.push(installedVersion);
}
installer_1.DotnetCoreInstaller.addToPath();
}
Expand All @@ -463,6 +483,13 @@ function run() {
if (sourceUrl) {
auth.configAuthentication(sourceUrl, configFile);
}
const comparisonRange = globalJsonFileInput
? versions[versions.length - 1]
: '*';
const versionToOutput = semver_1.default.maxSatisfying(installedDotnetVersions, comparisonRange, {
includePrerelease: true
});
core.setOutput('dotnet-version', versionToOutput);
const matchersPath = path_1.default.join(__dirname, '..', '.github');
core.info(`##[add-matcher]${path_1.default.join(matchersPath, 'csc.json')}`);
}
Expand Down
24 changes: 23 additions & 1 deletion src/installer.ts
Expand Up @@ -164,7 +164,7 @@ export class DotnetCoreInstaller {
}
}

public async installDotnet() {
public async installDotnet(): Promise<string> {
const windowsDefaultOptions = [
'-NoLogo',
'-Sta',
Expand Down Expand Up @@ -238,5 +238,27 @@ export class DotnetCoreInstaller {
if (exitCode) {
throw new Error(`Failed to install dotnet ${exitCode}. ${stdout}`);
}

return this.outputDotnetVersion(stdout);
}

private outputDotnetVersion(logs: string): string {
let resolvedVersion: string = '';
const installedByScriptPattern = /Installed version is (?<version>\d+\.\d+\.\d.*)$/m;
const preinstalledOnRunnerPattern = /.NET Core SDK with version '(?<version>\d+\.\d+\.\d.*)'/m;

const regExpressions: RegExp[] = [
installedByScriptPattern,
preinstalledOnRunnerPattern
];

for (let regExp of regExpressions) {
if (regExp.test(logs)) {
resolvedVersion = logs.match(regExp)!.groups!.version;
break;
}
}

return resolvedVersion;
}
}
19 changes: 18 additions & 1 deletion src/setup-dotnet.ts
Expand Up @@ -2,6 +2,7 @@ import * as core from '@actions/core';
import {DotnetCoreInstaller} from './installer';
import * as fs from 'fs';
import path from 'path';
import semver from 'semver';
import * as auth from './authutil';

const qualityOptions = [
Expand All @@ -26,6 +27,7 @@ export async function run() {
// Proxy, auth, (etc) are still set up, even if no version is identified
//
const versions = core.getMultilineInput('dotnet-version');
const installedDotnetVersions: string[] = [];

const globalJsonFileInput = core.getInput('global-json-file');
if (globalJsonFileInput) {
Expand Down Expand Up @@ -60,7 +62,8 @@ export async function run() {
const uniqueVersions = new Set<string>(versions);
for (const version of uniqueVersions) {
dotnetInstaller = new DotnetCoreInstaller(version, quality);
await dotnetInstaller.installDotnet();
const installedVersion = await dotnetInstaller.installDotnet();
installedDotnetVersions.push(installedVersion);
}
DotnetCoreInstaller.addToPath();
}
Expand All @@ -71,6 +74,20 @@ export async function run() {
auth.configAuthentication(sourceUrl, configFile);
}

const comparisonRange: string = globalJsonFileInput
? versions[versions.length - 1]!
: '*';

const versionToOutput = semver.maxSatisfying(
installedDotnetVersions,
comparisonRange,
{
includePrerelease: true
}
);

core.setOutput('dotnet-version', versionToOutput);

const matchersPath = path.join(__dirname, '..', '.github');
core.info(`##[add-matcher]${path.join(matchersPath, 'csc.json')}`);
} catch (error) {
Expand Down