Commit Graph
125 Commits
Author SHA1 Message Date
eugene yokota abe1b26bb7 [2.0.x] fix: Guard diskcache against path traversal (#9616)
**Problem**
There are several places in diskcache where resolve is called
without guards.

**Solution**
This adds guards to prevent path traversal.
2026-08-17 15:52:05 -04:00
eugene yokota 5d5e2badf2 [2.0.x] fix: Preserve file timestamp in local diskcache (#9559)
**Problem**
In theory, we shouldn't have to care about the timestamps,
but for local optimization it's still a useful key for caching.
Currently local Analysis cache gets invalidated after
syncBlob replaces the Analysis file with a symlink.

**Solution**
This preserves the original timestamp when copying files to
the local diskcache.
2026-08-06 21:06:43 -04:00
Eugene Yokota aacbf737c0 Add remote-cache test 2026-07-26 15:03:07 -04:00
BrianHotoppandClaude Opus 4.8 67b8c54432 [2.x] fix: Keep file I/O out of cache-write serialization (#9496)
putBlobsIfNeeded reads each blob's hash and size once, up front, and
returns only plain HashedVirtualFileRef values, so serializing an ActionResult
(disk, in-memory, or remote store) performs no file I/O and nothing re-stats a
blob after its CAS entry is written: a file vanishing once stored no longer
prevents the write, and I/O errors on an output file surface upfront at blob
storage time rather than mid-serialization.

Fixes #9349

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-25 15:57:32 -04:00
66e0a3ed93 [2.x] fix: Register every Def.declareOutput execution, not one per call site (#9492)
The cached-task macro allocated one mutable slot per syntactic
Def.declareOutput / Def.declareOutputDirectory call site and snapshotted the
slots into the task's outputs after the body ran. A call inside a loop or .map
over a runtime-determined list is a single syntactic site executed many times,
so each iteration overwrote the same slot and only the last file was cached and
restored on a cache hit. There was also no way for a conditional call site that
did not execute to stay out of the outputs: its slot remained null.

Declared outputs now accumulate in a per-task ListBuffer: the macro emits one
buffer at the top of the cached body and rewrites each call site to
ActionCache.registerOutput(vf, buffer), which appends and returns the value.
Every execution registers, an unexecuted site contributes nothing, and the
static multi-site shape is unchanged.

Refs #9462 (the declareOutput-in-a-loop half)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: eugene yokota <[email protected]>
2026-07-25 15:57:06 -04:00
90ce3b99f8 [2.x] fix: Re-extract a declared output directory on cache hit when it is missing (#9473)
A directory declared via Def.declareOutputDirectory is packaged as a sibling
<dir>.sbtdir.zip, so deleting the directory leaves the zip behind. On a cache
hit, syncFile's up-to-date short-circuit saw the zip in sync (same digest,
already a CAS symlink) and returned without the unpack side effect, which only
ran from the file-write path: the directory was never restored. For sbt's own
compile, whose classes directory is declared this way, rm -rf of the classes
directory with a warm cache meant run failed with ClassNotFoundException and no
recompile; only deleting the zip as well (or the whole cache) recovered.

The up-to-date branch now re-extracts when the extracted directory itself is
missing: a single stat on the warm path, per review preference over a
manifest-based per-file check. Partial deletions inside a still-existing
directory are not repaired, consistent with treating target/ contents as
sbt-managed.

Refs #9462 (the directory-restoration half; the declareOutput-in-a-loop half is
a separate macro-layer issue)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: eugene yokota <[email protected]>
2026-07-25 15:55:33 -04:00
Stas Shevchenkoandsshevchenko 8d2219e906 [2.x] fix: Fixes partial cache restoration (#9488)
A genuinely broken restore now degrades to the onsite task instead of a
silent cache hit with incomplete outputs. Related to #9349.

Co-authored-by: sshevchenko <[email protected]>
2026-07-25 15:55:09 -04:00
0f4c26fe04 [2.0.x] fix: Don't cache environmental (position-less) compile failures (#9464) (#9475)
Failure caching assumes a CompileFailed is a function of
the sources, which is true for source errors. But zinc also surfaces I/O write
failures ("error writing X.class") as compiler problems, so an environmental
failure (a concurrent target/ deletion, a permission blip) was cached under the
same mechanism and replayed from the global action cache on every later build,
even after the cause was gone. When the poisoned task is the metabuild compile
this is self-sustaining and unrecoverable from inside sbt: project loading
fails, so no task -- including clean -- can run, and only deleting the global
cache by hand recovers. The replayed diagnostics also name files/permissions
that no longer exist.

Refs #9455

Co-authored-by: BrianHotopp <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-20 00:28:39 -04:00
Anatolii Kmetiukandbitloi 743fe18f46 [2.x] fix: Fix FileAlreadyExistsException in packageDirectory (#9315)
**Problem**
Intermittent java.nio.file.FileAlreadyExistsException when publishing
classes.sbtdir.zip during action-cache packaging under parallel tasks
(#9043). Copying from a temp directory straight into the final path races
on the fixed destination name.

**Solution**
Stage the built zip next to the destination with a unique temp file, then
replace the final path via Files.move with REPLACE_EXISTING and ATOMIC_MOVE,
falling back to a non-atomic move when needed. Add a concurrent
packageDirectory test.

Closes #9043

Generated-by: Cursor (AI-assisted)

Co-authored-by: bitloi <[email protected]>
2026-06-08 23:37:56 -04:00
eugene yokota 06d7047003 [2.0.x] fix: ByteStream chunked upload/download (#9298) (#9301)
**Problem**

BatchUpdateBlobsRequest suffers from gRPC's message size limitation.

**Solution**

For larger files, we switch to using the ByteStream API, chunked to 1MB at a time.
2026-06-07 15:47:38 -04:00
Eugene Yokota f1c914f4ae [2.x] Reimplement FarmHash
**Problem**
sbtn and server uses FarmHash.

**Solution**
This reimplements FarmHash using Scala.
2026-05-31 16:57:12 -04:00
Eugene Yokota 82e5b28c9c Refactor VarHandle to Access typeclass 2026-05-31 16:41:54 -04:00
eugene yokota 27f09caba5 [2.0.x] Migrate FarmHash usage to xxhash64 (#9267)
Problem
ZAHa, which we use for FarmHash, uses Unsafe.

Solution
This ports xxhash64 to Scala.
2026-05-31 16:41:09 -04:00
eugene yokota ae1192109d [2.0.x] ci: Scalafmt 3.11.1 (#9280)
Apply Scalafmt
2026-05-31 16:30:32 -04:00
f78a141dd5 [2.0.x] fix: Report a missing input file clearly instead of an opaque SerializationException (#9275)
When a file referenced by a task's inputs/outputs (e.g. `Compile / resources +=
file("nope.txt")`) does not exist, hashing the task's cache key threw a
NoSuchFileException deep inside sjsonnew serialization. It surfaced as an opaque
sjsonnew.SerializationException that dumped the entire input list, with the real
cause buried several `Caused by:` levels down, so users routinely mistook it for
a corrupt cache and reached for `clean`.

ActionCache.mkInput now catches the hashing failure, detects a NoSuchFileException
anywhere in the cause chain (ActionCache.findMissingFile), and throws a
MessageOnlyException naming the file:

    [error] file referenced by the build does not exist: nope.txt

util-cache gains a dependency on util-control (a leaf module, no cycle) for
MessageOnlyException.

Fixes #9217.

Co-authored-by: Brian Hotopp <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-05-30 21:13:42 -04:00
eugene yokota fc15e03c33 [2.0.x] feat: Execution log (#9203)
**Problem**
We need some tooling to debug caching issues.

**Solution**
This adds an exeprimental execution log support,
which shows input and output of cached tasks.
2026-05-17 03:34:20 -04:00
Dream ecfbbba389 [2.x] feat: Add cacheVersion setting for global cache invalidation (#8993)
* [2.x] feat: Add cacheVersion setting for global cache invalidation

**Problem**
There was no escape hatch to invalidate all task caches when needed.

**Solution**
Add `Global / cacheVersion` setting that incorporates into the cache key
hash. Changing it invalidates all caches. Defaults to reading system
property `sbt.cacheversion`, or else 0L. When 0L, the hash is identical
to the previous behavior (backward compatible).

Fixes #8992

* [2.x] refactor: Simplify BuildWideCacheConfiguration and add cacheVersion test

- Replace auxiliary constructors with default parameter values
- Add unit test verifying cacheVersion invalidates the cache

* [2.x] fix: Restore auxiliary constructors for binary compatibility

* [2.x] test: Improve cacheVersion scripted test and add release note

- Scripted test now verifies cache invalidation via a counter
  that increments only when the task body actually executes
- Add release note documenting the cacheVersion setting
2026-05-17 03:17:12 -04:00
eugene yokotaandAnatolii Kmetiuk 8c5c1e6b6d [2.0.x] fix: Ensure resources are copied atomically (#9234)
Bumps io to released 1.12.0 which contains the atomic-write changes
to IO.copyFile, IO.transfer(InputStream, File), and IO.jar/zip.
ActionCacheStore.putBlob now uses IO.transfer instead of duplicating
staging logic.

Co-authored-by: Anatolii Kmetiuk <[email protected]>
2026-05-17 00:54:58 -04:00
eugene yokota 5fe73c82fd [2.x] perf: Use bytecodeHash for incremental test (#9152)
**Problem**
There's perf issue building ClassStamp for the incremental test.

**Solution**
This reuses the bytecodeHash created during compilation.
2026-04-29 23:10:59 -04:00
eugene yokotaandIdan Ben-Zvi cf94c29478 [2.0.x] fix: fall back to the onsite task when cache fails (#8890) (#8954)
**Problem**
When the remote cache server (e.g. bazel-remote using S3 for storage) reports an AC (Action Cache)
hit but the underlying CAS (Content Addressable Storage) blob is missing or
corrupt, ActionCache.cache propagates the resulting exception (typically
java.io.FileNotFoundException) directly to the SBT task engine process with no interception of the propogated error.
This causes a build failure instead of a graceful cache miss.

The three unguarded call sites are:
1. organicTask - syncBlobs after a successful put only caught NoSuchFileException,
   missing FileNotFoundException and other IO errors.
2. getWithFailure / readFromSymlink fast-path - syncBlobs inside flatMap with no
   exception handling.
3. getWithFailure main branch - both syncBlobs calls and the subsequent IO.read
   were completely unguarded.

**Solution**
Guard all three call sites with NonFatal catches:
- Cache read failures (getWithFailure) return Left(None) which the caller
  interprets as a cache miss, triggering organic recomputation.
- Cache write failures (organicTask) are demoted to a debug-level log; the task
  result that was already computed is returned successfully.

Two regression tests are added to ActionCacheTest:
1. Tests the main getWithFailure branch using the default relative-path converter.
2. Tests the readFromSymlink fast-path using an absolute-path converter so the
   symlink created on the first run is found by Files.isSymbolicLink on the second.

Co-authored-by: Idan Ben-Zvi <[email protected]>
2026-03-21 17:29:46 -04:00
bitloi b53342da15 Fix ActionCache: treat invalid/empty JSON as cache miss
Catch parse errors when reading cached values and return Left(None)
so the task is re-run instead of failing with IncompleteParseException.
Fixes plugins/dotty-sandwich scripted test flake.
2026-02-15 07:50:38 +01:00
eugene yokota 3e3cde19b9 [2.x] fix: Disk cache label (#8716)
**Problem**
Currently symlink hit is reported separately from the disk cache,
which is not necessary.

**Solution**
Rename the symlink label to disk.
2026-02-07 23:40:16 -05:00
calm c4a88328da [2.x] fix: handle NoSuchFileException during cache storage (#8699) 2026-02-06 06:36:07 -05:00
NeedmeFordev 81c3d7d6c6 [2.x] fix: Fixes ActionCache tests on Windows (#8628)
Fixes #8627

- Use ${OUT} placeholder and remove StringVirtualFile1 special-case handling
- Make dirZipExt check generic (use vf instead of svf: StringVirtualFile1)
- Let syncBlobs handle StringVirtualFile1 transparently via fileConverter.toPath()
2026-01-28 15:27:46 -05:00
calm 5789a7ef77 [2.x] feat: Implement ivyless publishLocal (#8634)
Fixes #8631

**Changes:**
- Add `useIvy` setting key (defaults to `true`)
- Add `ivylessPublishLocalImpl` helper that publishes without Ivy
- Modify `publishLocal` to use ivyless publisher when `useIvy := false`
- Generate ivy.xml via `lmcoursier.IvyXml`
- Generate MD5/SHA-1 checksums for all files
- Add scripted test `dependency-management/ivyless-publish-local`
2026-01-27 00:05:43 -05:00
dive2tech cf8899919d [2.x] Fix potential division by zero in CacheEventLog.toSummary (#8635)
Add guard to prevent division by zero when calculating hitRate.
If total is 0, hitRate is set to 0.0 instead of causing NaN or Infinity.
2026-01-26 15:47:16 -05:00
E.G f4bd7689a3 [2.x] test: Migrate FileInfoSpec.scala to verify.BasicTestSuite (#8544)
* test: Migrate FileInfoSpec.scala to verify.BasicTestSuite (#8542)

Migrate FileInfoSpec.scala from ScalaTest's AnyFlatSpec to
verify.BasicTestSuite, following the pattern established by other
test files in the util-cache module.

Changes:
- Replace AnyFlatSpec class with BasicTestSuite object
- Convert 'it should ... in' syntax to 'test(...)' syntax
- Use Scala 3 syntax with colon indentation
- Change === to == for assertions (BasicTestSuite style)
- Add explicit Unit return types for consistency
- Add 'end FileInfoSpec' marker

Fixes #8542
2026-01-15 22:19:30 -05:00
E.GandGlobalStar117 0afc624009 [2.x] test: Migrate SingletonCacheSpec.scala to verify.BasicTestSuite (#8546)
Migrate SingletonCacheSpec.scala from ScalaTest's AnyFlatSpec to
verify.BasicTestSuite, following the pattern established by other
test files in the util-cache module.

Changes:
- Replace AnyFlatSpec class with BasicTestSuite object
- Convert 'should ... in' syntax to 'test(...)' syntax
- Use Scala 3 syntax with colon indentation
- Change === to == for assertions (BasicTestSuite style)
- Replace intercept[Exception] with scala.util.Try pattern
- Add 'end SingletonCacheSpec' and 'end ComplexType' markers

Related to the ongoing test migration effort.

Co-authored-by: GlobalStar117 <[email protected]>
2026-01-15 14:09:25 -05:00
azdrojowa123 106b8b9978 [2.x] Always create symlinks to the cache in the target locations #8445 (#8461)
* always create symlinks to the cache in the target locations, even if the digest matches #8445
* create a test (currently failing even on #develop) that fails because if `zipPath` in `sbt.util.ActionCache.packageDirectory` is a symlink to the CAS, in later calls, this path in the CAS gets overridden by the new sources.

- in this test, after "run 1" in line 15, the produced file "target/out/jvm/scala-3.7.4/a/classes.sbtdir.zip" is a symlink to the CAS, let's call it SH1.
- when "run 3" is executed, `IO.zip` saves the new value to `zipPath`, which is "target/out/jvm/scala-3.7.4/a/classes.sbtdir.zip -> SH1", so SH1 gets overridden.
- when the last "run 1" is executed, the cache retrieves SH1, but it contains the data from "run 3" (the test fails with "actual A.x is 3").

* when packaging a directory into a zip, use a temp directory to avoid overwriting the cache #8461
2026-01-12 19:38:59 -05:00
MkDev11 fe6125d8d1 [2.x] feat: Cache failed compilation to avoid repeated failures (#8490)
When a compilation fails with CompileFailed, the failure is now cached
so that subsequent builds with the same inputs don't re-run the failed
compilation. This significantly improves the experience when using BSP
clients like Metals that may trigger many compilations in a row.

The implementation:
- Adds CachedCompileFailure, CachedProblem, and CachedPosition types
  to serialize compilation failures
- Modifies ActionCache.cache to catch CompileFailed exceptions and
  store them in the cache with exitCode=1
- On cache lookup, checks for cached failures first and re-throws
  the cached exception if found
- Fixes DiskActionCacheStore.put to preserve exitCode from request
- Adds unit test to verify cached failure behavior

Fixes #7662
2026-01-12 16:03:05 -05:00
MkDev11 985cf94bb7 fix: Only disable symlinks when truly not supported (#8479) 2026-01-11 13:04:37 -05:00
MkDev11 db06acb31e test: Migrate CacheSpec to verify.BasicTestSuite (#8474)
Migrate CacheSpec from ScalaTest AnyFlatSpec to verify.BasicTestSuite
following the project's test framework standardization.

Changes:
- Convert class to object extending BasicTestSuite
- Replace ScalaTest 'should/in' syntax with test() method
- Replace fail() with assert(false, message)
- Replace === with ==
- Use Scala 3 indentation-based syntax
- Remove ScalaTest import, add verify.BasicTestSuite import

All tests pass successfully.

Fixes #8466

Generated-by: Cascade (AI pair programmer)
2026-01-11 07:19:22 -05:00
john0030710 3abddf461a Fix side-effect file syncing when using symlink optimization
- Ensure output files are synced even when reading from symlink
- Call findActionResult to get ActionResult for side-effect files
- Maintains performance benefit while ensuring correctness
- Addresses @eed3si9n's concern about tasks generating files on the side
2026-01-10 00:25:59 +01:00
john0030710 08fe019dc0 Use proper exception handling with Exception.nonFatalCatch.opt
- Replace try-catch with Exception.nonFatalCatch.opt for cleaner code
- Follows Scala best practices for non-fatal exception handling
- More functional and idiomatic approach
- Avoids catching fatal exceptions like VirtualMachineError
2026-01-10 00:23:05 +01:00
john0030710 6a63565b0e Improve symlink optimization using FileConverter
- Use config.fileConverter.toPath() instead of string manipulation
- Avoid hardcoded '/' prefix removal
- More robust and maintainable approach
- Fix IO.read() to include UTF-8 charset parameter
2026-01-10 00:20:32 +01:00
john0030710 d998161a6e Fix #8429: Add symlink optimization to ActionCache.get
- Check for symlinked value files before reading AC JSON
- When symlink exists and is valid, read directly from it
- Fallback to original AC file reading if symlink read fails
- Improves performance by avoiding unnecessary AC file reads
- All existing tests pass
2026-01-09 15:11:09 +01:00
Eugene Yokota 7d76f8f33e [2.x] fix: Workaround for NoSuchFileException
**Problem**
In some scripted tests, we've seen NoSuchFileException.

**Solution**
Catch the exceptions related to caching.
2025-12-25 04:38:43 -05:00
Eugene Yokota c8edb171ee [2.x] Adds cleanExpunge command
**Problem**
We might want to clear the disk cache.

**Solution**
This adds cleanExpunge command.
2025-11-22 23:12:18 -05:00
Eugene Yokota 4265b92aa4 perf: Cache content hash of binary files
**Problem**
sha256 is currently a bottleneck for no-op compilation.

**Solution**
This adds a local, in-memory cache of sha256 hashes of binary files
using their timestamp and file size.
The size of the digest cache can be configured using localDigestCacheByteSize,
which is set to 1MB by default.
2025-11-11 15:59:42 -05:00
Matt Dziuban 77ca8ea29a Add test for recovering from invalid JSON in disk cache. 2025-10-07 14:01:38 -04:00
Matt Dziuban c660c56644 Move IO.read and Parser.parseUnsafe calls inside try. 2025-10-07 13:28:10 -04:00
xuwei-k 14b7ddf867 remove redundant val modifier 2025-10-06 09:28:27 +09:00
Eugene Yokota 2fa3159f18 Update to Contraband 0.8.0 2025-09-19 00:46:34 -04:00
xuwei-k 708fffa290 delete unused "internal" package code 2025-09-06 19:41:12 +09:00
Eugene Yokota 353ee59cdb fix: Workaround syncFile issue
**Problem**
We get non-deterministic NoSuchFileException.

**Solution**
Catch the exception.
2025-08-21 08:13:49 +02:00
Eugene Yokota be5152dbfb sjson-new 0.14.0-M4 2025-08-15 22:12:06 -04:00
Eugene Yokota 6a7b56a645 Default to cached task
This flips the default `:=` operation to the cached task.
To opt out of the cache, use `Def.uncached(...)` or
mark the key with `@cacheLevel(include = Array.empty)`
2025-06-21 08:54:51 +09:00
Eugene Yokota 3b4547718e Remove deprecated methods 2025-05-11 05:23:41 -04:00
Eugene Yokota 967ecb49bc deps: Scala 3.7.0 2025-05-11 01:30:07 -04:00
xuwei-k fa6daec812 remove unused imports 2025-05-09 16:46:48 +09:00