From fcb48fe54489f543800c5fa36c28d69fa5d9dab3 Mon Sep 17 00:00:00 2001 From: kiranmagic7 <262980978+kiranmagic7@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:25:28 +0530 Subject: [PATCH 1/3] fix(desktop): run Vite directly in Tauri dev command Signed-off-by: kiranmagic7 <262980978+kiranmagic7@users.noreply.github.com> --- desktop/src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 05dc5553397..ef93310f5fb 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -5,7 +5,7 @@ "identifier": "xyz.block.buzz.app", "build": { "beforeDevCommand": { - "script": "exec ./node_modules/.bin/vite", + "script": "node ./node_modules/vite/bin/vite.js", "cwd": "..", "wait": false }, From 637c0d4618342b1a2aa91762cde15846dfff9f30 Mon Sep 17 00:00:00 2001 From: sumit-m <33051892+sumit-m@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:15:44 +0530 Subject: [PATCH 2/3] fix(dev): launch Vite through its Node entry point on Windows Tauri runs beforeDevCommand through cmd /C on Windows, which has no exec and cannot execute the extensionless .bin/vite shim, so just dev fails before the app starts. Vite's Node entry point is portable; exec is kept elsewhere so Tauri's Ctrl+C still reaches Vite directly. Complements #3515, which fixes the same launch for tauri.conf.json but not for the instance-env.sh path that just dev uses. Signed-off-by: sumit-m <33051892+sumit-m@users.noreply.github.com> --- scripts/instance-env.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/instance-env.sh b/scripts/instance-env.sh index bc185d98f18..38f644182fd 100755 --- a/scripts/instance-env.sh +++ b/scripts/instance-env.sh @@ -25,7 +25,16 @@ if [[ "${BUZZ_RESET_WEBVIEW_STATE:-0}" == "1" ]]; then DEV_URL="${DEV_URL}?resetDevState=1" fi -BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"exec ./node_modules/.bin/vite --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev\",\"productName\":\"Buzz Dev\"}" +# Tauri runs this through `cmd /C` on Windows, which has no `exec` and cannot +# execute the extensionless `.bin/vite` shim. Vite's Node entry point is +# portable; `exec` still hands the shell's process slot to Vite elsewhere so +# Tauri's Ctrl+C reaches it directly. +case "${BUZZ_TEST_PLATFORM:-$(uname -s)}" in + MINGW*|MSYS*|CYGWIN*) VITE_LAUNCH="node ./node_modules/vite/bin/vite.js" ;; + *) VITE_LAUNCH="exec node ./node_modules/vite/bin/vite.js" ;; +esac + +BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev\",\"productName\":\"Buzz Dev\"}" unset VITE_DEV_BRANCH # In worktrees, extract a label from the branch name and derive a unique app @@ -89,7 +98,7 @@ if git rev-parse --is-inside-work-tree &>/dev/null; then if swift "$GENERATE_DEV_ICON" "$BASE_ICON" "$DEV_ICON" "$BUZZ_WORKTREE_LABEL"; then echo "🌳 Worktree: ${BUZZ_WORKTREE_LABEL}" export VITE_DEV_BRANCH="$BUZZ_WORKTREE_LABEL" - BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"exec ./node_modules/.bin/vite --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev.${BUZZ_INSTANCE_SLUG}\",\"productName\":\"Buzz Dev (${BUZZ_WORKTREE_LABEL})\",\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}" + BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev.${BUZZ_INSTANCE_SLUG}\",\"productName\":\"Buzz Dev (${BUZZ_WORKTREE_LABEL})\",\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}" fi fi fi From 42238faeef7ce52ab1a1b4c291b474606a74a738 Mon Sep 17 00:00:00 2001 From: Aria-iu <2357648739@qq.com> Date: Tue, 1 Sep 2026 04:14:21 -0500 Subject: [PATCH 3/3] fix(dev): resolve Vite through pnpm across platforms Use pnpm's project-local executable resolution instead of coupling the Tauri dev command to Vite's internal JavaScript entry point. Preserve the POSIX exec handoff outside Windows, skip the macOS icon generator elsewhere, and cover both generated command variants with a CI contract test. Builds on the platform handling from block/buzz#3515 and block/buzz#3534. Signed-off-by: Aria-iu <2357648739@qq.com> --- .github/workflows/ci.yml | 2 + desktop/src-tauri/tauri.conf.json | 2 +- scripts/instance-env.sh | 16 ++-- scripts/test-desktop-dev-command-contract.sh | 88 ++++++++++++++++++++ 4 files changed, 99 insertions(+), 9 deletions(-) create mode 100755 scripts/test-desktop-dev-command-contract.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 004051f5b19..8dcf826595d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,8 @@ jobs: scripts/test-mobile-release-candidate-publisher.sh - name: Mobile worktree identity contract run: scripts/test-mobile-worktree-overrides.sh + - name: Desktop dev command contract + run: scripts/test-desktop-dev-command-contract.sh - name: Codex security review contract run: just security-review-check - name: Rust cache contract diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index ef93310f5fb..fb1c42a8ac4 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -5,7 +5,7 @@ "identifier": "xyz.block.buzz.app", "build": { "beforeDevCommand": { - "script": "node ./node_modules/vite/bin/vite.js", + "script": "pnpm exec vite", "cwd": "..", "wait": false }, diff --git a/scripts/instance-env.sh b/scripts/instance-env.sh index 38f644182fd..f67b2b0791c 100755 --- a/scripts/instance-env.sh +++ b/scripts/instance-env.sh @@ -26,15 +26,15 @@ if [[ "${BUZZ_RESET_WEBVIEW_STATE:-0}" == "1" ]]; then fi # Tauri runs this through `cmd /C` on Windows, which has no `exec` and cannot -# execute the extensionless `.bin/vite` shim. Vite's Node entry point is -# portable; `exec` still hands the shell's process slot to Vite elsewhere so -# Tauri's Ctrl+C reaches it directly. +# execute the extensionless `.bin/vite` shim. `pnpm exec` resolves the local +# Vite binary on every platform; elsewhere `exec` still hands the shell's +# process slot to pnpm so Tauri's Ctrl+C reaches the launched command. case "${BUZZ_TEST_PLATFORM:-$(uname -s)}" in - MINGW*|MSYS*|CYGWIN*) VITE_LAUNCH="node ./node_modules/vite/bin/vite.js" ;; - *) VITE_LAUNCH="exec node ./node_modules/vite/bin/vite.js" ;; + MINGW*|MSYS*|CYGWIN*) VITE_LAUNCH="pnpm exec vite" ;; + *) VITE_LAUNCH="exec pnpm exec vite" ;; esac -BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev\",\"productName\":\"Buzz Dev\"}" +BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":{\"script\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\",\"cwd\":\"..\",\"wait\":false}},\"identifier\":\"xyz.block.buzz.app.dev\",\"productName\":\"Buzz Dev\"}" unset VITE_DEV_BRANCH # In worktrees, extract a label from the branch name and derive a unique app @@ -95,10 +95,10 @@ if git rev-parse --is-inside-work-tree &>/dev/null; then GENERATE_DEV_ICON="$WORKTREE_ROOT/scripts/generate-dev-icon.swift" BASE_ICON="$WORKTREE_ROOT/desktop/src-tauri/icons/icon.icns" - if swift "$GENERATE_DEV_ICON" "$BASE_ICON" "$DEV_ICON" "$BUZZ_WORKTREE_LABEL"; then + if [[ "$(uname -s)" == Darwin ]] && command -v swift &>/dev/null && swift "$GENERATE_DEV_ICON" "$BASE_ICON" "$DEV_ICON" "$BUZZ_WORKTREE_LABEL"; then echo "🌳 Worktree: ${BUZZ_WORKTREE_LABEL}" export VITE_DEV_BRANCH="$BUZZ_WORKTREE_LABEL" - BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev.${BUZZ_INSTANCE_SLUG}\",\"productName\":\"Buzz Dev (${BUZZ_WORKTREE_LABEL})\",\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}" + BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":{\"script\":\"${VITE_LAUNCH} --port ${BUZZ_VITE_PORT} --strictPort\",\"cwd\":\"..\",\"wait\":false}},\"identifier\":\"xyz.block.buzz.app.dev.${BUZZ_INSTANCE_SLUG}\",\"productName\":\"Buzz Dev (${BUZZ_WORKTREE_LABEL})\",\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}" fi fi fi diff --git a/scripts/test-desktop-dev-command-contract.sh b/scripts/test-desktop-dev-command-contract.sh new file mode 100755 index 00000000000..fa5922b523b --- /dev/null +++ b/scripts/test-desktop-dev-command-contract.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +assert_dev_command() { + node -e ' + const config = JSON.parse(process.argv[1]); + const expectedScript = process.argv[2]; + const command = config.build.beforeDevCommand; + if ( + typeof command !== "object" || + command.script !== expectedScript || + command.cwd !== ".." || + command.wait !== false + ) { + console.error("unexpected beforeDevCommand:", JSON.stringify(command)); + process.exit(1); + } + ' "$1" "$2" +} + +config_for_platform() { + local platform=$1 + ( + cd "$repo_root/desktop" + export BUZZ_TEST_PLATFORM="$platform" + source ../scripts/instance-env.sh >/dev/null 2>&1 + printf '%s' "$BUZZ_TAURI_CONFIG" + ) +} + +port_from_json() { + node -e ' + const config = JSON.parse(process.argv[1]); + process.stdout.write(new URL(config.build.devUrl).port); + ' "$1" +} + +base_config=$(cat "$repo_root/desktop/src-tauri/tauri.conf.json") +assert_dev_command "$base_config" "pnpm exec vite" + +windows_config=$(config_for_platform MINGW64_NT-10.0) +windows_port=$(port_from_json "$windows_config") +assert_dev_command "$windows_config" "pnpm exec vite --port ${windows_port} --strictPort" + +unix_config=$(config_for_platform Darwin) +unix_port=$(port_from_json "$unix_config") +assert_dev_command "$unix_config" "exec pnpm exec vite --port ${unix_port} --strictPort" + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT +mkdir -p "$tmp/bin" +cat > "$tmp/bin/git" <<'MOCK_GIT' +#!/usr/bin/env bash +case "$*" in + "rev-parse --show-toplevel") printf '%s\n' "$TEST_REPO_ROOT" ;; + "rev-parse --is-inside-work-tree") printf '%s\n' true ;; + "rev-parse --git-dir") printf '%s\n' "$TEST_REPO_ROOT/.git/worktrees/windows-test" ;; + "rev-parse --git-common-dir") printf '%s\n' "$TEST_REPO_ROOT/.git" ;; + "rev-parse --abbrev-ref HEAD") printf '%s\n' feature/windows-test ;; + *) exit 1 ;; +esac +MOCK_GIT +cat > "$tmp/bin/uname" <<'MOCK_UNAME' +#!/usr/bin/env bash +printf '%s\n' MINGW64_NT-10.0 +MOCK_UNAME +cat > "$tmp/bin/swift" <<'MOCK_SWIFT' +#!/usr/bin/env bash +touch "$SWIFT_CALLED_MARKER" +exit 1 +MOCK_SWIFT +chmod +x "$tmp/bin/git" "$tmp/bin/uname" "$tmp/bin/swift" + +export TEST_REPO_ROOT="$repo_root" +export SWIFT_CALLED_MARKER="$tmp/swift-called" +( + export PATH="$tmp/bin:$PATH" + cd "$repo_root/desktop" + source ../scripts/instance-env.sh >/dev/null 2>&1 +) +if [[ -e "$SWIFT_CALLED_MARKER" ]]; then + echo "expected Windows desktop setup to skip the macOS Swift icon generator" >&2 + exit 1 +fi + +echo "desktop dev command contract test passed"