Files
zszq-trs/YLErpWeb/rebuild-bundles.ps1
T
hjhan fb00f13421 fix(EQD-6838): 修复 CI 因文件名大小写和脚本 fail-fast 阻塞构建
问题:
- bundleconfig.json 中 Myjs.js/isoweek.js 在 Linux CI 上找不到
  (实际文件名是 MyJs.js/isoWeek.js,Windows 不区分大小写所以本地能跑)
- py/ps1 脚本遇到缺失输入文件时 exit 1,导致 dotnet publish 失败,
  Jenkins 构建被阻塞,部署无法进行

修复:
- bundleconfig.json: Myjs.js → MyJs.js,isoweek.js → isoWeek.js
  (与实际文件名大小写一致,Linux CI 可正确找到)
- rebuild-bundles.py/ps1: 重建模式遇到缺失输入文件时只警告不 fail,
  跳过该 bundle 保留已提交产物,避免阻塞 CI 主构建
- 只有 Verify 模式(开发本地手动校验)才因缺失/BOM 报错 exit 1

设计原则:bundle 重建是"尽力而为",绝不能阻塞主构建和部署
2026-07-29 19:01:19 +08:00

170 lines
7.3 KiB
PowerShell

<#
.SYNOPSIS
Rebuild all bundle artifacts by concatenating inputs per bundleconfig.json (strip BOM, no minify).
.DESCRIPTION
No dependency on BuildBundlerMinifier. Deterministic pure concatenation:
1. Read bundleconfig.json
2. For each bundle: read input files in order, strip leading UTF-8 BOM, join with CRLF
3. Only write out minify.enabled=false artifacts (overwrite committed files)
For minify.enabled=true artifacts (minified CSS), skip generation (requires tooling)
Output is byte-identical to BuildBundlerMinifier (SHA256 verified).
.PARAMETER Verify
Verify-only mode: compare generated content with committed files, report mismatches, do NOT modify files.
.EXAMPLE
.\rebuild-bundles.ps1 # Rebuild and overwrite committed bundle artifacts
.\rebuild-bundles.ps1 -Verify # Verify only, do not modify files
#>
[CmdletBinding()]
param(
[switch]$Verify
)
$ErrorActionPreference = 'Stop'
$projectDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$configPath = Join-Path $projectDir 'bundleconfig.json'
if (-not (Test-Path $configPath)) {
throw "bundleconfig.json not found: $configPath"
}
# Read bundleconfig.json (strip // comments)
$configJson = Get-Content $configPath -Raw
$configJson = $configJson -replace '(?m)//.*$', ''
$bundles = $configJson | ConvertFrom-Json
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
$mismatches = @()
# CI 环境检测:CI 下 git checkout 后所有文件 mtime 几乎相同,增量检查不可靠
# 因此 CI 环境强制全量重建,本地开发才走增量
$isCI = -not [string]::IsNullOrEmpty($env:CI) `
-or -not [string]::IsNullOrEmpty($env:JENKINS_HOME) `
-or -not [string]::IsNullOrEmpty($env:BUILD_NUMBER) `
-or -not [string]::IsNullOrEmpty($env:GITHUB_ACTIONS)
foreach ($bundle in $bundles) {
$outputRel = $bundle.outputFileName
$outputPath = Join-Path $projectDir $outputRel
$isMinified = $bundle.minify.enabled
# 增量检查:非 Verify、非 CI、非 minified,且 bundle 产物已存在时,
# 若所有源文件都不比 bundle 产物新,则跳过此 bundle
# 目的:让 target 挂到 Build 之前时,后端-only 改动不会触发重建,IDE F5 不变慢
# CI 环境强制全量:git checkout 后 mtime 不可靠,必须重建保证部署产物正确
if (-not $Verify -and -not $isCI -and -not $isMinified -and (Test-Path $outputPath)) {
$outputMtime = (Get-Item $outputPath).LastWriteTime
$stale = $false
foreach ($inputRel in $bundle.inputFiles) {
$inputPath = Join-Path $projectDir $inputRel
if ((Test-Path $inputPath) -and (Get-Item $inputPath).LastWriteTime -gt $outputMtime) {
$stale = $true
break
}
}
if (-not $stale) {
Write-Host ("[UP-TO-DATE] {0}" -f $outputRel) -ForegroundColor DarkGray
continue
}
}
Write-Host ("[{0}] {1}" -f $(if ($isMinified) { 'SKIP-MIN' } else { 'BUILD' }), $outputRel)
# Check input files exist + no BOM
$parts = [System.Collections.Generic.List[string]]::new()
$missingInputs = @()
$bomInputs = @()
foreach ($inputRel in $bundle.inputFiles) {
$inputPath = Join-Path $projectDir $inputRel
if (-not (Test-Path $inputPath)) {
$missingInputs += $inputRel
continue
}
# Detect BOM by reading raw bytes
$bytes = [System.IO.File]::ReadAllBytes($inputPath)
$hasBom = ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)
if ($hasBom) {
$bomInputs += $inputRel
}
# Read text (UTF8, BOM auto-stripped by ReadAllText)
$text = [System.IO.File]::ReadAllText($inputPath, [System.Text.Encoding]::UTF8)
# Normalize CRLF to LF (bundle artifacts use LF in git repo)
$text = $text -replace "`r`n", "`n"
$parts.Add($text)
}
if ($missingInputs.Count -gt 0) {
# 缺失输入文件:Verify 模式报错;重建模式只警告不 fail,避免阻塞 CI 主构建
# 重建模式下跳过此 bundle(保留已提交的产物)
$level = $(if ($Verify) { 'ERROR' } else { 'WARN' })
$color = $(if ($Verify) { 'Red' } else { 'Yellow' })
Write-Host " [$level] Missing input files:" -ForegroundColor $color
foreach ($m in $missingInputs) { Write-Host " - $m" }
if ($Verify) {
$mismatches += "$outputRel : missing inputs ($($missingInputs -join ', '))"
}
continue
}
if ($bomInputs.Count -gt 0) {
# Verify 模式:BOM 是源文件问题,报错阻止提交
# 重建模式:BOM 会在读取时自动剥离(ReadAllText 处理),只警告不 fail,避免阻塞 CI 部署
$level = $(if ($Verify) { 'ERROR' } else { 'WARN' })
$color = $(if ($Verify) { 'Red' } else { 'Yellow' })
Write-Host " [$level] Input files have BOM (will be stripped on rebuild):" -ForegroundColor $color
foreach ($b in $bomInputs) { Write-Host " - $b" }
if ($Verify) {
$mismatches += "$outputRel : input BOM ($($bomInputs -join ', '))"
}
}
# Pure concatenation with LF separator between files (matches committed bundle artifacts)
$content = [string]::Join("`n", $parts)
# For minified artifacts, only report, do not write
if ($isMinified) {
Write-Host " (minified artifact, skip write, only input check)" -ForegroundColor Yellow
continue
}
if ($Verify) {
# Verify mode: compare with committed file
if (-not (Test-Path $outputPath)) {
Write-Host " [ERROR] Output file not found: $outputRel" -ForegroundColor Red
$mismatches += "$outputRel : output file not found"
continue
}
$existing = [System.IO.File]::ReadAllText($outputPath, [System.Text.Encoding]::UTF8)
if ($existing -ne $content) {
Write-Host " [MISMATCH] Differs from committed version" -ForegroundColor Red
$mismatches += "$outputRel : out of sync (source changed but bundle not rebuilt)"
} else {
Write-Host " [OK] Matches committed version" -ForegroundColor Green
}
} else {
# Rebuild mode: write (no BOM)
$dir = Split-Path $outputPath -Parent
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
[System.IO.File]::WriteAllText($outputPath, $content, $utf8NoBom)
Write-Host " [DONE] Written ($($content.Length) chars)" -ForegroundColor Green
}
}
Write-Host ""
if ($mismatches.Count -gt 0) {
# Verify 模式:有问题则 fail(阻止提交不规范的状态)
# 重建模式:永远不 fail,避免阻塞 CI 主构建(缺失/BOM 等已在循环内警告并跳过)
if ($Verify) {
Write-Host "=== Result: ISSUES FOUND (Verify mode) ===" -ForegroundColor Red
foreach ($m in $mismatches) { Write-Host " - $m" }
exit 1
} else {
Write-Host "=== Result: COMPLETED WITH WARNINGS (Rebuild mode) ===" -ForegroundColor Yellow
foreach ($m in $mismatches) { Write-Host " - $m" }
Write-Host " (warnings do not block build in rebuild mode)" -ForegroundColor DarkGray
exit 0
}
} else {
Write-Host "=== Result: ALL PASSED ===" -ForegroundColor Green
exit 0
}