Added windows installer

This commit is contained in:
Eliezer Croitoru 2026-09-09 17:23:01 +03:00
parent 9621a23c79
commit e19c9509d3
5 changed files with 372 additions and 0 deletions

BIN
dist/livetrace-3.3.2-setup.exe vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,30 @@
# Dockerfile.installer
# Builds the livetrace Windows installer (.exe) using NSIS on Linux.
#
# Usage (from your project root):
# docker build -f Dockerfile.installer -t livetrace-installer .
# docker run --rm -v "$PWD/dist":/out livetrace-installer
#
# The finished installer lands at ./dist/livetrace-3.3.2-setup.exe
FROM debian:bookworm-slim
# Install NSIS (makensis) — no Go needed, binary is pre-built
RUN apt-get update && \
apt-get install -y --no-install-recommends nsis && \
rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Copy the NSIS script and helper
COPY installer/livetrace-installer.nsi .
COPY installer/EnvVarUpdate.nsh .
# Copy the pre-built Windows binary from dist/
COPY dist/livetrace-windows-amd64.exe .
# Build the installer
RUN makensis livetrace-installer.nsi
# Default: copy the output to /out (bind-mount your dist/ folder there)
CMD ["sh", "-c", "cp livetrace-*.exe /out/ && echo 'Installer written to /out/'"]

142
installer/EnvVarUpdate.nsh Normal file
View File

@ -0,0 +1,142 @@
; EnvVarUpdate.nsh
; Adds/removes a value from a Windows environment variable (PATH-style, semicolon-delimited)
; Usage:
; ${EnvVarUpdate} $0 "PATH" "A" "HKLM" "$INSTDIR" ; append
; ${UnEnvVarUpdate} $0 "PATH" "HKLM" "$INSTDIR" ; remove
!ifndef ENVVARUPDATE_NSH
!define ENVVARUPDATE_NSH
!include "LogicLib.nsh"
!include "WinMessages.nsh"
; --------------------------------------------------------------------------
; Macro: _EnvVarUpdate
; Action: "A" = Append, "P" = Prepend, "R" = Remove
; --------------------------------------------------------------------------
!macro _EnvVarUpdate OUTVAR EnvVarName Action RegLoc PathString
Push $0
Push $1
Push $2
Push $3
Push $4
Push $5
; Read current value
ReadRegStr $1 ${RegLoc} \
"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" \
"${EnvVarName}"
; Strip trailing semicolon
StrCpy $5 $1 1 -1
${If} $5 == ";"
StrCpy $1 $1 -1
${EndIf}
; Check if PathString already exists
Push "${PathString}"
Push $1
Call _EnvVarUpdateStrStr
Pop $2 ; $2 = position, or "" if not found
${If} "${Action}" == "A"
${If} $2 == ""
${If} $1 == ""
StrCpy $3 "${PathString}"
${Else}
StrCpy $3 "$1;${PathString}"
${EndIf}
WriteRegExpandStr ${RegLoc} \
"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" \
"${EnvVarName}" "$3"
${EndIf}
${ElseIf} "${Action}" == "P"
${If} $2 == ""
${If} $1 == ""
StrCpy $3 "${PathString}"
${Else}
StrCpy $3 "${PathString};$1"
${EndIf}
WriteRegExpandStr ${RegLoc} \
"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" \
"${EnvVarName}" "$3"
${EndIf}
${ElseIf} "${Action}" == "R"
${If} $2 != ""
; Remove entry
StrLen $4 "${PathString}"
StrCpy $0 $1 $2 ; everything before the match
IntOp $4 $2 + $4 ; position after the match
StrCpy $5 $1 "" $4 ; everything after the match
; Strip leading/trailing semicolons from join point
StrCpy $4 $5 1
${If} $4 == ";"
StrCpy $5 $5 "" 1
${EndIf}
StrCpy $4 $0 1 -1
${If} $4 == ";"
StrCpy $0 $0 -1
${EndIf}
${If} $0 == ""
StrCpy $3 $5
${ElseIf} $5 == ""
StrCpy $3 $0
${Else}
StrCpy $3 "$0;$5"
${EndIf}
WriteRegExpandStr ${RegLoc} \
"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" \
"${EnvVarName}" "$3"
${EndIf}
${EndIf}
; Broadcast change to all windows
SendMessage ${HWND_BROADCAST} ${WM_SETTINGCHANGE} 0 "STR:Environment" /TIMEOUT=5000
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
!macroend
; Helper: find substring position (returns "" if not found)
Function _EnvVarUpdateStrStr
Exch $R0 ; haystack
Exch
Exch $R1 ; needle
Push $R2
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrLen $R4 $R0
IntOp $R4 $R4 - $R3
StrCpy $R2 0
loop:
IntCmp $R2 $R4 done done
StrCpy $R5 $R0 $R3 $R2
StrCmp $R5 $R1 found
IntOp $R2 $R2 + 1
Goto loop
found:
StrCpy $R0 $R2
Goto end
done:
StrCpy $R0 ""
end:
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
!define EnvVarUpdate `!insertmacro _EnvVarUpdate`
!define UnEnvVarUpdate `!insertmacro _EnvVarUpdate "" "${__LINE__}" "R"`
!endif ; ENVVARUPDATE_NSH

62
installer/build-installer.sh Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env bash
# build-installer.sh
# Builds livetrace-3.3.2-setup.exe on Linux — natively or via Docker.
#
# Usage:
# ./installer/build-installer.sh # requires: apt install nsis
# ./installer/build-installer.sh --docker # requires: docker
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
VERSION="3.3.2"
OUTPUT="livetrace-${VERSION}-setup.exe"
# ── Docker build ───────────────────────────────────────────────────────────
if [[ "${1:-}" == "--docker" ]]; then
echo ">>> Building installer via Docker …"
docker build \
-f "${SCRIPT_DIR}/Dockerfile.installer" \
-t livetrace-installer \
"${PROJECT_ROOT}"
docker run --rm \
-v "${PROJECT_ROOT}/dist":/out \
livetrace-installer
echo ""
echo "✓ Done: ${PROJECT_ROOT}/dist/${OUTPUT}"
exit 0
fi
# ── Native build ───────────────────────────────────────────────────────────
if ! command -v makensis &>/dev/null; then
echo "ERROR: makensis not found."
echo " Install: sudo apt install nsis"
echo " Or run: $0 --docker"
exit 1
fi
SRC_EXE="${PROJECT_ROOT}/dist/livetrace-windows-amd64.exe"
if [[ ! -f "${SRC_EXE}" ]]; then
echo "ERROR: ${SRC_EXE} not found."
echo " Run your Go build first, e.g.:"
echo " GOOS=windows GOARCH=amd64 go build -o dist/livetrace-windows-amd64.exe ./src"
exit 1
fi
echo ">>> Building installer with makensis …"
# Copy binary next to the .nsi script so NSIS can find it
cp "${SRC_EXE}" "${SCRIPT_DIR}/livetrace-windows-amd64.exe"
cd "${SCRIPT_DIR}"
makensis livetrace-installer.nsi
# Move the output to dist/
mv "${SCRIPT_DIR}/${OUTPUT}" "${PROJECT_ROOT}/dist/${OUTPUT}"
rm -f "${SCRIPT_DIR}/livetrace-windows-amd64.exe"
echo ""
echo "✓ Done: ${PROJECT_ROOT}/dist/${OUTPUT}"
echo " Size: $(du -sh "${PROJECT_ROOT}/dist/${OUTPUT}" | cut -f1)"

View File

@ -0,0 +1,138 @@
; livetrace-installer.nsi
; NSIS installer for livetrace v3.3.2
; Builds on Linux (or in Docker) with: makensis livetrace-installer.nsi
;
; Place livetrace-windows-amd64.exe in the same directory before building.
Unicode True
;
; Metadata
;
!define APPNAME "livetrace"
!define APPVERSION "3.3.2"
!define PUBLISHER "ngtech1ltd"
; Source binary name (as built by Go)
!define SRC_EXE "livetrace-windows-amd64.exe"
; Name it will have after installation
!define INST_EXE "livetrace.exe"
; Registry key for Add/Remove Programs
!define REG_UNINSTALL \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
;
; Installer settings
;
Name "${APPNAME} ${APPVERSION}"
OutFile "livetrace-${APPVERSION}-setup.exe"
InstallDir "$PROGRAMFILES64\${APPNAME}"
InstallDirRegKey HKLM "${REG_UNINSTALL}" "InstallLocation"
; Administrator rights required (Program Files + system PATH)
RequestExecutionLevel admin
SetCompressor /SOLID lzma
SetCompressorDictSize 32
;
; UI
;
!include "MUI2.nsh"
!define MUI_ABORTWARNING
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
;
; Version info (visible in Properties Details)
;
VIProductVersion "3.3.2.0"
VIAddVersionKey "ProductName" "${APPNAME}"
VIAddVersionKey "ProductVersion" "${APPVERSION}"
VIAddVersionKey "CompanyName" "${PUBLISHER}"
VIAddVersionKey "FileDescription" "${APPNAME} Installer"
VIAddVersionKey "FileVersion" "${APPVERSION}"
VIAddVersionKey "LegalCopyright" "(c) ${PUBLISHER}"
;
; PATH helpers (PowerShell avoids NSIS macro
; namespace issues between install / uninstall)
;
; Append $INSTDIR to the machine-wide PATH
; Note: $$p / $$parts / $$_ are PowerShell variables $$ escapes the $ for NSIS
!macro AddToPath
DetailPrint "Adding $INSTDIR to system PATH …"
nsExec::ExecToLog \
'powershell.exe -NonInteractive -NoProfile -Command \
"$$p = [Environment]::GetEnvironmentVariable(\"Path\",\"Machine\"); \
if ($$p -notlike \"*$INSTDIR*\") { \
[Environment]::SetEnvironmentVariable(\"Path\", \"$$p;$INSTDIR\", \"Machine\") \
}"'
!macroend
; Remove $INSTDIR from the machine-wide PATH
!macro RemoveFromPath
DetailPrint "Removing $INSTDIR from system PATH …"
nsExec::ExecToLog \
'powershell.exe -NonInteractive -NoProfile -Command \
"$$parts = [Environment]::GetEnvironmentVariable(\"Path\",\"Machine\") -split \";\"; \
$$parts = $$parts | Where-Object { $$_ -ne \"$INSTDIR\" }; \
[Environment]::SetEnvironmentVariable(\"Path\", ($$parts -join \";\"), \"Machine\")"'
!macroend
;
; Install section
;
Section "livetrace (required)" SecMain
SectionIn RO ; cannot be deselected
SetOutPath "$INSTDIR"
; Copy binary rename to plain "livetrace.exe" on the way in
File "/oname=${INST_EXE}" "${SRC_EXE}"
; Add install dir to system PATH
!insertmacro AddToPath
; Register with Add/Remove Programs
WriteRegStr HKLM "${REG_UNINSTALL}" "DisplayName" "${APPNAME} ${APPVERSION}"
WriteRegStr HKLM "${REG_UNINSTALL}" "DisplayVersion" "${APPVERSION}"
WriteRegStr HKLM "${REG_UNINSTALL}" "Publisher" "${PUBLISHER}"
WriteRegStr HKLM "${REG_UNINSTALL}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "${REG_UNINSTALL}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "${REG_UNINSTALL}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S'
WriteRegDWORD HKLM "${REG_UNINSTALL}" "NoModify" 1
WriteRegDWORD HKLM "${REG_UNINSTALL}" "NoRepair" 1
; Write the uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
SectionEnd
;
; Uninstall section
;
Section "Uninstall"
; Remove from system PATH
!insertmacro RemoveFromPath
; Delete files
Delete "$INSTDIR\${INST_EXE}"
Delete "$INSTDIR\uninstall.exe"
RMDir "$INSTDIR"
; Remove Add/Remove Programs entry
DeleteRegKey HKLM "${REG_UNINSTALL}"
SectionEnd