diff --git a/README.md b/README.md index a8a7eb6..dd9cc9b 100644 --- a/README.md +++ b/README.md @@ -28,19 +28,19 @@ Find all of your Unity installs: Get-UnitySetupInstance # Example output: -# Version Components Path -# ------- ---------- ---- -# 2017.1.2f1 Setup, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.1.2f1\ -# 2017.1.3f1 Setup, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.1.3f1\ -# 2017.2.1f1 Setup, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.2.1f1\ -# 2017.3.1f1 Setup, Metro, UWP_IL2CPP, Linux, Vuforia C:\Program Files\Unity-2017.3.1f1\ -# 2018.1.0b4 Setup, Metro, UWP_IL2CPP, Vuforia C:\Program Files\Unity-2018.1.0b4\ -# 2018.1.0b8 All C:\Program Files\Unity-2018.1.0b8\ -# 2017.1.0p5 Setup, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.0p5\ -# 2017.1.1f1 Setup, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.1f1\ -# 2017.1.1p3 Setup, StandardAssets, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.1p3\ -# 2017.2.0f3 Setup, Metro, UWP_IL2CPP, Vuforia C:\Program Files\Unity.2017.2.0f3\ -# 2017.3.0f3 Setup, Metro, UWP_IL2CPP, Mac, Vuforia C:\Program Files\Unity.2017.3.0f3\ +# Version Components Path +# ------- ---------- ---- +# 2017.1.2f1 Windows, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.1.2f1\ +# 2017.1.3f1 Windows, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.1.3f1\ +# 2017.2.1f1 Windows, Metro, UWP_IL2CPP C:\Program Files\Unity-2017.2.1f1\ +# 2017.3.1f1 Windows, Metro, UWP_IL2CPP, Linux, Vuforia C:\Program Files\Unity-2017.3.1f1\ +# 2018.1.0b4 Windows, Metro, UWP_IL2CPP, Vuforia C:\Program Files\Unity-2018.1.0b4\ +# 2018.1.0b8 All C:\Program Files\Unity-2018.1.0b8\ +# 2017.1.0p5 Windows, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.0p5\ +# 2017.1.1f1 Windows, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.1f1\ +# 2017.1.1p3 Windows, StandardAssets, Metro, UWP_IL2CPP C:\Program Files\Unity.2017.1.1p3\ +# 2017.2.0f3 Windows, Metro, UWP_IL2CPP, Vuforia C:\Program Files\Unity.2017.2.0f3\ +# 2017.3.0f3 Windows, Metro, UWP_IL2CPP, Mac, Vuforia C:\Program Files\Unity.2017.3.0f3\ ``` Select the Unity installs that you want: @@ -78,31 +78,32 @@ Find the installers for a particular version: Find-UnitySetupInstaller -Version '2017.3.0f3' | Format-Table # Example output: -# ComponentType Version Length LastModified DownloadUrl -# ------------- ------- ------ ------------ ----------- -# Setup 2017.3.0f3 553688024 2017-12-18 08:15:20 https://netstorage.unity3d.com/unity/... -# Documentation 2017.3.0f3 358911256 2017-12-18 08:18:37 https://netstorage.unity3d.com/unity/... -# StandardAssets 2017.3.0f3 189886032 2017-12-18 08:15:52 https://netstorage.unity3d.com/unity/... -# Metro 2017.3.0f3 172298008 2017-12-18 08:17:44 https://netstorage.unity3d.com/unity/... -# UWP_IL2CPP 2017.3.0f3 152933480 2017-12-18 08:17:55 https://netstorage.unity3d.com/unity/... -# Android 2017.3.0f3 194240888 2017-12-18 08:16:06 https://netstorage.unity3d.com/unity/... -# iOS 2017.3.0f3 802853872 2017-12-18 08:17:21 https://netstorage.unity3d.com/unity/... -# AppleTV 2017.3.0f3 273433528 2017-12-18 08:16:24 https://netstorage.unity3d.com/unity/... -# Facebook 2017.3.0f3 32131560 2017-12-18 08:16:27 https://netstorage.unity3d.com/unity/... -# Linux 2017.3.0f3 122271984 2017-12-18 08:17:30 https://netstorage.unity3d.com/unity/... -# Vuforia 2017.3.0f3 65677296 2017-12-18 08:18:00 https://netstorage.unity3d.com/unity/... -# WebGL 2017.3.0f3 134133288 2017-12-18 08:18:09 https://netstorage.unity3d.com/unity/... +# ComponentType Version Length LastModified DownloadUrl +# ------------- ------- ------ ------------ ----------- +# Windows 2017.3.0f3 553688024 12/18/2017 8:05:31 AM https://download.unity3d.com/download_unity/... +# Linux 2017.3.0f3 122271984 12/18/2017 8:06:53 AM https://download.unity3d.com/download_unity/... +# Mac 2017.3.0f3 28103888 12/18/2017 8:06:53 AM https://download.unity3d.com/download_unity/... +# Documentation 2017.3.0f3 358911256 12/18/2017 8:07:34 AM https://download.unity3d.com/download_unity/... +# StandardAssets 2017.3.0f3 189886032 12/18/2017 8:05:50 AM https://download.unity3d.com/download_unity/... +# Metro 2017.3.0f3 172298008 12/18/2017 8:07:04 AM https://download.unity3d.com/download_unity/... +# UWP_IL2CPP 2017.3.0f3 152933480 12/18/2017 8:07:10 AM https://download.unity3d.com/download_unity/... +# Android 2017.3.0f3 194240888 12/18/2017 8:05:58 AM https://download.unity3d.com/download_unity/... +# iOS 2017.3.0f3 802853872 12/18/2017 8:06:46 AM https://download.unity3d.com/download_unity/... +# AppleTV 2017.3.0f3 273433528 12/18/2017 8:06:09 AM https://download.unity3d.com/download_unity/... +# Facebook 2017.3.0f3 32131560 12/18/2017 8:06:12 AM https://download.unity3d.com/download_unity/... +# Vuforia 2017.3.0f3 65677296 12/18/2017 8:07:12 AM https://download.unity3d.com/download_unity/... +# WebGL 2017.3.0f3 134133288 12/18/2017 8:07:19 AM https://download.unity3d.com/download_unity/... ``` Limit what components you search for: ```powershell -Find-UnitySetupInstaller -Version 2017.3.0f3 -Components 'Setup','Documentation' | Format-Table +Find-UnitySetupInstaller -Version 2017.3.0f3 -Components 'Windows','Documentation' | Format-Table # Example output: -# ComponentType Version Length LastModified DownloadUrl -# ------------- ------- ------ ------------ ----------- -# Setup 2017.3.0f3 553688024 2017-12-18 08:15:20 https://netstorage.unity3d.com/unity/... -# Documentation 2017.3.0f3 358911256 2017-12-18 08:18:37 https://netstorage.unity3d.com/unity/... +# ComponentType Version Length LastModified DownloadUrl +# ------------- ------- ------ ------------ ----------- +# Windows 2017.3.0f3 553688024 12/18/2017 8:05:31 AM https://download.unity3d.com/download_unity/... +# Documentation 2017.3.0f3 358911256 12/18/2017 8:07:34 AM https://download.unity3d.com/download_unity/... ``` Install UnitySetup instances: @@ -114,6 +115,28 @@ Find-UnitySetupInstaller -Version '2017.3.0f3' | Install-UnitySetupInstance Install-UnitySetupInstance -Installers (Find-UnitySetupInstaller -Version '2017.3.0f3') ``` +Manage Unity licenses. +```powershell +# Get any active licenses +Get-UnityLicense + +# Example Output: +# LicenseVersion : 6.x +# Serial : System.Security.SecureString +# UnityVersion : 2017.4.2f2 +# DisplaySerial : AB-CDEF-GHIJ-KLMN-OPQR-XXXX +# ActivationDate : 2017-07-13 16:32:16 +# StartDate : 2017-07-12 00:00:00 +# StopDate : 2019-01-01 00:00:00 +# UpdateDate : 2018-05-11 23:47:10 + +# Activate a license +Start-UnityEditor -Credential -Serial -Wait + +# Return license +Start-UnityEditor -Credential -ReturnLicense -Wait +``` + ### DSC UnitySetup includes the xUnitySetupInstance DSC Resource. An example configuration might look like: @@ -122,15 +145,28 @@ UnitySetup includes the xUnitySetupInstance DSC Resource. An example configurati Install multiple versions of Unity and several components #> Configuration Sample_xUnitySetupInstance_Install { + param( + [PSCredential]$UnityCredential, + [PSCredential]$UnitySerial + ) Import-DscResource -ModuleName UnitySetup Node 'localhost' { xUnitySetupInstance Unity { - Versions = '2017.3.1f1,2018.1.0b9' - Components = 'Setup', 'Mac', 'Linux', 'Metro', 'iOS' + Versions = '2017.4.2f2,2018.1.0f2' + Components = 'Windows', 'Mac', 'Linux', 'Metro', 'iOS' + Ensure = 'Present' + } + + xUnityLicense UnityLicense { + Name = 'UL01' + Credential = $UnityCredential + Serial = $UnitySerial Ensure = 'Present' + UnityVersion = '2017.4.2f2' + DependsOn = '[xUnitySetupInstance]Unity' } } } @@ -148,4 +184,8 @@ This project welcomes contributions and suggestions. Most contributions require When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. \ No newline at end of file +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +# Reporting Security Issues + +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). \ No newline at end of file diff --git a/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.psm1 b/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.psm1 new file mode 100644 index 0000000..f06cac5 --- /dev/null +++ b/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.psm1 @@ -0,0 +1,109 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +function Get-TargetResource { + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential, + + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Serial, + + [parameter(Mandatory = $false)] + [System.String] + $UnityVersion, + + [parameter(Mandatory = $true)] + [System.String] + $Name + ) + + @{ 'Licenses' = (Get-UnityLicense) } +} + + +function Set-TargetResource { + [CmdletBinding()] + param + ( + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential, + + [ValidateSet("Present", "Absent")] + [System.String] + $Ensure = 'Present', + + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Serial, + + [System.String] + $UnityVersion, + + [parameter(Mandatory = $true)] + [System.String] + $Name + ) + + if ( Test-TargetResource @PSBoundParameters ) { return } + + $unityArgs = @{ + 'Credential' = $Credential + 'Wait' = $true + } + + if ( $UnityVersion ) { $unityArgs['Version'] = $UnityVersion } + if ( $Ensure -eq 'Present' ) { $unityArgs['Serial'] = $Serial.Password } + else { $unityArgs['ReturnLicense'] = $true } + + Start-UnityEditor @unityArgs -Verbose +} + + +function Test-TargetResource { + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential, + + [ValidateSet("Present", "Absent")] + [System.String] + $Ensure = 'Present', + + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Serial, + + [parameter(Mandatory = $false)] + [System.String] + $UnityVersion, + + [parameter(Mandatory = $true)] + [System.String] + $Name + ) + + foreach ( $license in (Get-UnityLicense -Serial $Serial.Password) ) { + Write-Verbose "Found license: $license" + + $currentSerial = [System.Net.NetworkCredential]::new($null, $license.Serial).Password + $passedSerial = $Serial.GetNetworkCredential().Password + if ( $currentSerial -ne $passedSerial ) { continue } + + return $Ensure -eq 'Present' + } + + return $Ensure -eq 'Absent' +} + + +Export-ModuleMember -Function *-TargetResource + diff --git a/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.schema.mof b/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.schema.mof new file mode 100644 index 0000000..3394e96 --- /dev/null +++ b/UnitySetup/DSCResources/xUnityLicense/xUnityLicense.schema.mof @@ -0,0 +1,11 @@ + +[ClassVersion("1.0.0.0"), FriendlyName("xUnityLicense")] +class xUnityLicense : OMI_BaseResource +{ + [Key] string Name; + [Required, EmbeddedInstance("MSFT_Credential")] String Credential; + [Required, EmbeddedInstance("MSFT_Credential")] String Serial; + [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; + [Write] string UnityVersion; +}; + diff --git a/UnitySetup/DSCResources/xUnitySetupInstance/xUnitySetupInstance.psm1 b/UnitySetup/DSCResources/xUnitySetupInstance/xUnitySetupInstance.psm1 index f23990f..f6fb73c 100644 --- a/UnitySetup/DSCResources/xUnitySetupInstance/xUnitySetupInstance.psm1 +++ b/UnitySetup/DSCResources/xUnitySetupInstance/xUnitySetupInstance.psm1 @@ -1,4 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. <# .Synopsis Returns the status of Unity installs and the set of their overlapping components. diff --git a/UnitySetup/Examples/Sample_xUnity.ps1 b/UnitySetup/Examples/Sample_xUnity.ps1 new file mode 100644 index 0000000..68c4dce --- /dev/null +++ b/UnitySetup/Examples/Sample_xUnity.ps1 @@ -0,0 +1,44 @@ +<# + Create a custom configuration by passing in necessary values +#> +Configuration Sample_xUnity { + param + ( + [System.String] + $Version = '2017.4.2f2', + + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [System.String[]] + $Components = @('Windows', 'Mac', 'Linux', 'Metro', 'iOS', 'Android'), + + [PSCredential] + $UnityCredential, + + [PSCredential] + $UnitySerial + ) + + Import-DscResource -ModuleName UnitySetup + + Node 'localhost' { + + xUnitySetupInstance Unity { + Versions = $Version + Components = $Components + Ensure = $Ensure + DependsOn = if( $Ensure -eq 'Absent' ) { '[xUnityLicense]UnityLicense' } else { $null } + } + + xUnityLicense UnityLicense { + Name = 'UL01' + Credential = $UnityCredential + Serial = $UnitySerial + Ensure = $Ensure + UnityVersion = $Version + DependsOn = if( $Ensure -eq 'Present' ) { '[xUnitySetupInstance]Unity' } else { $null } + } + } +} \ No newline at end of file diff --git a/UnitySetup/Examples/Sample_xUnitySetupInstance.ps1 b/UnitySetup/Examples/Sample_xUnitySetupInstance.ps1 deleted file mode 100644 index 7280942..0000000 --- a/UnitySetup/Examples/Sample_xUnitySetupInstance.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -<# - Create a custom configuration by passing in necessary values -#> -Configuration Sample_xUnitySetupInstance { - param - ( - [System.String] - $Versions = '2017.3.0f3,2018.1.0b9', - - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure = 'Present', - - [System.String[]] - $Components = @('Setup', 'Linux', 'Metro', 'Mac', 'iOS') - ) - - Import-DscResource -ModuleName UnitySetup - - Node 'localhost' { - - xUnitySetupInstance Unity { - Versions = $Versions - Components = $Components - Ensure = $Ensure - } - } -} \ No newline at end of file diff --git a/UnitySetup/Examples/Sample_xUnitySetupInstance_Install.ps1 b/UnitySetup/Examples/Sample_xUnitySetupInstance_Install.ps1 deleted file mode 100644 index 7763dc3..0000000 --- a/UnitySetup/Examples/Sample_xUnitySetupInstance_Install.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -<# - Install multiple versions of Unity and several components -#> -Configuration Sample_xUnitySetupInstance_Install { - - Import-DscResource -ModuleName UnitySetup - - Node 'localhost' { - - xUnitySetupInstance Unity { - Versions = '2017.3.1f1,2018.1.0b9' - Components = 'Setup', 'Mac', 'Linux', 'Metro', 'iOS' - Ensure = 'Present' - } - } -} \ No newline at end of file diff --git a/UnitySetup/Examples/Sample_xUnitySetupInstance_Uninstall.ps1 b/UnitySetup/Examples/Sample_xUnitySetupInstance_Uninstall.ps1 deleted file mode 100644 index c5f0764..0000000 --- a/UnitySetup/Examples/Sample_xUnitySetupInstance_Uninstall.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -<# - Uninstall multiple versions of Unity -#> -Configuration Sample_xUnitySetupInstance_Install { - - Import-DscResource -ModuleName UnitySetup - - Node 'localhost' { - - xUnitySetupInstance Unity { - Versions = '2017.3.1f1,2018.1.0b9' - Ensure = 'Absent' - } - } -} \ No newline at end of file diff --git a/UnitySetup/Examples/Sample_xUnity_Install.ps1 b/UnitySetup/Examples/Sample_xUnity_Install.ps1 new file mode 100644 index 0000000..866f004 --- /dev/null +++ b/UnitySetup/Examples/Sample_xUnity_Install.ps1 @@ -0,0 +1,30 @@ +<# + Install multiple versions of Unity and several components +#> +Configuration Sample_xUnity_Install { + + param( + [PSCredential]$UnityCredential, + [PSCredential]$UnitySerial + ) + + Import-DscResource -ModuleName UnitySetup + + Node 'localhost' { + + xUnitySetupInstance Unity { + Versions = '2017.4.2f2' + Components = 'Windows', 'Mac', 'Linux', 'Metro', 'iOS', 'Android' + Ensure = 'Present' + } + + xUnityLicense UnityLicense { + Name = 'UL01' + Credential = $UnityCredential + Serial = $UnitySerial + Ensure = 'Present' + UnityVersion = '2017.4.2f2' + DependsOn = '[xUnitySetupInstance]Unity' + } + } +} \ No newline at end of file diff --git a/UnitySetup/Examples/Sample_xUnity_Uninstall.ps1 b/UnitySetup/Examples/Sample_xUnity_Uninstall.ps1 new file mode 100644 index 0000000..95d153f --- /dev/null +++ b/UnitySetup/Examples/Sample_xUnity_Uninstall.ps1 @@ -0,0 +1,29 @@ +<# + Uninstall multiple versions of Unity +#> +Configuration Sample_xUnity_Install { + + param( + [PSCredential]$UnityCredential, + [PSCredential]$UnitySerial + ) + + Import-DscResource -ModuleName UnitySetup + + Node 'localhost' { + + xUnitySetupInstance Unity { + Versions = '2017.4.2f2' + Ensure = 'Absent' + DependsOn = '[xUnityLicense]UnityLicense' + } + + xUnityLicense UnityLicense { + Name = 'UL01' + Credential = $UnityCredential + Serial = $UnitySerial + Ensure = 'Absent' + UnityVersion = '2017.4.2f2' + } + } +} \ No newline at end of file diff --git a/UnitySetup/UnitySetup.psd1 b/UnitySetup/UnitySetup.psd1 index d5b8271..ad94d75 100644 --- a/UnitySetup/UnitySetup.psd1 +++ b/UnitySetup/UnitySetup.psd1 @@ -14,7 +14,7 @@ RootModule = 'UnitySetup' # Version number of this module. - ModuleVersion = '3.0' + ModuleVersion = '4.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -82,7 +82,8 @@ 'Select-UnitySetupInstance', 'Uninstall-UnitySetupInstance', 'Start-UnityEditor', - 'ConvertTo-UnitySetupComponent' + 'ConvertTo-UnitySetupComponent', + 'Get-UnityLicense' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/UnitySetup/UnitySetup.psm1 b/UnitySetup/UnitySetup.psm1 index bfef146..39002e9 100644 --- a/UnitySetup/UnitySetup.psm1 +++ b/UnitySetup/UnitySetup.psm1 @@ -2,25 +2,32 @@ # Licensed under the MIT License. Import-Module powershell-yaml -MinimumVersion '0.3' -ErrorAction Stop -[Flags()] +[Flags()] enum UnitySetupComponent { - Setup = (1 -shl 0) - Documentation = (1 -shl 1) - StandardAssets = (1 -shl 2) - Windows_IL2CPP = (1 -shl 3) - Metro = (1 -shl 4) - UWP_IL2CPP = (1 -shl 5) - Android = (1 -shl 6) - iOS = (1 -shl 7) - AppleTV = (1 -shl 8) - Facebook = (1 -shl 9) - Linux = (1 -shl 10) - Mac = (1 -shl 11) + Windows = (1 -shl 0) + Linux = (1 -shl 1) + Mac = (1 -shl 2) + Documentation = (1 -shl 3) + StandardAssets = (1 -shl 4) + Windows_IL2CPP = (1 -shl 5) + Metro = (1 -shl 6) + UWP_IL2CPP = (1 -shl 7) + Android = (1 -shl 8) + iOS = (1 -shl 9) + AppleTV = (1 -shl 10) + Facebook = (1 -shl 11) Vuforia = (1 -shl 12) WebGL = (1 -shl 13) All = (1 -shl 14) - 1 } +[Flags()] +enum OperatingSystem { + Windows + Linux + Mac +} + class UnitySetupInstaller { [UnitySetupComponent] $ComponentType [UnityVersion] $Version @@ -33,38 +40,68 @@ class UnitySetupInstance { [UnityVersion]$Version [UnitySetupComponent]$Components [string]$Path - + UnitySetupInstance([string]$path) { - - $ivyPath = [io.path]::Combine("$path", 'Editor\Data\UnityExtensions\Unity\Networking\ivy.xml'); + + $currentOS = Get-OperatingSystem + $ivyPath = switch ($currentOS) { + ([OperatingSystem]::Windows) { 'Editor\Data\UnityExtensions\Unity\Networking\ivy.xml' } + ([OperatingSystem]::Linux) { throw "UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!"; } + ([OperatingSystem]::Mac) { 'Unity.app/Contents/UnityExtensions/Unity/Networking/ivy.xml' } + } + + $ivyPath = [io.path]::Combine("$path", $ivyPath); if (!(Test-Path $ivyPath)) { throw "Path is not a Unity setup: $path"} [xml]$xmlDoc = Get-Content $ivyPath if ( !($xmlDoc.'ivy-module'.info.unityVersion)) { throw "Unity setup ivy is missing version: $ivyPath" - } + } $this.Path = $path $this.Version = $xmlDoc.'ivy-module'.info.unityVersion - $this.Components = [UnitySetupComponent]::Setup - - $componentTests = @{ - [UnitySetupComponent]::Documentation = , "$Path\Editor\Data\Documentation"; - [UnitySetupComponent]::StandardAssets = , "$Path\Editor\Standard Assets"; - [UnitySetupComponent]::Windows_IL2CPP = , "$Path\Editor\Data\PlaybackEngines\windowsstandalonesupport\Variations\win32_development_il2cpp"; - [UnitySetupComponent]::Metro = "$Path\Editor\Data\PlaybackEngines\MetroSupport\Templates\UWP_.NET_D3D", - "$Path\Editor\Data\PlaybackEngines\MetroSupport\Templates\UWP_D3D"; - [UnitySetupComponent]::UWP_IL2CPP = , "$Path\Editor\Data\PlaybackEngines\MetroSupport\Templates\UWP_IL2CPP_D3D"; - [UnitySetupComponent]::Android = , "$Path\Editor\Data\PlaybackEngines\AndroidPlayer"; - [UnitySetupComponent]::iOS = , "$Path\Editor\Data\PlaybackEngines\iOSSupport"; - [UnitySetupComponent]::AppleTV = , "$Path\Editor\Data\PlaybackEngines\AppleTVSupport"; - [UnitySetupComponent]::Facebook = , "$Path\Editor\Data\PlaybackEngines\Facebook"; - [UnitySetupComponent]::Linux = , "$Path\Editor\Data\PlaybackEngines\LinuxStandaloneSupport"; - [UnitySetupComponent]::Mac = , "$Path\Editor\Data\PlaybackEngines\MacStandaloneSupport"; - [UnitySetupComponent]::Vuforia = , "$Path\Editor\Data\PlaybackEngines\VuforiaSupport"; - [UnitySetupComponent]::WebGL = , "$Path\Editor\Data\PlaybackEngines\WebGLSupport"; + + $playbackEnginePath = $null + $componentTests = switch ($currentOS) { + ([OperatingSystem]::Windows) { + $this.Components = [UnitySetupComponent]::Windows + $playbackEnginePath = [io.path]::Combine("$Path", "Editor\Data\PlaybackEngines"); + @{ + [UnitySetupComponent]::Documentation = , [io.path]::Combine("$Path", "Editor\Data\Documentation"); + [UnitySetupComponent]::StandardAssets = , [io.path]::Combine("$Path", "Editor\Standard Assets"); + [UnitySetupComponent]::Windows_IL2CPP = , [io.path]::Combine("$playbackEnginePath", "windowsstandalonesupport\Variations\win32_development_il2cpp"); + [UnitySetupComponent]::Metro = [io.path]::Combine("$playbackEnginePath", "MetroSupport\Templates\UWP_.NET_D3D"), + [io.path]::Combine("$playbackEnginePath", "MetroSupport\Templates\UWP_D3D"); + [UnitySetupComponent]::UWP_IL2CPP = , [io.path]::Combine("$playbackEnginePath", "MetroSupport\Templates\UWP_IL2CPP_D3D"); + [UnitySetupComponent]::Linux = , [io.path]::Combine("$playbackEnginePath", "LinuxStandaloneSupport"); + [UnitySetupComponent]::Mac = , [io.path]::Combine("$playbackEnginePath", "MacStandaloneSupport"); + } + } + ([OperatingSystem]::Linux) { + $this.Components = [UnitySetupComponent]::Linux + + throw "UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!"; + } + ([OperatingSystem]::Mac) { + $this.Components = [UnitySetupComponent]::Mac + $playbackEnginePath = [io.path]::Combine("$Path", "PlaybackEngines"); + @{ + [UnitySetupComponent]::Documentation = , [io.path]::Combine("$Path", "Documentation"); + [UnitySetupComponent]::StandardAssets = , [io.path]::Combine("$Path", "Standard Assets"); + [UnitySetupComponent]::Windows = , [io.path]::Combine("$playbackEnginePath", "WindowsStandaloneSupport"); + [UnitySetupComponent]::Linux = , [io.path]::Combine("$playbackEnginePath", "LinuxStandaloneSupport"); + } + } } + # Common playback engines: + $componentTests[[UnitySetupComponent]::Android] = , [io.path]::Combine("$playbackEnginePath", "AndroidPlayer"); + $componentTests[[UnitySetupComponent]::iOS] = , [io.path]::Combine("$playbackEnginePath", "iOSSupport"); + $componentTests[[UnitySetupComponent]::AppleTV] = , [io.path]::Combine("$playbackEnginePath", "AppleTVSupport"); + $componentTests[[UnitySetupComponent]::Facebook] = , [io.path]::Combine("$playbackEnginePath", "Facebook"); + $componentTests[[UnitySetupComponent]::Vuforia] = , [io.path]::Combine("$playbackEnginePath", "VuforiaSupport"); + $componentTests[[UnitySetupComponent]::WebGL] = , [io.path]::Combine("$playbackEnginePath", "WebGLSupport"); + $componentTests.Keys | ForEach-Object { foreach ( $test in $componentTests[$_] ) { if ( Test-Path -PathType Container -Path $test ) { @@ -77,16 +114,16 @@ class UnitySetupInstance { } class UnityProjectInstance { - [UnityVersion]$Version + [UnityVersion]$Version [string]$Path - + UnityProjectInstance([string]$path) { $versionFile = [io.path]::Combine($path, "ProjectSettings\ProjectVersion.txt") if (!(Test-Path $versionFile)) { throw "Path is not a Unity project: $path"} $fileVersion = (Get-Content $versionFile -Raw | ConvertFrom-Yaml)['m_EditorVersion']; if (!$fileVersion) { throw "Project is missing a version in: $versionFile"} - + $this.Path = $path $this.Version = $fileVersion } @@ -110,13 +147,13 @@ class UnityVersion : System.IComparable { $parts = $version.Split('-') $parts[0] -match "(\d+)\.(\d+)\.(\d+)([fpb])(\d+)" | Out-Null - if ( $Matches.Count -ne 6 ) { throw "Invalid unity version: $version" } + if ( $Matches.Count -ne 6 ) { throw "Invalid unity version: $version" } $this.Major = [int]($Matches[1]); $this.Minor = [int]($Matches[2]); $this.Revision = [int]($Matches[3]); $this.Release = [char]($Matches[4]); $this.Build = [int]($Matches[5]); - + if ($parts.Length -gt 1) { $this.Suffix = $parts[1]; } @@ -125,20 +162,20 @@ class UnityVersion : System.IComparable { [int] CompareTo([object]$obj) { if ($null -eq $obj) { return 1 } if ($obj -isnot [UnityVersion]) { throw "Object is not a UnityVersion"} - + return [UnityVersion]::Compare($this, $obj) } static [int] Compare([UnityVersion]$a, [UnityVersion]$b) { if ($a.Major -lt $b.Major) { return -1 } if ($a.Major -gt $b.Major) { return 1 } - + if ($a.Minor -lt $b.Minor) { return -1 } if ($a.Minor -gt $b.Minor) { return 1 } - + if ($a.Revision -lt $b.Revision) { return -1 } if ($a.Revision -gt $b.Revision) { return 1 } - + if ($a.Release -lt $b.Release) { return -1 } if ($a.Release -gt $b.Release) { return 1 } @@ -154,11 +191,51 @@ class UnityVersion : System.IComparable { <# .Synopsis - Help to create UnitySetupComponent + Easy way to determine the current operating system platform being executed on. +.DESCRIPTION + Determine which operating system that's executing the script for things like path variants. +.OUTPUTS + Get-OperatingSystem returns a [OperatingSystem] enumeration based off the Powershell platform being run on. +.EXAMPLE + $OS = Get-OperatingSystem +.EXAMPLE + # Loosely typed. + switch (Get-OperatingSystem) { + Windows { echo "On Windows" } + Linux { echo "On Linux" } + Mac { echo "On Mac" } + } +.EXAMPLE + # Strongly typed. + switch (Get-OperatingSystem) { + ([OperatingSystem]::Windows) { echo "On Windows" } + ([OperatingSystem]::Linux) { echo "On Linux" } + ([OperatingSystem]::Mac) { echo "On Mac" } + } +.EXAMPLE + if (Get-OperatingSystem == [OperatingSystem]::Linux) { + echo "On Linux" + } +#> +function Get-OperatingSystem { + if ((-not $global:PSVersionTable.Platform) -or ($global:PSVersionTable.Platform -eq "Win32NT")) { + return [OperatingSystem]::Windows + } + elseif ($global:PSVersionTable.OS.Contains("Linux")) { + return [OperatingSystem]::Linux + } + elseif ($global:PSVersionTable.OS.Contains("Darwin")) { + return [OperatingSystem]::Mac + } +} + +<# +.Synopsis + Help to create UnitySetupComponent .PARAMETER Components What components would you like included? .EXAMPLE - ConvertTo-UnitySetupComponent Setup,Metro + ConvertTo-UnitySetupComponent Windows,Metro #> function ConvertTo-UnitySetupComponent { [CmdletBinding()] @@ -182,7 +259,7 @@ function ConvertTo-UnitySetupComponent { .EXAMPLE Find-UnitySetupInstaller -Version 2017.3.0f3 .EXAMPLE - Find-UnitySetupInstaller -Version 2017.3.0f3 -Components Setup,Documentation + Find-UnitySetupInstaller -Version 2017.3.0f3 -Components Windows,Documentation #> function Find-UnitySetupInstaller { [CmdletBinding()] @@ -202,7 +279,6 @@ function Find-UnitySetupInstaller { ) $installerTemplates = @{ - [UnitySetupComponent]::Setup = , "Windows64EditorInstaller/UnitySetup64-$Version.exe"; [UnitySetupComponent]::Documentation = , "WindowsDocumentationInstaller/UnityDocumentationSetup-$Version.exe"; [UnitySetupComponent]::StandardAssets = , "WindowsStandardAssetsInstaller/UnityStandardAssetsSetup-$Version.exe"; [UnitySetupComponent]::Metro = , "TargetSupportInstaller/UnitySetup-Metro-Support-for-Editor-$Version.exe"; @@ -219,6 +295,26 @@ function Find-UnitySetupInstaller { [UnitySetupComponent]::Windows_IL2CPP = , "TargetSupportInstaller/UnitySetup-Windows-IL2CPP-Support-for-Editor-$Version.exe"; } + $currentOS = Get-OperatingSystem + switch ($currentOS) { + ([OperatingSystem]::Windows) { + $setupComponent = [UnitySetupComponent]::Windows + $installerTemplates[$setupComponent] = , "Windows64EditorInstaller/UnitySetup64-$Version.exe"; + } + ([OperatingSystem]::Linux) { + $setupComponent = [UnitySetupComponent]::Linux + # TODO: $installerTemplates[$setupComponent] = , "???/UnitySetup64-$Version.exe"; + + throw "Find-UnitySetupInstaller has not been implemented on the Linux platform. Contributions welcomed!"; + } + ([OperatingSystem]::Mac) { + $setupComponent = [UnitySetupComponent]::Mac + # TODO: $installerTemplates[$setupComponent] = , "???/UnitySetup64-$Version.exe"; + + throw "Find-UnitySetupInstaller has not been implemented on the Mac platform. Contributions welcomed!"; + } + } + # By default Tls12 protocol is not enabled, but is what backs Unity's website, so enable it $secProtocol = [System.Net.ServicePointManager]::SecurityProtocol if ( ($secProtocol -band [System.Net.SecurityProtocolType]::Tls12) -eq 0 ) { @@ -244,13 +340,13 @@ function Find-UnitySetupInstaller { foreach ($page in $searchPages) { $webResult = Invoke-WebRequest $page -UseBasicParsing - $prototypeLink = $webResult.Links | Select-Object -ExpandProperty href -ErrorAction SilentlyContinue | Where-Object { - $_ -match "$($installerTemplates[[UnitySetupComponent]::Setup])$" + $prototypeLink = $webResult.Links | Select-Object -ExpandProperty href -ErrorAction SilentlyContinue | Where-Object { + $_ -match "$($installerTemplates[$setupComponent])$" } if ($null -ne $prototypeLink) { break } } - + if ($null -eq $prototypeLink) { throw "Could not find archives for Unity version $Version" } @@ -263,7 +359,7 @@ function Find-UnitySetupInstaller { else { $knownBaseUrls = $knownBaseUrls | Sort-Object -Property @{ Expression = {[math]::Abs(($_.CompareTo($linkComponents[0])))}; Ascending = $true} } - + $installerTemplates.Keys | Where-Object { $Components -band $_ } | ForEach-Object { $templates = $installerTemplates.Item($_); $result = $null @@ -315,7 +411,6 @@ function Find-UnitySetupInstaller { #> function Install-UnitySetupInstance { [CmdletBinding()] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Scope = 'Function')] param( [parameter(ValueFromPipeline = $true)] [UnitySetupInstaller[]] $Installers, @@ -353,7 +448,7 @@ function Install-UnitySetupInstance { $localDestinations += , "C:\Program Files\Unity-$($i.Version)" } - if ( Test-Path $destPath ) { + if ( Test-Path $destPath ) { $destItem = Get-Item $destPath if ( ($destItem.Length -eq $i.Length ) -and ($destItem.LastWriteTime -eq $i.LastModified) ) { Write-Verbose "Skipping download because it's already in the cache: $($i.DownloadUrl)" @@ -373,38 +468,30 @@ function Install-UnitySetupInstance { New-Item "$destDirectory" -ItemType Directory | Out-Null } } - + Start-BitsTransfer -Source $downloadSource -Destination $downloadDest } - $spins = @('|', '/', '-', '\') for ($i = 0; $i -lt $localInstallers.Length; $i++) { $installer = $localInstallers[$i] $destination = $localDestinations[$i] $startProcessArgs = @{ 'FilePath' = $installer; - 'ArgumentList' = @("/S", "/D=$($localDestinations[$i])"); + 'ArgumentList' = @("/S", "/D=$destination"); 'PassThru' = $true; - } - - if ($Verb) { - $startProcessArgs['Verb'] = $Verb + 'Wait' = $true; } - $spinnerIndex = 0 + Write-Verbose "$(Get-Date): Installing $installer to $destination." $process = Start-Process @startProcessArgs - while (!$process.HasExited) { - Write-Host "`rInstalling $installer to $destination - $($spins[$spinnerIndex++ % $spins.Length])" -NoNewline - Start-Sleep -Milliseconds 100 - } - - if ( $process.ExitCode -ne 0) { - Write-Host "`bFailed." - Write-Error "Installing $installer failed with exit code: $($process.ExitCode)" - } - else { - Write-Host "`bSucceeded." + if ( $process ) { + if ( $process.ExitCode -ne 0) { + Write-Error "$(Get-Date): Failed with exit code: $($process.ExitCode)" + } + else { + Write-Verbose "$(Get-Date): Succeeded." + } } } } @@ -414,7 +501,7 @@ function Install-UnitySetupInstance { .Synopsis Uninstall Unity Setup Instances .DESCRIPTION - Uninstall the specified Unity Setup Instances + Uninstall the specified Unity Setup Instances .PARAMETER Instance What instances of UnitySetup should be uninstalled .EXAMPLE @@ -432,7 +519,7 @@ function Uninstall-UnitySetupInstance { $uninstaller = Get-ChildItem "$($setupInstance.Path)" -Filter 'Uninstall.exe' -Recurse | Select-Object -First 1 -ExpandProperty FullName - if ($null -eq $uninstaller) { + if ($null -eq $uninstaller) { Write-Error "Could not find Uninstaller.exe under $($setupInstance.Path)" continue } @@ -469,13 +556,31 @@ function Get-UnitySetupInstance { [CmdletBinding()] param( [parameter(Mandatory = $false)] - [string[]] $BasePath = @('C:\Program Files*\Unity*', 'C:\Program Files\Unity\Hub\Editor\*') + [string[]] $BasePath ) + switch (Get-OperatingSystem) { + ([OperatingSystem]::Windows) { + if (-not $BasePath) { + $BasePath = @('C:\Program Files*\Unity*', 'C:\Program Files\Unity\Hub\Editor\*') + } + $ivyPath = 'Editor\Data\UnityExtensions\Unity\Networking\ivy.xml' + } + ([OperatingSystem]::Linux) { + throw "Get-UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!"; + } + ([OperatingSystem]::Mac) { + if (-not $BasePath) { + $BasePath = @('/Applications/Unity*') + } + $ivyPath = 'Unity.app/Contents/UnityExtensions/Unity/Networking/ivy.xml' + } + } + foreach ( $folder in $BasePath ) { - $path = [io.path]::Combine("$folder", 'Editor\Data\UnityExtensions\Unity\Networking\ivy.xml'); + $path = [io.path]::Combine("$folder", $ivyPath); - Get-ChildItem $path -Recurse -ErrorAction Ignore | + Get-ChildItem $path -Recurse -ErrorAction Ignore | ForEach-Object { [UnitySetupInstance]::new((Join-Path $_.Directory "..\..\..\..\..\" | Convert-Path)) } @@ -512,17 +617,17 @@ function Select-UnitySetupInstance { [parameter(Mandatory = $true, ValueFromPipeline = $true)] [UnitySetupInstance[]] $Instances ) - + process { - if ( $Version ) { + if ( $Version ) { $Instances = $Instances | Where-Object { [UnityVersion]::Compare($_.Version, $Version) -eq 0 } } if ( $Latest ) { - foreach ( $i in $Instances ) { - if ( $null -eq $latestInstance -or [UnityVersion]::Compare($i.Version, $latestInstance.Version) -gt 0) { + foreach ( $i in $Instances ) { + if ( $null -eq $latestInstance -or [UnityVersion]::Compare($i.Version, $latestInstance.Version) -gt 0) { $latestInstance = $i - } + } } } elseif ( $Instances.Count -gt 0 ) { $Instances } @@ -538,7 +643,7 @@ function Select-UnitySetupInstance { .DESCRIPTION Recursively discovers Unity projects and their Unity version .PARAMETER BasePath - Under what base pattern should we look for Unity projects? Defaults to '$PWD'. + Under what base pattern should we look for Unity projects? Defaults to '$PWD'. .EXAMPLE Get-UnityProjectInstance .EXAMPLE @@ -599,6 +704,14 @@ function Get-UnityProjectInstance { The log file for the Unity Editor to write to. .PARAMETER BuildTarget The platform build target for the Unity Editor to start in. +.PARAMETER AcceptAPIUpdate + Accept the API Updater automatically. Implies BatchMode unless explicitly specified by the user. +.PARAMETER Credential + What user name and password should be used by Unity for activation? +.PARAMETER Serial + What serial should be used by Unity for activation? Implies BatchMode and Quit if they're not supplied by the User. +.PARAMETER ReturnLicense + Unity should return the current license it's been activated with. Implies Quit if not supplied by the User. .PARAMETER BatchMode Should the Unity Editor start in batch mode? .PARAMETER Quit @@ -621,9 +734,9 @@ function Get-UnityProjectInstance { function Start-UnityEditor { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Context")] param( - [parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = 'Projects')] - [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ProjectsLatest')] - [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ProjectsVersion')] + [parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = 'Projects', Position = 0)] + [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ProjectsLatest', Position = 0)] + [parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ProjectsVersion', Position = 0)] [ValidateNotNullOrEmpty()] [UnityProjectInstance[]] $Project, [parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = 'Setups')] @@ -655,6 +768,16 @@ function Start-UnityEditor { [ValidateSet('StandaloneOSX', 'StandaloneWindows', 'iOS', 'Android', 'StandaloneLinux', 'StandaloneWindows64', 'WebGL', 'WSAPlayer', 'StandaloneLinux64', 'StandaloneLinuxUniversal', 'Tizen', 'PSP2', 'PS4', 'XBoxOne', 'N3DS', 'WiiU', 'tvOS', 'Switch')] [string]$BuildTarget, [parameter(Mandatory = $false)] + [switch]$AcceptAPIUpdate, + [parameter(Mandatory = $false)] + [pscredential]$Credential, + [parameter(Mandatory = $false)] + [securestring]$Serial, + [parameter(Mandatory = $false)] + [switch]$ReturnLicense, + [parameter(Mandatory = $false)] + [switch]$ForceFree, + [parameter(Mandatory = $false)] [switch]$BatchMode, [parameter(Mandatory = $false)] [switch]$Quit, @@ -663,7 +786,7 @@ function Start-UnityEditor { [parameter(Mandatory = $false)] [switch]$PassThru ) - process { + process { switch -wildcard ( $PSCmdlet.ParameterSetName ) { 'Context' { $projectInstances = [UnityProjectInstance[]]@() @@ -680,17 +803,17 @@ function Start-UnityEditor { } } } - 'Projects*' { + 'Projects*' { $projectInstances = $Project $setupInstances = [UnitySetupInstance[]]@() } - 'Setups' { + 'Setups' { $projectInstances = [UnityProjectInstance[]]@() $setupInstances = $Setup } 'Latest' { $projectInstances = [UnityProjectInstance[]]@() - + $currentFolderProject = if (!$IgnoreProjectContext) { Get-UnityProjectInstance $PWD.Path } if ($null -ne $currentFolderProject) { $projectInstances += , $currentFolderProject @@ -722,6 +845,20 @@ function Start-UnityEditor { } $sharedArgs = @() + if ( $ReturnLicense ) { + if ( -not $PSBoundParameters.ContainsKey('BatchMode') ) { $BatchMode = $true } + if ( -not $PSBoundParameters.ContainsKey('Quit') ) { $Quit = $true } + + $sharedArgs += '-returnLicense' + } + if ( $Serial ) { + if ( -not $PSBoundParameters.ContainsKey('BatchMode') ) { $BatchMode = $true } + if ( -not $PSBoundParameters.ContainsKey('Quit') ) { $Quit = $true } + } + if ( $AcceptAPIUpdate ) { + $sharedArgs += '-accept-apiupdate' + if ( -not $PSBoundParameters.ContainsKey('BatchMode')) { $BatchMode = $true } + } if ( $CreateProject ) { $sharedArgs += "-createProject", $CreateProject } if ( $ExecuteMethod ) { $sharedArgs += "-executeMethod", $ExecuteMethod } if ( $OutputPath ) { $sharedArgs += "-buildOutput", $OutputPath } @@ -731,10 +868,12 @@ function Start-UnityEditor { if ( $Quit ) { $sharedArgs += "-quit" } if ( $ExportPackage ) { $sharedArgs += "-exportPackage", "$ExportPackage" } if ( $ImportPackage ) { $sharedArgs += "-importPackage", "$ImportPackage" } + if ( $Credential ) { $sharedArgs += '-username', $Credential.UserName } + if ( $ForceFree) { $sharedArgs += '-force-free' } $instanceArgs = @() - foreach ( $p in $projectInstances ) { - + foreach ( $p in $projectInstances ) { + if ( $Latest ) { $setupInstance = Get-UnitySetupInstance | Select-UnitySetupInstance -Latest if ($setupInstance.Count -eq 0) { @@ -749,55 +888,89 @@ function Start-UnityEditor { continue } } - else { + else { $setupInstance = Get-UnitySetupInstance | Select-UnitySetupInstance -Version $p.Version if ($setupInstance.Count -eq 0) { Write-Error "Could not find Unity Editor for version $($p.Version)" continue } } - + $projectPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($($p.Path)) $instanceArgs += , ("-projectPath", $projectPath) $setupInstances += , $setupInstance } + $currentOS = Get-OperatingSystem + for ($i = 0; $i -lt $setupInstances.Length; $i++) { $setupInstance = $setupInstances[$i] - $editor = Get-ChildItem "$($setupInstance.Path)" -Filter 'Unity.exe' -Recurse | - Select-Object -First 1 -ExpandProperty FullName - if ([string]::IsNullOrEmpty($editor)) { - Write-Error "Could not find Unity.exe under setup instance path: $($setupInstance.Path)" - continue + switch ($currentOS) { + ([OperatingSystem]::Windows) { + $editor = Get-ChildItem "$($setupInstance.Path)" -Filter 'Unity.exe' -Recurse | + Select-Object -First 1 -ExpandProperty FullName + + if ([string]::IsNullOrEmpty($editor)) { + Write-Error "Could not find Unity.exe under setup instance path: $($setupInstance.Path)" + continue + } + } + ([OperatingSystem]::Linux) { + throw "Start-UnityEditor has not been implemented on the Linux platform. Contributions welcomed!"; + } + ([OperatingSystem]::Mac) { + $editor = [io.path]::Combine("$($setupInstance.Path)", "Unity.app/Contents/MacOS/Unity") + + if ([string]::IsNullOrEmpty($editor)) { + Write-Error "Could not find Unity app under setup instance path: $($setupInstance.Path)" + continue + } + } } - + # clone the shared args list $unityArgs = $sharedArgs | ForEach-Object { $_ } if ( $instanceArgs[$i] ) { $unityArgs += $instanceArgs[$i] } + $setProcessArgs = @{ 'FilePath' = $editor; 'PassThru' = $true; 'ErrorAction' = 'Stop'; + 'RedirectStandardOutput' = New-TemporaryFile; + 'RedirectStandardError' = New-TemporaryFile; } - if ($unityArgs -and $unityArgs.Length -gt 0) { - $setProcessArgs['ArgumentList'] = $unityArgs - } + if ($Wait) { $setProcessArgs['Wait'] = $true } + + Write-Verbose "Redirecting standard output to $($setProcessArgs['RedirectStandardOutput'])" + Write-Verbose "Redirecting standard error to $($setProcessArgs['RedirectStandardError'])" - if (-not $PSCmdlet.ShouldProcess("$editor $unityArgs", "Start-Process")) { + $actionString = "$editor $unityArgs" + if( $Credential ) { $actionString += " -password (hidden)"} + if( $Serial ) { $actionString += " -serial (hidden)"} + + if (-not $PSCmdlet.ShouldProcess($actionString, "Start-Process")) { continue } + # Defered till after potential display by ShouldProcess + if ( $Credential ) { $unityArgs += '-password', $Credential.GetNetworkCredential().Password } + if ( $Serial ) { $unityArgs += '-serial', [System.Net.NetworkCredential]::new($null, $Serial).Password } + + if ($unityArgs -and $unityArgs.Length -gt 0) { + $setProcessArgs['ArgumentList'] = $unityArgs + } + $process = Start-Process @setProcessArgs if ( $Wait ) { - $process.WaitForExit(); if ( $process.ExitCode -ne 0 ) { if ( $LogFile -and (Test-Path $LogFile -Type Leaf) ) { + Write-Verbose "Writing $LogFile to Information stream Tagged as 'Logs'" Get-Content $LogFile | ForEach-Object { Write-Information -MessageData $_ -Tags 'Logs' } } - Write-Error "Unity quit with non-zero exit code" + Write-Error "Unity quit with non-zero exit code: $($process.ExitCode)" } } @@ -806,12 +979,57 @@ function Start-UnityEditor { } } +function ConvertTo-DateTime { + param([string] $Text) + + if( -not $text -or $text.Length -eq 0 ) { [DateTime]::MaxValue } + else { [DateTime]$Text } +} + +<# +.Synopsis + Get the active Unity licenses for the machine. +.PARAMETER Serial + Filter licenses to the specified serial +.EXAMPLE + Get-UnityLicense +#> +function Get-UnityLicense +{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification="Used to convert discovered plaintext serials into secure strings.")] + param([SecureString]$Serial) + + $licenseFiles = Get-ChildItem "C:\ProgramData\Unity\Unity_*.ulf" -ErrorAction 'SilentlyContinue' + foreach ( $licenseFile in $licenseFiles ) { + Write-Verbose "Discovered License File at $licenseFile" + $doc = [xml](Get-Content "$licenseFile") + $devBytes = [System.Convert]::FromBase64String($doc.root.License.DeveloperData.Value) + + # The first four bytes look like a count so skip that to pull out the serial string + $licenseSerial = [String]::new($devBytes[4..($devBytes.Length - 1)]) + if( $Serial -and [System.Net.NetworkCredential]::new($null, $Serial).Password -ne $licenseSerial ) { continue; } + + $license = $doc.root.License + [PSCustomObject]@{ + 'LicenseVersion' = $license.LicenseVersion.Value + 'Serial' = ConvertTo-SecureString $licenseSerial -AsPlainText -Force + 'UnityVersion' = [UnityVersion]$license.ClientProvidedVersion.Value + 'DisplaySerial' = $license.SerialMasked.Value + 'ActivationDate' = ConvertTo-DateTime $license.InitialActivationDate.Value + 'StartDate' = ConvertTo-DateTime $license.StartDate.Value + 'StopDate' = ConvertTo-DateTime $license.StopDate.Value + 'UpdateDate' = ConvertTo-DateTime $license.UpdateDate.Value + } + } +} + @( @{ 'Name' = 'gusi'; 'Value' = 'Get-UnitySetupInstance' }, @{ 'Name' = 'gupi'; 'Value' = 'Get-UnityProjectInstance' }, @{ 'Name' = 'susi'; 'Value' = 'Select-UnitySetupInstance' }, - @{ 'Name' = 'sue'; 'Value' = 'Start-UnityEditor' } -) | ForEach-Object { + @{ 'Name' = 'sue'; 'Value' = 'Start-UnityEditor' } +) | ForEach-Object { $alias = Get-Alias -Name $_.Name -ErrorAction 'SilentlyContinue' if ( -not $alias ) { @@ -825,4 +1043,4 @@ function Start-UnityEditor { else { Write-Warning "Alias $($_.Name) already configured by $($alias.Source)" } -} \ No newline at end of file +} diff --git a/build.ps1 b/build.ps1 index bc3f140..e6c2ab1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + param([int]$Revision = 0, [string]$Suffix = '') Import-Module 'PowerShellGet' -Force