Skip to content

Comments

fast test builds#12502

Open
turadg wants to merge 21 commits intomasterfrom
ta/fast-test-builds
Open

fast test builds#12502
turadg wants to merge 21 commits intomasterfrom
ta/fast-test-builds

Conversation

@turadg
Copy link
Member

@turadg turadg commented Feb 20, 2026

refs: #12381

Description

This saves several seconds per buildAndExtract in boot/RunUtils tests. It does this through refactoring the code behind agoric run to run in-process and to broaden the caching capabilities. It also refactors build caching to consolidate some of the logic. It also adds instrumentation and cleans up some of the earlier time logging.

It should be reviewable by commit.

Security Considerations

Bundles under test come from cache. Some filesystem and access as the source code.

Scaling Considerations

n/a

Documentation Considerations

Should be transparent

Testing Considerations

CI. Also run locally and have confirmed no rebuilding on subsequent runs.

Upgrade Considerations

n/a

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ta/fast-test-builds

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security
Copy link

socket-security bot commented Feb 20, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@turadg turadg requested a review from michaelfig February 20, 2026 22:46
@turadg turadg requested a review from dckc February 21, 2026 00:16
@turadg turadg marked this pull request as ready for review February 21, 2026 00:16
Copilot AI review requested due to automatic review settings February 21, 2026 00:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly improves test build performance by implementing in-process proposal execution and comprehensive build caching. The changes refactor the code behind agoric run to execute proposal builder scripts in-process (rather than via shell) and add a sophisticated dependency-aware caching layer that persists across test runs.

Changes:

  • Introduced cross-process directory locking with stale lock recovery for build caches
  • Added in-process proposal building mode with fallback to shell execution for compatibility
  • Implemented dependency-aware caching with fingerprint-based invalidation
  • Added opt-in Chrome Trace Event format profiling for boot tests
  • Consolidated atomic file write utilities and bundle cache tooling

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/internal/src/build-cache.js New directory locking and atomic write utilities for cross-process coordination
packages/internal/src/build-cache-types.ts TypeScript types for build cache events and powers
packages/internal/test/build-cache.test.js Comprehensive tests for directory locking and atomic writes
packages/agoric-cli/src/proposals.js New in-process proposal builder with shell fallback
packages/deploy-script-support/src/writeCoreEvalParts.js Added onWriteCoreEval callback for in-process material capture
packages/deploy-script-support/src/helpers.js Wired through new endowments for onWriteCoreEval, log, writeFile
packages/deploy-script-support/src/cachedBundleSpec.js Updated to use writeFileAtomic
packages/boot/tools/supports.ts Major refactoring with proposal cache, profiling, and build modes
packages/boot/test/tools/proposal-extractor-cache.test.ts Tests for proposal cache hit/miss and invalidation
packages/boot/README.md Documentation for test profiling feature
packages/SwingSet/tools/bundleTool.js Refactored to use shared directory locking and event system
packages/SwingSet/test/bundleTool.test.ts Updated tests for new API
packages/agoric-cli/src/lib/bundles.js Added null safety for optional fileName
packages/agoric-cli/package.json Exported new proposals.js module
packages/cosmic-swingset/test/inquisitor.test.ts Updated to use new bundleTool API
packages/boot/test/configs.test.js Updated to use new bundleTool API
packages/SwingSet/src/controller/initializeSwingset.js Updated to use makeNodeBundleCache directly
packages/SwingSet/package.json Added TypeScript support for AVA tests
PLAN.md Implementation plan document

@@ -0,0 +1,59 @@
# @aglocal/boot
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README has a typo in the package name. It should be "@agoric/boot" not "@aglocal/boot". This typo in the header appears to be a simple error and should be corrected to match the actual package name used throughout the codebase.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +142
let atomicWriteSequence = 0;

/**
* @param {{
* fs: Pick<import('node:fs/promises'), 'rename' | 'writeFile'>;
* filePath: string;
* data: string;
* now: () => number;
* pid: number;
* }} options
*/
export const writeFileAtomic = async ({ fs, filePath, data, now, pid }) => {
atomicWriteSequence += 1;
const tempPath = `${filePath}.${pid}.${now()}.${atomicWriteSequence}.tmp`;
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The atomicWriteSequence is a module-level mutable variable that is not safe for concurrent use across multiple imports or module instances. If multiple parts of the codebase import this module, they will share the same sequence counter, which could lead to collisions in temporary file names despite the sequence being incremented. Consider making this a WeakMap keyed by the fs object, or better yet, use crypto.randomBytes or a UUID for uniqueness instead of a sequence number to avoid any potential race conditions.

Copilot uses AI. Check for mistakes.
Comment on lines +362 to +395
const queueWrite = () => {
session.writeQueue = session.writeQueue
.catch(() => {})
.then(async () => {
const metadataEvents: BootProfileMetadataEvent[] = [
{
name: 'process_name',
ph: 'M',
pid,
tid: 0,
ts: 0,
args: { name: 'agoric-boot-tests' },
},
{
name: 'thread_name',
ph: 'M',
pid,
tid: 0,
ts: 0,
args: { name: 'main' },
},
];
const traceFile: BootProfileTraceFile = {
displayTimeUnit: 'ms',
traceEvents: [...metadataEvents, ...session.events],
};
await fs.mkdir(dirname(profilePath), { recursive: true });
await fs.writeFile(
`${profilePath}`,
`${JSON.stringify(traceFile, null, 2)}\n`,
'utf8',
);
})
.catch(() => {});
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profiler writes are queued sequentially using a promise chain pattern (session.writeQueue.catch().then()...), but errors are silently caught at lines 364 and 395. This means if a write fails (e.g., due to disk full or permissions), the error is swallowed and subsequent writes might also fail silently. While this is acceptable for optional profiling data, it would be helpful to at least log errors to console.error so developers are aware when profiling is not working correctly. Consider adding minimal error logging before the catch returns.

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +230
const getTargetNames = (canonicalRootPath, targetName, options0) => {
const requestedTargetName =
targetName || path.basename(canonicalRootPath, '.js');
const optionsKey = optionsKeyFor(options0);
const hashKey = createHash('sha256')
.update(`${canonicalRootPath}\n${optionsKey}`)
.digest('hex')
.slice(0, 12);
const cacheTargetName = `${sanitizeName(requestedTargetName)}-${hashKey}`;
return harden({ requestedTargetName, cacheTargetName, optionsKey });
};
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTargetNames function computes a hash-based cache target name that includes the options (line 224-228). However, this means that bundles with different build options will have different cache target names and won't share cached bundles even if the source is identical. While this is technically correct for cache invalidation, it could lead to unnecessary bundle duplication when the same source is built with different metadata-only options that don't affect the bundle content. Consider documenting this behavior or optimizing the hash key to only include options that actually affect bundle content.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant