Add afterInstall.ps1 and update tool paths to APPDATA

Introduces afterInstall.ps1 for post-install setup, downloading and extracting required tools to the APPDATA\WelsonJS directory. Updates code in http.js, python3.js, ovftool.js, wamr.js, and system.js to reference binaries from the new APPDATA location. Modifies setup.iss to run the PowerShell script after installation.
This commit is contained in:
Namhyeon Go 2025-11-20 16:44:20 +09:00
parent 6f172aafe1
commit 33c64d50c9
7 changed files with 341 additions and 22 deletions

297
afterInstall.ps1 Normal file
View File

@ -0,0 +1,297 @@
# ================================
# CONFIGURATION
# ================================
$AppName = "welsonjs"
$TargetDir = Join-Path $env:APPDATA $AppName
$TmpDir = Join-Path $env:TEMP "$AppName-downloads"
Write-Host ""
Write-Host "[*] Target directory : $TargetDir"
Write-Host "[*] Temporary directory: $TmpDir"
Write-Host ""
# Ensure base directories exist
New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
# ================================
# ARCHITECTURE DETECTION
# ================================
$arch = "x86"
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { $arch = "x64" }
elseif ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { $arch = "arm64" }
if ($env:PROCESSOR_ARCHITEW6432) { $arch = "x64" }
Write-Host "[*] Detected architecture: $arch"
Write-Host ""
# ================================
# HELPER FUNCTIONS
# ================================
function Ensure-EmptyDirectory {
param(
[Parameter(Mandatory=$true)]
[string]$Path
)
# If a file exists at the path, remove it
if (Test-Path $Path -PathType Leaf) {
Write-Host "[WARN] A file exists at path '$Path'. Deleting it..."
Remove-Item -Path $Path -Force
}
# Ensure directory exists
if (-not (Test-Path $Path -PathType Container)) {
Write-Host "[*] Creating directory: $Path"
New-Item -ItemType Directory -Path $Path -Force | Out-Null
}
}
function Download-File {
param(
[Parameter(Mandatory=$true)]
[string]$Url,
[Parameter(Mandatory=$true)]
[string]$Destination
)
Write-Host "[*] Downloading file..."
Write-Host " URL : $Url"
Write-Host " OUT : $Destination"
try {
Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing -ErrorAction Stop
Write-Host "[OK] Download completed."
Write-Host ""
}
catch {
Write-Host "[ERROR] Failed to download: $Url"
Write-Host $_.Exception.Message
throw
}
}
function Extract-Zip {
param(
[Parameter(Mandatory=$true)]
[string]$ZipPath,
[Parameter(Mandatory=$true)]
[string]$DestDir
)
Write-Host "[*] Extracting ZIP:"
Write-Host " $ZipPath"
Write-Host " -> $DestDir"
# Make sure destination directory exists and is a directory
Ensure-EmptyDirectory -Path $DestDir
# Temporary extraction folder inside destination
$TmpExtract = Join-Path $DestDir "__tmp_extract__"
Ensure-EmptyDirectory -Path $TmpExtract
Write-Host "[DEBUG] PowerShell command:"
Write-Host " Expand-Archive -LiteralPath `"$ZipPath`" -DestinationPath `"$TmpExtract`" -Force"
try {
Expand-Archive -LiteralPath $ZipPath -DestinationPath $TmpExtract -Force -ErrorAction Stop
}
catch {
Write-Host "[ERROR] Failed to extract ZIP with Expand-Archive: $ZipPath"
Write-Host $_.Exception.Message
throw
}
# Check extracted entries
$entries = Get-ChildItem -LiteralPath $TmpExtract
if (-not $entries -or $entries.Count -eq 0) {
Write-Host "[ERROR] No entries were extracted from ZIP: $ZipPath"
throw "ZIP appears to be empty or extraction failed."
}
if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) {
Write-Host "[*] Detected single root directory inside ZIP. Flattening..."
# Move children of the single root directory into destination
$innerItems = Get-ChildItem -LiteralPath $entries[0].FullName
foreach ($item in $innerItems) {
Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force
}
}
else {
Write-Host "[*] ZIP has multiple top-level entries. Copying all..."
foreach ($item in $entries) {
Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force
}
}
# Clean up temp extraction folder
Remove-Item -LiteralPath $TmpExtract -Recurse -Force
Write-Host "[OK] ZIP extraction completed."
Write-Host ""
}
function Extract-TarGz {
param(
[Parameter(Mandatory=$true)]
[string]$TarGzPath,
[Parameter(Mandatory=$true)]
[string]$DestDir
)
Write-Host "[*] Extracting TAR.GZ:"
Write-Host " $TarGzPath"
Write-Host " -> $DestDir"
Ensure-EmptyDirectory -Path $DestDir
# Modern Windows ships with tar, but we validate its presence
$tarCmd = Get-Command tar -ErrorAction SilentlyContinue
if (-not $tarCmd) {
Write-Host "[ERROR] 'tar' command not found. Cannot extract TAR.GZ."
throw "tar command not found."
}
Write-Host "[DEBUG] tar command:"
Write-Host " tar -xzf `"$TarGzPath`" -C `"$DestDir`""
try {
& tar -xzf "$TarGzPath" -C "$DestDir"
if ($LASTEXITCODE -ne 0) {
throw "tar exit code $LASTEXITCODE"
}
Write-Host "[OK] TAR.GZ extraction completed."
Write-Host ""
}
catch {
Write-Host "[ERROR] Failed to extract TAR.GZ: $TarGzPath"
Write-Host $_.Exception.Message
throw
}
}
# ================================
# SET DOWNLOAD URLS BASED ON ARCH
# ================================
$PythonUrl = $null
$CurlUrl = $null
$YaraUrl = $null
$WamrUrl = $null
switch ($arch) {
"x64" {
# Python embeddable (x64)
$PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-amd64.zip"
# curl (x64, mingw)
$CurlUrl = "https://curl.se/windows/latest.cgi?p=win64-mingw.zip"
# YARA (x64, GitHub — as you specified)
$YaraUrl = "https://github.com/VirusTotal/yara/releases/download/v4.5.5/yara-4.5.5-2368-win64.zip"
# WAMR (x64 only)
$WamrUrl = "https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR-2.4.3/iwasm-2.4.3-x86_64-windows-2022.tar.gz"
}
"arm64" {
# Python embeddable (ARM64)
$PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-arm64.zip"
# curl (ARM64)
$CurlUrl = "https://curl.se/windows/latest.cgi?p=win64a-mingw.zip"
# DO NOT install YARA/WAMR on ARM64
$YaraUrl = $null
$WamrUrl = $null
}
default {
# Treat anything else as x86
# Python embeddable (x86)
$PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-win32.zip"
# curl (x86)
$CurlUrl = "https://downloads.sourceforge.net/project/muldersoft/cURL/curl-8.17.0-win-x86-full.2025-11-09.zip";
# Do NOT install YARA/WAMR on x86 (same policy as before)
$YaraUrl = $null
$WamrUrl = $null
}
}
Write-Host "[*] Python URL: $PythonUrl"
Write-Host "[*] curl URL : $CurlUrl"
if ($YaraUrl) {
Write-Host "[*] YARA URL : $YaraUrl"
} else {
Write-Host "[*] YARA : skipped on this architecture"
}
if ($WamrUrl) {
Write-Host "[*] WAMR URL : $WamrUrl"
} else {
Write-Host "[*] WAMR : skipped on this architecture"
}
Write-Host ""
# ================================
# DOWNLOAD FILES
# ================================
$PythonZip = Join-Path $TmpDir "python.zip"
$CurlZip = Join-Path $TmpDir "curl.zip"
$YaraZip = Join-Path $TmpDir "yara.zip"
$WamrTgz = Join-Path $TmpDir "wamr.tar.gz"
try {
Download-File -Url $PythonUrl -Destination $PythonZip
Download-File -Url $CurlUrl -Destination $CurlZip
if ($YaraUrl) {
Download-File -Url $YaraUrl -Destination $YaraZip
}
if ($WamrUrl) {
Download-File -Url $WamrUrl -Destination $WamrTgz
}
}
catch {
Write-Host "[FATAL] Download phase failed."
exit 1
}
# ================================
# EXTRACT FILES
# ================================
try {
Extract-Zip -ZipPath $PythonZip -DestDir (Join-Path $TargetDir "python")
Extract-Zip -ZipPath $CurlZip -DestDir (Join-Path $TargetDir "curl")
if ($YaraUrl) {
Extract-Zip -ZipPath $YaraZip -DestDir (Join-Path $TargetDir "yara")
}
if ($WamrUrl) {
Extract-TarGz -TarGzPath $WamrTgz -DestDir (Join-Path $TargetDir "wamr")
}
}
catch {
Write-Host "[FATAL] Extraction phase failed."
exit 1
}
# ================================
# FINISH
# ================================
Write-Host "[*] All tools installed successfully."
Write-Host "[*] Installed into: $TargetDir"
Write-Host ""
exit 0

View File

@ -119,16 +119,21 @@ var HTTPObject = function(engine) {
} else if (this.engine == "CURL") { } else if (this.engine == "CURL") {
this._interface = SHELL.create(); this._interface = SHELL.create();
// the location of cURL binary // set cURL executable file location
var arch = SYS.getArch(); var default_curl_path = SYS.getAppDataDir() + "\\curl\\curl.exe";
if (arch.toLowerCase().indexOf("arm") > -1) { if (FILE.fileExists(default_curl_path)) {
this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe"); this.setBinPath(default_curl_path);
} else if (arch.indexOf("64") > -1) {
this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe");
} else { } else {
this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe"); var arch = SYS.getArch();
if (arch.toLowerCase().indexOf("arm") > -1) {
this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe");
} else if (arch.indexOf("64") > -1) {
this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe");
} else {
this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe");
}
} }
// do not clear after calling the `exec` // do not clear after calling the `exec`
this._interface.setIsPreventClear(true); this._interface.setIsPreventClear(true);
} else if (this.engine == "BITS") { } else if (this.engine == "BITS") {

View File

@ -1,16 +1,17 @@
// ovftool.js // ovftool.js
// Copyright 2019-2025, Namhyeon Go <gnh1201@catswords.re.kr> and the WelsonJS contributors. // Namhyeon Go <gnh1201@catswords.re.kr> and the WelsonJS contributors.
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// https://github.com/gnh1201/welsonjs // https://github.com/gnh1201/welsonjs
// //
// Download OVFTool (Open Virtualization Format (OVF) Tool): // Download OVFTool (Open Virtualization Format (OVF) Tool):
// https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest // https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest
// //
var SYS = require("lib/system");
var SHELL = require("lib/shell"); var SHELL = require("lib/shell");
var CRED = require("lib/credentials"); var CRED = require("lib/credentials");
function OVFObject() { function OVFObject() {
this.binPath = "bin\\x64\\VMware-ovftool-4.6.3-24031167-win.x86_64\\ovftool\\ovftool.exe"; this.binPath = SYS.getAppDataDir() + "\\ovftool\\ovftool.exe";
this.hostname = ""; this.hostname = "";
this.port = 443; this.port = 443;
this.resourceName = ""; this.resourceName = "";
@ -66,7 +67,7 @@ function create() {
exports.setCredential = setCredential; exports.setCredential = setCredential;
exports.create = create; exports.create = create;
exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.2"; exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.3";
exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.AUTHOR = "gnh1201@catswords.re.kr";
exports.global = global; exports.global = global;
exports.require = global.require; exports.require = global.require;

View File

@ -13,13 +13,18 @@ function PythonObject() {
this.version = "3.13.2"; this.version = "3.13.2";
this.create = function() { this.create = function() {
var arch = SYS.getArch(); var default_python3_path = SYS.getAppDataDir() + "\\python\\python.exe";
if (arch.toLowerCase().indexOf("arm") > -1) { if (FILE.fileExists(default_python3_path)) {
this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe"); this.setBinPath(default_python3_path);
} else if (arch.indexOf("64") > -1) {
this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe");
} else { } else {
this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe"); var arch = SYS.getArch();
if (arch.toLowerCase().indexOf("arm") > -1) {
this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe");
} else if (arch.indexOf("64") > -1) {
this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe");
} else {
this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe");
}
} }
}; };
@ -57,6 +62,6 @@ exports.execScript = function(scriptName, args) {
return (new PythonObject()).execScript(scriptName, args); return (new PythonObject()).execScript(scriptName, args);
}; };
exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.1"; exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.2";
exports.global = global; exports.global = global;
exports.require = global.require; exports.require = global.require;

View File

@ -37,6 +37,10 @@ function getEnvString(envName) {
})(envName.toUpperCase()); })(envName.toUpperCase());
} }
function getAppDataDir() {
return getEnvString("APPDATA") + "\\WelsonJS";
}
function get32BitFolder() { function get32BitFolder() {
var base = getEnvString("WINDIR"); var base = getEnvString("WINDIR");
var syswow64 = base + "\\SysWOW64\\"; var syswow64 = base + "\\SysWOW64\\";
@ -198,6 +202,7 @@ function getProcessVersion() {
exports.createProcess = createProcess; exports.createProcess = createProcess;
exports.getEnvString = getEnvString; exports.getEnvString = getEnvString;
exports.getAppDataDir = getAppDataDir;
exports.get32BitFolder = get32BitFolder; exports.get32BitFolder = get32BitFolder;
exports.isElevated = isElevated; exports.isElevated = isElevated;
exports.getOS = getOS; exports.getOS = getOS;
@ -219,6 +224,6 @@ exports.createShortcut = createShortcut;
exports.ping = ping; exports.ping = ping;
exports.getProcessVersion = getProcessVersion; exports.getProcessVersion = getProcessVersion;
exports.VERSIONINFO = "System Module (system.js) version 0.1.5"; exports.VERSIONINFO = "System Module (system.js) version 0.1.6";
exports.global = global; exports.global = global;
exports.require = global.require; exports.require = global.require;

View File

@ -1,15 +1,16 @@
// wamr.js // wamr.js
// Copyright 2019-2025, Namhyeon Go <gnh1201@catswords.re.kr> and the WelsonJS contributors. // Namhyeon Go <gnh1201@catswords.re.kr> and the WelsonJS contributors.
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// https://github.com/gnh1201/welsonjs // https://github.com/gnh1201/welsonjs
// //
// WAMR(WebAssembly Micro Runtime) integration for WelsonJS framework // WAMR(WebAssembly Micro Runtime) integration
// https://github.com/bytecodealliance/wasm-micro-runtime // https://github.com/bytecodealliance/wasm-micro-runtime
// //
var SYS = require("lib/system");
var SHELL = require("lib/shell"); var SHELL = require("lib/shell");
var WAMRObject = function() { var WAMRObject = function() {
this.binPath = "bin\\iwasm"; this.binPath = SYS.getAppDataDir() + "\\wamr\\iwasm.exe";
this.verbose = 0; this.verbose = 0;
this.stackSize = 0; this.stackSize = 0;
@ -60,3 +61,7 @@ var WAMRObject = function() {
exports.create = function() { exports.create = function() {
return new WAMRObject(); return new WAMRObject();
}; };
exports.VERSIONINFO = "WAMR(WebAssembly Micro Runtime) integration (wamr.js) version 0.1.1";
exports.global = global;
exports.require = global.require;

View File

@ -84,6 +84,7 @@ Name: "{group}\Uninstall {cm:AppName}"; Filename: "{uninstallexe}"; AfterInstall
; Filename: {app}\bin\gtk2-runtime-2.24.33-2021-01-30-ts-win64.exe; ; Filename: {app}\bin\gtk2-runtime-2.24.33-2021-01-30-ts-win64.exe;
; Filename: {app}\bin\nmap-7.92\VC_redist.x86.exe; ; Filename: {app}\bin\nmap-7.92\VC_redist.x86.exe;
; Filename: {app}\bin\nmap-7.92\npcap-1.50.exe; ; Filename: {app}\bin\nmap-7.92\npcap-1.50.exe;
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -NoProfile -File ""{app}\afterInstall.ps1"""; WorkingDir: "{app}"; Flags: nowait
Filename: {app}\installService.bat; Flags: nowait Filename: {app}\installService.bat; Flags: nowait
Filename: {app}\bin\x86\WelsonJS.Launcher.exe; Flags: nowait Filename: {app}\bin\x86\WelsonJS.Launcher.exe; Flags: nowait