feat(build): astro 构建管线与媒体落盘
Plan 4 Task 5:build.service snapshotting 后接真实构建——building 态进 pipeline.ts(spawn `pnpm --filter @sf/theme-pilot-minimal exec astro build` (SF_BUILD_INPUT env;非零退出+stderr 标记判据)→ dist 定位与 target_file 完整性 → 媒体 copyPath→dist/public_path(sha256 全量校验)→ sitemap.xml/ robots.txt(编码 URL 原样)→ route-manifest.json(§12.1 字段面、哈希链复用) → build_artifacts dist/manifest 登记)→ validating(空,Task 6) → 报告+report 行 → succeeded;失败任何一步 failed+error_code/error_summary(stderr 摘要) +input/dist 清残。 dist 双位置纪律:主题包 dist 是暂存,产物汇齐后拷出 <artifactsRoot>/jobs/ <jobId>/dist(job 级权威),暂存随即清空。无网证明:ASTRO_TELEMETRY_DISABLED=1 (实测 @astrojs/telemetry 构建期真调 fetch)+ NODE_OPTIONS 透传(测试注入 fetch/net stub 构建仍成功)。主题集成补面:ProductList 模板 + /products/ 主列表分派特判(DB 物化形状 targetContentId=null,Task 4 夹具曾被占位绕过)。 测试:tests/integration/build-pipeline.test.ts 五用例(全链/夹具直跑/五禁 断言/失败路径);build-snapshot 状态机补 building + 清场补 build_artifacts。 root 359 + theme 26 全绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -16,6 +16,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import { Command } from 'commander'
|
||||
import { renderReportHtml, runWxrAdapterReport, type WxrAdapterReport } from '@sf/adapter-wp'
|
||||
import { getDb } from '../apps/api/src/db/client.js'
|
||||
import { PipelineError } from '../apps/api/src/modules/build/pipeline.js'
|
||||
import { BuildError, createBuildJob } from '../apps/api/src/modules/build/build.service.js'
|
||||
import {
|
||||
ImportError,
|
||||
@@ -301,7 +302,7 @@ program
|
||||
|
||||
program
|
||||
.command('build:create')
|
||||
.description('冻结 Render Snapshot:物化六文件(site/content/taxonomies/routes/assets/render-manifest)+ build_input_snapshots 行;dist 产物是 Task 5')
|
||||
.description('冻结 Render Snapshot 并构建:物化六文件 + build_input_snapshots 行 → astro 构建(媒体落盘/sitemap/robots/route-manifest)→ dist 产物 + build_artifacts 登记')
|
||||
.requiredOption('--site <key>', '站点 key')
|
||||
.option('--key <key>', '幂等键(缺省 = site+published 集合哈希+主题版本 自动派生;重复提交同 key 复用既有 job 不重跑)')
|
||||
.option('--artifacts <dir>', '产物根目录(缺省 repoRoot/artifacts)')
|
||||
@@ -335,6 +336,12 @@ program
|
||||
` hashes: content=${result.sourceHashes.content.slice(0, 12)}… route=${result.sourceHashes.route.slice(0, 12)}… asset=${result.sourceHashes.asset.slice(0, 12)}…`,
|
||||
)
|
||||
console.log(` report: ${result.reportPath}`)
|
||||
if (result.artifacts !== undefined) {
|
||||
console.log(
|
||||
` dist: ${result.artifacts.distPath}(${result.artifacts.distFileCount} 文件 sha256=${result.artifacts.distSha256.slice(0, 12)}…)`,
|
||||
)
|
||||
console.log(` route-manifest: ${result.artifacts.routeManifestPath}`)
|
||||
}
|
||||
console.log(` total ${result.durationMs.toFixed(1)}ms`)
|
||||
if (result.status === 'failed') process.exitCode = 1
|
||||
} finally {
|
||||
@@ -343,7 +350,7 @@ program
|
||||
})
|
||||
|
||||
// parseAsync:action 内抛错走此 catch(commander 默认会 process.exit(1) 并打印堆栈,
|
||||
// 这里收敛为单行错误信息 + exitCode——SourceRegisterError/ImportError/PublishError/BuildError 打 code,其余原样呈现)
|
||||
// 这里收敛为单行错误信息 + exitCode——SourceRegisterError/ImportError/PublishError/BuildError/PipelineError 打 code,其余原样呈现)
|
||||
program.parseAsync(process.argv).catch((err: unknown) => {
|
||||
if (err instanceof SourceRegisterError) {
|
||||
console.error(`[sf] ${err.code}: ${err.message}`)
|
||||
@@ -356,6 +363,9 @@ program.parseAsync(process.argv).catch((err: unknown) => {
|
||||
console.error(`[sf] ${err.code}: ${err.message}`)
|
||||
} else if (err instanceof BuildError) {
|
||||
console.error(`[sf] ${err.code}: ${err.message}`)
|
||||
} else if (err instanceof PipelineError) {
|
||||
// 构建管线阶段失败(spawn/媒体/manifest)——code + stderr 摘要(job 行已有 error 面)
|
||||
console.error(`[sf] ${err.code}: ${err.message}`)
|
||||
} else {
|
||||
console.error(`[sf] ${err instanceof Error ? err.message : String(err)}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user