Skip to content

feat: Incremental Build#1267

Draft
RandomByte wants to merge 251 commits into
mainfrom
feat/incremental-build-4
Draft

feat: Incremental Build#1267
RandomByte wants to merge 251 commits into
mainfrom
feat/incremental-build-4

Conversation

@RandomByte
Copy link
Copy Markdown
Member

Implementation of RFC 0017 Incremental Build

This PR supersedes previous PoC: #1238

JIRA: CPOUI5FOUNDATION-1174

@RandomByte RandomByte marked this pull request as draft January 7, 2026 12:28
@coveralls
Copy link
Copy Markdown

coveralls commented Jan 7, 2026

Coverage Status

coverage: 99.566% (+4.9%) from 94.658%
when pulling b727ebc on feat/incremental-build-4
into cb29ec1 on main.

@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch 3 times, most recently from 5224cd2 to 4904c84 Compare January 9, 2026 09:22
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 950fc6d to 41eed91 Compare January 14, 2026 15:28
@maxreichmann maxreichmann force-pushed the feat/incremental-build-4 branch from bb39565 to 2a21507 Compare January 20, 2026 10:01
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch 3 times, most recently from 6233816 to f858659 Compare January 20, 2026 16:58
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 71db1d0 to a2c371f Compare January 26, 2026 09:54
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 7364b4b to cf43f0c Compare February 9, 2026 13:32
@maxreichmann maxreichmann force-pushed the feat/incremental-build-4 branch 2 times, most recently from 252b966 to 874a943 Compare February 16, 2026 17:17
Comment thread packages/project/test/fixtures/application.a/custom-tasks/custom-task-2.js Outdated
Comment thread packages/project/test/fixtures/application.a/custom-tasks/custom-task-0.js Outdated
@maxreichmann maxreichmann force-pushed the feat/incremental-build-4 branch 5 times, most recently from df275e5 to 0345502 Compare February 27, 2026 10:34
Comment thread packages/project/test/fixtures/application.a/task.dependency-change.js Outdated
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 9fc2509 to 20ba653 Compare March 5, 2026 16:23
@maxreichmann maxreichmann force-pushed the feat/incremental-build-4 branch from 20ba653 to 9fc2509 Compare March 5, 2026 16:34
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 9fc2509 to 940376d Compare March 5, 2026 16:40
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 9197670 to 44d1107 Compare March 20, 2026 15:05
@maxreichmann maxreichmann force-pushed the feat/incremental-build-4 branch from 44d1107 to d7c402c Compare March 26, 2026 14:05
@maxreichmann
Copy link
Copy Markdown
Member

maxreichmann commented Mar 26, 2026

Rebased onto origin/main

Comment thread internal/e2e-tests/test/build.js Fixed
Comment thread internal/e2e-tests/test/version.js Fixed
RandomByte and others added 13 commits May 11, 2026 10:45
Resources <= 128 bytes are stored uncompressed in the CAS, avoiding
gzip overhead that exceeds the compression benefit for tiny inputs.
The read path uses gzip magic byte detection for backward compatibility.
Build tests: add getRootPath stub and cacheDir to expected args to
match the new cache directory parameter in graph.build().

Serve tests: adapt to the non-returning handler pattern (pOnError)
by using fire-and-settle instead of awaiting handler completion,
and account for the new error callback passed to serverServe.
serveResources: Rewrite tests for simplified middleware that now uses
resource.getIntegrity() for ETag and res.send(buffer) instead of
streaming, properties encoding, manifest generation, and version
replacement.

MiddlewareManager: Add required sources parameter to constructor
calls and update expected middleware list (serveThemes removed).

main: Update expected CSS content (minified by build server), update
discovery app_pages entries, remove tests for files no longer produced
by the build server (css_variables, library_skeleton).

caching: Replace obsolete manifestEnhancer and version placeholder
tests with basic ETag caching tests.

Also fix port conflict between caching and acceptRemoteConnections
tests.
Both test files used port 3334 causing ECONNRESET when run concurrently.
The BuildServer's chokidar file watchers kept the event loop alive
after the HTTP server was closed, causing test timeouts.
Set UI5_DATA_DIR to an absolute path (test/tmp/.ui5-data) so builder
tests use an isolated cache directory instead of the user's ~/.ui5
home, preventing cross-contamination between test runs and local state.
…rrors

Move PRAGMA busy_timeout=5000 to be the first statement after opening
the database connection. This ensures concurrent processes will wait
rather than immediately fail with "database is locked" when they race
to set journal_mode=WAL on a freshly created database.
Update stringReplacer and nonAsciiEscaper tests to expect undefined in
the result array when a resource is not modified, matching the
committed processor behavior of signaling no-ops to enable incremental
build optimizations.
Separate build-status and project-build-status event handling:
use buildStatusEventStub for determining project build order (via
project-build-start status), and projectBuildStatusEventStub for
extracting per-project task execution details.
The frozen source reader was inserted into the reader chain without
style-specific path mapping, causing buildtime-path resources to leak
into dist/flat/runtime queries. Add an applyStyleToReader callback so
ProjectResources can wrap the frozen reader with the same transform
the source reader receives.
Memory adapter and Proxy reader called the deprecated getStatInfo()
to check the nodir option, which triggered a deprecation log via
@ui5/logger. This caused spurious verbose log entries that leaked
into downstream tests using global logger mocks.
… Module

Project._addReadersForWriter used unshift which reversed the stage
priority order, causing earlier stages (e.g. replaceCopyright) to
shadow later stages (e.g. replaceVersion) in the result reader chain.
Change to push so that later stages have higher priority, matching
ComponentProject behavior.
…hed tags

The build manifest version was bumped to "1.0" but the version check in
ProjectBuildContext only accepted "0.1" and "0.2", causing pre-built
dependencies to be rebuilt from scratch instead of being skipped.

Also ensure getProjectResourceTagCollection() applies cached stage tags
before returning the collection, matching the pattern already used by
getResourceTagCollection().
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 0670d94 to d029efb Compare May 11, 2026 08:45
On Windows the SQLite database file handle kept the cache.db locked
after the build finished. When a subsequent build tried to clean the
output directory, fs.rm failed with EBUSY. Close the CacheManager at
the end of buildToTarget, after all deferred disk writes have completed.
Add tests for previously uncovered modules: MonitoredReader,
MonitoredReaderWriter, MonitoredResourceTagCollection, and Proxy reader.
Also extend existing tests for ResourceTagCollection, resourceFactory,
and AbstractReaderWriter to cover missing functions. Functions coverage
rises from 81% to 95%.
Add targeted tests to bring @ui5/project coverage above the configured
thresholds (95% statements, 90% branches, 95% functions, 95% lines).
Also fix AVA timeout for parallel test execution and remove dead code
module utils/sanitizeFileName.js.

New test files:
- build/cache/utils.js, index/TreeNode.js, index/ResourceIndex.js
- build/BuildReader.js, helpers/WatchHandler.js, helpers/ProjectBuilderOutputStyle.js
- ui5framework/maven/CacheMode.js

Extended existing tests:
- HashTree.js, SharedHashTree.js, CacheManager.js
- specifications/Project.js, types/Component.js, types/Library.js
- types/Module.js, types/ThemeLibrary.js, extensions/Task.js
The CacheManager is a singleton per cache directory. When multiple
concurrent builds share the same singleton (e.g. in parallel test
execution), the first build to finish would close the database and
break any other build still using it.

Track the number of active consumers via a reference count that is
incremented on each create() call and decremented on close(). The
underlying SQLite database is only closed when the last consumer
releases its reference.
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 5c533bd to ee33924 Compare May 11, 2026 16:22
… BuildServer

When source files are modified while a build is in progress (detected by
condition rather than a fatal error. This fixes a race condition where
the build takes close to or exceeds 500ms, causing the integration
test's file writes to land during source revalidation.

- Add SourceChangedDuringBuildError for typed error handling
- Handle it alongside AbortBuildError in BuildServer (re-queue projects)
- Reset source index state on error to prevent infinite retry loops
- Pass abort signal to allTasksCompleted() so watcher-triggered aborts
  take precedence over the source-change error
Replace fragile setTimeout(0) with deterministic promise-based signals
to wait for the handler's async work to complete. On Node 22, esmock's
off-thread ESM loader hooks can take more than one microtask tick to
resolve dynamic imports, causing afterEach's esmock.purge() to nullify
the mock cache before the handler's imports resolve.

Use handlerReady (resolves on first stdout write) for most tests, and
openCalled (resolves when the open stub fires) for --open tests which
have an additional dynamic import after stdout output.
…ests

BuildServer.destroy() did not close the CacheManager, leaving SQLite file
handles open when subsequent tests tried to delete temp directories. On
Windows, open handles prevent file deletion (EBUSY).

- Close CacheManager in BuildServer.destroy() via new
  ProjectBuilder.closeCacheManager() method
- Close singleton instances in CacheManager create() test
- Make after.always rimraf best-effort in cache tests since test/tmp is
  already cleaned at the start of each run
- Handle EBUSY gracefully in isValid file-deletion test (not possible on
  Windows with open handles)
…n integration tests

- Add #destroyed flag to BuildServer to prevent new builds from being
  triggered after destroy() is called
- Clear pending build queue and timeout before awaiting active build
  to avoid race with setTimeout-based queue processing
- Add maxRetries to rmrf in integration tests for Windows file handle
  release delays after SQLite close
…AL on close

On Windows, SQLite WAL mode keeps file handles that prevent deletion even
after close(). Adding PRAGMA wal_checkpoint(TRUNCATE) before close flushes
WAL content back to the main database and releases all file handles cleanly.

This single fix in BuildCacheStorage.close() resolves all Windows EBUSY
errors across cache tests and integration tests without needing per-test
workarounds.

Additional fixes:
- Close CacheManager in BuildServer.destroy() to release DB handles
- Add #destroyed flag to prevent builds from starting after shutdown
- Close singleton instances in CacheManager create() test
Add tests for the serveIndex error-handling path (byGlob rejection) and
the sslUtil chmod failure callbacks when fixing outdated file permissions.
Add mocked unit tests covering the previously untested error-handling
functions in lib/server.js: the HTTP server error event handler,
buildServer error event forwarding, and the close() rejection handler
when buildServer.destroy() fails.
@RandomByte RandomByte force-pushed the feat/incremental-build-4 branch from 68f9cb9 to 4a58a19 Compare May 12, 2026 09:12
…e during index re-init

After a SourceChangedDuringBuildError, the source index is nulled and state
reset to RESTORING_PROJECT_INDICES. If projectSourcesChanged or
dependencyResourcesChanged was called before initSourceIndex ran again, the
state was incorrectly transitioned to REQUIRES_UPDATE, causing initSourceIndex
to skip initialization and a subsequent NPE on the null source index.
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.

6 participants