-
Notifications
You must be signed in to change notification settings - Fork 240
Support devnet in --network
flag
#3786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
120b679
Introduce `devnet` option for `--network`
MKowalski8 61b534d
Implement heuristic detection
MKowalski8 807dcba
Update docs
MKowalski8 9a1ea80
Changelog info
MKowalski8 dcbb672
Change displaying blockexplorer to heuristic detection
MKowalski8 7d95b14
Fix clippy
MKowalski8 0e04f81
Rewrite url detection
MKowalski8 a5510dd
Better error message
MKowalski8 a18a02d
Fix tests
MKowalski8 7299ae0
Rename
MKowalski8 f4b31ea
Fix new clippy
MKowalski8 2d0aa6c
Change docs
MKowalski8 eafe1ca
Return error when two instances found
MKowalski8 88071ee
Merge branch 'master' into 3764-support-devnet-via-network-flag
MKowalski8 a9b1b51
Merge fixes
MKowalski8 016fc2e
Fix comments
MKowalski8 7793096
Prepare to review
MKowalski8 4613458
Merge branch 'master' into 3764-support-devnet-via-network-flag
MKowalski8 7aaf4d4
Rewrite docker data extracting
MKowalski8 f73163e
Update docs
MKowalski8 f8c1ac6
Nits from review
MKowalski8 71357ba
Change verification to return error
MKowalski8 ed83201
Change filtering to `grep`
MKowalski8 079e8db
Merge branch 'master' into 3764-support-devnet-via-network-flag
MKowalski8 3513d28
Fix fmt
MKowalski8 629bb90
Review chnages
MKowalski8 d31aa8a
Rewrite to regex
MKowalski8 d097ad4
Update `is_port_reachable` to use `DevnetProvider`
MKowalski8 59134b9
Refactor tests
MKowalski8 ad680dd
Docs update
MKowalski8 50e9025
Add different port handling
MKowalski8 ccb3107
Rewrite docker extraction
MKowalski8 36e8ef9
Review nits
MKowalski8 0e5361b
Merge branch 'master' into 3764-support-devnet-via-network-flag
MKowalski8 ff75ecf
Error test cases
MKowalski8 625a05a
Fix lint
MKowalski8 184ec6e
Funcs relocation
MKowalski8 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
mod direct; | ||
mod docker; | ||
mod flag_parsing; | ||
|
||
use std::process::Command; | ||
|
||
use crate::helpers::devnet::provider::DevnetProvider; | ||
|
||
pub(super) const DEFAULT_DEVNET_HOST: &str = "127.0.0.1"; | ||
pub(super) const DEFAULT_DEVNET_PORT: u16 = 5050; | ||
|
||
#[derive(Debug, Clone)] | ||
pub(super) struct ProcessInfo { | ||
pub host: String, | ||
pub port: u16, | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum DevnetDetectionError { | ||
#[error( | ||
"Could not detect running starknet-devnet instance. Please use `--url <URL>` instead or start devnet." | ||
)] | ||
NoInstance, | ||
#[error( | ||
"Multiple starknet-devnet instances found. Please use `--url <URL>` to specify which one to use." | ||
)] | ||
MultipleInstances, | ||
#[error("Failed to execute process detection command.")] | ||
CommandFailed, | ||
#[error( | ||
"Found starknet-devnet process, but could not reach it. Please use `--url <URL>` to specify the correct URL." | ||
)] | ||
ProcessNotReachable, | ||
} | ||
|
||
pub async fn detect_devnet_url() -> Result<String, DevnetDetectionError> { | ||
detect_devnet_from_processes().await | ||
} | ||
|
||
#[must_use] | ||
pub async fn is_devnet_running() -> bool { | ||
detect_devnet_from_processes().await.is_ok() | ||
} | ||
|
||
async fn detect_devnet_from_processes() -> Result<String, DevnetDetectionError> { | ||
match find_devnet_process_info() { | ||
Ok(info) => { | ||
if is_devnet_url_reachable(&info.host, info.port).await { | ||
Ok(format!("http://{}:{}", info.host, info.port)) | ||
} else { | ||
Err(DevnetDetectionError::ProcessNotReachable) | ||
} | ||
} | ||
Err(DevnetDetectionError::NoInstance | DevnetDetectionError::CommandFailed) => { | ||
// Fallback to default starknet-devnet URL if reachable | ||
if is_devnet_url_reachable(DEFAULT_DEVNET_HOST, DEFAULT_DEVNET_PORT).await { | ||
Ok(format!( | ||
"http://{DEFAULT_DEVNET_HOST}:{DEFAULT_DEVNET_PORT}" | ||
)) | ||
} else { | ||
Err(DevnetDetectionError::NoInstance) | ||
} | ||
} | ||
Err(e) => Err(e), | ||
} | ||
} | ||
|
||
fn find_devnet_process_info() -> Result<ProcessInfo, DevnetDetectionError> { | ||
let output = Command::new("sh") | ||
.args(["-c", "ps aux | grep starknet-devnet | grep -v grep"]) | ||
.output() | ||
.map_err(|_| DevnetDetectionError::CommandFailed)?; | ||
let ps_output = String::from_utf8_lossy(&output.stdout); | ||
|
||
let devnet_processes: Result<Vec<ProcessInfo>, DevnetDetectionError> = ps_output | ||
.lines() | ||
.map(|line| { | ||
if line.contains("docker") || line.contains("podman") { | ||
docker::extract_devnet_info_from_docker_run(line) | ||
} else { | ||
direct::extract_devnet_info_from_direct_run(line) | ||
} | ||
}) | ||
.collect(); | ||
|
||
let devnet_processes = devnet_processes?; | ||
|
||
match devnet_processes.as_slice() { | ||
[single] => Ok(single.clone()), | ||
[] => Err(DevnetDetectionError::NoInstance), | ||
_ => Err(DevnetDetectionError::MultipleInstances), | ||
} | ||
} | ||
|
||
async fn is_devnet_url_reachable(host: &str, port: u16) -> bool { | ||
let url = format!("http://{host}:{port}"); | ||
|
||
let provider = DevnetProvider::new(&url); | ||
provider.ensure_alive().await.is_ok() | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
MKowalski8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
use super::*; | ||
|
||
#[tokio::test] | ||
async fn test_detect_devnet_url() { | ||
let result = detect_devnet_url().await; | ||
assert!(result.is_err()); | ||
assert!(matches!( | ||
result.unwrap_err(), | ||
DevnetDetectionError::NoInstance | ||
)); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
use crate::helpers::devnet::detection::flag_parsing::{ | ||
extract_port_from_flag, extract_string_from_flag, | ||
}; | ||
use crate::helpers::devnet::detection::{ | ||
DEFAULT_DEVNET_HOST, DEFAULT_DEVNET_PORT, DevnetDetectionError, ProcessInfo, | ||
}; | ||
|
||
pub fn extract_devnet_info_from_direct_run( | ||
cmdline: &str, | ||
) -> Result<ProcessInfo, DevnetDetectionError> { | ||
let mut port = extract_port_from_flag(cmdline, "--port"); | ||
let mut host = extract_string_from_flag(cmdline, "--host"); | ||
|
||
if port.is_none() | ||
&& let Ok(port_env) = std::env::var("PORT") | ||
{ | ||
port = Some( | ||
port_env | ||
.parse() | ||
.map_err(|_| DevnetDetectionError::ProcessNotReachable)?, | ||
); | ||
} | ||
|
||
if host.is_none() | ||
&& let Ok(host_env) = std::env::var("HOST") | ||
&& !host_env.is_empty() | ||
{ | ||
host = Some(host_env); | ||
} | ||
|
||
let final_port = port.unwrap_or(DEFAULT_DEVNET_PORT); | ||
let final_host = host.unwrap_or_else(|| DEFAULT_DEVNET_HOST.to_string()); | ||
|
||
Ok(ProcessInfo { | ||
host: final_host, | ||
port: final_port, | ||
}) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
// These tests are marked to run serially to avoid interference from environment variables | ||
#[test] | ||
fn test_direct_devnet_parsing() { | ||
test_extract_devnet_info_from_cmdline(); | ||
test_extract_devnet_info_with_both_envs(); | ||
test_invalid_env(); | ||
test_cmdline_args_override_env(); | ||
test_wrong_env_var(); | ||
} | ||
|
||
fn test_extract_devnet_info_from_cmdline() { | ||
let cmdline1 = "starknet-devnet --port 6000 --host 127.0.0.1"; | ||
let info1 = extract_devnet_info_from_direct_run(cmdline1).unwrap(); | ||
assert_eq!(info1.port, 6000); | ||
assert_eq!(info1.host, "127.0.0.1"); | ||
|
||
let cmdline2 = "/usr/bin/starknet-devnet --port=5000"; | ||
let info2 = extract_devnet_info_from_direct_run(cmdline2).unwrap(); | ||
assert_eq!(info2.port, 5000); | ||
assert_eq!(info2.host, "127.0.0.1"); | ||
|
||
let cmdline3 = "starknet-devnet --host 127.0.0.1"; | ||
let info3 = extract_devnet_info_from_direct_run(cmdline3).unwrap(); | ||
assert_eq!(info3.port, 5050); | ||
assert_eq!(info3.host, "127.0.0.1"); | ||
} | ||
|
||
fn test_extract_devnet_info_with_both_envs() { | ||
// SAFETY: Variables are only modified within this test and cleaned up afterwards | ||
unsafe { | ||
std::env::set_var("PORT", "9999"); | ||
std::env::set_var("HOST", "9.9.9.9"); | ||
} | ||
|
||
let cmdline = "starknet-devnet"; | ||
let info = extract_devnet_info_from_direct_run(cmdline).unwrap(); | ||
assert_eq!(info.port, 9999); | ||
assert_eq!(info.host, "9.9.9.9"); | ||
|
||
// SAFETY: Clean up environment variables to prevent interference | ||
unsafe { | ||
std::env::remove_var("PORT"); | ||
std::env::remove_var("HOST"); | ||
} | ||
} | ||
|
||
fn test_invalid_env() { | ||
// SAFETY: Variables are only modified within this test and cleaned up afterwards | ||
unsafe { | ||
std::env::set_var("PORT", "asdf"); | ||
std::env::set_var("HOST", "9.9.9.9"); | ||
} | ||
let cmdline = "starknet-devnet"; | ||
let result = extract_devnet_info_from_direct_run(cmdline); | ||
assert!(result.is_err()); | ||
assert!(matches!( | ||
result.unwrap_err(), | ||
DevnetDetectionError::ProcessNotReachable | ||
)); | ||
|
||
// SAFETY: Clean up environment variables to prevent interference | ||
unsafe { | ||
std::env::remove_var("PORT"); | ||
std::env::remove_var("HOST"); | ||
} | ||
} | ||
|
||
fn test_cmdline_args_override_env() { | ||
// SAFETY: Variables are only modified within this test and cleaned up afterwards | ||
unsafe { | ||
std::env::set_var("PORT", "3000"); | ||
std::env::set_var("HOST", "7.7.7.7"); | ||
} | ||
|
||
let cmdline = "starknet-devnet --port 9999 --host 192.168.1.1"; | ||
let info = extract_devnet_info_from_direct_run(cmdline).unwrap(); | ||
assert_eq!(info.port, 9999); | ||
assert_eq!(info.host, "192.168.1.1"); | ||
|
||
// SAFETY: Clean up environment variables to prevent interference | ||
unsafe { | ||
std::env::remove_var("PORT"); | ||
std::env::remove_var("HOST"); | ||
} | ||
} | ||
|
||
fn test_wrong_env_var() { | ||
// SAFETY: Variables are only modified within this test and cleaned up afterwards | ||
unsafe { | ||
std::env::set_var("PORT", "asdf"); | ||
} | ||
|
||
// Empty HOST env var should be ignored and defaults should be used | ||
let cmdline = "starknet-devnet"; | ||
let result = extract_devnet_info_from_direct_run(cmdline); | ||
assert!(result.is_err()); | ||
assert!(matches!( | ||
result.unwrap_err(), | ||
DevnetDetectionError::ProcessNotReachable | ||
)); | ||
|
||
// SAFETY: Clean up environment variables to prevent interference | ||
unsafe { | ||
std::env::remove_var("PORT"); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.