|
| 1 | +#!/usr/bin/env powershell |
| 2 | +#requires -version 4 |
| 3 | + |
| 4 | +<# |
| 5 | +.SYNOPSIS |
| 6 | +Executes KoreBuild commands. |
| 7 | +
|
| 8 | +.DESCRIPTION |
| 9 | +Downloads korebuild if required. Then executes the KoreBuild command. To see available commands, execute with `-Command help`. |
| 10 | +
|
| 11 | +.PARAMETER Command |
| 12 | +The KoreBuild command to run. |
| 13 | +
|
| 14 | +.PARAMETER Path |
| 15 | +The folder to build. Defaults to the folder containing this script. |
| 16 | +
|
| 17 | +.PARAMETER Channel |
| 18 | +The channel of KoreBuild to download. Overrides the value from the config file. |
| 19 | +
|
| 20 | +.PARAMETER DotNetHome |
| 21 | +The directory where .NET Core tools will be stored. |
| 22 | +
|
| 23 | +.PARAMETER ToolsSource |
| 24 | +The base url where build tools can be downloaded. Overrides the value from the config file. |
| 25 | +
|
| 26 | +.PARAMETER Update |
| 27 | +Updates KoreBuild to the latest version even if a lock file is present. |
| 28 | +
|
| 29 | +.PARAMETER ConfigFile |
| 30 | +The path to the configuration file that stores values. Defaults to korebuild.json. |
| 31 | +
|
| 32 | +.PARAMETER ToolsSourceSuffix |
| 33 | +The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. |
| 34 | +
|
| 35 | +.PARAMETER Arguments |
| 36 | +Arguments to be passed to the command |
| 37 | +
|
| 38 | +.NOTES |
| 39 | +This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be. |
| 40 | +When the lockfile is not present, KoreBuild will create one using latest available version from $Channel. |
| 41 | +
|
| 42 | +The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set |
| 43 | +in the file are overridden by command line parameters. |
| 44 | +
|
| 45 | +.EXAMPLE |
| 46 | +Example config file: |
| 47 | +```json |
| 48 | +{ |
| 49 | + "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json", |
| 50 | + "channel": "dev", |
| 51 | + "toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools" |
| 52 | +} |
| 53 | +``` |
| 54 | +#> |
| 55 | +[CmdletBinding(PositionalBinding = $false)] |
| 56 | +param( |
| 57 | + [Parameter(Mandatory = $true, Position = 0)] |
| 58 | + [string]$Command, |
| 59 | + [string]$Path = $PSScriptRoot, |
| 60 | + [Alias('c')] |
| 61 | + [string]$Channel, |
| 62 | + [Alias('d')] |
| 63 | + [string]$DotNetHome, |
| 64 | + [Alias('s')] |
| 65 | + [string]$ToolsSource, |
| 66 | + [Alias('u')] |
| 67 | + [switch]$Update, |
| 68 | + [string]$ConfigFile, |
| 69 | + [string]$ToolsSourceSuffix, |
| 70 | + [Parameter(ValueFromRemainingArguments = $true)] |
| 71 | + [string[]]$Arguments |
| 72 | +) |
| 73 | + |
| 74 | +Set-StrictMode -Version 2 |
| 75 | +$ErrorActionPreference = 'Stop' |
| 76 | + |
| 77 | +# |
| 78 | +# Functions |
| 79 | +# |
| 80 | + |
| 81 | +function Get-KoreBuild { |
| 82 | + |
| 83 | + $lockFile = Join-Path $Path 'korebuild-lock.txt' |
| 84 | + |
| 85 | + if (!(Test-Path $lockFile) -or $Update) { |
| 86 | + Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix |
| 87 | + } |
| 88 | + |
| 89 | + $version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1 |
| 90 | + if (!$version) { |
| 91 | + Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'" |
| 92 | + } |
| 93 | + $version = $version.TrimStart('version:').Trim() |
| 94 | + $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) |
| 95 | + |
| 96 | + if (!(Test-Path $korebuildPath)) { |
| 97 | + Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" |
| 98 | + New-Item -ItemType Directory -Path $korebuildPath | Out-Null |
| 99 | + $remotePath = "$ToolsSource/korebuild/artifacts/$version/korebuild.$version.zip" |
| 100 | + |
| 101 | + try { |
| 102 | + $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" |
| 103 | + Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix |
| 104 | + if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) { |
| 105 | + # Use built-in commands where possible as they are cross-plat compatible |
| 106 | + Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath |
| 107 | + } |
| 108 | + else { |
| 109 | + # Fallback to old approach for old installations of PowerShell |
| 110 | + Add-Type -AssemblyName System.IO.Compression.FileSystem |
| 111 | + [System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath) |
| 112 | + } |
| 113 | + } |
| 114 | + catch { |
| 115 | + Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore |
| 116 | + throw |
| 117 | + } |
| 118 | + finally { |
| 119 | + Remove-Item $tmpfile -ErrorAction Ignore |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + return $korebuildPath |
| 124 | +} |
| 125 | + |
| 126 | +function Join-Paths([string]$path, [string[]]$childPaths) { |
| 127 | + $childPaths | ForEach-Object { $path = Join-Path $path $_ } |
| 128 | + return $path |
| 129 | +} |
| 130 | + |
| 131 | +function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) { |
| 132 | + if ($RemotePath -notlike 'http*') { |
| 133 | + Copy-Item $RemotePath $LocalPath |
| 134 | + return |
| 135 | + } |
| 136 | + |
| 137 | + $retries = 10 |
| 138 | + while ($retries -gt 0) { |
| 139 | + $retries -= 1 |
| 140 | + try { |
| 141 | + Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath |
| 142 | + return |
| 143 | + } |
| 144 | + catch { |
| 145 | + Write-Verbose "Request failed. $retries retries remaining" |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + Write-Error "Download failed: '$RemotePath'." |
| 150 | +} |
| 151 | + |
| 152 | +# |
| 153 | +# Main |
| 154 | +# |
| 155 | + |
| 156 | +# Load configuration or set defaults |
| 157 | + |
| 158 | +$Path = Resolve-Path $Path |
| 159 | +if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' } |
| 160 | + |
| 161 | +if (Test-Path $ConfigFile) { |
| 162 | + try { |
| 163 | + $config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json |
| 164 | + if ($config) { |
| 165 | + if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel } |
| 166 | + if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource} |
| 167 | + } |
| 168 | + } |
| 169 | + catch { |
| 170 | + Write-Warning "$ConfigFile could not be read. Its settings will be ignored." |
| 171 | + Write-Warning $Error[0] |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +if (!$DotNetHome) { |
| 176 | + $DotNetHome = if ($env:DOTNET_HOME) { $env:DOTNET_HOME } ` |
| 177 | + elseif ($env:USERPROFILE) { Join-Path $env:USERPROFILE '.dotnet'} ` |
| 178 | + elseif ($env:HOME) {Join-Path $env:HOME '.dotnet'}` |
| 179 | + else { Join-Path $PSScriptRoot '.dotnet'} |
| 180 | +} |
| 181 | + |
| 182 | +if (!$Channel) { $Channel = 'dev' } |
| 183 | +if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' } |
| 184 | + |
| 185 | +# Execute |
| 186 | + |
| 187 | +$korebuildPath = Get-KoreBuild |
| 188 | +Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') |
| 189 | + |
| 190 | +try { |
| 191 | + Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile |
| 192 | + Invoke-KoreBuildCommand $Command @Arguments |
| 193 | +} |
| 194 | +finally { |
| 195 | + Remove-Module 'KoreBuild' -ErrorAction Ignore |
| 196 | +} |
0 commit comments