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
1 change: 1 addition & 0 deletions Cargo.lock

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

14 changes: 7 additions & 7 deletions crates/dojo/dojo-snf-test/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ version = 1

[[package]]
name = "dojo"
version = "1.6.0-alpha.1"
version = "1.7.0-alpha.2"
dependencies = [
"dojo_macros",
]

[[package]]
name = "dojo_macros"
version = "1.6.0-alpha.1"
version = "1.7.0-alpha.2"

[[package]]
name = "dojo_snf_test"
version = "1.6.0-alpha.1"
version = "1.7.0-alpha.2"
dependencies = [
"dojo",
"snforge_std",
]

[[package]]
name = "snforge_scarb_plugin"
version = "0.43.1"
version = "0.48.1"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:178e1e2081003ae5e40b5a8574654bed15acbd31cce651d4e74fe2f009bc0122"
checksum = "sha256:2dd27e8215eea8785b3930e9f452e11b429ca262b1c1fbb071bfc173b9ebc125"

[[package]]
name = "snforge_std"
version = "0.43.1"
version = "0.48.1"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:17bc65b0abfb9b174784835df173f9c81c9ad39523dba760f30589ef53cf8bb5"
checksum = "sha256:89f759fa685d48ed0ba7152d2ac2eb168da08dfa8b84b2bee96e203dc5b2413e"
dependencies = [
"snforge_scarb_plugin",
]
1 change: 1 addition & 0 deletions crates/sozo/scarb_metadata_ext/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ dunce.workspace = true
scarb-metadata.workspace = true
smol_str.workspace = true
scarb-interop.workspace = true
tracing.workspace = true
58 changes: 45 additions & 13 deletions crates/sozo/scarb_metadata_ext/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use dojo_world::local::WorldLocal;
use scarb_interop::fsx;
use scarb_metadata::{DepKind, Metadata, MetadataCommand, MetadataCommandError, PackageMetadata};
use serde::Serialize;
use tracing::debug;

#[derive(Debug, PartialEq)]
pub enum TestRunner {
Expand All @@ -20,7 +21,7 @@ pub enum TestRunner {

const CAIRO_TEST_RUNNER_NAME: &str = "cairo_test";
const SNF_TEST_RUNNER_NAME: &str = "snforge_std";
const DOJO_MACROS_NAME: &str = "dojo_macros";
const DOJO_WORLD_NAME: &str = "dojo::world::world_contract::world";

impl From<&String> for TestRunner {
fn from(value: &String) -> Self {
Expand Down Expand Up @@ -297,30 +298,61 @@ enum WorldPackageResult {
/// performing some tasks like binding generation and migrations.
///
/// To solve the issue where a virtual workspace has no package name, this function looks for a
/// package that is *not* a `lib` target and uses `dojo_macros` as a dependency.
/// If it finds exactly one package that matches the criteria, it returns the package name.
/// package that has a starknet-contract target that has a build-external-contracts parameter that
/// contains the dojo world. We can't rely on the package being a library, since some libraries
/// might be used in the contracts to be deployed with a world. For this reason, we first look for a
/// package that is not a library and then a package that is a library. If it finds exactly one
/// package that matches the criteria, it returns the package name.
///
/// Otherwise, it returns a `MultiplePackages` variant, which will be handled by the caller, which
/// generally should indicate to the user that using multiple packages with contracts to be deployed
/// with a world is not supported at the workspace level. Therefore, the user will want to build and
/// migrate the package with contracts themselves by going into the package directory and running
/// the commands.
fn default_dojo_package_name(packages: &[PackageMetadata]) -> WorldPackageResult {
let mut candidates = Vec::new();
let mut not_lib_candidates = Vec::new();
let mut lib_candidates = Vec::new();

for p in packages {
if p.targets.iter().all(|t| t.kind != "lib") {
for d in &p.dependencies {
if d.name == DOJO_MACROS_NAME {
candidates.push(p.name.clone());
let is_lib = p.targets.iter().any(|t| t.kind == "lib");

debug!(%p.name, is_lib, "Verifying package type.");

for t in &p.targets {
Comment on lines +318 to +321
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

Potential compile error in tracing field shorthand

debug!(%p.name, ...) is not valid shorthand because p.name isn’t an identifier. Give the field an explicit key.

-        debug!(%p.name, is_lib, "Verifying package type.");
+        debug!(package = %p.name, is_lib, "Verifying package type.");
📝 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
debug!(%p.name, is_lib, "Verifying package type.");
for t in &p.targets {
debug!(package = %p.name, is_lib, "Verifying package type.");
for t in &p.targets {
🤖 Prompt for AI Agents
In crates/sozo/scarb_metadata_ext/src/metadata.rs around lines 318 to 321, the
tracing macro uses shorthand debug!(%p.name, ...) which is invalid because
p.name is not an identifier; change the shorthand to explicit key/value pairs
(for example name = %p.name and is_lib = is_lib) so the call becomes debug!(name
= %p.name, is_lib = is_lib, "Verifying package type."); ensure all fields passed
to debug! use explicit keys.

if t.kind == "starknet-contract" {
if let Some(build_external_contracts) =
t.params.as_object().and_then(|obj| obj.get("build-external-contracts"))
{
if let Some(build_external_contracts) = build_external_contracts.as_array() {
if build_external_contracts
.contains(&serde_json::Value::String(DOJO_WORLD_NAME.to_string()))
{
if is_lib {
lib_candidates.push(p.name.clone());
} else {
not_lib_candidates.push(p.name.clone());
}
}
}
}
}
}
}
Comment on lines 312 to 340
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Limit detection to workspace members only

default_dojo_package_name scans all self.packages via caller, which commonly includes dependencies. This can pick a dependency’s package (or cause false “Multiple packages” errors).

  • Filter to workspace members at the call site (workspace_package_name) before invoking default_dojo_package_name.
  • If PackageMetadata exposes manifest_path, filter by starts_with(self.workspace.root). Otherwise, use an explicit workspace members list if available.

Example (outside this hunk, for workspace_package_name):

let root = &self.workspace.root;
let pkgs = self
    .packages
    .iter()
    .filter(|p| p.manifest_path.starts_with(root));
match default_dojo_package_name(pkgs) { /* unchanged */ }

To support that, generalize the helper signature in this hunk:

-fn default_dojo_package_name(packages: &[PackageMetadata]) -> WorldPackageResult {
+fn default_dojo_package_name<'a, I>(packages: I) -> WorldPackageResult
+where
+    I: IntoIterator<Item = &'a PackageMetadata>,
+{
-    for p in packages {
+    for p in packages {
         // unchanged
     }

Verification prompts:


🌐 Web query:

Does `scarb metadata`’s `packages` array include dependencies in addition to workspace members?

💡 Result:

Short answer: Yes — the metadata "packages" array contains the resolved packages for the project (workspace members plus their dependencies), not only workspace members. (docs.swmansion.com, docs.rs)

Citations:


Filter default_dojo_package_name inputs to workspace members only
ohayo, sensei! scarb metadata returns both workspace members and their dependencies (docs.swmansion.com); filter to avoid false “multiple packages” errors.

  • At the call site (e.g., in workspace_package_name), do:
let root = &self.workspace.root;
let pkgs = self
    .packages
    .iter()
    .filter(|p| p.manifest_path.starts_with(root));
match default_dojo_package_name(pkgs) { /* unchanged */ }
  • Generalize the helper signature:
-fn default_dojo_package_name(packages: &[PackageMetadata]) -> WorldPackageResult {
+fn default_dojo_package_name<'a, I>(packages: I) -> WorldPackageResult
+where
+    I: IntoIterator<Item = &'a PackageMetadata>,
 {
     // unchanged
 }
🤖 Prompt for AI Agents
crates/sozo/scarb_metadata_ext/src/metadata.rs around lines 312-340:
default_dojo_package_name currently inspects all packages returned by `scarb
metadata` (which includes dependencies) causing false multiple-package errors;
change it to operate only on workspace members by (A) generalizing the function
signature to accept an iterator or IntoIterator over &PackageMetadata (e.g.,
impl IntoIterator<Item = &'a PackageMetadata> or similar) instead of a
&[PackageMetadata], and update the function body to iterate that iterator; and
(B) update call sites (e.g., workspace_package_name) to filter self.packages to
workspace members before calling the helper by checking that
package.manifest_path.starts_with(self.workspace.root) and pass the filtered
iterator into default_dojo_package_name. Ensure all call sites are adjusted to
the new signature.


match candidates.len() {
0 => WorldPackageResult::NoPackage,
1 => WorldPackageResult::Ok(candidates[0].clone()),
_ => WorldPackageResult::MultiplePackages(candidates),
}
debug!(?not_lib_candidates, ?lib_candidates, "Collect candidates for Sozo build.");

// Prioritize not lib candidates, then lib candidates.
let r = match not_lib_candidates.len() {
0 => match lib_candidates.len() {
0 => WorldPackageResult::NoPackage,
1 => WorldPackageResult::Ok(lib_candidates[0].clone()),
_ => WorldPackageResult::MultiplePackages(lib_candidates),
},
1 => WorldPackageResult::Ok(not_lib_candidates[0].clone()),
_ => WorldPackageResult::MultiplePackages(not_lib_candidates),
};

debug!(?r, "Extract default dojo package name from workspace.");

r
}
Loading