Skip to content

Commit e864794

Browse files
Murariclaude
andcommitted
Port final parity gaps: self-update, background SCIP, capabilities summary, disable Ubuntu CI
- Add platform_triple() and self_update() for direct binary download via gh release - Enhance cmd_update() to try binary download first, fall back to install script - Add spawn_scip_child_process() for background SCIP enrichment (non-blocking index) - Add print_capabilities_summary() post-install help text, wire into cmd_install() - Comment out Ubuntu builds in CI and release workflows (lbug/KuzuDB not yet building on Linux) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent be1d4a7 commit e864794

4 files changed

Lines changed: 238 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,29 @@ jobs:
2525
runs-on: ${{ matrix.os }}
2626
strategy:
2727
matrix:
28-
os: [ubuntu-latest, macos-latest, windows-latest]
28+
# os: [ubuntu-latest, macos-latest, windows-latest]
29+
os: [macos-latest, windows-latest]
2930
steps:
3031
- uses: actions/checkout@v4
3132
- uses: dtolnay/rust-toolchain@stable
3233
with:
3334
components: clippy
34-
- name: Install cmake
35-
if: runner.os == 'Linux'
36-
run: sudo apt-get update && sudo apt-get install -y cmake
35+
# - name: Install cmake
36+
# if: runner.os == 'Linux'
37+
# run: sudo apt-get update && sudo apt-get install -y cmake
3738
- run: cargo clippy --all-targets -- -D warnings
3839

3940
test:
4041
name: Test (${{ matrix.os }})
4142
runs-on: ${{ matrix.os }}
4243
strategy:
4344
matrix:
44-
os: [ubuntu-latest, macos-latest, windows-latest]
45+
# os: [ubuntu-latest, macos-latest, windows-latest]
46+
os: [macos-latest, windows-latest]
4547
steps:
4648
- uses: actions/checkout@v4
4749
- uses: dtolnay/rust-toolchain@stable
48-
- name: Install cmake
49-
if: runner.os == 'Linux'
50-
run: sudo apt-get update && sudo apt-get install -y cmake
50+
# - name: Install cmake
51+
# if: runner.os == 'Linux'
52+
# run: sudo apt-get update && sudo apt-get install -y cmake
5153
- run: cargo test --all

.github/workflows/release.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ jobs:
2323
- os: macos-13
2424
target: x86_64-apple-darwin
2525
archive: tar.gz
26-
- os: ubuntu-latest
27-
target: x86_64-unknown-linux-gnu
28-
archive: tar.gz
26+
# - os: ubuntu-latest
27+
# target: x86_64-unknown-linux-gnu
28+
# archive: tar.gz
2929
- os: windows-latest
3030
target: x86_64-pc-windows-msvc
3131
archive: zip
@@ -34,10 +34,9 @@ jobs:
3434
- uses: dtolnay/rust-toolchain@stable
3535
with:
3636
targets: ${{ matrix.target }}
37-
- name: Install cmake (Linux)
38-
if: runner.os == 'Linux'
39-
run: sudo apt-get update && sudo apt-get install -y cmake
40-
37+
# - name: Install cmake (Linux)
38+
# if: runner.os == 'Linux'
39+
# run: sudo apt-get update && sudo apt-get install -y cmake
4140
- name: Build
4241
run: cargo build --release --target ${{ matrix.target }} -p infigraph-cli -p infigraph-mcp
4342

crates/infigraph-cli/src/index.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,54 @@ pub(crate) fn cmd_index(root: &Path, full: bool, no_embed: bool) -> Result<()> {
8888
Err(e) => eprintln!("warning: document indexing failed: {e}"),
8989
}
9090

91-
// Auto-SCIP: detect languages and run available SCIP indexers
92-
auto_scip(root, &result, prism.store())?;
91+
// Drop prism to release the GraphStore handle before background SCIP
92+
let detected_languages: std::collections::HashSet<String> = result.extractions.iter()
93+
.map(|e| e.language.clone())
94+
.collect();
95+
drop(prism);
96+
97+
// SCIP enrichment in a detached child process — parent returns immediately.
98+
spawn_scip_child_process(root, &detected_languages);
9399

94100
Ok(())
95101
}
96102

103+
fn spawn_scip_child_process(root: &Path, detected_languages: &std::collections::HashSet<String>) {
104+
use crate::scip_download;
105+
106+
let indexers = scip_download::indexers_for_languages(detected_languages);
107+
if indexers.is_empty() { return; }
108+
109+
let count = indexers.len();
110+
let indexer_names: Vec<&str> = indexers.iter().map(|i| i.binary_name).collect();
111+
println!("SCIP enrichment starting in background ({count} indexer(s): {})...", indexer_names.join(", "));
112+
113+
let langs: String = detected_languages.iter().cloned().collect::<Vec<_>>().join(",");
114+
115+
let exe = match std::env::current_exe() {
116+
Ok(e) => e,
117+
Err(_) => return,
118+
};
119+
120+
let log_path = root.join(".infigraph").join("scip-enrich.log");
121+
let stderr_target = match std::fs::File::create(&log_path) {
122+
Ok(f) => std::process::Stdio::from(f),
123+
Err(_) => std::process::Stdio::null(),
124+
};
125+
126+
let _ = std::process::Command::new(exe)
127+
.arg("scip-enrich")
128+
.arg("--languages")
129+
.arg(&langs)
130+
.current_dir(root)
131+
.stdin(std::process::Stdio::null())
132+
.stdout(std::process::Stdio::null())
133+
.stderr(stderr_target)
134+
.spawn();
135+
136+
eprintln!(" Log: {}", log_path.display());
137+
}
138+
97139
pub(crate) fn on_path(cmd: &str) -> bool {
98140
let lookup = if cfg!(windows) { "where" } else { "which" };
99141
std::process::Command::new(lookup)

crates/infigraph-cli/src/install.rs

Lines changed: 178 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,7 @@ pub(crate) fn cmd_install() -> Result<()> {
7272
if configured.is_empty() {
7373
println!("No agents were configured.");
7474
} else {
75-
println!(
76-
"\nInstalled infigraph MCP server for {} agent(s): {}",
77-
configured.len(),
78-
configured.join(", ")
79-
);
75+
print_capabilities_summary(&configured);
8076
}
8177

8278
// Write primary search instructions to ~/.claude/CLAUDE.md
@@ -396,6 +392,163 @@ pub(crate) fn cmd_uninstall() -> Result<()> {
396392
Ok(())
397393
}
398394

395+
pub(crate) fn platform_triple() -> Result<(String, String, String)> {
396+
let os = std::env::consts::OS;
397+
let arch = std::env::consts::ARCH;
398+
let os_tag = match os {
399+
"macos" => "apple-darwin",
400+
"linux" => "unknown-linux-gnu",
401+
"windows" => "pc-windows-msvc",
402+
_ => anyhow::bail!("unsupported OS: {os}"),
403+
};
404+
let arch_tag = match arch {
405+
"x86_64" => "x86_64",
406+
"aarch64" => "aarch64",
407+
_ => anyhow::bail!("unsupported architecture: {arch}"),
408+
};
409+
let target = format!("{arch_tag}-{os_tag}");
410+
Ok((os_tag.to_string(), arch_tag.to_string(), target))
411+
}
412+
413+
pub(crate) fn self_update(version: &str) -> Result<()> {
414+
let os = std::env::consts::OS;
415+
let (_, _, target) = platform_triple()?;
416+
let archive_ext = if os == "windows" { "zip" } else { "tar.gz" };
417+
let asset_name = format!("infigraph-{target}.{archive_ext}");
418+
let tag = format!("v{version}");
419+
420+
let gh_host =
421+
std::env::var("INFIGRAPH_GH_HOST").unwrap_or_else(|_| "github.com".to_string());
422+
let gh_owner =
423+
std::env::var("INFIGRAPH_GH_OWNER").unwrap_or_else(|_| "intuit".to_string());
424+
let gh_repo = "infigraph";
425+
let full_repo = format!("{gh_host}/{gh_owner}/{gh_repo}");
426+
427+
println!("Downloading {asset_name} from release {tag}...");
428+
429+
let install_dir = std::env::var("INFIGRAPH_INSTALL_DIR")
430+
.map(PathBuf::from)
431+
.unwrap_or_else(|_| {
432+
dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(".local").join("bin")
433+
});
434+
435+
let tmp_dir = std::env::temp_dir();
436+
let download_path = tmp_dir.join(&asset_name);
437+
438+
let mut gh_args = vec![
439+
"release".to_string(), "download".to_string(), tag.clone(),
440+
"--repo".to_string(), format!("{gh_owner}/{gh_repo}"),
441+
"--pattern".to_string(), asset_name.clone(),
442+
"--dir".to_string(), tmp_dir.to_string_lossy().to_string(),
443+
"--clobber".to_string(),
444+
];
445+
if gh_host != "github.com" {
446+
gh_args.push("--hostname".to_string());
447+
gh_args.push(gh_host.clone());
448+
}
449+
450+
let status = std::process::Command::new("gh")
451+
.args(&gh_args)
452+
.status()
453+
.context("failed to run `gh release download`")?;
454+
455+
if !status.success() {
456+
anyhow::bail!("download failed for {asset_name} in release {tag} from {full_repo}");
457+
}
458+
459+
std::fs::create_dir_all(&install_dir)?;
460+
461+
let bin_suffix = if os == "windows" { ".exe" } else { "" };
462+
for bin in &["infigraph", "infigraph-mcp", "lsp-to-scip"] {
463+
let bin_path = install_dir.join(format!("{bin}{bin_suffix}"));
464+
let old_path = install_dir.join(format!("{bin}{bin_suffix}.old"));
465+
if bin_path.exists() {
466+
let _ = std::fs::remove_file(&old_path);
467+
let _ = std::fs::rename(&bin_path, &old_path);
468+
}
469+
}
470+
471+
if archive_ext == "zip" {
472+
let status = std::process::Command::new("unzip")
473+
.args(["-o", &download_path.to_string_lossy(), "-d", &install_dir.to_string_lossy()])
474+
.status()?;
475+
if !status.success() {
476+
anyhow::bail!("failed to extract zip");
477+
}
478+
} else {
479+
let status = std::process::Command::new("tar")
480+
.args(["-xzf", &download_path.to_string_lossy(), "-C", &install_dir.to_string_lossy()])
481+
.status()?;
482+
if !status.success() {
483+
anyhow::bail!("failed to extract tar.gz");
484+
}
485+
}
486+
487+
let _ = std::fs::remove_file(&download_path);
488+
489+
for bin in &["infigraph", "infigraph-mcp", "lsp-to-scip"] {
490+
let _ = std::fs::remove_file(install_dir.join(format!("{bin}{bin_suffix}.old")));
491+
}
492+
493+
if os == "macos" {
494+
for bin in &["infigraph", "infigraph-mcp", "lsp-to-scip"] {
495+
let _ = std::process::Command::new("xattr")
496+
.args(["-dr", "com.apple.quarantine", &install_dir.join(bin).to_string_lossy()])
497+
.status();
498+
}
499+
}
500+
501+
if let Some(cache_path) = update_cache_path() {
502+
let _ = std::fs::remove_file(&cache_path);
503+
}
504+
505+
println!("Installed v{version} to {}", install_dir.display());
506+
Ok(())
507+
}
508+
509+
pub(crate) fn print_capabilities_summary(configured: &[&str]) {
510+
let version = env!("CARGO_PKG_VERSION");
511+
let count = configured.len();
512+
let agents = configured.join(", ");
513+
514+
println!();
515+
println!("Infigraph v{version} installed for {count} agent(s): {agents}");
516+
println!();
517+
println!("What you can do now:");
518+
println!();
519+
println!(" Index & Search");
520+
println!(" infigraph index Index your codebase (code + docs)");
521+
println!(" infigraph search \"query\" Hybrid BM25 + semantic search");
522+
println!(" infigraph search-docs \"q\" Search indexed documents");
523+
println!();
524+
println!(" Analysis");
525+
println!(" infigraph dead-code Find unreachable functions");
526+
println!(" infigraph security Scan for vulnerabilities (30+ patterns)");
527+
println!(" infigraph complexity Cyclomatic complexity hotspots");
528+
println!(" infigraph check CI quality gate (exit non-zero on violations)");
529+
println!(" infigraph review AI-powered PR review");
530+
println!(" infigraph vulns OSV vulnerability scanning");
531+
println!();
532+
println!(" Code Navigation");
533+
println!(" infigraph impact <symbol> Blast radius of a change");
534+
println!(" infigraph routes Detect HTTP/gRPC endpoints");
535+
println!(" infigraph cluster Detect functional modules");
536+
println!(" infigraph architecture Codebase overview");
537+
println!(" infigraph refs <symbol> Find all references");
538+
println!();
539+
println!(" Visualization");
540+
println!(" infigraph visualize Interactive graph in browser");
541+
println!(" infigraph viz-sym <symbol> Focused subgraph for one symbol");
542+
println!();
543+
println!(" Multi-Repo");
544+
println!(" infigraph group create <name> Create a service group");
545+
println!(" infigraph group link Link cross-service calls");
546+
println!();
547+
println!(" Get started:");
548+
println!(" cd your-project && infigraph init");
549+
println!();
550+
}
551+
399552
pub(crate) fn update_cache_path() -> Option<PathBuf> {
400553
dirs::home_dir().map(|h| h.join(".infigraph").join("update_check.json"))
401554
}
@@ -492,9 +645,26 @@ pub(crate) fn print_update_hint(handle: Option<std::thread::JoinHandle<()>>) {
492645
}
493646

494647
pub(crate) fn cmd_update() -> Result<()> {
495-
println!("Updating infigraph...");
496-
println!("Downloading latest install script and running it.");
497-
println!("This will fetch the latest binary and re-register MCP configs.\n");
648+
let current = env!("CARGO_PKG_VERSION");
649+
650+
// Try direct binary download via gh release if a new version is available
651+
if let Some(latest) = fetch_latest_version() {
652+
if version_newer(&latest, current) {
653+
println!("Updating infigraph: v{current} → v{latest}");
654+
match self_update(&latest) {
655+
Ok(()) => return Ok(()),
656+
Err(e) => {
657+
eprintln!("Binary update failed ({e}), falling back to install script...");
658+
}
659+
}
660+
} else {
661+
println!("Already at latest version v{current}.");
662+
return Ok(());
663+
}
664+
}
665+
666+
// Fallback: install script
667+
println!("Downloading latest install script and running it.\n");
498668

499669
let gh_host =
500670
std::env::var("INFIGRAPH_GH_HOST").unwrap_or_else(|_| "github.com".to_string());

0 commit comments

Comments
 (0)