Update
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -305,6 +306,443 @@ def _process_control_ps_lines(manifest: dict[str, Any], phase: str) -> list[str]
|
||||
]
|
||||
|
||||
|
||||
INTERACTIVE_LAUNCHER_CSHARP = r"""using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
public sealed class AssetManagerLaunchResult
|
||||
{
|
||||
public bool UserFound { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public int SessionId { get; set; }
|
||||
public int ProcessId { get; set; }
|
||||
public int ErrorCode { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
public static class AssetManagerInteractiveLauncher
|
||||
{
|
||||
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
|
||||
private const int WTS_CURRENT_SERVER = 0;
|
||||
private const int WTS_USER_NAME = 5;
|
||||
private const int WTS_DOMAIN_NAME = 7;
|
||||
private const int WTS_ACTIVE = 0;
|
||||
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct WTS_SESSION_INFO
|
||||
{
|
||||
public int SessionID;
|
||||
public IntPtr pWinStationName;
|
||||
public int State;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
public int cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public int dwX;
|
||||
public int dwY;
|
||||
public int dwXSize;
|
||||
public int dwYSize;
|
||||
public int dwXCountChars;
|
||||
public int dwYCountChars;
|
||||
public int dwFillAttribute;
|
||||
public int dwFlags;
|
||||
public short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint WTSGetActiveConsoleSessionId();
|
||||
|
||||
[DllImport("wtsapi32.dll", SetLastError = true)]
|
||||
private static extern bool WTSEnumerateSessionsW(
|
||||
IntPtr hServer,
|
||||
int Reserved,
|
||||
int Version,
|
||||
out IntPtr ppSessionInfo,
|
||||
out int pCount
|
||||
);
|
||||
|
||||
[DllImport("wtsapi32.dll")]
|
||||
private static extern void WTSFreeMemory(IntPtr pMemory);
|
||||
|
||||
[DllImport("wtsapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern bool WTSQuerySessionInformationW(
|
||||
IntPtr hServer,
|
||||
int sessionId,
|
||||
int wtsInfoClass,
|
||||
out IntPtr ppBuffer,
|
||||
out int pBytesReturned
|
||||
);
|
||||
|
||||
[DllImport("wtsapi32.dll", SetLastError = true)]
|
||||
private static extern bool WTSQueryUserToken(uint sessionId, out IntPtr token);
|
||||
|
||||
[DllImport("userenv.dll", SetLastError = true)]
|
||||
private static extern bool CreateEnvironmentBlock(
|
||||
out IntPtr lpEnvironment,
|
||||
IntPtr hToken,
|
||||
bool bInherit
|
||||
);
|
||||
|
||||
[DllImport("userenv.dll", SetLastError = true)]
|
||||
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
|
||||
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern bool CreateProcessAsUserW(
|
||||
IntPtr hToken,
|
||||
string lpApplicationName,
|
||||
StringBuilder lpCommandLine,
|
||||
IntPtr lpProcessAttributes,
|
||||
IntPtr lpThreadAttributes,
|
||||
bool bInheritHandles,
|
||||
uint dwCreationFlags,
|
||||
IntPtr lpEnvironment,
|
||||
string lpCurrentDirectory,
|
||||
ref STARTUPINFO lpStartupInfo,
|
||||
out PROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
private static string QuerySessionString(int sessionId, int infoClass)
|
||||
{
|
||||
IntPtr buffer = IntPtr.Zero;
|
||||
int bytes = 0;
|
||||
try
|
||||
{
|
||||
if (!WTSQuerySessionInformationW(
|
||||
new IntPtr(WTS_CURRENT_SERVER),
|
||||
sessionId,
|
||||
infoClass,
|
||||
out buffer,
|
||||
out bytes
|
||||
))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
if (buffer == IntPtr.Zero || bytes <= 1)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return Marshal.PtrToStringUni(buffer) ?? string.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (buffer != IntPtr.Zero)
|
||||
{
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string SessionUserName(int sessionId)
|
||||
{
|
||||
string user = QuerySessionString(sessionId, WTS_USER_NAME).Trim();
|
||||
if (user.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
string domain = QuerySessionString(sessionId, WTS_DOMAIN_NAME).Trim();
|
||||
return domain.Length == 0 ? user : domain + "\\" + user;
|
||||
}
|
||||
|
||||
private static int FindActiveSession(out string userName)
|
||||
{
|
||||
userName = string.Empty;
|
||||
uint consoleId = WTSGetActiveConsoleSessionId();
|
||||
IntPtr buffer = IntPtr.Zero;
|
||||
int count = 0;
|
||||
if (WTSEnumerateSessionsW(
|
||||
new IntPtr(WTS_CURRENT_SERVER),
|
||||
0,
|
||||
1,
|
||||
out buffer,
|
||||
out count
|
||||
))
|
||||
{
|
||||
int firstActiveSession = -1;
|
||||
string firstActiveUser = string.Empty;
|
||||
try
|
||||
{
|
||||
int size = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
|
||||
IntPtr current = buffer;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WTS_SESSION_INFO row = (WTS_SESSION_INFO)Marshal.PtrToStructure(
|
||||
current,
|
||||
typeof(WTS_SESSION_INFO)
|
||||
);
|
||||
current = new IntPtr(current.ToInt64() + size);
|
||||
if (row.State != WTS_ACTIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string user = SessionUserName(row.SessionID);
|
||||
if (user.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (consoleId != INVALID_SESSION_ID && row.SessionID == (int)consoleId)
|
||||
{
|
||||
userName = user;
|
||||
return row.SessionID;
|
||||
}
|
||||
if (firstActiveSession < 0)
|
||||
{
|
||||
firstActiveSession = row.SessionID;
|
||||
firstActiveUser = user;
|
||||
}
|
||||
}
|
||||
if (firstActiveSession >= 0)
|
||||
{
|
||||
userName = firstActiveUser;
|
||||
return firstActiveSession;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (buffer != IntPtr.Zero)
|
||||
{
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (consoleId != INVALID_SESSION_ID)
|
||||
{
|
||||
string consoleUser = SessionUserName((int)consoleId);
|
||||
if (consoleUser.Length > 0)
|
||||
{
|
||||
userName = consoleUser;
|
||||
return (int)consoleId;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static string QuoteCommandArgument(string value)
|
||||
{
|
||||
return "\"" + (value ?? string.Empty).Replace("\"", "\\\"") + "\"";
|
||||
}
|
||||
|
||||
public static AssetManagerLaunchResult Launch(
|
||||
string applicationPath,
|
||||
string arguments,
|
||||
string workingDirectory
|
||||
)
|
||||
{
|
||||
AssetManagerLaunchResult result = new AssetManagerLaunchResult();
|
||||
result.UserName = string.Empty;
|
||||
result.ErrorMessage = string.Empty;
|
||||
|
||||
string userName;
|
||||
int sessionId = FindActiveSession(out userName);
|
||||
if (sessionId < 0)
|
||||
{
|
||||
result.UserFound = false;
|
||||
result.ErrorMessage = "No active interactive user session found.";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.UserFound = true;
|
||||
result.SessionId = sessionId;
|
||||
result.UserName = userName;
|
||||
|
||||
IntPtr userToken = IntPtr.Zero;
|
||||
IntPtr environment = IntPtr.Zero;
|
||||
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
|
||||
try
|
||||
{
|
||||
if (!WTSQueryUserToken((uint)sessionId, out userToken))
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
result.ErrorCode = error;
|
||||
result.ErrorMessage = "WTSQueryUserToken failed: " + new Win32Exception(error).Message;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!CreateEnvironmentBlock(out environment, userToken, false))
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
result.ErrorCode = error;
|
||||
result.ErrorMessage = "CreateEnvironmentBlock failed: " + new Win32Exception(error).Message;
|
||||
return result;
|
||||
}
|
||||
|
||||
STARTUPINFO startup = new STARTUPINFO();
|
||||
startup.cb = Marshal.SizeOf(typeof(STARTUPINFO));
|
||||
startup.lpDesktop = "winsta0\\default";
|
||||
|
||||
string currentDirectory = workingDirectory;
|
||||
if (string.IsNullOrWhiteSpace(currentDirectory))
|
||||
{
|
||||
currentDirectory = Path.GetDirectoryName(applicationPath);
|
||||
}
|
||||
|
||||
StringBuilder commandLine = new StringBuilder();
|
||||
commandLine.Append(QuoteCommandArgument(applicationPath));
|
||||
if (!string.IsNullOrWhiteSpace(arguments))
|
||||
{
|
||||
commandLine.Append(" ");
|
||||
commandLine.Append(arguments);
|
||||
}
|
||||
|
||||
bool created = CreateProcessAsUserW(
|
||||
userToken,
|
||||
applicationPath,
|
||||
commandLine,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
false,
|
||||
CREATE_UNICODE_ENVIRONMENT,
|
||||
environment,
|
||||
currentDirectory,
|
||||
ref startup,
|
||||
out processInfo
|
||||
);
|
||||
if (!created)
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
result.ErrorCode = error;
|
||||
result.ErrorMessage = "CreateProcessAsUser failed: " + new Win32Exception(error).Message;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
result.ProcessId = processInfo.dwProcessId;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (processInfo.hThread != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
if (processInfo.hProcess != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(processInfo.hProcess);
|
||||
}
|
||||
if (environment != IntPtr.Zero)
|
||||
{
|
||||
DestroyEnvironmentBlock(environment);
|
||||
}
|
||||
if (userToken != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(userToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def _manifest_bool(value: Any, default: bool = False) -> bool:
|
||||
if value is None:
|
||||
return bool(default)
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() in {"1", "true", "yes", "on"}
|
||||
return bool(value)
|
||||
|
||||
|
||||
def _clean_manifest_text(value: Any, limit: int) -> str:
|
||||
text = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f]+", "", str(value or "")).strip()
|
||||
return text[:limit]
|
||||
|
||||
|
||||
def _post_install_values(manifest: dict[str, Any]) -> tuple[bool, str, str, bool, bool]:
|
||||
post_install = manifest.get("post_install") or {}
|
||||
if not isinstance(post_install, dict):
|
||||
post_install = {}
|
||||
return (
|
||||
_manifest_bool(post_install.get("start_application"), False),
|
||||
_clean_manifest_text(post_install.get("executable"), 1024),
|
||||
_clean_manifest_text(post_install.get("arguments"), 2048),
|
||||
_manifest_bool(post_install.get("only_if_user_logged_on"), True),
|
||||
_manifest_bool(post_install.get("fail_job_on_error"), False),
|
||||
)
|
||||
|
||||
|
||||
def _interactive_launch_ps_lines(manifest: dict[str, Any]) -> list[str]:
|
||||
start_application, executable, arguments, only_if_user_logged_on, fail_job_on_error = _post_install_values(manifest)
|
||||
if not start_application or not executable:
|
||||
return []
|
||||
launcher_source = base64.b64encode(INTERACTIVE_LAUNCHER_CSHARP.encode("utf-8")).decode("ascii")
|
||||
return [
|
||||
f"$postInstallExecutable = {_ps_quote(executable)}",
|
||||
f"$postInstallArguments = {_ps_quote(arguments)}",
|
||||
"$postInstallOnlyIfUserLoggedOn = $" + ("true" if only_if_user_logged_on else "false"),
|
||||
"$postInstallFailJobOnError = $" + ("true" if fail_job_on_error else "false"),
|
||||
"$postInstallStartStatus = 'pending'",
|
||||
"$postInstallStartUser = ''",
|
||||
"$postInstallStartPid = 0",
|
||||
"",
|
||||
"function Invoke-PackagePostInstallStart {",
|
||||
" if ($rebootRequired) {",
|
||||
" $script:postInstallStartStatus = 'skipped_reboot'",
|
||||
" Write-JobLog 'Application start skipped because a reboot is required.' | Out-Null",
|
||||
" return",
|
||||
" }",
|
||||
" $applicationPath = [Environment]::ExpandEnvironmentVariables([string]$postInstallExecutable)",
|
||||
" if (-not [System.IO.File]::Exists($applicationPath)) {",
|
||||
" $script:postInstallStartStatus = 'executable_not_found'",
|
||||
" $message = 'Post-install application executable not found: ' + $applicationPath",
|
||||
" Write-JobLog $message | Out-Null",
|
||||
" if ($postInstallFailJobOnError) { throw $message }",
|
||||
" return",
|
||||
" }",
|
||||
" if (-not ([System.Management.Automation.PSTypeName]'AssetManagerInteractiveLauncher').Type) {",
|
||||
f" $launcherSource = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String({_ps_quote(launcher_source)}))",
|
||||
" Add-Type -TypeDefinition $launcherSource -Language CSharp -ErrorAction Stop",
|
||||
" }",
|
||||
" $workingDirectory = [System.IO.Path]::GetDirectoryName($applicationPath)",
|
||||
" if ([string]::IsNullOrWhiteSpace($workingDirectory)) { $workingDirectory = $PSScriptRoot }",
|
||||
" $launchResult = [AssetManagerInteractiveLauncher]::Launch($applicationPath, [string]$postInstallArguments, $workingDirectory)",
|
||||
" if (-not $launchResult.UserFound) {",
|
||||
" $script:postInstallStartStatus = 'skipped_no_user'",
|
||||
" Write-JobLog 'No active interactive user session found. Application start skipped.' | Out-Null",
|
||||
" if (-not $postInstallOnlyIfUserLoggedOn -and $postInstallFailJobOnError) { throw 'No active interactive user session found for application start.' }",
|
||||
" return",
|
||||
" }",
|
||||
" $script:postInstallStartUser = [string]$launchResult.UserName",
|
||||
" Write-JobLog ('Logged-on user for application start: ' + [string]$launchResult.UserName + '; session=' + [string]$launchResult.SessionId) | Out-Null",
|
||||
" if (-not $launchResult.Success) {",
|
||||
" $script:postInstallStartStatus = 'failed'",
|
||||
" $message = 'Application start failed: ' + [string]$launchResult.ErrorMessage + '; Win32=' + [string]$launchResult.ErrorCode",
|
||||
" Write-JobLog $message | Out-Null",
|
||||
" if ($postInstallFailJobOnError) { throw $message }",
|
||||
" return",
|
||||
" }",
|
||||
" $script:postInstallStartStatus = 'started'",
|
||||
" $script:postInstallStartPid = [int]$launchResult.ProcessId",
|
||||
" Write-JobLog ('Application start requested successfully: ' + $applicationPath + '; PID=' + [string]$launchResult.ProcessId) | Out-Null",
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
|
||||
|
||||
def normalize_package_manifest(manifest: dict[str, Any]) -> tuple[dict[str, Any], list[str]]:
|
||||
if not isinstance(manifest, dict):
|
||||
raise ValueError("package manifest is not an object")
|
||||
@@ -361,6 +799,26 @@ def normalize_package_manifest(manifest: dict[str, Any]) -> tuple[dict[str, Any]
|
||||
arguments = _append_install_argument(arguments, "/FORCECLOSEAPPLICATIONS", r"(^|\s)/(NO)?FORCECLOSEAPPLICATIONS(?=\s|$)")
|
||||
install["arguments"] = arguments
|
||||
|
||||
existing_post_install = result.get("post_install")
|
||||
start_configured = isinstance(existing_post_install, dict) and "start_application" in existing_post_install
|
||||
executable_configured = isinstance(existing_post_install, dict) and "executable" in existing_post_install
|
||||
post_install = result.setdefault("post_install", {})
|
||||
if not isinstance(post_install, dict):
|
||||
post_install = {}
|
||||
result["post_install"] = post_install
|
||||
if "greenshot" in identity and not start_configured:
|
||||
post_install["start_application"] = True
|
||||
notes.append("greenshot_post_install_start")
|
||||
else:
|
||||
post_install["start_application"] = _manifest_bool(post_install.get("start_application"), False)
|
||||
if "greenshot" in identity and not executable_configured:
|
||||
post_install["executable"] = r"C:\Program Files\Greenshot\Greenshot.exe"
|
||||
else:
|
||||
post_install["executable"] = _clean_manifest_text(post_install.get("executable"), 1024)
|
||||
post_install["arguments"] = _clean_manifest_text(post_install.get("arguments"), 2048)
|
||||
post_install["only_if_user_logged_on"] = _manifest_bool(post_install.get("only_if_user_logged_on"), True)
|
||||
post_install["fail_job_on_error"] = _manifest_bool(post_install.get("fail_job_on_error"), False)
|
||||
|
||||
return result, notes
|
||||
|
||||
def delete_package_storage(package_id: int) -> None:
|
||||
@@ -794,6 +1252,31 @@ def update_package_process_control(
|
||||
return manifest
|
||||
|
||||
|
||||
def update_package_post_install(
|
||||
package_id: int,
|
||||
start_application: bool,
|
||||
executable: str,
|
||||
arguments: str = "",
|
||||
only_if_user_logged_on: bool = True,
|
||||
fail_job_on_error: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
manifest = load_package_manifest(package_id)
|
||||
manifest["post_install"] = {
|
||||
"start_application": bool(start_application),
|
||||
"executable": _clean_manifest_text(executable, 1024),
|
||||
"arguments": _clean_manifest_text(arguments, 2048),
|
||||
"only_if_user_logged_on": bool(only_if_user_logged_on),
|
||||
"fail_job_on_error": bool(fail_job_on_error),
|
||||
}
|
||||
manifest, _notes = normalize_package_manifest(manifest)
|
||||
manifest_path = package_directory(package_id) / "package.json"
|
||||
manifest_path.write_text(
|
||||
json.dumps(manifest, ensure_ascii=True, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return manifest
|
||||
|
||||
|
||||
def build_deployment_user_script(manifest: dict[str, Any], action: str) -> str:
|
||||
action = str(action or "").strip().lower()
|
||||
if action not in {"install", "uninstall", "reinstall"}:
|
||||
@@ -862,6 +1345,12 @@ def build_deployment_user_script(manifest: dict[str, Any], action: str) -> str:
|
||||
" return [bool]$last",
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
|
||||
if action in {"install", "reinstall"}:
|
||||
lines += _interactive_launch_ps_lines(manifest)
|
||||
|
||||
lines += [
|
||||
"Write-JobLog (\"Software deployment started: $deploymentPackage; action=$deploymentAction\")",
|
||||
]
|
||||
|
||||
@@ -872,6 +1361,16 @@ def build_deployment_user_script(manifest: dict[str, Any], action: str) -> str:
|
||||
"$detected = Wait-PackageDetection $true 30",
|
||||
"if ($detected -eq $false) { throw 'Installation completed but the detection rule did not find the software.' }",
|
||||
]
|
||||
if _interactive_launch_ps_lines(manifest):
|
||||
lines += [
|
||||
"try { Invoke-PackagePostInstallStart }",
|
||||
"catch {",
|
||||
" $script:postInstallStartStatus = 'failed'",
|
||||
" $message = 'Post-install application start error: ' + [string]$_.Exception.Message",
|
||||
" Write-JobLog $message | Out-Null",
|
||||
" if ($postInstallFailJobOnError) { throw }",
|
||||
"}",
|
||||
]
|
||||
elif action == "uninstall":
|
||||
lines += [
|
||||
f"$deploymentExitCode = [int](Invoke-PackagePowerShell {ps(uninstall_script)})",
|
||||
@@ -890,6 +1389,16 @@ def build_deployment_user_script(manifest: dict[str, Any], action: str) -> str:
|
||||
"$detected = Wait-PackageDetection $true 30",
|
||||
"if ($detected -eq $false) { throw 'Reinstallation completed but the detection rule did not find the software.' }",
|
||||
]
|
||||
if _interactive_launch_ps_lines(manifest):
|
||||
lines += [
|
||||
"try { Invoke-PackagePostInstallStart }",
|
||||
"catch {",
|
||||
" $script:postInstallStartStatus = 'failed'",
|
||||
" $message = 'Post-install application start error: ' + [string]$_.Exception.Message",
|
||||
" Write-JobLog $message | Out-Null",
|
||||
" if ($postInstallFailJobOnError) { throw }",
|
||||
"}",
|
||||
]
|
||||
|
||||
lines += [
|
||||
"$JobResult['deployment_action'] = $deploymentAction",
|
||||
@@ -897,6 +1406,14 @@ def build_deployment_user_script(manifest: dict[str, Any], action: str) -> str:
|
||||
"$JobResult['process_exit_code'] = $deploymentExitCode",
|
||||
"$JobResult['reboot_required'] = $rebootRequired",
|
||||
"if ($null -ne $detected) { $JobResult['detected_after_action'] = [bool]$detected }",
|
||||
]
|
||||
if action in {"install", "reinstall"} and _interactive_launch_ps_lines(manifest):
|
||||
lines += [
|
||||
"$JobResult['post_install_start_status'] = $postInstallStartStatus",
|
||||
"if (-not [string]::IsNullOrWhiteSpace($postInstallStartUser)) { $JobResult['post_install_start_user'] = $postInstallStartUser }",
|
||||
"if ($postInstallStartPid -gt 0) { $JobResult['post_install_start_pid'] = $postInstallStartPid }",
|
||||
]
|
||||
lines += [
|
||||
"Write-JobLog (\"Software deployment completed: $deploymentPackage; action=$deploymentAction; reboot=$rebootRequired\")",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
Reference in New Issue
Block a user