diff --git a/.gitignore b/.gitignore index 20ab407f7..8912b80ed 100644 --- a/.gitignore +++ b/.gitignore @@ -236,9 +236,6 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ -# Typescript v1 declaration files -typings/ - # Visual Studio 6 build log *.plg diff --git a/.vscode/settings.json b/.vscode/settings.json index bcc57a1a3..7033a6d52 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,5 +32,6 @@ ], "url": "assets/ExpressionMetadata.schema.json" } - ] + ], + "typescript.tsdk": "node_modules\\typescript\\lib" } diff --git a/assets/install scripts/determine-linux-distro.sh b/assets/install scripts/determine-linux-distro.sh deleted file mode 100755 index be075ddc5..000000000 --- a/assets/install scripts/determine-linux-distro.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright © Microsoft Corporation -# All rights reserved. -# -# Licensed under the MIT License. See LICENSE-CODE in the project root for details. -# -# Exit codes: -# 0 - Success -# 4 - Distribution not supported by script -# - -set H+ - -#openSUSE - Has to be first since apt-get is available but package names different -if type zypper > /dev/null 2>&1; then - echo "SUSE" - exit 0 - -# Debian / Ubuntu -elif type apt-get > /dev/null 2>&1; then - echo "Debian" - exit 0 - -#RHL/Fedora/CentOS -elif type yum > /dev/null 2>&1; then - echo "RedHat" - exit 0 - -#ArchLinux -elif type pacman > /dev/null 2>&1; then - echo "ArchLinux" - exit 0 - -#Solus -elif type eopkg > /dev/null 2>&1; then - echo "Solus" - exit 0 - -#Alpine Linux -elif type apk > /dev/null 2>&1; then - echo "Alpine" - exit 0 - -# Distro not supported -else - echo "UNKNOWN" - exit 4 -fi diff --git a/assets/install scripts/dotnet-install.cmd b/assets/install scripts/dotnet-install.cmd deleted file mode 100644 index 9db720383..000000000 --- a/assets/install scripts/dotnet-install.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@ECHO OFF -PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0dotnet-install.ps1' %*; exit $LASTEXITCODE" \ No newline at end of file diff --git a/assets/install scripts/dotnet-install.ps1 b/assets/install scripts/dotnet-install.ps1 deleted file mode 100644 index 3ecd622d5..000000000 --- a/assets/install scripts/dotnet-install.ps1 +++ /dev/null @@ -1,687 +0,0 @@ -# -# Copyright (c) .NET Foundation and contributors. All rights reserved. -# Licensed under the MIT license. See LICENSE file in the project root for full license information. -# - -<# -.SYNOPSIS - Installs dotnet cli -.DESCRIPTION - Installs dotnet cli. If dotnet installation already exists in the given directory - it will update it only if the requested version differs from the one already installed. -.PARAMETER Channel - Default: LTS - Download from the Channel specified. Possible values: - - Current - most current release - - LTS - most current supported release - - 2-part version in a format A.B - represents a specific release - examples: 2.0, 1.0 - - Branch name - examples: release/2.0.0, Master - Note: The version parameter overrides the channel parameter. -.PARAMETER Version - Default: latest - Represents a build version on specific channel. Possible values: - - latest - most latest build on specific channel - - coherent - most latest coherent build on specific channel - coherent applies only to SDK downloads - - 3-part version in a format A.B.C - represents specific version of build - examples: 2.0.0-preview2-006120, 1.1.0 -.PARAMETER InstallDir - Default: %LocalAppData%\Microsoft\dotnet - Path to where to install dotnet. Note that binaries will be placed directly in a given directory. -.PARAMETER Architecture - Default: - this value represents currently running OS architecture - Architecture of dotnet binaries to be installed. - Possible values are: , amd64, x64, x86, arm64, arm -.PARAMETER SharedRuntime - This parameter is obsolete and may be removed in a future version of this script. - The recommended alternative is '-Runtime dotnet'. - Installs just the shared runtime bits, not the entire SDK. -.PARAMETER Runtime - Installs just a shared runtime, not the entire SDK. - Possible values: - - dotnet - the Microsoft.NETCore.App shared runtime - - aspnetcore - the Microsoft.AspNetCore.App shared runtime - - windowsdesktop - the Microsoft.WindowsDesktop.App shared runtime -.PARAMETER DryRun - If set it will not perform installation but instead display what command line to use to consistently install - currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link - with specific version so that this command can be used deterministicly in a build script. - It also displays binaries location if you prefer to install or download it yourself. -.PARAMETER NoPath - By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder. - If set it will display binaries location but not set any environment variable. -.PARAMETER Verbose - Displays diagnostics information. -.PARAMETER AzureFeed - Default: https://dotnetcli.azureedge.net/dotnet - This parameter typically is not changed by the user. - It allows changing the URL for the Azure feed used by this installer. -.PARAMETER UncachedFeed - This parameter typically is not changed by the user. - It allows changing the URL for the Uncached feed used by this installer. -.PARAMETER FeedCredential - Used as a query string to append to the Azure feed. - It allows changing the URL to use non-public blob storage accounts. -.PARAMETER ProxyAddress - If set, the installer will use the proxy when making web requests -.PARAMETER ProxyUseDefaultCredentials - Default: false - Use default credentials, when using proxy address. -.PARAMETER SkipNonVersionedFiles - Default: false - Skips installing non-versioned files if they already exist, such as dotnet.exe. -.PARAMETER NoCdn - Disable downloading from the Azure CDN, and use the uncached feed directly. -.PARAMETER JSonFile - Determines the SDK version from a user specified global.json file - Note: global.json must have a value for 'SDK:Version' -#> -[cmdletbinding()] -param( - [string]$Channel = "LTS", - [string]$Version = "Latest", - [string]$JSonFile, - [string]$InstallDir = "", - [string]$Architecture = "", - [ValidateSet("dotnet", "aspnetcore", "windowsdesktop", IgnoreCase = $false)] - [string]$Runtime, - [Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")] - [switch]$SharedRuntime, - [switch]$DryRun, - [switch]$NoPath, - [string]$AzureFeed = "https://dotnetcli.azureedge.net/dotnet", - [string]$UncachedFeed = "https://dotnetcli.blob.core.windows.net/dotnet", - [string]$FeedCredential, - [string]$ProxyAddress, - [switch]$ProxyUseDefaultCredentials, - [switch]$SkipNonVersionedFiles, - [switch]$NoCdn -) - -Set-StrictMode -Version Latest -$ErrorActionPreference = "Stop" -$ProgressPreference = "SilentlyContinue" - -if ($NoCdn) { - $AzureFeed = $UncachedFeed -} - -$BinFolderRelativePath = "" - -if ($SharedRuntime -and (-not $Runtime)) { - $Runtime = "dotnet" -} - -# example path with regex: shared/1.0.0-beta-12345/somepath -$VersionRegEx = "/\d+\.\d+[^/]+/" -$OverrideNonVersionedFiles = !$SkipNonVersionedFiles - -function Say($str) { - Write-Host "dotnet-install: $str" -} - -function Say-Verbose($str) { - Write-Verbose "dotnet-install: $str" -} - -function Say-Invocation($Invocation) { - $command = $Invocation.MyCommand; - $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ") - Say-Verbose "$command $args" -} - -function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [int]$SecondsBetweenAttempts = 1) { - $Attempts = 0 - - while ($true) { - try { - return $ScriptBlock.Invoke() - } - catch { - $Attempts++ - if ($Attempts -lt $MaxAttempts) { - Start-Sleep $SecondsBetweenAttempts - } - else { - throw - } - } - } -} - -function Get-Machine-Architecture() { - Say-Invocation $MyInvocation - - # possible values: amd64, x64, x86, arm64, arm - return $ENV:PROCESSOR_ARCHITECTURE -} - -function Get-CLIArchitecture-From-Architecture([string]$Architecture) { - Say-Invocation $MyInvocation - - switch ($Architecture.ToLower()) { - { $_ -eq "" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) } - { ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" } - { $_ -eq "x86" } { return "x86" } - { $_ -eq "arm" } { return "arm" } - { $_ -eq "arm64" } { return "arm64" } - default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" } - } -} - -# The version text returned from the feeds is a 1-line or 2-line string: -# For the SDK and the dotnet runtime (2 lines): -# Line 1: # commit_hash -# Line 2: # 4-part version -# For the aspnetcore runtime (1 line): -# Line 1: # 4-part version -function Get-Version-Info-From-Version-Text([string]$VersionText) { - Say-Invocation $MyInvocation - - $Data = -split $VersionText - - $VersionInfo = @{ - CommitHash = $(if ($Data.Count -gt 1) { $Data[0] }) - Version = $Data[-1] # last line is always the version number. - } - return $VersionInfo -} - -function Load-Assembly([string] $Assembly) { - try { - Add-Type -Assembly $Assembly | Out-Null - } - catch { - # On Nano Server, Powershell Core Edition is used. Add-Type is unable to resolve base class assemblies because they are not GAC'd. - # Loading the base class assemblies is not unnecessary as the types will automatically get resolved. - } -} - -function GetHTTPResponse([Uri] $Uri) { - Invoke-With-Retry( - { - - $HttpClient = $null - - try { - # HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet. - Load-Assembly -Assembly System.Net.Http - - if (-not $ProxyAddress) { - try { - # Despite no proxy being explicitly specified, we may still be behind a default proxy - $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy; - if ($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) { - $ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString - $ProxyUseDefaultCredentials = $true - } - } - catch { - # Eat the exception and move forward as the above code is an attempt - # at resolving the DefaultProxy that may not have been a problem. - $ProxyAddress = $null - Say-Verbose("Exception ignored: $_.Exception.Message - moving forward...") - } - } - - if ($ProxyAddress) { - $HttpClientHandler = New-Object System.Net.Http.HttpClientHandler - $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address = $ProxyAddress; UseDefaultCredentials = $ProxyUseDefaultCredentials } - $HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler - } - else { - - $HttpClient = New-Object System.Net.Http.HttpClient - } - # Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out - # 20 minutes allows it to work over much slower connections. - $HttpClient.Timeout = New-TimeSpan -Minutes 20 - $Response = $HttpClient.GetAsync("${Uri}${FeedCredential}").Result - if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode))) { - # The feed credential is potentially sensitive info. Do not log FeedCredential to console output. - $ErrorMsg = "Failed to download $Uri." - if ($Response -ne $null) { - $ErrorMsg += " $Response" - } - - throw $ErrorMsg - } - - return $Response - } - finally { - if ($HttpClient -ne $null) { - $HttpClient.Dispose() - } - } - }) -} - -function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) { - Say-Invocation $MyInvocation - - $VersionFileUrl = $null - if ($Runtime -eq "dotnet") { - $VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version" - } - elseif ($Runtime -eq "aspnetcore") { - $VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version" - } - # Currently, the WindowsDesktop runtime is manufactured with the .Net core runtime - elseif ($Runtime -eq "windowsdesktop") { - $VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version" - } - elseif (-not $Runtime) { - if ($Coherent) { - $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version" - } - else { - $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version" - } - } - else { - throw "Invalid value for `$Runtime" - } - try { - $Response = GetHTTPResponse -Uri $VersionFileUrl - } - catch { - throw "Could not resolve version information." - } - $StringContent = $Response.Content.ReadAsStringAsync().Result - - switch ($Response.Content.Headers.ContentType) { - { ($_ -eq "application/octet-stream") } { $VersionText = $StringContent } - { ($_ -eq "text/plain") } { $VersionText = $StringContent } - { ($_ -eq "text/plain; charset=UTF-8") } { $VersionText = $StringContent } - default { throw "``$Response.Content.Headers.ContentType`` is an unknown .version file content type." } - } - - $VersionInfo = Get-Version-Info-From-Version-Text $VersionText - - return $VersionInfo -} - -function Parse-Jsonfile-For-Version([string]$JSonFile) { - Say-Invocation $MyInvocation - - If (-Not (Test-Path $JSonFile)) { - throw "Unable to find '$JSonFile'" - } - try { - $JSonContent = Get-Content($JSonFile) -Raw | ConvertFrom-Json | Select-Object -expand "sdk" -ErrorAction SilentlyContinue - } - catch { - throw "Json file unreadable: '$JSonFile'" - } - if ($JSonContent) { - try { - $JSonContent.PSObject.Properties | ForEach-Object { - $PropertyName = $_.Name - if ($PropertyName -eq "version") { - $Version = $_.Value - Say-Verbose "Version = $Version" - } - } - } - catch { - throw "Unable to parse the SDK node in '$JSonFile'" - } - } - else { - throw "Unable to find the SDK node in '$JSonFile'" - } - If ($Version -eq $null) { - throw "Unable to find the SDK:version node in '$JSonFile'" - } - return $Version -} - -function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version, [string]$JSonFile) { - Say-Invocation $MyInvocation - - if (-not $JSonFile) { - switch ($Version.ToLower()) { - { $_ -eq "latest" } { - $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False - return $LatestVersionInfo.Version - } - { $_ -eq "coherent" } { - $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True - return $LatestVersionInfo.Version - } - default { return $Version } - } - } - else { - return Parse-Jsonfile-For-Version $JSonFile - } -} - -function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) { - Say-Invocation $MyInvocation - - if ($Runtime -eq "dotnet") { - $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip" - } - elseif ($Runtime -eq "aspnetcore") { - $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip" - } - elseif ($Runtime -eq "windowsdesktop") { - $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificVersion-win-$CLIArchitecture.zip" - } - elseif (-not $Runtime) { - $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip" - } - else { - throw "Invalid value for `$Runtime" - } - - Say-Verbose "Constructed primary named payload URL: $PayloadURL" - - return $PayloadURL -} - -function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) { - Say-Invocation $MyInvocation - - if (-not $Runtime) { - $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip" - } - elseif ($Runtime -eq "dotnet") { - $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-win-$CLIArchitecture.$SpecificVersion.zip" - } - else { - return $null - } - - Say-Verbose "Constructed legacy named payload URL: $PayloadURL" - - return $PayloadURL -} - -function Get-User-Share-Path() { - Say-Invocation $MyInvocation - - $InstallRoot = $env:DOTNET_INSTALL_DIR - if (!$InstallRoot) { - $InstallRoot = "$env:LocalAppData\Microsoft\dotnet" - } - return $InstallRoot -} - -function Resolve-Installation-Path([string]$InstallDir) { - Say-Invocation $MyInvocation - - if ($InstallDir -eq "") { - return Get-User-Share-Path - } - return $InstallDir -} - -function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) { - Say-Invocation $MyInvocation - - $DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion - Say-Verbose "Is-Dotnet-Package-Installed: DotnetPackagePath=$DotnetPackagePath" - return Test-Path $DotnetPackagePath -PathType Container -} - -function Get-Absolute-Path([string]$RelativeOrAbsolutePath) { - # Too much spam - # Say-Invocation $MyInvocation - - return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath) -} - -function Get-Path-Prefix-With-Version($path) { - $match = [regex]::match($path, $VersionRegEx) - if ($match.Success) { - return $entry.FullName.Substring(0, $match.Index + $match.Length) - } - - return $null -} - -function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip, [string]$OutPath) { - Say-Invocation $MyInvocation - - $ret = @() - foreach ($entry in $Zip.Entries) { - $dir = Get-Path-Prefix-With-Version $entry.FullName - if ($dir -ne $null) { - $path = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $dir) - if (-Not (Test-Path $path -PathType Container)) { - $ret += $dir - } - } - } - - $ret = $ret | Sort-Object | Get-Unique - - $values = ($ret | foreach { "$_" }) -join ";" - Say-Verbose "Directories to unpack: $values" - - return $ret -} - -# Example zip content and extraction algorithm: -# Rule: files if extracted are always being extracted to the same relative path locally -# .\ -# a.exe # file does not exist locally, extract -# b.dll # file exists locally, override only if $OverrideFiles set -# aaa\ # same rules as for files -# ... -# abc\1.0.0\ # directory contains version and exists locally -# ... # do not extract content under versioned part -# abc\asd\ # same rules as for files -# ... -# def\ghi\1.0.1\ # directory contains version and does not exist locally -# ... # extract content -function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) { - Say-Invocation $MyInvocation - - Load-Assembly -Assembly System.IO.Compression.FileSystem - Set-Variable -Name Zip - try { - $Zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath) - - $DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath - - foreach ($entry in $Zip.Entries) { - $PathWithVersion = Get-Path-Prefix-With-Version $entry.FullName - if (($PathWithVersion -eq $null) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) { - $DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName) - $DestinationDir = Split-Path -Parent $DestinationPath - $OverrideFiles = $OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath)) - if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) { - New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null - [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles) - } - } - } - } - finally { - if ($Zip -ne $null) { - $Zip.Dispose() - } - } -} - -function DownloadFile($Source, [string]$OutPath) { - if ($Source -notlike "http*") { - # Using System.IO.Path.GetFullPath to get the current directory - # does not work in this context - $pwd gives the current directory - if (![System.IO.Path]::IsPathRooted($Source)) { - $Source = $(Join-Path -Path $pwd -ChildPath $Source) - } - $Source = Get-Absolute-Path $Source - Say "Copying file from $Source to $OutPath" - Copy-Item $Source $OutPath - return - } - - $Stream = $null - - try { - $Response = GetHTTPResponse -Uri $Source - $Stream = $Response.Content.ReadAsStreamAsync().Result - $File = [System.IO.File]::Create($OutPath) - $Stream.CopyTo($File) - $File.Close() - } - finally { - if ($Stream -ne $null) { - $Stream.Dispose() - } - } -} - -function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolderRelativePath) { - $BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath) - if (-Not $NoPath) { - $SuffixedBinPath = "$BinPath;" - if (-Not $env:path.Contains($SuffixedBinPath)) { - Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process." - $env:path = $SuffixedBinPath + $env:path - } - else { - Say-Verbose "Current process PATH already contains `"$BinPath`"" - } - } - else { - Say "Binaries of dotnet can be found in $BinPath" - } -} - -$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture -$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile -$DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture -$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture - -$InstallRoot = Resolve-Installation-Path $InstallDir -Say-Verbose "InstallRoot: $InstallRoot" -$ScriptName = $MyInvocation.MyCommand.Name - -if ($DryRun) { - Say "Payload URLs:" - Say "Primary named payload URL: $DownloadLink" - if ($LegacyDownloadLink) { - Say "Legacy named payload URL: $LegacyDownloadLink" - } - $RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`"" - if ($Runtime -eq "dotnet") { - $RepeatableCommand += " -Runtime `"dotnet`"" - } - elseif ($Runtime -eq "aspnetcore") { - $RepeatableCommand += " -Runtime `"aspnetcore`"" - } - foreach ($key in $MyInvocation.BoundParameters.Keys) { - if (-not (@("Architecture", "Channel", "DryRun", "InstallDir", "Runtime", "SharedRuntime", "Version") -contains $key)) { - $RepeatableCommand += " -$key `"$($MyInvocation.BoundParameters[$key])`"" - } - } - Say "Repeatable invocation: $RepeatableCommand" - exit 0 -} - -if ($Runtime -eq "dotnet") { - $assetName = ".NET Core Runtime" - $dotnetPackageRelativePath = "shared\Microsoft.NETCore.App" -} -elseif ($Runtime -eq "aspnetcore") { - $assetName = "ASP.NET Core Runtime" - $dotnetPackageRelativePath = "shared\Microsoft.AspNetCore.App" -} -elseif ($Runtime -eq "windowsdesktop") { - $assetName = ".NET Core Windows Desktop Runtime" - $dotnetPackageRelativePath = "shared\Microsoft.WindowsDesktop.App" -} -elseif (-not $Runtime) { - $assetName = ".NET Core SDK" - $dotnetPackageRelativePath = "sdk" -} -else { - throw "Invalid value for `$Runtime" -} - -# Check if the SDK version is already installed. -$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion -if ($isAssetInstalled) { - Say "$assetName version $SpecificVersion is already installed." - Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath - exit 0 -} - -New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null - -$installDrive = $((Get-Item $InstallRoot).PSDrive.Name); -$diskInfo = Get-PSDrive -Name $installDrive -if ($diskInfo.Free / 1MB -le 100) { - Say "There is not enough disk space on drive ${installDrive}:" - exit 0 -} - -$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) -Say-Verbose "Zip path: $ZipPath" - -$DownloadFailed = $false -Say "Downloading link: $DownloadLink" -try { - DownloadFile -Source $DownloadLink -OutPath $ZipPath -} -catch { - Say "Cannot download: $DownloadLink" - if ($LegacyDownloadLink) { - $DownloadLink = $LegacyDownloadLink - $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) - Say-Verbose "Legacy zip path: $ZipPath" - Say "Downloading legacy link: $DownloadLink" - try { - DownloadFile -Source $DownloadLink -OutPath $ZipPath - } - catch { - Say "Cannot download: $DownloadLink" - $DownloadFailed = $true - } - } - else { - $DownloadFailed = $true - } -} - -if ($DownloadFailed) { - throw "Could not find/download: `"$assetName`" with version = $SpecificVersion`nRefer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support" -} - -Say "Extracting zip from $DownloadLink" -Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot - -# Check if the SDK version is installed; if not, fail the installation. -$isAssetInstalled = $false - -# if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. -if ($SpecificVersion -Match "rtm" -or $SpecificVersion -Match "servicing") { - $ReleaseVersion = $SpecificVersion.Split("-")[0] - Say-Verbose "Checking installation: version = $ReleaseVersion" - $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $ReleaseVersion -} - -# Check if the SDK version is installed. -if (!$isAssetInstalled) { - Say-Verbose "Checking installation: version = $SpecificVersion" - $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion -} - -if (!$isAssetInstalled) { - throw "`"$assetName`" with version = $SpecificVersion failed to install with an unknown error." -} - -Remove-Item $ZipPath - -Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath - -Say "Installation finished" -exit 0 diff --git a/assets/install scripts/dotnet-install.sh b/assets/install scripts/dotnet-install.sh deleted file mode 100755 index 45e443eda..000000000 --- a/assets/install scripts/dotnet-install.sh +++ /dev/null @@ -1,1092 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) .NET Foundation and contributors. All rights reserved. -# Licensed under the MIT license. See LICENSE file in the project root for full license information. -# - -# Stop script on NZEC -set -e -# Stop script if unbound variable found (use ${var:-} if intentional) -set -u -# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success -# This is causing it to fail -set -o pipefail - -# Use in the the functions: eval $invocation -invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"' - -# standard output may be used as a return value in the functions -# we need a way to write text on the screen in the functions so that -# it won't interfere with the return value. -# Exposing stream 3 as a pipe to standard output of the script itself -exec 3>&1 - -# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors. -# See if stdout is a terminal -if [ -t 1 ] && command -v tput > /dev/null; then - # see if it supports colors - ncolors=$(tput colors) - if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then - bold="$(tput bold || echo)" - normal="$(tput sgr0 || echo)" - black="$(tput setaf 0 || echo)" - red="$(tput setaf 1 || echo)" - green="$(tput setaf 2 || echo)" - yellow="$(tput setaf 3 || echo)" - blue="$(tput setaf 4 || echo)" - magenta="$(tput setaf 5 || echo)" - cyan="$(tput setaf 6 || echo)" - white="$(tput setaf 7 || echo)" - fi -fi - -say_warning() { - printf "%b\n" "${yellow:-}dotnet_install: Warning: $1${normal:-}" -} - -say_err() { - printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2 -} - -say() { - # using stream 3 (defined in the beginning) to not interfere with stdout of functions - # which may be used as return value - printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3 -} - -say_verbose() { - if [ "$verbose" = true ]; then - say "$1" - fi -} - -# This platform list is finite - if the SDK/Runtime has supported Linux distribution-specific assets, -# then and only then should the Linux distribution appear in this list. -# Adding a Linux distribution to this list does not imply distribution-specific support. -get_legacy_os_name_from_platform() { - eval $invocation - - platform="$1" - case "$platform" in - "centos.7") - echo "centos" - return 0 - ;; - "debian.8") - echo "debian" - return 0 - ;; - "debian.9") - echo "debian.9" - return 0 - ;; - "fedora.23") - echo "fedora.23" - return 0 - ;; - "fedora.24") - echo "fedora.24" - return 0 - ;; - "fedora.27") - echo "fedora.27" - return 0 - ;; - "fedora.28") - echo "fedora.28" - return 0 - ;; - "opensuse.13.2") - echo "opensuse.13.2" - return 0 - ;; - "opensuse.42.1") - echo "opensuse.42.1" - return 0 - ;; - "opensuse.42.3") - echo "opensuse.42.3" - return 0 - ;; - "rhel.7"*) - echo "rhel" - return 0 - ;; - "ubuntu.14.04") - echo "ubuntu" - return 0 - ;; - "ubuntu.16.04") - echo "ubuntu.16.04" - return 0 - ;; - "ubuntu.16.10") - echo "ubuntu.16.10" - return 0 - ;; - "ubuntu.18.04") - echo "ubuntu.18.04" - return 0 - ;; - "alpine.3.4.3") - echo "alpine" - return 0 - ;; - esac - return 1 -} - -get_linux_platform_name() { - eval $invocation - - if [ -n "$runtime_id" ]; then - echo "${runtime_id%-*}" - return 0 - else - if [ -e /etc/os-release ]; then - . /etc/os-release - echo "$ID${VERSION_ID:+.${VERSION_ID}}" - return 0 - elif [ -e /etc/redhat-release ]; then - local redhatRelease=$(&1 || true) | grep -q musl -} - -get_current_os_name() { - eval $invocation - - local uname=$(uname) - if [ "$uname" = "Darwin" ]; then - echo "osx" - return 0 - elif [ "$uname" = "FreeBSD" ]; then - echo "freebsd" - return 0 - elif [ "$uname" = "Linux" ]; then - local linux_platform_name - linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; } - - if [ "$linux_platform_name" = "rhel.6" ]; then - echo $linux_platform_name - return 0 - elif is_musl_based_distro; then - echo "linux-musl" - return 0 - else - echo "linux" - return 0 - fi - fi - - say_err "OS name could not be detected: UName = $uname" - return 1 -} - -get_legacy_os_name() { - eval $invocation - - local uname=$(uname) - if [ "$uname" = "Darwin" ]; then - echo "osx" - return 0 - elif [ -n "$runtime_id" ]; then - echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}") - return 0 - else - if [ -e /etc/os-release ]; then - . /etc/os-release - os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "") - if [ -n "$os" ]; then - echo "$os" - return 0 - fi - fi - fi - - say_verbose "Distribution specific OS name and version could not be detected: UName = $uname" - return 1 -} - -machine_has() { - eval $invocation - - hash "$1" > /dev/null 2>&1 - return $? -} - - -check_min_reqs() { - local hasMinimum=false - if machine_has "curl"; then - hasMinimum=true - elif machine_has "wget"; then - hasMinimum=true - fi - - if [ "$hasMinimum" = "false" ]; then - say_err "curl (recommended) or wget are required to download dotnet. Install missing prerequisite to proceed." - return 1 - fi - return 0 -} - -check_pre_reqs() { - eval $invocation - - if [ "${DOTNET_INSTALL_SKIP_PREREQS:-}" = "1" ]; then - return 0 - fi - - if [ "$(uname)" = "Linux" ]; then - if is_musl_based_distro; then - if ! command -v scanelf > /dev/null; then - say_warning "scanelf not found, please install pax-utils package." - return 0 - fi - LDCONFIG_COMMAND="scanelf --ldpath -BF '%f'" - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libintl)" ] && say_warning "Unable to locate libintl. Probable prerequisite missing; install libintl (or gettext)." - else - if [ ! -x "$(command -v ldconfig)" ]; then - say_verbose "ldconfig is not in PATH, trying /sbin/ldconfig." - LDCONFIG_COMMAND="/sbin/ldconfig" - else - LDCONFIG_COMMAND="ldconfig" - fi - local librarypath=${LD_LIBRARY_PATH:-} - LDCONFIG_COMMAND="$LDCONFIG_COMMAND -NXv ${librarypath//:/ }" - fi - - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep zlib)" ] && say_warning "Unable to locate zlib. Probable prerequisite missing; install zlib." - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep ssl)" ] && say_warning "Unable to locate libssl. Probable prerequisite missing; install libssl." - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libicu)" ] && say_warning "Unable to locate libicu. Probable prerequisite missing; install libicu." - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep lttng)" ] && say_warning "Unable to locate liblttng. Probable prerequisite missing; install libcurl." - [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libcurl)" ] && say_warning "Unable to locate libcurl. Probable prerequisite missing; install libcurl." - fi - - return 0 -} - -# args: -# input - $1 -to_lowercase() { - #eval $invocation - - echo "$1" | tr '[:upper:]' '[:lower:]' - return 0 -} - -# args: -# input - $1 -remove_trailing_slash() { - #eval $invocation - - local input="${1:-}" - echo "${input%/}" - return 0 -} - -# args: -# input - $1 -remove_beginning_slash() { - #eval $invocation - - local input="${1:-}" - echo "${input#/}" - return 0 -} - -# args: -# root_path - $1 -# child_path - $2 - this parameter can be empty -combine_paths() { - eval $invocation - - # TODO: Consider making it work with any number of paths. For now: - if [ ! -z "${3:-}" ]; then - say_err "combine_paths: Function takes two parameters." - return 1 - fi - - local root_path="$(remove_trailing_slash "$1")" - local child_path="$(remove_beginning_slash "${2:-}")" - say_verbose "combine_paths: root_path=$root_path" - say_verbose "combine_paths: child_path=$child_path" - echo "$root_path/$child_path" - return 0 -} - -get_machine_architecture() { - eval $invocation - - if command -v uname > /dev/null; then - CPUName=$(uname -m) - case $CPUName in - armv7l) - echo "arm" - return 0 - ;; - aarch64) - echo "arm64" - return 0 - ;; - esac - fi - - # Always default to 'x64' - echo "x64" - return 0 -} - -# args: -# architecture - $1 -get_normalized_architecture_from_architecture() { - eval $invocation - - local architecture="$(to_lowercase "$1")" - case "$architecture" in - \) - echo "$(get_normalized_architecture_from_architecture "$(get_machine_architecture)")" - return 0 - ;; - amd64|x64) - echo "x64" - return 0 - ;; - arm) - echo "arm" - return 0 - ;; - arm64) - echo "arm64" - return 0 - ;; - esac - - say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" - return 1 -} - -# The version text returned from the feeds is a 1-line or 2-line string: -# For the SDK and the dotnet runtime (2 lines): -# Line 1: # commit_hash -# Line 2: # 4-part version -# For the aspnetcore runtime (1 line): -# Line 1: # 4-part version - -# args: -# version_text - stdin -get_version_from_version_info() { - eval $invocation - - cat | tail -n 1 | sed 's/\r$//' - return 0 -} - -# args: -# install_root - $1 -# relative_path_to_package - $2 -# specific_version - $3 -is_dotnet_package_installed() { - eval $invocation - - local install_root="$1" - local relative_path_to_package="$2" - local specific_version="${3//[$'\t\r\n']}" - - local dotnet_package_path="$(combine_paths "$(combine_paths "$install_root" "$relative_path_to_package")" "$specific_version")" - say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path" - - if [ -d "$dotnet_package_path" ]; then - return 0 - else - return 1 - fi -} - -# args: -# azure_feed - $1 -# channel - $2 -# normalized_architecture - $3 -# coherent - $4 -get_latest_version_info() { - eval $invocation - - local azure_feed="$1" - local channel="$2" - local normalized_architecture="$3" - local coherent="$4" - - local version_file_url=null - if [[ "$runtime" == "dotnet" ]]; then - version_file_url="$uncached_feed/Runtime/$channel/latest.version" - elif [[ "$runtime" == "aspnetcore" ]]; then - version_file_url="$uncached_feed/aspnetcore/Runtime/$channel/latest.version" - elif [ -z "$runtime" ]; then - if [ "$coherent" = true ]; then - version_file_url="$uncached_feed/Sdk/$channel/latest.coherent.version" - else - version_file_url="$uncached_feed/Sdk/$channel/latest.version" - fi - else - say_err "Invalid value for \$runtime" - return 1 - fi - say_verbose "get_latest_version_info: latest url: $version_file_url" - - download "$version_file_url" - return $? -} - -# args: -# json_file - $1 -parse_jsonfile_for_version() { - eval $invocation - - local json_file="$1" - if [ ! -f "$json_file" ]; then - say_err "Unable to find \`$json_file\`" - return 1 - fi - - sdk_section=$(cat $json_file | awk '/"sdk"/,/}/') - if [ -z "$sdk_section" ]; then - say_err "Unable to parse the SDK node in \`$json_file\`" - return 1 - fi - - sdk_list=$(echo $sdk_section | awk -F"[{}]" '{print $2}') - sdk_list=${sdk_list//[\" ]/} - sdk_list=${sdk_list//,/$'\n'} - sdk_list="$(echo -e "${sdk_list}" | tr -d '[[:space:]]')" - - local version_info="" - while read -r line; do - IFS=: - while read -r key value; do - if [[ "$key" == "version" ]]; then - version_info=$value - fi - done <<< "$line" - done <<< "$sdk_list" - if [ -z "$version_info" ]; then - say_err "Unable to find the SDK:version node in \`$json_file\`" - return 1 - fi - - unset IFS; - echo "$version_info" - return 0 -} - -# args: -# azure_feed - $1 -# channel - $2 -# normalized_architecture - $3 -# version - $4 -# json_file - $5 -get_specific_version_from_version() { - eval $invocation - - local azure_feed="$1" - local channel="$2" - local normalized_architecture="$3" - local version="$(to_lowercase "$4")" - local json_file="$5" - - if [ -z "$json_file" ]; then - case "$version" in - latest) - local version_info - version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1 - say_verbose "get_specific_version_from_version: version_info=$version_info" - echo "$version_info" | get_version_from_version_info - return 0 - ;; - coherent) - local version_info - version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" true)" || return 1 - say_verbose "get_specific_version_from_version: version_info=$version_info" - echo "$version_info" | get_version_from_version_info - return 0 - ;; - *) - echo "$version" - return 0 - ;; - esac - else - local version_info - version_info="$(parse_jsonfile_for_version "$json_file")" || return 1 - echo "$version_info" - return 0 - fi -} - -# args: -# azure_feed - $1 -# channel - $2 -# normalized_architecture - $3 -# specific_version - $4 -construct_download_link() { - eval $invocation - - local azure_feed="$1" - local channel="$2" - local normalized_architecture="$3" - local specific_version="${4//[$'\t\r\n']}" - - local osname - osname="$(get_current_os_name)" || return 1 - - local download_link=null - if [[ "$runtime" == "dotnet" ]]; then - download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_version-$osname-$normalized_architecture.tar.gz" - elif [[ "$runtime" == "aspnetcore" ]]; then - download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_version-$osname-$normalized_architecture.tar.gz" - elif [ -z "$runtime" ]; then - download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_version-$osname-$normalized_architecture.tar.gz" - else - return 1 - fi - - echo "$download_link" - return 0 -} - -# args: -# azure_feed - $1 -# channel - $2 -# normalized_architecture - $3 -# specific_version - $4 -construct_legacy_download_link() { - eval $invocation - - local azure_feed="$1" - local channel="$2" - local normalized_architecture="$3" - local specific_version="${4//[$'\t\r\n']}" - - local distro_specific_osname - distro_specific_osname="$(get_legacy_os_name)" || return 1 - - local legacy_download_link=null - if [[ "$runtime" == "dotnet" ]]; then - legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" - elif [ -z "$runtime" ]; then - legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" - else - return 1 - fi - - echo "$legacy_download_link" - return 0 -} - -get_user_install_path() { - eval $invocation - - if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then - echo "$DOTNET_INSTALL_DIR" - else - echo "$HOME/.dotnet" - fi - return 0 -} - -# args: -# install_dir - $1 -resolve_installation_path() { - eval $invocation - - local install_dir=$1 - if [ "$install_dir" = "" ]; then - local user_install_path="$(get_user_install_path)" - say_verbose "resolve_installation_path: user_install_path=$user_install_path" - echo "$user_install_path" - return 0 - fi - - echo "$install_dir" - return 0 -} - -# args: -# relative_or_absolute_path - $1 -get_absolute_path() { - eval $invocation - - local relative_or_absolute_path=$1 - echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")" - return 0 -} - -# args: -# input_files - stdin -# root_path - $1 -# out_path - $2 -# override - $3 -copy_files_or_dirs_from_list() { - eval $invocation - - local root_path="$(remove_trailing_slash "$1")" - local out_path="$(remove_trailing_slash "$2")" - local override="$3" - local osname="$(get_current_os_name)" - local override_switch=$( - if [ "$override" = false ]; then - if [ "$osname" = "linux-musl" ]; then - printf -- "-u"; - else - printf -- "-n"; - fi - fi) - - cat | uniq | while read -r file_path; do - local path="$(remove_beginning_slash "${file_path#$root_path}")" - local target="$out_path/$path" - if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then - mkdir -p "$out_path/$(dirname "$path")" - if [ -d "$target" ]; then - rm -rf "$target" - fi - cp -R $override_switch "$root_path/$path" "$target" - fi - done -} - -# args: -# zip_path - $1 -# out_path - $2 -extract_dotnet_package() { - eval $invocation - - local zip_path="$1" - local out_path="$2" - - local temp_out_path="$(mktemp -d "$temporary_file_template")" - - local failed=false - tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true - - local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/' - find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false - find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files" - - rm -rf "$temp_out_path" - - if [ "$failed" = true ]; then - say_err "Extraction failed" - return 1 - fi -} - -# args: -# remote_path - $1 -# [out_path] - $2 - stdout if not provided -download() { - eval $invocation - - local remote_path="$1" - local out_path="${2:-}" - - if [[ "$remote_path" != "http"* ]]; then - cp "$remote_path" "$out_path" - return $? - fi - - local failed=false - if machine_has "curl"; then - downloadcurl "$remote_path" "$out_path" || failed=true - elif machine_has "wget"; then - downloadwget "$remote_path" "$out_path" || failed=true - else - failed=true - fi - if [ "$failed" = true ]; then - say_verbose "Download failed: $remote_path" - return 1 - fi - return 0 -} - -downloadcurl() { - eval $invocation - local remote_path="$1" - local out_path="${2:-}" - - # Append feed_credential as late as possible before calling curl to avoid logging feed_credential - remote_path="${remote_path}${feed_credential}" - - local failed=false - if [ -z "$out_path" ]; then - curl --retry 10 -sSL -f --create-dirs "$remote_path" || failed=true - else - curl --retry 10 -sSL -f --create-dirs -o "$out_path" "$remote_path" || failed=true - fi - if [ "$failed" = true ]; then - say_verbose "Curl download failed" - return 1 - fi - return 0 -} - -downloadwget() { - eval $invocation - local remote_path="$1" - local out_path="${2:-}" - - # Append feed_credential as late as possible before calling wget to avoid logging feed_credential - remote_path="${remote_path}${feed_credential}" - - local failed=false - if [ -z "$out_path" ]; then - wget -q --tries 10 -O - "$remote_path" || failed=true - else - wget --tries 10 -O "$out_path" "$remote_path" || failed=true - fi - if [ "$failed" = true ]; then - say_verbose "Wget download failed" - return 1 - fi - return 0 -} - -calculate_vars() { - eval $invocation - valid_legacy_download_link=true - - normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")" - say_verbose "normalized_architecture=$normalized_architecture" - - specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")" - say_verbose "specific_version=$specific_version" - if [ -z "$specific_version" ]; then - say_err "Could not resolve version information." - return 1 - fi - - download_link="$(construct_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" - say_verbose "Constructed primary named payload URL: $download_link" - - legacy_download_link="$(construct_legacy_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false - - if [ "$valid_legacy_download_link" = true ]; then - say_verbose "Constructed legacy named payload URL: $legacy_download_link" - else - say_verbose "Cound not construct a legacy_download_link; omitting..." - fi - - install_root="$(resolve_installation_path "$install_dir")" - say_verbose "InstallRoot: $install_root" -} - -install_dotnet() { - eval $invocation - local download_failed=false - local asset_name='' - local asset_relative_path='' - - if [[ "$runtime" == "dotnet" ]]; then - asset_relative_path="shared/Microsoft.NETCore.App" - asset_name=".NET Core Runtime" - elif [[ "$runtime" == "aspnetcore" ]]; then - asset_relative_path="shared/Microsoft.AspNetCore.App" - asset_name="ASP.NET Core Runtime" - elif [ -z "$runtime" ]; then - asset_relative_path="sdk" - asset_name=".NET Core SDK" - else - say_err "Invalid value for \$runtime" - return 1 - fi - - # Check if the SDK version is already installed. - if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then - say "$asset_name version $specific_version is already installed." - return 0 - fi - - mkdir -p "$install_root" - zip_path="$(mktemp "$temporary_file_template")" - say_verbose "Zip path: $zip_path" - - say "Downloading link: $download_link" - - # Failures are normal in the non-legacy case for ultimately legacy downloads. - # Do not output to stderr, since output to stderr is considered an error. - download "$download_link" "$zip_path" 2>&1 || download_failed=true - - # if the download fails, download the legacy_download_link - if [ "$download_failed" = true ]; then - say "Cannot download: $download_link" - - if [ "$valid_legacy_download_link" = true ]; then - download_failed=false - download_link="$legacy_download_link" - zip_path="$(mktemp "$temporary_file_template")" - say_verbose "Legacy zip path: $zip_path" - say "Downloading legacy link: $download_link" - download "$download_link" "$zip_path" 2>&1 || download_failed=true - - if [ "$download_failed" = true ]; then - say "Cannot download: $download_link" - fi - fi - fi - - if [ "$download_failed" = true ]; then - say_err "Could not find/download: \`$asset_name\` with version = $specific_version" - say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support" - return 1 - fi - - say "Extracting zip from $download_link" - extract_dotnet_package "$zip_path" "$install_root" - - # Check if the SDK version is installed; if not, fail the installation. - # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. - if [[ $specific_version == *"rtm"* || $specific_version == *"servicing"* ]]; then - IFS='-' - read -ra verArr <<< "$specific_version" - release_version="${verArr[0]}" - unset IFS; - say_verbose "Checking installation: version = $release_version" - if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$release_version"; then - return 0 - fi - fi - - # Check if the standard SDK version is installed. - say_verbose "Checking installation: version = $specific_version" - if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then - return 0 - fi - - say_err "\`$asset_name\` with version = $specific_version failed to install with an unknown error." - return 1 -} - -args=("$@") - -local_version_file_relative_path="/.version" -bin_folder_relative_path="" -temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX" - -channel="LTS" -version="Latest" -json_file="" -install_dir="" -architecture="" -dry_run=false -no_path=false -no_cdn=false -azure_feed="https://dotnetcli.azureedge.net/dotnet" -uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet" -feed_credential="" -verbose=false -runtime="" -runtime_id="" -override_non_versioned_files=true -non_dynamic_parameters="" - -while [ $# -ne 0 ] -do - name="$1" - case "$name" in - -c|--channel|-[Cc]hannel) - shift - channel="$1" - ;; - -v|--version|-[Vv]ersion) - shift - version="$1" - ;; - -i|--install-dir|-[Ii]nstall[Dd]ir) - shift - install_dir="$1" - ;; - --arch|--architecture|-[Aa]rch|-[Aa]rchitecture) - shift - architecture="$1" - ;; - --shared-runtime|-[Ss]hared[Rr]untime) - say_warning "The --shared-runtime flag is obsolete and may be removed in a future version of this script. The recommended usage is to specify '--runtime dotnet'." - if [ -z "$runtime" ]; then - runtime="dotnet" - fi - ;; - --runtime|-[Rr]untime) - shift - runtime="$1" - if [[ "$runtime" != "dotnet" ]] && [[ "$runtime" != "aspnetcore" ]]; then - say_err "Unsupported value for --runtime: '$1'. Valid values are 'dotnet' and 'aspnetcore'." - if [[ "$runtime" == "windowsdesktop" ]]; then - say_err "WindowsDesktop archives are manufactured for Windows platforms only." - fi - exit 1 - fi - ;; - --dry-run|-[Dd]ry[Rr]un) - dry_run=true - ;; - --no-path|-[Nn]o[Pp]ath) - no_path=true - non_dynamic_parameters+=" $name" - ;; - --verbose|-[Vv]erbose) - verbose=true - non_dynamic_parameters+=" $name" - ;; - --no-cdn|-[Nn]o[Cc]dn) - no_cdn=true - non_dynamic_parameters+=" $name" - ;; - --azure-feed|-[Aa]zure[Ff]eed) - shift - azure_feed="$1" - non_dynamic_parameters+=" $name "\""$1"\""" - ;; - --uncached-feed|-[Uu]ncached[Ff]eed) - shift - uncached_feed="$1" - non_dynamic_parameters+=" $name "\""$1"\""" - ;; - --feed-credential|-[Ff]eed[Cc]redential) - shift - feed_credential="$1" - non_dynamic_parameters+=" $name "\""$1"\""" - ;; - --runtime-id|-[Rr]untime[Ii]d) - shift - runtime_id="$1" - non_dynamic_parameters+=" $name "\""$1"\""" - ;; - --jsonfile|-[Jj][Ss]on[Ff]ile) - shift - json_file="$1" - ;; - --skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles) - override_non_versioned_files=false - non_dynamic_parameters+=" $name" - ;; - -?|--?|-h|--help|-[Hh]elp) - script_name="$(basename "$0")" - echo ".NET Tools Installer" - echo "Usage: $script_name [-c|--channel ] [-v|--version ] [-p|--prefix ]" - echo " $script_name -h|-?|--help" - echo "" - echo "$script_name is a simple command line interface for obtaining dotnet cli." - echo "" - echo "Options:" - echo " -c,--channel Download from the channel specified, Defaults to \`$channel\`." - echo " -Channel" - echo " Possible values:" - echo " - Current - most current release" - echo " - LTS - most current supported release" - echo " - 2-part version in a format A.B - represents a specific release" - echo " examples: 2.0; 1.0" - echo " - Branch name" - echo " examples: release/2.0.0; Master" - echo " Note: The version parameter overrides the channel parameter." - echo " -v,--version Use specific VERSION, Defaults to \`$version\`." - echo " -Version" - echo " Possible values:" - echo " - latest - most latest build on specific channel" - echo " - coherent - most latest coherent build on specific channel" - echo " coherent applies only to SDK downloads" - echo " - 3-part version in a format A.B.C - represents specific version of build" - echo " examples: 2.0.0-preview2-006120; 1.1.0" - echo " -i,--install-dir Install under specified location (see Install Location below)" - echo " -InstallDir" - echo " --architecture Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`." - echo " --arch,-Architecture,-Arch" - echo " Possible values: x64, arm, and arm64" - echo " --runtime Installs a shared runtime only, without the SDK." - echo " -Runtime" - echo " Possible values:" - echo " - dotnet - the Microsoft.NETCore.App shared runtime" - echo " - aspnetcore - the Microsoft.AspNetCore.App shared runtime" - echo " --dry-run,-DryRun Do not perform installation. Display download link." - echo " --no-path, -NoPath Do not set PATH for the current process." - echo " --verbose,-Verbose Display diagnostics information." - echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user." - echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user." - echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified." - echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable." - echo " -SkipNonVersionedFiles" - echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly." - echo " --jsonfile Determines the SDK version from a user specified global.json file." - echo " Note: global.json must have a value for 'SDK:Version'" - echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)." - echo " -RuntimeId" - echo " -?,--?,-h,--help,-Help Shows this help message" - echo "" - echo "Obsolete parameters:" - echo " --shared-runtime The recommended alternative is '--runtime dotnet'." - echo " This parameter is obsolete and may be removed in a future version of this script." - echo " Installs just the shared runtime bits, not the entire SDK." - echo "" - echo "Install Location:" - echo " Location is chosen in following order:" - echo " - --install-dir option" - echo " - Environmental variable DOTNET_INSTALL_DIR" - echo " - $HOME/.dotnet" - exit 0 - ;; - *) - say_err "Unknown argument \`$name\`" - exit 1 - ;; - esac - - shift -done - -if [ "$no_cdn" = true ]; then - azure_feed="$uncached_feed" -fi - -check_min_reqs -calculate_vars -script_name=$(basename "$0") - -if [ "$dry_run" = true ]; then - say "Payload URLs:" - say "Primary named payload URL: $download_link" - if [ "$valid_legacy_download_link" = true ]; then - say "Legacy named payload URL: $legacy_download_link" - fi - repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\""" - if [[ "$runtime" == "dotnet" ]]; then - repeatable_command+=" --runtime "\""dotnet"\""" - elif [[ "$runtime" == "aspnetcore" ]]; then - repeatable_command+=" --runtime "\""aspnetcore"\""" - fi - repeatable_command+="$non_dynamic_parameters" - say "Repeatable invocation: $repeatable_command" - exit 0 -fi - -check_pre_reqs -install_dotnet - -bin_path="$(get_absolute_path "$(combine_paths "$install_root" "$bin_folder_relative_path")")" -if [ "$no_path" = false ]; then - say "Adding to current process PATH: \`$bin_path\`. Note: This change will be visible only when sourcing script." - export PATH="$bin_path":"$PATH" -else - say "Binaries of dotnet can be found in $bin_path" -fi - -say "Installation finished successfully." diff --git a/assets/install scripts/install-linux-prereqs.sh b/assets/install scripts/install-linux-prereqs.sh deleted file mode 100755 index 38ef73405..000000000 --- a/assets/install scripts/install-linux-prereqs.sh +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright © Microsoft Corporation -# All rights reserved. -# -# Licensed under the MIT License. See LICENSE-CODE in the project root for details. -# -# Exit codes: -# 0 - Success -# 1 - Unexpected failure -# 3 - Do not have permissions to run command -# 4 - Distribution not supported by script -# -# $1 - Distro to install -# $2 - String of additional dependencies to install -# $3 - Set to true to skip .NET Core dependency install entirely. -# Useful for secondary libraries and non-.NET Core scenarios. -# $4 - More info URL - -DISTRO=$1 -ADDITIONAL_DEPS=$2 -if [ "$3" = "true" ]; then SKIPDOTNETCORE=1; else SKIPDOTNETCORE=0; fi -MORE_INFO=$4 - -BASEDIR=$(dirname "$0") - -# Utility function for exiting -exitScript() -{ - echo -e "\nPress enter to dismiss this message" - read - exit $1 -} - -# Wrapper function to only use sudo if not already root -sudoIf() -{ - if [ "$(id -u)" -ne 0 ]; then - sudo $1 $2 - else - $1 $2 - fi -} - -# Utility function that waits for any existing installation operations to complete -# on Debian/Ubuntu based distributions and then calls apt-get -aptSudoIf() -{ - while sudoIf fuser /var/lib/dpkg/lock >/dev/null 2>&1; do - echo -ne "(*) Waiting for other package operations to complete.\r" - sleep 0.2 - echo -ne "(*) Waiting for other package operations to complete..\r" - sleep 0.2 - echo -ne "(*) Waiting for other package operations to complete...\r" - sleep 0.2 - echo -ne "\r\033[K" - done - sudoIf apt-get "$1" -} - -checkNetCoreDeps(){ - if [ $SKIPDOTNETCORE -eq 0 ]; then - # Install .NET Core dependencies - if ! "$1" "$2"; then - echo "(!) .NET Core dependency install failed!" - exitScript 1 - fi - fi -} - -checkAdditionalDeps(){ - if [ "$ADDITIONAL_DEPS" -ne "" ]; then - # Install additional dependencies - if ! "$1" "$2 $ADDITIONAL_DEPS"; then - echo "(!) Failed to install additional dependencies!" - exitScript 1 - fi - fi -} - - -cat << EOF - -Linux Dependency Installer - -One or more extensions installed requires a number of prerequisites that this script -will attempt to install for you. This process requires admin / root access. - -EOF - -# Disable history substitution given use of "!" or errors occur in certain distros -set H+ - -# Determine the distro if not passed in -if [ "$DISTRO" = "" ]; then - DISTRO=$(bash ${BASEDIR}/determine-linux-distro.sh || sh ${BASEDIR}/determine-linux-distro.sh) -fi - -# If not already root, validate user has sudo access and error if not. -if [ "$(id -u)" -ne 0 ]; then - -# Can't indent or text will be indented -cat << EOF -To begin the installation process, your OS will now ask you to enter your -admin / root (sudo) password. - -EOF - # Validate user actually can use sudo - if ! sudo -v > /dev/null 2>&1; then - -# Can't indent or text will be indented -cat << EOF - -(!) Dependency installation failed! You do not have the needed admin / root - access to install Live Share's dependencies. Contact your system admin - and ask them to install the required libraries described here: -EOF - echo $3 - exitScript 3 - else - echo "" - fi -fi - -#openSUSE - Has to be first since apt-get is available but package names different -if [ "$DISTRO" = "SUSE" ]; then - echo "(*) Detected SUSE (unoffically/community supported)" - installAdditionalDeps sudoIf "zypper -n in" - checkNetCoreDeps sudoIf "zypper -n in libopenssl1_0_0 libicu krb5 libz1" - -# Debian / Ubuntu -elif [ "$DISTRO" = "Debian" ]; then - echo "(*) Detected Debian / Ubuntu" - - # Get latest package data - echo -e "\n(*) Updating package lists..." - if ! aptSudoIf "update"; then - echo "(!) Failed to update list of available packages!" - exitScript 1 - fi - - installAdditionalDeps aptSudoIf "install -yq" - checkNetCoreDeps aptSudoIf "install -yq libicu[0-9][0-9] libkrb5-3 zlib1g $ADDITIONAL_DEPS" - if [ $SKIPDOTNETCORE -eq 0 ]; then - # Determine which version of libssl to install - # dpkg-query can return "1" in some distros if the package is not found. "2" is an unexpected error - LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1) - if [ $? -eq 2 ]; then - echo "(!) Failed see if libssl already installed!" - exitScript 1 - fi - if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then - # No libssl install 1.0.2 for Debian, 1.0.0 for Ubuntu - if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then - if ! aptSudoIf "install -yq libssl1.0.2"; then - echo "(!) libssl1.0.2 installation failed!" - exitScript 1 - fi - else - if ! aptSudoIf "install -yq libssl1.0.0"; then - echo "(!) libssl1.0.0 installation failed!" - exitScript 1 - fi - fi - else - echo "(*) libssl1.0.x already installed." - fi - fi - -#RHL/Fedora/CentOS -elif [ "$DISTRO" = "RedHat" ]; then - echo "(*) Detected RHL / Fedora / CentOS" - - # Update package repo indexes - returns 0 if no pacakges to upgrade, - # 100 if there are packages to upgrade, and 1 on error - echo -e "\n(*) Updating package lists..." - sudoIf "yum check-update" >/dev/null 2>&1 - if [ $? -eq 1 ]; then - echo "(!) Failed to update package list!" - exitScript 1 - fi - - installAdditionalDeps sudoIf "yum -y install" - checkNetCoreDeps sudoIf "yum -y install openssl-libs krb5-libs libicu zlib" - # Install openssl-compat10 for Fedora 29. Does not exist in - # CentOS, so validate package exists first. - if [ $SKIPDOTNETCORE -eq 0 ]; then - if ! sudoIf "yum -q list compat-openssl10" >/dev/null 2>&1; then - echo "(*) compat-openssl10 not required." - else - if ! sudoIf "yum -y install compat-openssl10"; then - echo "(!) compat-openssl10 install failed" - exitScript 1 - fi - fi - fi - -#ArchLinux -elif [ "$DISTRO" = "ArchLinux" ]; then - echo "(*) Detected Arch Linux (unoffically/community supported)" - installAdditionalDeps sudoIf "pacman -Sq --noconfirm --needed" - checkNetCoreDeps sudoIf "pacman -Sq --noconfirm --needed gcr liburcu openssl-1.0 krb5 icu zlib" - -#Solus -elif [ "$DISTRO" = "Solus" ]; then - echo "(*) Detected Solus (unoffically/community supported)" - installAdditionalDeps sudoIf "eopkg -y it" - checkNetCoreDeps sudoIf "eopkg -y it libicu openssl zlib kerberos" - -#Alpine Linux -elif [ "$DISTRO" = "Alpine" ]; then - echo "(*) Detected Alpine Linux" - - # Update package repo indexes - echo -e "\n(*) Updating and upgrading..." - if ! sudoIf "apk update --wait 30"; then - echo "(!) Failed to update package lists." - exitScript 1 - fi - # Upgrade to avoid package dependency conflicts - if ! sudoIf "apk upgrade"; then - echo "(!) Failed to upgrade." - exitScript 1 - fi - - installAdditionalDeps sudoIf "apk add --no-cache" - sudoIf "apk add --no-cache libssl1.0 icu krb5 zlib" - -# Unknown distro -else - echo -e "(!) We are unable to install dependencies for this version of Linux.\nSee $MORE_INFO for info on requirements." - exit 4 - # Don't pause on exit here - we'll handle this in the extension -fi - -echo -e "\n(*) Success!\n" -# Don't pause on exit here - we'll handle this in the extension \ No newline at end of file diff --git a/assets/install scripts/source.txt b/assets/install scripts/source.txt deleted file mode 100644 index c1844f417..000000000 --- a/assets/install scripts/source.txt +++ /dev/null @@ -1,5 +0,0 @@ -https://dot.net/v1/dotnet-install.sh -https://dot.net/v1/dotnet-install.ps1 - -See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script for more information. - diff --git a/gulpfile.ts b/gulpfile.ts index 2c33e9e5b..a3619a58a 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -3,24 +3,29 @@ * Licensed under the MIT License. See License.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// tslint:disable:no-unsafe-any no-console prefer-template +// tslint:disable:no-unsafe-any no-console prefer-template no-implicit-dependencies export-name import * as cp from 'child_process'; +import { File } from 'decompress'; import * as fse from 'fs-extra'; +import * as glob from 'glob'; import * as gulp from 'gulp'; import * as os from 'os'; import * as path from 'path'; import * as process from 'process'; -// tslint:disable-next-line:no-implicit-dependencies import * as recursiveReadDir from 'recursive-readdir'; import * as shelljs from 'shelljs'; -import { gulp_installAzureAccount, gulp_webpack } from 'vscode-azureextensiondev'; +import { Stream } from 'stream'; +import { gulp_webpack } from 'vscode-azureextensiondev'; import { dotnetVersion, languageServerFolderName } from './src/constants'; import { assert } from './src/fixed_assert'; import { getTempFilePath } from './test/support/getTempFilePath'; -// tslint:disable-next-line:no-require-imports +// tslint:disable:no-require-imports +import decompress = require('gulp-decompress'); +import download = require('gulp-download'); import rimraf = require('rimraf'); +// tslint:enable:no-require-imports const filesAndFoldersToPackage: string[] = [ 'dist', @@ -333,9 +338,34 @@ async function verifyTestsReferenceOnlyExtensionBundle(testFolder: string): Prom } } +export function gulp_installDotNetExtension(): Promise | Stream { + const extensionName = '.NET Install Tool for Extension Authors'; + console.log(`Installing ${extensionName}`); + const version: string = '0.1.0'; + const extensionPath: string = path.join(os.homedir(), `.vscode/extensions/ms-dotnettools.vscode-dotnet-runtime-${version}`); + console.log(extensionPath); + const existingExtensions: string[] = glob.sync(extensionPath.replace(version, '*')); + if (existingExtensions.length === 0) { + // tslint:disable-next-line:no-http-string + return download(`http://ms-vscode.gallery.vsassets.io/_apis/public/gallery/publisher/ms-dotnettools/extension/vscode-dotnet-runtime/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage`) + .pipe(decompress({ + filter: (file: File): boolean => file.path.startsWith('extension/'), + map: (file: File): File => { + file.path = file.path.slice(10); + return file; + } + })) + .pipe(gulp.dest(extensionPath)); + } else { + console.log(`${extensionName} already installed.`); + // We need to signal to gulp that we've completed this async task + return Promise.resolve(); + } +} + exports['webpack-dev'] = gulp.series(() => gulp_webpack('development'), buildGrammars); exports['webpack-prod'] = gulp.series(() => gulp_webpack('production'), buildGrammars); -exports.test = gulp.series(gulp_installAzureAccount, test); +exports.test = gulp.series(gulp_installDotNetExtension, test); exports['build-grammars'] = buildGrammars; exports['watch-grammars'] = (): unknown => gulp.watch('grammars/**', buildGrammars); exports['get-language-server'] = getLanguageServer; diff --git a/package-lock.json b/package-lock.json index d5af101ac..2ef40b328 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,15 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/decompress": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/decompress/-/decompress-4.2.3.tgz", + "integrity": "sha512-W24e3Ycz1UZPgr1ZEDHlK4XnvOr+CpJH3qNsFeqXwwlW/9END9gxn3oJSsp7gYdiQxrXUHwUUd3xuzVz37MrZQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -601,6 +610,23 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, + "archive-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz", + "integrity": "sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA=", + "dev": true, + "requires": { + "file-type": "^4.2.0" + }, + "dependencies": { + "file-type": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", + "integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU=", + "dev": true + } + } + }, "archiver": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", @@ -761,6 +787,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -1122,6 +1154,12 @@ "tweetnacl": "^0.14.3" } }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -1312,6 +1350,22 @@ "ieee754": "^1.1.4" } }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1329,6 +1383,12 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2152,6 +2212,12 @@ "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2173,6 +2239,136 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "dependencies": { + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "dev": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + } + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "dev": true, + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + } + } + }, "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", @@ -2368,6 +2564,41 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -2921,6 +3152,12 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -3804,6 +4041,16 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -3819,9 +4066,9 @@ } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4000,6 +4247,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -4046,6 +4299,103 @@ } } }, + "gulp-decompress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-2.0.3.tgz", + "integrity": "sha512-SfRzsVQPQthBQ/5e/9EXPODtoauRFk5lFKdTSW5FSt3MhiKqACAR5ivGK+GNjYxdxEvlU/xlJL+0oij4xET3aQ==", + "dev": true, + "requires": { + "archive-type": "^4.0.0", + "decompress": "^4.0.0", + "plugin-error": "^1.0.1", + "readable-stream": "^2.0.2", + "vinyl": "^2.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "gulp-download": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/gulp-download/-/gulp-download-0.0.1.tgz", + "integrity": "sha1-VKMBj8YZs0HM9kkWBvet8L08c5c=", + "dev": true, + "requires": { + "gulp-util": "^3.0.8", + "request": "^2.88.2", + "request-progress": "^3.0.0", + "through": "^2.3.8" + }, + "dependencies": { + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, "gulp-sourcemaps": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", @@ -4085,6 +4435,100 @@ } } }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, "gulplog": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", @@ -4108,12 +4552,30 @@ "har-schema": "^2.0.0" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", @@ -4492,6 +4954,12 @@ "is-extglob": "^2.1.1" } }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", + "dev": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -4895,6 +5363,60 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -4907,12 +5429,33 @@ "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", "dev": true }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", "dev": true }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -4924,6 +5467,50 @@ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, "lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", @@ -5522,6 +6109,15 @@ "uuid": "^3.2.1" } }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", @@ -6574,6 +7170,18 @@ } } }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6928,6 +7536,15 @@ "uuid": "^3.3.2" } }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7062,6 +7679,26 @@ "ajv-keywords": "^3.1.0" } }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "dev": true, + "requires": { + "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", @@ -7605,6 +8242,15 @@ "strip-bom": "^2.0.0" } }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "requires": { + "is-natural-number": "^4.0.1" + } + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -7728,6 +8374,12 @@ } } }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -7820,6 +8472,12 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -8044,6 +8702,16 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, + "unbzip2-stream": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz", + "integrity": "sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", diff --git a/package.json b/package.json index 331fd6001..d252924ce 100644 --- a/package.json +++ b/package.json @@ -36,14 +36,13 @@ "icon": "AzureRMTools128x128.png", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { - "vscode": "^1.31.0" + "vscode": "^1.41.0" }, "preview": true, "activationEvents": [ "onLanguage:arm-template", "onLanguage:json", "onLanguage:jsonc", - "onCommand:azurerm-vscode-tools.uninstallDotnet", "onCommand:azurerm-vscode-tools.sortTemplate", "onCommand:azurerm-vscode-tools.sortFunctions", "onCommand:azurerm-vscode-tools.sortOutputs", @@ -158,12 +157,6 @@ } ], "commands": [ - { - "$comment": "============= General commands =============", - "category": "Azure Resource Manager Tools", - "title": "Remove Local Dotnet Core Installation", - "command": "azurerm-vscode-tools.uninstallDotnet" - }, { "category": "Azure Resource Manager Tools", "title": "Reset Global State", @@ -373,7 +366,7 @@ "compile": "gulp build-grammars && npm run get-language-server && gulp verify-test-uses-extension-bundle && tsc -watch -p ./", "build": "gulp build-grammars && npm run get-language-server && gulp verify-test-uses-extension-bundle && tsc -p ./", "get-language-server": "gulp get-language-server", - "lint": "tslint --project tsconfig.json -t verbose", + "lint": "tslint --project tsconfig.json -e src/*.d.ts -t verbose", "lint-fix": "tslint --project tsconfig.json -t verbose --fix", "package": "npm run webpack-prod && npm run get-language-server && gulp package", "postinstall": "node ./node_modules/vscode/bin/install", @@ -389,6 +382,7 @@ "verify-test-uses-extension-bundle": "gulp verify-test-uses-extension-bundle" }, "devDependencies": { + "@types/decompress": "^4.2.3", "@types/fs-extra": "^5.1.0", "@types/gulp": "^4.0.6", "@types/mocha": "^2.2.41", @@ -397,8 +391,12 @@ "@types/recursive-readdir": "^2.2.0", "@types/rimraf": "2.0.2", "@types/shelljs": "0.8.3", - "@types/webpack": "^4.41.11", + "@types/webpack": "^4.41.5", + "decompress": "^4.2.0", + "glob": "^7.1.6", "gulp": "^4.0.2", + "gulp-decompress": "^2.0.2", + "gulp-download": "^0.0.1", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.23.3", "mocha-multi-reporters": "^1.1.7", @@ -423,5 +421,8 @@ "vscode-azureextensionui": "^0.29.12", "vscode-jsonrpc": "^4.0.0", "vscode-languageclient": "^4.4.0" - } + }, + "extensionDependencies": [ + "ms-dotnettools.vscode-dotnet-runtime" + ] } diff --git a/src/AzureRMTools.ts b/src/AzureRMTools.ts index be1604408..0f2729f59 100644 --- a/src/AzureRMTools.ts +++ b/src/AzureRMTools.ts @@ -10,9 +10,8 @@ import * as fse from 'fs-extra'; import * as path from 'path'; import * as vscode from "vscode"; import { AzureUserInput, callWithTelemetryAndErrorHandling, callWithTelemetryAndErrorHandlingSync, createAzExtOutputChannel, createTelemetryReporter, IActionContext, registerCommand, registerUIExtensionVariables, TelemetryProperties } from "vscode-azureextensionui"; -import { uninstallDotnet } from "./acquisition/dotnetAcquisition"; import * as Completion from "./Completion"; -import { armTemplateLanguageId, configKeys, configPrefix, expressionsDiagnosticsCompletionMessage, expressionsDiagnosticsSource, extensionName, globalStateKeys } from "./constants"; +import { armTemplateLanguageId, configKeys, configPrefix, expressionsDiagnosticsCompletionMessage, expressionsDiagnosticsSource, globalStateKeys, outputChannelName } from "./constants"; import { DeploymentDocument } from "./DeploymentDocument"; import { DeploymentTemplate } from "./DeploymentTemplate"; import { ext } from "./extensionVariables"; @@ -21,8 +20,7 @@ import * as Hover from './Hover'; import { IncorrectArgumentsCountIssue } from "./IncorrectArgumentsCountIssue"; import * as Json from "./JSON"; import * as language from "./Language"; -import { reloadSchemas } from "./languageclient/reloadSchemas"; -import { startArmLanguageServer, stopArmLanguageServer } from "./languageclient/startArmLanguageServer"; +import { startArmLanguageServer } from "./languageclient/startArmLanguageServer"; import { DeploymentFileMapping } from "./parameterFiles/DeploymentFileMapping"; import { DeploymentParameters } from "./parameterFiles/DeploymentParameters"; import { considerQueryingForParameterFile, getFriendlyPathToFile, openParameterFile, openTemplateFile, selectParameterFile } from "./parameterFiles/parameterFiles"; @@ -59,7 +57,7 @@ const invalidRenameError = "Only parameters, variables, user namespaces and user export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number; loadEndTime: number }): Promise { ext.context = context; ext.reporter = createTelemetryReporter(context); - ext.outputChannel = createAzExtOutputChannel(extensionName, configPrefix); + ext.outputChannel = createAzExtOutputChannel(outputChannelName, configPrefix); ext.ui = new AzureUserInput(context.globalState); context.subscriptions.push(ext.completionItemsSpy); @@ -116,13 +114,6 @@ export class AzureRMTools { onCompletionActivated(actionContext, <{ [key: string]: string }>args); }); registerCommand("azurerm-vscode-tools.treeview.goto", (_actionContext: IActionContext, range: vscode.Range) => jsonOutline.goToDefinition(range)); - registerCommand('azurerm-vscode-tools.uninstallDotnet', async () => { - await stopArmLanguageServer(); - await uninstallDotnet(); - }); - registerCommand("azurerm-vscode-tools.reloadSchemas", async () => { - await reloadSchemas(); - }); registerCommand("azurerm-vscode-tools.sortTemplate", async (_context: IActionContext, uri?: vscode.Uri, editor?: vscode.TextEditor) => { editor = editor || vscode.window.activeTextEditor; uri = uri || vscode.window.activeTextEditor?.document.uri; @@ -181,6 +172,7 @@ export class AzureRMTools { vscode.window.onDidChangeActiveTextEditor(this.onActiveTextEditorChanged, this, context.subscriptions); vscode.workspace.onDidOpenTextDocument(this.onDocumentOpened, this, context.subscriptions); vscode.workspace.onDidChangeTextDocument(this.onDocumentChanged, this, context.subscriptions); + vscode.workspace.onDidCloseTextDocument(this.onDocumentClosed, this, ext.context.subscriptions); vscode.workspace.onDidChangeConfiguration( async () => { this._mapping.resetCache(); @@ -676,102 +668,105 @@ export class AzureRMTools { } this._areDeploymentTemplateEventsHookedUp = true; - vscode.window.onDidChangeTextEditorSelection(this.onTextSelectionChanged, this, ext.context.subscriptions); - vscode.workspace.onDidCloseTextDocument(this.onDocumentClosed, this, ext.context.subscriptions); + callWithTelemetryAndErrorHandlingSync("ensureDeploymentTemplateEventsHookedUp", (actionContext: IActionContext) => { + actionContext.telemetry.suppressIfSuccessful = true; - const hoverProvider: vscode.HoverProvider = { - provideHover: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { - return await this.onProvideHover(document, position, token); - } - }; - ext.context.subscriptions.push(vscode.languages.registerHoverProvider(templateDocumentSelector, hoverProvider)); - - // Code actions provider - const codeActionProvider: vscode.CodeActionProvider = { - provideCodeActions: async ( - textDocument: vscode.TextDocument, - range: vscode.Range | vscode.Selection, - context: vscode.CodeActionContext, - token: vscode.CancellationToken - ): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => { - return await this.onProvideCodeActions(textDocument, range, context, token); - } - }; - ext.context.subscriptions.push( - vscode.languages.registerCodeActionsProvider( - templateOrParameterDocumentSelector, - codeActionProvider, - { - providedCodeActionKinds: [ - vscode.CodeActionKind.QuickFix - ] - } - )); + vscode.window.onDidChangeTextEditorSelection(this.onTextSelectionChanged, this, ext.context.subscriptions); - // tslint:disable-next-line:no-suspicious-comment - const completionProvider: vscode.CompletionItemProvider = { - provideCompletionItems: async ( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken, - context: vscode.CompletionContext - ): Promise => { - return await this.onProvideCompletions(document, position, token, context); - }, - resolveCompletionItem: (item: vscode.CompletionItem, token: vscode.CancellationToken): vscode.CompletionItem => { - return this.onResolveCompletionItem(item, token); - } - }; - ext.context.subscriptions.push( - vscode.languages.registerCompletionItemProvider( - templateOrParameterDocumentSelector, - completionProvider, - "'", - "[", - ".", - '"', - '(', - ',', - ' ' - )); + const hoverProvider: vscode.HoverProvider = { + provideHover: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { + return await this.onProvideHover(document, position, token); + } + }; + ext.context.subscriptions.push(vscode.languages.registerHoverProvider(templateDocumentSelector, hoverProvider)); + + // Code actions provider + const codeActionProvider: vscode.CodeActionProvider = { + provideCodeActions: async ( + textDocument: vscode.TextDocument, + range: vscode.Range | vscode.Selection, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => { + return await this.onProvideCodeActions(textDocument, range, context, token); + } + }; + ext.context.subscriptions.push( + vscode.languages.registerCodeActionsProvider( + templateOrParameterDocumentSelector, + codeActionProvider, + { + providedCodeActionKinds: [ + vscode.CodeActionKind.QuickFix + ] + } + )); - // tslint:disable-next-line:no-suspicious-comment - const definitionProvider: vscode.DefinitionProvider = { - provideDefinition: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { - return await this.onProvideDefinition(document, position, token); - } - }; - ext.context.subscriptions.push( - vscode.languages.registerDefinitionProvider( - templateOrParameterDocumentSelector, - definitionProvider)); - - const referenceProvider: vscode.ReferenceProvider = { - provideReferences: async (document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise => { - return this.onProvideReferences(document, position, context, token); - } - }; - ext.context.subscriptions.push(vscode.languages.registerReferenceProvider(templateOrParameterDocumentSelector, referenceProvider)); + // tslint:disable-next-line:no-suspicious-comment + const completionProvider: vscode.CompletionItemProvider = { + provideCompletionItems: async ( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context: vscode.CompletionContext + ): Promise => { + return await this.onProvideCompletions(document, position, token, context); + }, + resolveCompletionItem: (item: vscode.CompletionItem, token: vscode.CancellationToken): vscode.CompletionItem => { + return this.onResolveCompletionItem(item, token); + } + }; + ext.context.subscriptions.push( + vscode.languages.registerCompletionItemProvider( + templateOrParameterDocumentSelector, + completionProvider, + "'", + "[", + ".", + '"', + '(', + ',', + ' ' + )); - const signatureHelpProvider: vscode.SignatureHelpProvider = { - provideSignatureHelp: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { - return await this.onProvideSignatureHelp(document, position, token); - } - }; - ext.context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(templateDocumentSelector, signatureHelpProvider, ",", "(", "\n")); + // tslint:disable-next-line:no-suspicious-comment + const definitionProvider: vscode.DefinitionProvider = { + provideDefinition: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { + return await this.onProvideDefinition(document, position, token); + } + }; + ext.context.subscriptions.push( + vscode.languages.registerDefinitionProvider( + templateOrParameterDocumentSelector, + definitionProvider)); + + const referenceProvider: vscode.ReferenceProvider = { + provideReferences: async (document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise => { + return this.onProvideReferences(document, position, context, token); + } + }; + ext.context.subscriptions.push(vscode.languages.registerReferenceProvider(templateOrParameterDocumentSelector, referenceProvider)); - const renameProvider: vscode.RenameProvider = { - provideRenameEdits: async (document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise => { - return await this.onProvideRename(document, position, newName, token); - }, - prepareRename: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { - return await this.prepareRename(document, position, token); - } - }; - ext.context.subscriptions.push(vscode.languages.registerRenameProvider(templateOrParameterDocumentSelector, renameProvider)); + const signatureHelpProvider: vscode.SignatureHelpProvider = { + provideSignatureHelp: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { + return await this.onProvideSignatureHelp(document, position, token); + } + }; + ext.context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(templateDocumentSelector, signatureHelpProvider, ",", "(", "\n")); + + const renameProvider: vscode.RenameProvider = { + provideRenameEdits: async (document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise => { + return await this.onProvideRename(document, position, newName, token); + }, + prepareRename: async (document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise => { + return await this.prepareRename(document, position, token); + } + }; + ext.context.subscriptions.push(vscode.languages.registerRenameProvider(templateOrParameterDocumentSelector, renameProvider)); - // tslint:disable-next-line:no-floating-promises // Don't wait - startArmLanguageServer(); + // tslint:disable-next-line:no-floating-promises // Don't wait + startArmLanguageServer(); + }); } private async updateEditorState(): Promise { diff --git a/src/acquisition/DotnetCoreAcquisitionWorker.ts b/src/acquisition/DotnetCoreAcquisitionWorker.ts deleted file mode 100644 index 0fbe686c7..000000000 --- a/src/acquisition/DotnetCoreAcquisitionWorker.ts +++ /dev/null @@ -1,244 +0,0 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as fse from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import * as process from 'process'; -import { Memento } from 'vscode'; -import { EventStream } from './EventStream'; -import { DotnetAcquisitionCompleted, DotnetAcquisitionInstallError, DotnetAcquisitionMessage, DotnetAcquisitionScriptError, DotnetAcquisitionStarted, DotnetAcquisitionUnexpectedError } from './EventStreamEvents'; - -// tslint:disable-next-line:no-require-imports -import rimraf = require('rimraf'); - -export class DotnetCoreAcquisitionWorker { - private readonly installingVersionsKey: string = 'installing'; - private readonly installDir: string; - private readonly scriptPath: string; - private readonly dotnetExecutable: string; - - // tslint:disable-next-line: no-suspicious-comment - // TODO: Represent this in package.json OR utilize the channel argument in dotnet-install to dynamically acquire the - // latest for a specific channel. Concerns for using the dotnet-install channel mechanism: - // 1. Is the specified "latest" version available on the CDN yet? - // 2. Would need to build a mechanism to occasionally query latest so you don't pay the cost on every acquire. - private readonly latestVersionMap: { [version: string]: string | undefined } = { - '1.0': '1.0.16', - 1.1: '1.1.13', - '2.0': '2.0.9', - 2.1: '2.1.11', - 2.2: '2.2.5', - '3.0': '3.0.2' - }; - - private acquisitionPromises: { [version: string]: Promise | undefined }; - - constructor( - private readonly storagePath: string, - private readonly extensionState: Memento, - private readonly scriptsPath: string, - private readonly eventStream: EventStream - ) { - // tslint:disable-next-line: strict-boolean-expressions - const dotnetInstallFolderName: string = process.env.ARM_DOTNET_INSTALL_FOLDER || '.dotnet'; - - const scriptName = os.platform() === 'win32' ? 'dotnet-install.cmd' : 'dotnet-install.sh'; - this.scriptPath = path.join(this.scriptsPath, scriptName); - this.installDir = path.join(this.storagePath, dotnetInstallFolderName); - const dotnetExtension = os.platform() === 'win32' ? '.exe' : ''; - this.dotnetExecutable = `dotnet${dotnetExtension}`; - this.acquisitionPromises = {}; - } - - private removeFolderRecursively(folderPath: string, dotnetVersion: string): void { - this.eventStream.post(new DotnetAcquisitionMessage(dotnetVersion, `Removing folder ${folderPath}`)); - rimraf.sync(folderPath); - this.eventStream.post(new DotnetAcquisitionMessage(dotnetVersion, `Finished removing folder ${folderPath}`)); - } - - public async uninstallAll(): Promise { - this.acquisitionPromises = {}; - - this.removeFolderRecursively(this.installDir, "(all)"); - - await this.extensionState.update(this.installingVersionsKey, []); - } - - // tslint:disable-next-line: promise-function-async - public acquire(version: string, telemetryProperties: { [key: string]: string | undefined }): Promise { - telemetryProperties.requestedVersion = version; - - const resolvedVersion = this.latestVersionMap[version]; - if (resolvedVersion) { - version = resolvedVersion; - } - - telemetryProperties.resolvedVersion = version; - - const existingAcquisitionPromise = this.acquisitionPromises[version]; - telemetryProperties.acquisitionAlreadyInProcess = "false"; - if (existingAcquisitionPromise) { - // This version of dotnet is already being acquired. Memoize the promise. - - telemetryProperties.acquisitionAlreadyInProcess = "true"; - return existingAcquisitionPromise; - } else { - // We're the only one acquiring this version of dotnet, start the acquisition process. - - const acquisitionPromise = this.acquireCore(version, telemetryProperties).catch(error => { - delete this.acquisitionPromises[version]; - throw error; - }); - - this.acquisitionPromises[version] = acquisitionPromise; - return acquisitionPromise; - } - } - - private async acquireCore(version: string, telemetryProperties: { [key: string]: string | undefined }): Promise { - const dotnetInstallDir = this.getDotnetInstallDir(version); - const dotnetPath = path.join(dotnetInstallDir, this.dotnetExecutable); - const installingVersions = this.extensionState.get(this.installingVersionsKey, []); - - const partialInstall = installingVersions.indexOf(version) >= 0; - if (partialInstall) { - // Partial install, we never updated our extension to no longer be 'installing'. - // uninstall everything and then re-install. - telemetryProperties.partialInstallFound = "true"; - - this.eventStream.post(new DotnetAcquisitionMessage(version, `Found an incompletely-installed version of dotnet ${version}... Uninstalling.`)); - await this.uninstall(version); - } else { - telemetryProperties.partialInstallFound = "false"; - } - - if (fs.existsSync(dotnetPath)) { - // Version requested has already been installed. - telemetryProperties.alreadyInstalled = "true"; - return dotnetPath; - } else { - telemetryProperties.alreadyInstalled = "false"; - } - - // We update the extension state to indicate we're starting a .NET Core installation. - installingVersions.push(version); - await this.extensionState.update(this.installingVersionsKey, installingVersions); - - let dotnetInstallDirEscaped: string; - if (os.platform() === 'win32') { - // Need to escape apostrophes with two apostrophes - dotnetInstallDirEscaped = dotnetInstallDir.replace(/'/g, "''"); - - // Surround with single quotes instead of double quotes (see https://github.com/dotnet/cli/issues/11521) - dotnetInstallDirEscaped = `'${dotnetInstallDirEscaped}'`; - } else { - dotnetInstallDirEscaped = `"${dotnetInstallDir}"`; - } - - const args = [ - '-InstallDir', dotnetInstallDirEscaped, - '-Runtime', 'dotnet', // Installs just the shared runtime, not the entire SDK (the Microsoft.NETCore.App shared runtime) - '-Version', version, - ]; - - const installCommand = `"${this.scriptPath}" ${args.join(' ')}`; - - this.eventStream.post(new DotnetAcquisitionStarted(version, installCommand)); - await this.installDotnet(installCommand, version, dotnetPath); - - // Need to re-query our installing versions because there may have been concurrent acquisitions that - // changed its value. - const latestInstallingVersions = this.extensionState.get(this.installingVersionsKey, []); - const versionIndex = latestInstallingVersions.indexOf(version); - if (versionIndex >= 0) { - latestInstallingVersions.splice(versionIndex, 1); - await this.extensionState.update(this.installingVersionsKey, latestInstallingVersions); - } - - return dotnetPath; - } - - private async uninstall(version: string): Promise { - this.eventStream.post(new DotnetAcquisitionMessage(version, `Uninstalling dotnet version ${version}`)); - - delete this.acquisitionPromises[version]; - - const dotnetInstallDir = this.getDotnetInstallDir(version); - this.removeFolderRecursively(dotnetInstallDir, version); - - const installingVersions = this.extensionState.get(this.installingVersionsKey, []); - const versionIndex = installingVersions.indexOf(version); - if (versionIndex >= 0) { - installingVersions.splice(versionIndex, 1); - await this.extensionState.update(this.installingVersionsKey, installingVersions); - } - - this.eventStream.post(new DotnetAcquisitionMessage(version, `Finished uninstalling dotnet version ${version}`)); - } - - private getDotnetInstallDir(version: string): string { - const dotnetInstallDir = path.join(this.installDir, version); - return dotnetInstallDir; - } - - // tslint:disable-next-line: promise-function-async - private async installDotnet(installCommand: string, version: string, dotnetPath: string): Promise { - await new Promise((resolve, reject): void => { - try { - cp.exec(installCommand, { cwd: process.cwd(), maxBuffer: 500 * 1024 }, (error, stdout, stderr) => { - if (stdout) { - this.eventStream.post(new DotnetAcquisitionMessage(version, stdout)); - } - if (stderr) { - this.eventStream.post(new DotnetAcquisitionMessage(version, `STDERR: ${stderr}`)); - } - - if (error) { - this.eventStream.post(new DotnetAcquisitionInstallError(error, version)); - reject(error); - } else if (stderr && stderr.length > 0) { - this.eventStream.post(new DotnetAcquisitionScriptError(stderr, version)); - reject(stderr); - } else { - this.eventStream.post(new DotnetAcquisitionCompleted(version, dotnetPath)); - resolve(); - } - }); - } catch (error) { - this.eventStream.post(new DotnetAcquisitionUnexpectedError(error, version)); - reject(error); - } - }); - - await this.validateDotnetInstall(version, dotnetPath); - } - - private async validateDotnetInstall(version: string, dotnetPath: string): Promise { - const dotnetValidationFailed = `Validation of .dotnet installation for version ${version} failed:`; - - this.eventStream.post(new DotnetAcquisitionMessage(version, `Validating dotnet installation at ${dotnetPath}`)); - - const folder = path.dirname(dotnetPath); - const folderExists = await fse.pathExists(folder); - if (!folderExists) { - throw new Error(`${dotnetValidationFailed} Expected installation folder ${folder} does not exist.`); - } - - const fileExists = await fse.pathExists(dotnetPath); - if (!fileExists) { - throw new Error(`${dotnetValidationFailed} Expected executable does not exist at "${dotnetPath}"`); - } - - const stat = await fse.stat(dotnetPath); - if (!stat.isFile()) { - throw new Error(`${dotnetValidationFailed} Expected executable file exists but is not a file: "${dotnetPath}"`); - } - - this.eventStream.post(new DotnetAcquisitionMessage(version, `Validation succeeded.`)); - } -} diff --git a/src/acquisition/DotnetCoreDependencyInstaller.ts b/src/acquisition/DotnetCoreDependencyInstaller.ts deleted file mode 100644 index 0b4bf9626..000000000 --- a/src/acquisition/DotnetCoreDependencyInstaller.ts +++ /dev/null @@ -1,200 +0,0 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as path from 'path'; -import { which } from 'shelljs'; -import * as vscode from 'vscode'; - -// tslint:disable: prefer-template - -const moreInfoUrl = 'https://aka.ms/dotnet-linux-prereqs'; - -export class DotnetCoreDependencyInstaller { - private readonly platform: NodeJS.Platform = process.platform; - - public constructor(private _outputChannel: vscode.OutputChannel) { - - } - - public signalIndicatesMissingLinuxDependencies(signal: string | undefined | null): boolean { - // If the dotnet does a SIGABRT on Linux this can mean that there are missing dependencies. - // In fact, many signals can actually be an indicator of missing libraries on Linux as we have seen - // SIGSEGV frequently as well. Checking the error stream is not accurate enough as on openSUSE15 - // we get a SIGABRT with no standard error output if libssl1_0_0 is missing (which it is vanilla). - if (!!signal && this.platform === 'linux' && - ['SIGABRT', - 'SIGIOT', - 'SIGSYS', - 'SIGPIPE', - 'SIGSEGV', - 'SIGILL', - 'SIGBUS', - 'SIGPFE', - 'SIGSYS'].includes(signal)) { - return true; - } - return false; - } - - public async installLinuxDependencies(additionalLibs: { [key: string]: string } = {}, skipDotNetCore: boolean = false): Promise { - const scriptRoot = path.join(__dirname, '..', 'install scripts'); - const shellCommand = this.getShellCommand(); - - // Determine the distro - const result = cp.spawnSync(shellCommand, [path.join(scriptRoot, 'determine-linux-distro.sh')]); - // tslint:disable-next-line: strict-boolean-expressions - if (!!result.status) { - // tslint:disable-next-line: restrict-plus-operands - this._outputChannel.appendLine('Failed to determine distro. Exit code: ' + result.status); - return result.status; - } - const distro = result.stdout.toString().trim(); - this._outputChannel.appendLine('Found distro ' + distro); - const additionalLibsKey = distro.toLowerCase(); // Always use lower case for this - - // Run the installer for the distro passing in any additional libs for it - return await this.executeCommandInTerminal( - 'Linux dependency installer (.NET Core)', - shellCommand, - [path.join(scriptRoot, 'install-linux-prereqs.sh'), - distro, - (additionalLibs[additionalLibsKey] ? `"${additionalLibs[additionalLibsKey]}"` : ''), - skipDotNetCore.toString(), - moreInfoUrl]); - } - - public async promptLinuxDependencyInstall(telemetryProperties: { [key: string]: string | undefined }, failureMessage: string, additionalLibs: { [key: string]: string } = {}, skipDotNetCore: boolean = false): Promise { - // tslint:disable-next-line:no-constant-condition - while (true) { - const response = await vscode.window.showErrorMessage( - failureMessage + ' You may be missing key Linux libraries. Install them now?', - 'More Info', - 'Install', - 'Cancel'); - // tslint:disable-next-line: strict-boolean-expressions - telemetryProperties.response = response || ''; - - if (response === 'More Info') { - // Don't return, user has to explicitly hit "cancel" - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(moreInfoUrl)); - } else if (response === 'Install') { - const exitCode = await this.installLinuxDependencies(additionalLibs, skipDotNetCore); - - if (exitCode !== 0) { - const msg = (exitCode === 4 ? - 'Your Linux distribution is not supported by the automated installer' : - 'The dependency installer failed.'); - // Terminal will pause for input on error so this is just an info message with a more info button - const failResponse = await vscode.window.showErrorMessage( - msg + ' Try installing dependencies manually.', - 'More Info'); - if (failResponse === 'More Info') { - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(moreInfoUrl)); - } - return false; - } - - const reloadResponse = await vscode.window.showInformationMessage( - 'The dependency installer has completed successfully. Reload to activate your extensions!', - 'Reload Now'); - if (reloadResponse === 'Reload Now') { - await vscode.commands.executeCommand('workbench.action.reloadWindow'); - } - return true; - } else { - // response === 'Cancel' - return false; - } - - } - } - - private getShellCommand(): string { - if (this.platform === 'win32') { - return which('cmd').toString(); - } - // Test for existence of bash which won't exist on the base Alpine Linux container, use sh instead there - const shellCommand = which('bash'); - // shellCommand will be null if bash is not found - // tslint:disable-next-line: strict-boolean-expressions - return shellCommand ? shellCommand.toString() : which('sh').toString(); - } - - public async executeCommandInTerminal(name: string, command: string, args: string[] = [], promptAfterRun: boolean = false): Promise { - // tslint:disable-next-line:typedef - return await new Promise((resolve, reject) => { - const fullCommand = `"${command}" ${(args.length > 0 ? ' "' + args.join('" "') + '"' : '')}`; - // tslint:disable-next-line: restrict-plus-operands insecure-random - const exitCodeFile = path.join(__dirname, '..', 'terminal-exit-code-' + Math.floor(Math.random() * 1000000)); - // tslint:disable-next-line:prefer-array-literal - const commandList = new Array(); - if (this.platform === 'win32') { - // Note that "|| echo %ERRORLEVEL% >"" in this command sequence is a hack to get the exit code - // from the executed command given VS Code terminal does not return it. - commandList.push( - 'cls', - `echo 0 > "${exitCodeFile}"`, - `${fullCommand} || echo %ERRORLEVEL% > "${exitCodeFile}"` - ); - if (promptAfterRun) { - commandList.push('pause'); - } - } else { - // Note that "|| echo $? >" in this command sequence is a hack to get the exit code from the - // executed command given VS Code terminal does not return it. - commandList.push( - 'clear', - `echo 0 > "${exitCodeFile}"`, - `${fullCommand} || echo $? > "${exitCodeFile}"` - ); - if (promptAfterRun) { - commandList.push( - 'echo "Press enter to close the terminal window."', - 'sync', - 'read' - ); - } - } - commandList.push('exit 0'); - const commandSequence = commandList.join(' && '); - // Use a long name to reduce the chances of overlap with other extension installers - const terminal = vscode.window.createTerminal(name); - terminal.sendText(commandSequence, true); - terminal.show(false); - // If the scripts executes successfully, reload - vscode.window.onDidCloseTerminal((terminalEvent) => { - if (terminalEvent.name === terminal.name) { - // Hack to get exit code - VS Code terminal does not return it - try { - if (fs.existsSync(exitCodeFile)) { - const exitFile = fs.readFileSync(exitCodeFile).toString().trim(); - fs.unlinkSync(exitCodeFile); // Delete file since we don't need it anymore - if (exitFile) { - const exitCode = parseInt(exitFile, 10); - if (exitCode === 0) { - // NOOP - } else { - // Possible that this is an expected exit code, so just a warning - this._outputChannel.appendLine(`Non-zero exit code detected: ${exitCode}`); - } - resolve(exitCode); - } else { - const message = `Terminal "${name}" run resulted in empty exit file. Command likely did not execute successfully.`; - reject(new Error(message)); - } - } else { - const message = `Terminal "${name}" did not generate an exit file. Command likely did not execute successfully.`; - reject(new Error(message)); - } - } catch (err) { - reject(err); - } - } - }); - }); - } -} diff --git a/src/acquisition/EventStream.ts b/src/acquisition/EventStream.ts deleted file mode 100644 index 73e8b8027..000000000 --- a/src/acquisition/EventStream.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { IEvent } from './IEvent'; - -export class EventStream { - private readonly subscribeEmitter: vscode.EventEmitter; - - constructor() { - this.subscribeEmitter = new vscode.EventEmitter(); - } - - public post(event: IEvent): void { - this.subscribeEmitter.fire(event); - } - - public get subscribe(): vscode.Event { return this.subscribeEmitter.event; } -} diff --git a/src/acquisition/EventStreamEvents.ts b/src/acquisition/EventStreamEvents.ts deleted file mode 100644 index 3e77f1492..000000000 --- a/src/acquisition/EventStreamEvents.ts +++ /dev/null @@ -1,81 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExecException } from 'child_process'; -import { EventType } from './EventType'; -import { IEvent } from './IEvent'; - -// tslint:disable max-classes-per-file - -export class DotnetAcquisitionStarted implements IEvent { - public readonly eventType: EventType = EventType.DotnetAcquisitionStart; - - constructor(public readonly version: string, public readonly installCommand: string) { - } -} - -export abstract class DotnetAcquisitionError implements IEvent { - public readonly eventType: EventType = EventType.DotnetAcquisitionError; - - constructor(public readonly version: string) { - } - - public abstract getErrorMessage(): string; -} - -export class DotnetAcquisitionUnexpectedError extends DotnetAcquisitionError { - constructor(private readonly error: unknown, version: string) { - super(version); - } - - public getErrorMessage(): string { - // tslint:disable-next-line: strict-boolean-expressions - if (this.error) { - let error = <{ toString?(): string }>this.error; - return !!error.toString ? error.toString() : String(this.error); - } - - return ''; - } -} - -export class DotnetAcquisitionInstallError extends DotnetAcquisitionError { - constructor(private readonly error: ExecException, version: string) { - super(version); - } - - public getErrorMessage(): string { - return `Exit code: ${this.error.code} -Message: ${this.error.message}`; - } -} - -export class DotnetAcquisitionScriptError extends DotnetAcquisitionError { - constructor(private readonly error: string, version: string) { - super(version); - } - - public getErrorMessage(): string { - return this.error; - } -} - -export class DotnetAcquisitionCompleted implements IEvent { - public readonly eventType: EventType = EventType.DotnetAcquisitionCompleted; - - constructor(public readonly version: string, public readonly dotnetPath: string) { - } -} - -export class DotnetAcquisitionMessage implements IEvent { - public readonly eventType: EventType = EventType.DotnetAcquisitionMessage; - - constructor(public readonly version: string, private readonly message: string) { - } - - public getMessage(): string { - return `[dotnet ${this.version}]: ${this.message}`; - } -} diff --git a/src/acquisition/EventType.ts b/src/acquisition/EventType.ts deleted file mode 100644 index 585da6853..000000000 --- a/src/acquisition/EventType.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export enum EventType { - DotnetAcquisitionStart, - DotnetAcquisitionError, - DotnetAcquisitionCompleted, - DotnetAcquisitionMessage, -} diff --git a/src/acquisition/IEvent.ts b/src/acquisition/IEvent.ts deleted file mode 100644 index 5ab3f2927..000000000 --- a/src/acquisition/IEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import { EventType } from './EventType'; - -export interface IEvent { - eventType: EventType; -} diff --git a/src/acquisition/OutputChannelObserver.ts b/src/acquisition/OutputChannelObserver.ts deleted file mode 100644 index 131f7d09e..000000000 --- a/src/acquisition/OutputChannelObserver.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as os from 'os'; -import * as vscode from 'vscode'; -import { DotnetAcquisitionCompleted, DotnetAcquisitionError, DotnetAcquisitionMessage, DotnetAcquisitionStarted } from './EventStreamEvents'; -import { EventType } from './EventType'; -import { IEvent } from './IEvent'; -import { IEventStreamObserver } from './IEventStreamObserver'; - -export class OutputChannelObserver implements IEventStreamObserver { - private readonly inProgressDownloads: string[] = []; - private downloadProgressInterval: NodeJS.Timeout | undefined; - private _log: string = ""; - - constructor(private readonly outputChannel: vscode.OutputChannel) { - } - - public get log(): string { - return this._log; - } - - private append(message: string): void { - this.outputChannel.append(message); - this._log += message; - } - - private appendLine(message: string): void { - this.outputChannel.appendLine(message); - this._log += `${message}\n`; // Use OS-independent newline - } - - public post(event: IEvent): void { - switch (event.eventType) { - case EventType.DotnetAcquisitionStart: - { - const acquisitionStarted = event as DotnetAcquisitionStarted; - - this.inProgressDownloads.push(acquisitionStarted.version); - - if (this.inProgressDownloads.length > 1) { - // Already a download in progress - this.appendLine(` -- Concurrent download of '${acquisitionStarted.version}' started!`); - this.appendLine(''); - } else { - this.outputChannel.show(); - this.startDownloadIndicator(); - } - - const versionString = this.inProgressDownloads.join(', '); - this.appendLine(`Using this command to install .NET Core version ${acquisitionStarted.version}:${os.EOL}${acquisitionStarted.installCommand}`); - this.append(`Downloading .NET Core tooling version(s) ${versionString}...`); - } - break; - case EventType.DotnetAcquisitionCompleted: - { - const acquisitionCompleted = event as DotnetAcquisitionCompleted; - this.appendLine(' Done!'); - this.appendLine(`.NET Core ${acquisitionCompleted.version} executable path: ${acquisitionCompleted.dotnetPath}`); - this.appendLine(''); - - this.inProgressVersionDone(acquisitionCompleted.version); - - if (this.inProgressDownloads.length > 0) { - const versionString = `'${this.inProgressDownloads.join('\', \'')}'`; - this.append(`Still downloading .NET Core tooling version(s) ${versionString} ...`); - } else { - this.stopDownloadIndicator(); - } - } - break; - case EventType.DotnetAcquisitionError: - { - const error = event as DotnetAcquisitionError; - this.appendLine(' Error!'); - this.appendLine(`Failed to download .NET Core tooling ${error.version}:`); - this.appendLine(error.getErrorMessage()); - this.appendLine(''); - - this.inProgressVersionDone(error.version); - - if (this.inProgressDownloads.length > 0) { - const versionString = this.inProgressDownloads.join(', '); - this.append(`Still downloading .NET Core tooling version(s) ${versionString} ...`); - } else { - this.stopDownloadIndicator(); - } - } - break; - case EventType.DotnetAcquisitionMessage: - { - const msg = event as DotnetAcquisitionMessage; - this.appendLine(msg.getMessage()); - } - break; - default: - console.error(`Unexpected event type ${event.eventType}`); - break; - } - } - - private startDownloadIndicator(): void { - this.downloadProgressInterval = setInterval(() => this.append('.'), 1000); - } - - private stopDownloadIndicator(): void { - if (this.downloadProgressInterval) { - clearTimeout(this.downloadProgressInterval); - this.downloadProgressInterval = undefined; - } - } - - private inProgressVersionDone(version: string): void { - const index = this.inProgressDownloads.indexOf(version); - if (index >= 0) { - this.inProgressDownloads.splice(index, 1); - } - } -} diff --git a/src/acquisition/StatusBarObserver.ts b/src/acquisition/StatusBarObserver.ts deleted file mode 100644 index 5c88e9b96..000000000 --- a/src/acquisition/StatusBarObserver.ts +++ /dev/null @@ -1,63 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { EventType } from './EventType'; -import { IEvent } from './IEvent'; -import { IEventStreamObserver } from './IEventStreamObserver'; - -enum StatusBarColors { - Red = 'rgb(218,0,0)', - Green = 'rgb(0,218,0)', -} - -export class StatusBarObserver implements IEventStreamObserver { - private _log: string = ""; - - constructor(private readonly statusBarItem: vscode.StatusBarItem) { - } - - public get log(): string { - return this._log; - } - - public post(event: IEvent): void { - switch (event.eventType) { - case EventType.DotnetAcquisitionStart: - this.setAndShowStatusBar('$(cloud-download) Downloading .NET Core tooling...', 'dotnet.showAcquisitionLog', '', 'Downloading .NET Core tooling...'); - break; - case EventType.DotnetAcquisitionCompleted: - this.resetAndHideStatusBar(); - break; - case EventType.DotnetAcquisitionError: - this.setAndShowStatusBar('$(alert) Error acquiring .NET Core tooling!', 'dotnet.showAcquisitionLog', StatusBarColors.Red, 'Error acquiring .NET Core tooling'); - break; - case EventType.DotnetAcquisitionMessage: - break; - default: - // tslint:disable-next-line: no-console - console.log(`StatusBarObserver.post: Unexpected event type ${event.eventType}`); - break; - } - } - - public setAndShowStatusBar(text: string, command: string, color?: string, tooltip?: string): void { - this.statusBarItem.text = text; - this.statusBarItem.command = command; - this.statusBarItem.color = color; - this.statusBarItem.tooltip = tooltip; - this.statusBarItem.show(); - - this._log += `Status bar: ${text}\n`; // Use OS-independent newline - } - - public resetAndHideStatusBar(): void { - this.statusBarItem.text = ''; - this.statusBarItem.command = undefined; - this.statusBarItem.color = undefined; - this.statusBarItem.tooltip = undefined; - this.statusBarItem.hide(); - } -} diff --git a/src/acquisition/acquireSharedDotnetInstallation.ts b/src/acquisition/acquireSharedDotnetInstallation.ts new file mode 100644 index 000000000..3b774ddae --- /dev/null +++ b/src/acquisition/acquireSharedDotnetInstallation.ts @@ -0,0 +1,53 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { commands } from 'vscode'; +import { callWithTelemetryAndErrorHandling, IActionContext, parseError } from 'vscode-azureextensionui'; +import { ext } from '../extensionVariables'; +import { wrapError } from '../util/wrapError'; + +interface IDotnetAcquireResult { + dotnetPath: string; +} + +// Returns undefined if acquisition fails. +export async function acquireSharedDotnetInstallation(version: string): Promise { + return await callWithTelemetryAndErrorHandling('acquireSharedDotnet', async (actionContext: IActionContext) => { + // If this fails, the dotnet.acquire extension should display its own error, so no need to do + // it here, other than to our output channel. + actionContext.errorHandling.suppressDisplay = true; + + let message: string | undefined; + let result: IDotnetAcquireResult | undefined; + let dotnetPath: string | undefined; + + try { + result = await commands.executeCommand('dotnet.acquire', { version }); + } catch (err) { + message = parseError(err).message; + } + if (!message) { + if (!result) { + message = "dotnet.acquire returned undefined"; + } else { + dotnetPath = result.dotnetPath; + if (!dotnetPath) { + message = "dotnet.acquire returned undefined"; + } + } + } + + if (message) { + const linkMessage = `This extension requires .NET Core for full functionality, but we were unable to download and install a local copy for the extension. If this error persists, please see https://aka.ms/vscode-armtools-dotnet for troubleshooting tips.`; + const err = wrapError(linkMessage, `Details: ${message}`); + ext.outputChannel.appendLog(parseError(err).message); + ext.outputChannel.appendLog(`See '.NET Core Tooling' in the output window for more information.`); + ext.outputChannel.show(); + throw err; + } + + return dotnetPath; + }); +} diff --git a/src/acquisition/dotnetAcquisition.ts b/src/acquisition/dotnetAcquisition.ts deleted file mode 100644 index e4d028e2b..000000000 --- a/src/acquisition/dotnetAcquisition.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import { parseError } from 'vscode-azureextensionui'; -import { assetsPath } from '../constants'; -import { ext } from '../extensionVariables'; -import { wrapError } from '../util/wrapError'; -import { DotnetCoreAcquisitionWorker } from './DotnetCoreAcquisitionWorker'; -import { DotnetCoreDependencyInstaller } from './DotnetCoreDependencyInstaller'; -import { EventStream } from './EventStream'; -import { IEventStreamObserver } from './IEventStreamObserver'; -import { OutputChannelObserver } from './OutputChannelObserver'; -import { StatusBarObserver } from './StatusBarObserver'; - -let acquisitionWorker: DotnetCoreAcquisitionWorker; -let initialized = false; -const eventStreamObservers: IEventStreamObserver[] = []; - -function initializeDotnetAcquire(): void { - if (initialized) { - return; - } - initialized = true; - - let context = ext.context; - let parentExtensionId = ext.extensionId; - let scriptsPath = path.join(assetsPath, 'install scripts'); - - const extension = vscode.extensions.getExtension(parentExtensionId); - - if (!extension) { - throw new Error(`Could not resolve dotnet acquisition extension '${parentExtensionId}' location`); - } - - eventStreamObservers.push(new StatusBarObserver(vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, Number.MIN_VALUE))); - eventStreamObservers.push(new OutputChannelObserver(ext.outputChannel)); - const eventStream = new EventStream(); - - for (const observer of eventStreamObservers) { - eventStream.subscribe(event => observer.post(event)); - } - - // tslint:disable-next-line: non-literal-fs-path - if (!fs.existsSync(ext.context.globalStoragePath)) { - // tslint:disable-next-line: non-literal-fs-path - fs.mkdirSync(context.globalStoragePath); - } - acquisitionWorker = new DotnetCoreAcquisitionWorker( - context.globalStoragePath, - context.globalState, - scriptsPath, - eventStream); -} - -function getStreamLogs(): string { - return eventStreamObservers.map(observer => observer.log).filter(log => !!log).join('\n'); // Use OS-independent newline -} - -export async function dotnetAcquire( - version: string, - telemetryProperties: { [key: string]: string | undefined }, - issueProperties: { [key: string]: string | undefined } -): Promise { - try { - initializeDotnetAcquire(); - - if (!version || version === 'latest') { - throw new Error(`Cannot acquire .NET Core version "${version}". Please provide a valid version.`); - } - return await acquisitionWorker.acquire(version, telemetryProperties); - } catch (err) { - const linkMessage = `>>>> This extension requires .NET Core for full functionality, but we were unable to download and install a local copy for the extension. If this error persists, please see https://aka.ms/vscode-armtools-dotnet for troubleshooting tips. `; - err = wrapError(linkMessage, err); - throw err; - } finally { - issueProperties.dotnetAcquireLog = getStreamLogs(); - } -} - -export async function ensureDotnetDependencies( - dotnetPath: string, args: string[], - telemetryProperties: { [key: string]: string | undefined }, - issueProperties: { [key: string]: string | undefined } -): Promise { - try { - initializeDotnetAcquire(); - - if (os.platform() !== 'linux') { - // We can't handle installing dependencies for anything other than Linux - telemetryProperties.skipped = "true"; - } - - const result = cp.spawnSync(dotnetPath, args); - const installer = new DotnetCoreDependencyInstaller(ext.outputChannel); - if (installer.signalIndicatesMissingLinuxDependencies(result.signal)) { - telemetryProperties.signalIndicatesMissing = "true"; - await installer.promptLinuxDependencyInstall(telemetryProperties, 'Failed to successfully run the language server.'); - } else { - telemetryProperties.signalIndicatesMissing = "false"; - } - } finally { - issueProperties.dotnetAcquireLog = getStreamLogs(); - } -} - -export async function uninstallDotnet(): Promise { - initializeDotnetAcquire(); - - ext.outputChannel.show(); - ext.outputChannel.appendLine("Removing local installation of dotnet core..."); - - try { - await acquisitionWorker.uninstallAll(); - } catch (error) { - let message = parseError(error).message; - if (message.includes('EPERM')) { - error = new Error(`Dotnet core may be in use. Please close all deployment template files, then restart VS Code and try again. ${message}`); - } - - throw error; - } - - ext.outputChannel.appendLine("Done. Please restart VS Code."); -} diff --git a/src/constants.ts b/src/constants.ts index 498316747..a75296468 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -21,7 +21,7 @@ export const languageFriendlyName = 'Azure Resource Manager Template'; export const armTemplateLanguageId = 'arm-template'; export const languageServerFolderName = 'languageServer'; export const extensionName = 'Azure Resource Manager Tools'; -export const outputWindowName = extensionName; +export const outputChannelName = extensionName; // String that shows up in our errors as the source in parentheses export const expressionsDiagnosticsSource = "arm-template (expressions)"; diff --git a/src/languageclient/startArmLanguageServer.ts b/src/languageclient/startArmLanguageServer.ts index fd4a63b5c..bfadf19b9 100644 --- a/src/languageclient/startArmLanguageServer.ts +++ b/src/languageclient/startArmLanguageServer.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import { ProgressLocation, window, workspace } from 'vscode'; import { callWithTelemetryAndErrorHandling, callWithTelemetryAndErrorHandlingSync, IActionContext, parseError } from 'vscode-azureextensionui'; import { LanguageClient, LanguageClientOptions, RevealOutputChannelOn, ServerOptions } from 'vscode-languageclient'; -import { dotnetAcquire, ensureDotnetDependencies } from '../acquisition/dotnetAcquisition'; +import { acquireSharedDotnetInstallation } from '../acquisition/acquireSharedDotnetInstallation'; import { armTemplateLanguageId, configKeys, configPrefix, dotnetVersion, languageFriendlyName, languageServerFolderName, languageServerName } from '../constants'; import { ext } from '../extensionVariables'; import { assert } from '../fixed_assert'; @@ -26,6 +26,7 @@ export enum LanguageServerState { Started, Stopped, } + export async function stopArmLanguageServer(): Promise { ext.outputChannel.appendLine(`Stopping ${languageServerName}...`); // Work-around for https://github.com/microsoft/vscode/issues/83254 - store languageServerState global via ext to keep it a singleton @@ -50,8 +51,13 @@ export async function startArmLanguageServer(): Promise { try { // The server is implemented in .NET Core. We run it by calling 'dotnet' with the dll as an argument let serverDllPath: string = findLanguageServer(); - let dotnetExePath: string = await acquireDotnet(serverDllPath); - await ensureDependencies(dotnetExePath, serverDllPath); + let dotnetExePath: string | undefined = await getDotNetPath(); + if (!dotnetExePath) { + // Acquisition failed + ext.languageServerState = LanguageServerState.Failed; + return; + } + await startLanguageClient(serverDllPath, dotnetExePath); ext.languageServerState = LanguageServerState.Started; @@ -166,30 +172,36 @@ export async function startLanguageClient(serverDllPath: string, dotnetExePath: }); } -async function acquireDotnet(dotnetExePath: string): Promise { - const resultPath = await callWithTelemetryAndErrorHandling('acquireDotnet', async (actionContext: IActionContext) => { +async function getDotNetPath(): Promise { + return await callWithTelemetryAndErrorHandling("getDotNetPath", async (actionContext: IActionContext) => { actionContext.errorHandling.rethrow = true; + let dotnetPath: string | undefined; + const overriddenDotNetExePath = workspace.getConfiguration(configPrefix).get(configKeys.dotnetExePath); if (typeof overriddenDotNetExePath === "string" && !!overriddenDotNetExePath) { if (!(await isFile(overriddenDotNetExePath))) { throw new Error(`Invalid path given for ${configPrefix}.${configKeys.dotnetExePath} setting. Must point to dotnet executable. Could not find file ${overriddenDotNetExePath}`); } - dotnetExePath = overriddenDotNetExePath; + dotnetPath = overriddenDotNetExePath; actionContext.telemetry.properties.overriddenDotNetExePath = "true"; } else { actionContext.telemetry.properties.overriddenDotNetExePath = "false"; - ext.outputChannel.appendLine(`This extension requires .NET Core for full functionality.`); - dotnetExePath = await dotnetAcquire(dotnetVersion, actionContext.telemetry.properties, actionContext.errorHandling.issueProperties); - if (!(await isFile(dotnetExePath))) { - throw new Error(`The path returned for .net core does not exist: ${dotnetExePath}`); + dotnetPath = await acquireSharedDotnetInstallation(dotnetVersion); + if (!dotnetPath) { + // Acquisition failed. Error will already have been displayed. + return undefined; + } + + if (!(await isFile(dotnetPath))) { + throw new Error(`The path returned for .net core does not exist: ${dotnetPath}`); } // Telemetry: dotnet version actually used try { // E.g. "c:\Users\\AppData\Roaming\Code - Insiders\User\globalStorage\msazurermtools.azurerm-vscode-tools\.dotnet\2.2.5\dotnet.exe" - const versionMatch = dotnetExePath.match(/dotnet[\\/]([^\\/]+)[\\/]/); + const versionMatch = dotnetPath.match(/dotnet[\\/]([^\\/]+)[\\/]/); // tslint:disable-next-line: strict-boolean-expressions const actualVersion = versionMatch && versionMatch[1] || 'unknown'; actionContext.telemetry.properties.dotnetVersionInstalled = actualVersion; @@ -198,14 +210,10 @@ async function acquireDotnet(dotnetExePath: string): Promise { } } - ext.outputChannel.appendLine(`Using dotnet core executable at ${dotnetExePath}`); + ext.outputChannel.appendLine(`Using dotnet core executable at ${dotnetPath}`); - return dotnetExePath; + return dotnetPath; }); - - assert(typeof resultPath === "string", "Should have thrown instead of returning undefined"); - // tslint:disable-next-line:no-non-null-assertion // Asserted - return resultPath!; } function findLanguageServer(): string { @@ -244,23 +252,6 @@ function findLanguageServer(): string { return serverDllPath!; } -async function ensureDependencies(dotnetExePath: string, serverDllPath: string): Promise { - await callWithTelemetryAndErrorHandling('ensureDotnetDependencies', async (actionContext: IActionContext) => { - actionContext.errorHandling.rethrow = true; - - // Attempt to determine by running a .net app whether additional runtime dependencies are missing on the machine (Linux only), - // and if necessary prompt the user whether to install them. - await ensureDotnetDependencies( - dotnetExePath, - [ - serverDllPath, - '--help' - ], - actionContext.telemetry.properties, - actionContext.errorHandling.issueProperties); - }); -} - async function isFile(pathPath: string): Promise { return (await fse.pathExists(pathPath)) && (await fse.stat(pathPath)).isFile(); } diff --git a/src/util/wrapError.ts b/src/util/wrapError.ts index 1bdd59970..caf747d08 100644 --- a/src/util/wrapError.ts +++ b/src/util/wrapError.ts @@ -13,7 +13,10 @@ import { parseError } from 'vscode-azureextensionui'; * @param innerError Original error or other item that was thrown */ export function wrapError(outerMessage: string, innerError: unknown): Error { - const newMessage = outerMessage + os.EOL + parseError(innerError).message; + // Note: We add a space as well as an EOL because in some vscode scenarios the EOL + // doesn't show up in the UI + // tslint:disable-next-line:prefer-template + const newMessage = outerMessage + " " + os.EOL + parseError(innerError).message; if (innerError instanceof Error) { const copy = cloneError(innerError); diff --git a/test/wrapError.test.ts b/test/wrapError.test.ts index 47f845487..5c78929ec 100644 --- a/test/wrapError.test.ts +++ b/test/wrapError.test.ts @@ -4,22 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as os from 'os'; import { parseError } from 'vscode-azureextensionui'; -import { wrapError } from '../extension.bundle'; +import { ext, wrapError } from '../extension.bundle'; suite("wrapError", () => { test("outer string, inner string", () => { let wrapped = wrapError('Outer error.', 'Inner error.'); assert(wrapped instanceof Error); - assert.equal(parseError(wrapped).message, `Outer error.${os.EOL}Inner error.`); + assert.equal(parseError(wrapped).message, `Outer error. ${ext.EOL}Inner error.`); }); test("outer string, inner error", () => { const inner = new Error('Inner error.'); let wrapped = wrapError('Outer error.', inner); assert(wrapped instanceof Error); - assert.equal(parseError(wrapped).message, `Outer error.${os.EOL}Inner error.`); + assert.equal(parseError(wrapped).message, `Outer error. ${ext.EOL}Inner error.`); assert.equal(wrapped.stack, inner.stack); assert.equal(wrapped.name, inner.name); }); @@ -41,7 +40,7 @@ suite("wrapError", () => { let wrapped = wrapError('Outer error.', inner); assert(wrapped instanceof Error); - assert.equal(parseError(wrapped).message, `Outer error.${os.EOL}Inner message.`); + assert.equal(parseError(wrapped).message, `Outer error. ${ext.EOL}Inner message.`); assert.equal(wrapped.stack, inner.stack); assert.equal(wrapped.name, inner.name); }); diff --git a/tsconfig.json b/tsconfig.json index f5364f1f1..c31292ce0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,16 @@ "noUnusedLocals": true, "strict": true, "alwaysStrict": true, - "experimentalDecorators": true + "typeRoots": [ + "./node_modules/@types", + // This is needed because ts-node ignores files which are not + // discoverable through dependencies (which means it won't find + // *.d.ts files, even though tsc would). But doing it this way means + // that you can't simply have loose .d.ts files under ./typings, they + // must instead be structured as node modules with separate folders + // and index.d.ts files + "./typings" + ] }, "exclude": [ "node_modules" diff --git a/typings/gulp-decompress/index.d.ts b/typings/gulp-decompress/index.d.ts new file mode 100644 index 000000000..a5b1f59bb --- /dev/null +++ b/typings/gulp-decompress/index.d.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'gulp-decompress' { + import * as decompress from 'decompress'; + import { Transform } from 'stream'; + + function gulp_decompress(opts: decompress.DecompressOptions): Transform; + + export = gulp_decompress; +} diff --git a/src/acquisition/IEventStreamObserver.ts b/typings/gulp-download/index.d.ts similarity index 68% rename from src/acquisition/IEventStreamObserver.ts rename to typings/gulp-download/index.d.ts index ec0b0e287..93e74a05f 100644 --- a/src/acquisition/IEventStreamObserver.ts +++ b/typings/gulp-download/index.d.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEvent } from './IEvent'; +declare module "gulp-download" { + import { Stream } from "stream"; -export interface IEventStreamObserver { - post(event: IEvent): void; - log: string; + function gulp_download(urls: string | string[]): Stream; + + export = gulp_download; }