diff --git a/.github/workflows/cairo-zero-ci.yml b/.github/workflows/cairo-zero-ci.yml index 484eaf88d..e6efb1990 100644 --- a/.github/workflows/cairo-zero-ci.yml +++ b/.github/workflows/cairo-zero-ci.yml @@ -126,7 +126,7 @@ jobs: - name: Extract Katana Version id: extract_katana_version run: | - KATANA_VERSION=$(grep -oP '^KATANA_VERSION = \K.*' Makefile) + KATANA_VERSION=$(grep -E "^KATANA_VERSION\s*=" kakarot_scripts/setup/setup.py | cut -d'"' -f2) echo "katana_version=$KATANA_VERSION" >> "$GITHUB_OUTPUT" - uses: astral-sh/setup-uv@v2 with: @@ -148,7 +148,7 @@ jobs: key: katana-${{ steps.extract_katana_version.outputs.katana_version }} - name: Install Katana if: steps.cached-katana.outputs.cache-hit != 'true' - run: make install-katana + run: make setup "katana" - name: Run tests run: | cp .env.example .env diff --git a/Makefile b/Makefile index 8d615e3d7..e1f876474 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,6 @@ endif KKRT_SSJ_RELEASE_ID = 176384150 # Kakarot SSJ artifacts for precompiles. KKRT_SSJ_BUILD_ARTIFACT_URL = $(shell curl -L https://api.github.com/repos/kkrt-labs/kakarot-ssj/releases/${KKRT_SSJ_RELEASE_ID} | jq -r '.assets[0].browser_download_url') -KATANA_VERSION = v1.0.0-alpha.16 ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) BUILD_DIR = build @@ -26,6 +25,12 @@ $(SSJ_DIR): $(SSJ_ZIP) $(SSJ_ZIP): curl -sL -o $(SSJ_ZIP) "$(KKRT_SSJ_BUILD_ARTIFACT_URL)" +# Accepts "katana" as an argument to setup only Katana (for CI). +setup: + @python kakarot_scripts/setup/setup.py $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + uv sync --all-extras --dev +katana: ; + build: $(SSJ_DIR) uv run compile @@ -35,9 +40,6 @@ deploy: build build-sol fetch-ef-tests: uv run ef_tests -setup: - uv sync --all-extras --dev - test-cairo-zero: deploy uv run pytest cairo_zero/tests/src -m "not NoCI" --log-cli-level=INFO -n logical --seed 42 uv run pytest tests/end_to_end --seed 42 @@ -82,9 +84,6 @@ build-sol: git submodule update --init --recursive forge build --names --force -install-katana: - cargo install --git https://github.com/dojoengine/dojo --locked --tag "${KATANA_VERSION}" katana - run-katana: katana --chain-id test --validate-max-steps 1000000 --invoke-max-steps 9000000 --eth-gas-price 0 --strk-gas-price 0 --disable-fee --seed 0 diff --git a/README.md b/README.md index 867d2b684..2ff4f1b49 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,13 @@ dependencies and run commands. To install uv: curl -LsSf https://astral.sh/uv/install.sh | sh ``` +To setup the project and install +[all dependencies](docs/CONTRIBUTING.md#prerequisites): + +```bash +make setup +``` + To build the CairoZero files: ```bash diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 7cd8c7dc8..4d350a049 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -34,26 +34,30 @@ libraries, e.g. `cairo-lang`. - Install [uv](https://github.com/astral-sh/uv) to manage python dependencies and run commands -- Install [jq](https://jqlang.github.io/jq/) to process JSON from the CLI -- Install - [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) to - manage our Rust dependencies. -- Install [docker](https://docs.docker.com/get-docker/) to build some - experimental solidity contracts that require a custom solc compiler -- Install [foundry](https://book.getfoundry.sh/getting-started/installation) to - compile the Solidity contracts we use for testing. -- Install - [scarb using asdf](https://docs.swmansion.com/scarb/download.html#install-via-asdf) - to the Cairo contracts we use as dependencies. As these dependencies rely on - different versions of Cairo, you will need to install the following versions - of Scarb: - - ```sh - asdf install scarb 0.7.0 - asdf install scarb 2.6.5 - ``` -- Install [Go](https://go.dev/doc/install) to profile tests using `pprof` +- Setup the environment using `make setup`. Alternatively, you can install the + dependencies manually as follows: + + - Install [jq](https://jqlang.github.io/jq/) to process JSON from the CLI + - Install + [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) + to manage our Rust dependencies. + - Install [docker](https://docs.docker.com/get-docker/) to build some + experimental solidity contracts that require a custom solc compiler + - Install [foundry](https://book.getfoundry.sh/getting-started/installation) + to compile the Solidity contracts we use for testing. + - Install + [scarb using asdf](https://docs.swmansion.com/scarb/download.html#install-via-asdf) + to the Cairo contracts we use as dependencies. As these dependencies rely on + different versions of Cairo, you will need to install the following versions + of Scarb: + + ```sh + asdf install scarb 0.7.0 + asdf install scarb 2.6.5 + ``` + + - Install [Go](https://go.dev/doc/install) to profile tests using `pprof` - Install the two VSCode extensions for Cairo: - [Cairo Syntax](https://marketplace.visualstudio.com/items?itemName=starkware.cairo) @@ -75,31 +79,25 @@ To set up a development environment, please follow these steps: make setup ``` -3. Install katana - - ```sh - make install-katana - ``` - -4. Build the Solidity contracts +3. Build the Solidity contracts ```sh make build-sol ``` -5. Copy the default environment variables +4. Copy the default environment variables ```sh cp .env.example .env ``` -6. Start a local katana instance +5. Start a local katana instance ```sh make run-katana ``` -7. Run tests +6. Run tests ```sh make test diff --git a/kakarot_scripts/setup/setup.py b/kakarot_scripts/setup/setup.py new file mode 100644 index 000000000..a2876813e --- /dev/null +++ b/kakarot_scripts/setup/setup.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 + +import logging +import os +import shutil +import subprocess +import sys +from typing import Optional + +KATANA_VERSION = "v1.0.0-alpha.16" +ASDF_VERSION = "v0.14.1" + +SHELL_CONFIG_FILES = { + "bash": [".bashrc", ".bash_profile"], + "zsh": [".zshrc"], + "fish": [".config/fish/config.fish"], +} + + +class SetupError(Exception): + """Custom exception for setup errors.""" + + pass + + +def run_command(command: str, error_message: str) -> None: + try: + subprocess.run(command, check=True, shell=True) + except subprocess.CalledProcessError as e: + raise SetupError(f"{error_message}: {e}") from e + + +def is_command_available(command: str) -> bool: + return shutil.which(command) is not None + + +def get_version(command: str) -> Optional[str]: + try: + result = subprocess.run([command, "--version"], capture_output=True, text=True) + return result.stdout.strip() + except subprocess.CalledProcessError: + return None + + +def get_shell_config_file() -> Optional[str]: + shell = os.environ.get("SHELL", "").split("/")[-1] + home = os.path.expanduser("~") + + for file in SHELL_CONFIG_FILES.get(shell, []): + full_path = os.path.join(home, file) + if os.path.exists(full_path): + return full_path + + return None + + +def install_dependency( + name: str, install_command: str, check_command: str, version: Optional[str] = None +) -> None: + if is_command_available(check_command): + if version: + current_version = get_version(check_command) + if current_version == version: + logger.info(f"{name} version {version} is already installed.") + return + logger.info(f"Updating {name} to version {version}...") + else: + logger.info(f"{name} is already installed.") + return + else: + logger.info(f"Installing {name}...") + + run_command(install_command, f"Failed to install/update {name}") + + +def setup_katana() -> None: + install_dependency( + "katana", + f'cargo install --git https://github.com/dojoengine/dojo --locked --tag "{KATANA_VERSION}" katana', + "katana", + version=KATANA_VERSION, + ) + + +def setup_local() -> None: + # Install dependencies + install_dependency( + "jq", + "brew install jq" if sys.platform == "darwin" else "sudo apt-get install -y jq", + "jq", + ) + install_dependency("cargo", "curl https://sh.rustup.rs -sSf | sh -s -- -y", "cargo") + run_command(". $HOME/.cargo/env", "Failed to source cargo environment") + + install_dependency("uv", "curl -LsSf https://astral.sh/uv/install.sh | sh", "uv") + + if not is_command_available("docker"): + logger.warning( + "❌ Please install Docker manually from https://docs.docker.com/get-docker/" + ) + else: + logger.info("Docker is already installed.") + + install_dependency( + "foundry", "curl -L https://foundry.paradigm.xyz | bash && foundryup", "forge" + ) + + # Install asdf and related tools + if not is_command_available("asdf"): + logger.info("Installing asdf...") + run_command( + f"git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch {ASDF_VERSION}", + "Failed to install asdf", + version=ASDF_VERSION, + ) + shell_config = get_shell_config_file() + if shell_config: + run_command( + f"echo '. $HOME/.asdf/asdf.sh' >> {shell_config}", + "Failed to source asdf environment", + ) + logger.info("Please restart your terminal to use asdf.") + else: + logger.warning("Please add asdf to your shell configuration manually.") + else: + logger.info("asdf is already installed.") + + run_command( + "asdf plugin add scarb && asdf plugin add starknet-foundry || true", + "Failed to add asdf plugins", + ) + run_command("asdf install", "Failed to install asdf tools") + + install_dependency( + "go", + ( + "brew install go" + if sys.platform == "darwin" + else "sudo apt-get install -y golang-go" + ), + "go", + ) + setup_katana() + + logger.info("All dependencies have been installed or were already available!") + + +def main() -> None: + try: + if len(sys.argv) > 1 and sys.argv[1] == "katana": + setup_katana() + else: + setup_local() + except SetupError as e: + logger.error(f"❌ Error: {e}") + sys.exit(1) + + +# Set up logging +logging.basicConfig(level=logging.INFO, format="%(message)s") +logger = logging.getLogger(__name__) + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index d61e3e883..4ca9f3b6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ ] [project.scripts] +setup = "kakarot_scripts.setup.setup:main" compile = "kakarot_scripts.compile_kakarot:main" deploy = "kakarot_scripts.deployment.main:main_sync" ef_tests = "kakarot_scripts.ef_tests.fetch:generate_tests"