-
Notifications
You must be signed in to change notification settings - Fork 384
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #390 from chocolatey/core_extension
Core extension
- Loading branch information
Showing
12 changed files
with
437 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# CHANGELOG | ||
|
||
## Version 1.0 | ||
|
||
- Merged `mm-choco.extension`. | ||
- Merged `chocolatey-uninstall.extension`. | ||
- Added `Get-PackageCacheLocation` | ||
- Added `CHANGELOG.md` and `README.md`. | ||
- Refactoring and more documentation. | ||
|
||
## Version 0.1.3 | ||
|
||
- `Get-WebContent` - Download file with choco internals. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# chocolatey-core.extension | ||
|
||
|
||
This is the Powershell module that extends Chocolatey with new functions. | ||
|
||
|
||
## Installation | ||
|
||
Install via chocolatey: `choco install chocolatey-core.extension`. | ||
|
||
The module is usually automatically installed as a dependency. | ||
|
||
|
||
## Usage | ||
|
||
To create a package that uses a extension function add the following to the `nuspec` specification: | ||
|
||
<dependencies> | ||
<dependency id="chocolatey-core.extension" version="1.0" /> | ||
</dependencies> | ||
|
||
**NOTE**: Make sure you use adequate _minimum_ version. | ||
|
||
To test the functions you can import the module directly or via the `chocolateyInstaller.psm1` module: | ||
|
||
PS> import-module $Env:ChocolateyInstall\helpers\chocolateyInstaller.psm1 | ||
PS> import-module $Env:ChocolateyInstall\extensions\chocolatey-core\*.psm1 | ||
|
||
You can now test any of the functions: | ||
|
||
PS> Get-AppInstallLocation choco -Verbose | ||
|
||
VERBOSE: Trying local and machine (x32 & x64) Uninstall keys | ||
VERBOSE: Trying Program Files with 2 levels depth | ||
VERBOSE: Trying PATH | ||
C:\ProgramData\chocolatey\bin | ||
|
||
Keep in mind that function may work only in the context of the `chocolateyInstaller.ps1`. | ||
|
||
To get the list of functions, load the module directly and invoke the following command: | ||
|
||
Get-Command -Module chocolatey-core | ||
|
||
To get the help for the specific function use `man`: | ||
|
||
man Get-UninstallRegistryKey | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
extensions/chocolatey-core.extension/extensions/Get-AppInstallLocation.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<# | ||
.SYNOPSIS | ||
Get application install location | ||
.DESCRIPTION | ||
Function tries to find install location in multiple places. It returns $null if all fail. The following | ||
locations are tried: | ||
- local and machine (x32 & x64) various Uninstall keys | ||
- x32 & x64 Program Files up to the 2nd level of depth | ||
- native commands available via PATH | ||
- locale and machine registry key SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths | ||
Use Verbose parameter to see which of the above locations was used for the result, if any. | ||
.EXAMPLE | ||
PS> Get-AppInstallLocation choco | ||
Returns the install location of the application 'choco'. | ||
.OUTPUTS | ||
[String] or $null | ||
#> | ||
function Get-AppInstallLocation { | ||
[CmdletBinding()] | ||
param( | ||
# Regular expression pattern | ||
[ValidateNotNullOrEmpty()] | ||
[string] $AppNamePattern | ||
) | ||
|
||
function strip($path) { if ($path.EndsWith('\')) { return $path -replace '.$' } else { $path } } | ||
|
||
$ErrorActionPreference = "SilentlyContinue" | ||
|
||
Write-Verbose "Trying local and machine (x32 & x64) Uninstall keys" | ||
[array] $key = Get-UninstallRegistryKey $AppNamePattern | ||
if ($key.Count -eq 1) { | ||
Write-Verbose "Trying Uninstall key property 'InstallLocation'" | ||
$location = $key.InstallLocation | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
|
||
Write-Verbose "Trying Uninstall key property 'UninstallString'" | ||
$location = $key.UninstallString.Replace('"', '') | ||
if ($location) { $location = Split-Path $location } | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
|
||
Write-Verbose "Trying Uninstall key property 'DisplayIcon'" | ||
$location = $key.DisplayIcon | ||
if ($location) { $location = Split-Path $location } | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
} else { Write-Verbose "Found $($key.Count) keys, aborting this method" } | ||
|
||
$dirs = $Env:ProgramFiles, "$Env:ProgramFiles\*\*" | ||
if (Get-ProcessorBits 64) { $dirs += ${ENV:ProgramFiles(x86)}, "${ENV:ProgramFiles(x86)}\*\*" } | ||
Write-Verbose "Trying Program Files with 2 levels depth: $dirs" | ||
$location = (ls $dirs | ? {$_.PsIsContainer}) -match $AppNamePattern | select -First 1 | % {$_.FullName} | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
|
||
Write-Verbose "Trying native commands on PATH" | ||
$location = (Get-Command -CommandType Application) -match $AppNamePattern | select -First 1 | % { Split-Path $_.Source } | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
|
||
$appPaths = "\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" | ||
Write-Verbose "Trying Registry: $appPaths" | ||
$location = (ls "HKCU:\$appPaths", "HKLM:\$appPaths") -match $AppNamePattern | select -First 1 | ||
if ($location) { $location = Split-Path $location } | ||
if ($location -and (Test-Path $location)) { return strip $location } | ||
|
||
Write-Verbose "No location found" | ||
} |
37 changes: 37 additions & 0 deletions
37
extensions/chocolatey-core.extension/extensions/Get-PackageCacheLocation.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<# | ||
.SYNOPSIS | ||
Get temporary location for the package based on its name and version. | ||
|
||
.DESCRIPTION | ||
The function returns package cache directory within $Env:TEMP. It will not create the directory | ||
if it doesn't exist. | ||
|
||
This function is useful when you have to obtain the file using `Get-ChocolateyWebFile` in order | ||
to perform certain installation steps that other helpers can't do. | ||
|
||
.EXAMPLE | ||
Get-PackageCacheLocation | ||
|
||
.OUTPUTS | ||
[String] | ||
|
||
.LINKS | ||
Get-ChocolateyWebFile | ||
#> | ||
function Get-PackageCacheLocation { | ||
[CmdletBinding()] | ||
param ( | ||
# Name of the package, by default $Env:ChocolateyPackageName | ||
[string] $Name = $Env:ChocolateyPackageName, | ||
# Version of the package, by default $Env:ChocolateyPackageVersion | ||
[string] $Version = $Env:ChocolateyPackageVersion | ||
) | ||
|
||
if (!$Name) { Write-Warning 'Environment variable $Env:ChocolateyPackageName is not set' } | ||
$res = Join-Path $Env:TEMP $Name | ||
|
||
if (!$Version) { Write-Warning 'Environment variable $Env:ChocolateyPackageVersion is not set' } | ||
$res = Join-Path $packageTemp $Version | ||
|
||
$res | ||
} |
24 changes: 24 additions & 0 deletions
24
extensions/chocolatey-core.extension/extensions/Get-PackageParameters.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<# | ||
.SYNOPSIS | ||
Parses parameters of the package | ||
|
||
.EXAMPLE | ||
Get-PackageParameters "/Shortcut /InstallDir:'c:\program files\xyz' /NoStartup" | set r | ||
if ($r.Shortcut) {... } | ||
Write-Host $r.InstallDir | ||
|
||
.OUTPUTS | ||
[HashTable] | ||
#> | ||
function Get-PackageParameters([string] $Parameters = $Env:ChocolateyPackageParameters) { | ||
$res = @{} | ||
$re = "\/([a-zA-Z]+)(:([`"'])?([a-zA-Z0-9- _\\:\.]+)([`"'])?)?" | ||
$results = $Parameters | sls $re -AllMatches | % Matches | ||
foreach ($m in $results) { | ||
$a = $m.Value -split ':' | ||
$opt = $a[0].Substring(1); $val = $a[1..100] | ||
if ($val -match '^(".+")|(''.+'')$') {$val = $val -replace '^.|.$'} | ||
$res[ $opt ] = if ($val) { $val } else { $true } | ||
} | ||
$res | ||
} |
94 changes: 94 additions & 0 deletions
94
extensions/chocolatey-core.extension/extensions/Get-UninstallRegistryKey.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<# | ||
.SYNOPSIS | ||
Retrieve registry key(s) for system-installed applications from an exact or wildcard search. | ||
|
||
.DESCRIPTION | ||
This function will attempt to retrieve a matching registry key for an already installed application, | ||
usually to be used with a chocolateyUninstall.ps1 automation script. | ||
|
||
The function also prevents `Get-ItemProperty` from failing when handling wrongly encoded registry keys. | ||
|
||
.PARAMETER SoftwareName | ||
Part or all of the Display Name as you see it in Programs and Features. | ||
It should be enough to be unique. | ||
|
||
If the display name contains a version number, such as "Launchy (2.5)", it is recommended you use a | ||
fuzzy search `"Launchy (*)"` (the wildcard `*`) so if Launchy auto-updates or is updated outside | ||
of chocolatey, the uninstall script will not fail. | ||
|
||
Take care not to abuse fuzzy/glob pattern searches. Be conscious of programs that may have shared | ||
or common root words to prevent overmatching. For example, "SketchUp*" would match two keys with software | ||
names "SketchUp 2016" and "SketchUp Viewer" that are different programs released by the same company. | ||
|
||
.INPUTS | ||
System.String | ||
|
||
.OUTPUTS | ||
PSCustomObject | ||
|
||
.EXAMPLE | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "VLC media player" | ||
$key.UninstallString | ||
|
||
Exact match: software name in Programs and Features is "VLC media player" | ||
|
||
.EXAMPLE | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "Gpg4win (*)" | ||
$key.UninstallString | ||
|
||
Version match: software name is "Gpg4Win (2.3.0)" | ||
|
||
.EXAMPLE | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "SketchUp [0-9]*" | ||
$key.UninstallString | ||
|
||
Version match: software name is "SketchUp 2016" | ||
Note that the similar software name "SketchUp Viewer" would not be matched. | ||
|
||
.LINK | ||
Uninstall-ChocolateyPackage | ||
#> | ||
function Get-UninstallRegistryKey { | ||
[CmdletBinding()] | ||
param( | ||
[Parameter(Mandatory=$true, ValueFromPipeline=$true)] | ||
[ValidateNotNullOrEmpty()] | ||
[string] $SoftwareName | ||
) | ||
Write-Debug "Running 'Get-UninstallRegistryKey' for `'$env:ChocolateyPackageName`' with SoftwareName:`'$SoftwareName`'"; | ||
|
||
$ErrorActionPreference = 'Stop' | ||
$local_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
$machine_key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
$machine_key6432 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
|
||
Write-Verbose "Retrieving all uninstall registry keys" | ||
[array]$keys = Get-ChildItem -Path @($machine_key6432, $machine_key, $local_key) -ea 0 | ||
Write-Debug "Registry uninstall keys on system: $($keys.Count)" | ||
|
||
Write-Debug "Error handling check: `'Get-ItemProperty`' fails if a registry key is encoded incorrectly." | ||
[int]$maxAttempts = $keys.Count | ||
for ([int]$attempt = 1; $attempt -le $maxAttempts; $attempt++) | ||
{ | ||
$success = $false | ||
|
||
try { | ||
[array]$foundKey = Get-ItemProperty -Path $keys.PsPath -ea 0 | ? { $_.DisplayName -like $SoftwareName } | ||
$success = $true | ||
} catch { | ||
Write-Debug "Found bad key." | ||
foreach ($key in $keys){ try{ Get-ItemProperty $key.PsPath > $null } catch { $badKey = $key.PsPath }} | ||
Write-Verbose "Skipping bad key: $badKey" | ||
[array]$keys = $keys | ? { $badKey -NotContains $_.PsPath } | ||
} | ||
|
||
if ($success) { break; } | ||
if ($attempt -eq 10) { | ||
Write-Warning "Found more than 10 bad registry keys. Run command again with `'--verbose --debug`' for more info." | ||
Write-Debug "Each key searched should correspond to an installed program. It is very unlikely to have more than a few programs with incorrectly encoded keys, if any at all. This may be indicative of one or more corrupted registry branches." | ||
} | ||
} | ||
|
||
Write-Debug "Found $($foundKey.Count) uninstall registry key(s) with SoftwareName:`'$SoftwareName`'"; | ||
return $foundKey | ||
} |
Oops, something went wrong.