Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .github/workflows/make_installers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Set up uv
uses: astral-sh/setup-uv@v5

- name: Build bundle
shell: cmd
run: installers\install-python-microscopy.bat bundle

- name: Get installed version
# The installer no longer bundles a pre-built venv (see pyme_setup.iss —
# uv venvs bake in an absolute interpreter path, so one built on the
# runner can't be relocated to an end user's machine). It installs PYME
# on the target machine instead, so all we need from CI is the version
# string, read straight from pyproject.toml.
- name: Get package version
id: version
shell: pwsh
run: |
$v = & "bundle\venv\Scripts\python.exe" `
-c "import importlib.metadata; print(importlib.metadata.version('python-microscopy'))"
"version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
$content = Get-Content pyproject.toml -Raw
if ($content -notmatch '(?m)^version\s*=\s*"([^"]+)"') {
throw "Could not find version in pyproject.toml"
}
"version=$($Matches[1])" | Out-File -FilePath $env:GITHUB_OUTPUT -Append

- name: Build installer
shell: pwsh
run: |
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" `
/DAppSourceDir="$env:GITHUB_WORKSPACE\bundle" `
/DRepoDir="$env:GITHUB_WORKSPACE" `
/DAppVersion="${{ steps.version.outputs.version }}" `
installers\windows\pyme_setup.iss

Expand Down
14 changes: 11 additions & 3 deletions installers/install-python-microscopy.bat
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@echo off
setlocal EnableDelayedExpansion

:: ---- Config (mirrors installer_defines.sh keep in sync) ----
:: ---- Config (mirrors installer_defines.sh - keep in sync) ----
set "TARGET_PYTHON=3.13"
set "PACKAGE_NAME=python-microscopy"
set "ENTRY_POINTS=PYMEAcquire PYMEImage PYMEVis PYMEClusterOfOne"
Expand All @@ -13,7 +13,7 @@ echo Installing PYME to: !DEST!
if not exist "!DEST!\" mkdir "!DEST!"

:: ---- Locate or download uv ----
:: Context A (CI): uv is in PATH via astral-sh/setup-uv action the where check passes immediately.
:: Context A (CI): uv is in PATH via astral-sh/setup-uv action - the where check passes immediately.
:: Context B (end-user): uv.exe is downloaded to DEST\bin\ and used from there.
where uv >nul 2>&1
if not errorlevel 1 (
Expand All @@ -24,8 +24,16 @@ if not errorlevel 1 (
)

:: ---- Managed Python + virtual environment ----
:: UV_PYTHON_INSTALL_DIR is pointed under DEST rather than uv's default
:: (%APPDATA%\uv\python\...): on Windows 11 24H2+, an installer-spawned
:: process is denied creation of uv's per-minor-version junction there by
:: RedirectionGuard ("the path cannot be traversed because it contains an
:: untrusted mount point", os error 448). DEST isn't a protected known
:: folder, so it isn't subject to that restriction, and this also keeps
:: the whole install self-contained under DEST.
set "UV_PYTHON_INSTALL_DIR=!DEST!\uv-python"
echo Installing Python !TARGET_PYTHON!...
"!UV!" python install !TARGET_PYTHON!
"!UV!" python install --no-bin !TARGET_PYTHON!
if errorlevel 1 (echo ERROR: uv python install failed & exit /b 1)

echo Creating virtual environment...
Expand Down
66 changes: 53 additions & 13 deletions installers/windows/pyme_setup.iss
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
; PYME Windows Installer — produced by InnoSetup on windows-latest.
;
; Typical CI invocation (after running install.bat to create the bundle):
; set VERSION=<output of: python -c "import importlib.metadata; print(importlib.metadata.version('python-microscopy'))">
; ISCC.exe /DAppSourceDir=C:\path\to\bundle /DAppVersion=%VERSION% pyme_setup.iss
; This installer does NOT ship a pre-built venv. uv-managed venvs bake in an
; absolute path to their Python interpreter (see venv\pyvenv.cfg "home="
; and the Scripts\*.exe trampolines) — building the venv in CI and copying
; it into the installer leaves every launcher pointing at the CI runner's
; profile, which doesn't exist on the end user's machine ("uv trampoline
; failed to canonicalize script path"). Instead, this installer bundles
; install-python-microscopy.bat and runs it on the target machine, so the
; venv it creates is tied to that machine from the start.
;
; Typical CI invocation (from a checkout of the repo, no bundle build needed):
; set VERSION=<version string for this release>
; ISCC.exe /DRepoDir=C:\path\to\checkout /DAppVersion=%VERSION% pyme_setup.iss

#ifndef AppSourceDir
#define AppSourceDir "..\..\PYME_bundle"
#ifndef RepoDir
#define RepoDir "..\.."
#endif
#ifndef AppVersion
#define AppVersion "0.0.0"
#endif

#define AppName "PYME"
#define AppPublisher "Baddeley Lab, University of Auckland"
; Icons ship with the package inside the venv tree.
; Icons are staged from the source tree (not the venv) so they're available
; before install-python-microscopy.bat has run.
; pymeLogo.png has no .ico equivalent — pmanal.ico is used in its place.
#define IconsDir "{app}\venv\Lib\site-packages\PYME\resources\icons"
; (ISPP doesn't expand a nested {#RepoDir} used inside another #define's
; value, so IconsDir is spelled out directly wherever it's needed below.)

[Setup]
; AppId uniquely identifies this application for upgrades and uninstall — do not change.
Expand All @@ -28,6 +39,16 @@ DefaultGroupName={#AppName}
; Per-user install by default; elevation dialog allows all-users install.
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
; Inno Setup enables Windows' RedirectionGuard mitigation on Setup (and the
; processes it spawns, including install-python-microscopy.bat's uv calls)
; by default on Windows 11 / 10 22H2+. It blocks traversal of NTFS
; junctions/symlinks created by non-elevated processes - which is exactly
; how our non-elevated `uv python install` sets up its per-minor-version
; Python directory link, so uv fails with "the path cannot be traversed
; because it contains an untrusted mount point" (os error 448) the moment
; it tries to use the link it just created. Since [Run] here is the actual
; installation step (not incidental file copying), disable the mitigation.
RedirectionGuard=no
OutputBaseFilename=PYME-{#AppVersion}-setup
Compression=lzma2
SolidCompression=yes
Expand All @@ -36,12 +57,31 @@ WizardStyle=modern
CloseApplications=no

[Files]
Source: "{#AppSourceDir}\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs
Source: "{#RepoDir}\installers\install-python-microscopy.bat"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RepoDir}\PYME\resources\icons\pmacquire.ico"; DestDir: "{app}\icons"; Flags: ignoreversion
Source: "{#RepoDir}\PYME\resources\icons\pmanal.ico"; DestDir: "{app}\icons"; Flags: ignoreversion
Source: "{#RepoDir}\PYME\resources\icons\pmvis.ico"; DestDir: "{app}\icons"; Flags: ignoreversion

[Run]
; Runs on the target machine so uv, the managed Python, and the venv it
; creates are all tied to this machine — not skippable in silent installs,
; since the app is non-functional without it.
; Invoked via shellexec (the .bat file association) rather than a hand-built
; "cmd /C" command line: cmd.exe's /C only preserves quoting verbatim when
; its argument contains exactly two quote characters (see `cmd /?`); with
; two quoted tokens (script + {app}, both of which may contain spaces, e.g.
; under "Program Files") that count is four, so cmd falls back to blindly
; stripping the first and last quote character instead, corrupting the
; command line. Letting the shell resolve the .bat association sidesteps
; that quoting pitfall entirely.
Filename: "{app}\install-python-microscopy.bat"; Parameters: """{app}"""; \
WorkingDir: "{app}"; StatusMsg: "Installing Python and PYME (requires internet access; this can take several minutes)..."; \
Flags: waituntilterminated shellexec

[Icons]
Name: "{group}\PYMEAcquire"; Filename: "{app}\PYMEAcquire.cmd"; IconFilename: "{#IconsDir}\pmacquire.ico"
Name: "{group}\PYMEImage"; Filename: "{app}\PYMEImage.cmd"; IconFilename: "{#IconsDir}\pmanal.ico"
Name: "{group}\PYMEVis"; Filename: "{app}\PYMEVis.cmd"; IconFilename: "{#IconsDir}\pmvis.ico"
Name: "{group}\PYMEClusterOfOne"; Filename: "{app}\PYMEClusterOfOne.cmd"; IconFilename: "{#IconsDir}\pmanal.ico"
Name: "{group}\PYME Console"; Filename: "{app}\pyme-console.cmd"; IconFilename: "{#IconsDir}\pmanal.ico"
Name: "{group}\PYMEAcquire"; Filename: "{app}\PYMEAcquire.cmd"; IconFilename: "{app}\icons\pmacquire.ico"
Name: "{group}\PYMEImage"; Filename: "{app}\PYMEImage.cmd"; IconFilename: "{app}\icons\pmanal.ico"
Name: "{group}\PYMEVis"; Filename: "{app}\PYMEVis.cmd"; IconFilename: "{app}\icons\pmvis.ico"
Name: "{group}\PYMEClusterOfOne"; Filename: "{app}\PYMEClusterOfOne.cmd"; IconFilename: "{app}\icons\pmanal.ico"
Name: "{group}\PYME Console"; Filename: "{app}\pyme-console.cmd"; IconFilename: "{app}\icons\pmanal.ico"
Name: "{group}\Uninstall PYME"; Filename: "{uninstallexe}"
Loading