Adds cycle detection to DependencyGraph using DFS with back-edge detection.
This will be used by the cycle detection feature for build errors.
Each cycle is represented as a path that starts and ends at the same node,
e.g., [A, B, C, A].
Adds buildStorePathGraphFromScan() to bridge the generic DependencyGraph
infrastructure with Nix's store path reference scanning. This enables
building annotated dependency graphs that track which files create which
dependencies.
## New API: buildStorePathGraphFromScan()
**Purpose**: Convert file-level scan results into a StorePath-level graph
**How it works**:
1. Calls scanForReferencesDeep() to find which files contain references
2. For each file that references another store path:
- Adds edge: rootStorePath → referencedStorePath
- Attaches FileListEdgeProperty with the file path
3. Returns DependencyGraph<StorePath, FileListEdgeProperty>
**Key benefit**: The graph carries file-level provenance. When you see
an edge A→B, you can query which files in A reference B. Essential for:
- why-depends output showing exact file locations
- Future: detailed cycle error messages
## API Changes
**scanForReferencesDeep() callback signature**:
```cpp
// Before: std::function<void(FileRefScanResult)>
// After: std::function<void(const FileRefScanResult &)>
```
Pass by const-ref to avoid copying large result structures.
## Usage Example
```cpp
auto graph = buildStorePathGraphFromScan(
*accessor,
CanonPath("/nix/store/abc-foo"),
storePathAbc,
candidateRefs
);
// Query file annotations
auto edgeProp = graph.getEdgeProperty(storePathAbc, storePathDef);
// edgeProp->files contains files that created this edge
```
## Implementation Details
- Uses DependencyGraph's edge properties feature
- FileListEdgeProperty defined in dependency-graph.hh
- Merges duplicate edges (multiple files → same path)
- Debug logging for discovered edges
- Zero overhead if file-level detail not needed
Introduces a reusable directed graph template built on Boost Graph Library
(BGL) to provide graph operations for store path dependency analysis. This
will be used by `nix why-depends` and future cycle detection.
Implement `uploadPart()` for uploading individual parts in S3 multipart
uploads:
- Constructs URL with `?partNumber=N&uploadId=ID` query parameters
- Uploads chunk data with `application/octet-stream` mime type
- Extracts and returns `ETag` from response
This is a good default (the methods that allow for an arbitrary choice
of source accessor are generally preferable both to implement and to
use). And it also pays its way by allowing us to delete *both* the
`DummyStore` and `LocalStore` implementations.
Add concurrency group configuration to the CI workflow to automatically
cancel outdated runs when a PR receives new commits or is force-pushed.
This prevents wasting CI resources on superseded code.
Introduces `scanForReferencesDeep` to provide per-file granularity when
scanning for store path references, enabling better diagnostics for
cycle detection and `nix why-depends --precise`.
Implement `abortMultipartUpload()` for cleaning up incomplete multipart
uploads on error:
- Constructs URL with `?uploadId=ID` query parameter
- Issues `DELETE` request to abort the multipart upload
With #14314, in some places in the parser we started using C++ objects
directly rather than pointers. In those places lines like `$$ = $1` now
imply a copy when we don't need one. This commit changes those to `$$ =
std::move($1)` to avoid those copies.
Previously it used the `ThreadPool` default,
i.e. `std:🧵:hardware_concurrency()`. But copying signatures is
not primarily CPU-bound so it makes more sense to use the
`http-connections` setting (since we're typically copying from/to a
binary cache).
The `showBytes()` function was redundant with `renderSize()` as the
latter automatically selects the appropriate unit (KiB, MiB, GiB, etc.)
based on the value, whereas `showBytes()` always formatted as MiB
regardless of size.
Co-authored-by: Bernardo Meurer Costa <beme@anthropic.com>