2019-09-10 00:27:23 +07:00
// Load tempDirectory before it gets wiped by tool-cache
import * as core from '@actions/core' ;
import * as exec from '@actions/exec' ;
import * as io from '@actions/io' ;
2020-01-26 13:37:54 +07:00
import hc = require ( '@actions/http-client' ) ;
2019-09-10 00:27:23 +07:00
import { chmodSync } from 'fs' ;
import * as path from 'path' ;
2020-09-15 23:36:09 +07:00
import { ExecOptions } from '@actions/exec/lib/interfaces' ;
2019-09-10 00:27:23 +07:00
import * as semver from 'semver' ;
const IS_WINDOWS = process . platform === 'win32' ;
2020-04-10 00:26:10 +07:00
/ * *
* Represents the inputted version information
* /
2020-04-05 00:23:59 +07:00
export class DotNetVersionInfo {
2020-09-15 23:36:09 +07:00
public inputVersion : string ;
2020-04-10 00:38:39 +07:00
private fullversion : string ;
2020-04-05 00:23:59 +07:00
private isExactVersionSet : boolean = false ;
2019-09-10 00:27:23 +07:00
constructor ( version : string ) {
2020-09-15 23:36:09 +07:00
this . inputVersion = version ;
2020-04-05 00:23:59 +07:00
// Check for exact match
2020-04-10 00:38:39 +07:00
if ( semver . valid ( semver . clean ( version ) || '' ) != null ) {
2020-04-05 00:23:59 +07:00
this . fullversion = semver . clean ( version ) as string ;
this . isExactVersionSet = true ;
return ;
}
2021-03-17 18:56:08 +07:00
const parts : string [ ] = version . split ( '.' ) ;
2020-04-05 20:37:29 +07:00
2020-04-10 00:38:39 +07:00
if ( parts . length < 2 || parts . length > 3 ) this . throwInvalidVersionFormat ( ) ;
2020-04-05 20:37:29 +07:00
2020-04-10 00:38:39 +07:00
if ( parts . length == 3 && parts [ 2 ] !== 'x' && parts [ 2 ] !== '*' ) {
this . throwInvalidVersionFormat ( ) ;
2020-04-05 20:37:29 +07:00
}
2021-03-17 18:56:08 +07:00
const major = this . getVersionNumberOrThrow ( parts [ 0 ] ) ;
2021-03-17 18:58:04 +07:00
const minor = [ 'x' , '*' ] . includes ( parts [ 1 ] )
? parts [ 1 ]
: this . getVersionNumberOrThrow ( parts [ 1 ] ) ;
2020-04-04 23:05:12 +07:00
2020-04-10 00:26:10 +07:00
this . fullversion = major + '.' + minor ;
2020-04-04 23:05:12 +07:00
}
2020-04-10 00:38:39 +07:00
private getVersionNumberOrThrow ( input : string ) : number {
try {
if ( ! input || input . trim ( ) === '' ) this . throwInvalidVersionFormat ( ) ;
2020-04-05 20:37:29 +07:00
let number = Number ( input ) ;
2020-04-10 00:38:39 +07:00
if ( Number . isNaN ( number ) || number < 0 ) this . throwInvalidVersionFormat ( ) ;
2020-04-05 20:37:29 +07:00
return number ;
} catch {
this . throwInvalidVersionFormat ( ) ;
return - 1 ;
}
}
private throwInvalidVersionFormat() {
2021-03-17 16:38:57 +07:00
throw new Error (
'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'
) ;
2020-04-05 20:37:29 +07:00
}
2020-04-05 00:23:59 +07:00
/ * *
* If true exacatly one version should be resolved
* /
2020-04-10 00:38:39 +07:00
public isExactVersion ( ) : boolean {
2020-04-05 00:23:59 +07:00
return this . isExactVersionSet ;
2020-04-04 23:05:12 +07:00
}
2020-04-10 00:38:39 +07:00
public version ( ) : string {
2020-04-05 00:23:59 +07:00
return this . fullversion ;
2020-04-04 23:05:12 +07:00
}
}
export class DotnetCoreInstaller {
2020-07-17 05:23:46 +07:00
constructor ( version : string , includePrerelease : boolean = false ) {
2020-09-15 23:36:09 +07:00
this . version = version ;
2020-07-17 05:23:46 +07:00
this . includePrerelease = includePrerelease ;
2019-09-10 00:27:23 +07:00
}
public async installDotnet() {
2020-09-15 23:36:09 +07:00
let output = '' ;
let resultCode = 0 ;
2019-09-10 00:27:23 +07:00
2020-09-15 23:36:09 +07:00
let calculatedVersion = await this . resolveVersion (
new DotNetVersionInfo ( this . version )
) ;
2020-04-04 23:05:12 +07:00
2020-09-15 23:36:09 +07:00
var envVariables : { [ key : string ] : string } = { } ;
for ( let key in process . env ) {
if ( process . env [ key ] ) {
let value : any = process . env [ key ] ;
envVariables [ key ] = value ;
2020-04-04 23:05:12 +07:00
}
2019-09-10 00:27:23 +07:00
}
if ( IS_WINDOWS ) {
let escapedScript = path
2020-09-15 23:36:09 +07:00
. join ( __dirname , '..' , 'externals' , 'install-dotnet.ps1' )
2019-09-10 00:27:23 +07:00
. replace ( /'/g , "''" ) ;
let command = ` & ' ${ escapedScript } ' ` ;
2020-09-15 23:36:09 +07:00
if ( calculatedVersion ) {
command += ` -Version ${ calculatedVersion } ` ;
}
if ( process . env [ 'https_proxy' ] != null ) {
command += ` -ProxyAddress ${ process . env [ 'https_proxy' ] } ` ;
}
// This is not currently an option
if ( process . env [ 'no_proxy' ] != null ) {
command += ` -ProxyBypassList ${ process . env [ 'no_proxy' ] } ` ;
}
2019-09-10 00:27:23 +07:00
2020-09-15 23:36:09 +07:00
// process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used
2019-09-10 00:27:23 +07:00
const powershellPath = await io . which ( 'powershell' , true ) ;
2020-09-15 23:36:09 +07:00
var options : ExecOptions = {
listeners : {
stdout : ( data : Buffer ) = > {
output += data . toString ( ) ;
}
} ,
env : envVariables
} ;
2019-09-10 00:27:23 +07:00
resultCode = await exec . exec (
` " ${ powershellPath } " ` ,
[
'-NoLogo' ,
'-Sta' ,
'-NoProfile' ,
'-NonInteractive' ,
'-ExecutionPolicy' ,
'Unrestricted' ,
'-Command' ,
command
] ,
2020-09-15 23:36:09 +07:00
options
2019-09-10 00:27:23 +07:00
) ;
} else {
2020-09-15 23:36:09 +07:00
let escapedScript = path
. join ( __dirname , '..' , 'externals' , 'install-dotnet.sh' )
. replace ( /'/g , "''" ) ;
chmodSync ( escapedScript , '777' ) ;
const scriptPath = await io . which ( escapedScript , true ) ;
2019-09-10 00:27:23 +07:00
2020-09-15 23:36:09 +07:00
let scriptArguments : string [ ] = [ ] ;
2020-09-24 22:26:00 +07:00
if ( calculatedVersion ) {
scriptArguments . push ( '--version' , calculatedVersion ) ;
2020-09-15 23:36:09 +07:00
}
// process.env must be explicitly passed in for DOTNET_INSTALL_DIR to be used
resultCode = await exec . exec ( ` " ${ scriptPath } " ` , scriptArguments , {
2019-09-10 00:27:23 +07:00
listeners : {
stdout : ( data : Buffer ) = > {
output += data . toString ( ) ;
}
2020-09-15 23:36:09 +07:00
} ,
env : envVariables
2019-09-10 00:27:23 +07:00
} ) ;
}
2021-11-23 17:03:56 +07:00
if ( resultCode != 0 ) {
throw new Error ( ` Failed to install dotnet ${ resultCode } . ${ output } ` ) ;
}
}
static addToPath() {
2020-09-15 23:36:09 +07:00
if ( process . env [ 'DOTNET_INSTALL_DIR' ] ) {
core . addPath ( process . env [ 'DOTNET_INSTALL_DIR' ] ) ;
2020-09-24 22:26:00 +07:00
core . exportVariable ( 'DOTNET_ROOT' , process . env [ 'DOTNET_INSTALL_DIR' ] ) ;
2020-09-15 23:36:09 +07:00
} else {
if ( IS_WINDOWS ) {
// This is the default set in install-dotnet.ps1
core . addPath (
path . join ( process . env [ 'LocalAppData' ] + '' , 'Microsoft' , 'dotnet' )
) ;
core . exportVariable (
'DOTNET_ROOT' ,
path . join ( process . env [ 'LocalAppData' ] + '' , 'Microsoft' , 'dotnet' )
) ;
} else {
// This is the default set in install-dotnet.sh
core . addPath ( path . join ( process . env [ 'HOME' ] + '' , '.dotnet' ) ) ;
2020-09-24 22:26:00 +07:00
core . exportVariable (
'DOTNET_ROOT' ,
path . join ( process . env [ 'HOME' ] + '' , '.dotnet' )
) ;
2020-09-15 23:36:09 +07:00
}
2019-09-10 00:27:23 +07:00
}
2020-09-15 23:36:09 +07:00
console . log ( process . env [ 'PATH' ] ) ;
2019-09-10 00:27:23 +07:00
}
2020-09-15 23:36:09 +07:00
// versionInfo - versionInfo of the SDK/Runtime
async resolveVersion ( versionInfo : DotNetVersionInfo ) : Promise < string > {
if ( versionInfo . isExactVersion ( ) ) {
return versionInfo . version ( ) ;
2019-09-10 00:27:23 +07:00
}
2020-01-26 13:37:54 +07:00
const httpClient = new hc . HttpClient ( 'actions/setup-dotnet' , [ ] , {
allowRetries : true ,
maxRetries : 3
} ) ;
2020-04-05 00:23:59 +07:00
2019-11-08 23:15:28 +07:00
const releasesJsonUrl : string = await this . getReleasesJsonUrl (
2020-01-26 13:37:54 +07:00
httpClient ,
2020-04-05 00:23:59 +07:00
versionInfo . version ( ) . split ( '.' )
2019-11-08 23:15:28 +07:00
) ;
2020-01-26 13:37:54 +07:00
const releasesResponse = await httpClient . getJson < any > ( releasesJsonUrl ) ;
const releasesResult = releasesResponse . result || { } ;
let releasesInfo : any [ ] = releasesResult [ 'releases' ] ;
2019-09-10 00:27:23 +07:00
releasesInfo = releasesInfo . filter ( ( releaseInfo : any ) = > {
return (
2020-07-17 05:23:46 +07:00
semver . satisfies ( releaseInfo [ 'sdk' ] [ 'version' ] , versionInfo . version ( ) , {
includePrerelease : this.includePrerelease
} ) ||
2020-04-10 00:38:39 +07:00
semver . satisfies (
releaseInfo [ 'sdk' ] [ 'version-display' ] ,
2020-07-17 05:23:46 +07:00
versionInfo . version ( ) ,
{
includePrerelease : this.includePrerelease
}
2020-04-10 00:38:39 +07:00
)
2019-09-10 00:27:23 +07:00
) ;
} ) ;
2020-04-05 00:23:59 +07:00
// Exclude versions that are newer than the latest if using not exact
2020-09-15 23:36:09 +07:00
let latestSdk : string = releasesResult [ 'latest-sdk' ] ;
2020-04-05 00:23:59 +07:00
2020-09-15 23:36:09 +07:00
releasesInfo = releasesInfo . filter ( ( releaseInfo : any ) = >
2020-07-17 05:23:46 +07:00
semver . lte ( releaseInfo [ 'sdk' ] [ 'version' ] , latestSdk , {
includePrerelease : this.includePrerelease
} )
2020-09-15 23:36:09 +07:00
) ;
2020-04-05 00:23:59 +07:00
// Sort for latest version
2020-04-10 00:38:39 +07:00
releasesInfo = releasesInfo . sort ( ( a , b ) = >
2020-07-17 05:23:46 +07:00
semver . rcompare ( a [ 'sdk' ] [ 'version' ] , b [ 'sdk' ] [ 'version' ] , {
includePrerelease : this.includePrerelease
} )
2020-04-10 00:38:39 +07:00
) ;
2020-04-04 23:05:12 +07:00
2020-09-15 23:36:09 +07:00
if ( releasesInfo . length == 0 ) {
2021-03-17 16:38:57 +07:00
throw new Error (
` Could not find dotnet core version. Please ensure that specified version ${ versionInfo . inputVersion } is valid. `
) ;
2019-09-10 00:27:23 +07:00
}
2020-09-15 23:36:09 +07:00
let release = releasesInfo [ 0 ] ;
return release [ 'sdk' ] [ 'version' ] ;
2019-09-10 00:27:23 +07:00
}
2019-11-08 23:15:28 +07:00
private async getReleasesJsonUrl (
2020-01-26 13:37:54 +07:00
httpClient : hc.HttpClient ,
2019-11-08 23:15:28 +07:00
versionParts : string [ ]
) : Promise < string > {
2020-01-26 13:37:54 +07:00
const response = await httpClient . getJson < any > ( DotNetCoreIndexUrl ) ;
const result = response . result || { } ;
let releasesInfo : any [ ] = result [ 'releases-index' ] ;
2020-09-24 22:26:00 +07:00
2019-11-08 23:15:28 +07:00
releasesInfo = releasesInfo . filter ( ( info : any ) = > {
// channel-version is the first 2 elements of the version (e.g. 2.1), filter out versions that don't match 2.1.x.
const sdkParts : string [ ] = info [ 'channel-version' ] . split ( '.' ) ;
2020-09-24 22:26:00 +07:00
if (
versionParts . length >= 2 &&
! ( versionParts [ 1 ] == 'x' || versionParts [ 1 ] == '*' )
) {
2019-11-08 23:15:28 +07:00
return versionParts [ 0 ] == sdkParts [ 0 ] && versionParts [ 1 ] == sdkParts [ 1 ] ;
}
return versionParts [ 0 ] == sdkParts [ 0 ] ;
} ) ;
2020-09-24 22:26:00 +07:00
2019-11-08 23:15:28 +07:00
if ( releasesInfo . length === 0 ) {
2021-03-17 16:38:57 +07:00
throw new Error (
` Could not find info for version ${ versionParts . join (
'.'
) } at $ { DotNetCoreIndexUrl } `
) ;
2019-11-08 23:15:28 +07:00
}
2020-09-24 22:26:00 +07:00
2021-11-23 20:58:49 +07:00
const releaseInfo = releasesInfo [ 0 ] ;
if ( releaseInfo [ 'support-phase' ] === 'eol' ) {
core . warning (
` ${ releaseInfo [ 'product' ] } ${ releaseInfo [ 'channel-version' ] } is no longer supported and will not receive security updates in the future. Please refer to https://aka.ms/dotnet-core-support for more information about the .NET support policy. `
) ;
}
return releaseInfo [ 'releases.json' ] ;
2019-09-10 00:27:23 +07:00
}
2020-09-15 23:36:09 +07:00
private version : string ;
2020-07-17 05:23:46 +07:00
private includePrerelease : boolean ;
2019-09-10 00:27:23 +07:00
}
2019-11-08 23:15:28 +07:00
const DotNetCoreIndexUrl : string =
'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json' ;