Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,29 @@ jobs:
- run: cargo +stable test --workspace --exclude code-system-graph-fuzz --all-targets --all-features --locked
- run: RUSTDOCFLAGS="-D warnings" cargo +stable doc --workspace --exclude code-system-graph-fuzz --all-features --no-deps --locked

macos-sqlite:
name: macOS SQLite tests
runs-on: macos-latest
native-platforms:
name: Native ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: macOS x86_64
os: macos-15-intel
- name: macOS ARM64
os: macos-15
- name: Windows x86_64
os: windows-2025
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: macos-sqlite
- run: cargo +stable test -p code-system-graph-store-sqlite --all-targets --locked -- --test-threads=1
shared-key: native-${{ matrix.os }}
- run: cargo +stable build --workspace --exclude code-system-graph-fuzz --all-features --locked
- run: cargo +stable test --workspace --exclude code-system-graph-fuzz --all-targets --all-features --locked -- --test-threads=1

msrv:
name: Rust 1.97.1 MSRV
Expand Down Expand Up @@ -106,7 +117,9 @@ jobs:
- uses: Swatinem/rust-cache@v2
- uses: anchore/sbom-action/download-syft@v0
- run: scripts/package-release.sh
- run: scripts/smoke-install.sh "dist/code-system-graph-x86_64-unknown-linux-gnu-v1.0.0"
- run: |
version="$(awk -F '"' '/^version = / { print $2; exit }' Cargo.toml)"
scripts/smoke-install.sh "dist/code-system-graph-x86_64-unknown-linux-gnu-v${version}"

release-workloads:
name: Linux release workloads
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,13 @@ jobs:
run: cargo build --locked --release --target ${{ matrix.target }} --bin csgraph --bin code-system-graph-hooks

- name: Run target tests
if: runner.os == 'Linux'
run: cargo test --workspace --exclude code-system-graph-fuzz --all-features --locked --target ${{ matrix.target }}

- name: Run target tests serially
if: runner.os != 'Linux'
run: cargo test --workspace --exclude code-system-graph-fuzz --all-features --locked --target ${{ matrix.target }} -- --test-threads=1

- name: Package Unix archive
if: runner.os != 'Windows'
shell: bash
Expand All @@ -160,6 +165,18 @@ jobs:
tar -C dist -czf "dist/${asset_dir}.tgz" "${asset_dir}"
rm -rf "dist/${asset_dir}"

- name: Smoke-test Unix archive
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
version="${RELEASE_TAG#v}"
asset_dir="${PRODUCT_NAME}-${TARGET}-v${version}"
extracted="$(mktemp -d)"
trap 'rm -rf "$extracted"' EXIT
tar -C "$extracted" -xzf "dist/${asset_dir}.tgz"
scripts/smoke-install.sh "$extracted/${asset_dir}"

- name: Package Windows archive
if: runner.os == 'Windows'
shell: pwsh
Expand All @@ -176,6 +193,23 @@ jobs:
Compress-Archive -Path "dist\$assetDir" -DestinationPath "dist\$assetDir.zip" -Force
Remove-Item "dist\$assetDir" -Recurse -Force

- name: Smoke-test Windows archive
if: runner.os == 'Windows'
shell: pwsh
run: |
$version = $env:RELEASE_TAG -replace '^v', ''
$assetDir = "$env:PRODUCT_NAME-$env:TARGET-v$version"
$extracted = Join-Path $env:RUNNER_TEMP "code-system-graph-release-smoke"
Expand-Archive -Path "dist\$assetDir.zip" -DestinationPath $extracted -Force
$binDir = Join-Path $extracted "$assetDir\bin"
$actualVersion = & (Join-Path $binDir "csgraph.exe") --version
if ($actualVersion -ne "csgraph $version") {
throw "unexpected csgraph version: $actualVersion"
}
if (-not (Test-Path (Join-Path $binDir "code-system-graph-hooks.exe"))) {
throw "code-system-graph-hooks.exe is missing from the archive"
}

- name: Upload release asset
uses: actions/upload-artifact@v7
with:
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
All notable public changes to Code System Graph are documented in this file. Code System Graph follows Semantic
Versioning.

## [1.0.1] - 2026-08-04

### Fixed

- Fixed the portable capability-directory reader so Windows builds preserve the diagnostic path
without moving it before the bounded read.
- Canonicalized work-sidecar parent directories before SQLite opens them, preserving final-file
`NOFOLLOW` protection while supporting the standard symlinked `/var` path on macOS.
- Pinned the CLI and its tests to the bundled SQLite implementation so macOS and Windows use the
same validated database engine as the persistence crate.
- Raised the `csgraph` executable stack on Windows to match the extraction workload without
changing process memory or execution-policy limits.
- Serialized native Windows and macOS test execution to stay within platform file-descriptor and
filesystem concurrency limits while retaining the complete test suite.

### Release engineering

- Added full native Windows x86_64 and macOS x86_64/ARM64 build and test gates to pull-request CI.
- Added archive smoke tests for Unix and Windows release assets before publication.
- Made release validation and installation smoke tests derive the workspace version instead of
embedding `1.0.0`.

## [1.0.0] - 2026-08-04

First public release of Code System Graph.
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ default-members = ["crates/*"]
resolver = "3"

[workspace.package]
version = "1.0.0"
version = "1.0.1"
edition = "2024"
rust-version = "1.97.1"
description = "Local system-level code intelligence across repositories."
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
Understand what connects your repositories and what a change could break before you ship it.

[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
![Source version](https://img.shields.io/badge/source-v1.0.0-orange.svg)
![Source version](https://img.shields.io/badge/source-v1.0.1-orange.svg)
[![crates.io](https://img.shields.io/crates/v/code-system-graph.svg)](https://crates.io/crates/code-system-graph)
![Platform](https://img.shields.io/badge/validated-Linux%20x86__64-1793d1.svg)
![Platforms](https://img.shields.io/badge/validated-Linux%20%7C%20macOS%20%7C%20Windows-1793d1.svg)
![Privacy](https://img.shields.io/badge/privacy-local%20%7C%20no%20telemetry-2ea44f.svg)
![Agents](https://img.shields.io/badge/agents-5%20supported-7c3aed.svg)

Expand Down Expand Up @@ -122,8 +122,9 @@ validation matrix remains in [Extractor coverage](docs/EXTRACTOR_COVERAGE.md).

### 1. Install

Linux x86_64 is the validated installation target for prebuilt binaries and release archives. See
[Installation](docs/INSTALLATION.md) for platform status, verification, upgrades, and uninstall.
Prebuilt binaries are available for Linux x86_64/ARM64, macOS x86_64/ARM64, and Windows x86_64.
See [Installation](docs/INSTALLATION.md) for platform status, verification, upgrades, and
uninstall.

**Recommended: prebuilt binaries with cargo-binstall** (no Rust compiler required):

Expand Down Expand Up @@ -311,7 +312,7 @@ Where is coverage incomplete or stale?
- one or more local repository directories;
- a `code-system-graph.yaml` manifest with a workspace name and repository paths;
- a writable path for the embedded SQLite database;
- Linux x86_64 for the currently validated installation path.
- Linux x86_64/ARM64, macOS x86_64/ARM64, or Windows x86_64 for a prebuilt installation.

### Optional

Expand Down
12 changes: 6 additions & 6 deletions crates/code-system-graph-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ atomic-write-file = "0.3.0"
axum = "0.8.9"
clap = { version = "4.6.4", features = ["derive"] }
clap_complete = "4.6.8"
code-system-graph-core = { version = "1.0.0", path = "../code-system-graph-core" }
code-system-graph-hooks = { version = "1.0.0", path = "../code-system-graph-hooks" }
code-system-graph-model = { version = "1.0.0", path = "../code-system-graph-model" }
code-system-graph-store-sqlite = { version = "1.0.0", path = "../code-system-graph-store-sqlite" }
code-system-graph-core = { version = "1.0.1", path = "../code-system-graph-core" }
code-system-graph-hooks = { version = "1.0.1", path = "../code-system-graph-hooks" }
code-system-graph-model = { version = "1.0.1", path = "../code-system-graph-model" }
code-system-graph-store-sqlite = { version = "1.0.1", path = "../code-system-graph-store-sqlite" }
rmcp = { version = "3.1.0", features = ["transport-io"] }
notify = "8.2.0"
rusqlite = "0.40.1"
rusqlite = { version = "0.40.1", features = ["bundled"] }
schemars = "1.2.2"
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
Expand Down Expand Up @@ -64,5 +64,5 @@ workspace = true

[dev-dependencies]
reqwest = { version = "0.13.4", default-features = false, features = ["json", "rustls"] }
rusqlite = "0.40.1"
rusqlite = { version = "0.40.1", features = ["bundled"] }
tempfile = "3.27.0"
11 changes: 11 additions & 0 deletions crates/code-system-graph-cli/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Target-specific linker configuration for the `csgraph` executable.

fn main() {
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows")
&& std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc")
{
// MSVC executables default to a 1 MiB main stack, which is insufficient for the
// synchronous extraction worker entered from the async CLI dispatcher.
println!("cargo:rustc-link-arg-bin=csgraph=/STACK:8388608");
}
}
3 changes: 2 additions & 1 deletion crates/code-system-graph-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5995,14 +5995,15 @@ mod budget_regression_tests {
"version: 1\nname: source-budget-unit\nextractionBudgets:\n maxIdentifierBytesPerValue: 3\nrepos:\n api:\n path: api\n",
)
.expect("manifest fixture");
let expected_artifact = encode_native_path(&Path::new("src").join("routes.rs")).display;

let result = scan_workspace_direct(&config, &database, &ScanOverrides::default());
assert!(
matches!(
&result,
Err(ApplicationError::ExtractionLimit(error))
if error.resource == code_system_graph_core::ExtractionResource::IdentifierBytesPerValue
&& error.artifact == "src/routes.rs"
&& error.artifact == expected_artifact
&& error.extractor == "code-system-graph.source.rust"
),
"unexpected direct source budget result: {result:?}"
Expand Down
35 changes: 35 additions & 0 deletions crates/code-system-graph-cli/src/work_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ impl WorkState {
pub(crate) fn open(database: &Path, database_instance_id: &str) -> Result<Self, String> {
let path = work_path(database);
ensure_private_file(&path)?;
let path = canonicalize_parent(&path)?;
ensure_safe_sqlite_siblings(&path)?;
match Self::open_existing(&path, database_instance_id) {
Ok(state) => Ok(state),
Expand Down Expand Up @@ -1005,6 +1006,22 @@ pub(crate) fn work_path(database: &Path) -> PathBuf {
PathBuf::from(value)
}

fn canonicalize_parent(path: &Path) -> Result<PathBuf, String> {
let parent = path
.parent()
.ok_or_else(|| format!("work sidecar path `{}` has no parent", path.display()))?;
let file_name = path
.file_name()
.ok_or_else(|| format!("work sidecar path `{}` has no file name", path.display()))?;
let canonical_parent = fs::canonicalize(parent).map_err(|error| {
format!(
"failed to canonicalize work sidecar parent `{}`: {error}",
parent.display()
)
})?;
Ok(canonical_parent.join(file_name))
}

fn classify_existing_sidecar_error(error: &rusqlite::Error) -> WorkOpenError {
match error {
rusqlite::Error::SqliteFailure(details, _)
Expand Down Expand Up @@ -1100,6 +1117,24 @@ mod tests {

use super::*;

#[cfg(unix)]
#[test]
fn work_state_should_open_below_symlinked_parent() {
use std::os::unix::fs::symlink;

let temporary = tempfile::tempdir().expect("temporary directory");
let canonical_parent = temporary.path().join("canonical");
fs::create_dir(&canonical_parent).expect("canonical parent");
let symlinked_parent = temporary.path().join("symlinked");
symlink(&canonical_parent, &symlinked_parent).expect("symlinked parent");

let database = symlinked_parent.join("graph.db");
let state = WorkState::open(&database, "database-instance").expect("work state");
drop(state);

assert!(canonical_parent.join("graph.db.work-v1.db").is_file());
}

fn fingerprint(hash: &str) -> ArtifactFingerprint {
ArtifactFingerprint {
repo_id: RepoId::new("repo:api"),
Expand Down
6 changes: 4 additions & 2 deletions crates/code-system-graph-cli/tests/extraction_budgets_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use code_system_graph::{
ApplicationError, ScanOverrides, scan_workspace, scan_workspace_with_overrides
};
use code_system_graph_core::{BatchPlanError, ExitCode, ExtractionResource};
use code_system_graph_core::{BatchPlanError, ExitCode, ExtractionResource, encode_native_path};
use code_system_graph_store_sqlite::SqliteStore;

fn manifest(max_work: Option<u64>) -> String {
Expand Down Expand Up @@ -60,14 +60,16 @@ fn configured_source_value_budget_should_apply_before_focused_observations() ->
&config,
"version: 1\nname: source-budget-e2e\nextractionBudgets:\n maxIdentifierBytesPerValue: 3\nrepos:\n api:\n path: api\n",
)?;
let expected_artifact =
encode_native_path(&std::path::Path::new("src").join("routes.rs")).display;

let result = scan_workspace(&config, &database);
assert!(
matches!(
&result,
Err(ApplicationError::ExtractionLimit(error))
if error.resource == ExtractionResource::IdentifierBytesPerValue
&& error.artifact == "src/routes.rs"
&& error.artifact == expected_artifact
&& error.extractor == "code-system-graph.source.rust"
),
"unexpected source budget result: {result:?}"
Expand Down
Loading