diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 799883de..3bc1fd1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,8 @@ jobs: include: - os: macos runner: macos-latest + - os: windows + runner: windows-2025 steps: - name: Checkout diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 54f18923..6fce4cae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ on: jobs: build: - name: Build macOS (${{ matrix.arch }}) + name: Build ${{ matrix.os == 'macos' && 'macOS' || 'Windows' }} (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} strategy: matrix: @@ -25,6 +25,9 @@ jobs: - os: macos arch: x64 runner: macos-15-intel + - os: windows + arch: x64 + runner: windows-2025 steps: - name: Checkout code @@ -68,6 +71,13 @@ jobs: node-version: "24.1.0" cache: "pnpm" + - name: Install Vulkan SDK + if: matrix.os == 'windows' + uses: humbletim/install-vulkan-sdk@v1.2 + with: + version: 1.3.290.0 + cache: true + - name: Log Node.js architecture and platform run: | echo "=== Node.js Process Information ===" @@ -88,6 +98,16 @@ jobs: GGML_NATIVE: OFF # CI mac runners lack i8mm support; keep CPU features conservative here run: pnpm --filter @prismical/whisper-wrapper build:native + - name: Build whisper native binaries (vulkan) + if: matrix.os == 'windows' + env: + GGML_NATIVE: OFF + # whisper.cpp's vulkan-shaders-gen sub-cmake TryCompile pushes paths + # past Windows' 260-char MAX_PATH from D:\a\prismical\prismical\... . + # Build out-of-tree on a short path; build-addon.js honors this env. + WHISPER_BUILD_OUT_DIR: C:\b + run: pnpm --filter @prismical/whisper-wrapper build:native:vulkan + - name: Download Node.js binaries working-directory: apps/desktop run: pnpm download-node @@ -145,6 +165,40 @@ jobs: echo "Building unsigned macOS ${{ matrix.arch }} PR artifacts" pnpm make:${{ matrix.arch }} + - name: Build artifacts (Windows release) + if: matrix.os == 'windows' && github.event_name != 'pull_request' + working-directory: apps/desktop + env: + POSTHOG_HOST: https://app.posthog.com + TELEMETRY_ENABLED: true + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} + FEEDBACK_SURVEY_ID: ${{ secrets.FEEDBACK_SURVEY_ID }} + AUTH_CLIENT_ID: ${{ secrets.AUTH_CLIENT_ID }} + AUTHORIZATION_ENDPOINT: ${{ secrets.AUTHORIZATION_ENDPOINT }} + AUTH_TOKEN_ENDPOINT: ${{ secrets.AUTH_TOKEN_ENDPOINT }} + API_ENDPOINT: ${{ secrets.API_ENDPOINT }} + AUTH_REDIRECT_URI: ${{ secrets.AUTH_REDIRECT_URI }} + run: | + echo "Building unsigned Windows ${{ matrix.arch }} release artifacts" + pnpm make:windows + + - name: Build artifacts (Windows PR) + if: matrix.os == 'windows' && github.event_name == 'pull_request' + working-directory: apps/desktop + env: + TELEMETRY_ENABLED: "false" + POSTHOG_HOST: https://app.posthog.com + POSTHOG_API_KEY: "" + FEEDBACK_SURVEY_ID: "" + AUTH_CLIENT_ID: "" + AUTHORIZATION_ENDPOINT: "" + AUTH_TOKEN_ENDPOINT: "" + API_ENDPOINT: "" + AUTH_REDIRECT_URI: "" + run: | + echo "Building unsigned Windows ${{ matrix.arch }} PR artifacts" + pnpm make:windows + - name: Get version from package.json id: package_version working-directory: apps/desktop @@ -161,6 +215,16 @@ jobs: echo "Renamed $dmg → Prismical-macos-${{ matrix.arch }}.dmg" done + - name: Rename artifacts (Windows) + if: matrix.os == 'windows' + shell: bash + run: | + cd apps/desktop/out/make/squirrel.windows/${{ matrix.arch }} + for exe in *.exe; do + mv "$exe" "Prismical-windows-${{ matrix.arch }}.exe" + echo "Renamed $exe → Prismical-windows-${{ matrix.arch }}.exe" + done + - name: Upload artifacts (macOS) if: matrix.os == 'macos' uses: actions/upload-artifact@v4 @@ -170,10 +234,20 @@ jobs: apps/desktop/out/make/Prismical-macos-${{ matrix.arch }}.dmg apps/desktop/out/make/zip/darwin/${{ matrix.arch }}/*.zip + - name: Upload artifacts (Windows) + if: matrix.os == 'windows' + uses: actions/upload-artifact@v4 + with: + name: windows-${{ matrix.arch }} + path: | + apps/desktop/out/make/squirrel.windows/${{ matrix.arch }}/Prismical-windows-${{ matrix.arch }}.exe + apps/desktop/out/make/squirrel.windows/${{ matrix.arch }}/*.nupkg + apps/desktop/out/make/squirrel.windows/${{ matrix.arch }}/RELEASES + release: name: Create Release needs: build - if: github.event_name != 'pull_request' + if: github.event_name == 'workflow_dispatch' || github.ref_type == 'tag' runs-on: ubuntu-latest permissions: contents: write @@ -346,13 +420,19 @@ jobs: echo "- **Apple Silicon (M1/M2/M3)**: Download the DMG or ZIP file for arm64" echo "- **Intel**: Download the DMG or ZIP file for x64" echo "" + echo "#### Windows" + echo "- **Windows (x64)**: Download the .exe installer for 64-bit Windows" + echo "" echo "### Installation" echo "" echo "**macOS**:" echo "- **DMG**: Download and open the DMG file, then drag Prismical to your Applications folder" echo "- **ZIP**: Download and extract the ZIP file, then drag Prismical to your Applications folder" echo "" - echo "Windows builds are temporarily disabled." + echo "**Windows**:" + echo "- Download and run the .exe installer" + echo "- Follow the installation wizard" + echo "- The app will be installed to your user AppData folder and a shortcut will be created" echo "" echo "The ZIP files are primarily for automatic updates. We recommend using the DMG files for initial installation on macOS." } > release-notes.md @@ -459,10 +539,10 @@ jobs: - name: List artifacts run: | echo "=== Full artifacts directory structure ===" - find artifacts -type f \( -name "*.dmg" -o -name "*.zip" \) | sort + find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.nupkg" -o -name "RELEASES" \) | sort echo "" echo "=== Detailed file listing ===" - find artifacts -type f \( -name "*.dmg" -o -name "*.zip" \) -exec ls -la {} \; + find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.nupkg" -o -name "RELEASES" \) -exec ls -la {} \; - name: Create Release uses: softprops/action-gh-release@v2 @@ -479,5 +559,8 @@ jobs: artifacts/macos-arm64/zip/darwin/arm64/*.zip artifacts/macos-x64/*.dmg artifacts/macos-x64/zip/darwin/x64/*.zip + artifacts/windows-x64/*.exe + artifacts/windows-x64/*.nupkg + artifacts/windows-x64/RELEASES env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/apps/desktop/forge.config.ts b/apps/desktop/forge.config.ts index 9fd979f4..8987c1e0 100644 --- a/apps/desktop/forge.config.ts +++ b/apps/desktop/forge.config.ts @@ -413,6 +413,11 @@ const config: ForgeConfig = { icon: "./assets/logo", // Path to your icon file appBundleId: "com.prismical.desktop", // Proper bundle ID extraResource: [ + `${ + process.platform === "win32" + ? "../../packages/native-helpers/windows-helper/bin" + : "../../packages/native-helpers/swift-helper/bin" + }`, "./src/db/migrations", // Only include the platform-specific node binary `./node-binaries/${process.platform}-${process.arch}/node${ diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 8fb155c2..4ad9bfd0 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -40,11 +40,14 @@ "db:generate": "drizzle-kit generate", "db:push": "drizzle-kit push", "db:migrate": "drizzle-kit migrate", - "build:deps": "pnpm download-node && pnpm build:types && pnpm build:whisper && pnpm build:audio-capture && pnpm build:mic-detector", + "build:deps": "pnpm download-node && pnpm build:types && pnpm build:whisper && pnpm build:audio-capture && pnpm build:mic-detector && pnpm build:native-helper", "build:audio-capture": "pnpm --filter @prismical/audio-capture build", "build:mic-detector": "pnpm --filter @prismical/mic-detector build", "build:whisper": "pnpm --filter @prismical/whisper-wrapper build", "build:types": "pnpm --filter @prismical/types build", + "build:swift-helper": "pnpm --filter @prismical/swift-helper build", + "build:windows-helper": "pnpm --filter @prismical/windows-helper build", + "build:native-helper": "node -p \"process.platform === 'darwin' ? 'build:swift-helper' : process.platform === 'win32' ? 'build:windows-helper' : 'echo No native helpers'\" | xargs pnpm run", "dev:prepare": "pnpm download-node", "dev": "electron-forge start", "kill": "bash scripts/kill-dev.sh", diff --git a/packages/native-helpers/mic-detector/bin/prismical-mic-detector b/packages/native-helpers/mic-detector/bin/prismical-mic-detector index 90cf0bf2..82175d58 100755 Binary files a/packages/native-helpers/mic-detector/bin/prismical-mic-detector and b/packages/native-helpers/mic-detector/bin/prismical-mic-detector differ diff --git a/packages/whisper-wrapper/bin/build-addon.js b/packages/whisper-wrapper/bin/build-addon.js index a427b9f5..7a4e1bd2 100644 --- a/packages/whisper-wrapper/bin/build-addon.js +++ b/packages/whisper-wrapper/bin/build-addon.js @@ -40,8 +40,12 @@ if (!fs.existsSync(addonDir) || !hasRequiredWhisperSources) { process.exit(1); } -const buildDir = path.join(pkgDir, "build"); -if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir); +// WHISPER_BUILD_OUT_DIR lets CI place the cmake build tree on a short Windows +// path so vulkan-shaders-gen's sub-cmake TryCompile doesn't hit MAX_PATH (260). +const buildDir = process.env.WHISPER_BUILD_OUT_DIR + ? path.resolve(process.env.WHISPER_BUILD_OUT_DIR) + : path.join(pkgDir, "build"); +if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir, { recursive: true }); const cacheDir = path.join(pkgDir, ".cmake-js"); if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir);