From 4e1f1e959a3d3fc86381b21aea56c05dc482cde7 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Fri, 8 May 2026 09:08:46 +0000 Subject: [PATCH] ci(windows): do not pre-create dist/node_modules before Defender exclusion Run 25546676715 / job 74984469728 (Windows Studio UI CI / Chat UI Tests) broke on the previous commit (2843e2a9). Symptom: install.log: "frontend up to date" studio.log: FileNotFoundError: D:\\a\\unsloth\\unsloth\\studio\\frontend\\dist\\index.html Playwright: TimeoutError waiting for "#new-password" (60s) Root cause: the Pre-install Windows tweaks step's loop did if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p } Add-MpPreference -ExclusionPath $p before install.ps1 ran. That created an empty studio/frontend/dist directory whose mtime was newer than every source file. setup.ps1's mtime-based "is the frontend stale?" check at studio/setup.ps1 line 1281-1296 then concluded "frontend up to date, skip rebuild", so vite never wrote anything into dist. Studio booted with an empty dist directory and crashed on GET /change-password (the static-file handler at studio/backend/main.py:489 read_bytes()'d a non-existent index.html). The same trap broke the frontend-dist actions/cache attempt earlier in this branch (commit d65f8b19 -> reverted in e1345d5f). Same root cause: any process that puts a fresh-mtime directory at studio/frontend/dist before the build silences the Vite rebuild. Fix: drop the New-Item call. Add-MpPreference accepts paths that do not yet exist; the exclusion is registered and applies when the path materialises. The failure is bisected to this single line, and reverting just that line restores green. Applied identically to all 4 Windows workflows so api/ui/update/inference jobs all stay green. --- .../workflows/studio-windows-api-smoke.yml | 7 ++++++- .../studio-windows-inference-smoke.yml | 21 ++++++++++++++++--- .github/workflows/studio-windows-ui-smoke.yml | 7 ++++++- .../workflows/studio-windows-update-smoke.yml | 12 ++++++++++- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/.github/workflows/studio-windows-api-smoke.yml b/.github/workflows/studio-windows-api-smoke.yml index 429627cc7d..c8c2192b2e 100644 --- a/.github/workflows/studio-windows-api-smoke.yml +++ b/.github/workflows/studio-windows-api-smoke.yml @@ -89,6 +89,12 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories. See + # studio-windows-update-smoke.yml for the full rationale -- + # creating an empty studio/frontend/dist trips setup.ps1's + # mtime-based staleness check into "frontend up to date, skip + # rebuild" and Studio boots with an empty dist directory. + # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -96,7 +102,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch { diff --git a/.github/workflows/studio-windows-inference-smoke.yml b/.github/workflows/studio-windows-inference-smoke.yml index e81cf2944c..9a2fb46e62 100644 --- a/.github/workflows/studio-windows-inference-smoke.yml +++ b/.github/workflows/studio-windows-inference-smoke.yml @@ -99,6 +99,12 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories. See + # studio-windows-update-smoke.yml for the full rationale -- + # creating an empty studio/frontend/dist trips setup.ps1's + # mtime-based staleness check into "frontend up to date, skip + # rebuild" and Studio boots with an empty dist directory. + # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -106,7 +112,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch { @@ -390,6 +395,12 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories. See + # studio-windows-update-smoke.yml for the full rationale -- + # creating an empty studio/frontend/dist trips setup.ps1's + # mtime-based staleness check into "frontend up to date, skip + # rebuild" and Studio boots with an empty dist directory. + # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -397,7 +408,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch { @@ -763,6 +773,12 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories. See + # studio-windows-update-smoke.yml for the full rationale -- + # creating an empty studio/frontend/dist trips setup.ps1's + # mtime-based staleness check into "frontend up to date, skip + # rebuild" and Studio boots with an empty dist directory. + # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -770,7 +786,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch { diff --git a/.github/workflows/studio-windows-ui-smoke.yml b/.github/workflows/studio-windows-ui-smoke.yml index fc62b2c5a8..1858cc7f8f 100644 --- a/.github/workflows/studio-windows-ui-smoke.yml +++ b/.github/workflows/studio-windows-ui-smoke.yml @@ -98,6 +98,12 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories. See + # studio-windows-update-smoke.yml for the full rationale -- + # creating an empty studio/frontend/dist trips setup.ps1's + # mtime-based staleness check into "frontend up to date, skip + # rebuild" and Studio boots with an empty dist directory. + # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -105,7 +111,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch { diff --git a/.github/workflows/studio-windows-update-smoke.yml b/.github/workflows/studio-windows-update-smoke.yml index 209c7a540b..d06ea2a760 100644 --- a/.github/workflows/studio-windows-update-smoke.yml +++ b/.github/workflows/studio-windows-update-smoke.yml @@ -103,6 +103,17 @@ jobs: Write-Host "npm version before upgrade: $(npm -v)" npm install -g 'npm@^11' 2>&1 | Out-Host Write-Host "npm version after upgrade: $(npm -v)" + # NOTE: do NOT pre-create these directories before adding the + # exclusion -- creating an empty studio/frontend/dist trips + # setup.ps1 line 1281-1296's mtime-based "is the frontend + # stale?" check into "up to date, skip rebuild", because the + # newly-created dist's mtime is younger than every source + # file. Studio then boots with an empty dist and 500s on + # GET / with FileNotFoundError: dist\index.html. See run + # 25546676715 / job 74984469728. + # Add-MpPreference accepts paths that do not yet exist; the + # exclusion is registered and applies when the path + # materialises. foreach ($p in @( "$env:USERPROFILE\.unsloth", "$env:USERPROFILE\AppData\Local\uv", @@ -110,7 +121,6 @@ jobs: "$env:GITHUB_WORKSPACE\studio\frontend\dist" )) { try { - if (-not (Test-Path $p)) { New-Item -ItemType Directory -Force -Path $p | Out-Null } Add-MpPreference -ExclusionPath $p -ErrorAction Stop Write-Host "Defender exclusion added: $p" } catch {