From b49a705f6bad1f35383373c9f1460d1867d4e2cb Mon Sep 17 00:00:00 2001 From: Maxim Lobanov Date: Sat, 31 Oct 2020 18:41:04 +0300 Subject: [PATCH] need to test --- images/macos/helpers/Xcode.Helpers.psm1 | 20 ++- images/macos/helpers/Xcode.Installer.psm1 | 160 ++++++++++++++++++ .../configuration/configure-machine.sh | 4 + images/macos/provision/core/xcode-tools.sh | 69 -------- images/macos/provision/core/xcode.ps1 | 38 +++++ images/macos/provision/utils/xcode-utils.sh | 118 ------------- .../software-report/SoftwareReport.Xcode.psm1 | 24 ++- images/macos/templates/macOS-10.15.json | 10 +- images/macos/toolsets/toolset-10.15.json | 24 +-- 9 files changed, 245 insertions(+), 222 deletions(-) create mode 100644 images/macos/helpers/Xcode.Installer.psm1 delete mode 100755 images/macos/provision/core/xcode-tools.sh create mode 100644 images/macos/provision/core/xcode.ps1 delete mode 100644 images/macos/provision/utils/xcode-utils.sh diff --git a/images/macos/helpers/Xcode.Helpers.psm1 b/images/macos/helpers/Xcode.Helpers.psm1 index 52a182d57..2b2ebeef3 100644 --- a/images/macos/helpers/Xcode.Helpers.psm1 +++ b/images/macos/helpers/Xcode.Helpers.psm1 @@ -8,9 +8,7 @@ function Get-XcodeRootPath { } function Get-DefaultXcodeRootPath { - $defaultXcodePath = "/Applications/Xcode.app" - $defaultXcodeItem = Get-Item -Path $defaultXcodePath - return $defaultXcodeItem.Target + return (Get-Item -Path "/Applications/Xcode.app").Target } function Get-XcodeToolPath { @@ -29,6 +27,22 @@ function Get-XcodeToolPath { return Join-Path $XcodeRootPath "Contents/Developer/usr/bin" $ToolName } +function Get-XcodeVersionInfo { + param( + [Parameter(Mandatory)] + [string]$XcodeRootPath + ) + + $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" + [string]$output = Invoke-Expression "$xcodebuildPath -version" + $versionOutputParts = $output.Split(" ") + return @{ + Version = [System.Version]::Parse($versionOutputParts[1]) + Build = $versionOutputParts[4] + } +} + + function Switch-Xcode { param ( [Parameter(ParameterSetName = 'Version')] diff --git a/images/macos/helpers/Xcode.Installer.psm1 b/images/macos/helpers/Xcode.Installer.psm1 new file mode 100644 index 000000000..619d167e9 --- /dev/null +++ b/images/macos/helpers/Xcode.Installer.psm1 @@ -0,0 +1,160 @@ +Import-Module "$PSScriptRoot/Xcode.Helpers.psm1" + +function Install-XcodeVersion { + param( + [Parameter(Mandatory)] + [string]$Version, + [Parameter(Mandatory)] + [string]$LocalLink + ) + + $xcodeDownloadDirectory = "$env:HOME/Library/Caches/XcodeInstall" + $xcodeTargetPath = Get-XcodeRootPath -Version $LocalLink + Push-Location $xcodeDownloadDirectory + + Invoke-DownloadXcodeArchive -Version $Version + Expand-XcodeXipArchive -DownloadDirectory $xcodeDownloadDirectory -TargetPath $xcodeTargetPath + Confirm-XcodeIntegrity -XcodeRootPath $xcodeTargetPath + Approve-XcodeLicense -XcodeRootPath $xcodeTargetPath + + Get-ChildItem $xcodeDownloadDirectory | Remove-Item -Force +} + +function Invoke-DownloadXcodeArchive { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + $resolvedVersion = Resolve-ExactXcodeVersion -Version $Version + if (-not $resolvedVersion) { + throw "Version '$Version' can't be matched to any available version" + } + + # TO-DO: Consider replacing of xcversion with own implementation + Write-Host "Downloading Xcode $resolvedVersion" + xcversion install "$resolvedVersion" --no-install +} + +function Resolve-ExactXcodeVersion { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + # TO-DO + return $Version +} + +function Expand-XcodeXipArchive { + param( + [Parameter(Mandatory)] + [string]$DownloadDirectory, + [Parameter(Mandatory)] + [string]$TargetPath + ) + + $xcodeXipPath = Get-ChildItem -Path $DownloadDirectory -Filter "Xcode_*.xip" | Select-Object -First 1 + + Write-Host "Extracting Xcode from '$xcodeXipPath'" + Push-Location $DownloadDirectory + xip -x $xcodeXipPath + Pop-Location + + if (Test-Path "$DownloadDirectory/Xcode-beta.app") { + Write-Host "Renaming Xcode-beta.app to Xcode.app" + Rename-File -Path "$DownloadDirectory/Xcode-beta.app" -NewName "Xcode.app" + } + + if (-not (Test-Path "$DownloadDirectory/Xcode.app")) { + throw "XIP archive '$xcodeXipPath' doesn't contain 'Xcode.app'" + } + + Write-Host "Moving '$DownloadDirectory/Xcode.app' to '$TargetPath'" + Move-Item -Path "$DownloadDirectory/Xcode.app" -Destination $TargetPath +} + +function Confirm-XcodeIntegrity { + param( + [Parameter(Mandatory)] + [string]$XcodeRootPath + ) + + if (Test-XcodeStableRelease -XcodeRootPath $XcodeRootPath) { + spctl --assess --raw $XcodeRootPath + } +} + +function Approve-XcodeLicense { + param( + [Parameter(Mandatory)] + $XcodeRootPath + ) + + $xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" + sudo $xcodeBuildPath -license accept +} + +function Install-XcodeAdditionalPackages { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + Write-Host "Installing additional packages for Xcode $Version..." + $xcodeRootPath = Get-XcodeRootPath -Version $Version + $packages = Get-ChildItem -Path "$xcodeRootPath/Contents/Resources/Packages" -Filter "*.pkg" -Name -File + $packages | ForEach-Object { & sudo installer -pkg $_ -target / -allowUntrusted } +} + +function Invoke-XcodeRunFirstLaunch { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + if ($Version.StartsWith("8.") -or $Version.StartsWith("9.")) { + return + } + + Write-Host "Running 'runFirstLaunch' for Xcode $Version..." + $xcodeRootPath = Get-XcodeToolPath -Version $Version -ToolName "xcodebuild" + & sudo $xcodeRootPath -runFirstLaunch +} + +function Build-XcodeSymlinks { + param( + [Parameter(Mandatory)] + [string]$Version, + [Parameter(Mandatory)] + [string[]]$Symlinks + ) + + $sourcePath = Get-XcodeRootPath -Version $Version + $Symlinks | ForEach-Object { + $targetPath = Get-XcodeRootPath -Version $_ + Write-Host "Creating symlink: '$targetPath' -> '$sourcePath'" + New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath + } +} + +function Build-ProvisionatorSymlink { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + $sourcePath = Get-XcodeRootPath -Version $Version + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $sourcePath + + $targetVersion = [SemVer]::Parse($versionInfo.Version).ToString() + $targetPath = Get-XcodeRootPath -Version $targetVersion + if ($sourcePath -ne $targetPath) { + Write-Host "Creating provisionator symlink: '$targetPath' -> '$sourcePath'" + New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath + } +} + +function Set-XcodeDeveloperDirEnvironmentVariables { + # TO-DO +} \ No newline at end of file diff --git a/images/macos/provision/configuration/configure-machine.sh b/images/macos/provision/configuration/configure-machine.sh index dbfae6731..126c5a7c1 100644 --- a/images/macos/provision/configuration/configure-machine.sh +++ b/images/macos/provision/configuration/configure-machine.sh @@ -8,11 +8,15 @@ echo "Configuring system time to GMT..." rm -f /etc/localtime ln -sf /usr/share/zoneinfo/UTC /etc/localtime +echo "Enabling safari driver..." # https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari # Safari’s executable is located at /usr/bin/safaridriver # Configure Safari to Enable WebDriver Support sudo safaridriver --enable +echo "Enabling developer mode..." +sudo /usr/sbin/DevToolsSecurity --enable + # Turn off hibernation and get rid of the sleepimage sudo pmset hibernatemode 0 sudo rm -f /var/vm/sleepimage diff --git a/images/macos/provision/core/xcode-tools.sh b/images/macos/provision/core/xcode-tools.sh deleted file mode 100755 index d9f33fe92..000000000 --- a/images/macos/provision/core/xcode-tools.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -# The script currently requires 2 external variables to be set: XCODE_INSTALL_USER -# and XCODE_INSTALL_PASSWORD, in order to access the Apple Developer Center - -set -e - -source ~/utils/utils.sh -source ~/utils/xcode-utils.sh - -if [ -z $XCODE_INSTALL_USER ] || [ -z $XCODE_INSTALL_PASSWORD ]; then - echo "Required environment variables XCODE_INSTALL_USER and XCODE_INSTALL_PASSWORD are not set" - exit 1 -fi - -XCODE_COUNT=$(get_toolset_value '.xcode.versions | length') -XCODE_LOCAL_LIST=$(get_toolset_value '.xcode.versions[].localVersion') -DEFAULT_XCODE_VERSION=$(get_toolset_value '.xcode.default') -WORK_DIR="${HOME}/Library/Caches/XcodeInstall" - -# Update the list of available versions -xcversion update - -for ((XCODE_INDEX=0; XCODE_INDEX '/Applications/Xcode.app'" -ln -s "/Applications/Xcode_${DEFAULT_XCODE_VERSION}.app" "/Applications/Xcode.app" - -echo "Enabling developer mode" -sudo /usr/sbin/DevToolsSecurity --enable - -echo "Setting environment variables 'XCODE__DEVELOPER_DIR'" -setXcodeDeveloperDirVariables - -echo "Doing cleanup. Emptying ${WORK_DIR}..." -rm -rf "$WORK_DIR" diff --git a/images/macos/provision/core/xcode.ps1 b/images/macos/provision/core/xcode.ps1 new file mode 100644 index 000000000..f3ea55647 --- /dev/null +++ b/images/macos/provision/core/xcode.ps1 @@ -0,0 +1,38 @@ +# The script currently requires 2 external variables to be set: XCODE_INSTALL_USER +# and XCODE_INSTALL_PASSWORD, in order to access the Apple Developer Center + +$ErrorActionPreference = "Stop" + +Import-Module "$PSScriptRoot/../../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/../../helpers/Xcode.Installer.psm1" + +if ([string]::IsNullOrEmpty($env:XCODE_INSTALL_USER) -or [string]::IsNullOrEmpty($env:XCODE_INSTALL_PASSWORD)) { + throw "Required environment variables XCODE_INSTALL_USER and XCODE_INSTALL_PASSWORD are not set" +} + +$os = Get-OSVersion +$xcodeVersions = Get-ToolsetValue "xcode.versions" +$defaultXcode = Get-ToolsetValue "xcode.default" + +& xcversion update + +Write-Host "Installing Xcode versions..." +foreach ($xcode in $xcodeVersions) { + Install-XcodeVersion -Version $xcode.version -LocalLink $xcode.localLink + Build-XcodeSymlinks -Version $xcode.localLink -Symlinks $xcode.symlinks + Build-ProvisionatorSymlink -Version $xcode.version +} + +Write-Host "Configuring Xcode versions..." +if ($os.IsLessThanCatalina) { + Install-XcodeAdditionalPackages -Version $xcodeVersions[0].localLink +} +$xcodeVersions | ForEach-Object { Invoke-XcodeRunFirstLaunch -Version $_.localLink } +Invoke-XcodeRunFirstLaunch -Version $defaultXcode + +Write-Host "Setting default Xcode to $defaultXcode" +Switch-Xcode -Version $defaultXcode +New-Item -Path "/Applications/Xcode.app" -ItemType SymbolicLink -Value (Get-XcodeRootPath -Version $defaultXcode) + +Write-Host "Setting environment variables 'XCODE__DEVELOPER_DIR'" +Set-XcodeDeveloperDirEnvironmentVariables \ No newline at end of file diff --git a/images/macos/provision/utils/xcode-utils.sh b/images/macos/provision/utils/xcode-utils.sh deleted file mode 100644 index 0105f28a1..000000000 --- a/images/macos/provision/utils/xcode-utils.sh +++ /dev/null @@ -1,118 +0,0 @@ -downloadXcode() { - local XCODE_VERSION="$1" - - VERSION_TO_INSTALL=$(resolveLatestXcodeVersion "$XCODE_VERSION") - if [[ -z "$VERSION_TO_INSTALL" ]]; then - echo "No versions were found matching $XCODE_VERSION" - exit 1 - fi - - echo "Downloading Xcode $VERSION_TO_INSTALL ..." - xcversion install "$VERSION_TO_INSTALL" --no-install -} - -resolveLatestXcodeVersion() { - local XCODE_VERSION="$1" - - if [[ $XCODE_VERSION == "12" ]]; then - echo "12.0.1" - elif [[ ! $XCODE_VERSION =~ "_beta" ]]; then - echo "${XCODE_VERSION//_/ }" - else - local XCODE_BETA="${XCODE_VERSION/_/ }" - echo "$(xcversion list | sort -r | grep -m 1 "$XCODE_BETA")" - fi -} - -validateXcodeIntegrity() { - local WORKING_DIR="$1" - local XCODE_VERSION="$2" - - if echo $XCODE_VERSION | grep "beta"; then - return 0 - fi - - spctl --assess --raw "${WORKING_DIR}/Xcode.app" -} - -approveXcodeLicense() { - local XCODE_VERSION="$1" - sudo "/Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer/usr/bin/xcodebuild" -license accept -} - -installAdditionalPackages() { - # should be called only for old High Sierra and Mojave - local XCODE_VERSION="$1" - find /Applications/Xcode_${XCODE_VERSION}.app/Contents/Resources/Packages/ -name '*.pkg' | xargs -I '{}' sudo installer -pkg '{}' -target / -allowUntrusted -} - -runFirstLaunch() { - local XCODE_VERSION="$1" - sudo "/Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer/usr/bin/xcodebuild" -runFirstLaunch -} - -runFirstLaunchXcodeList() { - local XCODE_LIST=("$@") - if is_Less_Catalina; then - echo "Install additional packages for Xcode ${XCODE_LIST[0]}" - installAdditionalPackages ${XCODE_LIST[0]} - fi - - for XCODE_VERSION in "${XCODE_LIST[@]}" - do - if [[ $XCODE_VERSION == 8* || $XCODE_VERSION == 9* ]]; then - continue - fi - - echo "Running 'runFirstLaunch' for Xcode ${XCODE_VERSION}..." - runFirstLaunch $XCODE_VERSION - done -} - -setXcodeDeveloperDirVariables() { - stable_xcode_versions=$(get_xcode_list_from_toolset | tr " " "\n" | grep -v "beta") - major_versions=($(echo ${stable_xcode_versions[@]} | tr " " "\n" | cut -d '.' -f 1 | uniq)) - for MAJOR_VERSION in "${major_versions[@]}" - do - LATEST_STABLE_VERSION=$(echo "${stable_xcode_versions[*]}" | grep "${MAJOR_VERSION}" | tail -n 1) - LATEST_STABLE_VERSION=$(echo $LATEST_STABLE_VERSION | cut -d"_" -f 1) - echo "export XCODE_${MAJOR_VERSION}_DEVELOPER_DIR=/Applications/Xcode_${LATEST_STABLE_VERSION}.app/Contents/Developer" >> "$HOME/.bashrc" - done -} - -extractXcodeXip() { - local WORKING_DIR="$1" - local XCODE_XIP=$(find "$WORKING_DIR" -name "Xcode_*.xip" | head -n1) - - pushd $WORKING_DIR - xip -x "${XCODE_XIP}" - popd - - if [[ -d "${WORKING_DIR}/Xcode-beta.app" ]]; then - mv -f "${WORKING_DIR}/Xcode-beta.app" "${WORKING_DIR}/Xcode.app" - fi - - if [[ ! -d "${WORKING_DIR}/Xcode.app" ]]; then - echo "'Xcode.app' doesn't exist after Xcode XIP extraction" - exit 1 - fi -} - -createXcodeSymlinks() { - local SOURCE_XCODE=$1 && shift - local SYMLINKS=($@) - - - for TARGET_XCODE in "${SYMLINKS[@]}" - do - echo "Creating symlink '/Applications/Xcode_${SOURCE_XCODE}.app' -> '/Applications/Xcode_${TARGET_XCODE}.app'" - ln -sf "/Applications/Xcode_${SOURCE_XCODE}.app" "/Applications/Xcode_${TARGET_XCODE}.app" - done - - # TO-DO, symlink is not correct - local FULL_XCODE_VERSION=$(echo "${XCODE_VERSION}.0.0" | cut -d'.' -f 1,2,3) - if [ $FULL_XCODE_VERSION != $SOURCE_XCODE ]; then - echo "Creating symlink '/Applications/Xcode_${SOURCE_XCODE}.app' -> '/Applications/Xcode_${FULL_XCODE_VERSION}.app'" - ln -sf "/Applications/Xcode_${SOURCE_XCODE}.app" "/Applications/Xcode_${FULL_XCODE_VERSION}.app" - fi -} \ No newline at end of file diff --git a/images/macos/software-report/SoftwareReport.Xcode.psm1 b/images/macos/software-report/SoftwareReport.Xcode.psm1 index 3d4cfa52d..8fb841933 100644 --- a/images/macos/software-report/SoftwareReport.Xcode.psm1 +++ b/images/macos/software-report/SoftwareReport.Xcode.psm1 @@ -1,4 +1,5 @@ Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/../helpers/Xcode.Helpers.psm1" $os = Get-OSVersion @@ -7,19 +8,16 @@ function Get-XcodePaths { return $xcodePaths | Select-Object -ExpandProperty Fullname } -function Get-XcodeVersionInfo { - [string]$output = Invoke-Expression "xcodebuild -version" - $versionOutputParts = $output.Split(" ") - return @{ - Version = [System.Version]::Parse($versionOutputParts[1]) - Build = $versionOutputParts[4] - } -} - function Get-XcodeSDKList { - $versionInfo = Get-XcodeVersionInfo + param( + [Parameter(Mandatory)] + [string]$XcodeRootPath + ) + + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $XcodeRootPath + $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" if ($versionInfo.Version -le [System.Version]::Parse("9.4.1")) { - $output = Invoke-Expression "xcodebuild -showsdks" + $output = Invoke-Expression "$xcodebuildPath -showsdks" $sdkList = $output | Where-Object { $_ -Match "-sdk" } return $sdkList | ForEach-Object { @@ -31,7 +29,7 @@ function Get-XcodeSDKList { } } - [string]$output = Invoke-Expression "xcodebuild -showsdks -json" + [string]$output = Invoke-Expression "$xcodebuildPath -showsdks -json" return $output | ConvertFrom-Json } @@ -43,7 +41,7 @@ function Get-XcodeInfoList { $xcodeRootPath = $_ Switch-Xcode -XcodeRootPath $xcodeRootPath - $versionInfo = Get-XcodeVersionInfo + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath $versionInfo.Path = $xcodeRootPath $versionInfo.IsDefault = ($xcodeRootPath -eq $defaultXcodeRootPath) $versionInfo.IsStable = Test-XcodeStableRelease -XcodeRootPath $xcodeRootPath diff --git a/images/macos/templates/macOS-10.15.json b/images/macos/templates/macOS-10.15.json index a65bd72ac..b7752e126 100644 --- a/images/macos/templates/macOS-10.15.json +++ b/images/macos/templates/macOS-10.15.json @@ -131,21 +131,17 @@ "scripts": [ "./provision/core/xcode-clt.sh", "./provision/core/homebrew.sh", - "./provision/core/dotnet.sh", - "./provision/core/python.sh", "./provision/core/azcopy.sh", "./provision/core/openssl.sh", "./provision/core/ruby.sh", "./provision/core/rubygem.sh", - "./provision/core/powershell.sh", - "./provision/core/git.sh", - "./provision/core/node.sh" + "./provision/core/powershell.sh" ] }, { "type": "shell", - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} {{ .Path }}", - "script": "./provision/core/xcode-tools.sh", + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} pwsh -f {{ .Path }}", + "script": "./provision/core/xcode.ps1", "environment_vars": [ "XCODE_INSTALL_USER={{user `xcode_install_user`}}", "XCODE_INSTALL_PASSWORD={{user `xcode_install_password`}}" diff --git a/images/macos/toolsets/toolset-10.15.json b/images/macos/toolsets/toolset-10.15.json index 94568cd00..21b098283 100644 --- a/images/macos/toolsets/toolset-10.15.json +++ b/images/macos/toolsets/toolset-10.15.json @@ -2,18 +2,18 @@ "xcode": { "default": "12", "versions": [ - { "localVersion": "12.2", "version": "12.2_beta" }, - { "localVersion": "12.1", "version": "12.1_GM_seed" }, - { "localVersion": "12.0", "version": "12.0.1", "symlinks": ["12"] }, - { "localVersion": "11.7", "version": "11.7", "symlinks": ["11.7_beta"] }, - { "localVersion": "11.6", "version": "11.6", "symlinks": ["11.6_beta"] }, - { "localVersion": "11.5", "version": "11.5", "symlinks": ["11.5_beta"] }, - { "localVersion": "11.4", "version": "11.4.1", "symlinks": ["11.4_beta"] }, - { "localVersion": "11.3", "version": "11.3.1", "symlinks": ["11.3", "11.3_beta"] }, - { "localVersion": "11.2", "version": "11.2.1", "symlinks": ["11.2", "11.2_beta"] }, - { "localVersion": "11.1", "version": "11.1", "symlinks": ["11.1_beta"] }, - { "localVersion": "11.0", "version": "11", "symlinks": ["11"] }, - { "localVersion": "10.3", "version": "10.3", "symlinks": ["10.3_beta"] } + { "link": "12.2", "version": "12.2 beta 3" }, + { "link": "12.1", "version": "12.1.1 Release Candidate" }, + { "link": "12", "version": "12.0.1" }, + { "link": "11.7", "version": "11.7", "symlinks": ["11.7_beta"] }, + { "link": "11.6", "version": "11.6", "symlinks": ["11.6_beta"] }, + { "link": "11.5", "version": "11.5", "symlinks": ["11.5_beta"] }, + { "link": "11.4.1", "version": "11.4.1", "symlinks": ["11.4", "11.4_beta"] }, + { "link": "11.3.1", "version": "11.3.1", "symlinks": ["11.3", "11.3_beta"] }, + { "link": "11.2.1", "version": "11.2.1", "symlinks": ["11.2", "11.2_beta"] }, + { "link": "11.1", "version": "11.1", "symlinks": ["11.1_beta"] }, + { "link": "11", "version": "11" }, + { "link": "10.3", "version": "10.3", "symlinks": ["10.3_beta"] } ] }, "xamarin": {