Enhance Windows installer with MSI support and shortcut creation
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
- Updated farmcontrol-server.nsi to include desktop and Start Menu shortcut creation. - Added new macros in installer.nsh for managing shortcuts during installation and uninstallation. - Introduced build-windows-msi.ps1 script for generating MSI installers, integrating with existing build processes. - Modified finalize-desktop-artifacts.mjs to call the new MSI build script, ensuring proper artifact generation.
This commit is contained in:
parent
5c683ae576
commit
0aa26eab66
@ -1,4 +1,5 @@
|
|||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
|
!include "LogicLib.nsh"
|
||||||
!include "installer.nsh"
|
!include "installer.nsh"
|
||||||
|
|
||||||
!ifndef OUTFILE
|
!ifndef OUTFILE
|
||||||
@ -24,12 +25,20 @@ RequestExecutionLevel admin
|
|||||||
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
|
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
|
||||||
|
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
!insertmacro MUI_LANGUAGE "English"
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
Function .onInit
|
||||||
|
${If} ${Silent}
|
||||||
|
SetAutoClose true
|
||||||
|
${EndIf}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
Section "Farm Control Server" SecMain
|
Section "Farm Control Server" SecMain
|
||||||
|
SectionIn RO
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
File /r "${APP_SOURCE_DIR}\*"
|
File /r "${APP_SOURCE_DIR}\*"
|
||||||
|
|
||||||
@ -52,6 +61,20 @@ Section "Farm Control Server" SecMain
|
|||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Desktop shortcut" SecDesktop
|
||||||
|
!insertmacro createDesktopShortcut
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Start Menu shortcut" SecStartMenu
|
||||||
|
!insertmacro createStartMenuShortcut
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecMain} "Install Farm Control Server."
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} "Create a shortcut on the Desktop."
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} "Create a shortcut in the Start Menu."
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||||
|
|
||||||
Section "Uninstall"
|
Section "Uninstall"
|
||||||
!insertmacro customUnInstall
|
!insertmacro customUnInstall
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,21 @@
|
|||||||
|
!macro createDesktopShortcut
|
||||||
|
CreateShortCut "$DESKTOP\Farm Control Server.lnk" "$INSTDIR\bin\launcher.exe"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro createStartMenuShortcut
|
||||||
|
CreateDirectory "$SMPROGRAMS\Farm Control Server"
|
||||||
|
CreateShortCut "$SMPROGRAMS\Farm Control Server\Farm Control Server.lnk" "$INSTDIR\bin\launcher.exe"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro removeDesktopShortcut
|
||||||
|
Delete "$DESKTOP\Farm Control Server.lnk"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro removeStartMenuShortcut
|
||||||
|
Delete "$SMPROGRAMS\Farm Control Server\Farm Control Server.lnk"
|
||||||
|
RMDir "$SMPROGRAMS\Farm Control Server"
|
||||||
|
!macroend
|
||||||
|
|
||||||
!macro customInstall
|
!macro customInstall
|
||||||
DetailPrint "Register farmcontrolserver URI Handler"
|
DetailPrint "Register farmcontrolserver URI Handler"
|
||||||
DeleteRegKey HKCR "farmcontrolserver"
|
DeleteRegKey HKCR "farmcontrolserver"
|
||||||
@ -10,5 +28,7 @@
|
|||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro customUnInstall
|
!macro customUnInstall
|
||||||
|
!insertmacro removeDesktopShortcut
|
||||||
|
!insertmacro removeStartMenuShortcut
|
||||||
DeleteRegKey HKCR "farmcontrolserver"
|
DeleteRegKey HKCR "farmcontrolserver"
|
||||||
!macroend
|
!macroend
|
||||||
|
|||||||
35
packaging/windows/msi-wrapped.wxs
Normal file
35
packaging/windows/msi-wrapped.wxs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||||
|
<Product Id="__PRODUCT_ID__" Name="Farm Control Server" UpgradeCode="__UPGRADE_CODE__" Version="__VERSION__" Language="1033" Codepage="65001" Manufacturer="Tom Butcher">
|
||||||
|
<Package Compressed="yes" InstallerVersion="500"/>
|
||||||
|
|
||||||
|
<Condition Message="Windows 7 and above is required"><![CDATA[Installed OR VersionNT >= 601]]></Condition>
|
||||||
|
|
||||||
|
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
|
||||||
|
<MediaTemplate CompressionLevel="high" EmbedCab="yes"/>
|
||||||
|
|
||||||
|
<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>
|
||||||
|
<Property Id="MSIINSTALLPERUSER" Secure="yes" Value="1"/>
|
||||||
|
|
||||||
|
<Binary Id="WrappedExe" SourceFile="__SETUP_EXE__"/>
|
||||||
|
|
||||||
|
<CustomAction Id="RunInstaller" Return="check" Execute="deferred"
|
||||||
|
HideTarget="no" Impersonate="no" BinaryKey="WrappedExe" ExeCommand="/S"/>
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="TempFolder">
|
||||||
|
<Component Id="EmptyComponent" Guid="7145262D-7149-4F1A-9A69-F377E38F04DA" KeyPath="yes">
|
||||||
|
<CreateFolder />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Feature Id="EmptyFeature" Level="0">
|
||||||
|
<ComponentRef Id="EmptyComponent" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="RunInstaller" After="ProcessComponents" />
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
190
scripts/build-windows-msi.ps1
Normal file
190
scripts/build-windows-msi.ps1
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$SetupExe,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$OutputMsi,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Version,
|
||||||
|
|
||||||
|
[string]$UpgradeCode = "194198A0-0A65-52A3-9E49-CCDE26A5BF65",
|
||||||
|
|
||||||
|
[string]$ProductId = "F2AECE98-63E9-5A32-9B4B-9D7C6254CBA8"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
function Get-MsiVersion {
|
||||||
|
param([string]$InputVersion)
|
||||||
|
|
||||||
|
$parts = $InputVersion.Split(".")
|
||||||
|
while ($parts.Count -lt 4) {
|
||||||
|
$parts += "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($parts[0..3] -join ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
function Find-SevenZip {
|
||||||
|
$candidates = @(
|
||||||
|
"${env:ProgramFiles}\7-Zip\7z.exe",
|
||||||
|
"${env:ProgramFiles(x86)}\7-Zip\7z.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($candidate in $candidates) {
|
||||||
|
if (Test-Path $candidate) {
|
||||||
|
return $candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Ensure-WixToolset {
|
||||||
|
$cacheRoot = Join-Path $env:LOCALAPPDATA "farmcontrol-server-build"
|
||||||
|
$wixRoot = Join-Path $cacheRoot "wix-4.0.0.5512.2"
|
||||||
|
$candle = Join-Path $wixRoot "candle.exe"
|
||||||
|
$light = Join-Path $wixRoot "light.exe"
|
||||||
|
|
||||||
|
if ((Test-Path $candle) -and (Test-Path $light)) {
|
||||||
|
return @{
|
||||||
|
Candle = $candle
|
||||||
|
Light = $light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$archivePath = Join-Path $cacheRoot "wix-4.0.0.5512.2.7z"
|
||||||
|
$archiveUrl = "https://github.com/electron-userland/electron-builder-binaries/releases/download/wix-4.0.0.5512.2/wix-4.0.0.5512.2.7z"
|
||||||
|
|
||||||
|
if (-not (Test-Path $archivePath)) {
|
||||||
|
New-Item -ItemType Directory -Path $cacheRoot -Force | Out-Null
|
||||||
|
Write-Host "Downloading WiX toolset..."
|
||||||
|
Invoke-WebRequest -Uri $archiveUrl -OutFile $archivePath
|
||||||
|
}
|
||||||
|
|
||||||
|
$sevenZip = Find-SevenZip
|
||||||
|
if (-not $sevenZip) {
|
||||||
|
throw "Could not find 7-Zip. Install WiX Toolset or 7-Zip on the Windows build agent."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $wixRoot) {
|
||||||
|
Remove-Item $wixRoot -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path $wixRoot -Force | Out-Null
|
||||||
|
& $sevenZip x $archivePath "-o$wixRoot" -y | Out-Null
|
||||||
|
|
||||||
|
$candleMatches = Get-ChildItem -Path $wixRoot -Filter candle.exe -Recurse -ErrorAction SilentlyContinue
|
||||||
|
if ($candleMatches.Count -gt 0) {
|
||||||
|
$candleDir = $candleMatches[0].DirectoryName
|
||||||
|
$candle = Join-Path $candleDir "candle.exe"
|
||||||
|
$light = Join-Path $candleDir "light.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $candle) -or -not (Test-Path $light)) {
|
||||||
|
throw "WiX download did not provide candle.exe/light.exe under $wixRoot"
|
||||||
|
}
|
||||||
|
|
||||||
|
return @{
|
||||||
|
Candle = $candle
|
||||||
|
Light = $light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Find-WixToolset {
|
||||||
|
$searchRoots = @(
|
||||||
|
$env:WIX_TOOLSET_PATH,
|
||||||
|
"${env:ProgramFiles(x86)}\WiX Toolset v3.11\bin",
|
||||||
|
"${env:ProgramFiles}\WiX Toolset v3.11\bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($root in $searchRoots) {
|
||||||
|
if (-not $root) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$candle = Join-Path $root "candle.exe"
|
||||||
|
$light = Join-Path $root "light.exe"
|
||||||
|
if ((Test-Path $candle) -and (Test-Path $light)) {
|
||||||
|
return @{
|
||||||
|
Candle = $candle
|
||||||
|
Light = $light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cachedRoot = Join-Path $env:LOCALAPPDATA "farmcontrol-server-build\wix-4.0.0.5512.2"
|
||||||
|
if (Test-Path $cachedRoot) {
|
||||||
|
$cachedCandle = Get-ChildItem -Path $cachedRoot -Filter candle.exe -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||||
|
if ($cachedCandle) {
|
||||||
|
$candleDir = $cachedCandle.DirectoryName
|
||||||
|
return @{
|
||||||
|
Candle = Join-Path $candleDir "candle.exe"
|
||||||
|
Light = Join-Path $candleDir "light.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$candleCommand = Get-Command candle.exe -ErrorAction SilentlyContinue
|
||||||
|
$lightCommand = Get-Command light.exe -ErrorAction SilentlyContinue
|
||||||
|
if ($candleCommand -and $lightCommand) {
|
||||||
|
return @{
|
||||||
|
Candle = $candleCommand.Source
|
||||||
|
Light = $lightCommand.Source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ensure-WixToolset
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootDir = Split-Path -Parent $PSScriptRoot
|
||||||
|
$templatePath = Join-Path $rootDir "packaging/windows/msi-wrapped.wxs"
|
||||||
|
$workDir = Join-Path $env:TEMP "farmcontrol-server-msi"
|
||||||
|
|
||||||
|
if (Test-Path $workDir) {
|
||||||
|
Remove-Item $workDir -Recurse -Force
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Path $workDir | Out-Null
|
||||||
|
|
||||||
|
$setupExePath = (Resolve-Path -LiteralPath $SetupExe).Path
|
||||||
|
$outputMsiPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputMsi)
|
||||||
|
$outputDir = Split-Path $outputMsiPath -Parent
|
||||||
|
if (-not (Test-Path $outputDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$msiVersion = Get-MsiVersion $Version
|
||||||
|
$wxsContent = Get-Content -LiteralPath $templatePath -Raw
|
||||||
|
$wxsContent = $wxsContent.Replace("__PRODUCT_ID__", $ProductId)
|
||||||
|
$wxsContent = $wxsContent.Replace("__UPGRADE_CODE__", $UpgradeCode)
|
||||||
|
$wxsContent = $wxsContent.Replace("__VERSION__", $msiVersion)
|
||||||
|
$wxsContent = $wxsContent.Replace("__SETUP_EXE__", $setupExePath)
|
||||||
|
|
||||||
|
$projectWxs = Join-Path $workDir "project.wxs"
|
||||||
|
Set-Content -LiteralPath $projectWxs -Value $wxsContent -Encoding UTF8
|
||||||
|
|
||||||
|
$wix = Find-WixToolset
|
||||||
|
$wixObj = Join-Path $workDir "project.wixobj"
|
||||||
|
|
||||||
|
Push-Location $workDir
|
||||||
|
try {
|
||||||
|
& $wix.Candle "-arch" "x64" $projectWxs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "candle.exe failed with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
|
||||||
|
& $wix.Light "-out" $outputMsiPath "-spdb" "-sw1076" $wixObj
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "light.exe failed with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
Remove-Item $workDir -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $outputMsiPath)) {
|
||||||
|
throw "MSI installer was not created at $outputMsiPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Created MSI installer at $outputMsiPath"
|
||||||
@ -358,6 +358,51 @@ function buildWindowsNsis(appDir, arch) {
|
|||||||
return exePath;
|
return exePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildWindowsMsi(setupExePath, arch) {
|
||||||
|
const scriptPath = path.join(rootDir, "scripts/build-windows-msi.ps1");
|
||||||
|
const msiPath = path.join(
|
||||||
|
artifactDir,
|
||||||
|
getReleaseArtifactName(version, arch, "msi"),
|
||||||
|
);
|
||||||
|
const powershell =
|
||||||
|
process.env.SystemRoot
|
||||||
|
? path.join(
|
||||||
|
process.env.SystemRoot,
|
||||||
|
"System32",
|
||||||
|
"WindowsPowerShell",
|
||||||
|
"v1.0",
|
||||||
|
"powershell.exe",
|
||||||
|
)
|
||||||
|
: "powershell.exe";
|
||||||
|
|
||||||
|
const result = spawnSync(
|
||||||
|
powershell,
|
||||||
|
[
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy",
|
||||||
|
"Bypass",
|
||||||
|
"-File",
|
||||||
|
scriptPath,
|
||||||
|
"-SetupExe",
|
||||||
|
setupExePath,
|
||||||
|
"-OutputMsi",
|
||||||
|
msiPath,
|
||||||
|
"-Version",
|
||||||
|
version,
|
||||||
|
],
|
||||||
|
{ stdio: "inherit" },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.status !== 0) {
|
||||||
|
throw new Error(
|
||||||
|
`build-windows-msi.ps1 failed with exit code ${result.status ?? 1}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Published ${msiPath}`);
|
||||||
|
return msiPath;
|
||||||
|
}
|
||||||
|
|
||||||
if (buildEnv === "dev") {
|
if (buildEnv === "dev") {
|
||||||
console.log("finalize-desktop-artifacts: skipping dev build");
|
console.log("finalize-desktop-artifacts: skipping dev build");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@ -400,7 +445,8 @@ if (targetOs === "macos") {
|
|||||||
|
|
||||||
let published;
|
let published;
|
||||||
try {
|
try {
|
||||||
published = [buildWindowsNsis(appDir, arch)];
|
const setupExe = buildWindowsNsis(appDir, arch);
|
||||||
|
published = [setupExe, buildWindowsMsi(setupExe, arch)];
|
||||||
|
|
||||||
if (installerFiles.setupZip) {
|
if (installerFiles.setupZip) {
|
||||||
published.push(publishArtifact(installerFiles.setupZip, arch, "zip"));
|
published.push(publishArtifact(installerFiles.setupZip, arch, "zip"));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user