From f95e5798bd0b343b65f9cc7d69e4ca38130b086b Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 29 Jan 2020 21:04:13 -0700 Subject: [PATCH] Pin the .NET Core SDK --- .travis.yml | 23 ++++-- CONTRIBUTING.md | 15 +++- appveyor.yml | 4 +- azure-pipelines.yml | 11 ++- global.json | 5 ++ tools/DotNetSdkVersion.ps1 | 2 + tools/Install-DotNetSdk.ps1 | 160 ++++++++++++++++++++++++++++++++++++ tools/Set-EnvVars.ps1 | 79 ++++++++++++++++++ 8 files changed, 287 insertions(+), 12 deletions(-) create mode 100644 global.json create mode 100644 tools/DotNetSdkVersion.ps1 create mode 100644 tools/Install-DotNetSdk.ps1 create mode 100644 tools/Set-EnvVars.ps1 diff --git a/.travis.yml b/.travis.yml index cc1e3c35a..a6993951b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,24 @@ # see travis-ci.org for details language: csharp -dist: xenial -dotnet: 2.1.802 mono: none -osx_image: xcode8.3 -os: - - osx - - linux +matrix: + include: + - os: linux + dist: xenial + before_install: + - | + wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + sudo apt-get update + sudo apt-get install -y powershell + - os: osx + osx_image: xcode8.3 + before_install: + - brew update # This is necessary to get pwsh 6.2 instead of some 6.0-preview that isn't named `pwsh` + - brew cask install powershell + fast_finish: true before_install: - date -u @@ -18,6 +28,7 @@ before_install: install: - git fetch --unshallow + - pwsh ./tools/Install-DotNetSdk.ps1 ; export PATH=~/.dotnet:$PATH # Build libgit2, LibGit2Sharp and run the tests script: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e1c1ab31d..218cb2a28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ We love Pull Requests! Your contributions help make LibGit2Sharp great. ## Getting Started -So you want to contribute to LibGit2Sharp. Great! Contributions take many forms from +So you want to contribute to LibGit2Sharp. Great! Contributions take many forms from submitting issues, writing documentation, to making code changes. We welcome it all. But first things first... @@ -14,18 +14,25 @@ But first things first... * Clearly describe the issue including steps to reproduce when it is a bug. * Make sure you fill in the earliest version that you know has the issue. * Fork the repository on GitHub, then clone it using your favorite Git client. -* Make sure the project builds and all tests pass on your machine by running +* Make sure the project builds and all tests pass on your machine by running the `buildandtest.cmd` script on Windows or `buildandtest.sh` on Linux/Mac. ## LibGit2 LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono. -LibGit2 is a git submodule referencing the [libgit2 project](https://github.com/libgit2/libgit2). To learn more about +LibGit2 is a git submodule referencing the [libgit2 project](https://github.com/libgit2/libgit2). To learn more about submodules read [here](http://git-scm.com/book/en/v2/Git-Tools-Submodules). To build libgit2 see [here](https://github.com/libgit2/libgit2sharp/wiki/How-to-build-x64-libgit2-and-LibGit2Sharp). ## Making Changes +Make sure you have the required .NET Core SDK and runtimes installed. +The easiest way to do this is run our `tools\Install-DotNetSdk.ps1` script. +Using the `-InstallLocality Machine` switch requires elevation but ensures +that Visual Studio will be able to load the solution even when launched from a shortcut. + +Then proceed to: + * Create a topic branch off master (don't work directly on master). * Implement your feature or fix your bug. Please following existing coding styles and do not introduce new ones. * Make atomic, focused commits with good commit messages. @@ -42,7 +49,7 @@ Some things that will increase the chance that your pull request is accepted. * Following existing code conventions. * Including unit tests that would otherwise fail without the patch, but pass after applying it. * Updating the documentation and tests that are affected by the contribution. -* If code from elsewhere is used, proper credit and a link to the source should exist in the code comments. +* If code from elsewhere is used, proper credit and a link to the source should exist in the code comments. Then licensing issues can be checked against LibGit2Sharp's very permissive MIT based open source license. * Having a configured git client that converts line endings to LF. [See here.](https://help.github.com/articles/dealing-with-line-endings/). # Additional Resources diff --git a/appveyor.yml b/appveyor.yml index 3db8b2b8a..3b46955f4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: '{build}' -os: Visual Studio 2017 +os: Visual Studio 2019 branches: only: @@ -68,6 +68,8 @@ install: cinst curl -y } + ./tools/Install-DotNetSdk.ps1 + before_build: - ps: | msbuild "$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp.sln" ` diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5d703aadf..d34e9af7c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,16 +10,25 @@ variables: jobs: - job: Windows pool: - vmImage: 'VS2017-Win2016' + vmImage: 'windows-2019' steps: + - pwsh: ./tools/Install-DotNetSdk.ps1 + displayName: Installing .NET Core SDK and runtimes - script: buildandtest.cmd + displayName: Build and test - job: Linux pool: vmImage: 'Ubuntu 16.04' steps: + - pwsh: ./tools/Install-DotNetSdk.ps1 + displayName: Installing .NET Core SDK and runtimes - script: ./buildandtest.sh + displayName: Build and test - job: macOS pool: vmImage: 'macOS 10.13' steps: + - pwsh: ./tools/Install-DotNetSdk.ps1 + displayName: Installing .NET Core SDK and runtimes - script: ./buildandtest.sh + displayName: Build and test diff --git a/global.json b/global.json new file mode 100644 index 000000000..b354da963 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "2.1.802" + } +} diff --git a/tools/DotNetSdkVersion.ps1 b/tools/DotNetSdkVersion.ps1 new file mode 100644 index 000000000..41b89d7fd --- /dev/null +++ b/tools/DotNetSdkVersion.ps1 @@ -0,0 +1,2 @@ +$globalJson = Get-Content -Path "$PSScriptRoot\..\global.json" | ConvertFrom-Json +$globalJson.sdk.version diff --git a/tools/Install-DotNetSdk.ps1 b/tools/Install-DotNetSdk.ps1 new file mode 100644 index 000000000..e769aa2ed --- /dev/null +++ b/tools/Install-DotNetSdk.ps1 @@ -0,0 +1,160 @@ +<# +.SYNOPSIS +Installs the .NET SDK specified in the global.json file at the root of this repository, +along with supporting .NET Core runtimes used for testing. +.DESCRIPTION +This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location, +unless `-InstallLocality machine` is specified. +.PARAMETER InstallLocality +A value indicating whether dependencies should be installed locally to the repo or at a per-user location. +Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. +Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. +Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. +When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. +Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. +Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. +#> +[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] +Param ( + [ValidateSet('repo','user','machine')] + [string]$InstallLocality='user' +) + +$DotNetInstallScriptRoot = "$PSScriptRoot/../obj/tools" +if (!(Test-Path $DotNetInstallScriptRoot)) { New-Item -ItemType Directory -Path $DotNetInstallScriptRoot | Out-Null } +$DotNetInstallScriptRoot = Resolve-Path $DotNetInstallScriptRoot + +# Look up actual required .NET Core SDK version from global.json +$sdkVersion = & "$PSScriptRoot/DotNetSdkVersion.ps1" + +# Search for all .NET Core runtime versions referenced from MSBuild projects and arrange to install them. +$runtimeVersions = @() +Get-ChildItem "$PSScriptRoot\..\*.*proj" -Recurse |% { + $projXml = [xml](Get-Content -Path $_) + $targetFrameworks = $projXml.Project.PropertyGroup.TargetFramework + if (!$targetFrameworks) { + $targetFrameworks = $projXml.Project.PropertyGroup.TargetFrameworks + if ($targetFrameworks) { + $targetFrameworks = $targetFrameworks -Split ';' + } + } + $targetFrameworks |? { $_ -match 'netcoreapp(\d+\.\d+)' } |% { + $runtimeVersions += $Matches[1] + } +} + +Function Get-FileFromWeb([Uri]$Uri, $OutDir) { + $OutFile = Join-Path $OutDir $Uri.Segments[-1] + if (!(Test-Path $OutFile)) { + Write-Verbose "Downloading $Uri..." + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) + } finally { + # This try/finally causes the script to abort + } + } + + $OutFile +} + +Function Get-InstallerExe($Version, [switch]$Runtime) { + $sdkOrRuntime = 'Sdk' + if ($Runtime) { $sdkOrRuntime = 'Runtime' } + + # Get the latest/actual version for the specified one + if (([Version]$Version).Build -eq -1) { + $versionInfo = -Split (Invoke-WebRequest -Uri "https://dotnetcli.blob.core.windows.net/dotnet/$sdkOrRuntime/$Version/latest.version" -UseBasicParsing) + $Version = $versionInfo[-1] + } + + Get-FileFromWeb -Uri "https://dotnetcli.blob.core.windows.net/dotnet/$sdkOrRuntime/$Version/dotnet-$($sdkOrRuntime.ToLowerInvariant())-$Version-win-x64.exe" -OutDir "$DotNetInstallScriptRoot" +} + +Function Install-DotNet($Version, [switch]$Runtime) { + if ($Runtime) { $sdkSubstring = '' } else { $sdkSubstring = 'SDK ' } + Write-Host "Downloading .NET Core $sdkSubstring$Version..." + $Installer = Get-InstallerExe -Version $Version -Runtime:$Runtime + Write-Host "Installing .NET Core $sdkSubstring$Version..." + cmd /c start /wait $Installer /install /quiet + if ($LASTEXITCODE -ne 0) { + throw "Failure to install .NET Core SDK" + } +} + +if ($InstallLocality -eq 'machine') { + if ($IsMacOS -or $IsLinux) { + Write-Error "Installing the .NET Core SDK or runtime at a machine-wide location is only supported by this script on Windows." + exit 1 + } + + if ($PSCmdlet.ShouldProcess(".NET Core SDK $sdkVersion", "Install")) { + Install-DotNet -Version $sdkVersion + } + + $runtimeVersions |% { + if ($PSCmdlet.ShouldProcess(".NET Core runtime $_", "Install")) { + Install-DotNet -Version $_ -Runtime + } + } + + return +} + +$switches = @( + '-Architecture','x64' +) +$envVars = @{ + # For locally installed dotnet, skip first time experience which takes a long time + 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' = 'true'; +} + +if ($InstallLocality -eq 'repo') { + $DotNetInstallDir = "$DotNetInstallScriptRoot/.dotnet" +} elseif ($env:AGENT_TOOLSDIRECTORY) { + $DotNetInstallDir = "$env:AGENT_TOOLSDIRECTORY/dotnet" +} else { + $DotNetInstallDir = Join-Path $HOME .dotnet +} + +Write-Host "Installing .NET Core SDK and runtimes to $DotNetInstallDir" -ForegroundColor Blue + +if ($DotNetInstallDir) { + $switches += '-InstallDir',$DotNetInstallDir + $envVars['DOTNET_MULTILEVEL_LOOKUP'] = '0' + $envVars['DOTNET_ROOT'] = $DotNetInstallDir +} + +if ($IsMacOS -or $IsLinux) { + $DownloadUri = "https://dot.net/v1/dotnet-install.sh" + $DotNetInstallScriptPath = "$DotNetInstallScriptRoot/dotnet-install.sh" +} else { + $DownloadUri = "https://dot.net/v1/dotnet-install.ps1" + $DotNetInstallScriptPath = "$DotNetInstallScriptRoot/dotnet-install.ps1" +} + +if (-not (Test-Path $DotNetInstallScriptPath)) { + Invoke-WebRequest -Uri $DownloadUri -OutFile $DotNetInstallScriptPath -UseBasicParsing + if ($IsMacOS -or $IsLinux) { + chmod +x $DotNetInstallScriptPath + } +} + +if ($PSCmdlet.ShouldProcess(".NET Core SDK $sdkVersion", "Install")) { + Invoke-Expression -Command "$DotNetInstallScriptPath -Version $sdkVersion $switches" +} else { + Invoke-Expression -Command "$DotNetInstallScriptPath -Version $sdkVersion $switches -DryRun" +} + +$switches += '-Runtime','dotnet' + +$runtimeVersions | Get-Unique |% { + if ($PSCmdlet.ShouldProcess(".NET Core runtime $_", "Install")) { + Invoke-Expression -Command "$DotNetInstallScriptPath -Channel $_ $switches" + } else { + Invoke-Expression -Command "$DotNetInstallScriptPath -Channel $_ $switches -DryRun" + } +} + +if ($PSCmdlet.ShouldProcess("Set DOTNET environment variables to discover these installed runtimes?")) { + & "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars -PrependPath $DotNetInstallDir | Out-Null +} diff --git a/tools/Set-EnvVars.ps1 b/tools/Set-EnvVars.ps1 new file mode 100644 index 000000000..907659a7b --- /dev/null +++ b/tools/Set-EnvVars.ps1 @@ -0,0 +1,79 @@ +<# +.SYNOPSIS + Set environment variables in the environment. + Azure Pipeline and CMD environments are considered. +.PARAMETER Variables + A hashtable of variables to be set. +.OUTPUTS + A boolean indicating whether the environment variables can be expected to propagate to the caller's environment. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +Param( + [Parameter(Mandatory=$true, Position=1)] + $Variables, + [string[]]$PrependPath +) + +if ($Variables.Count -eq 0) { + return $true +} + +$cmdInstructions = !$env:TF_BUILD -and !$env:GITHUB_ACTIONS -and $env:PS1UnderCmd -eq '1' +if ($cmdInstructions) { + Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe" + Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue +} else { + Write-Host "Environment variables set:" -ForegroundColor Blue + $envVars + if ($PrependPath) { + Write-Host "Paths prepended to PATH: $PrependPath" + } +} + +if ($env:TF_BUILD) { + Write-Host "Azure Pipelines detected. Logging commands will be used to propagate environment variables and prepend path." +} + +if ($env:GITHUB_ACTIONS) { + Write-Host "GitHub Actions detected. Logging commands will be used to propagate environment variables and prepend path." +} + +$Variables.GetEnumerator() |% { + Set-Item -Path env:$($_.Key) -Value $_.Value + + # If we're running in a cloud CI, set these environment variables so they propagate. + if ($env:TF_BUILD) { + Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" + } + if ($env:GITHUB_ACTIONS) { + Write-Host "::set-env name=$($_.Key)::$($_.Value)" + } + + if ($cmdInstructions) { + Write-Host "SET $($_.Key)=$($_.Value)" + } +} + +$pathDelimiter = ';' +if ($IsMacOS -or $IsLinux) { + $pathDelimiter = ':' +} + +if ($PrependPath) { + $PrependPath |% { + $newPathValue = "$_$pathDelimiter$env:PATH" + Set-Item -Path env:PATH -Value $newPathValue + if ($cmdInstructions) { + Write-Host "SET PATH=$newPathValue" + } + + if ($env:TF_BUILD) { + Write-Host "##vso[task.prependpath]$_" + } + if ($env:GITHUB_ACTIONS) { + Write-Host "::add-path::$_" + } + } +} + +return !$cmdInstructions