-
-
Notifications
You must be signed in to change notification settings - Fork 180
feat: add Windows support (updater + dictation) #323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a2f1afd
0d1c674
3b14fd4
54ea0f6
9b533f5
30cad9b
2544941
8195845
a49d3bc
17b6e21
125647a
1738da8
12f17cb
1d07666
ee26759
3091919
e3417dd
d7a2b30
2856368
10abdb9
0172c47
6bcf130
04ebaea
353a763
a2909b6
85c9e2a
4c5b26d
b8b4e94
3ba7875
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,12 +219,74 @@ jobs: | |
| name: appimage-${{ matrix.arch }} | ||
| path: src-tauri/target/release/bundle/appimage/*.AppImage* | ||
|
|
||
| build_windows: | ||
| runs-on: windows-latest | ||
| environment: release | ||
| env: | ||
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | ||
| TAURI_SIGNING_PRIVATE_KEY_B64: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_B64 }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: setup node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: lts/* | ||
| cache: npm | ||
|
|
||
| - name: install Rust stable | ||
| uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| - name: Install LLVM (bindgen) | ||
| run: choco install llvm -y --no-progress | ||
|
|
||
| - name: Configure LLVM (bindgen) | ||
| run: | | ||
| echo "LIBCLANG_PATH=C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_ENV | ||
| echo "C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_PATH | ||
|
|
||
| - name: install frontend dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Write Tauri signing key | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| python - <<'PY' | ||
| import base64 | ||
| import os | ||
| from pathlib import Path | ||
|
|
||
| raw = base64.b64decode(os.environ["TAURI_SIGNING_PRIVATE_KEY_B64"]) | ||
| home = Path.home() | ||
| target = home / ".tauri" | ||
| target.mkdir(parents=True, exist_ok=True) | ||
| (target / "codexmonitor.key").write_bytes(raw) | ||
| PY | ||
|
|
||
| - name: build windows bundles | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| export TAURI_SIGNING_PRIVATE_KEY | ||
| TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/codexmonitor.key")" | ||
| npm run tauri:build:win | ||
|
|
||
| - name: Upload Windows artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: windows-artifacts | ||
| path: | | ||
| src-tauri/target/release/bundle/nsis/*.exe* | ||
| src-tauri/target/release/bundle/msi/*.msi* | ||
|
|
||
| release: | ||
| runs-on: ubuntu-latest | ||
| environment: release | ||
| needs: | ||
| - build_macos | ||
| - build_linux | ||
| - build_windows | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
@@ -244,6 +306,12 @@ jobs: | |
| path: release-artifacts | ||
| merge-multiple: true | ||
|
|
||
| - name: Download Windows artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: windows-artifacts | ||
| path: release-artifacts | ||
|
|
||
| - name: Build latest.json | ||
| run: | | ||
| set -euo pipefail | ||
|
|
@@ -345,6 +413,27 @@ jobs: | |
| "signature": sig_path.read_text().strip(), | ||
| } | ||
|
|
||
| exe_candidates = sorted(artifacts_dir.rglob("*.exe"), key=lambda p: p.name.lower()) | ||
| windows_installer = None | ||
| for candidate in exe_candidates: | ||
| lowered = candidate.name.lower() | ||
| if "setup" in lowered or "installer" in lowered: | ||
| windows_installer = candidate | ||
| break | ||
| if windows_installer is None and exe_candidates: | ||
| windows_installer = exe_candidates[0] | ||
| if windows_installer is None: | ||
| raise SystemExit("No Windows installer (.exe) found for latest.json") | ||
|
|
||
| win_sig_path = windows_installer.with_suffix(windows_installer.suffix + ".sig") | ||
| if not win_sig_path.exists(): | ||
| raise SystemExit(f"Missing signature for {windows_installer.name}") | ||
|
|
||
| platforms["windows-x86_64"] = { | ||
| "url": f"https://github.com/Dimillian/CodexMonitor/releases/download/v${VERSION}/{windows_installer.name}", | ||
| "signature": win_sig_path.read_text().strip(), | ||
| } | ||
|
|
||
| payload = { | ||
| "version": "${VERSION}", | ||
| "notes": notes, | ||
|
|
@@ -378,6 +467,8 @@ jobs: | |
| release-artifacts/CodexMonitor.app.tar.gz \ | ||
| release-artifacts/CodexMonitor.app.tar.gz.sig \ | ||
| release-artifacts/*.AppImage* \ | ||
| release-artifacts/nsis/*.exe* \ | ||
| release-artifacts/msi/*.msi* \ | ||
|
Comment on lines
+470
to
+471
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In the Useful? React with 👍 / 👎. |
||
| release-artifacts/latest.json | ||
|
|
||
| - name: Bump version and open PR | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # Windows Support Execution Plan | ||
|
|
||
| Source of truth for requirements: `SPEC.md`. | ||
| This plan is **live** and tracks what has landed and what remains for the Windows milestone (auto-updater + dictation). | ||
|
|
||
| ## Workstream | ||
|
|
||
| ### 1) Docs | ||
|
|
||
| - [x] Add `SPEC.md` (Windows scope, updater + dictation required). | ||
| - [x] Keep `PLAN.md` current as work lands. | ||
|
|
||
| ### 2) Git safety + PR | ||
|
|
||
| - [x] Push `feature/windows-support` to a fork remote and set upstream to avoid accidental `main` pushes. | ||
| - [x] Open a draft PR early so CI runs on every push. | ||
|
|
||
| ### 3) Windows UX + path correctness | ||
|
|
||
| - [x] Make “Reveal in Finder” platform-aware (Explorer on Windows). | ||
| - [x] Fix path joining in the frontend so Windows absolute/relative paths behave. | ||
| - [x] Make backend `open_workspace_in` work cross-platform (macOS/Windows/Linux). | ||
| - [x] Make default “Open in” targets sensible on Windows (Explorer + command-based editors). | ||
| - [x] Make default shortcuts Windows-friendly (Ctrl/Alt; avoid Cmd+Ctrl collapse). | ||
|
|
||
| ### 4) Dictation on Windows (required) | ||
|
|
||
| - [x] Enable Whisper dictation on Windows (`whisper-rs` + `cpal`) by removing the Windows stub. | ||
| - [x] Update Windows build checks (`doctor:win`) to require LLVM/Clang + CMake. | ||
| - [x] Fix `doctor:win` dependency detection on Unix (no shell builtins). | ||
|
|
||
| ### 5) CI (required) | ||
|
|
||
| - [x] Add a Windows CI job that runs a Tauri debug build with `src-tauri/tauri.windows.conf.json`. | ||
|
|
||
| ### 6) Release + updater (required) | ||
|
|
||
| - [x] Enable Windows updater artifacts in `src-tauri/tauri.windows.conf.json`. | ||
| - [x] Add a Windows release build job to `.github/workflows/release.yml`. | ||
| - [x] Extend `latest.json` generation to include Windows URL + signature. | ||
|
|
||
| ## Validation (run after each step) | ||
|
|
||
| - `npm run lint` | ||
| - `npm run test` | ||
| - `npm run typecheck` | ||
| - Rust checks are executed in CI for macOS + Windows jobs added by this plan. | ||
|
|
||
| ## Manual checklist (Windows) | ||
|
|
||
| - [ ] `npm run tauri:build:win` succeeds on Windows 10/11. | ||
| - [ ] App launches and can open workspaces. | ||
| - [ ] Adding a workspace succeeds when `codex --version` works in Windows Terminal. | ||
| - [ ] “Reveal in Explorer” opens the right folder. | ||
| - [ ] Shortcut hints use Ctrl/Alt labels and work on Windows. | ||
| - [ ] Theme dropdown options are readable in Dark/Dim with Reduce Transparency off. | ||
| - [ ] Auto-updater finds and applies the latest release. | ||
| - [ ] Dictation works end-to-end (download → hold-to-talk → transcript). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # Windows Support Spec | ||
|
|
||
| ## Goal | ||
|
|
||
| Ship a **fully functioning Windows version** of CodexMonitor with: | ||
|
|
||
| - **Auto-updater** enabled and wired into the release workflow (`latest.json` includes Windows). | ||
| - **Dictation** working on Windows (same UX + model management as other platforms). | ||
|
|
||
| This spec is the source of truth for Windows support requirements and acceptance criteria. | ||
|
|
||
| ## Target Platforms | ||
|
|
||
| - Windows 10/11, **x86_64** (GitHub Actions `windows-latest`). | ||
|
|
||
| ## Non-Goals (for this milestone) | ||
|
|
||
| - Authenticode / EV code signing (nice-to-have; not required to be functional). | ||
| - Windows ARM64 builds. | ||
|
|
||
| ## Functional Requirements | ||
|
|
||
| ### Updater (Required) | ||
|
|
||
| - Windows release artifacts must be produced by CI and attached to GitHub Releases. | ||
| - `latest.json` must include a **Windows platform entry** with: | ||
| - correct asset URL | ||
| - signature generated by Tauri updater signing | ||
| - In-app updater must be enabled for Windows builds (same endpoints + pubkey as other platforms). | ||
| - The updater entry for Windows uses the **NSIS installer** (`.exe` + `.exe.sig`) as the updater bundle. | ||
|
|
||
| ### Dictation (Required) | ||
|
|
||
| Dictation must work on Windows with the same surface area as other platforms: | ||
|
|
||
| - Model status: missing/downloading/ready/error | ||
| - Model download/cancel/remove | ||
| - Session lifecycle: start/listening/stop->processing/transcript/cancel | ||
| - Emits the same Tauri events: | ||
| - `dictation-download` | ||
| - `dictation-event` | ||
|
|
||
| Implementation choice for this milestone: | ||
|
|
||
| - Use the existing Whisper-based implementation on Windows (via `whisper-rs` + `cpal`). | ||
|
|
||
| ### Windows UX Correctness (Required) | ||
|
|
||
| - “Reveal in Finder” strings must be platform-aware (“Explorer” on Windows). | ||
| - Opening paths in an editor or file manager must behave correctly on Windows. | ||
| - Shortcut hints and formatting must be platform-aware on Windows (Ctrl/Alt labels; no macOS-only glyphs). | ||
| - Settings selects (for example Theme) must remain readable in Dark/Dim with Reduce Transparency off. | ||
|
|
||
| ## Build & Tooling Requirements | ||
|
|
||
| ### Windows Build Prereqs (dev + CI) | ||
|
|
||
| Required on Windows to build dictation (Whisper + bindgen): | ||
|
|
||
| - CMake | ||
| - LLVM/Clang (for `bindgen` / `libclang`) | ||
|
|
||
| `npm run doctor:win` must fail fast with actionable instructions if missing. | ||
| `npm run doctor:win` must correctly detect installed dependencies on Windows/macOS/Linux. | ||
|
|
||
| ## CI / Release Requirements | ||
|
|
||
| ### CI (Required) | ||
|
|
||
| - Add a Windows job to `.github/workflows/ci.yml` that: | ||
| - installs deps | ||
| - runs a Windows Tauri debug build (`--no-bundle`) using the Windows config | ||
|
|
||
| ### Release (Required) | ||
|
|
||
| Update `.github/workflows/release.yml` to include Windows: | ||
|
|
||
| - Build Windows bundles on `windows-latest` | ||
| - Upload `.msi` / `.exe` plus updater `.sig` artifacts | ||
| - Include Windows in generated `latest.json` | ||
|
|
||
| ## Acceptance Criteria | ||
|
|
||
| - `npm run tauri:build:win` succeeds on Windows. | ||
| - Windows release workflow publishes installers and `latest.json` that enables in-app updates. | ||
| - Dictation works on Windows end-to-end (model download → hold-to-talk → transcript). | ||
| - Adding a workspace works when `codex --version` runs in Windows Terminal (Codex PATH handling is correct). | ||
| - Repo checks pass: | ||
| - `npm run lint` | ||
| - `npm run test` | ||
| - `npm run typecheck` | ||
| - Rust checks executed in CI for Windows/macOS as configured |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Windows installer detection logic searches for .exe files containing "setup" or "installer" in lowercase filenames, with a fallback to the first .exe found if no match. This heuristic approach could be fragile if:
Consider either:
*_x64-setup.exeor similar)For robustness, at minimum add a log message showing which installer was selected.