diff --git a/images/win/scripts/Installers/Configure-Shell.ps1 b/images/win/scripts/Installers/Configure-Shell.ps1 new file mode 100644 index 000000000..8eb163bcf --- /dev/null +++ b/images/win/scripts/Installers/Configure-Shell.ps1 @@ -0,0 +1,23 @@ +# Create shells folder +$shellPath = "C:\shells" +New-Item -Path $shellPath -ItemType Directory | Out-Null + +# sh and bash <--> C:\msys64\usr\bin\bash.exe +New-Item -ItemType SymbolicLink -Path "$shellPath\bash.exe" -Target "C:\msys64\usr\bin\bash.exe" | Out-Null +New-Item -ItemType SymbolicLink -Path "$shellPath\sh.exe" -Target "C:\msys64\usr\bin\sh.exe" | Out-Null + +# WSL is available on Windows Server 2019 +if (Test-IsWin19) +{ + # winbash <--> C:\Windows\System32\bash.exe + New-Item -ItemType SymbolicLink -Path "$shellPath\winbash.exe" -Target "$env:SystemRoot\System32\bash.exe" | Out-Null +} + +# gitbash <--> C:\Program Files\Git\bin\bash.exe +New-Item -ItemType SymbolicLink -Path "$shellPath\gitbash.exe" -Target "$env:ProgramFiles\Git\bin\bash.exe" | Out-Null + +# msysbash <--> C:\msys64\usr\bin\bash.exe +New-Item -ItemType SymbolicLink -Path "$shellPath\msysbash.exe" -Target "C:\msys64\usr\bin\bash.exe" | Out-Null + +# Add shells to PATH +Add-MachinePathItem $shellPath \ No newline at end of file diff --git a/images/win/scripts/Installers/Install-Git.ps1 b/images/win/scripts/Installers/Install-Git.ps1 index 27a619695..6c89250e9 100644 --- a/images/win/scripts/Installers/Install-Git.ps1 +++ b/images/win/scripts/Installers/Install-Git.ps1 @@ -26,7 +26,7 @@ Install-Binary -Url $downloadUrl ` "/SP-", ` "/CLOSEAPPLICATIONS", ` "/RESTARTAPPLICATIONS", ` - "/o:PathOption=CmdTools", ` + "/o:PathOption=Cmd", ` "/o:BashTerminalOption=ConHost", ` "/o:EnableSymlinks=Enabled", ` "/COMPONENTS=gitlfs") @@ -36,15 +36,5 @@ Choco-Install -PackageName hub # Disable GCM machine-wide [Environment]::SetEnvironmentVariable("GCM_INTERACTIVE", "Never", [System.EnvironmentVariableTarget]::Machine) -Add-MachinePathItem "C:\Program Files\Git\bin" - -if (Test-IsWin16) { - $env:Path += ";$env:ProgramFiles\Git\usr\bin\" -} - -# Add well-known SSH host keys to ssh_known_hosts -ssh-keyscan -t rsa github.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" -ssh-keyscan -t rsa ssh.dev.azure.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" - Invoke-PesterTests -TestFile "Git" -TestName "Git" Invoke-PesterTests -TestFile "CLI.Tools" -TestName "Hub CLI" diff --git a/images/win/scripts/Installers/Install-Mingw64.ps1 b/images/win/scripts/Installers/Install-Mingw64.ps1 index b6c860485..2b999f450 100644 --- a/images/win/scripts/Installers/Install-Mingw64.ps1 +++ b/images/win/scripts/Installers/Install-Mingw64.ps1 @@ -7,7 +7,7 @@ Choco-Install -PackageName mingw # Make a copy of mingw32-make.exe to make.exe, which is a more discoverable name # and so the same command line can be used on Windows as on macOS and Linux -$path = where.exe mingw32-make.exe | Get-Item +$path = Get-Command mingw32-make.exe -CommandType All | Where-Object { $_.Path.Contains("C:\ProgramData\Chocolatey") } | Get-Item Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') Invoke-PesterTests -TestFile "Tools" -TestName "Mingw64" diff --git a/images/win/scripts/Installers/Install-Msys2.ps1 b/images/win/scripts/Installers/Install-Msys2.ps1 index f0da38503..dea3b95d0 100644 --- a/images/win/scripts/Installers/Install-Msys2.ps1 +++ b/images/win/scripts/Installers/Install-Msys2.ps1 @@ -9,35 +9,19 @@ $dash = "-" * 40 -$origPath = $env:PATH -$gitPath = "$env:ProgramFiles\Git" - -$msys2_release = "https://api.github.com/repos/msys2/msys2-installer/releases/latest" - -$msys2Uri = ((Invoke-RestMethod $msys2_release).assets | Where-Object { - $_.name -match "x86_64" -and $_.name.EndsWith("tar.xz") }).browser_download_url - -# Download the latest msys2 x86_64, filename includes release date -Write-Host "Starting msys2 download using $($msys2Uri.split('/')[-1])" +# Downloading msys2 +$msys2Release = "https://api.github.com/repos/msys2/msys2-installer/releases/latest" +$msys2Uri = ((Invoke-RestMethod $msys2Release).assets | Where-Object { + $_.name -match "x86_64" -and $_.name.EndsWith("sfx.exe") }).browser_download_url $msys2File = Start-DownloadWithRetry -Url $msys2Uri -Write-Host "Finished download" -# nix style path for tar -$msys2FileU = "/$msys2File".replace(':', '').replace('\', '/') - -# Git tar needs exe's from mingw64\bin -$env:PATH = "$gitPath\usr\bin;$gitPath\mingw64\bin;$origPath" - -$tar = "$gitPath\usr\bin\tar.exe" - -# extract tar.xz to C:\ +# extract sfx.exe to C:\ Write-Host "Starting msys2 extraction" -&$tar -xJf $msys2FileU -C /c/ -Remove-Item $msys2File +& $msys2File -y -oC:\ Write-Host "Finished extraction" # Add msys2 bin tools folders to PATH temporary -$env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;$origPath" +$env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;$env:PATH" Write-Host "`n$dash bash pacman-key --init" bash.exe -c "pacman-key --init 2>&1" @@ -73,6 +57,10 @@ Write-Host "`n$dash Install mingw32 packages" $pre = "mingw-w64-i686-" pacman.exe -S --noconfirm --needed --noprogressbar $tools32.replace('___', $pre).split(' ') +# install openssh +Write-Host "`n$dash Install openssh package" +pacman.exe -S --noconfirm --needed --noprogressbar openssh + # clean all packages to decrease image size Write-Host "`n$dash Clean packages" pacman.exe -Scc --noconfirm @@ -88,4 +76,23 @@ pacman.exe -Q | grep -v ^mingw-w64- Write-Host "`nMSYS2 installation completed" +# Environment +# add C:\msys64\mingw64\bin and C:\msys64\usr\bin to PATH +# C:\msys64\mingw64\bin add after C:\Windows\System32 to not replace built-in tar.exe +$regEnvKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' +$pathValue = Get-ItemPropertyValue -Path $regEnvKey -Name 'Path' +$pathValue += ";C:\msys64\mingw64\bin;C:\msys64\usr\bin" +Set-ItemProperty -Path $regEnvKey -Name 'Path' -Value $pathValue + +# Add well-known SSH host keys to ssh_known_hosts to Msys2 +ssh-keyscan -t rsa github.com >> "C:\msys64\etc\ssh\ssh_known_hosts" +ssh-keyscan -t rsa ssh.dev.azure.com >> "C:\msys64\etc\ssh\ssh_known_hosts" + +# Add well-known SSH host keys to ssh_known_hosts to Git +if (Test-Path "C:\Program Files\Git\etc\ssh") +{ + ssh-keyscan -t rsa github.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" + ssh-keyscan -t rsa ssh.dev.azure.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" +} + Invoke-PesterTests -TestFile "MSYS2" diff --git a/images/win/scripts/SoftwareReport/SoftwareReport.Common.psm1 b/images/win/scripts/SoftwareReport/SoftwareReport.Common.psm1 index 05b101aac..0ca95ee71 100644 --- a/images/win/scripts/SoftwareReport/SoftwareReport.Common.psm1 +++ b/images/win/scripts/SoftwareReport/SoftwareReport.Common.psm1 @@ -271,6 +271,13 @@ function Get-PacmanVersion { return "- Pacman $pacmanVersion" } +function Get-ShellTarget { + $shells = Get-ChildItem C:\shells -File | Select-Object @{n="Name";e={ + $name = $_.Name + if ($name -eq 'bash.exe') {"$name (Default)"} else {$name}}},@{n="Target";e={@($_.Target)[0]}} | Sort-Object Name + $shells | New-MDTable -Columns ([ordered]@{Name = "left"; Target = "left";}) +} + function Get-YAMLLintVersion { yamllint --version } \ No newline at end of file diff --git a/images/win/scripts/SoftwareReport/SoftwareReport.Generator.ps1 b/images/win/scripts/SoftwareReport/SoftwareReport.Generator.ps1 index 1b3bf299a..06e1054da 100644 --- a/images/win/scripts/SoftwareReport/SoftwareReport.Generator.ps1 +++ b/images/win/scripts/SoftwareReport/SoftwareReport.Generator.ps1 @@ -142,14 +142,22 @@ $markdown += New-MDList -Style Unordered -Lines @( (Get-SeleniumWebDriverVersion -Driver "iexplorer") ) +$markdown += New-MDHeader "Shells" -Level 3 +$markdown += Get-ShellTarget +$markdown += New-MDNewLine + $markdown += New-MDHeader "MSYS2" -Level 3 $markdown += Get-PacmanVersion $markdown += New-MDNewLine +$markdown += New-MDHeader "Notes:" -Level 5 $markdown += @' ``` Location: C:\msys64 -Note: MSYS2 is pre-installed on image but not added to PATH. +1. MSYS2 is pre-installed on image +2. C:\msys64\mingw64\bin is added to PATH and has lower precedence than C:\Windows\System32 +3. C:\msys64\usr\bin is added to PATH and has lower precedence than C:\Windows\System32 +4. Default bash.exe shell is set to the C:\msys64\usr\bin\bash.exe ``` '@ $markdown += New-MDNewLine diff --git a/images/win/scripts/Tests/Git.Tests.ps1 b/images/win/scripts/Tests/Git.Tests.ps1 index 8dd159fa4..e7675ee07 100644 --- a/images/win/scripts/Tests/Git.Tests.ps1 +++ b/images/win/scripts/Tests/Git.Tests.ps1 @@ -1,5 +1,5 @@ Describe "Git" { - $gitTools = 'bash', 'awk', 'git', 'git-lfs' + $gitTools = 'git', 'git-lfs' $gitTestCases = $gitTools | ForEach-Object { @{ toolName = $_ diff --git a/images/win/scripts/Tests/Shell.Tests.ps1 b/images/win/scripts/Tests/Shell.Tests.ps1 new file mode 100644 index 000000000..915940e2a --- /dev/null +++ b/images/win/scripts/Tests/Shell.Tests.ps1 @@ -0,0 +1,50 @@ +Describe "Shell" { + $shellTestCases = @( + @{Name = "C:\shells\bash.exe"; Target = "C:\msys64\usr\bin\bash.exe"}, + @{Name = "C:\shells\sh.exe"; Target = "C:\msys64\usr\bin\sh.exe"}, + @{Name = "C:\shells\gitbash.exe"; Target = "$env:ProgramFiles\Git\bin\bash.exe"}, + @{Name = "C:\shells\msysbash.exe"; Target = "C:\msys64\usr\bin\bash.exe"} + ) + + $pathTestCases = @( + @{Path = "C:\shells"}, + @{Path = "C:\msys64\mingw64\bin"}, + @{Path = "C:\msys64\usr\bin"} + ) + + $IsWin16 = Test-IsWin16 + + It "Default bash.exe from MSYS2" { + (Get-Command bash).Path | Should -BeExactly "C:\shells\bash.exe" + } + + It "Default sh.exe from MSYS2" { + (Get-Command sh).Path | Should -BeExactly "C:\shells\sh.exe" + } + + It "Folder C:\shells exists" { + "C:\shells" | Should -Exist + } + + It "C:\Windows\System32 before C:\msys64\mingw64\bin and C:\msys64\usr\bin" { + $path = $env:Path.Split(";").ToLower() + $indexOfSystem32 = $path.IndexOf("c:\windows\system32") + + $path.IndexOf("c:\msys64\mingw64\bin") | Should -BeGreaterThan $indexOfSystem32 + $path.IndexOf("c:\msys64\usr\bin") | Should -BeGreaterThan $indexOfSystem32 + } + + It "C:\shells\winbash.exe target to $env:SystemRoot\System32\bash.exe" -Skip:$IsWin16 { + $Name = "C:\shells\winbash.exe" + $Target = "$env:SystemRoot\System32\bash.exe" + (Get-Item $Name).Target | Should -BeExactly $Target + } + + It " target to " -TestCases $shellTestCases { + (Get-Item $Name).Target | Should -BeExactly $Target + } + + It " is in PATH" -TestCases $pathTestCases { + $env:Path.Split(";") | Should -Contain $Path + } +} \ No newline at end of file diff --git a/images/win/windows2016.json b/images/win/windows2016.json index 3c4b65663..8c1314d63 100644 --- a/images/win/windows2016.json +++ b/images/win/windows2016.json @@ -332,7 +332,8 @@ "type": "powershell", "scripts": [ "{{ template_dir }}/scripts/Installers/Install-WindowsUpdates.ps1", - "{{ template_dir }}/scripts/Installers/Configure-DynamicPort.ps1" + "{{ template_dir }}/scripts/Installers/Configure-DynamicPort.ps1", + "{{ template_dir }}/scripts/Installers/Configure-Shell.ps1" ], "elevated_user": "{{user `install_user`}}", "elevated_password": "{{user `install_password`}}" diff --git a/images/win/windows2019.json b/images/win/windows2019.json index 6b9f3a868..a002f77f5 100644 --- a/images/win/windows2019.json +++ b/images/win/windows2019.json @@ -330,7 +330,8 @@ "type": "powershell", "scripts": [ "{{ template_dir }}/scripts/Installers/Install-WindowsUpdates.ps1", - "{{ template_dir }}/scripts/Installers/Configure-DynamicPort.ps1" + "{{ template_dir }}/scripts/Installers/Configure-DynamicPort.ps1", + "{{ template_dir }}/scripts/Installers/Configure-Shell.ps1" ], "elevated_user": "{{user `install_user`}}", "elevated_password": "{{user `install_password`}}"