welsonjs/postInstall.ps1
Namhyeon, Go 505801664a Refactor installer scripts and update components
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.
2025-12-01 12:29:46 +09:00

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