fix: windows (#1056)

* fix windows

* fix: address code review feedback - unused imports and async function

- Remove unused imports in TypeScript files:
  - platform.test.ts: remove unused beforeEach, afterAll, test, os, fs, ShellType; add missing afterEach, it
  - cli-tool-manager.ts: remove unused getPathDelimiter
  - paths.ts: remove unused homeDir variable

- Remove unused imports in Python files:
  - client.py: remove unused get_claude_detection_paths import
  - test_platform.py: remove unused pytest, MagicMock, find_executable, get_comspec_path

- Fix findExecutable in platform/index.ts: change from async to sync
  (function uses synchronous existsSync, should not be async)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: correct Windows path construction and Python type annotations

Fixes:
1. Windows path construction - path.join('C:', ...) produces 'C:foo'
   (relative to C: drive), not 'C:\foo'. Changed to 'C:\Program Files'
   as the first segment to get proper absolute paths.

2. getCurrentOS() now handles unknown platforms (e.g., FreeBSD) by
   defaulting to Linux for Unix-like systems.

3. getPlatformDescription() now has a fallback when OS mapping fails.

4. Shell config test now accepts cmd.exe fallback (when PowerShell
   paths don't exist in test environments).

5. Python ruff fixes - sorted imports and modernized type annotations
   (list instead of List, dict instead of Dict, X | None instead of
   Optional[X]).

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix comment (sentry) and tests

* try and fix lint and tests

* fix tests

* fix remaining

* we need to add more tests, i have a PR for this but it has been ignored

* fix(tests): normalize path separators in fs.existsSync mock for Windows

path.join() uses backslashes on Windows, so the mock now normalizes
paths to forward slashes before comparison to ensure tests pass
cross-platform.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(platform): address code review findings for cross-platform safety

- Fix argument quoting vulnerability in build_windows_command using subprocess.list2cmdline()
- Remove duplicate Claude detection paths from client.py by using platform module
- Add empty string check in withExecutableExtension (TypeScript)
- Add empty string check in isSecurePath (TypeScript) for cross-platform consistency
- Add Homebrew paths for macOS in getClaudeExecutablePath (TypeScript)
- Fix misleading CI step name (was "Setup Node.js and Python", now "Setup Python")
- Fix expandWindowsEnvVars to provide sensible defaults for unset env vars

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(platform): condense path construction and expand Python glob patterns

- Condense multi-line path construction in get_claude_detection_paths_structured()
  to single-line expressions for cleaner ruff formatting
- Fix getPythonPaths() to expand glob patterns (Python3*) using readdirSync
  instead of returning literal glob strings that existsSync can't handle
- Add expandDirPattern helper function for safe directory pattern matching

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(platform): return Python commands as argument sequences

Change get_python_commands() / getPythonCommands() to return command
arguments as sequences (list of lists) instead of space-separated strings.

Before: ["py -3", "python"] - broken with subprocess/shutil.which
After:  [["py", "-3"], ["python"]] - works correctly

This allows callers to:
- Pass cmd directly to subprocess.run(cmd)
- Use cmd[0] with shutil.which() for executable lookup

Updated both Python and TypeScript implementations for consistency.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

---------

Co-authored-by: Claude Opus 4.5 <[email protected]>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Andy <[email protected]>
This commit is contained in:
Alex
2026-01-16 10:09:56 +01:00
committed by GitHub
co-authored by Claude Opus 4.5 factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> Andy
parent 30638c2f16
commit d6234f52b1
15 changed files with 2286 additions and 190 deletions
+13 -1
View File
@@ -40,9 +40,21 @@ Follow conventional commits: `<type>: <subject>`
- [ ] I've followed the code principles (SOLID, DRY, KISS)
- [ ] My PR is small and focused (< 400 lines ideally)
## Platform Testing Checklist
**CRITICAL:** This project supports Windows, macOS, and Linux. Platform-specific bugs are a common source of breakage.
- [ ] **Windows tested** (either on Windows or via CI)
- [ ] **macOS tested** (either on macOS or via CI)
- [ ] **Linux tested** (CI covers this)
- [ ] Used centralized `platform/` module instead of direct `process.platform` checks
- [ ] No hardcoded paths (used `findExecutable()` or platform abstractions)
**If you only have access to one OS:** CI now tests on all platforms. Ensure all checks pass before submitting.
## CI/Testing Requirements
- [ ] All CI checks pass
- [ ] All CI checks pass on **all platforms** (Windows, macOS, Linux)
- [ ] All existing tests pass
- [ ] New features include test coverage
- [ ] Bug fixes include regression tests
+103 -17
View File
@@ -1,3 +1,11 @@
# Cross-Platform CI Pipeline
#
# Tests on all target platforms (Linux, Windows, macOS) to catch
# platform-specific bugs before they merge. ALL platforms must pass.
#
# Why this matters: Platform-specific code often breaks when developers
# commit from one OS without testing on others. This CI prevents that.
name: CI
on:
@@ -15,15 +23,21 @@ permissions:
actions: read
jobs:
# Python tests
# --------------------------------------------------------------------------
# Python Backend Tests - All Platforms
# --------------------------------------------------------------------------
test-python:
runs-on: ubuntu-latest
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Don't cancel all jobs if one platform fails
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.12', '3.13']
steps:
- name: Checkout
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
@@ -31,13 +45,14 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
- name: Install uv package manager
uses: astral-sh/setup-uv@v4
with:
version: "latest"
- name: Install dependencies
working-directory: apps/backend
shell: bash
run: |
uv venv
uv pip install -r requirements.txt
@@ -45,22 +60,32 @@ jobs:
- name: Run tests
working-directory: apps/backend
shell: bash
env:
PYTHONPATH: ${{ github.workspace }}/apps/backend
run: |
source .venv/bin/activate
if [ "$RUNNER_OS" == "Windows" ]; then
source .venv/Scripts/activate
else
source .venv/bin/activate
fi
pytest ../../tests/ -v --tb=short -x
- name: Run tests with coverage
- name: Run coverage (Python 3.12 only)
if: matrix.python-version == '3.12'
working-directory: apps/backend
shell: bash
env:
PYTHONPATH: ${{ github.workspace }}/apps/backend
run: |
source .venv/bin/activate
pytest ../../tests/ -v --cov=. --cov-report=xml --cov-report=term-missing --cov-fail-under=20
if [ "$RUNNER_OS" == "Windows" ]; then
source .venv/Scripts/activate
else
source .venv/bin/activate
fi
pytest ../../tests/ -v --cov=. --cov-report=xml --cov-report=term-missing --cov-fail-under=10
- name: Upload coverage reports
- name: Upload coverage to Codecov
if: matrix.python-version == '3.12'
uses: codecov/codecov-action@v4
with:
@@ -69,11 +94,20 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# Frontend lint, typecheck, test, and build
# --------------------------------------------------------------------------
# Frontend Tests - All Platforms
# --------------------------------------------------------------------------
test-frontend:
runs-on: ubuntu-latest
name: Frontend on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
@@ -83,9 +117,11 @@ jobs:
- name: Get npm cache directory
id: npm-cache
shell: bash
run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v4
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ${{ steps.npm-cache.outputs.dir }}
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
@@ -95,18 +131,68 @@ jobs:
working-directory: apps/frontend
run: npm ci --ignore-scripts
- name: Lint
- name: Run linter
working-directory: apps/frontend
run: npm run lint
- name: Type check
- name: Run TypeScript type check
working-directory: apps/frontend
run: npm run typecheck
- name: Run tests
- name: Run unit tests
working-directory: apps/frontend
run: npm run test
- name: Build
- name: Build application
working-directory: apps/frontend
run: npm run build
# --------------------------------------------------------------------------
# Platform-Specific Integration Tests
# --------------------------------------------------------------------------
test-platform-integration:
name: Platform Integration Tests on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
# Only run integration tests after basic tests pass
needs: [test-python, test-frontend]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"
- name: Install backend dependencies
working-directory: apps/backend
shell: bash
run: |
uv venv
uv pip install -r requirements.txt
uv pip install -r ../../tests/requirements-test.txt
- name: Run platform-specific tests
working-directory: apps/backend
shell: bash
env:
PYTHONPATH: ${{ github.workspace }}/apps/backend
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
source .venv/Scripts/activate
else
source .venv/bin/activate
fi
pytest ../../tests/test_platform.py -v --tb=short