perf(windows): isolate packaging temporary files

This commit is contained in:
winewill committed 2026-10-20 21:10:29 +08:00
1 parent 9ee94429b9
commit 122da6fcff
8 files changed
+47 -11

No files matched your search

+5
View File
@@ -171,7 +171,12 @@ jobs:
working-directory: ${{ steps.build_workspace.outputs.directory }}
env:
PACKAGE_VERSION: ${{ steps.settings.outputs.version }}
PACKAGE_BUILD_TEMP: ${{ steps.build_workspace.outputs.temporaryDirectory }}
run: |
if ($env:PACKAGE_BUILD_TEMP) {
$env:TEMP = $env:PACKAGE_BUILD_TEMP
$env:TMP = $env:PACKAGE_BUILD_TEMP
}
$packageArgs = @('run', 'package:desktop:win:x64', '--build-version', $env:PACKAGE_VERSION)
if ($env:PACKAGE_SIGNING -eq 'unsigned') { $packageArgs += '--unsigned' }
pnpm @packageArgs
+2 -2
View File
@@ -24,8 +24,8 @@
en: 5b4f0f80a117f202
zh: ff05be196c181087
/desktop-packaging/windows-actions-packaging:
en: 68d800ed10cac4a6
zh: f72ac7e939af7ad5
en: faf2b4cffb576757
zh: b9c807eea360d53d
/desktop-packaging/macos-self-hosted-packaging:
en: a3af9523fdc60036
zh: 5e18d4740e9b7f57
+1 -1
View File
@@ -113,7 +113,7 @@ Select the workflow in Actions, choose the source branch, preset, signing mode a
Open the completed run in Actions and download its Artifacts. `windows-x64-signed-<version>-<run_id>-<run_attempt>` and `windows-x64-unsigned-<version>-<run_id>-<run_attempt>` contain the corresponding EXE; `windows-package-logs-<run_id>-<run_attempt>` contains packaging diagnostics. The upload steps are `Retain verified signed installer`, `Retain verified unsigned installer`, and `Retain packaging diagnostics`; their step durations and timestamped logs show upload time. These artifacts are not a Release or download-site publication.
For path-sensitive Windows build measurements, `build_root` optionally selects an absolute directory outside existing Git checkouts. Each run clones the validated source commit into a new `windows-package-<run-id>-<attempt>/source` directory; ignored files and previous build outputs are not copied. Dependency and signing caches retain their existing locations. Successful runs remove only their owned checkout after artifact uploads; failed runs retain it for inspection. Leave the input empty to build in the original Actions checkout. Include checkout creation and cleanup in the complete Job timing.
For path-sensitive Windows build measurements, `build_root` optionally selects an absolute directory outside existing Git checkouts. Each run clones the validated source commit into a new `windows-package-<run-id>-<attempt>/source` directory; ignored files and previous build outputs are not copied. Only the packaging process uses a sibling run-owned `temp` directory through `TEMP` and `TMP`; Actions steps and post-job hooks retain their original temporary directories. Dependency and signing caches retain their existing locations. Successful runs remove only their owned checkout after artifact uploads; failed runs retain it for inspection. Leave the input empty to build in the original Actions checkout. Include checkout creation and cleanup in the complete Job timing.
For cache measurements, `cache_namespace` selects a retained subdirectory for both dependency stores and upstream downloads; leave it empty for normal shared caches. Use a new name for a cold sample and repeat that name for warm samples. Names contain 1–64 lowercase letters, digits or hyphens, start with a letter or digit, and cannot be Windows device names. This does not reset the separate signature cache or OS file cache; report those states and Job start-to-completion time alongside phase timings and artifact size.
+1 -1
View File
@@ -113,7 +113,7 @@ runner 的受监督启动和恢复见 [Windows runner 机器准备](../windows-s
在 Actions 打开完成的运行并下载 Artifacts。`windows-x64-signed-<version>-<run_id>-<run_attempt>` 与 `windows-x64-unsigned-<version>-<run_id>-<run_attempt>` 包含对应 EXE;`windows-package-logs-<run_id>-<run_attempt>` 包含打包诊断。上传步骤名为 `Retain verified signed installer`、`Retain verified unsigned installer` 和 `Retain packaging diagnostics`;步骤耗时与带时间戳的日志显示上传用时。这些产物不等于发布 Release 或下载站内容。
测量 Windows 构建路径对性能的影响时,可用 `build_root` 指定现有 Git checkout 之外的绝对目录。每轮任务将已验证的源码提交克隆到新的 `windows-package-<run-id>-<attempt>/source` 目录;不会复制忽略文件或此前的构建输出。依赖缓存和签名缓存沿用原有位置。成功任务在产物上传后仅删除本轮所属的 checkout;失败任务保留该目录供检查。输入留空时使用原 Actions checkout。完整 Job 耗时须包含 checkout 创建和清理。
测量 Windows 构建路径对性能的影响时,可用 `build_root` 指定现有 Git checkout 之外的绝对目录。每轮任务将已验证的源码提交克隆到新的 `windows-package-<run-id>-<attempt>/source` 目录;不会复制忽略文件或此前的构建输出。仅打包进程通过 `TEMP` 和 `TMP` 使用同轮所属的同级 `temp` 目录;Actions 步骤及 Job 后置钩子保留原临时目录。依赖缓存和签名缓存沿用原有位置。成功任务在产物上传后仅删除本轮所属的 checkout;失败任务保留该目录供检查。输入留空时使用原 Actions checkout。完整 Job 耗时须包含 checkout 创建和清理。
测量缓存时,`cache_namespace` 为两个依赖 store 和上游下载选择保留的子目录;留空使用正常共享缓存。新名称用于冷缓存采样,再次使用同名用于热缓存采样。名称由 1–64 个小写字母、数字或连字符组成,以字母或数字开头,且不能为 Windows 设备名。这不会重置独立的签名缓存或操作系统文件缓存;应将其状态、Job 开始至完成的总耗时与阶段耗时和产物体积一并报告。
@@ -9,9 +9,9 @@ export interface WindowsBuildWorkspaceOptions {
/**
* Clone the clean selected commit into a new run-owned directory.
* @param options - Original checkout and Actions run identity.
* @returns Checkout to use for packaging.
* @returns Checkout and temporary directory owned by this run.
*/
export function prepareWindowsBuildWorkspace(options: WindowsBuildWorkspaceOptions): { directory: string }
export function prepareWindowsBuildWorkspace(options: WindowsBuildWorkspaceOptions): { directory: string; temporaryDirectory: string }
/**
* Remove the directory only when its ownership record matches the run.
* @param options - Original checkout and Actions run identity.
@@ -30,7 +30,7 @@ function locations(options) {
}
const owner = join(root, `windows-package-${runId}-${runAttempt}`)
assertUnlinkedAncestors(owner)
return { root, owner, directory: join(owner, 'source') }
return { root, owner, directory: join(owner, 'source'), temporaryDirectory: join(owner, 'temp') }
}
function git(directory, args) {
@@ -44,7 +44,7 @@ function ownership(options, paths) {
/**
* Clone the validated commit into a new run-owned checkout; failures retain diagnostics.
* @param {{sourceDirectory: string, buildRoot: string, runId: string, runAttempt: string, sourceSha: string}} options - Source and run identity.
* @returns {{directory: string}} Checkout to use for packaging.
* @returns {{directory: string, temporaryDirectory: string}} Checkout and temporary directory owned by this run.
*/
export function prepareWindowsBuildWorkspace(options) {
const paths = locations(options)
@@ -54,12 +54,13 @@ export function prepareWindowsBuildWorkspace(options) {
mkdirSync(paths.root, { recursive: true })
mkdirSync(paths.owner)
writeFileSync(join(paths.owner, 'owner.json'), ownership(options, paths), { flag: 'wx' })
mkdirSync(paths.temporaryDirectory)
git(paths.owner, ['clone', '--no-hardlinks', '--no-checkout', resolve(options.sourceDirectory), paths.directory])
git(paths.directory, ['checkout', '--detach', options.sourceSha])
if (git(paths.directory, ['rev-parse', 'HEAD']) !== options.sourceSha || git(paths.directory, ['status', '--porcelain'])) {
throw new Error('Isolated packaging checkout does not match the selected commit')
}
return { directory: paths.directory }
return { directory: paths.directory, temporaryDirectory: paths.temporaryDirectory }
}
/**
@@ -89,6 +90,6 @@ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1]
else {
if (process.argv.length !== 2 || !process.env.GITHUB_OUTPUT) throw new Error('Expected Actions output file and no arguments')
const result = prepareWindowsBuildWorkspace(options)
appendFileSync(process.env.GITHUB_OUTPUT, `directory=${result.directory}\n`)
appendFileSync(process.env.GITHUB_OUTPUT, `directory=${result.directory}\ntemporaryDirectory=${result.temporaryDirectory}\n`)
}
}
+4 -1
View File
@@ -40,7 +40,9 @@ it('clones the exact clean commit and removes only its run-owned checkout', () =
stdio: 'pipe',
})
const directory = join(options.buildRoot, 'windows-package-123-1', 'source')
expect(readFileSync(output, 'utf8')).toBe(`directory=${directory}\n`)
const temporaryDirectory = join(options.buildRoot, 'windows-package-123-1', 'temp')
expect(readFileSync(output, 'utf8')).toBe(`directory=${directory}\ntemporaryDirectory=${temporaryDirectory}\n`)
writeFileSync(join(temporaryDirectory, 'temporary.txt'), 'owned temporary data')
expect(readFileSync(join(directory, 'source.txt'), 'utf8')).toBe('selected source')
expect(existsSync(join(directory, 'private.env'))).toBe(false)
expect(() => prepareWindowsBuildWorkspace(options)).toThrow()
@@ -52,6 +54,7 @@ it('clones the exact clean commit and removes only its run-owned checkout', () =
symlinkSync(external, join(directory, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir')
removeWindowsBuildWorkspace(options)
expect(existsSync(directory)).toBe(false)
expect(existsSync(temporaryDirectory)).toBe(false)
expect(readFileSync(join(external, 'keep.txt'), 'utf8')).toBe('keep')
expect(readFileSync(join(sourceDirectory, 'source.txt'), 'utf8')).toBe('selected source')
const linked = join(root, 'linked')
+27
View File
@@ -160,6 +160,33 @@ it('routes all packaging consumers to the selected workspace and removes it only
}
})
it('scopes the optional temporary directory to installer building without changing Actions or later steps', () => {
const build = job.steps.find(step => step.name === 'Build installer and verify packaged runtime')!
expect(build.env?.PACKAGE_BUILD_TEMP).toBe('${{ steps.build_workspace.outputs.temporaryDirectory }}')
expect(build.run).toContain([
'if ($env:PACKAGE_BUILD_TEMP) {',
' $env:TEMP = $env:PACKAGE_BUILD_TEMP',
' $env:TMP = $env:PACKAGE_BUILD_TEMP',
'}',
].join('\n'))
expect(build.run!.indexOf('if ($env:PACKAGE_BUILD_TEMP)')).toBeLessThan(build.run!.indexOf('$packageArgs ='))
expect(build.run).not.toContain('GITHUB_ENV')
expect(build.run).not.toContain('RUNNER_TEMP')
expect(job.env).not.toHaveProperty('TEMP')
expect(job.env).not.toHaveProperty('TMP')
expect(job.env).not.toHaveProperty('PACKAGE_BUILD_TEMP')
for (const step of job.steps.filter(step => step !== build)) {
expect(step.env ?? {}).not.toHaveProperty('PACKAGE_BUILD_TEMP')
expect(step.env ?? {}).not.toHaveProperty('TEMP')
expect(step.env ?? {}).not.toHaveProperty('TMP')
expect(step.run ?? '').not.toMatch(/\$env:(?:TEMP|TMP)\s*=/u)
}
const selected = job.steps.find(step => step.id === 'build_workspace')!
const defaultBranch = selected.run!.split('} else {')[0]!
expect(defaultBranch).toContain('"directory=$env:GITHUB_WORKSPACE" >> $env:GITHUB_OUTPUT')
expect(defaultBranch).not.toContain('temporaryDirectory')
})
it.skipIf(process.platform !== 'win32')('keeps the dependency store stable across run-specific pnpm homes', () => {
const directory = mkdtempSync(resolve(tmpdir(), 'windows-package-store-'))
try {