问题: - 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 重建是"尽力而为",绝不能阻塞主构建和部署
170 lines
7.3 KiB
PowerShell
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
|
|
}
|