Skip to content

Commit 07ad353

Browse files
authored
Merge direct desktop installer assets for OpenHCS 0.6.2
Ship direct desktop installer assets for 0.6.2
2 parents a280e6d + 8d5d555 commit 07ad353

18 files changed

Lines changed: 323 additions & 113 deletions

File tree

.github/workflows/integration-tests.yml

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -504,22 +504,21 @@ jobs:
504504
$releaseVersion = $releaseVersion.Trim()
505505
$stage = Join-Path $env:RUNNER_TEMP "OpenHCS-Windows-Smoke"
506506
$installRoot = Join-Path $env:RUNNER_TEMP "OpenHCS Installed"
507+
$contractPath = Join-Path $env:RUNNER_TEMP "installer_contract.json"
507508
New-Item -ItemType Directory -Path $stage | Out-Null
508-
Copy-Item `
509-
packaging/installers/windows/Install-OpenHCS.ps1 `
510-
-Destination $stage
509+
python scripts/render_installer_contract.py `
510+
--version $releaseVersion `
511+
--output $contractPath
511512
& packaging/installers/windows/Build-InstallerLauncher.ps1 `
512-
-OutputDirectory $stage
513+
-OutputDirectory $stage `
514+
-ContractPath $contractPath
513515
if ($LASTEXITCODE -ne 0) {
514516
throw "Windows GUI installer launcher build failed."
515517
}
516-
python scripts/render_installer_contract.py `
517-
--version $releaseVersion `
518-
--output (Join-Path $stage "installer_contract.json")
519518
520-
$launcher = Join-Path $stage "Install-OpenHCS.exe"
519+
$launcher = Join-Path $stage "OpenHCS-Windows-Installer.exe"
521520
if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) {
522-
throw "Windows GUI installer launcher was not staged."
521+
throw "Single-file Windows GUI installer was not staged."
523522
}
524523
$subsystem = python -c "import struct,sys; from pathlib import Path; data=Path(sys.argv[1]).read_bytes(); pe=struct.unpack_from('<I',data,0x3c)[0]; assert data[pe:pe+4]==b'PE\0\0'; print(struct.unpack_from('<H',data,pe+24+68)[0])" $launcher
525524
if ($subsystem.Trim() -ne "2") {
@@ -534,19 +533,32 @@ jobs:
534533
$cancellationPath = Join-Path `
535534
([IO.Path]::GetTempPath()) `
536535
("openhcs-installer-cancel-{0}.marker" -f [Guid]::NewGuid().ToString("N"))
537-
& $powerShellExecutable `
538-
-NoProfile -ExecutionPolicy Bypass `
539-
-File (Join-Path $stage "Install-OpenHCS.ps1") `
540-
-Worker -InstallRoot $installRoot `
541-
-CancellationPath $cancellationPath
542-
if ($LASTEXITCODE -ne 0) {
543-
throw "Windows installer failed with exit code $LASTEXITCODE."
536+
$installerStartInfo = [Diagnostics.ProcessStartInfo]::new()
537+
$installerStartInfo.FileName = $launcher
538+
$installerStartInfo.UseShellExecute = $false
539+
foreach ($argument in @(
540+
"-Worker",
541+
"-InstallRoot", $installRoot,
542+
"-CancellationPath", $cancellationPath
543+
)) {
544+
[void]$installerStartInfo.ArgumentList.Add([string]$argument)
545+
}
546+
$installerProcess = [Diagnostics.Process]::Start($installerStartInfo)
547+
try {
548+
$installerProcess.WaitForExit()
549+
$installerExitCode = $installerProcess.ExitCode
550+
}
551+
finally {
552+
$installerProcess.Dispose()
553+
}
554+
if ($installerExitCode -ne 0) {
555+
throw "Windows installer failed with exit code $installerExitCode."
544556
}
545557
546558
$desktopRoot = [Environment]::GetFolderPath("DesktopDirectory")
547559
$summaryJson = python -m scripts.smoke_installed_desktop `
548560
--platform windows `
549-
--contract (Join-Path $stage "installer_contract.json") `
561+
--contract $contractPath `
550562
--install-root $installRoot `
551563
--desktop-root $desktopRoot
552564
if ($LASTEXITCODE -ne 0) {
@@ -630,6 +642,34 @@ jobs:
630642
"$staged_contract" \
631643
"$installer_app"
632644
645+
dmg_source="$RUNNER_TEMP/OpenHCS Installer DMG"
646+
installer_dmg="$RUNNER_TEMP/OpenHCS-macOS-Installer.dmg"
647+
mount_point="$RUNNER_TEMP/OpenHCS Installer Mount"
648+
mkdir -p "$dmg_source" "$mount_point"
649+
ditto "$installer_app" "$dmg_source/OpenHCS Installer.app"
650+
hdiutil create \
651+
-volname "OpenHCS Installer" \
652+
-srcfolder "$dmg_source" \
653+
-format UDZO \
654+
"$installer_dmg"
655+
hdiutil verify "$installer_dmg"
656+
hdiutil attach \
657+
-nobrowse \
658+
-readonly \
659+
-mountpoint "$mount_point" \
660+
"$installer_dmg"
661+
mounted_dmg=true
662+
cleanup_dmg_mount() {
663+
if [[ "$mounted_dmg" == true ]]; then
664+
hdiutil detach "$mount_point" || true
665+
fi
666+
}
667+
trap cleanup_dmg_mount EXIT
668+
test -d "$mount_point/OpenHCS Installer.app"
669+
hdiutil detach "$mount_point"
670+
mounted_dmg=false
671+
trap - EXIT
672+
633673
mkdir -p "$smoke_home"
634674
export HOME="$smoke_home"
635675
export UV_FIND_LINKS="$GITHUB_WORKSPACE/dist"

.github/workflows/publish.yml

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,23 @@ jobs:
4040
$source = Get-Content packaging/installers/windows/Install-OpenHCS.ps1 -Raw
4141
[void][scriptblock]::Create($source)
4242
43-
- name: Build release-pinned Windows installer archive
43+
- name: Build release-pinned single-file Windows installer
4444
shell: pwsh
4545
run: |
46-
$stage = Join-Path $env:RUNNER_TEMP "OpenHCS-Windows-Installer"
4746
$releaseVersion = $env:GITHUB_REF_NAME -replace '^v', ''
48-
New-Item -ItemType Directory -Path $stage | Out-Null
49-
Copy-Item `
50-
packaging/installers/windows/Install-OpenHCS.ps1 `
51-
-Destination $stage
47+
$contractPath = Join-Path $env:RUNNER_TEMP "installer_contract.json"
48+
python scripts/render_installer_contract.py `
49+
--version $releaseVersion `
50+
--output $contractPath
5251
& packaging/installers/windows/Build-InstallerLauncher.ps1 `
53-
-OutputDirectory $stage
52+
-OutputDirectory $env:GITHUB_WORKSPACE `
53+
-ContractPath $contractPath
5454
if ($LASTEXITCODE -ne 0) {
5555
throw "Windows GUI installer launcher build failed."
5656
}
57-
python scripts/render_installer_contract.py `
58-
--version $releaseVersion `
59-
--output (Join-Path $stage "installer_contract.json")
60-
$launcher = Join-Path $stage "Install-OpenHCS.exe"
57+
$launcher = Join-Path $env:GITHUB_WORKSPACE "OpenHCS-Windows-Installer.exe"
6158
if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) {
62-
throw "Windows GUI installer launcher was not staged."
59+
throw "Single-file Windows GUI installer was not staged."
6360
}
6461
$subsystem = python -c "import struct,sys; from pathlib import Path; data=Path(sys.argv[1]).read_bytes(); pe=struct.unpack_from('<I',data,0x3c)[0]; assert data[pe:pe+4]==b'PE\0\0'; print(struct.unpack_from('<H',data,pe+24+68)[0])" $launcher
6562
if ($subsystem.Trim() -ne "2") {
@@ -68,14 +65,12 @@ jobs:
6865
if ((Get-Item -LiteralPath $launcher).Length -gt 2MB) {
6966
throw "Windows installer launcher unexpectedly embeds a large runtime."
7067
}
71-
Compress-Archive -Path (Join-Path $stage "*") `
72-
-DestinationPath OpenHCS-Windows-Installer.zip
7368
74-
- name: Upload Windows installer archive
69+
- name: Upload single-file Windows installer
7570
uses: actions/upload-artifact@v4
7671
with:
7772
name: openhcs-windows-installer
78-
path: OpenHCS-Windows-Installer.zip
73+
path: OpenHCS-Windows-Installer.exe
7974
if-no-files-found: error
8075

8176
build-macos-installer:
@@ -96,25 +91,33 @@ jobs:
9691
bash -n packaging/installers/macos/install-openhcs.sh
9792
bash -n packaging/installers/macos/build-installer.sh
9893
99-
- name: Build release-pinned macOS installer archive
94+
- name: Build release-pinned macOS installer disk image
10095
run: |
10196
STAGE="$RUNNER_TEMP/OpenHCS-macOS-Installer"
97+
DMG_SOURCE="$RUNNER_TEMP/OpenHCS-macOS-DMG"
10298
mkdir -p "$STAGE"
99+
mkdir -p "$DMG_SOURCE"
103100
python scripts/render_installer_contract.py \
104101
--version "${GITHUB_REF_NAME#v}" \
105102
--output "$STAGE/installer_contract.json"
106103
packaging/installers/macos/build-installer.sh \
107104
"$STAGE/installer_contract.json" \
108105
"$STAGE/OpenHCS Installer.app"
109-
ditto -c -k --sequesterRsrc --keepParent \
106+
ditto \
110107
"$STAGE/OpenHCS Installer.app" \
111-
"$GITHUB_WORKSPACE/OpenHCS-macOS-Installer.zip"
112-
113-
- name: Upload macOS installer archive
108+
"$DMG_SOURCE/OpenHCS Installer.app"
109+
hdiutil create \
110+
-volname "OpenHCS Installer" \
111+
-srcfolder "$DMG_SOURCE" \
112+
-format UDZO \
113+
"$GITHUB_WORKSPACE/OpenHCS-macOS-Installer.dmg"
114+
hdiutil verify "$GITHUB_WORKSPACE/OpenHCS-macOS-Installer.dmg"
115+
116+
- name: Upload macOS installer disk image
114117
uses: actions/upload-artifact@v4
115118
with:
116119
name: openhcs-macos-installer
117-
path: OpenHCS-macOS-Installer.zip
120+
path: OpenHCS-macOS-Installer.dmg
118121
if-no-files-found: error
119122

120123
build-and-publish:
@@ -173,7 +176,7 @@ jobs:
173176
twine check dist/*
174177
twine upload dist/* --skip-existing
175178
176-
- name: Download desktop installer archives
179+
- name: Download desktop installers
177180
uses: actions/download-artifact@v4
178181
with:
179182
pattern: openhcs-*-installer

docs/source/development/mcp_release.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ the install-surface projections:
1616

1717
.. code-block:: bash
1818
19-
RELEASE_VERSION=0.6.1
19+
RELEASE_VERSION=0.6.2
2020
python scripts/sync_mcp_release_metadata.py
2121
python scripts/sync_mcp_release_metadata.py --check
2222
python scripts/sync_mcp_release_metadata.py --check --expected-version "$RELEASE_VERSION"
@@ -119,11 +119,12 @@ metadata against that version, asks for confirmation, and pushes one annotated
119119
tag.
120120

121121
That tag starts ``.github/workflows/publish.yml``. The workflow builds and
122-
validates the Windows and macOS installer archives first, then builds and smoke
122+
validates the Windows and macOS installer assets first, then builds and smoke
123123
tests the OpenHCS wheel outside the checkout. After publishing the wheel and
124124
source distribution to PyPI, it creates one GitHub Release containing those
125-
Python artifacts plus ``OpenHCS-Windows-Installer.zip`` and
126-
``OpenHCS-macOS-Installer.zip``. The dependent MCP Registry job waits until the
125+
Python artifacts plus the directly runnable
126+
``OpenHCS-Windows-Installer.exe`` and the single-file
127+
``OpenHCS-macOS-Installer.dmg``. The dependent MCP Registry job waits until the
127128
exact PyPI version is downloadable, validates the generated registry metadata,
128129
and publishes it through GitHub OIDC.
129130

docs/source/guide_for_biologists/installation_and_setup.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ the local MCP server, Napari, and Fiji/Bio-Formats support. GPU libraries are
1818
not included. PyImageJ resolves and caches the Fiji/Bio-Formats Java
1919
distribution on first use rather than embedding a standalone ``Fiji.app``.
2020

21-
Download the installer archive for your operating system from the matching
22-
`GitHub release <https://github.com/OpenHCSDev/openhcs/releases>`_, extract it,
23-
and open the included installer. Re-running the same installer updates the
21+
Download the installer for your operating system from the matching
22+
`GitHub release <https://github.com/OpenHCSDev/openhcs/releases>`_. On Windows,
23+
download and run ``OpenHCS-Windows-Installer.exe``. On macOS, open
24+
``OpenHCS-macOS-Installer.dmg`` and then open ``OpenHCS Installer``. Neither
25+
platform requires ZIP extraction. Re-running the same installer updates the
2426
isolated environment. Installation details and failures are retained in the
2527
OpenHCS user log directory shown by the installer.
2628

2729
The initial bootstrap installers are not code-signed or notarized. Windows
2830
SmartScreen or macOS Gatekeeper may therefore ask you to confirm that you trust
29-
the downloaded release. The release archive is only the bootstrap interface;
30-
Python and OpenHCS remain managed by uv and PyPI in the dedicated environment.
31+
the downloaded release. The downloaded installer is only the bootstrap
32+
interface; Python and OpenHCS remain managed by uv and PyPI in the dedicated
33+
environment.
3134

3235
Manual installation
3336
-------------------

openhcs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from openhcs._source_dependencies import ensure_source_checkout_external_paths
1616

17-
__version__ = "0.6.1"
17+
__version__ = "0.6.2"
1818

1919
# Configure polystore defaults for OpenHCS integration
2020
os.environ.setdefault("POLYSTORE_METADATA_FILENAME", "openhcs_metadata.json")

packaging/codex/openhcs/.codex-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openhcs",
3-
"version": "0.6.1",
3+
"version": "0.6.2",
44
"description": "Inspect, author, validate, and run OpenHCS microscopy workflows through the local OpenHCS MCP server.",
55
"author": {
66
"name": "OpenHCSDev",

packaging/codex/openhcs/.mcp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"command": "uvx",
55
"args": [
66
"--from",
7-
"openhcs[gui,mcp]==0.6.1",
7+
"openhcs[gui,mcp]==0.6.2",
88
"openhcs-mcp"
99
]
1010
}

packaging/installers/README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ entry point remain authoritative.
3636

3737
## What users see
3838

39-
The release archives are intended for users who do not want to work in a
40-
terminal. After extracting the downloaded archive, installation stays inside a
41-
small native window:
39+
The release assets are intended for users who do not want to work in a
40+
terminal. No archive extraction is required: Windows users run one executable,
41+
and macOS users open one disk image. Installation stays inside a small native
42+
window:
4243

4344
- Windows presents Welcome, installation-folder, progress, and Finish pages.
4445
The final page can launch OpenHCS immediately.
@@ -56,7 +57,7 @@ From the repository environment:
5657
```bash
5758
python -m pytest -q tests/installer
5859
python scripts/render_installer_contract.py \
59-
--version 0.6.1 \
60+
--version 0.6.2 \
6061
--output /tmp/openhcs-installer-contract.json
6162
```
6263

@@ -66,14 +67,15 @@ each adapter.
6667
## Release assets
6768

6869
Tag publication renders a contract pinned to the tag version and attaches two
69-
archives to the GitHub release:
70+
directly usable files to the GitHub release:
7071

71-
- `OpenHCS-Windows-Installer.zip` contains a small GUI-subsystem
72-
`Install-OpenHCS.exe`, its internal PowerShell worker, and the pinned
73-
contract. Extract the archive and double-click `Install-OpenHCS.exe`.
74-
- `OpenHCS-macOS-Installer.zip` contains a compiled `OpenHCS Installer.app`
75-
with the bootstrap and pinned contract embedded as application resources.
76-
Extract the archive and double-click the application.
72+
- `OpenHCS-Windows-Installer.exe` is a small GUI-subsystem executable with its
73+
PowerShell worker and pinned contract embedded. Double-click the downloaded
74+
file.
75+
- `OpenHCS-macOS-Installer.dmg` contains the compiled
76+
`OpenHCS Installer.app`, whose bootstrap and pinned contract are embedded as
77+
application resources. Open the downloaded disk image, then open the
78+
application.
7779

7880
Pull-request CI parses the Windows PowerShell source and compiles the
7981
GUI-subsystem launcher on Windows, and compiles the universal Swift/AppKit
@@ -85,7 +87,7 @@ MCP generates a two-channel synthetic plate, the packaged neurite preset runs
8587
through the real execution server, Napari receives the result, MCP validates
8688
mounted nonzero viewer payloads, and the smoke shuts down only its dynamically
8789
allocated TCP runtime/viewer endpoints. The tag workflow repeats the source
88-
gates before making either archive a release asset.
90+
gates before making either file a release asset.
8991

9092
Users can run the same portable acceptance after installation:
9193

0 commit comments

Comments
 (0)