Skip to content
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

feat(zkstack_cli): Add the ability to run the server from Docker without building it #3314

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5224856
refactor: wrap server run shell command in function
manuelmauro Nov 21, 2024
b7e8386
refactor: name arg module consistently with command module
manuelmauro Nov 21, 2024
c6eb6c6
refactor: use module folder for server command
manuelmauro Nov 21, 2024
c3dccee
refactor: create module for wait subcommand
manuelmauro Nov 21, 2024
159ebdf
refactor: create module for build subcommand
manuelmauro Nov 21, 2024
9d72c07
refactor: create module for run subcommand
manuelmauro Nov 21, 2024
c85ba08
refactor: create module for server run args
manuelmauro Nov 21, 2024
80f15ee
refactor: clippy
manuelmauro Nov 21, 2024
52bbf71
feat: add debug execution mode for server
manuelmauro Nov 22, 2024
a513136
feat: draft docker execution mode for server
manuelmauro Nov 22, 2024
ba7314e
feat: remove hardcoded volume folders
manuelmauro Nov 22, 2024
9676335
feat: draft option to specify docker image's tag
manuelmauro Nov 22, 2024
ccb1740
chore: update autocompletion files
manuelmauro Nov 25, 2024
fc4a31b
Merge branch 'main' into manuel-run-server-from-docker
manuelmauro Nov 25, 2024
c4e0bc9
feat: add utils to get list of github tags
manuelmauro Nov 25, 2024
f8b5ea3
feat: add selector for docker image tag
manuelmauro Nov 25, 2024
8bc6966
fix: remove temporary test
manuelmauro Nov 25, 2024
477a11c
feat: deduplicate cargo run command
manuelmauro Nov 25, 2024
27138a2
refactor: use const for repo url
manuelmauro Nov 25, 2024
a0e3c38
refactor: remove /config volume from dockerized server
manuelmauro Nov 26, 2024
711f3b4
Merge branch 'main' into manuel-run-server-from-docker
manuelmauro Nov 26, 2024
4061cbf
feat: add mode/tag to server run subcommands
manuelmauro Nov 26, 2024
debb507
fix: fix docker volumes/paths
manuelmauro Nov 26, 2024
d80eaa5
feat: add option to run dockerized server in genesis subcommand
manuelmauro Nov 26, 2024
5b81995
chore: regenerate autocomplete files
manuelmauro Nov 26, 2024
eef40fd
chore: update autocompletion files
manuelmauro Nov 27, 2024
fa85308
refactor: bail instead of unwrapping
manuelmauro Nov 27, 2024
f990a0a
refactor: improve execution mode enum
manuelmauro Nov 27, 2024
03a615e
refactor: clippy
manuelmauro Nov 27, 2024
70c17a6
feat: add execution mode options to missing subcommands
manuelmauro Nov 27, 2024
221deb7
chore: regenerate autocompletion files
manuelmauro Nov 27, 2024
5b1854c
refactor: add message const for select tag prompt
manuelmauro Nov 27, 2024
7711a3c
refactor: wrap docker run command in a function
manuelmauro Nov 27, 2024
9087f0a
feat: use folder for config files in docker
manuelmauro Nov 27, 2024
2a2e712
Merge branch 'main' into manuel-run-server-from-docker
manuelmauro Nov 27, 2024
f3ec2ee
refactor: clippy
manuelmauro Nov 27, 2024
54b8141
docs: improve CLI help
manuelmauro Nov 28, 2024
1939883
chore: update autocompletion files
manuelmauro Nov 28, 2024
bbe5b7d
feat: add port mapping to server's docker run
manuelmauro Nov 29, 2024
698e558
feat: expose all required ports from the server
manuelmauro Nov 29, 2024
fc211f9
feat: adjust host to execution mode
manuelmauro Nov 29, 2024
f054118
fix: fix debug execution mode
manuelmauro Nov 29, 2024
1bb08b3
refactor: clippy
manuelmauro Nov 29, 2024
ff1418c
Merge branch 'main' into manuel-run-server-from-docker
manuelmauro Nov 29, 2024
a58cf27
chore: update lock file
manuelmauro Nov 29, 2024
55b0a6c
fix: adjust host only when required
manuelmauro Nov 30, 2024
c09b95e
fix: remove unwrapping
manuelmauro Nov 30, 2024
282d05f
Merge branch 'main' into manuel-run-server-from-docker
manuelmauro Dec 3, 2024
2180bd9
chore: update cargo lock
manuelmauro Dec 3, 2024
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
2 changes: 2 additions & 0 deletions zkstack_cli/Cargo.lock

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

2 changes: 2 additions & 0 deletions zkstack_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ lazy_static = "1.4.0"
once_cell = "1.19.0"
prost = "0.12.1"
rand = "0.8.5"
reqwest = { version = "0.12.8", features = ["json"] }
semver = "1.0.23"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.9"
Expand Down
2 changes: 2 additions & 0 deletions zkstack_cli/crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ xshell.workspace = true
thiserror.workspace = true
strum.workspace = true
git_version_macro.workspace = true
reqwest.workspace = true
semver.workspace = true
112 changes: 112 additions & 0 deletions zkstack_cli/crates/common/src/github.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use anyhow::{Context, Result};
use reqwest::{
header::{HeaderMap, HeaderValue},
Client,
};
use semver::Version;
use serde::{Deserialize, Serialize};

const GITHUB_API_REPO_URL: &str = "https://api.github.com/repos/matter-labs/zksync-era";

#[derive(Debug, Serialize, Deserialize)]
pub struct Tag {
pub name: String,
pub commit: Commit,
pub zipball_url: String,
pub tarball_url: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Commit {
pub sha: String,
pub url: String,
}

/// Fetches and sorts GitHub repository tags by semantic version
pub struct GitHubTagFetcher {
client: Client,
auth_token: Option<String>,
}

impl GitHubTagFetcher {
pub fn new(auth_token: Option<String>) -> Result<Self> {
let client = Client::new();
Ok(Self { client, auth_token })
}

pub async fn get_newest_core_tags(&self, limit: Option<usize>) -> Result<Vec<Tag>> {
let mut all_tags = Vec::new();
let mut page = 1;

// Set up headers
let mut headers = HeaderMap::new();
headers.insert(
"Accept",
HeaderValue::from_static("application/vnd.github+json"),
);
headers.insert("User-Agent", HeaderValue::from_static("zkstack"));
headers.insert(
"X-GitHub-Api-Version",
HeaderValue::from_static("2022-11-28"),
);

if let Some(token) = &self.auth_token {
headers.insert(
"Authorization",
HeaderValue::from_str(&format!("Bearer {}", token))
.context("Invalid authorization header")?,
);
}

// Fetch all pages
loop {
let url = format!("{}/tags?page={}&per_page=100", GITHUB_API_REPO_URL, page);

let response = self
.client
.get(&url)
.headers(headers.clone())
.send()
.await?;

if !response.status().is_success() {
return Err(anyhow::anyhow!(
"GitHub API request failed with status: {}",
response.status()
));
}

let page_tags: Vec<Tag> = response.json().await?;
if page_tags.is_empty() {
break;
}

all_tags.extend(page_tags);
page += 1;
}

// filter tag names containing "core"
all_tags.retain(|t| t.name.contains("core"));

// Sort tags by semantic version
all_tags.sort_by(|a, b| {
let version_a = clean_version(&a.name);
let version_b = clean_version(&b.name);
version_b.cmp(&version_a) // Reverse order (newest first)
});

// Apply limit if specified
Ok(if let Some(limit) = limit {
all_tags.into_iter().take(limit).collect()
} else {
all_tags
})
}
}

/// Cleans and parses version strings into semver::Version
fn clean_version(tag_name: &str) -> Version {
// Remove "core-v" prefix and parse the version string
let cleaned = tag_name.trim_start_matches("core-v");
Version::parse(cleaned).unwrap_or_else(|_| Version::new(0, 0, 0))
}
1 change: 1 addition & 0 deletions zkstack_cli/crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod external_node;
pub mod files;
pub mod forge;
pub mod git;
pub mod github;
pub mod server;
pub mod version;
pub mod wallets;
Expand Down
209 changes: 186 additions & 23 deletions zkstack_cli/crates/common/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{ffi::OsStr, path::PathBuf};

use anyhow::Context;
use xshell::{cmd, Shell};

use crate::cmd::Cmd;
Expand All @@ -19,6 +20,17 @@ pub enum ServerMode {
Genesis,
}

/// Possible execution modes.
#[derive(Clone, Debug, Default)]
pub enum ExecutionMode {
#[default]
Release,
Debug,
Docker {
tag: String,
},
}

impl Server {
/// Creates a new instance of the server.
pub fn new(components: Option<Vec<String>>, code_path: PathBuf, uring: bool) -> Self {
Expand All @@ -31,16 +43,18 @@ impl Server {

/// Runs the server.
#[allow(clippy::too_many_arguments)]
pub fn run<P>(
pub async fn run<P>(
&self,
shell: &Shell,
execution_mode: ExecutionMode,
server_mode: ServerMode,
genesis_path: P,
wallets_path: P,
general_path: P,
secrets_path: P,
contracts_path: P,
mut additional_args: Vec<String>,
ports: Vec<u16>,
) -> anyhow::Result<()>
where
P: AsRef<OsStr>,
Expand All @@ -56,28 +70,20 @@ impl Server {

let uring = self.uring.then_some("--features=rocksdb/io-uring");

let mut cmd = Cmd::new(
cmd!(
shell,
"cargo run --release --bin zksync_server {uring...} --
--genesis-path {genesis_path}
--wallets-path {wallets_path}
--config-path {general_path}
--secrets-path {secrets_path}
--contracts-config-path {contracts_path}
"
)
.args(additional_args)
.env_remove("RUSTUP_TOOLCHAIN"),
);

// If we are running server in normal mode
// we need to get the output to the console
if let ServerMode::Normal = server_mode {
cmd = cmd.with_force_run();
}

cmd.run()?;
run_server(
shell,
uring,
genesis_path,
wallets_path,
general_path,
secrets_path,
contracts_path,
additional_args,
execution_mode,
server_mode,
ports,
)
.await?;

Ok(())
}
Expand All @@ -99,3 +105,160 @@ impl Server {
})
}
}

#[allow(clippy::too_many_arguments)]
async fn run_server<P>(
shell: &Shell,
uring: Option<&str>,
genesis_path: P,
wallets_path: P,
general_path: P,
secrets_path: P,
contracts_path: P,
additional_args: Vec<String>,
execution_mode: ExecutionMode,
server_mode: ServerMode,
ports: Vec<u16>,
) -> anyhow::Result<()>
where
P: AsRef<OsStr>,
{
let mut cmd = match execution_mode {
ExecutionMode::Release => cargo_run(
shell,
true,
uring,
genesis_path,
wallets_path,
general_path,
secrets_path,
contracts_path,
additional_args,
),
ExecutionMode::Debug => cargo_run(
shell,
false,
uring,
genesis_path,
wallets_path,
general_path,
secrets_path,
contracts_path,
additional_args,
),
ExecutionMode::Docker { tag } => docker_run(
shell,
genesis_path,
wallets_path,
general_path,
secrets_path,
contracts_path,
additional_args,
tag,
ports,
),
};

// If we are running server in normal mode
// we need to get the output to the console
if let ServerMode::Normal = server_mode {
cmd = cmd.with_force_run();
}

cmd.run().context("Failed to run server")
}

#[allow(clippy::too_many_arguments)]
fn cargo_run<'a, P>(
shell: &'a Shell,
release: bool,
uring: Option<&str>,
genesis_path: P,
wallets_path: P,
general_path: P,
secrets_path: P,
contracts_path: P,
additional_args: Vec<String>,
) -> Cmd<'a>
where
P: AsRef<OsStr>,
{
let compilation_mode: &str = if release { "--release" } else { "" };

Cmd::new(
cmd!(
shell,
"cargo run {compilation_mode} --bin zksync_server {uring...} --
--genesis-path {genesis_path}
--wallets-path {wallets_path}
--config-path {general_path}
--secrets-path {secrets_path}
--contracts-config-path {contracts_path}
"
)
.args(additional_args)
.env_remove("RUSTUP_TOOLCHAIN"),
)
}

#[allow(clippy::too_many_arguments)]
fn docker_run<P>(
shell: &Shell,
genesis_path: P,
wallets_path: P,
general_path: P,
secrets_path: P,
contracts_path: P,
additional_args: Vec<String>,
tag: String,
ports: Vec<u16>,
) -> Cmd<'_>
where
P: AsRef<OsStr>,
{
let genesis_path = genesis_path.as_ref().to_string_lossy();
let wallets_path = wallets_path.as_ref().to_string_lossy();
let general_path = general_path.as_ref().to_string_lossy();
let secrets_path = secrets_path.as_ref().to_string_lossy();
let contracts_path = contracts_path.as_ref().to_string_lossy();

// do not expose postgres and reth ports
let ports = ports
.into_iter()
.filter(|p| *p != 5432 && *p != 8545)
.collect::<Vec<_>>();

let mut cmd = cmd!(shell, "docker run")
.arg("--platform")
.arg("linux/amd64")
.arg("-v")
.arg(format!("{genesis_path}:/config/genesis.yaml"))
.arg("-v")
.arg(format!("{wallets_path}:/config/wallets.yaml"))
.arg("-v")
.arg(format!("{general_path}:/config/general.yaml"))
.arg("-v")
.arg(format!("{secrets_path}:/config/secrets.yaml"))
.arg("-v")
.arg(format!("{contracts_path}:/config/contracts.yaml"));

for p in ports {
cmd = cmd.arg("-p").arg(format!("{p}:{p}"));
}

cmd = cmd
.arg(format!("matterlabs/server-v2:{tag}"))
.arg("--genesis-path")
.arg("/config/genesis.yaml")
.arg("--wallets-path")
.arg("/config/wallets.yaml")
.arg("--config-path")
.arg("/config/general.yaml")
.arg("--secrets-path")
.arg("/config/secrets.yaml")
.arg("--contracts-config-path")
.arg("/config/contracts.yaml")
.args(additional_args);

Cmd::new(cmd)
}
Loading
Loading