`agent.mjs` 现在有两个消费方: 1. scripts/pack.mjs → Windows 便携包 / 自更新载荷(Release 资产) 2. scripts/prepare-console.mjs → 桌面控制台的随包资源(控制台要能托起本机被控端) 若各写一份 esbuild 配置就是「两套真相」——改了一处漏另一处时构建过、单测过, 只在真机产物里暴露(本项目已多次吃过这类亏)。故配置只此一份。 bundle-agent.mjs 导出 repoRoot / repoVersion / gitShortCommit / bundleAgent, 沿用原有全部约束:JS API(pnpm 隔离布局下 .bin/esbuild 在 Windows 不存在)、 不注入构建时间(否则每次打包字节不同,指纹比对失真)、 define 用 JSON.stringify 得纯双引号。 本次对 pack.mjs 的输出无语义变化(同一份 options 原样迁移)。
85 lines
3.4 KiB
JavaScript
85 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 被控端单文件打包(esbuild)—— **单一实现**。
|
||
*
|
||
* 为什么抽出来:`agent.mjs` 现在有**两个消费方** ——
|
||
* 1. `scripts/pack.mjs` → Windows 便携包 / 自更新载荷(Release 资产)
|
||
* 2. `scripts/prepare-console.mjs` → 桌面控制台的随包资源(控制台要能托起本机被控端)
|
||
*
|
||
* 若各写一份 esbuild 配置,迟早漂移(本项目已多次吃过「两份真相」的亏:改了一处漏另一处,
|
||
* 构建过、单测过,只在真机暴露)。故配置只此一份。
|
||
*
|
||
* ## 几个「不这么写就会错」的细节
|
||
* - **用 esbuild 的 JS API,不 spawn `node_modules/.bin/esbuild`** —— pnpm 隔离布局下
|
||
* 那个 shim 在 Windows 上不存在(真机 CI 踩过 `ENOENT`);JS API 只依赖 `require('esbuild')`。
|
||
* - **不注入构建时间** —— 注入后同一份代码每次打包字节都不同,「指纹是否最新」永远失真。
|
||
* - `define` 的值按 **JS 表达式**解析,故用 `JSON.stringify` 得到纯双引号;写成单引号包裹
|
||
* 会让引号成为值的一部分(真机踩过:版本号显示成 `v"1.4.0"`)。
|
||
*/
|
||
|
||
import { execFileSync } from 'node:child_process';
|
||
import { existsSync, readFileSync } from 'node:fs';
|
||
import { dirname, join, resolve } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
/** 仓库根目录(本文件在 scripts/lib/ 下)。 */
|
||
export const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||
|
||
/** 被控端与协议包的版本源:**根** `package.json`(非 apps/agent 的)。 */
|
||
export function repoVersion(root = repoRoot) {
|
||
return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
|
||
}
|
||
|
||
/** 当前 git 短 sha(取不到时返回 `unknown`,不抛)。 */
|
||
export function gitShortCommit(root = repoRoot) {
|
||
try {
|
||
return execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: root }).toString().trim();
|
||
} catch {
|
||
return 'unknown';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 把 `apps/agent/dist/index.js` 打包成自包含单文件。
|
||
*
|
||
* @param {object} opts
|
||
* @param {string} opts.outfile 产物路径
|
||
* @param {string} [opts.root] 仓库根
|
||
* @param {string} [opts.logLevel]
|
||
* @returns {Promise<{ outfile: string, version: string, commit: string, bytes: number }>}
|
||
*/
|
||
export async function bundleAgent({ outfile, root = repoRoot, logLevel = 'info' }) {
|
||
const entry = join(root, 'apps', 'agent', 'dist', 'index.js');
|
||
if (!existsSync(entry)) {
|
||
throw new Error(
|
||
`未找到被控端构建产物:${entry}\n` +
|
||
' 先构建:pnpm --filter nodeagent-agent build(或 pnpm -r build)',
|
||
);
|
||
}
|
||
|
||
const version = repoVersion(root);
|
||
const commit = gitShortCommit(root);
|
||
|
||
// 动态 import:只有真正需要打包时才解析 esbuild(prepare-console 的其它分支不受影响)
|
||
const esbuild = await import('esbuild');
|
||
await esbuild.build({
|
||
entryPoints: [entry],
|
||
bundle: true,
|
||
platform: 'node',
|
||
target: 'node22',
|
||
format: 'esm',
|
||
banner: {
|
||
js: 'import { createRequire } from "node:module"; const require = createRequire(import.meta.url);',
|
||
},
|
||
define: {
|
||
__AGENT_VERSION__: JSON.stringify(version),
|
||
__BUILD_COMMIT__: JSON.stringify(commit),
|
||
},
|
||
outfile,
|
||
logLevel,
|
||
});
|
||
|
||
const { statSync } = await import('node:fs');
|
||
return { outfile, version, commit, bytes: statSync(outfile).size };
|
||
}
|