diff --git a/lib/core.ps1 b/lib/core.ps1 index 71569d5cd5..25c3b25905 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -524,6 +524,45 @@ function movedir($from, $to) { } } +function Confirm-IsEmptyDirectory { + param ( + [String] + $Path + ) + return @(Get-ChildItem -Path $Path -File -Recurse -Force -ErrorAction SilentlyContinue).Length -eq 0 +} + +function Remove-Directory { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [String] + $Path, + [Switch] + $OnlyEmpty + ) + if (Test-Path $Path) { + if ($OnlyEmpty) { + (Get-ChildItem $Path -Directory -Recurse -Force -ErrorAction SilentlyContinue).FullName | ForEach-Object { + if (Confirm-IsEmptyDirectory -Path $_) { + Remove-Directory -Path $_ + } + } + if (Confirm-IsEmptyDirectory -Path $Path) { + Remove-Directory -Path $Path + } + } else { + try { + Remove-Item $Path -Recurse -Force -ErrorAction Stop + } catch [System.IO.PathTooLongException] { + & "$env:COMSPEC" /c "rmdir /s /q $Path" + } catch [System.UnauthorizedAccessException] { + warn "Couldn't remove '$Path': unauthorized access." + } + } + } +} + function get_app_name($path) { if ($path -match '([^/\\]+)[/\\]current[/\\]') { return $matches[1].tolower() diff --git a/lib/decompress.ps1 b/lib/decompress.ps1 index 57d0ea57e3..e3a0570e3e 100644 --- a/lib/decompress.ps1 +++ b/lib/decompress.ps1 @@ -1,33 +1,38 @@ function Test-7zipRequirement { - [CmdletBinding(DefaultParameterSetName = "URL")] + [CmdletBinding(DefaultParameterSetName = 'Manifest')] [OutputType([Boolean])] param ( - [Parameter(Mandatory = $true, ParameterSetName = "URL")] - [String[]] - $URL, - [Parameter(Mandatory = $true, ParameterSetName = "File")] + [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Manifest')] + [PSObject] + $Manifest, + [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'Manifest')] + [String] + $Architecture, + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [String] $File ) - if ($URL) { - if ((get_config 7ZIPEXTRACT_USE_EXTERNAL)) { - return $false + if ($File) { + return $File -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^.]+)?$' + } else { + $uri = url $Manifest $Architecture + $installer = installer $Manifest $Architecture + if (($installer.type -eq 'nsis')) { + return $true } else { - return ($URL | Where-Object { Test-7zipRequirement -File $_ }).Count -gt 0 + return ($uri | Where-Object { Test-7zipRequirement -File $_ }).Count -gt 0 } - } else { - return $File -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^.]+)?$' } } function Test-ZstdRequirement { - [CmdletBinding(DefaultParameterSetName = "URL")] + [CmdletBinding(DefaultParameterSetName = 'URL')] [OutputType([Boolean])] param ( - [Parameter(Mandatory = $true, ParameterSetName = "URL")] + [Parameter(Mandatory = $true, ParameterSetName = 'URL')] [String[]] $URL, - [Parameter(Mandatory = $true, ParameterSetName = "File")] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [String] $File ) @@ -42,17 +47,49 @@ function Test-LessmsiRequirement { [CmdletBinding()] [OutputType([Boolean])] param ( - [Parameter(Mandatory = $true)] - [String[]] - $URL + [Parameter(Mandatory = $true, Position = 0)] + [PSObject] + $Manifest, + [Parameter(Mandatory = $true, Position = 1)] + [String] + $Architecture ) + $uri = url $Manifest $Architecture if ((get_config MSIEXTRACT_USE_LESSMSI)) { - return ($URL | Where-Object { $_ -match '\.msi$' }).Count -gt 0 + return ($uri | Where-Object { $_ -match '\.msi$' }).Count -gt 0 } else { return $false } } +function Test-InnounpRequirement { + [CmdletBinding()] + [OutputType([Boolean])] + param ( + [Parameter(Mandatory = $true, Position = 0)] + [PSObject] + $Manifest, + [Parameter(Mandatory = $true, Position = 1)] + [String] + $Architecture + ) + return (installer $Manifest $Architecture).type -eq 'inno' +} + +function Test-DarkRequirement { + [CmdletBinding()] + [OutputType([Boolean])] + param ( + [Parameter(Mandatory = $true, Position = 0)] + [PSObject] + $Manifest, + [Parameter(Mandatory = $true, Position = 1)] + [String] + $Architecture + ) + return (installer $Manifest $Architecture).type -eq 'wix' +} + function Expand-7zipArchive { [CmdletBinding()] param ( @@ -67,7 +104,7 @@ function Expand-7zipArchive { [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, - [ValidateSet("All", "Skip", "Rename")] + [ValidateSet('All', 'Skip', 'Rename')] [String] $Overwrite, [Switch] @@ -77,36 +114,36 @@ function Expand-7zipArchive { try { $7zPath = (Get-Command '7z' -CommandType Application | Select-Object -First 1).Source } catch [System.Management.Automation.CommandNotFoundException] { - abort "Cannot find external 7-Zip (7z.exe) while '7ZIPEXTRACT_USE_EXTERNAL' is 'true'!`nRun 'scoop config 7ZIPEXTRACT_USE_EXTERNAL false' or install 7-Zip manually and try again." + abort 'Cannot find external 7-Zip (7z.exe) while "7ZIPEXTRACT_USE_EXTERNAL" is "true"!`nRun "scoop config 7ZIPEXTRACT_USE_EXTERNAL false" or install 7-Zip manually and try again.' } } else { $7zPath = Get-HelperPath -Helper 7zip } - $LogPath = "$(Split-Path $Path)\7zip.log" - $ArgList = @('x', "`"$Path`"", "-o`"$DestinationPath`"", '-y') - $IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$') - if (!$IsTar -and $ExtractDir) { - $ArgList += "-ir!`"$ExtractDir\*`"" + $logPath = "$(Split-Path $Path)\7zip.log" + $argList = @('x', "`"$Path`"", "-o`"$DestinationPath`"", '-y') + $isTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$') + if (!$isTar -and $ExtractDir) { + $argList += "-ir!`"$ExtractDir\*`"" } if ($Switches) { - $ArgList += (-split $Switches) + $argList += (-split $Switches) } switch ($Overwrite) { - "All" { $ArgList += "-aoa" } - "Skip" { $ArgList += "-aos" } - "Rename" { $ArgList += "-aou" } + 'All' { $argList += '-aoa' } + 'Skip' { $argList += '-aos' } + 'Rename' { $argList += '-aou' } } - $Status = Invoke-ExternalCommand $7zPath $ArgList -LogPath $LogPath - if (!$Status) { - abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" + $status = Invoke-ExternalCommand $7zPath $argList -LogPath $logPath + if (!$status) { + abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $logPath)`n$(new_issue_msg $app $bucket 'decompress error')" } - if (!$IsTar -and $ExtractDir) { + if (!$isTar -and $ExtractDir) { movedir "$DestinationPath\$ExtractDir" $DestinationPath | Out-Null } - if (Test-Path $LogPath) { - Remove-Item $LogPath -Force + if (Test-Path $logPath) { + Remove-Item $logPath -Force } - if ($IsTar) { + if ($isTar) { # Check for tar $Status = Invoke-ExternalCommand $7zPath @('l', "`"$Path`"") -LogPath $LogPath if ($Status) { @@ -187,40 +224,40 @@ function Expand-MsiArchive { [Switch] $Removal ) - $DestinationPath = $DestinationPath.TrimEnd("\") + $DestinationPath = $DestinationPath.TrimEnd('\') if ($ExtractDir) { - $OriDestinationPath = $DestinationPath + $oriDestinationPath = $DestinationPath $DestinationPath = "$DestinationPath\_tmp" } if ((get_config MSIEXTRACT_USE_LESSMSI)) { - $MsiPath = Get-HelperPath -Helper Lessmsi - $ArgList = @('x', "`"$Path`"", "`"$DestinationPath\\`"") + $msiPath = Get-HelperPath -Helper Lessmsi + $argList = @('x', "`"$Path`"", "`"$DestinationPath\\`"") } else { - $MsiPath = 'msiexec.exe' - $ArgList = @('/a', "`"$Path`"", '/qn', "TARGETDIR=`"$DestinationPath\\SourceDir`"") + $msiPath = 'msiexec.exe' + $argList = @('/a', "`"$Path`"", '/qn', "TARGETDIR=`"$DestinationPath\\SourceDir`"") } - $LogPath = "$(Split-Path $Path)\msi.log" + $logPath = "$(Split-Path $Path)\msi.log" if ($Switches) { - $ArgList += (-split $Switches) + $argList += (-split $Switches) } - $Status = Invoke-ExternalCommand $MsiPath $ArgList -LogPath $LogPath - if (!$Status) { - abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" + $status = Invoke-ExternalCommand $msiPath $argList -LogPath $logPath + if (!$status) { + abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $logPath)`n$(new_issue_msg $app $bucket 'decompress error')" } if ($ExtractDir -and (Test-Path "$DestinationPath\SourceDir")) { - movedir "$DestinationPath\SourceDir\$ExtractDir" $OriDestinationPath | Out-Null - Remove-Item $DestinationPath -Recurse -Force + movedir "$DestinationPath\SourceDir\$ExtractDir" $oriDestinationPath | Out-Null + Remove-Directory -Path $DestinationPath } elseif ($ExtractDir) { - movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null - Remove-Item $DestinationPath -Recurse -Force + movedir "$DestinationPath\$ExtractDir" $oriDestinationPath | Out-Null + Remove-Directory -Path $DestinationPath } elseif (Test-Path "$DestinationPath\SourceDir") { movedir "$DestinationPath\SourceDir" $DestinationPath | Out-Null } if (($DestinationPath -ne (Split-Path $Path)) -and (Test-Path "$DestinationPath\$(fname $Path)")) { Remove-Item "$DestinationPath\$(fname $Path)" -Force } - if (Test-Path $LogPath) { - Remove-Item $LogPath -Force + if (Test-Path $logPath) { + Remove-Item $logPath -Force } if ($Removal) { # Remove original archive file @@ -245,22 +282,23 @@ function Expand-InnoArchive { [Switch] $Removal ) - $LogPath = "$(Split-Path $Path)\innounp.log" - $ArgList = @('-x', "-d`"$DestinationPath`"", "`"$Path`"", '-y') + $logPath = "$(Split-Path $Path)\innounp.log" + $argList = @('-x', "-d`"$DestinationPath`"", "`"$Path`"", '-y') switch -Regex ($ExtractDir) { - "^[^{].*" { $ArgList += "-c{app}\$ExtractDir" } - "^{.*" { $ArgList += "-c$ExtractDir" } - Default { $ArgList += "-c{app}" } + '^\.$' { break } # Suppress '-cDIR' param + '^[^{].*' { $argList += "-c`"{app}\$ExtractDir`""; break } + '^{.*' { $argList += "-c`"$ExtractDir`""; break } + Default { $argList += '-c"{app}"' } } if ($Switches) { - $ArgList += (-split $Switches) + $argList += (-split $Switches) } - $Status = Invoke-ExternalCommand (Get-HelperPath -Helper Innounp) $ArgList -LogPath $LogPath - if (!$Status) { - abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" + $status = Invoke-ExternalCommand (Get-HelperPath -Helper Innounp) $argList -LogPath $logPath + if (!$status) { + abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $logPath)`n$(new_issue_msg $app $bucket 'decompress error')" } - if (Test-Path $LogPath) { - Remove-Item $LogPath -Force + if (Test-Path $logPath) { + Remove-Item $logPath -Force } if ($Removal) { # Remove original archive file @@ -283,40 +321,14 @@ function Expand-ZipArchive { $Removal ) if ($ExtractDir) { - $OriDestinationPath = $DestinationPath + $oriDestinationPath = $DestinationPath $DestinationPath = "$DestinationPath\_tmp" } - # All methods to unzip the file require .NET4.5+ - if ($PSVersionTable.PSVersion.Major -lt 5) { - Add-Type -AssemblyName System.IO.Compression.FileSystem - try { - [System.IO.Compression.ZipFile]::ExtractToDirectory($Path, $DestinationPath) - } catch [System.IO.PathTooLongException] { - # try to fall back to 7zip if path is too long - if (Test-HelperInstalled -Helper 7zip) { - Expand-7zipArchive $Path $DestinationPath -Removal - return - } else { - abort "Unzip failed: Windows can't handle the long paths in this zip file.`nRun 'scoop install 7zip' and try again." - } - } catch [System.IO.IOException] { - if (Test-HelperInstalled -Helper 7zip) { - Expand-7zipArchive $Path $DestinationPath -Removal - return - } else { - abort "Unzip failed: Windows can't handle the file names in this zip file.`nRun 'scoop install 7zip' and try again." - } - } catch { - abort "Unzip failed: $_" - } - } else { - # Use Expand-Archive to unzip in PowerShell 5+ - # Compatible with Pscx (https://github.com/Pscx/Pscx) - Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force - } + # Compatible with Pscx (https://github.com/Pscx/Pscx) + Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force if ($ExtractDir) { - movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null - Remove-Item $DestinationPath -Recurse -Force + movedir "$DestinationPath\$ExtractDir" $oriDestinationPath | Out-Null + Remove-Directory -Path $DestinationPath } if ($Removal) { # Remove original archive file @@ -339,17 +351,17 @@ function Expand-DarkArchive { [Switch] $Removal ) - $LogPath = "$(Split-Path $Path)\dark.log" - $ArgList = @('-nologo', "-x `"$DestinationPath`"", "`"$Path`"") + $logPath = "$(Split-Path $Path)\dark.log" + $argList = @('-nologo', "-x `"$DestinationPath`"", "`"$Path`"") if ($Switches) { - $ArgList += (-split $Switches) + $argList += (-split $Switches) } - $Status = Invoke-ExternalCommand (Get-HelperPath -Helper Dark) $ArgList -LogPath $LogPath - if (!$Status) { - abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" + $status = Invoke-ExternalCommand (Get-HelperPath -Helper Dark) $argList -LogPath $logPath + if (!$status) { + abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $logPath)`n$(new_issue_msg $app $bucket 'decompress error')" } - if (Test-Path $LogPath) { - Remove-Item $LogPath -Force + if (Test-Path $logPath) { + Remove-Item $logPath -Force } if ($Removal) { # Remove original archive file @@ -364,7 +376,7 @@ function extract_7zip($path, $to, $removal) { function extract_msi($path, $to, $removal) { Show-DeprecatedWarning $MyInvocation 'Expand-MsiArchive' - Expand-MsiArchive -Path $path -DestinationPath $to -Removal:$removal + Expand-MsiArchive -Path $path -DestinationPath $to -Removal:$removal @args } function unpack_inno($path, $to, $removal) { @@ -376,3 +388,146 @@ function extract_zip($path, $to, $removal) { Show-DeprecatedWarning $MyInvocation 'Expand-ZipArchive' Expand-ZipArchive -Path $path -DestinationPath $to -Removal:$removal } + +function Expand-NsisInstaller { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [String] + $Path, + [Parameter(Position = 1)] + [String] + $DestinationPath = (Split-Path $Path), + [String] + $Architecture, + [Switch] + $Removal + ) + Expand-7ZipArchive -Path $Path -DestinationPath $DestinationPath -Removal:$Removal + if (Test-Path "$DestinationPath\`$PLUGINSDIR\app-64.7z") { + if ($Architecture -eq '64bit') { + Expand-7ZipArchive -Path "$DestinationPath\`$PLUGINSDIR\app-64.7z" -DestinationPath $DestinationPath + } else { + abort "Software doesn't support $Architecture architecture!" + } + } elseif (Test-Path "$DestinationPath\`$PLUGINSDIR\app-32.7z") { + Expand-7ZipArchive -Path "$DestinationPath\`$PLUGINSDIR\app-32.7z" -DestinationPath $DestinationPath + } + @('*uninst*', '$*') | ForEach-Object { Get-Item "$DestinationPath\$_" | Remove-Item -Recurse -Force } +} + +function Expand-WixInstaller { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [String] + $Path, + [Parameter(Position = 1)] + [String] + $DestinationPath = (Split-Path $Path), + [String[]] + $Exclude, + [Switch] + $Removal + ) + Expand-DarkArchive -Path $Path -DestinationPath (ensure "$DestinationPath\_tmp") -Removal:$Removal + if ($Exclude) { + Remove-Item "$DestinationPath\_tmp\AttachedContainer\*.msi" -Include $Exclude -Force + } + Get-ChildItem "$DestinationPath\_tmp\AttachedContainer\*.msi" | ForEach-Object { Expand-MsiArchive $_ $DestinationPath } + Remove-Directory -Path "$DestinationPath\_tmp" +} + +function ConvertFrom-Inno { + [CmdletBinding()] + [OutputType([Hashtable])] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [Object[]] + $InputObject, + [String[]] + $Include, + [String[]] + $Exclude + ) + + $fileList = New-Object System.Collections.Generic.List[System.Object] + foreach ($file in ($InputObject -match '^Source:')) { + if ($file -match 'Source: "(?(?[^\\]*).*?)"; DestDir: "(?.*?)"; (?:DestName: "(?.*?)"; )?(?:Components: (?.*?);)?') { + $fileList.Add([PSCustomObject]@{source = $Matches.source; srcdir = $Matches.srcdir; destdir = $Matches.destdir; destname = $Matches.destname; components = $Matches.components }) + } + } + if ($fileList.components) { + $comps = $fileList.components | Select-Object -Unique + $includeComps = @() + $excludeComps = @() + if ($Include) { + $Include = $Include -split '\\' | Select-Object -Unique + foreach ($incFile in $Include) { + $incFile = '\b' + [Regex]::Escape($incFile) + '\b' + $includeComps += $comps | Where-Object { + ($_ -match "$incFile") -and ($_ -notmatch "not[^(]*?\(?[^(]*?$incFile") + } + $excludeComps += $comps | Where-Object { $_ -match "not[^(]*?\(?[^(]*?$incFile" } + } + } + if ($Exclude) { + foreach ($excFile in $Exclude) { + $excFile = '\b' + [Regex]::Escape($excFile) + '\b' + $excludeComps += $comps | Where-Object { ($_ -match "$excFile") -and ($_ -notmatch "not[^(]*?\(?[^(]*?$excFile") -and ($_ -notmatch "or[^(]*?$excFile") -and ($_ -notmatch "$excFile[^(]*?or") } + } + $includeComps = $includeComps | Where-Object { $_ -notin $excludeComps } + } + $included = $fileList | Where-Object { $_.components -in $includeComps } + $excluded = $fileList | Where-Object { $_.components -in $excludeComps } + } + + return @{ + FileList = $fileList; + Excluded = $excluded; + Included = $included; + Extracted = @($fileList.srcdir | Select-Object -Unique) -ne '{tmp}' + } +} + +function Expand-InnoInstaller { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [String] + $Path, + [Parameter(Position = 1)] + [String] + $DestinationPath = (Split-Path $Path), + [String] + $ExtractDir, + [String[]] + $Include, + [String[]] + $Exclude, + [Switch] + $Removal + ) + if ($ExtractDir) { + Expand-InnoArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal:$Removal + } elseif ($Include -or $Exclude) { + Expand-InnoArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir '.' -Switches 'install_script.iss' # Just extract install script + $installScript = Get-Content -Path "$DestinationPath\install_script.iss" + $innoFiles = ConvertFrom-Inno -InputObject $installScript -Include $Include -Exclude ($Exclude -notlike '{*}') + $innoFiles.Extracted | Where-Object { $_ -notin ($Exclude -like '{*}') } | ForEach-Object { + Expand-InnoArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $_ -Switches '-a' -Removal:$Removal + } + if ($innoFiles.Excluded) { + ($innoFiles.Excluded.source -replace '{.*?}', "$DestinationPath") | Remove-Item -Force -ErrorAction Ignore + } + if ($innoFiles.Included) { + $innoFiles.Included | Where-Object { $_.source -match ',' } | Rename-Item -Path { $_.source -replace '{.*?}', "$DestinationPath" } -NewName { $_.destname } -Force -ErrorAction Ignore + } + Get-ChildItem -Path $DestinationPath -Filter '*,*' -Recurse | Rename-Item -NewName { $_.name -Replace ',\d', '' } -Force -ErrorAction Ignore + Get-ChildItem -Path $DestinationPath -Filter '*,*' -Recurse | Remove-Item -Force -ErrorAction Ignore + Remove-Directory -Path $DestinationPath -OnlyEmpty + Remove-Item -Path "$DestinationPath\install_script.iss" -Force + } else { + Expand-InnoArchive -Path $Path -DestinationPath $DestinationPath -Removal:$Removal + } +} diff --git a/lib/depends.ps1 b/lib/depends.ps1 index 415f2405d9..ada942cd6b 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -83,15 +83,18 @@ function install_deps($manifest, $arch) { $test_url = script:url $manifest $arch if (-not $test_url) { $test_url = " " } - if (!(Test-HelperInstalled -Helper 7zip) -and (Test-7zipRequirement -URL $test_url)) { + if (!(Test-HelperInstalled -Helper 7zip) -and (Test-7zipRequirement -Manifest $manifest -Architecture $arch)) { $deps += '7zip' } - if (!(Test-HelperInstalled -Helper Lessmsi) -and (Test-LessmsiRequirement -URL $test_url)) { + if (!(Test-HelperInstalled -Helper Lessmsi) -and (Test-LessmsiRequirement -Manifest $manifest -Architecture $arch)) { $deps += 'lessmsi' } - if (!(Test-HelperInstalled -Helper Innounp) -and $manifest.innosetup) { + if (!(Test-HelperInstalled -Helper Innounp) -and (Test-InnounpRequirement -Manifest $manifest -Architecture $arch)) { $deps += 'innounp' } + if (!(Test-HelperInstalled -Helper Dark) -and (Test-DarkRequirement -Manifest $manifest -Architecture $arch)) { + $deps += 'dark' + } if (!(Test-HelperInstalled -Helper Zstd) -and (Test-ZstdRequirement -URL $test_url)) { $deps += 'zstd' } @@ -103,5 +106,9 @@ function install_deps($manifest, $arch) { $deps += script_deps $installer.script $deps += script_deps $post_install - return $deps | Select-Object -Unique + if ((get_config 7ZIPEXTRACT_USE_EXTERNAL)) { + $deps = @($deps | Select-Object -Unique) -ne '7zip' + } + + return $deps } diff --git a/lib/install.ps1 b/lib/install.ps1 index 88c1e8a0ae..e5877cf3f0 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -34,6 +34,24 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru return } + # Change 'innosetup' to 'installer.type:inno' + if ($manifest.innosetup) { + warn '"innosetup" is deprecated, please use "installer.type:inno" instead.' + warn 'If you maintain this manifest, please refer to the manifest reference docs.' + if ($manifest.$architecture.installer) { + $manifest.$architecture.installer | Add-Member -MemberType NoteProperty -Name type -Value 'inno' + } elseif ($manifest.installer) { + $manifest.installer | Add-Member -MemberType NoteProperty -Name type -Value 'inno' + } else { + $manifest | Add-Member -MemberType NoteProperty -Name installer -Value @{ type = 'inno' } + } + } + + # Change 'installer:xxx' to 'installer.type:xxx' + if ($manifest.installer -is [String]) { + $manifest.installer = @{ type = $manifest.installer } + } + if ((get_config 'manifest-review' $false) -and ($MyInvocation.ScriptName -notlike '*scoop-update*')) { Write-Output "Manifest: $app.json" Write-Output $manifest | ConvertToPrettyJson @@ -49,8 +67,9 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru $persist_dir = persistdir $app $global $fname = dl_urls $app $version $manifest $bucket $architecture $dir $use_cache $check_hash + Invoke-Extraction -FileName $fname -Manifest $manifest -Architecture $architecture -DestinationPath $dir pre_install $manifest $architecture - run_installer $fname $manifest $architecture $dir $global + Invoke-InstallerScript -AppName $app -FileName $fname -Manifest $manifest -Architecture $architecture -DestinationPath $dir -Global:$global ensure_install_dir_not_in_path $dir $global $dir = link_current $dir create_shims $manifest $dir $global $architecture @@ -536,21 +555,13 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c # we only want to show this warning once if(!$use_cache) { warn "Cache is being ignored." } - # can be multiple urls: if there are, then msi or installer should go last, + # can be multiple urls: if there are, then installer should go first, # so that $fname is set properly $urls = @(script:url $manifest $architecture) # can be multiple cookies: they will be used for all HTTP requests. $cookies = $manifest.cookie - $fname = $null - - # extract_dir and extract_to in manifest are like queues: for each url that - # needs to be extracted, will get the next dir from the queue - $extract_dirs = @(extract_dir $manifest $architecture) - $extract_tos = @(extract_to $manifest $architecture) - $extracted = 0; - # download first if(Test-Aria2Enabled) { dl_with_cache_aria2 $app $version $manifest $architecture $dir $cookies $use_cache $check_hash @@ -584,47 +595,91 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c } } - foreach($url in $urls) { - $fname = url_filename $url + return $urls | ForEach-Object { url_filename $_ } +} + +function Invoke-Extraction { + [CmdletBinding()] + param ( + [String[]] + $FileName, + [PSObject] + $Manifest, + [String] + $Architecture, + [String] + $DestinationPath + ) - $extract_dir = $extract_dirs[$extracted] - $extract_to = $extract_tos[$extracted] + # 'url', 'extract_dir' and 'extract_to' are paired + $uris = @(url $Manifest $Architecture) + $extractDirs = @(extract_dir $Manifest $Architecture) + $extractTos = @(extract_to $Manifest $Architecture) + $installer = installer $Manifest $Architecture + for ($i = 0; $i -lt $uris.Length; $i++) { + $fnArgs = @{ + Path = "$DestinationPath\$($FileName[$i])" + DestinationPath = "$DestinationPath\$($extractTos[$i])" + } # work out extraction method, if applicable - $extract_fn = $null - if ($manifest.innosetup) { - $extract_fn = 'Expand-InnoArchive' - } elseif($fname -match '\.zip$') { - # Use 7zip when available (more fast) - if (((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) { - $extract_fn = 'Expand-7zipArchive' - } else { - $extract_fn = 'Expand-ZipArchive' + $extractFn = $null + if ($i -eq 0 -and $installer.type) { + switch ($installer.type) { + 'inno' { + $extractFn = 'Expand-InnoInstaller' + $fnArgs.ExtractDir = $extractDirs[$i] + $fnArgs.Include = $installer.include + $fnArgs.Exclude = $installer.exclude + } + 'nsis' { + $extractFn = 'Expand-NsisInstaller' + $fnArgs.Architecture = $Architecture + } + 'wix' { + $extractFn = 'Expand-WixInstaller' + $fnArgs.Exclude = $installer.exclude + } + Default { + abort "Error in manifest: installer type $_ is not supported." + } } - } elseif($fname -match '\.msi$') { - # check manifest doesn't use deprecated install method - if(msi $manifest $architecture) { - warn "MSI install is deprecated. If you maintain this manifest, please refer to the manifest reference docs." - } else { - $extract_fn = 'Expand-MsiArchive' + } else { + switch -Regex ($fnArgs.Path) { + '.*\.zip$' { + if (((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) { + $extractFn = 'Expand-7zipArchive' + } else { + $extractFn = 'Expand-ZipArchive' + } + continue + } + '.*\.msi$' { + $extractFn = 'Expand-MsiArchive' + continue + } + ({ Test-ZstdRequirement -File $PSItem }) { + # Check Zstd first + $extractFn = 'Expand-ZstdArchive' + continue + } + ({ Test-7zipRequirement -File $PSItem }) { + # Then check 7zip + $extractFn = 'Expand-7zipArchive' + continue + } } - } elseif(Test-ZstdRequirement -File $fname) { # Zstd first - $extract_fn = 'Expand-ZstdArchive' - } elseif(Test-7zipRequirement -File $fname) { # 7zip - $extract_fn = 'Expand-7zipArchive' + $fnArgs.ExtractDir = $extractDirs[$i] } - - if($extract_fn) { - Write-Host "Extracting " -NoNewline - Write-Host $fname -f Cyan -NoNewline - Write-Host " ... " -NoNewline - & $extract_fn -Path "$dir\$fname" -DestinationPath "$dir\$extract_to" -ExtractDir $extract_dir -Removal - Write-Host "done." -f Green - $extracted++ + debug $fnArgs + if ($extractFn) { + Write-Host 'Extracting ' -NoNewline + Write-Host $(url_remote_filename $uris[$i]) -ForegroundColor Cyan -NoNewline + Write-Host ' ... ' -NoNewline + & $extractFn @fnArgs -Removal + Write-Host 'done.' -ForegroundColor Green } } - - $fname # returns the last downloaded file } function cookie_header($cookies) { @@ -724,88 +779,58 @@ function args($config, $dir, $global) { @() } -function run_installer($fname, $manifest, $architecture, $dir, $global) { - # MSI or other installer - $msi = msi $manifest $architecture - $installer = installer $manifest $architecture - if($installer.script) { - write-output "Running installer script..." - Invoke-Expression (@($installer.script) -join "`r`n") - return - } - - if($msi) { - install_msi $fname $dir $msi - } elseif($installer) { - install_prog $fname $dir $installer $global - } -} - -# deprecated (see also msi_installed) -function install_msi($fname, $dir, $msi) { - $msifile = "$dir\$(coalesce $msi.file "$fname")" - if(!(is_in_dir $dir $msifile)) { - abort "Error in manifest: MSI file $msifile is outside the app directory." - } - if(!($msi.code)) { abort "Error in manifest: Couldn't find MSI code."} - if(msi_installed $msi.code) { abort "The MSI package is already installed on this system." } - - $logfile = "$dir\install.log" - - $arg = @("/i `"$msifile`"", '/norestart', "/lvp `"$logfile`"", "TARGETDIR=`"$dir`"", - "INSTALLDIR=`"$dir`"") + @(args $msi.args $dir) +function Invoke-InstallerScript { + [CmdletBinding()] + param ( + [String] + $AppName, + [String[]] + $FileName, + [PSObject] + $Manifest, + [String] + $Architecture, + [String] + $DestinationPath, + [Switch] + $Global + ) + $installer = installer $Manifest $Architecture - if($msi.silent) { $arg += '/qn', 'ALLUSERS=2', 'MSIINSTALLPERUSER=1' } - else { $arg += '/qb-!' } + if ($installer.file -or $installer.args) { + # Installer filename is either explicit defined ('installer.file') or file name in the first URL + $progName = "$DestinationPath\$(coalesce $installer.file $FileName[0])" + if (!(is_in_dir $DestinationPath $progName)) { + abort "Error in manifest: Installer $progName is outside the app directory." + } + $fnArgs = @(args $installer.args $DestinationPath $Global) - $continue_exit_codes = @{ 3010 = "a restart is required to complete installation" } + if ($progName.EndsWith('.ps1')) { + & $progName @fnArgs + } else { + $isInstalled = Invoke-ExternalCommand $progName $fnArgs -Activity 'Running installer...' + if (!$isInstalled) { + abort "Installation aborted. You might need to run 'scoop uninstall $AppName' before trying again." + } - $installed = Invoke-ExternalCommand 'msiexec' $arg -Activity "Running installer..." -ContinueExitCodes $continue_exit_codes - if(!$installed) { - abort "Installation aborted. You might need to run 'scoop uninstall $app' before trying again." + # Don't remove installer if "keep" flag is set to true + if ($installer.keep -ne 'true') { + Remove-Item $progName + } + } } - Remove-Item $logfile - Remove-Item $msifile -} - -# deprecated -# get-wmiobject win32_product is slow and checks integrity of each installed program, -# so this uses the [wmi] type accelerator instead -# http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/14/use-powershell-to-find-and-uninstall-software.aspx -function msi_installed($code) { - $path = "hklm:\software\microsoft\windows\currentversion\uninstall\$code" - if(!(test-path $path)) { return $false } - $key = Get-Item $path - $name = $key.getvalue('displayname') - $version = $key.getvalue('displayversion') - $classkey = "IdentifyingNumber=`"$code`",Name=`"$name`",Version=`"$version`"" - try { $wmi = [wmi]"Win32_Product.$classkey"; $true } catch { $false } -} -function install_prog($fname, $dir, $installer, $global) { - $prog = "$dir\$(coalesce $installer.file "$fname")" - if(!(is_in_dir $dir $prog)) { - abort "Error in manifest: Installer $prog is outside the app directory." + if ($installer.script) { + Write-Host 'Running installer script...' -NoNewline + Invoke-Expression (@($installer.script) -join "`r`n") + Write-Host 'done.' -ForegroundColor Green } - $arg = @(args $installer.args $dir $global) - if($prog.endswith('.ps1')) { - & $prog @arg - } else { - $installed = Invoke-ExternalCommand $prog $arg -Activity "Running installer..." - if(!$installed) { - abort "Installation aborted. You might need to run 'scoop uninstall $app' before trying again." - } + return - # Don't remove installer if "keep" flag is set to true - if(!($installer.keep -eq "true")) { - Remove-Item $prog - } - } } function run_uninstaller($manifest, $architecture, $dir) { - $msi = msi $manifest $architecture $uninstaller = uninstaller $manifest $architecture $version = $manifest.version if($uninstaller.script) { @@ -814,31 +839,15 @@ function run_uninstaller($manifest, $architecture, $dir) { return } - if($msi -or $uninstaller) { - $exe = $null; $arg = $null; $continue_exit_codes = @{} - - if($msi) { - $code = $msi.code - $exe = "msiexec"; - $arg = @("/norestart", "/x $code") - if($msi.silent) { - $arg += '/qn', 'ALLUSERS=2', 'MSIINSTALLPERUSER=1' - } else { - $arg += '/qb-!' - } - - $continue_exit_codes.1605 = 'not installed, skipping' - $continue_exit_codes.3010 = 'restart required' - } elseif($uninstaller) { - $exe = "$dir\$($uninstaller.file)" - $arg = args $uninstaller.args - if(!(is_in_dir $dir $exe)) { - warn "Error in manifest: Installer $exe is outside the app directory, skipping." - $exe = $null; - } elseif(!(test-path $exe)) { - warn "Uninstaller $exe is missing, skipping." - $exe = $null; - } + if($uninstaller) { + $exe = "$dir\$($uninstaller.file)" + $arg = args $uninstaller.args + if(!(is_in_dir $dir $exe)) { + warn "Error in manifest: Installer $exe is outside the app directory, skipping." + $exe = $null; + } elseif(!(test-path $exe)) { + warn "Uninstaller $exe is missing, skipping." + $exe = $null; } if($exe) { diff --git a/lib/manifest.ps1 b/lib/manifest.ps1 index f2455d43fa..60f828e879 100644 --- a/lib/manifest.ps1 +++ b/lib/manifest.ps1 @@ -114,7 +114,6 @@ function generate_user_manifest($app, $bucket, $version) { function url($manifest, $arch) { arch_specific 'url' $manifest $arch } function installer($manifest, $arch) { arch_specific 'installer' $manifest $arch } function uninstaller($manifest, $arch) { arch_specific 'uninstaller' $manifest $arch } -function msi($manifest, $arch) { arch_specific 'msi' $manifest $arch } function hash($manifest, $arch) { arch_specific 'hash' $manifest $arch } function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch} function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch} diff --git a/schema.json b/schema.json index 6e0d568c23..30d581188b 100644 --- a/schema.json +++ b/schema.json @@ -125,10 +125,6 @@ "installer": { "$ref": "#/definitions/installer" }, - "msi": { - "$ref": "#/definitions/stringOrArrayOfStrings", - "description": "Deprecated" - }, "post_install": { "$ref": "#/definitions/stringOrArrayOfStrings" }, @@ -316,26 +312,50 @@ ] }, "installer": { - "additionalProperties": false, - "properties": { - "_comment": { - "description": "Undocumented: only used in scoop-extras/oraclejdk* and scoop-extras/appengine-go", - "type": "string" - }, - "args": { - "$ref": "#/definitions/stringOrArrayOfStrings" - }, - "file": { - "type": "string" - }, - "script": { - "$ref": "#/definitions/stringOrArrayOfStrings" + "anyOf": [ + { + "enum": [ + "inno", + "nsis", + "wix" + ] }, - "keep": { - "type": "boolean" + { + "additionalProperties": false, + "properties": { + "_comment": { + "description": "Undocumented: only used in scoop-extras/oraclejdk* and scoop-extras/appengine-go", + "type": "string" + }, + "args": { + "$ref": "#/definitions/stringOrArrayOfStrings" + }, + "file": { + "type": "string" + }, + "script": { + "$ref": "#/definitions/stringOrArrayOfStrings" + }, + "keep": { + "type": "boolean" + }, + "type": { + "enum": [ + "inno", + "nsis", + "wix" + ] + }, + "include": { + "$ref": "#/definitions/stringOrArrayOfStrings" + }, + "exclude": { + "$ref": "#/definitions/stringOrArrayOfStrings" + } + }, + "type": "object" } - }, - "type": "object" + ] }, "stringOrArrayOfStrings": { "anyOf": [ @@ -548,7 +568,7 @@ "type": "string" }, "innosetup": { - "description": "True if the installer InnoSetup based. Found in https://github.com/ScoopInstaller/Main/search?l=JSON&q=innosetup", + "description": "Deprecated. Use 'installer.type.inno' instead.", "type": "boolean" }, "installer": { @@ -557,10 +577,6 @@ "license": { "$ref": "#/definitions/license" }, - "msi": { - "$ref": "#/definitions/stringOrArrayOfStrings", - "description": "Deprecated" - }, "notes": { "$ref": "#/definitions/stringOrArrayOfStrings" }, diff --git a/test/Scoop-Decompress.Tests.ps1 b/test/Scoop-Decompress.Tests.ps1 index a80041f6b3..4dd92eec36 100644 --- a/test/Scoop-Decompress.Tests.ps1 +++ b/test/Scoop-Decompress.Tests.ps1 @@ -6,17 +6,40 @@ $isUnix = is_unix -function test_extract($extract_fn, $from, $removal) { - $to = (strip_ext $from) -replace '\.tar$', '' - & $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal - return $to +Describe 'Requirement function' -Tag 'Scoop' { + It 'Test 7zip requirement' { + Test-7zipRequirement @{ url = 'test.7z' } '64bit' | Should -BeTrue + Test-7zipRequirement @{ installer = @{ type = 'nsis' } } '64bit' | Should -BeTrue + Test-7zipRequirement @{ url = 'test.exe' } '64bit' | Should -BeFalse + Test-7zipRequirement -File 'test.xz' | Should -BeTrue + Test-7zipRequirement -File 'test.bin' | Should -BeFalse + } + It 'Test lessmsi requirement' { + Mock get_config { $true } + Test-LessmsiRequirement @{ url = 'test.msi' } '64bit' | Should -BeTrue + Test-LessmsiRequirement @{ url = 'test.exe' } '64bit' | Should -BeFalse + } + It 'Test innounp requirement' { + Test-InnounpRequirement @{ installer = @{ type = 'inno' } } '64bit' | Should -BeTrue + Test-InnounpRequirement @{ } '64bit' | Should -BeFalse + } + It 'Test dark requirement' { + Test-DarkRequirement @{ installer = @{ type = 'wix' } } '64bit' | Should -BeTrue + Test-DarkRequirement @{ } '64bit' | Should -BeFalse + } } Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { BeforeAll { $working_dir = setup_working 'decompress' - It "Decompression test cases should exist" { + function test_extract($extract_fn, $from, $removal) { + $to = (strip_ext $from) -replace '\.tar$', '' + & $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal + return $to + } + + It 'Decompression test cases should exist' { $testcases = "$working_dir\TestCases.zip" $testcases | Should -Exist compute_hash $testcases 'sha256' | Should -Be '3a442e85b466833eeafbd08c57d8f51bf7ff041867ee0bdb7db1f12480b3624a' @@ -26,7 +49,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { } } - Context "7zip extraction" { + Context '7zip extraction' { BeforeAll { if ($env:CI) { @@ -40,42 +63,42 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { $test4 = "$working_dir\7ZipTest4.tar.gz" } - It "extract normal compressed file" -Skip:$isUnix { - $to = test_extract "Expand-7zipArchive" $test1 + It 'extract normal compressed file' -Skip:$isUnix { + $to = test_extract 'Expand-7zipArchive' $test1 $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "extract nested compressed file" -Skip:$isUnix { + It 'extract nested compressed file' -Skip:$isUnix { # file ext: tgz - $to = test_extract "Expand-7zipArchive" $test2 + $to = test_extract 'Expand-7zipArchive' $test2 $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 # file ext: tar.bz2 - $to = test_extract "Expand-7zipArchive" $test3 + $to = test_extract 'Expand-7zipArchive' $test3 $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "extract nested compressed file with different inner name" -Skip:$isUnix { - $to = test_extract "Expand-7zipArchive" $test4 + It 'extract nested compressed file with different inner name' -Skip:$isUnix { + $to = test_extract 'Expand-7zipArchive' $test4 $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { + It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { $test1 | Should -Exist - test_extract "Expand-7zipArchive" $test1 $true + test_extract 'Expand-7zipArchive' $test1 $true $test1 | Should -Not -Exist } } - Context "zstd extraction" { + Context 'zstd extraction' { BeforeAll { if ($env:CI) { @@ -88,28 +111,28 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { $test2 = "$working_dir\ZstdTest.tar.zst" } - It "extract normal compressed file" -Skip:$isUnix { - $to = test_extract "Expand-ZstdArchive" $test1 + It 'extract normal compressed file' -Skip:$isUnix { + $to = test_extract 'Expand-ZstdArchive' $test1 $to | Should -Exist "$to\ZstdTest" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "extract nested compressed file" -Skip:$isUnix { - $to = test_extract "Expand-ZstdArchive" $test2 + It 'extract nested compressed file' -Skip:$isUnix { + $to = test_extract 'Expand-ZstdArchive' $test2 $to | Should -Exist "$to\ZstdTest" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { + It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { $test1 | Should -Exist - test_extract "Expand-ZstdArchive" $test1 $true + test_extract 'Expand-ZstdArchive' $test1 $true $test1 | Should -Not -Exist } } - Context "msi extraction" { + Context 'msi extraction' { BeforeAll { if ($env:CI) { @@ -121,29 +144,29 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { $test2 = "$working_dir\MSITestNull.msi" } - It "extract normal MSI file" -Skip:$isUnix { + It 'extract normal MSI file' -Skip:$isUnix { Mock get_config { $false } - $to = test_extract "Expand-MsiArchive" $test1 + $to = test_extract 'Expand-MsiArchive' $test1 $to | Should -Exist "$to\MSITest\empty" | Should -Exist (Get-ChildItem "$to\MSITest").Count | Should -Be 1 } - It "extract empty MSI file using lessmsi" -Skip:$isUnix { + It 'extract empty MSI file using lessmsi' -Skip:$isUnix { Mock get_config { $true } - $to = test_extract "Expand-MsiArchive" $test2 + $to = test_extract 'Expand-MsiArchive' $test2 $to | Should -Exist } - It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { + It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { Mock get_config { $false } $test1 | Should -Exist - test_extract "Expand-MsiArchive" $test1 $true + test_extract 'Expand-MsiArchive' $test1 $true $test1 | Should -Not -Exist } } - Context "inno extraction" { + Context 'inno extraction' { BeforeAll { if ($env:CI) { @@ -154,36 +177,36 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { $test = "$working_dir\InnoTest.exe" } - It "extract Inno Setup file" -Skip:$isUnix { - $to = test_extract "Expand-InnoArchive" $test + It 'extract Inno Setup file' -Skip:$isUnix { + $to = test_extract 'Expand-InnoArchive' $test $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { + It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { $test | Should -Exist - test_extract "Expand-InnoArchive" $test $true + test_extract 'Expand-InnoArchive' $test $true $test | Should -Not -Exist } } - Context "zip extraction" { + Context 'zip extraction' { BeforeAll { $test = "$working_dir\ZipTest.zip" } - It "extract compressed file" -Skip:$isUnix { - $to = test_extract "Expand-ZipArchive" $test + It 'extract compressed file' -Skip:$isUnix { + $to = test_extract 'Expand-ZipArchive' $test $to | Should -Exist "$to\empty" | Should -Exist (Get-ChildItem $to).Count | Should -Be 1 } - It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { + It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { $test | Should -Exist - test_extract "Expand-ZipArchive" $test $true + test_extract 'Expand-ZipArchive' $test $true $test | Should -Not -Exist } }