-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathinstall.ps1
More file actions
353 lines (323 loc) · 15.5 KB
/
Copy pathinstall.ps1
File metadata and controls
353 lines (323 loc) · 15.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# install.ps1 - Bootstrap the NeqSim devtools package on Windows.
#
# Fixes the common "pip not available / python not available" errors by
# locating a working Python interpreter (py launcher, python3, or python,
# skipping the Microsoft Store stub) and running `python -m pip` for you.
#
# Usage (from the repo root, in PowerShell):
# .\install.ps1 # install neqsim-dev-setup (editable)
# .\install.ps1 pdf # also install the optional [pdf] extras
# .\install.ps1 ocr # also install the optional [ocr] extras
# .\install.ps1 -Uv # use the fast 'uv' package manager instead of pip
# .\install.ps1 pdf -Uv # extras + uv
# .\install.ps1 -InstallJdk # also download a portable Temurin JDK (no admin)
#
# No need to activate a venv first, and no need to have `pip` on PATH.
#
# "File is not digitally signed" / execution-policy error?
# This is a Windows restriction on running scripts, not a problem with the
# file. Pick ONE of these (no admin rights needed):
#
# 1. Use the wrapper (simplest - bypasses the policy for one run only):
# .\install.cmd
#
# 2. Run this script with a one-time bypass (no permanent change):
# powershell -ExecutionPolicy Bypass -File .\install.ps1
#
# 3. Allow local scripts for your user account, then run normally:
# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
# Unblock-File -Path .\install.ps1 # clears the "downloaded" mark
# .\install.ps1
param(
[string]$Extras = "",
[switch]$Uv,
[switch]$InstallJdk,
[string]$JdkVersion = "21"
)
$ErrorActionPreference = "Stop"
$RepoRoot = $PSScriptRoot
$Devtools = Join-Path $RepoRoot "devtools"
# Return the launcher's fixed arguments (everything after the executable),
# safely handling single-element commands (avoids PowerShell's `1..0`
# descending-range footgun that would inject a $null and duplicate the exe).
function Get-PyArgs {
param([string[]]$Command)
if ($Command.Length -gt 1) {
return $Command[1..($Command.Length - 1)]
}
return @()
}
function Test-Python {
param([string[]]$Command)
try {
$rest = Get-PyArgs $Command
# A real interpreter prints its version; the Microsoft Store alias
# stub prints nothing (and often opens the Store), so require output.
$out = & $Command[0] @rest "-c" "import sys; print(sys.version.split()[0])" 2>$null
if ($LASTEXITCODE -eq 0 -and $out) {
return $true
}
} catch {
return $false
}
return $false
}
Write-Host "NeqSim devtools installer" -ForegroundColor Cyan
Write-Host "Locating a working Python interpreter..."
# Candidate launchers, most preferred first:
# 1. An active virtual environment (VIRTUAL_ENV) - install where the user expects.
# 2. `python` / `python3` - these respect an active venv (the Store stub is
# filtered out by Test-Python because it prints no version to stdout).
# 3. `py -3` last resort - the launcher IGNORES an active venv, so it must not
# win when a venv is active (otherwise we install into the system Python).
$candidates = @()
if ($env:VIRTUAL_ENV) {
$venvPy = Join-Path $env:VIRTUAL_ENV "Scripts\python.exe"
if (Test-Path $venvPy) {
$candidates += , @($venvPy)
}
}
$candidates += , @("python")
$candidates += , @("python3")
$candidates += , @("py", "-3")
$python = $null
foreach ($cand in $candidates) {
if (Test-Python -Command $cand) {
$python = $cand
break
}
}
if ($null -eq $python) {
Write-Host ""
Write-Host "ERROR: No working Python interpreter was found." -ForegroundColor Red
Write-Host ""
Write-Host "Fix: install Python 3.8+ from one of:" -ForegroundColor Yellow
Write-Host " * https://www.python.org/downloads/ (check 'Add python.exe to PATH')"
Write-Host " * winget install Python.Python.3.12"
Write-Host ""
Write-Host "If 'python' opens the Microsoft Store, disable the alias under:" -ForegroundColor Yellow
Write-Host " Settings > Apps > Advanced app settings > App execution aliases"
Write-Host " (turn off the python.exe / python3.exe entries), then re-run this script."
exit 1
}
$pyArgs = Get-PyArgs $python
$pythonDisplay = ($python -join " ")
$version = & $python[0] @pyArgs "-c" "import sys; print(sys.version.split()[0])"
$pythonExe = & $python[0] @pyArgs "-c" "import sys; print(sys.executable)"
Write-Host "Using Python $version via '$pythonDisplay'" -ForegroundColor Green
if ($env:VIRTUAL_ENV) {
Write-Host "Active virtual environment: $env:VIRTUAL_ENV" -ForegroundColor Green
}
# Build the install target (support optional extras like devtools[pdf]).
$target = $Devtools
if ($Extras) {
$target = "$Devtools[$Extras]"
}
if ($Uv) {
# Fast path: install with uv into the detected interpreter's environment.
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host ""
Write-Host "ERROR: -Uv was requested but 'uv' is not installed." -ForegroundColor Red
Write-Host "Install uv with one of:" -ForegroundColor Yellow
Write-Host " * winget install astral-sh.uv"
Write-Host " * powershell -c `"irm https://astral.sh/uv/install.ps1 | iex`""
Write-Host " * $pythonDisplay -m pip install uv"
Write-Host "Or re-run without -Uv to use pip."
exit 1
}
Write-Host "Installing (editable, via uv): $target"
& uv pip install --python "$pythonExe" -e "$target"
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: 'uv pip install' failed. See the output above for details." -ForegroundColor Red
exit 1
}
} else {
# Ensure pip is available for this interpreter.
& $python[0] @pyArgs "-m" "pip" "--version" *> $null
if ($LASTEXITCODE -ne 0) {
Write-Host "pip not found for this interpreter - bootstrapping with ensurepip..."
& $python[0] @pyArgs "-m" "ensurepip" "--upgrade"
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: could not bootstrap pip. Re-run the Python installer and enable pip." -ForegroundColor Red
exit 1
}
}
Write-Host "Installing (editable): $target"
& $python[0] @pyArgs "-m" "pip" "install" "-e" "$target"
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: pip install failed. See the output above for details." -ForegroundColor Red
exit 1
}
}
Write-Host ""
Write-Host "Ensuring the 'neqsim' command is on your PATH..." -ForegroundColor Cyan
& $python[0] @pyArgs (Join-Path $Devtools "ensure_on_path.py")
# A PATH change written to the registry reaches only NEW processes, so without
# this the command the installer just advertised fails in the window the user is
# sitting in. This script runs in the caller's PowerShell process, so putting the
# directory on $env:PATH here makes 'neqsim' work immediately.
$scriptDir = (& $python[0] @pyArgs (Join-Path $Devtools "ensure_on_path.py") "--print-script-dir" |
Select-Object -Last 1)
if ($scriptDir) { $scriptDir = $scriptDir.Trim() }
if ($scriptDir -and (Test-Path -LiteralPath $scriptDir)) {
$onPath = ($env:PATH -split [IO.Path]::PathSeparator) |
Where-Object { $_ -and ($_.TrimEnd('\') -ieq $scriptDir.TrimEnd('\')) }
if (-not $onPath) {
$env:PATH = $env:PATH + [IO.Path]::PathSeparator + $scriptDir
}
}
Write-Host ""
$neqsimCommand = Get-Command neqsim -ErrorAction SilentlyContinue
if ($neqsimCommand) {
$cli = "neqsim"
Write-Host "Done. The 'neqsim' command works in THIS window:" -ForegroundColor Green
Write-Host " $($neqsimCommand.Source)"
Write-Host "Try it now: neqsim doctor --skip-jar"
} else {
# Keep the examples below runnable: advertising 'neqsim ...' to someone who
# cannot run it is what sends people off debugging a working install.
$cli = "$pythonDisplay -m neqsim_cli"
Write-Host "Done, but the 'neqsim' command is not resolvable here." -ForegroundColor Yellow
Write-Host "Use this instead - it is the same entry point and always works:"
Write-Host " $cli --help"
Write-Host "To find out why: $cli doctor --skip-jar"
Write-Host "If you are in a VS Code terminal, fully quit and reopen VS Code - a new"
Write-Host "integrated terminal is NOT enough (VS Code captures PATH at launch)."
}
Write-Host ""
Write-Host "Where solved tasks are saved:" -ForegroundColor Cyan
Write-Host " Default (nothing set): <this repository>\task_solve"
Write-Host " Change it: $cli --set-task-root `"D:\Engineering Tasks`" (or 'cwd')"
Write-Host " Check it: $cli --show-task-root"
Write-Host "The setting is saved in ~\.neqsim\task_defaults.json and is used by"
Write-Host "'neqsim new-task' and the AI agents, so tasks can live outside the clone."
Write-Host ""
Write-Host "Word template for generated reports:" -ForegroundColor Cyan
Write-Host " Default (nothing set): built-in NeqSim report styling"
Write-Host " Change it: $cli --set-report-template `"C:\path\company template.docx`""
Write-Host " Check it: $cli --show-report-template"
Write-Host "Every Word report then inherits its styles, fonts, headers, and footers."
# ── JDK advisory (non-fatal) ─────────────────────────────────────────────
# The Python devtools do not need Java, but building the NeqSim JAR
# (mvnw.cmd install) and the local dev-notebook runtime DO. Check for a JDK
# so users on locked-down PCs get the portable-JDK remedy up front.
function Install-PortableJdk {
# Download a portable Temurin (Eclipse Adoptium) JDK into the user profile
# and set user-scope JAVA_HOME/PATH. Requires no administrator rights.
# The download is verified against the SHA-256 checksum published by the
# Adoptium assets API before it is extracted.
param([string]$Version = "21")
if ($env:PROCESSOR_ARCHITECTURE -match "ARM64") { $arch = "aarch64" } else { $arch = "x64" }
$dest = Join-Path $HOME ".neqsim\jdk"
$tmpZip = Join-Path $env:TEMP "temurin-$Version-$arch.zip"
$assetsUrl = "https://api.adoptium.net/v3/assets/latest/$Version/hotspot?architecture=$arch&image_type=jdk&os=windows&vendor=eclipse"
Write-Host ""
Write-Host "Querying Adoptium for a portable Temurin JDK $Version ($arch)..." -ForegroundColor Cyan
try {
$assets = Invoke-RestMethod -Uri $assetsUrl -UseBasicParsing
} catch {
Write-Host "ERROR: could not query the Adoptium API: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "Behind a proxy? Set `$env:HTTPS_PROXY and retry, or install a JDK manually." -ForegroundColor Yellow
return $null
}
$pkg = $null
foreach ($a in @($assets)) {
if ($a.binary -and $a.binary.image_type -eq "jdk" -and $a.binary.package -and $a.binary.package.link) {
$pkg = $a.binary.package
break
}
}
if (-not $pkg) {
Write-Host "ERROR: no matching JDK package in the Adoptium response." -ForegroundColor Red
return $null
}
$expected = $pkg.checksum
Write-Host "Downloading $($pkg.name)..."
try {
Invoke-WebRequest -Uri $pkg.link -OutFile $tmpZip -UseBasicParsing
} catch {
Write-Host "ERROR: JDK download failed: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "Behind a proxy? Set `$env:HTTPS_PROXY and retry, or install a JDK manually." -ForegroundColor Yellow
return $null
}
if ($expected) {
Write-Host "Verifying SHA-256 checksum..."
$actual = (Get-FileHash -Path $tmpZip -Algorithm SHA256).Hash.ToLower()
if ($actual -ne $expected.ToLower()) {
Write-Host "ERROR: checksum mismatch - download may be corrupt or tampered." -ForegroundColor Red
Write-Host " expected: $expected"
Write-Host " actual: $actual"
Remove-Item $tmpZip -ErrorAction SilentlyContinue
return $null
}
Write-Host "Checksum OK." -ForegroundColor Green
} else {
Write-Host "WARNING: Adoptium returned no checksum; skipping verification." -ForegroundColor Yellow
}
if (Test-Path $dest) { Remove-Item -Recurse -Force $dest }
New-Item -ItemType Directory -Force -Path $dest | Out-Null
Write-Host "Extracting to $dest ..."
Expand-Archive -Path $tmpZip -DestinationPath $dest -Force
Remove-Item $tmpZip -ErrorAction SilentlyContinue
# Temurin extracts to a versioned subfolder (e.g. jdk-21.0.5+11).
$jdkHome = Get-ChildItem -Path $dest -Directory |
Where-Object { Test-Path (Join-Path $_.FullName "bin\java.exe") } |
Select-Object -First 1
if (-not $jdkHome) {
Write-Host "ERROR: Could not find bin\java.exe in the extracted JDK." -ForegroundColor Red
return $null
}
$javaHomePath = $jdkHome.FullName
$jdkBin = Join-Path $javaHomePath "bin"
[Environment]::SetEnvironmentVariable("JAVA_HOME", $javaHomePath, "User")
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if (-not $userPath) { $userPath = "" }
if ($userPath -notlike "*$jdkBin*") {
[Environment]::SetEnvironmentVariable("PATH", ($userPath.TrimEnd(';') + ";" + $jdkBin), "User")
}
# Make it usable in this session too, so a following mvnw.cmd install works.
$env:JAVA_HOME = $javaHomePath
$env:PATH = "$jdkBin;$env:PATH"
Write-Host "Portable JDK installed (no admin):" -ForegroundColor Green
Write-Host " JAVA_HOME = $javaHomePath"
Write-Host " Added to user PATH: $jdkBin"
Write-Host " Open a NEW terminal for the persistent env vars to apply everywhere."
return $javaHomePath
}
$jdkFound = $false
$javaHome = [Environment]::GetEnvironmentVariable("JAVA_HOME")
if ($javaHome -and (Test-Path (Join-Path $javaHome "bin\java.exe"))) {
$jdkFound = $true
Write-Host ""
Write-Host "JDK detected via JAVA_HOME: $javaHome" -ForegroundColor Green
} elseif (Get-Command java -ErrorAction SilentlyContinue) {
$jdkFound = $true
Write-Host ""
Write-Host "JDK detected on PATH (java)." -ForegroundColor Green
if (-not $javaHome) {
Write-Host "Tip: set JAVA_HOME so all Java tooling agrees on one JDK." -ForegroundColor Yellow
}
}
if (-not $jdkFound) {
if ($InstallJdk) {
Install-PortableJdk -Version $JdkVersion
} else {
Write-Host ""
Write-Host "NOTE: No JDK found (needed for 'mvnw.cmd install' and local dev notebooks)." -ForegroundColor Yellow
Write-Host "Easiest fix (no admin): re-run with -InstallJdk to auto-download a portable JDK:" -ForegroundColor Yellow
Write-Host " .\install.ps1 -InstallJdk"
Write-Host "Or install one manually:" -ForegroundColor Yellow
Write-Host " 1. Download a Temurin JDK 21 .zip:"
Write-Host " https://adoptium.net/temurin/releases/?package=jdk"
Write-Host " 2. Extract into your profile, e.g. C:\Users\$env:USERNAME\jdk-21"
Write-Host " 3. Set user-scope env vars (no admin; open a new terminal to apply):"
Write-Host " [Environment]::SetEnvironmentVariable('JAVA_HOME','C:\Users\$env:USERNAME\jdk-21','User')"
Write-Host " [Environment]::SetEnvironmentVariable('PATH',`$env:PATH+';C:\Users\$env:USERNAME\jdk-21\bin','User')"
Write-Host " (Pure Python/pip 'neqsim' use still needs a JVM present via jpype.)"
Write-Host "Run '$pythonDisplay -m neqsim_cli doctor' to re-check your environment." -ForegroundColor Cyan
}
} elseif ($InstallJdk) {
Write-Host ""
Write-Host "A JDK is already available; skipping -InstallJdk." -ForegroundColor Yellow
Write-Host "Delete the old JAVA_HOME / PATH entries first if you want to switch to a portable JDK."
}