diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 49d2acf..292dbff 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -11,9 +11,18 @@ name: Deploy SOLUTION_ERP +# Path filter — skip CI khi commit chỉ docs/MD/skill (~110s saved per docs commit). +# Commit MD-only như "Docs: chốt session" sẽ KHÔNG trigger workflow. +# Lưu ý: nếu cùng 1 commit thay đổi cả MD + code → vẫn trigger (đúng behavior). on: push: branches: [main] + paths-ignore: + - 'docs/**' + - '**/*.md' + - '.claude/skills/**' + - '.gitignore' + - 'scripts/**.md' workflow_dispatch: jobs: @@ -92,22 +101,114 @@ jobs: --runtime win-x64 ` --self-contained false - - name: Build fe-admin + # ============== FE BUILD WITH NPM CACHE ============== + # Cache `node_modules` ở `C:\npm-cache-erp\\\` ngoài workspace. + # Cache key = SHA256(package.json) 16-char prefix → đổi deps = fresh install. + # Junction từ workspace → cache (instant, không file copy). + # Lần đầu (cold cache): 49s + 33s. Lần sau (warm cache): 1-2s mỗi app. + # Vite 8 rolldown native binding match platform — install trên runner Windows + # (gotcha #20 từ Phase 5) → cache install này dùng được lần sau. + - name: Build fe-admin (with npm junction cache) shell: powershell - working-directory: fe-admin run: | - # Vite 8 rolldown native binding must match platform; fresh resolve on Windows - Remove-Item node_modules, package-lock.json -Recurse -Force -ErrorAction SilentlyContinue - & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund - & 'C:\Program Files\nodejs\npm.cmd' run build + $ErrorActionPreference = 'Stop' + $hash = (Get-FileHash 'fe-admin/package.json' -Algorithm SHA256).Hash.Substring(0, 16) + $cacheDir = "C:\npm-cache-erp\fe-admin\$hash" + $cacheNm = "$cacheDir\node_modules" + $nmTarget = 'fe-admin\node_modules' - - name: Build fe-user + # Clean junction/dir cũ trong workspace nếu còn (act_runner thường clean nhưng phòng hờ) + if (Test-Path $nmTarget) { cmd /c rmdir /s /q $nmTarget } + + if (Test-Path $cacheNm) { + Write-Host "✓ npm cache HIT fe-admin (key: $hash)" + cmd /c mklink /J $nmTarget $cacheNm | Out-Null + } else { + Write-Host "✗ npm cache MISS fe-admin (key: $hash) — fresh install" + Push-Location fe-admin + try { + Remove-Item package-lock.json -Force -ErrorAction SilentlyContinue + & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund + if ($LASTEXITCODE -ne 0) { throw "npm install failed: $LASTEXITCODE" } + } finally { Pop-Location } + New-Item -ItemType Directory -Force $cacheDir | Out-Null + Move-Item $nmTarget $cacheNm -Force + cmd /c mklink /J $nmTarget $cacheNm | Out-Null + } + + Push-Location fe-admin + try { + & 'C:\Program Files\nodejs\npm.cmd' run build + if ($LASTEXITCODE -ne 0) { throw "npm build failed: $LASTEXITCODE" } + } finally { Pop-Location } + + - name: Build fe-user (with npm junction cache) shell: powershell - working-directory: fe-user run: | - Remove-Item node_modules, package-lock.json -Recurse -Force -ErrorAction SilentlyContinue - & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund - & 'C:\Program Files\nodejs\npm.cmd' run build + $ErrorActionPreference = 'Stop' + $hash = (Get-FileHash 'fe-user/package.json' -Algorithm SHA256).Hash.Substring(0, 16) + $cacheDir = "C:\npm-cache-erp\fe-user\$hash" + $cacheNm = "$cacheDir\node_modules" + $nmTarget = 'fe-user\node_modules' + + if (Test-Path $nmTarget) { cmd /c rmdir /s /q $nmTarget } + + if (Test-Path $cacheNm) { + Write-Host "✓ npm cache HIT fe-user (key: $hash)" + cmd /c mklink /J $nmTarget $cacheNm | Out-Null + } else { + Write-Host "✗ npm cache MISS fe-user (key: $hash) — fresh install" + Push-Location fe-user + try { + Remove-Item package-lock.json -Force -ErrorAction SilentlyContinue + & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund + if ($LASTEXITCODE -ne 0) { throw "npm install failed: $LASTEXITCODE" } + } finally { Pop-Location } + New-Item -ItemType Directory -Force $cacheDir | Out-Null + Move-Item $nmTarget $cacheNm -Force + cmd /c mklink /J $nmTarget $cacheNm | Out-Null + } + + Push-Location fe-user + try { + & 'C:\Program Files\nodejs\npm.cmd' run build + if ($LASTEXITCODE -ne 0) { throw "npm build failed: $LASTEXITCODE" } + } finally { Pop-Location } + + # Safety: convert junction → nothing trước khi act_runner có thể cleanup workspace. + # `cmd /c rmdir /q` chỉ remove junction reference, KHÔNG recurse vào target. + # Tránh trường hợp Remove-Item -Recurse từ runner cleanup follow junction + + # delete cache contents. + - name: Remove FE node_modules junctions (preserve cache) + if: always() + shell: powershell + run: | + foreach ($app in @('fe-admin', 'fe-user')) { + $junction = "$app\node_modules" + if (Test-Path $junction) { + cmd /c rmdir /q $junction 2>$null + Write-Host "Removed junction: $junction" + } + } + + # Cache cleanup — keep top 5 most recent per app (FIFO eviction). + # Mỗi cache ~250MB. 5 versions × 2 app × 250MB = 2.5 GB max disk usage. + - name: Prune old npm caches + if: always() + shell: powershell + run: | + foreach ($app in @('fe-admin', 'fe-user')) { + $root = "C:\npm-cache-erp\$app" + if (Test-Path $root) { + $stale = Get-ChildItem $root -Directory | + Sort-Object LastWriteTime -Descending | + Select-Object -Skip 5 + foreach ($d in $stale) { + Write-Host "Pruning stale cache: $($d.FullName)" + Remove-Item $d.FullName -Recurse -Force -ErrorAction SilentlyContinue + } + } + } - name: Deploy to IIS (local) if: github.ref == 'refs/heads/main'