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
26 changes: 26 additions & 0 deletions .github/workflows/sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,32 @@ jobs:
run: |
npx nx build @lightprotocol/zk-compression-cli

- name: Install bun
if: matrix.program == 'sdk-libs'
run: npm install -g bun

- name: Install CLI globally (npm, bun, yarn)
if: matrix.program == 'sdk-libs'
run: |
npm install -g @lightprotocol/zk-compression-cli
bun install -g @lightprotocol/zk-compression-cli
cd /tmp && yarn global add @lightprotocol/zk-compression-cli

- name: Test find_light_bin with npm
if: matrix.program == 'sdk-libs'
run: |
cargo test -p light-program-test --lib -- test_find_light_bin --nocapture

- name: Test find_light_bin with bun
if: matrix.program == 'sdk-libs'
run: |
PATH="$HOME/.bun/bin:$PATH" cargo test -p light-program-test --lib -- test_find_light_bin --nocapture

- name: Test find_light_bin with yarn
if: matrix.program == 'sdk-libs'
run: |
PATH="$(yarn global bin):$PATH" cargo test -p light-program-test --lib -- test_find_light_bin --nocapture

- name: Run sub-tests for ${{ matrix.program }}
if: matrix.sub-tests != null
run: |
Expand Down
3 changes: 1 addition & 2 deletions js/stateless.js/src/programs/system/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,7 @@ export function convertToPublicTransactionEvent(
convertByteArray(
Buffer.from(
new Uint8Array(
invokeData
.outputCompressedAccounts[
invokeData.outputCompressedAccounts[
index
].compressedAccount.data.data,
),
Expand Down
68 changes: 59 additions & 9 deletions sdk-libs/program-test/src/utils/find_light_bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pub fn find_light_bin() -> Option<PathBuf> {
// Run the 'which light' command to find the location of 'light' binary
#[cfg(not(feature = "devenv"))]
{
use std::process::Command;
use std::{fs, process::Command};

let output = Command::new("which")
.arg("light")
.output()
Expand All @@ -13,17 +14,37 @@ pub fn find_light_bin() -> Option<PathBuf> {
if !output.status.success() {
return None;
}
// Convert the output into a string (removing any trailing newline)

let light_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
// Get the parent directory of the 'light' binary
let mut light_bin_path = PathBuf::from(light_path);
light_bin_path.pop(); // Remove the 'light' binary itself
let light_path = PathBuf::from(&light_path);

// Assuming the node_modules path starts from '/lib/node_modules/...'
let node_modules_bin =
light_bin_path.join("../lib/node_modules/@lightprotocol/zk-compression-cli/bin");
// Follow the symlink to find the actual script location.
// This works for npm, bun, and yarn which all create symlinks from their
// bin directory to the actual script in the package.
let symlink_target = fs::read_link(&light_path).ok()?;

Some(node_modules_bin.canonicalize().unwrap_or(node_modules_bin))
// Resolve relative symlinks (e.g., "../lib/node_modules/...")
let resolved_path = if symlink_target.is_relative() {
let parent = light_path.parent()?;
parent.join(&symlink_target).canonicalize().ok()?
} else {
symlink_target.canonicalize().ok()?
};
Comment on lines +21 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Breaks direct installations by assuming symlink.

Line 24 uses fs::read_link(&light_path).ok()? which immediately returns None if light is not a symlink. This breaks scenarios where:

  • The light binary is copied directly to a bin directory
  • It's installed without package managers (direct download/build)
  • It's a hard link rather than a symbolic link

The function should handle both symlinks and regular files.

Apply this diff to handle both cases:

-        // Follow the symlink to find the actual script location.
-        // This works for npm, bun, and yarn which all create symlinks from their
-        // bin directory to the actual script in the package.
-        let symlink_target = fs::read_link(&light_path).ok()?;
-
-        // Resolve relative symlinks (e.g., "../lib/node_modules/...")
-        let resolved_path = if symlink_target.is_relative() {
-            let parent = light_path.parent()?;
-            parent.join(&symlink_target).canonicalize().ok()?
-        } else {
-            symlink_target.canonicalize().ok()?
-        };
+        // If light is a symlink (npm, bun, yarn), follow it to the actual script.
+        // Otherwise, use the binary path directly.
+        let resolved_path = if let Ok(symlink_target) = fs::read_link(&light_path) {
+            // Resolve relative symlinks (e.g., "../lib/node_modules/...")
+            if symlink_target.is_relative() {
+                let parent = light_path.parent()?;
+                parent.join(&symlink_target).canonicalize().ok()?
+            } else {
+                symlink_target.canonicalize().ok()?
+            }
+        } else {
+            // Not a symlink, use the path directly
+            light_path.canonicalize().ok()?
+        };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Follow the symlink to find the actual script location.
// This works for npm, bun, and yarn which all create symlinks from their
// bin directory to the actual script in the package.
let symlink_target = fs::read_link(&light_path).ok()?;
Some(node_modules_bin.canonicalize().unwrap_or(node_modules_bin))
// Resolve relative symlinks (e.g., "../lib/node_modules/...")
let resolved_path = if symlink_target.is_relative() {
let parent = light_path.parent()?;
parent.join(&symlink_target).canonicalize().ok()?
} else {
symlink_target.canonicalize().ok()?
};
// If light is a symlink (npm, bun, yarn), follow it to the actual script.
// Otherwise, use the binary path directly.
let resolved_path = if let Ok(symlink_target) = fs::read_link(&light_path) {
// Resolve relative symlinks (e.g., "../lib/node_modules/...")
if symlink_target.is_relative() {
let parent = light_path.parent()?;
parent.join(&symlink_target).canonicalize().ok()?
} else {
symlink_target.canonicalize().ok()?
}
} else {
// Not a symlink, use the path directly
light_path.canonicalize().ok()?
};


// Navigate up to find the package root (contains bin/ directory with .so files)
// The symlink target is typically: .../zk-compression-cli/test_bin/run
// We need to find: .../zk-compression-cli/bin/
let mut current = resolved_path.as_path();
while let Some(parent) = current.parent() {
let bin_dir = parent.join("bin");
// Check if this bin/ directory contains .so files (our target)
if bin_dir.exists() && bin_dir.join("account_compression.so").exists() {
return Some(bin_dir);
}
current = parent;
}

None
}
#[cfg(feature = "devenv")]
{
Expand All @@ -42,3 +63,32 @@ pub fn find_light_bin() -> Option<PathBuf> {
Some(light_path)
}
}

#[cfg(all(not(feature = "devenv"), test))]
mod tests {
use super::*;

#[test]
fn test_find_light_bin() {
let bin_path = find_light_bin();
println!("find_light_bin() returned: {:?}", bin_path);

if let Some(path) = &bin_path {
println!("Path exists: {}", path.exists());
println!(
"account_compression.so exists: {}",
path.join("account_compression.so").exists()
);
}

// Only assert if light CLI is installed
if bin_path.is_some() {
let path = bin_path.unwrap();
assert!(path.exists(), "bin directory should exist");
assert!(
path.join("account_compression.so").exists(),
"account_compression.so should exist in bin directory"
);
}
}
}