Skip to content
Merged
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
219 changes: 219 additions & 0 deletions .github/workflows/desktop-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
name: Desktop package

on:
workflow_dispatch:
push:
branches: ['release/desktop-*']

permissions:
contents: read

env:
CORE_REF: v2.12.1
CORE_COMMIT: 1838d76f7c49850345ab7306c3ffdcb663f81cfd
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
STANDTERM_DISABLE_AGENTINFO_CURRENT: '1'
PYTHONDONTWRITEBYTECODE: '1'

jobs:
macos-arm64:
runs-on: macos-15
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '24'
- uses: actions/setup-python@v5
with:
python-version: '3.12'
architecture: arm64
- name: Verify native tools and prepare isolated dependencies
run: |
test "$(uname -m)" = arm64
node -e "require('node:assert/strict').equal(process.arch, 'arm64')"
python -c "import platform; assert platform.machine() == 'arm64'"
test "$(git rev-parse "$CORE_REF^{commit}")" = "$CORE_COMMIT"
python -m venv tools/.venv_macos
tools/.venv_macos/bin/python -m pip install -r requirements.txt
tools/.venv_macos/bin/python -m pip freeze > tools/requirements_macos.txt
npm ci --prefix desktop --no-audit --no-fund
mkdir -p desktop/dist/evidence
- name: Test Desktop source
run: npm test --prefix desktop > desktop/dist/evidence/desktop-unit.log 2>&1
- name: Stage formal Core and build native DMG
run: |
stage=$(node desktop/stage-windows.cjs --macos --core-ref "$CORE_REF")
echo "DESKTOP_STAGE=$stage" >> "$GITHUB_ENV"
node desktop/test/core-release-inspect.cjs "$stage" "$CORE_REF" > desktop/dist/evidence/core-release.json
npm ci --prefix "$stage" --no-audit --no-fund
npm run make:mac --prefix "$stage"
- name: Inspect the app and mounted DMG
run: |
app="$DESKTOP_STAGE/out.noindex/mac-arm64/StandTermDesktop.app"
test -d "$app"
test "$(lipo -archs "$app/Contents/MacOS/StandTermDesktop")" = arm64
codesign --verify --deep --strict "$app"
node desktop/test/package-inspect.cjs "$app/Contents/Resources" "$DESKTOP_STAGE" "$DESKTOP_STAGE/node_modules/@electron/asar" > desktop/dist/evidence/package-inspection.log
dmg=("$DESKTOP_STAGE"/out.noindex/*.dmg)
test "${#dmg[@]}" = 1
mountpoint=$(mktemp -d "$RUNNER_TEMP/standterm-dmg.XXXXXX")
hdiutil attach "${dmg[0]}" -readonly -nobrowse -mountpoint "$mountpoint"
trap 'hdiutil detach "$mountpoint"' EXIT
node desktop/test/package-inspect.cjs "$mountpoint/StandTermDesktop.app/Contents/Resources" "$DESKTOP_STAGE" "$DESKTOP_STAGE/node_modules/@electron/asar" >> desktop/dist/evidence/package-inspection.log
echo "DESKTOP_APP=$app" >> "$GITHUB_ENV"
- name: Verify native shell initialization
run: tools/.venv_macos/bin/python desktop/test/macos_shell_smoke.py > desktop/dist/evidence/macos-shell.log 2>&1
- name: Exercise packaged application
id: smoke
continue-on-error: true
timeout-minutes: 5
run: |
runtime=$(mktemp -d "$RUNNER_TEMP/standterm-core.XXXXXX")
cp -R "$DESKTOP_APP/Contents/Resources/bundle/core/." "$runtime/"
export STANDTERM_DESKTOP_TEST_ROOT="$runtime"
export STANDTERM_DESKTOP_PYTHON="$GITHUB_WORKSPACE/tools/.venv_macos/bin/python"
export STANDTERM_AGENT_RUNTIME_DIR="$runtime/agent-state"
export STANDTERM_SESSION_RECOVERY_STORE="$runtime/recovery.json"
node desktop/test/packaged-smoke-runner.cjs "$DESKTOP_APP/Contents/MacOS/StandTermDesktop" macos > desktop/dist/evidence/packaged-smoke.log 2>&1
- name: Exercise first-run setup and reuse
id: setup
continue-on-error: true
timeout-minutes: 10
run: node desktop/node_modules/electron/cli.js desktop/test/macos-setup-integration.cjs "$GITHUB_WORKSPACE/tools/.venv_macos/bin/python" "$DESKTOP_STAGE" > desktop/dist/evidence/setup-integration.log 2>&1
- name: Record build identity and verification results
if: always() && env.DESKTOP_APP != ''
env:
SMOKE_RESULT: ${{ steps.smoke.outcome }}
SETUP_RESULT: ${{ steps.setup.outcome }}
run: |
cp tools/requirements_macos.txt desktop/dist/evidence/tested-runtime-dependencies.txt
cp "$DESKTOP_STAGE/bundle/manifest.json" desktop/dist/evidence/core-manifest.json
cp "$DESKTOP_STAGE/release-identity.json" desktop/dist/evidence/release-identity.json
tools/.venv_macos/bin/python - <<'PY'
import json, os, platform
from pathlib import Path
info = {
'sourceCommit': os.environ['GITHUB_SHA'],
'coreCommit': os.environ['CORE_COMMIT'],
'platform': platform.platform(), 'architecture': platform.machine(),
'python': platform.python_version(),
'runUrl': f"https://github.com/{os.environ['GITHUB_REPOSITORY']}/actions/runs/{os.environ['GITHUB_RUN_ID']}",
'packagedSmoke': os.environ['SMOKE_RESULT'],
'firstRunSetup': os.environ['SETUP_RESULT'],
'signature': 'Ad hoc; no Developer ID or notarization',
'manualAcceptance': 'Finder, Gatekeeper, IME and upgrade not exercised',
}
Path('desktop/dist/evidence/build-metadata.json').write_text(json.dumps(info, indent=2) + '\n')
PY
cp "$DESKTOP_STAGE"/out.noindex/*.dmg desktop/dist/evidence/
cd desktop/dist/evidence
shasum -a 256 *.dmg > SHA256SUMS.txt
- uses: actions/upload-artifact@v4
if: always()
with:
name: StandTerm-Desktop-macos-arm64
path: desktop/dist/evidence/
if-no-files-found: error
retention-days: 14
- name: Require native interaction checks
if: steps.smoke.outcome != 'success' || steps.setup.outcome != 'success'
run: exit 1

windows-x64:
runs-on: windows-2025
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '24'
- uses: actions/setup-python@v5
with:
python-version: '3.12'
architecture: x64
- name: Prepare native tools and isolated dependencies
run: |
node -e "require('node:assert/strict').equal(process.arch, 'x64')"
python -c "import struct,sys; assert sys.platform == 'win32' and struct.calcsize('P') == 8"
if ((git rev-parse "$env:CORE_REF^{commit}") -ne $env:CORE_COMMIT) { throw 'Unexpected Core release commit' }
python -m venv tools/.venv_win
tools/.venv_win/Scripts/python.exe -m pip install -r requirements.txt
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
tools/.venv_win/Scripts/python.exe -m pip freeze > tools/requirement_winenv.txt
npm ci --prefix desktop --no-audit --no-fund
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
New-Item -ItemType Directory desktop/dist/evidence
- name: Test Desktop source
run: npm test --prefix desktop *> desktop/dist/evidence/desktop-unit.log
- name: Stage formal Core and build native installer
run: |
$stage = node desktop/stage-windows.cjs --core-ref $env:CORE_REF
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
"DESKTOP_STAGE=$stage" >> $env:GITHUB_ENV
node desktop/test/core-release-inspect.cjs $stage $env:CORE_REF > desktop/dist/evidence/core-release.json
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm ci --prefix $stage --no-audit --no-fund
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm run make:win --prefix $stage
- name: Inspect the Windows application
run: |
node desktop/test/package-inspect.cjs "$env:DESKTOP_STAGE/out/win-unpacked/resources" $env:DESKTOP_STAGE "$env:DESKTOP_STAGE/node_modules/@electron/asar" > desktop/dist/evidence/package-inspection.log
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
"DESKTOP_APP=$env:DESKTOP_STAGE/out/win-unpacked/StandTermDesktop.exe" >> $env:GITHUB_ENV
- name: Exercise packaged application
id: smoke
continue-on-error: true
timeout-minutes: 5
run: |
$runtime = Join-Path $env:RUNNER_TEMP ('standterm-core-' + [guid]::NewGuid())
New-Item -ItemType Directory $runtime
Copy-Item "$env:DESKTOP_STAGE/out/win-unpacked/resources/bundle/core/*" $runtime -Recurse
$env:STANDTERM_DESKTOP_TEST_ROOT = $runtime
$env:STANDTERM_DESKTOP_PYTHON = "$env:GITHUB_WORKSPACE/tools/.venv_win/Scripts/python.exe"
$env:STANDTERM_AGENT_RUNTIME_DIR = "$runtime/agent-state"
$env:STANDTERM_SESSION_RECOVERY_STORE = "$runtime/recovery.json"
node desktop/test/packaged-smoke-runner.cjs $env:DESKTOP_APP windows *> desktop/dist/evidence/packaged-smoke.log
exit $LASTEXITCODE
- name: Exercise first-run preparation and reuse
id: setup
continue-on-error: true
timeout-minutes: 10
run: node desktop/test/bootstrap-integration.cjs "$env:GITHUB_WORKSPACE/tools/.venv_win/Scripts/python.exe" "$env:DESKTOP_STAGE/out/win-unpacked/resources/bundle" *> desktop/dist/evidence/setup-integration.log
- name: Record build identity and verification results
if: always() && env.DESKTOP_APP != ''
env:
SMOKE_RESULT: ${{ steps.smoke.outcome }}
SETUP_RESULT: ${{ steps.setup.outcome }}
run: |
Copy-Item tools/requirement_winenv.txt desktop/dist/evidence/tested-runtime-dependencies.txt
Copy-Item "$env:DESKTOP_STAGE/bundle/manifest.json" desktop/dist/evidence/core-manifest.json
Copy-Item "$env:DESKTOP_STAGE/release-identity.json" desktop/dist/evidence/release-identity.json
$info = [ordered]@{
sourceCommit = $env:GITHUB_SHA; coreCommit = $env:CORE_COMMIT
platform = 'win32'; architecture = 'x64'
runUrl = "https://github.com/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
packagedSmoke = $env:SMOKE_RESULT; firstRunSetup = $env:SETUP_RESULT
signature = (Get-AuthenticodeSignature $env:DESKTOP_APP).Status.ToString()
fileVersion = (Get-Item $env:DESKTOP_APP).VersionInfo.FileVersion
manualAcceptance = 'Installer upgrade, real IME and clipboard not exercised'
}
$info | ConvertTo-Json > desktop/dist/evidence/build-metadata.json
Copy-Item "$env:DESKTOP_STAGE/out/*Setup.exe" desktop/dist/evidence/
Get-ChildItem desktop/dist/evidence/*Setup.exe | ForEach-Object {
(Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower() + ' ' + $_.Name
} > desktop/dist/evidence/SHA256SUMS.txt
- uses: actions/upload-artifact@v4
if: always()
with:
name: StandTerm-Desktop-windows-x64
path: desktop/dist/evidence/
if-no-files-found: error
retention-days: 14
- name: Require native interaction checks
if: steps.smoke.outcome != 'success' || steps.setup.outcome != 'success'
run: exit 1
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@ It fixes Windows local shell startup when an executable path contains spaces.
Core 2.12 adds SSH Agent Tunnel with shared skills and helpers, Agent Panel permission
sync, Agent Info for the current tab, and SSH host fingerprint management.
IME input-line anchoring remains an [experimental PoC](docs/ime_anchor_poc.md).
This release does not publish new Desktop installers: the downloads below remain
Desktop 0.4.3 evaluation builds with their original bundled Core snapshot.
The separate Desktop 0.5.0 evaluation below bundles the formal Core 2.12.1 source.

![StandTerm Desktop with terminal rendering tests, local and SSH tabs, and a floating PowerShell terminal](standterm_desktop.png)

*Desktop development preview. The toolbar shown above is newer than the published
Desktop 0.4.3 installers.*
*Desktop preview. See the release notes for the current controls and validation.*

## Desktop Downloads (Evaluation)

[StandTerm Desktop 0.4.3](https://github.com/askac/standterm/releases/tag/desktop-v0.4.3)
[StandTerm Desktop 0.5.0 / Core 2.12.1](https://github.com/askac/standterm/releases/tag/desktop-v0.5.0-2.12.1)
is available as an evaluation pre-release, not a production-qualified release.

| Platform | Download | Required before installation |
| --- | --- | --- |
| Windows x64, including Windows + WSL | [Windows installer (.exe)](https://github.com/askac/standterm/releases/download/desktop-v0.4.3/StandTerm-Desktop-0.4.3-win32-x64-Setup.exe) | Python 3.10+ with venv/ensurepip in each selected environment; WSL mode also needs an existing WSL distribution. |
| macOS Apple Silicon | [macOS installer (.dmg)](https://github.com/askac/standterm/releases/download/desktop-v0.4.3/StandTerm-Desktop-0.4.3-mac-arm64.dmg) | Native arm64 Python 3.10+ with venv/ensurepip. Intel/Rosetta is not qualified. |
| Windows x64, including Windows + WSL | [Windows installer (.exe)](https://github.com/askac/standterm/releases/download/desktop-v0.5.0-2.12.1/StandTerm-Desktop-0.5.0-2.12.1-win32-x64-Setup.exe) | Python 3.10+ with venv/ensurepip in each selected environment; WSL mode also needs an existing WSL distribution. |
| macOS Apple Silicon | [macOS installer (.dmg)](https://github.com/askac/standterm/releases/download/desktop-v0.5.0-2.12.1/StandTerm-Desktop-0.5.0-2.12.1-mac-arm64.dmg) | Native arm64 Python 3.10+ with venv/ensurepip. Intel/Rosetta is not qualified. |

Packages include Electron and Core. **Git, Node.js and npm are not required**;
Python and its virtual environment are not bundled.
The optional advanced Git Core source requires Git in the selected backend
environment. Bundled Core recovery remains available without Git.

1. Download the package for your platform and check the release's
[SHA256SUMS](https://github.com/askac/standterm/releases/download/desktop-v0.4.3/SHA256SUMS).
[SHA256SUMS](https://github.com/askac/standterm/releases/download/desktop-v0.5.0-2.12.1/SHA256SUMS).
2. On Windows, run the installer and choose **Windows only**, **Windows + WSL**
or **WSL only**. Native Windows mode needs 64-bit Windows Python; installing
Windows Python does not satisfy WSL mode. On macOS, copy the app to a
Expand Down
Loading
Loading