From 29eb5d99a07e011e4675d1e5c9c7a91f4f50d60a Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 29 Apr 2026 18:24:09 +0700 Subject: [PATCH] [CLAUDE] CICD: path filter docs-only + npm junction cache (Option C optimize ~2/3 deploy time) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Optimize CI/CD theo Option C bàn trong chat: ==== Path filter (saving 100% time cho commit MD-only) ==== on: push: paths-ignore mới — skip CI khi commit chỉ docs/skill/MD/gitignore. - 'docs/**' - '**/*.md' - '.claude/skills/**' - '.gitignore' - 'scripts/**.md' Commit 'Docs: chốt session' và similar sẽ KHÔNG trigger workflow → save 196s/commit. Nếu cùng commit thay đổi cả MD + code → vẫn trigger (đúng behavior expected). Workflow file `.gitea/workflows/**` chính NÓ thì không trong paths-ignore → vẫn trigger khi sửa CI config (an toàn). ==== npm junction cache (saving ~70-80s code commit) ==== Replace Build fe-admin + fe-user steps với cache-aware version. Strategy: - Cache key = SHA256(package.json) 16-char prefix → đổi deps = miss → fresh - Cache stored: C:\npm-cache-erp\\\node_modules (ngoài workspace) - Junction `fe-admin\node_modules → cache` (instant, không file copy) - Lần đầu (cold): 49s + 33s = 82s (như cũ) - Lần sau (warm): mklink instant + skip npm install → ~3s + 3s = 6s (saving ~76s) Safety: - Trước Deploy: convert junction → nothing (cmd /c rmdir /q chỉ remove ref, không follow target). Tránh trường hợp act_runner cleanup workspace follow junction + delete cache. - Pruning: keep top 5 cache per app (~250MB × 5 × 2 = 2.5GB max disk usage). Stale evicted FIFO theo LastWriteTime DESC. Vite 8 rolldown native binding gotcha (#20) vẫn respect: cache install trên runner Windows nên rolldown binding match → reuse được. ==== Expected ==== - Commit MD-only: 0s CI (skip hoàn toàn) - Commit code lần đầu sau cache miss (vd npm update): ~3min (như cũ) - Commit code thường (cache hit): ~120s = 2 phút (giảm 38%) Verify dotnet test local: 71 pass / 2s (BE không thay đổi). Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitea/workflows/deploy.yml | 123 ++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 11 deletions(-) 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'