diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5bb76a..f52cefc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,9 +40,19 @@ 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 @@ -50,8 +60,9 @@ jobs: 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 @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17dadbd..b377d60 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 @@ -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 @@ -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: diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc290e..4350fc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/Cargo.lock b/Cargo.lock index 4255a95..d1568a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -398,7 +398,7 @@ dependencies = [ [[package]] name = "code-system-graph" -version = "1.0.0" +version = "1.0.1" dependencies = [ "anyhow", "atomic-write-file", @@ -431,7 +431,7 @@ dependencies = [ [[package]] name = "code-system-graph-core" -version = "1.0.0" +version = "1.0.1" dependencies = [ "async-trait", "atomic-write-file", @@ -478,7 +478,7 @@ dependencies = [ [[package]] name = "code-system-graph-hooks" -version = "1.0.0" +version = "1.0.1" dependencies = [ "atomic-write-file", "blake3", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "code-system-graph-model" -version = "1.0.0" +version = "1.0.1" dependencies = [ "blake3", "camino", @@ -505,7 +505,7 @@ dependencies = [ [[package]] name = "code-system-graph-store-sqlite" -version = "1.0.0" +version = "1.0.1" dependencies = [ "blake3", "code-system-graph-model", diff --git a/Cargo.toml b/Cargo.toml index 1102493..cfcaacb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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." diff --git a/README.md b/README.md index 97d7cda..d3bff12 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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): @@ -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 diff --git a/crates/code-system-graph-cli/Cargo.toml b/crates/code-system-graph-cli/Cargo.toml index e94d99e..aeb3260 100644 --- a/crates/code-system-graph-cli/Cargo.toml +++ b/crates/code-system-graph-cli/Cargo.toml @@ -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" @@ -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" diff --git a/crates/code-system-graph-cli/build.rs b/crates/code-system-graph-cli/build.rs new file mode 100644 index 0000000..2dae583 --- /dev/null +++ b/crates/code-system-graph-cli/build.rs @@ -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"); + } +} diff --git a/crates/code-system-graph-cli/src/lib.rs b/crates/code-system-graph-cli/src/lib.rs index 703efd3..1d08adb 100644 --- a/crates/code-system-graph-cli/src/lib.rs +++ b/crates/code-system-graph-cli/src/lib.rs @@ -5995,6 +5995,7 @@ 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!( @@ -6002,7 +6003,7 @@ mod budget_regression_tests { &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:?}" diff --git a/crates/code-system-graph-cli/src/work_state.rs b/crates/code-system-graph-cli/src/work_state.rs index 7da0a8d..e75ddb6 100644 --- a/crates/code-system-graph-cli/src/work_state.rs +++ b/crates/code-system-graph-cli/src/work_state.rs @@ -80,6 +80,7 @@ impl WorkState { pub(crate) fn open(database: &Path, database_instance_id: &str) -> Result { 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), @@ -1005,6 +1006,22 @@ pub(crate) fn work_path(database: &Path) -> PathBuf { PathBuf::from(value) } +fn canonicalize_parent(path: &Path) -> Result { + 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, _) @@ -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"), diff --git a/crates/code-system-graph-cli/tests/extraction_budgets_e2e.rs b/crates/code-system-graph-cli/tests/extraction_budgets_e2e.rs index f9d2d95..543cb8d 100644 --- a/crates/code-system-graph-cli/tests/extraction_budgets_e2e.rs +++ b/crates/code-system-graph-cli/tests/extraction_budgets_e2e.rs @@ -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) -> String { @@ -60,6 +60,8 @@ 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!( @@ -67,7 +69,7 @@ fn configured_source_value_budget_should_apply_before_focused_observations() -> &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:?}" diff --git a/crates/code-system-graph-cli/tests/http_server_e2e.rs b/crates/code-system-graph-cli/tests/http_server_e2e.rs index 31d44ee..d52bcaf 100644 --- a/crates/code-system-graph-cli/tests/http_server_e2e.rs +++ b/crates/code-system-graph-cli/tests/http_server_e2e.rs @@ -23,6 +23,27 @@ use tokio_util::sync::CancellationToken; const TEST_TOKEN: &str = "http-test-token"; static FIXTURE_COUNTER: AtomicU64 = AtomicU64::new(0); +fn is_early_body_rejection(error: &(dyn std::error::Error + 'static)) -> bool { + let mut current = Some(error); + while let Some(source) = current { + if source + .downcast_ref::() + .is_some_and(|io_error| { + matches!( + io_error.kind(), + std::io::ErrorKind::BrokenPipe + | std::io::ErrorKind::ConnectionAborted + | std::io::ErrorKind::ConnectionReset + ) + }) + { + return true; + } + current = source.source(); + } + false +} + struct Fixture { temporary: TempDir, manifest: PathBuf, @@ -389,7 +410,8 @@ async fn explore_route_should_reject_disabled_codegraph_before_execution() -> an #[tokio::test] async fn tool_request_should_reject_body_larger_than_one_mibibyte() -> anyhow::Result<()> { let server = RunningServer::start(None).await?; - let response = Client::new() + let client = Client::new(); + let response = client .post(server.url("/v1/tools/query")) .json(&json!({ "query": "x".repeat(1024 * 1024), @@ -401,12 +423,27 @@ async fn tool_request_should_reject_body_larger_than_one_mibibyte() -> anyhow::R "limit": 1 })) .send() - .await?; - let (status, body) = response_json(response).await?; - + .await; + match response { + Ok(response) => { + let (status, body) = response_json(response).await?; + assert_eq!( + (status, body["data"]["code"].as_str()), + (StatusCode::PAYLOAD_TOO_LARGE, Some("payload_too_large")) + ); + } + Err(error) => { + // Some kernels reset a connection when the server rejects the declared oversized body + // before the client finishes writing it. + assert!( + is_early_body_rejection(&error), + "unexpected oversized-body transport error: {error:#}" + ); + } + } assert_eq!( - (status, body["data"]["code"].as_str()), - (StatusCode::PAYLOAD_TOO_LARGE, Some("payload_too_large")) + client.get(server.url("/health")).send().await?.status(), + StatusCode::OK ); server.stop().await } @@ -452,8 +489,17 @@ async fn cancellation_should_stop_accepting_connections() -> anyhow::Result<()> .timeout(Duration::from_millis(500)) .build()?; + let startup_deadline = tokio::time::Instant::now() + Duration::from_secs(2); + let mut accepted_before_cancellation = false; + while tokio::time::Instant::now() < startup_deadline { + if server_serves_workspace(&client, address, &workspace).await { + accepted_before_cancellation = true; + break; + } + tokio::time::sleep(Duration::from_millis(25)).await; + } assert!( - server_serves_workspace(&client, address, &workspace).await, + accepted_before_cancellation, "server should accept connections before cancellation" ); diff --git a/crates/code-system-graph-cli/tests/ignore_policy_e2e.rs b/crates/code-system-graph-cli/tests/ignore_policy_e2e.rs index e4d28f8..cc82a4b 100644 --- a/crates/code-system-graph-cli/tests/ignore_policy_e2e.rs +++ b/crates/code-system-graph-cli/tests/ignore_policy_e2e.rs @@ -1,6 +1,7 @@ //! Acceptance coverage for configurable native discovery exclusions. use code_system_graph::scan_workspace; +use code_system_graph_core::encode_native_path; use code_system_graph_store_sqlite::SqliteStore; #[test] @@ -41,17 +42,19 @@ fn scan_should_apply_excludes_reopen_defaults_and_keep_explicit_artifacts() -> a let paths = SqliteStore::open_read_only(&database)? .load_current_artifact_fingerprints("ignored")? .into_iter() - .map(|fingerprint| fingerprint.path.display) + .map(|fingerprint| fingerprint.path) .collect::>(); + let native_path = + |relative: &str| encode_native_path(&relative.split('/').collect::()); assert_eq!( ( - paths.contains(&"src/lib.rs".to_owned()), - paths.contains(&"coverage/missed.rs".to_owned()), - paths.contains(&"generated/output/hidden.rs".to_owned()), - paths.contains(&"vendor/internal-sdk/src/lib.rs".to_owned()), - paths.contains(&"vendor/external/src/lib.rs".to_owned()), - paths.contains(&"vendor/contracts/openapi.yaml".to_owned()), + paths.contains(&native_path("src/lib.rs")), + paths.contains(&native_path("coverage/missed.rs")), + paths.contains(&native_path("generated/output/hidden.rs")), + paths.contains(&native_path("vendor/internal-sdk/src/lib.rs")), + paths.contains(&native_path("vendor/external/src/lib.rs")), + paths.contains(&native_path("vendor/contracts/openapi.yaml")), ), (true, false, false, true, false, true) ); diff --git a/crates/code-system-graph-cli/tests/security_e2e.rs b/crates/code-system-graph-cli/tests/security_e2e.rs index ada5ecb..fab4725 100644 --- a/crates/code-system-graph-cli/tests/security_e2e.rs +++ b/crates/code-system-graph-cli/tests/security_e2e.rs @@ -81,12 +81,17 @@ fn non_utf8_artifact_should_degrade_without_aborting_workspace_scan() -> anyhow: let first = scan_workspace(&manifest, &database)?; let second = scan_workspace(&manifest, &database)?; + let expected_artifact = ["db", "schema.sql"] + .iter() + .collect::() + .display() + .to_string(); assert!(!first.reused_snapshot); assert!(second.reused_snapshot); assert_eq!(first.discovered_input_count, 1); assert!(first.degradations.iter().any(|message| { - message.contains("db/schema.sql") + message.contains(&expected_artifact) && message.contains("invalid UTF-8") && message.contains("incomplete") })); diff --git a/crates/code-system-graph-core/Cargo.toml b/crates/code-system-graph-core/Cargo.toml index 80bb72b..d21b7b3 100644 --- a/crates/code-system-graph-core/Cargo.toml +++ b/crates/code-system-graph-core/Cargo.toml @@ -22,7 +22,7 @@ libc = "0.2" nix = { version = "0.31.3", features = ["fs"] } proto-parser = "1.14.3" pulldown-cmark = "0.13.4" -code-system-graph-model = { version = "1.0.0", path = "../code-system-graph-model" } +code-system-graph-model = { version = "1.0.1", path = "../code-system-graph-model" } reqwest = { version = "0.13.4", default-features = false, features = ["json", "rustls"] } rmcp = { version = "3.1.0", default-features = false, features = [ "client", diff --git a/crates/code-system-graph-core/src/capability_dir.rs b/crates/code-system-graph-core/src/capability_dir.rs index aa754b1..81b0761 100644 --- a/crates/code-system-graph-core/src/capability_dir.rs +++ b/crates/code-system-graph-core/src/capability_dir.rs @@ -1,7 +1,9 @@ //! Capability-scoped directory access that never follows symbolic links. use std::ffi::OsStr; -use std::fs::{self, File}; +use std::fs; +#[cfg(unix)] +use std::fs::File; use std::io::{Read, Write}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; @@ -636,7 +638,10 @@ fn read_file_bounded_portable( limit: max_bytes, }); } - let mut file = fs::File::open(&path).map_err(|source| CapabilityError::Io { path, source })?; + let file = fs::File::open(&path).map_err(|source| CapabilityError::Io { + path: path.clone(), + source, + })?; read_file_to_end_bounded(file, &path, max_bytes) } diff --git a/crates/code-system-graph-core/src/config.rs b/crates/code-system-graph-core/src/config.rs index 828eab4..a845475 100644 --- a/crates/code-system-graph-core/src/config.rs +++ b/crates/code-system-graph-core/src/config.rs @@ -754,6 +754,8 @@ mod tests { std::fs::write(&outside, "version: 1\n")?; #[cfg(unix)] std::os::unix::fs::symlink(&outside, repository.join(".code-system-graph.yaml"))?; + #[cfg(windows)] + std::os::windows::fs::symlink_file(&outside, repository.join(".code-system-graph.yaml"))?; let workspace = RepositoryConfig { path: ".".to_owned(), openapi: None, diff --git a/crates/code-system-graph-hooks/src/managed_root.rs b/crates/code-system-graph-hooks/src/managed_root.rs index 1c724c2..881a803 100644 --- a/crates/code-system-graph-hooks/src/managed_root.rs +++ b/crates/code-system-graph-hooks/src/managed_root.rs @@ -1,7 +1,9 @@ //! Capability-scoped repository root used for marker-owned host file installation. use std::ffi::OsStr; -use std::fs::{self, File}; +use std::fs; +#[cfg(unix)] +use std::fs::File; use std::io::{Read, Write}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; @@ -810,6 +812,7 @@ mod tests { let managed = ManagedRoot::open(&root)?; for relative in [ + #[cfg(not(windows))] Path::new(".code-system-graph/hooks/bad\x1bname"), Path::new(".code-system-graph/hooks/safe\u{202e}evil"), ] { diff --git a/crates/code-system-graph-store-sqlite/Cargo.toml b/crates/code-system-graph-store-sqlite/Cargo.toml index 4956601..557bf9b 100644 --- a/crates/code-system-graph-store-sqlite/Cargo.toml +++ b/crates/code-system-graph-store-sqlite/Cargo.toml @@ -13,7 +13,7 @@ categories = ["database", "development-tools"] [dependencies] blake3 = "1.8.5" -code-system-graph-model = { version = "1.0.0", path = "../code-system-graph-model" } +code-system-graph-model = { version = "1.0.1", path = "../code-system-graph-model" } rusqlite = { version = "0.40.1", features = ["backup", "bundled"] } same-file = "1.0.6" serde_json = "1.0.151" diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 5e618e3..a3fa653 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -5,10 +5,10 @@ for maintainers are in [Release engineering](RELEASE.md). ## Current availability -Code System Graph `1.0.0` is published on [crates.io](https://crates.io/crates/code-system-graph) -and [GitHub Releases](https://github.com/dertin/code-system-graph/releases). Linux x86_64 is the -only platform validated locally for build, tests, package lifecycle, and uninstall. Workflows exist -for other targets, but configured CI is not evidence that those platforms pass. +Code System Graph `1.0.1` is published on [crates.io](https://crates.io/crates/code-system-graph) +and [GitHub Releases](https://github.com/dertin/code-system-graph/releases). Native release CI +validates Linux x86_64/ARM64, macOS x86_64/ARM64, and Windows x86_64 before their archives are +published. ## Install with cargo-binstall (recommended) @@ -16,7 +16,7 @@ Requirements: - Cargo; - [cargo-binstall](https://github.com/cargo-bins/cargo-binstall); -- Linux x86_64 for the current prebuilt release archive. +- a supported target: Linux x86_64/ARM64, macOS x86_64/ARM64, or Windows x86_64. No Rust compiler is required. cargo-binstall downloads the official release archive declared by the crate metadata and installs: @@ -84,10 +84,18 @@ For a Linux x86_64 archive: ```bash sha256sum --ignore-missing --check SHA256SUMS -tar -xzf code-system-graph-x86_64-unknown-linux-gnu-v1.0.0.tgz -PREFIX="$HOME/.local" ./code-system-graph-x86_64-unknown-linux-gnu-v1.0.0/install.sh +tar -xzf code-system-graph-x86_64-unknown-linux-gnu-v1.0.1.tgz +PREFIX="$HOME/.local" ./code-system-graph-x86_64-unknown-linux-gnu-v1.0.1/install.sh ``` +Replace the target in the archive name with `x86_64-apple-darwin`, +`aarch64-apple-darwin`, or `aarch64-unknown-linux-gnu` as appropriate. Unix archives include the +same installer. + +The Windows archive is a ZIP file. Verify `SHA256SUMS`, extract +`code-system-graph-x86_64-pc-windows-msvc-v1.0.1.zip`, and add its `bin` directory containing +`csgraph.exe` and `code-system-graph-hooks.exe` to `PATH`. + `PREFIX` defaults to `$HOME/.local`. The installer places binaries under `$PREFIX/bin`, installed documentation under `$PREFIX/share/doc/code-system-graph`, and a private ownership manifest under `$PREFIX/share/code-system-graph`. @@ -145,7 +153,7 @@ The package installer preserves replaced binaries under: $PREFIX/share/code-system-graph/backups/ ``` -The 1.0.0 release supports one exact initial database schema. An incompatible local database is +The 1.0.x release line supports one exact initial database schema. An incompatible local database is disposable: remove it and run a full scan. Backup and restore accept only that exact schema and never migrate it. @@ -163,7 +171,7 @@ cargo uninstall code-system-graph-hooks Run `uninstall.sh` from the verified extracted package with the same prefix: ```bash -PREFIX="$HOME/.local" ./code-system-graph-x86_64-unknown-linux-gnu-v1.0.0/uninstall.sh +PREFIX="$HOME/.local" ./code-system-graph-x86_64-unknown-linux-gnu-v1.0.1/uninstall.sh ``` Before uninstalling either installation type, remove any optional agent hooks: @@ -185,10 +193,10 @@ Delete workspace data separately only after confirming that it is no longer need | Platform | Status | | --- | --- | -| Linux x86_64 | Locally validated for build, tests, package lifecycle, binstall, and uninstall | -| Linux ARM64 | Workflow configured; not validated on a release host | -| macOS x86_64 / ARM64 | Workflow configured; not validated | -| Windows x86_64 | Workflow configured; no validated native installer | +| Linux x86_64 | Native CI validates build, tests, archive lifecycle, binstall, and uninstall | +| Linux ARM64 | Native CI validates build, tests, and archive contents | +| macOS x86_64 / ARM64 | Native CI validates build, serial tests, archive install, and uninstall | +| Windows x86_64 | Native CI validates build, serial tests, ZIP contents, and binary startup | | Windows ARM64 | Not in the current release workflow | See [Release engineering](RELEASE.md) for the evidence and publication requirements behind this diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 3bceb4c..c89bbe4 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -1,31 +1,29 @@ -# Code System Graph 1.0.0 Release +# Code System Graph 1.0.1 Release -Code System Graph 1.0.0 is the first public release line. The source tree and package version are `1.0.0`. -Release validation has completed locally on native Linux x86_64. Continuous integration runs on -GitHub at `https://github.com/dertin/code-system-graph`. +Code System Graph 1.0.1 is the first cross-platform maintenance release. The source tree and +package version are `1.0.1`. Continuous integration runs on GitHub at +`https://github.com/dertin/code-system-graph`. -Platform claims below distinguish completed local evidence from configured but not yet validated -targets. Linux x86_64 is the only target with native build, test, packaging, and lifecycle evidence -today. +Platform claims below require native build, test, packaging, and archive-smoke evidence from the +release workflow. ## Platform validation - Linux x86_64 (`x86_64-unknown-linux-gnu`): locally validated for build, test, scale workloads, package creation, checksum and SBOM generation, installation, repeated installation, and uninstall. -- Linux ARM64 (`aarch64-unknown-linux-gnu`): workflow coverage is configured but has not been - executed on a native release host. -- macOS x86_64 and ARM64: workflow coverage is configured but has not been executed. Native macOS - build, test, package, and lifecycle validation must pass before macOS support is published. -- Windows x86_64: workflow coverage is configured but has not been executed. Native Windows build, - test, packaging, and installation validation must pass before Windows support is published. The - POSIX shell installer is not a native Windows installer. +- Linux ARM64 (`aarch64-unknown-linux-gnu`): native CI validates build, tests, and archive contents. +- macOS x86_64 and ARM64: native CI validates build, serial tests, archive installation, repeated + installation, and uninstall. +- Windows x86_64: native CI validates build, serial tests, ZIP contents, and binary startup. + `cargo-binstall` installs the ZIP directly; the POSIX shell installer is not a Windows installer. -Linux x86_64 evidence does not imply support or performance characteristics on another target. +Performance evidence remains Linux x86_64-specific and does not imply characteristics on another +target. ## Included capabilities -Code System Graph 1.0.0 includes: +Code System Graph 1.0.x includes: - multi-repository workspace registration with lossless native-path identity; - incremental, source-free extraction for package, HTTP, event, GraphQL, RPC, data, @@ -83,8 +81,8 @@ GNU tar, and SHA-256 tooling. The workspace MSRV remains 1.97.1 and is validated ```text SOURCE_DATE_EPOCH=0 scripts/package-release.sh x86_64-unknown-linux-gnu -scripts/smoke-install.sh dist/code-system-graph-x86_64-unknown-linux-gnu-v1.0.0 -sha256sum --check dist/code-system-graph-x86_64-unknown-linux-gnu-v1.0.0.sha256 +scripts/smoke-install.sh dist/code-system-graph-x86_64-unknown-linux-gnu-v1.0.1 +sha256sum --check dist/code-system-graph-x86_64-unknown-linux-gnu-v1.0.1.sha256 ``` The package contains `csgraph`, `code-system-graph-hooks`, public documentation, license and notice files, @@ -117,7 +115,7 @@ clean `main` branch aligned with `origin/main`, Cargo credentials for crates.io, selects `prepare`: ```text -.github/workflows/release.sh 1.0.0 prepare +.github/workflows/release.sh 1.0.1 prepare ``` Preparation runs the complete publish-readiness suite and dry-runs all five packages without @@ -125,7 +123,7 @@ creating a tag, publishing a crate, or dispatching a workflow. To perform the ir pass `publish` explicitly: ```text -.github/workflows/release.sh 1.0.0 publish +.github/workflows/release.sh 1.0.1 publish ``` Publish mode verifies that the workspace repository matches `origin`, creates and pushes the diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 43345cc..ff3f399 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,8 +12,8 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4.13" -code-system-graph-core = { version = "1.0.0", path = "../crates/code-system-graph-core" } -code-system-graph-model = { version = "1.0.0", path = "../crates/code-system-graph-model" } +code-system-graph-core = { version = "1.0.1", path = "../crates/code-system-graph-core" } +code-system-graph-model = { version = "1.0.1", path = "../crates/code-system-graph-model" } serde_json = "1.0.151" [[bin]] diff --git a/scripts/smoke-install.sh b/scripts/smoke-install.sh index 1f46f8c..c9ab816 100755 --- a/scripts/smoke-install.sh +++ b/scripts/smoke-install.sh @@ -6,6 +6,8 @@ if [[ $# -ne 1 ]]; then exit 2 fi +ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION="$(awk -F '"' '/^version = / { print $2; exit }' "$ROOT/Cargo.toml")" SOURCE="$(cd -- "$1" && pwd)" PREFIX="$(mktemp -d)" trap 'rm -rf -- "$PREFIX"' EXIT @@ -15,7 +17,7 @@ FIRST="$("$PREFIX/bin/csgraph" --version)" PREFIX="$PREFIX" "$SOURCE/install.sh" SECOND="$("$PREFIX/bin/csgraph" --version)" -[[ "$FIRST" == "csgraph 1.0.0" ]] +[[ "$FIRST" == "csgraph $VERSION" ]] [[ "$SECOND" == "$FIRST" ]] [[ -x "$PREFIX/bin/code-system-graph-hooks" ]] [[ -f "$PREFIX/share/code-system-graph/install-manifest-v1.txt" ]] diff --git a/scripts/validate-publish-ready.sh b/scripts/validate-publish-ready.sh index 3cb369c..1620564 100755 --- a/scripts/validate-publish-ready.sh +++ b/scripts/validate-publish-ready.sh @@ -3,7 +3,8 @@ set -euo pipefail ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" TARGET="$(rustc -vV | awk '/^host:/ { print $2 }')" -PACKAGE_DIR="$ROOT/dist/code-system-graph-$TARGET-v1.0.0" +VERSION="$(awk -F '"' '/^version = / { print $2; exit }' "$ROOT/Cargo.toml")" +PACKAGE_DIR="$ROOT/dist/code-system-graph-$TARGET-v$VERSION" cd "$ROOT" @@ -50,4 +51,4 @@ then exit 1 fi -printf 'Code System Graph 1.0.0 publish-readiness validation passed\n' +printf 'Code System Graph %s publish-readiness validation passed\n' "$VERSION"