Skip to content

Commit

Permalink
(chocolatey#1185) Add ability to run hook scripts
Browse files Browse the repository at this point in the history
This adds the code to run Powershell scripts (hook scripts) before and
after each type of package automation script runs. The hook scripts can
be for all packages, or for a specific package ID. They are put in
$env:ChocolateyInstall\hooks, or subfolders thereof.

The filename determines when they are run:
"<pre|post>-<install|beforemodify|uninstall>-<packageID|all>.ps1"
  • Loading branch information
TheCakeIsNaOH committed Mar 21, 2022
1 parent fc28da3 commit db39298
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 15 deletions.
24 changes: 21 additions & 3 deletions src/chocolatey.resources/helpers/chocolateyScriptRunner.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
[switch] $overrideArgs = $false,
[alias("x86")][switch] $forceX86 = $false,
[alias("params","parameters","pkgParams")][string]$packageParameters = '',
[string]$packageScript
[string]$packageScript,
[string[]]$preRunHookScripts,
[string[]]$postRunHookScripts
)

$global:DebugPreference = "SilentlyContinue"
Expand Down Expand Up @@ -45,8 +47,17 @@ $7zip = Join-Path $chocoTools '7z.exe'
$ShimGen = Join-Path $chocoTools 'shimgen.exe'
$checksumExe = Join-Path $chocoTools 'checksum.exe'

Write-Debug "Running `'$packageScript`'";
& "$packageScript"
if ($PSBoundParameters.ContainsKey('preRunHookScripts')) {
$preRunHookScripts.foreach({
Write-Debug "Running Pre-Run Hook `'$_`'";
& "$_"
})
}

if ($PSBoundParameters.ContainsKey('packageScript')) {
Write-Debug "Running package script `'$packageScript`'";
& "$packageScript"
}
$scriptSuccess = $?
$lastExecutableExitCode = $LASTEXITCODE

Expand All @@ -71,6 +82,13 @@ if ($exitCode -ne $null -and $exitCode -ne '' -and $exitCode -ne 0) {
Set-PowerShellExitCode $exitCode
}

if ($PSBoundParameters.ContainsKey('postRunHookScripts')) {
$postRunHookScripts.foreach({
Write-Debug "Running Post-Run Hook `'$_`'";
& "$_"
})
}

Write-Debug '----------------------------------------------------------------------'

Exit $exitCode
2 changes: 2 additions & 0 deletions src/chocolatey/infrastructure.app/ApplicationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ public static class ApplicationParameters
public static readonly string ChocolateyPackageInfoStoreLocation = _fileSystem.combine_paths(InstallLocation, ".chocolatey");
public static readonly string ExtensionsLocation = _fileSystem.combine_paths(InstallLocation, "extensions");
public static readonly string TemplatesLocation = _fileSystem.combine_paths(InstallLocation, "templates");
public static readonly string HooksLocation = _fileSystem.combine_paths(InstallLocation, "hooks");
public static readonly string HookPackageIdExtension = ".hook";
public static readonly string ChocolateyCommunityFeedPushSourceOld = "https://chocolatey.org/";
public static readonly string ChocolateyCommunityFeedPushSource = "https://push.chocolatey.org/";
public static readonly string ChocolateyCommunityGalleryUrl = "https://community.chocolatey.org/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace chocolatey.infrastructure.app.services
{
using System;
using System.Collections.Generic;
using System.Management.Automation.Runspaces;
using configuration;
using NuGet;
Expand Down Expand Up @@ -67,6 +68,6 @@ public interface IPowershellService
bool before_modify(ChocolateyConfiguration configuration, PackageResult packageResult);

void prepare_powershell_environment(IPackage package, ChocolateyConfiguration configuration, string packageDirectory);
PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action<Pipeline> additionalActionsBeforeScript);
PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action<Pipeline> additionalActionsBeforeScript, IEnumerable<string> hookPreScriptPathList, IEnumerable<string> hookPostScriptPathList);
}
}
64 changes: 53 additions & 11 deletions src/chocolatey/infrastructure.app/services/PowershellService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,41 @@ private string get_script_for_action(PackageResult packageResult, CommandNameTyp
return string.Empty;
}

private IEnumerable<string> get_hook_scripts(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command, bool isPre)
{
List<string> hookScriptPaths = new List<string>();
string filenameBase;

if (isPre)
{
filenameBase = "pre-";
}
else
{
filenameBase = "post-";
}

switch (command)
{
case CommandNameType.install:
filenameBase += "install-";
break;
case CommandNameType.uninstall:
filenameBase += "uninstall-";
break;
case CommandNameType.upgrade:
filenameBase += "beforemodify-";
break;
default:
throw new ApplicationException("Could not find CommandNameType to get hook scripts");
}

hookScriptPaths.AddRange(_fileSystem.get_files(ApplicationParameters.HooksLocation, "{0}all.ps1".format_with(filenameBase), SearchOption.AllDirectories));
hookScriptPaths.AddRange(_fileSystem.get_files(ApplicationParameters.HooksLocation, "{0}{1}.ps1".format_with(filenameBase, packageResult.Name), SearchOption.AllDirectories));

return hookScriptPaths;
}

public void noop_action(PackageResult packageResult, CommandNameType command)
{
var chocoInstall = get_script_for_action(packageResult, command);
Expand Down Expand Up @@ -134,7 +169,7 @@ private string get_helpers_folder()
return _fileSystem.combine_paths(ApplicationParameters.InstallLocation, "helpers");
}

public string wrap_script_with_module(string script, ChocolateyConfiguration config)
public string wrap_script_with_module(string script, IEnumerable<string> hookPreScriptPathList, IEnumerable<string> hookPostScriptPathList, ChocolateyConfiguration config)
{
var installerModule = _fileSystem.combine_paths(get_helpers_folder(), "chocolateyInstaller.psm1");
var scriptRunner = _fileSystem.combine_paths(get_helpers_folder(), "chocolateyScriptRunner.ps1");
Expand All @@ -148,18 +183,20 @@ public string wrap_script_with_module(string script, ChocolateyConfiguration con
installerModule,
scriptRunner,
string.IsNullOrWhiteSpace(_customImports) ? string.Empty : "& {0}".format_with(_customImports.EndsWith(";") ? _customImports : _customImports + ";"),
get_script_arguments(script, config)
get_script_arguments(script, hookPreScriptPathList, hookPostScriptPathList, config)
);
}

private string get_script_arguments(string script, ChocolateyConfiguration config)
private string get_script_arguments(string script, IEnumerable<string> hookPreScriptPathList, IEnumerable<string> hookPostScriptPathList, ChocolateyConfiguration config)
{
return "-packageScript '{0}' -installArguments '{1}' -packageParameters '{2}'{3}{4}".format_with(
return "-packageScript '{0}' -installArguments '{1}' -packageParameters '{2}'{3}{4}{5}{6}".format_with(
script,
prepare_powershell_arguments(config.InstallArguments),
prepare_powershell_arguments(config.PackageParameters),
config.ForceX86 ? " -forceX86" : string.Empty,
config.OverrideArguments ? " -overrideArgs" : string.Empty
config.OverrideArguments ? " -overrideArgs" : string.Empty,
hookPreScriptPathList.Any() ? " -preRunHookScripts {0}".format_with(string.Join(",", hookPreScriptPathList)) : string.Empty,
hookPostScriptPathList.Any() ? " -postRunHookScripts {0}".format_with(string.Join(",", hookPostScriptPathList)) : string.Empty
);
}

Expand Down Expand Up @@ -192,6 +229,10 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack
}

var chocoPowerShellScript = get_script_for_action(packageResult, command);

var hookPreScriptPathList = get_hook_scripts(configuration, packageResult, command, true);
var hookPostScriptPathList = get_hook_scripts(configuration, packageResult, command, false);

if (!string.IsNullOrEmpty(chocoPowerShellScript))
{
var failure = false;
Expand Down Expand Up @@ -266,8 +307,8 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack
try
{
result = configuration.Features.UsePowerShellHost
? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript, null), result)
: run_external_powershell(configuration, chocoPowerShellScript);
? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript, null, hookPreScriptPathList, hookPostScriptPathList), result)
: run_external_powershell(configuration, chocoPowerShellScript, hookPreScriptPathList, hookPostScriptPathList);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -328,11 +369,11 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack
return installerRun;
}

private PowerShellExecutionResults run_external_powershell(ChocolateyConfiguration configuration, string chocoPowerShellScript)
private PowerShellExecutionResults run_external_powershell(ChocolateyConfiguration configuration, string chocoPowerShellScript, IEnumerable<string> hookPreScriptPathList, IEnumerable<string> hookPostScriptPathList)
{
var result = new PowerShellExecutionResults();
result.ExitCode = PowershellExecutor.execute(
wrap_script_with_module(chocoPowerShellScript, configuration),
wrap_script_with_module(chocoPowerShellScript, hookPreScriptPathList, hookPostScriptPathList, configuration),
_fileSystem,
configuration.CommandExecutionTimeoutSeconds,
(s, e) =>
Expand Down Expand Up @@ -533,14 +574,15 @@ private void remove_assembly_resolver()
}
}

public PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action<Pipeline> additionalActionsBeforeScript)
public PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action<Pipeline> additionalActionsBeforeScript, IEnumerable<string> hookPreScriptPathList, IEnumerable<string> hookPostScriptPathList)
{
// since we control output in the host, always set these true
Environment.SetEnvironmentVariable("ChocolateyEnvironmentDebug", "true");
Environment.SetEnvironmentVariable("ChocolateyEnvironmentVerbose", "true");

var result = new PowerShellExecutionResults();
string commandToRun = wrap_script_with_module(chocoPowerShellScript, config);

string commandToRun = wrap_script_with_module(chocoPowerShellScript, hookPreScriptPathList, hookPostScriptPathList, config);
var host = new PoshHost(config);
this.Log().Debug(() => "Calling built-in PowerShell host with ['{0}']".format_with(commandToRun.escape_curly_braces()));

Expand Down

0 comments on commit db39298

Please sign in to comment.