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
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ env:

jobs:
build-windows:
name: build-windows(${{ matrix.arch }})
if: github.event.inputs.platform == 'all' || contains(github.event.inputs.platform, 'windows')
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- arch: x64
platform: win-x64
runner: windows-latest
runner: windows-2025-vs2026
unpacked: win-unpacked
- arch: arm64
platform: win-arm64
Expand Down
50 changes: 47 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,20 @@ jobs:
fi

build-windows:
name: build-windows(${{ matrix.arch }})
needs: [resolve-tag, validate-main-ancestor]
runs-on: windows-latest
runs-on: ${{ matrix.runner }}
strategy:
matrix:
arch: [x64]
include:
- arch: x64
platform: win-x64
runner: windows-2025-vs2026
unpacked: win-unpacked
- arch: arm64
platform: win-arm64
runner: windows-11-arm
unpacked: win-arm64-unpacked
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -177,7 +183,7 @@ jobs:
- name: Verify bundled plugins
shell: bash
run: |
pnpm run plugin:verify -- --name feishu --platform win32 --arch ${{ matrix.arch }} --plugin-root dist/win-unpacked/resources/app.asar.unpacked/plugins
pnpm run plugin:verify -- --name feishu --platform win32 --arch ${{ matrix.arch }} --plugin-root dist/${{ matrix.unpacked }}/resources/app.asar.unpacked/plugins

- name: Upload artifacts
uses: actions/upload-artifact@v6
Expand Down Expand Up @@ -412,6 +418,44 @@ jobs:
cp artifacts/deepchat-win-x64/*.blockmap release_assets/ 2>/dev/null || true
fi

# Process Windows arm64 artifacts
if [ -d "artifacts/deepchat-win-arm64" ]; then
cp artifacts/deepchat-win-arm64/*.exe release_assets/ 2>/dev/null || true
cp artifacts/deepchat-win-arm64/*.msi release_assets/ 2>/dev/null || true
cp artifacts/deepchat-win-arm64/*.zip release_assets/ 2>/dev/null || true
cp artifacts/deepchat-win-arm64/*.blockmap release_assets/ 2>/dev/null || true
fi

merge_windows_yml() {
local name="$1"
local x64="artifacts/deepchat-win-x64/$name"
local arm64="artifacts/deepchat-win-arm64/$name"
if [ -f "$x64" ] && [ -f "$arm64" ]; then
ruby -ryaml -e '
x64 = YAML.load_file(ARGV[0]) || {}
arm = YAML.load_file(ARGV[1]) || {}
merged = x64.dup
merged["version"] ||= arm["version"]
merged["releaseDate"] ||= arm["releaseDate"]
merged["releaseNotes"] ||= arm["releaseNotes"]
merged["path"] ||= arm["path"]
merged["sha512"] ||= arm["sha512"]
files = []
files.concat(x64["files"]) if x64["files"].is_a?(Array)
files.concat(arm["files"]) if arm["files"].is_a?(Array)
merged["files"] = files.uniq { |f| f["url"] }
File.write(ARGV[2], merged.to_yaml)
' "$x64" "$arm64" "release_assets/$name"
elif [ -f "$x64" ]; then
cp "$x64" "release_assets/$name"
elif [ -f "$arm64" ]; then
cp "$arm64" "release_assets/$name"
fi
}

merge_windows_yml latest.yml
merge_windows_yml beta.yml

# Process Linux x64 artifacts
if [ -d "artifacts/deepchat-linux-x64" ]; then
cp artifacts/deepchat-linux-x64/*.AppImage release_assets/ 2>/dev/null || true
Expand Down
115 changes: 105 additions & 10 deletions .github/workflows/windows-arm64-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ jobs:
- name: Install Windows arm64 runtimes
run: pnpm run installRuntime:win:arm64

- name: Verify DuckDB and VSS on Windows arm64
run: pnpm run smoke:duckdb:vss

- name: Build Windows arm64 package
run: |
pnpm run build
Expand All @@ -60,21 +63,113 @@ jobs:
run: |
pnpm run plugin:verify -- --name feishu --platform win32 --arch arm64 --plugin-root dist/win-arm64-unpacked/resources/app.asar.unpacked/plugins

- name: Run packaged E2E smoke tests
run: pnpm run e2e:smoke:ci
env:
DEEPCHAT_E2E_APP_MODE: packaged
DEEPCHAT_E2E_EXECUTABLE_PATH: ${{ github.workspace }}/dist/win-arm64-unpacked/DeepChat.exe
- name: Run Windows arm64 launch smoke test
run: pnpm exec playwright test -c test/e2e/playwright.ci.config.ts test/e2e/specs/01-launch.smoke.spec.ts

- name: Run packaged app process smoke
shell: pwsh
run: |
$diagRoot = Join-Path $env:GITHUB_WORKSPACE 'build/windows-arm64-e2e-diagnostics'
New-Item -ItemType Directory -Force -Path $diagRoot | Out-Null

$exe = Join-Path $env:GITHUB_WORKSPACE 'dist/win-arm64-unpacked/DeepChat.exe'
$stdout = Join-Path $diagRoot 'packaged-smoke-stdout.log'
$stderr = Join-Path $diagRoot 'packaged-smoke-stderr.log'
$chromiumLog = Join-Path $diagRoot 'packaged-smoke-chromium.log'

Get-Process -Name 'DeepChat' -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2

if (!(Test-Path $exe)) {
throw "Packaged executable not found: $exe"
}

$process = Start-Process `
-FilePath $exe `
-WorkingDirectory (Split-Path $exe) `
-ArgumentList @('--enable-logging', '--v=1', "--log-file=$chromiumLog") `
-RedirectStandardOutput $stdout `
-RedirectStandardError $stderr `
-PassThru

try {
Start-Sleep -Seconds 20
$process.Refresh()
if ($process.HasExited) {
"Packaged app exited during process smoke. Exit code: $($process.ExitCode)" |
Out-File -FilePath (Join-Path $diagRoot 'packaged-smoke-result.log') -Encoding utf8
throw "Packaged app exited during process smoke. Exit code: $($process.ExitCode)"
}

"Packaged app stayed alive for 20 seconds. PID: $($process.Id)" |
Out-File -FilePath (Join-Path $diagRoot 'packaged-smoke-result.log') -Encoding utf8
} finally {
$process.Refresh()
if (!$process.HasExited) {
taskkill /pid $($process.Id) /t /f | Out-Null
Start-Sleep -Seconds 2
}
}

- name: Collect Windows arm64 diagnostics
if: always()
shell: pwsh
run: |
$diagRoot = Join-Path $env:GITHUB_WORKSPACE 'build/windows-arm64-e2e-diagnostics'
New-Item -ItemType Directory -Force -Path $diagRoot | Out-Null

$filesystemLog = Join-Path $diagRoot 'filesystem.txt'
$paths = @(
"$env:APPDATA\DeepChat",
"$env:LOCALAPPDATA\DeepChat",
"$env:GITHUB_WORKSPACE\dist\win-arm64-unpacked",
"$env:GITHUB_WORKSPACE\runtime"
)

foreach ($path in $paths) {
"== $path ==" | Out-File -Append -FilePath $filesystemLog -Encoding utf8
if (Test-Path $path) {
Get-ChildItem -Force -Recurse $path |
Select-Object FullName, Length, LastWriteTime |
Format-Table -AutoSize |
Out-String -Width 4096 |
Out-File -Append -FilePath $filesystemLog -Encoding utf8
} else {
"MISSING" | Out-File -Append -FilePath $filesystemLog -Encoding utf8
}
}

$deepchatLogDir = Join-Path $env:APPDATA 'DeepChat\logs'
if (Test-Path $deepchatLogDir) {
Copy-Item -Path (Join-Path $deepchatLogDir '*') -Destination $diagRoot -Recurse -Force -ErrorAction SilentlyContinue
}

$nativeModulesLog = Join-Path $diagRoot 'native-modules.txt'
if (Test-Path "$env:GITHUB_WORKSPACE\dist\win-arm64-unpacked") {
Get-ChildItem -Force -Recurse "$env:GITHUB_WORKSPACE\dist\win-arm64-unpacked" -Filter '*.node' |
Select-Object FullName, Length, LastWriteTime |
Format-Table -AutoSize |
Out-String -Width 4096 |
Out-File -FilePath $nativeModulesLog -Encoding utf8
}

try {
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; StartTime = (Get-Date).AddMinutes(-30) } -ErrorAction Stop |
Where-Object { $_.Message -match 'DeepChat|Electron|Application Error|Faulting application' } |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message |
Format-List |
Out-File -FilePath (Join-Path $diagRoot 'windows-application-events.txt') -Encoding utf8
} catch {
$_ | Out-File -FilePath (Join-Path $diagRoot 'windows-application-events.txt') -Encoding utf8
}

- name: Upload Windows arm64 artifacts
- name: Upload Windows arm64 E2E diagnostics
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: deepchat-win-arm64-e2e-artifacts
name: deepchat-win-arm64-e2e-diagnostics
if-no-files-found: warn
path: |
dist/*
!dist/win-unpacked
!dist/win-arm64-unpacked
build/windows-arm64-e2e-diagnostics
test-results/e2e
playwright-report
13 changes: 10 additions & 3 deletions docs/features/windows-arm64-support/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@

## Architecture

- Validate a packaged/unpacked ARM64 build with a Windows ARM64 manual workflow running on GitHub's `windows-11-arm` runner and Playwright Electron smoke tests.
- Validate Windows ARM64 on GitHub's `windows-11-arm` runner with Playwright smoke tests against the built Electron app plus a separate process smoke for the packaged executable.
- Extend the manual build workflow's Windows matrix to produce `win-x64` and `win-arm64` artifacts while keeping the release workflow on Windows x64 only.
- Keep the Windows ARM64 runtime script explicit: install only verified native `uv`, `node`, and `ripgrep` artifacts.
- Use a `sharp` version that publishes `@img/sharp-win32-arm64`, otherwise main-process image helpers fail during E2E bootstrap on Windows ARM64.
- Provide a CI-specific E2E mode that runs only non-provider smoke specs against the runner profile.
- Keep `_electron.launch()` for interactive E2E coverage because the packaged Windows executable does not reliably expose a Playwright-controllable debug endpoint in CI.
- Start the packaged Windows ARM64 executable separately and verify it remains alive for a short smoke window, with process output, app logs, native module inventory, and Windows event logs uploaded as diagnostics.
- Run packaged executable smoke only after interactive E2E succeeds, so startup failures keep the main-process logs focused on the failing launch.

## E2E Data Flow

1. The Playwright fixture launches DeepChat with the default Electron `userData` path for the current runner/user.
1. The Playwright fixture launches the built Electron app with the default Electron `userData` path for the current runner/user.
2. CI Playwright config matches only launch and settings-navigation smoke specs.
3. Chat, session persistence, and provider connectivity specs remain available for local/manual runs with configured providers.
4. The Playwright fixture attaches renderer diagnostics and `userData/logs` main-process logs to each test result.
5. The packaged executable smoke runs outside Playwright and writes stdout/stderr, Chromium logs, app logs, filesystem inventory, native module inventory, and Windows application events into the diagnostics artifact.

## Runtime Behavior

Expand All @@ -25,4 +31,5 @@
- Existing RTK fallback coverage remains in place.
- Skill runtime tests cover the no-UV/no-system-Python auto-runtime failure path.
- The manual build workflow validates Windows x64 and Windows ARM64 artifact generation.
- The new manual workflow validates Windows ARM64 build, plugin bundle, app launch, route switching, and settings navigation.
- The new manual workflow validates Windows ARM64 build, plugin bundle, packaged executable startup, app launch, route switching, and settings navigation.
- The Windows ARM64 E2E workflow uploads Playwright reports, traces, screenshots, app logs, native module inventory, Windows event logs, and process-smoke logs only.
3 changes: 2 additions & 1 deletion docs/features/windows-arm64-support/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ DeepChat maintainers need a reliable way to validate Windows ARM64 builds withou
- A manual GitHub Actions workflow runs on `windows-11-arm` and builds the Windows ARM64 app.
- The workflow runs E2E smoke tests that do not require configured provider credentials.
- The E2E run uses the runner's default profile and validates launch, routing, and settings window behavior.
- Packaged-app validation starts the unpacked Windows ARM64 executable and verifies it stays alive long enough for a process-level smoke check.
- The manual build workflow can produce both Windows x64 and Windows ARM64 artifacts.
- Windows ARM64 bundles only verified native runtimes: `uv`, `node`, and `ripgrep`.
- `rtk` is not bundled on Windows ARM64 until upstream provides a Windows ARM64 binary.
- Existing Windows x64, macOS, and Linux runtime install scripts remain strict.
- The Windows ARM64 workflow uploads build artifacts and E2E diagnostics.
- The Windows ARM64 E2E workflow uploads only diagnostics, not packaged build outputs.

## Non-Goals

Expand Down
7 changes: 6 additions & 1 deletion docs/features/windows-arm64-support/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
- [x] Wire `installRuntime:win:arm64` to explicit `uv`, `node`, and `ripgrep` installation.
- [x] Add CI E2E support for non-provider smoke tests.
- [x] Keep E2E on the default runner profile.
- [x] Add packaged-app E2E launch mode.
- [x] Add packaged executable process smoke.
- [x] Split interactive E2E from packaged executable process smoke.
- [x] Add Windows ARM64 manual GitHub Actions workflow.
- [x] Enable Windows ARM64 in the manual build workflow.
- [x] Limit Windows ARM64 E2E artifacts to diagnostics.
- [x] Upload app logs, event logs, and native module inventory for Windows ARM64 failures.
- [x] Attach main-process logs directly to E2E test results.
- [x] Upgrade `sharp` to a version with Windows ARM64 optional dependency support.
- [x] Add targeted unit coverage for runtime fallback paths.
- [ ] Enable Windows ARM64 in the release workflow after the manual workflow passes on GitHub.
93 changes: 93 additions & 0 deletions docs/issues/windows-arm64-duckdb-upgrade/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Windows ARM64 DuckDB Upgrade Plan

## Planning Summary

The safest first increment is to upgrade DuckDB to a Windows ARM64-capable release and add a narrow CI smoke check for DuckDB + `vss` before launching the Electron app.

This isolates two independent risks:

1. native binding availability on `win32-arm64`
2. `vss` extension install/load compatibility on the upgraded DuckDB release

## Current Repository Constraints

- DeepChat imports DuckDB through `src/main/presenter/knowledgePresenter/database/duckdbPresenter.ts`.
- The main-process startup path reaches built-in knowledge base code early enough that a missing native binding crashes the app before E2E can observe a window.
- DeepChat's DuckDB flow uses both online `INSTALL/LOAD vss` and an offline copied extension path through `scripts/installVss.js` and runtime extension loading.
- Project guidance requires keeping an SDD folder for active issue work and running `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` after implementation.

## Proposed Changes

### 1. Dependency Upgrade

Update `package.json` to `@duckdb/node-api@1.5.3-r.1` and refresh `pnpm-lock.yaml`.

Expected effect:

- pnpm resolves `@duckdb/node-bindings@1.5.3-r.1`
- pnpm resolves `@duckdb/node-bindings-win32-arm64@1.5.3-r.1`
- Windows ARM64 can load the native binding instead of failing during module initialization

### 2. Early Windows ARM64 Verification

Add a dedicated workflow step before build/E2E that runs a repository script to verify:

- `@duckdb/node-api` imports successfully
- `DuckDBInstance.create(':memory:')` works
- `INSTALL vss` succeeds
- `LOAD vss` succeeds
- the script prints the resolved DuckDB package version for diagnostics

This step should fail with a narrow error message instead of allowing the workflow to proceed to a generic Electron launch timeout.

### 3. Reuse Existing VSS Flow

Keep the existing DeepChat runtime logic unchanged unless the upgraded API breaks it:

- `scripts/installVss.js` still performs `INSTALL vss` and copies the installed extension into `runtime/duckdb/extensions`
- `duckdbPresenter.ts` still prefers the bundled extension and falls back to online `INSTALL/LOAD`

This keeps the change focused on compatibility validation rather than behavior redesign.

## Validation Strategy

### Local / Repository Validation

Run:

- a targeted DuckDB/VSS smoke command
- any focused tests touching the changed code or scripts
- `pnpm run format`
- `pnpm run i18n`
- `pnpm run lint`

### CI Validation

The Windows ARM64 workflow itself becomes part of the validation by:

1. installing dependencies for `win32-arm64`
2. running the dedicated DuckDB/VSS smoke check
3. only then building and launching the Electron app

## Risks And Mitigations

### Risk 1: `vss` fails on Windows ARM64 even after the binding upgrade

Mitigation:

- fail in the dedicated verification step with a precise error
- keep a fallback path available for a later issue to disable built-in knowledge base on Windows ARM64 if needed

### Risk 2: DuckDB package upgrade changes API behavior

Mitigation:

- keep changes minimal and avoid refactoring knowledge-base code in the first pass
- use the existing `DuckDBInstance`/`connect`/`run` usage pattern already supported by current code

### Risk 3: Offline extension installation path changes

Mitigation:

- validate `scripts/installVss.js` against the upgraded package
- prefer a dedicated smoke script that exercises the same `INSTALL vss` and `LOAD vss` sequence used by production code
Loading