Skip to content

Commit

Permalink
feat(sozo): add a subcommand to generate overlay files (#2025)
Browse files Browse the repository at this point in the history
* initial commit

* add support for model overlays

* clean up

* add test for merge

* separate out match statement

* fix directory creation

* fix: fix test typo + add comment on isolated variant test

---------

Co-authored-by: glihm <[email protected]>
  • Loading branch information
lambda-0x and glihm authored Jun 10, 2024
1 parent 196732b commit a636d23
Show file tree
Hide file tree
Showing 27 changed files with 408 additions and 62 deletions.
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.

2 changes: 1 addition & 1 deletion bin/sozo/src/commands/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fs;
use anyhow::Result;
use camino::Utf8PathBuf;
use clap::Args;
use dojo_lang::compiler::{ABIS_DIR, BASE_DIR, MANIFESTS_DIR};
use dojo_world::manifest::{ABIS_DIR, BASE_DIR, MANIFESTS_DIR};
use scarb::core::Config;
use tracing::trace;

Expand Down
3 changes: 1 addition & 2 deletions bin/sozo/src/commands/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ use cairo_lang_compiler::db::RootDatabase;
use cairo_lang_filesystem::db::{AsFilesGroupMut, FilesGroupEx, PrivRawFileContentQuery};
use cairo_lang_filesystem::ids::FileId;
use clap::Args;
use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR};
use dojo_lang::scarb_internal::build_scarb_root_database;
use dojo_world::manifest::{BaseManifest, DeploymentManifest};
use dojo_world::manifest::{BaseManifest, DeploymentManifest, BASE_DIR, MANIFESTS_DIR};
use dojo_world::metadata::dojo_metadata_from_workspace;
use dojo_world::migration::world::WorldDiff;
use dojo_world::migration::TxnConfig;
Expand Down
13 changes: 12 additions & 1 deletion bin/sozo/src/commands/migrate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{anyhow, Context, Result};
use clap::{Args, Subcommand};
use dojo_lang::compiler::MANIFESTS_DIR;
use dojo_world::manifest::MANIFESTS_DIR;
use dojo_world::metadata::{dojo_metadata_from_workspace, Environment};
use dojo_world::migration::TxnConfig;
use katana_rpc_api::starknet::RPC_SPEC_VERSION;
Expand Down Expand Up @@ -50,13 +50,22 @@ pub enum MigrateCommand {
#[command(flatten)]
transaction: TransactionOptions,
},
#[command(about = "Generate overlays file.")]
GenerateOverlays,
}

impl MigrateArgs {
pub fn run(self, config: &Config) -> Result<()> {
trace!(args = ?self);
let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;

// This variant is tested before the match on `self.command` to avoid
// having the need to spin up a Katana to generate the files.
if let MigrateCommand::GenerateOverlays = self.command {
trace!("Generating overlays.");
return migration::generate_overlays(&ws);
}

let env_metadata = if config.manifest_path().exists() {
dojo_metadata_from_workspace(&ws).env().cloned()
} else {
Expand All @@ -81,6 +90,7 @@ impl MigrateArgs {

match self.command {
MigrateCommand::Plan => config.tokio_handle().block_on(async {
trace!(name, "Planning migration.");
migration::migrate(
&ws,
world_address,
Expand All @@ -99,6 +109,7 @@ impl MigrateArgs {
migration::migrate(&ws, world_address, rpc_url, account, &name, false, txn_config)
.await
}),
_ => unreachable!("other case handled above."),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/benches/src/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::path::PathBuf;

use anyhow::{anyhow, bail, Context, Ok, Result};
use clap::Parser;
use dojo_lang::compiler::{DojoCompiler, DEPLOYMENTS_DIR, MANIFESTS_DIR};
use dojo_lang::compiler::DojoCompiler;
use dojo_lang::plugin::CairoPluginRepository;
use dojo_world::manifest::DeploymentManifest;
use dojo_world::manifest::{DeploymentManifest, DEPLOYMENTS_DIR, MANIFESTS_DIR};
use futures::executor::block_on;
use katana_runner::KatanaRunner;
use scarb::compiler::CompilerRepository;
Expand Down
12 changes: 2 additions & 10 deletions crates/dojo-lang/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use camino::{Utf8Path, Utf8PathBuf};
use convert_case::{Case, Casing};
use dojo_world::manifest::{
AbiFormat, Class, ComputedValueEntrypoint, DojoContract, DojoModel, Manifest, ManifestMethods,
BASE_CONTRACT_NAME, WORLD_CONTRACT_NAME,
ABIS_DIR, BASE_CONTRACT_NAME, BASE_DIR, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR,
WORLD_CONTRACT_NAME,
};
use itertools::Itertools;
use scarb::compiler::helpers::{build_compiler_config, collect_main_crate_ids};
Expand All @@ -40,15 +41,6 @@ use crate::semantics::utils::find_module_rw;

const CAIRO_PATH_SEPARATOR: &str = "::";

pub const MANIFESTS_DIR: &str = "manifests";
pub const BASE_DIR: &str = "base";
pub const OVERLAYS_DIR: &str = "overlays";
pub const DEPLOYMENTS_DIR: &str = "deployments";
pub const ABIS_DIR: &str = "abis";

pub const CONTRACTS_DIR: &str = "contracts";
pub const MODELS_DIR: &str = "models";

pub const SOURCES_DIR: &str = "src";

pub(crate) const LOG_TARGET: &str = "dojo_lang::compiler";
Expand Down
3 changes: 1 addition & 2 deletions crates/dojo-test-utils/src/migration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anyhow::Result;
use camino::Utf8PathBuf;
use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR};
use dojo_world::manifest::{BaseManifest, OverlayManifest};
use dojo_world::manifest::{BaseManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR};
use dojo_world::migration::strategy::{prepare_for_migration, MigrationStrategy};
use dojo_world::migration::world::WorldDiff;
use katana_primitives::FieldElement;
Expand Down
3 changes: 1 addition & 2 deletions crates/dojo-world/src/contracts/world_test.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::time::Duration;

use camino::Utf8PathBuf;
use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR};
use dojo_test_utils::compiler;
use katana_runner::KatanaRunner;
use starknet::accounts::{Account, ConnectedAccount};
use starknet::core::types::{BlockId, BlockTag, FieldElement};

use super::{WorldContract, WorldContractReader};
use crate::manifest::{BaseManifest, OverlayManifest};
use crate::manifest::{BaseManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR};
use crate::migration::strategy::prepare_for_migration;
use crate::migration::world::WorldDiff;
use crate::migration::{Declarable, Deployable, TxnConfig};
Expand Down
174 changes: 171 additions & 3 deletions crates/dojo-world/src/manifest/manifest_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::io::Write;

use cainome::cairo_serde::{ByteArray, CairoSerde};
use camino::Utf8PathBuf;
use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR};
use dojo_test_utils::compiler;
use dojo_test_utils::rpc::MockJsonRpcTransport;
use katana_runner::KatanaRunner;
Expand All @@ -14,10 +13,14 @@ use starknet::macros::{felt, selector};
use starknet::providers::jsonrpc::{JsonRpcClient, JsonRpcMethod};

use super::{
parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayManifest,
parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayDojoContract,
OverlayManifest,
};
use crate::contracts::world::test::deploy_world;
use crate::manifest::{parse_models_events, AbstractManifestError, DeploymentManifest, Manifest};
use crate::manifest::{
parse_models_events, AbstractManifestError, DeploymentManifest, Manifest, OverlayClass,
OverlayDojoModel, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR,
};
use crate::migration::world::WorldDiff;

#[tokio::test]
Expand Down Expand Up @@ -473,3 +476,168 @@ fn test_abi_format_load_abi_string() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

#[test]
fn overlay_merge_for_contract_and_model_work_as_expected() {
let other = OverlayManifest {
contracts: vec![
OverlayDojoContract { name: "othercontract1".into(), ..Default::default() },
OverlayDojoContract { name: "othercontract2".into(), ..Default::default() },
OverlayDojoContract { name: "existingcontract".into(), ..Default::default() },
],
models: vec![
OverlayDojoModel { name: "othermodel1".into(), ..Default::default() },
OverlayDojoModel { name: "othermodel2".into(), ..Default::default() },
OverlayDojoModel { name: "existingmodel".into(), ..Default::default() },
],
..Default::default()
};

let mut current = OverlayManifest {
contracts: vec![
OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() },
OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() },
OverlayDojoContract { name: "existingcontract".into(), ..Default::default() },
],
models: vec![
OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() },
OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() },
OverlayDojoModel { name: "existingmodel".into(), ..Default::default() },
],
..Default::default()
};

let expected = OverlayManifest {
contracts: vec![
OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() },
OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() },
OverlayDojoContract { name: "existingcontract".into(), ..Default::default() },
OverlayDojoContract { name: "othercontract1".into(), ..Default::default() },
OverlayDojoContract { name: "othercontract2".into(), ..Default::default() },
],
models: vec![
OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() },
OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() },
OverlayDojoModel { name: "existingmodel".into(), ..Default::default() },
OverlayDojoModel { name: "othermodel1".into(), ..Default::default() },
OverlayDojoModel { name: "othermodel2".into(), ..Default::default() },
],
..Default::default()
};

current.merge(other);

assert_eq!(current, expected);
}

#[test]
fn overlay_merge_for_world_work_as_expected() {
// when other.world is none and current.world is some
let other = OverlayManifest { ..Default::default() };
let mut current = OverlayManifest {
world: Some(OverlayClass { name: "world".into(), ..Default::default() }),
..Default::default()
};
let expected = OverlayManifest {
world: Some(OverlayClass { name: "world".into(), ..Default::default() }),
..Default::default()
};
current.merge(other);

assert_eq!(current, expected);

// when other.world is some and current.world is none
let other = OverlayManifest {
world: Some(OverlayClass { name: "world".into(), ..Default::default() }),
..Default::default()
};
let mut current = OverlayManifest { ..Default::default() };
let expected = OverlayManifest {
world: Some(OverlayClass { name: "world".into(), ..Default::default() }),
..Default::default()
};

current.merge(other);
assert_eq!(current, expected);

// when other.world is some and current.world is some
let other = OverlayManifest {
world: Some(OverlayClass { name: "worldother".into(), ..Default::default() }),
..Default::default()
};
let mut current = OverlayManifest {
world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }),
..Default::default()
};
let expected = OverlayManifest {
world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }),
..Default::default()
};

current.merge(other);
assert_eq!(current, expected);

// when other.world is none and current.world is none
let other = OverlayManifest { ..Default::default() };
let mut current = OverlayManifest { ..Default::default() };
let expected = OverlayManifest { ..Default::default() };

current.merge(other);
assert_eq!(current, expected);
}

#[test]
fn overlay_merge_for_base_work_as_expected() {
// when other.base is none and current.base is some
let other = OverlayManifest { ..Default::default() };
let mut current = OverlayManifest {
base: Some(OverlayClass { name: "base".into(), ..Default::default() }),
..Default::default()
};
let expected = OverlayManifest {
base: Some(OverlayClass { name: "base".into(), ..Default::default() }),
..Default::default()
};
current.merge(other);

assert_eq!(current, expected);

// when other.base is some and current.base is none
let other = OverlayManifest {
base: Some(OverlayClass { name: "base".into(), ..Default::default() }),
..Default::default()
};
let mut current = OverlayManifest { ..Default::default() };
let expected = OverlayManifest {
base: Some(OverlayClass { name: "base".into(), ..Default::default() }),
..Default::default()
};

current.merge(other);
assert_eq!(current, expected);

// when other.base is some and current.base is some
let other = OverlayManifest {
base: Some(OverlayClass { name: "baseother".into(), ..Default::default() }),
..Default::default()
};
let mut current = OverlayManifest {
base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }),
..Default::default()
};
let expected = OverlayManifest {
base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }),
..Default::default()
};

current.merge(other);
assert_eq!(current, expected);

// when other.base is none and current.base is none
let other = OverlayManifest { ..Default::default() };
let mut current = OverlayManifest { ..Default::default() };
let expected = OverlayManifest { ..Default::default() };

current.merge(other);
assert_eq!(current, expected);
}
Loading

0 comments on commit a636d23

Please sign in to comment.