mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-12-06 14:23:41 +00:00
Renamed afterInstall.ps1 to postInstall.ps1 and added telemetry support for PostHog. Updated Python embeddable package URLs to version 3.14.0. Revised setup.iss to improve component granularity, update author info, and integrate the new post-install script and telemetry parameters.
477 lines
14 KiB
PowerShell
477 lines
14 KiB
PowerShell
# WelsonJS post-install script
|
|
# Namhyeon Go <gnh1201@catswords.re.kr>, and Catswords OSS contributors.
|
|
# https://github.com/gnh1201/welsonjs
|
|
|
|
# ================================
|
|
# PARAMETERS
|
|
# ================================
|
|
param(
|
|
[string]$TelemetryProvider = "",
|
|
[string]$TelemetryApiKey = "",
|
|
[string]$Version = "",
|
|
[string]$DistinctId = "",
|
|
[string]$Components = ""
|
|
)
|
|
|
|
# ================================
|
|
# TELEMETRY
|
|
# ================================
|
|
if ($TelemetryProvider -and $TelemetryProvider.ToLower() -eq "posthog") {
|
|
|
|
# Skip telemetry if API key is missing
|
|
if (-not $TelemetryApiKey -or $TelemetryApiKey.Trim() -eq "") {
|
|
# No-op: continue script
|
|
}
|
|
else {
|
|
|
|
# Resolve distinct ID (fallback to machine name)
|
|
$finalDistinctId = if ($DistinctId -and $DistinctId.Trim() -ne "") {
|
|
$DistinctId
|
|
} else {
|
|
$env:COMPUTERNAME
|
|
}
|
|
|
|
# Skip if distinct_id is empty (PostHog will ignore anyway)
|
|
if ($finalDistinctId -and $finalDistinctId.Trim() -ne "") {
|
|
|
|
# Parse Components into array
|
|
$componentsList = @()
|
|
if ($Components -and $Components.Trim() -ne "") {
|
|
$componentsList = $Components.Split(",") | ForEach-Object { $_.Trim() }
|
|
}
|
|
|
|
# Build single event payload for PostHog /i/v0/e endpoint
|
|
# Anonymous event is default: $process_person_profile = false
|
|
$body = @{
|
|
api_key = $TelemetryApiKey
|
|
event = "app_installed"
|
|
distinct_id = $finalDistinctId
|
|
properties = @{
|
|
"$process_person_profile" = $false # Anonymous event (no person profile)
|
|
version = $Version
|
|
os = "windows"
|
|
source = "post-install.ps1"
|
|
components = $componentsList
|
|
}
|
|
timestamp = (Get-Date).ToString("o") # ISO 8601 format
|
|
} | ConvertTo-Json -Depth 5
|
|
|
|
try {
|
|
Invoke-RestMethod `
|
|
-Uri "https://us.i.posthog.com/i/v0/e/" `
|
|
-Method Post `
|
|
-ContentType "application/json" `
|
|
-Body $body | Out-Null
|
|
}
|
|
catch {
|
|
# Ignore telemetry failure (installer must not break)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# ================================
|
|
# 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 -eq "AMD64") { $arch = "x64" }
|
|
elseif ($env:PROCESSOR_ARCHITEW6432 -eq "ARM64") { $arch = "arm64" }
|
|
|
|
Write-Host "[*] Detected architecture: $arch"
|
|
Write-Host ""
|
|
|
|
|
|
# ================================
|
|
# HELPER FUNCTIONS
|
|
# ================================
|
|
function Ensure-EmptyDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path
|
|
)
|
|
|
|
# If a file exists at this path, delete it
|
|
if (Test-Path $Path -PathType Leaf) {
|
|
Write-Host "[WARN] File exists at '$Path'. Removing..."
|
|
Remove-Item -Path $Path -Force
|
|
}
|
|
|
|
# Ensure a directory exists at this path
|
|
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]$DestinationPath
|
|
)
|
|
|
|
Write-Host "[*] Downloading file..."
|
|
Write-Host " URL : $Url"
|
|
Write-Host " OUT : $DestinationPath"
|
|
|
|
try {
|
|
Invoke-WebRequest -Uri $Url -OutFile $DestinationPath -UseBasicParsing -ErrorAction Stop
|
|
Write-Host "[OK] Download completed."
|
|
Write-Host ""
|
|
}
|
|
catch {
|
|
Write-Host "[ERROR] Failed to download: $Url"
|
|
if ($_ -is [System.Exception]) {
|
|
Write-Host $_.Exception.Message
|
|
} else {
|
|
Write-Host $_
|
|
}
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Extract-CompressedFile {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$CompressedPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationDirectory
|
|
)
|
|
|
|
Write-Host "[*] Extracting compressed file:"
|
|
Write-Host " $CompressedPath"
|
|
Write-Host " -> $DestinationDirectory"
|
|
|
|
# Ensure destination directory exists
|
|
Ensure-EmptyDirectory -Path $DestinationDirectory
|
|
|
|
# Temporary extraction workspace inside destination directory
|
|
$TemporaryExtractDirectory = Join-Path $DestinationDirectory "__tmp_extract__"
|
|
Ensure-EmptyDirectory -Path $TemporaryExtractDirectory
|
|
|
|
Write-Host "[DEBUG] Expand-Archive command:"
|
|
Write-Host " Expand-Archive -LiteralPath `"$CompressedPath`" -DestinationPath `"$TemporaryExtractDirectory`" -Force"
|
|
|
|
try {
|
|
Expand-Archive -LiteralPath $CompressedPath -DestinationPath $TemporaryExtractDirectory -Force -ErrorAction Stop
|
|
}
|
|
catch {
|
|
Write-Host "[ERROR] Failed to extract: $CompressedPath"
|
|
if ($_ -is [System.Exception]) {
|
|
Write-Host $_.Exception.Message
|
|
} else {
|
|
Write-Host $_
|
|
}
|
|
throw
|
|
}
|
|
|
|
# Move extracted content into final destination
|
|
$entries = Get-ChildItem -LiteralPath $TemporaryExtractDirectory
|
|
|
|
if (-not $entries -or $entries.Count -eq 0) {
|
|
Write-Host "[ERROR] No entries were extracted from archive: $CompressedPath"
|
|
throw "Archive appears to be empty or extraction failed."
|
|
}
|
|
|
|
if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) {
|
|
# Single root directory inside archive -> flatten
|
|
Write-Host "[*] Archive has a single root directory. Flattening..."
|
|
$innerItems = Get-ChildItem -LiteralPath $entries[0].FullName -Force
|
|
foreach ($item in $innerItems) {
|
|
Move-Item -LiteralPath $item.FullName -Destination $DestinationDirectory -Force
|
|
}
|
|
}
|
|
else {
|
|
# Multiple top-level entries
|
|
Write-Host "[*] Archive has multiple top-level entries. Moving all..."
|
|
foreach ($item in $entries) {
|
|
Move-Item -LiteralPath $item.FullName -Destination $DestinationDirectory -Force
|
|
}
|
|
}
|
|
|
|
# Clean up temporary extraction directory
|
|
Remove-Item -LiteralPath $TemporaryExtractDirectory -Recurse -Force
|
|
|
|
Write-Host "[OK] Extraction completed."
|
|
Write-Host ""
|
|
}
|
|
|
|
function Extract-TarGzArchive {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ArchivePath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationDirectory
|
|
)
|
|
|
|
Write-Host "[*] Extracting TAR.GZ archive:"
|
|
Write-Host " $ArchivePath"
|
|
Write-Host " -> $DestinationDirectory"
|
|
|
|
Ensure-EmptyDirectory -Path $DestinationDirectory
|
|
|
|
# Validate tar availability
|
|
$tarCommand = Get-Command tar -ErrorAction SilentlyContinue
|
|
if (-not $tarCommand) {
|
|
Write-Host "[ERROR] 'tar' command not found."
|
|
throw "tar not available on this system."
|
|
}
|
|
|
|
Write-Host "[DEBUG] tar command:"
|
|
Write-Host " tar -xzf `"$ArchivePath`" -C `"$DestinationDirectory`""
|
|
|
|
try {
|
|
& tar -xzf "$ArchivePath" -C "$DestinationDirectory"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "tar exited with code $LASTEXITCODE"
|
|
}
|
|
Write-Host "[OK] TAR.GZ extraction completed."
|
|
Write-Host ""
|
|
}
|
|
catch {
|
|
Write-Host "[ERROR] Failed to extract TAR.GZ archive: $ArchivePath"
|
|
if ($_ -is [System.Exception]) {
|
|
Write-Host $_.Exception.Message
|
|
} else {
|
|
Write-Host $_
|
|
}
|
|
throw
|
|
}
|
|
}
|
|
|
|
|
|
# ================================
|
|
# SET DOWNLOAD URLS BASED ON ARCH
|
|
# ================================
|
|
$PythonUrl = $null
|
|
$CurlUrl = $null
|
|
$YaraUrl = $null
|
|
$WamrUrl = $null
|
|
$WebsocatUrl = $null
|
|
$ArtifactsUrl = $null
|
|
|
|
switch ($arch) {
|
|
"x64" {
|
|
# Python embeddable (x64)
|
|
$PythonUrl = "https://www.python.org/ftp/python/3.14.0/python-3.14.0-embed-amd64.zip"
|
|
|
|
# curl (x64, mingw)
|
|
$CurlUrl = "https://curl.se/windows/latest.cgi?p=win64-mingw.zip"
|
|
|
|
# YARA (x64)
|
|
$YaraUrl = "https://github.com/VirusTotal/yara/releases/download/v4.5.5/yara-4.5.5-2368-win64.zip"
|
|
|
|
# WAMR (x64)
|
|
$WamrUrl = "https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR-2.4.3/iwasm-2.4.3-x86_64-windows-2022.tar.gz"
|
|
|
|
# websocat (x64)
|
|
$WebsocatUrl = "https://catswords.blob.core.windows.net/welsonjs/websocat-1.14.0.x86_64-pc-windows-gnu.zip"
|
|
|
|
# WelsonJS binary artifacts (x86 compatible)
|
|
$ArtifactsUrl = "https://catswords.blob.core.windows.net/welsonjs/artifacts.zip"
|
|
}
|
|
|
|
"arm64" {
|
|
# Python embeddable (ARM64)
|
|
$PythonUrl = "https://www.python.org/ftp/python/3.14.0/python-3.14.0-embed-win32.zip"
|
|
|
|
# curl (ARM64)
|
|
$CurlUrl = "https://curl.se/windows/latest.cgi?p=win64a-mingw.zip"
|
|
|
|
# No YARA / WAMR / websocat / artifacts for ARM64 in this script
|
|
$YaraUrl = $null
|
|
$WamrUrl = $null
|
|
$WebsocatUrl = $null
|
|
$ArtifactsUrl = $null
|
|
}
|
|
|
|
default {
|
|
# 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"
|
|
|
|
# YARA (x86)
|
|
$YaraUrl = "https://github.com/VirusTotal/yara/releases/download/v4.5.5/yara-4.5.5-2368-win32.zip"
|
|
|
|
# No WAMR for x86
|
|
$WamrUrl = $null
|
|
|
|
# websocat (x86)
|
|
$WebsocatUrl = "https://catswords.blob.core.windows.net/welsonjs/websocat-1.14.0.i686-pc-windows-gnu.zip"
|
|
|
|
# WelsonJS binary artifacts (x86 compatible)
|
|
$ArtifactsUrl = "https://catswords.blob.core.windows.net/welsonjs/artifacts.zip"
|
|
}
|
|
}
|
|
|
|
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"
|
|
}
|
|
if ($WebsocatUrl) {
|
|
Write-Host "[*] websocat URL : $WebsocatUrl"
|
|
} else {
|
|
Write-Host "[*] websocat : skipped on this architecture"
|
|
}
|
|
if ($ArtifactsUrl) {
|
|
Write-Host "[*] artifacts URL : $ArtifactsUrl"
|
|
} else {
|
|
Write-Host "[*] artifacts : skipped on this architecture"
|
|
}
|
|
Write-Host ""
|
|
|
|
|
|
# ================================
|
|
# DOWNLOAD FILES (websocat before artifacts)
|
|
# ================================
|
|
$PythonCompressed = Join-Path $TmpDir "python.zip"
|
|
$CurlCompressed = Join-Path $TmpDir "curl.zip"
|
|
$YaraCompressed = Join-Path $TmpDir "yara.zip"
|
|
$WamrArchive = Join-Path $TmpDir "wamr.tar.gz"
|
|
$WebsocatCompressed = Join-Path $TmpDir "websocat.zip"
|
|
$ArtifactsCompressed = Join-Path $TmpDir "artifacts.zip"
|
|
|
|
try {
|
|
# Python
|
|
Download-File -Url $PythonUrl -DestinationPath $PythonCompressed
|
|
|
|
# curl
|
|
Download-File -Url $CurlUrl -DestinationPath $CurlCompressed
|
|
|
|
# YARA (optional)
|
|
if ($YaraUrl) {
|
|
Download-File -Url $YaraUrl -DestinationPath $YaraCompressed
|
|
}
|
|
else
|
|
{
|
|
Write-Host "[*] YARA download skipped on this architecture."
|
|
}
|
|
|
|
# WAMR (optional)
|
|
if ($WamrUrl) {
|
|
Download-File -Url $WamrUrl -DestinationPath $WamrArchive
|
|
}
|
|
else
|
|
{
|
|
Write-Host "[*] WAMR download skipped on this architecture."
|
|
}
|
|
|
|
# websocat (optional)
|
|
if ($WebsocatUrl) {
|
|
Download-File -Url $WebsocatUrl -DestinationPath $WebsocatCompressed
|
|
}
|
|
else {
|
|
Write-Host "[*] websocat download skipped on this architecture."
|
|
}
|
|
|
|
# artifacts
|
|
if ($ArtifactsUrl) {
|
|
Download-File -Url $ArtifactsUrl -DestinationPath $ArtifactsCompressed
|
|
}
|
|
else {
|
|
Write-Host "[*] artifacts download skipped on this architecture."
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "[FATAL] Download phase failed."
|
|
if ($_ -is [System.Exception]) {
|
|
Write-Host $_.Exception.Message
|
|
} else {
|
|
Write-Host $_
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
|
|
# ================================
|
|
# EXTRACT / INSTALL (websocat before artifacts)
|
|
# ================================
|
|
try {
|
|
# Python
|
|
Extract-CompressedFile -CompressedPath $PythonCompressed -DestinationDirectory (Join-Path $TargetDir "python")
|
|
|
|
# curl
|
|
Extract-CompressedFile -CompressedPath $CurlCompressed -DestinationDirectory (Join-Path $TargetDir "curl")
|
|
|
|
# YARA
|
|
if ($YaraUrl) {
|
|
Extract-CompressedFile -CompressedPath $YaraCompressed -DestinationDirectory (Join-Path $TargetDir "yara")
|
|
}
|
|
else {
|
|
Write-Host "[*] YARA installation skipped on this architecture."
|
|
}
|
|
|
|
# WAMR (TAR.GZ)
|
|
if ($WamrUrl) {
|
|
Extract-TarGzArchive -ArchivePath $WamrArchive -DestinationDirectory (Join-Path $TargetDir "wamr")
|
|
}
|
|
else {
|
|
Write-Host "[*] WAMR installation skipped on this architecture."
|
|
}
|
|
|
|
# websocat
|
|
if ($WebsocatUrl) {
|
|
Extract-CompressedFile -CompressedPath $WebsocatCompressed -DestinationDirectory (Join-Path $TargetDir "websocat")
|
|
}
|
|
else {
|
|
Write-Host "[*] websocat installation skipped on this architecture."
|
|
}
|
|
|
|
# artifacts
|
|
if ($ArtifactsUrl) {
|
|
Extract-CompressedFile -CompressedPath $ArtifactsCompressed -DestinationDirectory (Join-Path $TargetDir "bin")
|
|
}
|
|
else {
|
|
Write-Host "[*] artifacts installation skipped on this architecture."
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "[FATAL] Extraction/installation phase failed."
|
|
if ($_ -is [System.Exception]) {
|
|
Write-Host $_.Exception.Message
|
|
} else {
|
|
Write-Host $_
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
|
|
# ================================
|
|
# FINISH
|
|
# ================================
|
|
Write-Host "[*] Installation completed successfully."
|
|
Write-Host "[*] Installed into: $TargetDir"
|
|
Write-Host ""
|
|
exit 0
|