Skip to content
Open
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
64 changes: 57 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ on:
workflow_dispatch:

permissions:
actions: read
actions: write
contents: read
packages: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -56,7 +57,7 @@ jobs:
- platform: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
bundles: "appimage deb"
- platform: windows-latest
- platform: windows-2022
target: x86_64-pc-windows-msvc
bundles: "msi nsis"

Expand Down Expand Up @@ -106,8 +107,14 @@ jobs:
- name: Install dependencies (Linux)
if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm'
run: |
# LLVM repo for libomp matching icebug prebuilt binaries (compiled with clang-20)
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.asc
echo "deb http://apt.llvm.org/noble llvm-toolchain-noble-20 main" | sudo tee /etc/apt/sources.list.d/llvm20.list
# Apache Arrow repo
wget https://apache.jfrog.io/artifactory/arrow/ubuntu/apache-arrow-apt-source-latest-noble.deb
sudo apt-get install -y ./apache-arrow-apt-source-latest-noble.deb
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libgirepository1.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev patchelf librsvg2-dev libfuse2
sudo apt-get install -y libgtk-3-dev libgirepository1.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev patchelf librsvg2-dev libfuse2 libarrow-dev libomp-20-dev

- name: Install frontend dependencies
run: npm ci
Expand Down Expand Up @@ -181,8 +188,8 @@ jobs:
env:
# Needed for linuxdeploy AppImage bundling on CI runners
APPIMAGE_EXTRACT_AND_RUN: "1"
# Ensure linuxdeploy can resolve and bundle liblbug.so
LD_LIBRARY_PATH: ${{ github.workspace }}/src-tauri/liblbug
# Ensure linuxdeploy can resolve and bundle liblbug.so and libnetworkit.so
LD_LIBRARY_PATH: ${{ github.workspace }}/src-tauri/liblbug:${{ github.workspace }}/src-tauri/icebug/lib
NO_STRIP: "true" # https://github.com/tauri-apps/tauri/issues/14796

- name: Build Tauri app (macOS)
Expand All @@ -191,9 +198,52 @@ jobs:
env:
CXXFLAGS: -I/opt/homebrew/opt/libomp/include

- name: Create vcpkg manifest (Windows)
if: matrix.platform == 'windows-2022'
shell: bash
run: |
cat > vcpkg.json << 'EOF'
{
"name": "bugscope-deps",
"version": "1.0.0",
"dependencies": [
"arrow",
"pkgconf"
]
}
EOF

- name: Setup vcpkg (Windows)
if: matrix.platform == 'windows-2022'
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: 'b322364f06308bdd24823f9d8f03fe0cc86fd46f'

- name: Authenticate vcpkg NuGet (Windows)
if: matrix.platform == 'windows-2022'
shell: pwsh
run: |
$nuget = & $env:VCPKG_ROOT/vcpkg fetch nuget | Out-String
$nuget = $nuget.Trim()
& $nuget sources add -Source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" -Name "GitHubPackages" -Username "${{ github.repository_owner }}" -Password "${{ secrets.GITHUB_TOKEN }}" -StorePasswordInClearText -Verbosity detailed
& $nuget setapikey "${{ secrets.GITHUB_TOKEN }}" -Source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"

- name: Install vcpkg packages (Windows)
if: matrix.platform == 'windows-2022'
shell: pwsh
run: |
& $env:VCPKG_ROOT/vcpkg install --recurse --clean-after-build --triplet x64-windows
env:
VCPKG_BINARY_SOURCES: 'clear;nuget,https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json,readwrite'

- name: Build Tauri app (Windows)
if: matrix.platform == 'windows-latest'
run: pnpm tauri build --target ${{ matrix.target }} --bundles ${{ matrix.bundles }} --verbose
if: matrix.platform == 'windows-2022'
shell: pwsh
run: |
$env:PKG_CONFIG_PATH = "$env:GITHUB_WORKSPACE\vcpkg_installed\x64-windows\lib\pkgconfig"
$env:PATH = "$env:GITHUB_WORKSPACE\vcpkg_installed\x64-windows\tools\pkgconf;$env:GITHUB_WORKSPACE\src-tauri\icebug\lib;$env:PATH"
$env:CXXFLAGS = "/std:c++20"
pnpm tauri build --target ${{ matrix.target }} --bundles ${{ matrix.bundles }} --verbose

- name: Upload artifacts
uses: actions/upload-artifact@v4
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2", features = [] }

[features]
default = []
default = ["icebug-analytics"]
icebug-analytics = ["dep:icebug"]

[dependencies]
Expand All @@ -25,5 +25,5 @@ walkdir = "2"
dirs = "5"
fastrand = "2"
reqwest = { version = "0.13.3", features = ["blocking", "json"] }
icebug = { version = "12.9.0", optional = true }
icebug = { version = "13.0.0", optional = true }
lbug = { version = "0.18.1", features = ["arrow"] }
83 changes: 59 additions & 24 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,31 +214,48 @@ fn scan_for_databases(dir: &Path, base_dir: &Path) -> Vec<DatabaseInfo> {
for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() {
if let Some(ext) = path.extension() {
if ext == "lbdb" {
let name = path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let relative_path = path
.strip_prefix(base_dir)
.unwrap_or(path)
.to_string_lossy()
.to_string();
databases.push(DatabaseInfo {
id: 0, // assigned later
name,
path: path.to_string_lossy().to_string(),
relative_path,
});
}
let abs_path = path.to_string_lossy().to_string();
// Let Database::new() validate by magic bytes — no extension whitelist.
// A quick probe to skip obviously non-DB files without a full open.
if !is_lbug_file_fast(&abs_path) {
continue;
}
let name = path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let relative_path = path
.strip_prefix(base_dir)
.unwrap_or(path)
.to_string_lossy()
.to_string();
databases.push(DatabaseInfo {
id: 0, // assigned later
name,
path: abs_path,
relative_path,
});
}
}
databases
}

/// Quick check that a file starts with Lbug's magic bytes, without a full database open.
/// The Lbug magic is the 4-byte sequence `b"LBUG"` at offset 0.
fn is_lbug_file_fast(path: &str) -> bool {
use std::io::Read;
let mut file = match std::fs::File::open(path) {
Ok(f) => f,
Err(_) => return false,
};
let mut magic = [0u8; 4];
if file.read_exact(&mut magic).is_err() {
return false;
}
&magic == b"LBUG"
}

fn get_all_databases(state: &AppState) -> Vec<DatabaseInfo> {
let scanned = scan_for_databases(&state.data_dir, &state.data_dir);
let custom = state.custom_databases.lock().unwrap();
Expand All @@ -264,9 +281,7 @@ fn database_info_from_path(file_path: &str) -> Result<DatabaseInfo, String> {
return Err("File not found".to_string());
}

if abs_path.extension().and_then(|e| e.to_str()) != Some("lbdb") {
return Err("Only .lbdb files are supported".to_string());
}
// Extension check removed: Database::new() validates by magic bytes and returns a clear error

let abs_path_str = abs_path.to_string_lossy().to_string();
let name = abs_path
Expand Down Expand Up @@ -1318,6 +1333,20 @@ fn collect_edge_graph(conn: &Connection, limit: usize) -> Result<GraphData, Stri
);
}

// Also fetch isolated (edgeless) nodes and merge them in, so a database with
// nodes but few or no relationships doesn't render as an empty graph.
let mut isolated_result = conn
.query(&format!("MATCH (n) WHERE NOT (n)--() RETURN n LIMIT {limit}"))
.map_err(|e| format!("Isolated node query failed: {}", e))?;

for row in &mut isolated_result {
for val in row.iter() {
if let Some(node) = graph_node_from_value(val) {
nodes.insert(node.id.clone(), node);
}
}
}

Ok(graph_data_without_clusters(
nodes.into_values().collect(),
links,
Expand Down Expand Up @@ -1769,9 +1798,15 @@ fn get_directories(
path: entry_path.to_string_lossy().to_string(),
entry_type: "directory".to_string(),
});
} else if name.ends_with(".lbdb") {
} else if is_lbug_file_fast(&entry_path.to_string_lossy()) {
// Strip any extension for display so .lbug, .lbdb, .ldb etc.
// all show a clean name.
let trimmed = match entry_path.file_stem() {
Some(stem) => stem.to_string_lossy().to_string(),
None => name.clone(),
};
files.push(DirEntry {
name: name.trim_end_matches(".lbdb").to_string(),
name: trimmed,
path: entry_path.to_string_lossy().to_string(),
entry_type: "file".to_string(),
});
Expand Down
Loading