diff --git a/mono/src/server/http_server.rs b/mono/src/server/http_server.rs index 2463ce309..48f100939 100644 --- a/mono/src/server/http_server.rs +++ b/mono/src/server/http_server.rs @@ -143,11 +143,6 @@ pub async fn start_http(ctx: AppContext, options: CommonHttpOptions) { } }); - tokio::spawn(super::init_tasks::run_initialization_tasks( - host.clone(), - port, - )); - tokio::pin!(server_handle); tokio::select! { diff --git a/mono/src/server/init_tasks.rs b/mono/src/server/init_tasks.rs deleted file mode 100644 index 79abbdc5e..000000000 --- a/mono/src/server/init_tasks.rs +++ /dev/null @@ -1,445 +0,0 @@ -use std::{ - path::{Path, PathBuf}, - process::Command, - time::Duration, -}; - -use tokio::time::sleep; - -const GIT_USER_EMAIL: &str = "mega-bot@example.com"; -const GIT_USER_NAME: &str = "Mega Bot"; -const BUCKAL_BUNDLES_REPO: &str = "https://github.com/buck2hub/buckal-bundles.git"; -const LIBRA_REPO: &str = "https://github.com/web3infra-foundation/libra.git"; -const COMMIT_MSG: &str = "import buckal-bundles"; -// Relative path from project root -const IMPORT_SCRIPT_PATH: &str = "scripts/import-buck2-deps/import-buck2-deps.py"; - -/// Runs the server initialization tasks asynchronously. -/// -/// This function spawns a blocking task to run workflows that depend on the server being up and running. -/// It waits for a short duration to ensure the server has started before kicking off the workflows. -/// -/// # Arguments -/// -/// * `host` - The host address the server is listening on. -/// * `port` - The port number the server is listening on. -pub async fn run_initialization_tasks(host: String, port: u16) { - tracing::info!("Initialization tasks scheduled. Waiting 5s for server startup..."); - sleep(Duration::from_secs(5)).await; - - let target_host = if host == "0.0.0.0" { - "127.0.0.1".to_string() - } else { - host - }; - let base_url = format!("http://{}:{}", target_host, port); - - // Run blocking tasks in a blocking thread to avoid blocking the async runtime - let _ = tokio::task::spawn_blocking(move || { - tracing::info!("Starting initialization workflows against {}", base_url); - - if let Err(e) = run_buckal_bundles_workflow(&base_url) { - tracing::error!("Buckal Bundles workflow failed: {:?}", e); - } else { - tracing::info!("Buckal Bundles workflow completed."); - } - - if let Err(e) = run_libra_workflow(&base_url) { - tracing::error!("Libra workflow failed: {:?}", e); - } else { - tracing::info!("Libra workflow completed."); - } - }) - .await; -} - -/// Executes the workflow to import buckal-bundles. -/// -/// This involves cloning the toolchains repo, importing the buckal-bundles repo into it, -/// committing the changes, and creating/merging a Change List (CL). -/// -/// # Arguments -/// -/// * `base_url` - The base URL of the Mega server. -fn run_buckal_bundles_workflow(base_url: &str) -> anyhow::Result<()> { - let timestamp = chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0); - let temp_dir = std::env::temp_dir().join(format!("mega-init-buckal-{}", timestamp)); - std::fs::create_dir_all(&temp_dir)?; - - // Use a closure to ensure cleanup happens even if errors occur - let result = (|| { - let toolchains_dir = prepare_toolchains_repo(base_url, &temp_dir)?; - - import_buckal_bundles(&toolchains_dir)?; - - commit_and_push(&toolchains_dir, COMMIT_MSG)?; - - handle_merge_request(base_url, COMMIT_MSG) - })(); - - // Cleanup - let _ = std::fs::remove_dir_all(&temp_dir); - result -} - -/// Prepares the toolchains repository by cloning it and configuring the git user. -/// -/// # Arguments -/// -/// * `base_url` - The base URL of the Mega server. -/// * `temp_dir` - The temporary directory where the repo should be cloned. -/// -/// # Returns -/// -/// The path to the cloned toolchains directory. -fn prepare_toolchains_repo(base_url: &str, temp_dir: &Path) -> anyhow::Result { - tracing::info!("Cloning toolchains to {:?}", temp_dir); - let toolchains_url = format!("{}/toolchains.git", base_url); - - run_git(temp_dir, &["clone", &toolchains_url])?; - - let toolchains_dir = temp_dir.join("toolchains"); - - // Configure git user - run_git(&toolchains_dir, &["config", "user.email", GIT_USER_EMAIL])?; - run_git(&toolchains_dir, &["config", "user.name", GIT_USER_NAME])?; - - Ok(toolchains_dir) -} - -/// Imports the buckal-bundles repository into the toolchains directory. -/// -/// This clones the buckal-bundles repo and removes its .git directory to treat it as a submodule/vendored code. -/// -/// # Arguments -/// -/// * `toolchains_dir` - The path to the toolchains directory. -fn import_buckal_bundles(toolchains_dir: &Path) -> anyhow::Result<()> { - tracing::info!("Cloning buckal-bundles inside toolchains..."); - run_git( - toolchains_dir, - &["clone", "--depth", "1", BUCKAL_BUNDLES_REPO], - )?; - - // remove buckal-bundles/.git file - let buckal_git_dir = toolchains_dir.join("buckal-bundles").join(".git"); - if buckal_git_dir.exists() { - std::fs::remove_dir_all(&buckal_git_dir)?; - } - Ok(()) -} - -/// Commits all changes in the directory and pushes them to the remote. -/// -/// # Arguments -/// -/// * `dir` - The directory containing the git repository. -/// * `msg` - The commit message. -fn commit_and_push(dir: &Path, msg: &str) -> anyhow::Result<()> { - tracing::info!("Committing and pushing changes..."); - run_git(dir, &["add", "."])?; - run_git(dir, &["commit", "-m", msg])?; - run_git(dir, &["push"])?; - Ok(()) -} - -/// Handles the creation and merging of a Change List (CL) for the imported changes. -/// -/// # Arguments -/// -/// * `base_url` - The base URL of the Mega server. -/// * `title` - The title of the CL to look for. -fn handle_merge_request(base_url: &str, title: &str) -> anyhow::Result<()> { - tracing::info!("Listing CLs to find merge request..."); - let client = reqwest::blocking::Client::builder() - .no_proxy() - .timeout(Duration::from_secs(10)) - .build()?; - - wait_http_ready(&client, base_url, Duration::from_secs(30))?; - - let link = find_cl_link_with_retry(&client, base_url, title, Duration::from_secs(90))?; - tracing::info!("Found CL with link: {}", link); - - merge_cl_with_retry(&client, base_url, &link, Duration::from_secs(60))?; - tracing::info!("Merge completed successfully: {}", link); - Ok(()) -} - -/// Executes the workflow to import libra dependencies. -/// -/// This involves cloning the libra repo and running a python script to import dependencies. -/// -/// # Arguments -/// -/// * `base_url` - The base URL of the Mega server. -fn run_libra_workflow(base_url: &str) -> anyhow::Result<()> { - let timestamp = chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0); - let temp_dir = std::env::temp_dir().join(format!("mega-init-libra-{}", timestamp)); - std::fs::create_dir_all(&temp_dir)?; - - let result = (|| { - tracing::info!("Cloning libra to {:?}", temp_dir); - run_git(&temp_dir, &["clone", LIBRA_REPO, "."])?; - - let script_path = std::env::current_dir()?.join(IMPORT_SCRIPT_PATH); - if !script_path.exists() { - return Err(anyhow::anyhow!( - "Import script not found at {:?}", - script_path - )); - } - - let third_party_path = temp_dir.join("third-party"); - - tracing::info!("Running import script for libra..."); - let status = Command::new("python3") - .arg(script_path) - .arg("--scan-root") - .arg(third_party_path) - .arg("--git-base-url") - .arg(base_url) - .arg("--no-signoff") - .arg("--no-gpg-sign") - .arg("--ui") - .arg("plain") - .arg("--jobs") - .arg("6") - .arg("--retry") - .arg("3") - .current_dir(&temp_dir) - .status()?; - - if !status.success() { - return Err(anyhow::anyhow!("Python script failed")); - } - Ok(()) - })(); - - let _ = std::fs::remove_dir_all(&temp_dir); - result -} - -/// Helper function to run a git command in a specific directory. -/// -/// # Arguments -/// -/// * `dir` - The directory to run the command in. -/// * `args` - The arguments to pass to the git command. -fn run_git(dir: &Path, args: &[&str]) -> anyhow::Result<()> { - let status = Command::new("git").args(args).current_dir(dir).status()?; - if !status.success() { - return Err(anyhow::anyhow!("Git command failed: git {:?}", args)); - } - Ok(()) -} - -/// Waits for the HTTP server to be ready by checking the status endpoint. -/// -/// # Arguments -/// -/// * `client` - The HTTP client to use. -/// * `base_url` - The base URL of the Mega server. -/// * `max_wait` - The maximum duration to wait. -fn wait_http_ready( - client: &reqwest::blocking::Client, - base_url: &str, - max_wait: Duration, -) -> anyhow::Result<()> { - let start = std::time::Instant::now(); - let status_url = format!("{}/api/v1/status", base_url); - let mut last_err: Option = None; - - while start.elapsed() < max_wait { - match client.get(&status_url).send() { - Ok(resp) if resp.status().is_success() => return Ok(()), - Ok(resp) => { - last_err = Some(anyhow::anyhow!( - "status endpoint not ready: {}", - resp.status() - )); - } - Err(e) => { - last_err = Some(anyhow::anyhow!("status endpoint request failed: {}", e)); - } - } - std::thread::sleep(Duration::from_secs(2)); - } - - Err(last_err.unwrap_or_else(|| anyhow::anyhow!("status endpoint not ready"))) -} - -/// Finds the Change List (CL) link for a given title with retries. -/// -/// # Arguments -/// -/// * `client` - The HTTP client to use. -/// * `base_url` - The base URL of the Mega server. -/// * `title` - The title of the CL to search for. -/// * `max_wait` - The maximum duration to wait/retry. -/// -/// # Returns -/// -/// The link identifier of the found CL. -fn find_cl_link_with_retry( - client: &reqwest::blocking::Client, - base_url: &str, - title: &str, - max_wait: Duration, -) -> anyhow::Result { - let start = std::time::Instant::now(); - let list_url = format!("{}/api/v1/cl/list", base_url); - let mut last_err: Option = None; - - while start.elapsed() < max_wait { - for page in 1..=5 { - let body = serde_json::json!({ - "pagination": { - "page": page, - "per_page": 20 - }, - "additional": { - "sort_by": "created_at", - "status": "open", - "asc": false - } - }); - - let resp = client - .post(&list_url) - .header("accept", "application/json") - .header("Content-Type", "application/json") - .json(&body) - .send(); - - let resp = match resp { - Ok(resp) => resp, - Err(e) => { - last_err = Some(anyhow::anyhow!("list CLs request failed: {}", e)); - continue; - } - }; - - let status = resp.status(); - let text = resp.text().unwrap_or_default(); - - if !status.is_success() { - let msg = if text.is_empty() { - format!("list CLs returned {}", status) - } else { - format!("list CLs returned {}: {}", status, truncate(&text, 300)) - }; - last_err = Some(anyhow::anyhow!(msg)); - continue; - } - - let json: serde_json::Value = match serde_json::from_str(&text) { - Ok(v) => v, - Err(e) => { - last_err = Some(anyhow::anyhow!("failed to parse CL list response: {}", e)); - continue; - } - }; - - if !json["req_result"].as_bool().unwrap_or(false) { - let err_msg = json["err_message"].as_str().unwrap_or("unknown error"); - last_err = Some(anyhow::anyhow!("CL list request failed: {}", err_msg)); - continue; - } - - if let Some(cl) = json["data"]["items"] - .as_array() - .and_then(|items| items.iter().find(|item| item["title"] == title)) - { - let link = cl["link"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing link"))?; - return Ok(link.to_string()); - } - } - - std::thread::sleep(Duration::from_secs(2)); - } - - Err(last_err.unwrap_or_else(|| anyhow::anyhow!("No CL found for import"))) -} - -/// Merges a Change List (CL) with retries. -/// -/// # Arguments -/// -/// * `client` - The HTTP client to use. -/// * `base_url` - The base URL of the Mega server. -/// * `link` - The link identifier of the CL to merge. -/// * `max_wait` - The maximum duration to wait/retry. -fn merge_cl_with_retry( - client: &reqwest::blocking::Client, - base_url: &str, - link: &str, - max_wait: Duration, -) -> anyhow::Result<()> { - let start = std::time::Instant::now(); - let merge_url = format!("{}/api/v1/cl/{}/merge-no-auth", base_url, link); - let mut last_err: Option = None; - - while start.elapsed() < max_wait { - let resp = client - .post(&merge_url) - .header("accept", "application/json") - .body("") - .send(); - - let resp = match resp { - Ok(resp) => resp, - Err(e) => { - last_err = Some(anyhow::anyhow!("merge request failed: {}", e)); - std::thread::sleep(Duration::from_secs(2)); - continue; - } - }; - - let status = resp.status(); - let text = resp.text().unwrap_or_default(); - - if !status.is_success() { - let msg = if text.is_empty() { - format!("merge returned {}", status) - } else { - format!("merge returned {}: {}", status, truncate(&text, 300)) - }; - last_err = Some(anyhow::anyhow!(msg)); - std::thread::sleep(Duration::from_secs(2)); - continue; - } - - let json: serde_json::Value = serde_json::from_str(&text)?; - if !json["req_result"].as_bool().unwrap_or(false) { - let err_msg = json["err_message"].as_str().unwrap_or("unknown error"); - last_err = Some(anyhow::anyhow!("Merge failed: {}", err_msg)); - std::thread::sleep(Duration::from_secs(2)); - continue; - } - - return Ok(()); - } - - Err(last_err.unwrap_or_else(|| anyhow::anyhow!("Merge failed"))) -} - -/// Truncates a string to a maximum length. -/// -/// # Arguments -/// -/// * `s` - The string to truncate. -/// * `max_len` - The maximum length. -/// -/// # Returns -/// -/// The truncated string. -fn truncate(s: &str, max_len: usize) -> String { - if s.len() <= max_len { - s.to_string() - } else { - s.chars().take(max_len).collect::() - } -} diff --git a/mono/src/server/mod.rs b/mono/src/server/mod.rs index f00633682..02c1dcb7a 100644 --- a/mono/src/server/mod.rs +++ b/mono/src/server/mod.rs @@ -1,7 +1,6 @@ use clap::Args; pub mod http_server; -pub mod init_tasks; pub mod ssh_server; #[derive(Args, Clone, Debug)] diff --git a/scripts/init_mega/README.md b/scripts/init_mega/README.md new file mode 100644 index 000000000..0371d3521 --- /dev/null +++ b/scripts/init_mega/README.md @@ -0,0 +1,100 @@ +# init_mega + +`init_mega.py` is a standalone Mega initialization script. After the Mega service is up, it runs a set of “import/initialization” workflows: + +- **Buckal Bundles import**: clones the `toolchains` repo, vendors the `buckal-bundles` repo into it and commits the changes, then uses the Mega API to find and merge the corresponding CL. +- **Libra dependency import**: clones the `libra` repo and calls the in-repo script `scripts/import-buck2-deps/import-buck2-deps.py` to import Buck2 dependencies under `third-party/` into Mega. + +This script is extracted from the server-side initialization logic so you can run initialization manually (locally or in CI) without relying on the server’s internal startup flow. + +## Requirements + +- Python 3 +- Git (available on the command line) +- A reachable Mega service (HTTP/HTTPS) + +## Parameters + +- `--base-url BASE_URL` + - Mega service base URL (default: `https://git.gitmega.com`). Use this to point to a local/dev server, for example: `http://127.0.0.1:8000` +- `--skip-buckal` + - Skip the Buckal Bundles workflow +- `--skip-libra` + - Skip the Libra workflow + +## Usage examples + +Run full initialization (use default base URL): + +```bash +python3 scripts/init_mega/init_mega.py +``` + +Run full initialization (override base URL): + +```bash +python3 scripts/init_mega/init_mega.py --base-url http://127.0.0.1:8000 +``` + +Run only Buckal Bundles (skip Libra, override base URL): + +```bash +python3 scripts/init_mega/init_mega.py --base-url http://127.0.0.1:8000 --skip-libra +``` + +Run only Libra (skip Buckal Bundles, override base URL): + +```bash +python3 scripts/init_mega/init_mega.py --base-url http://127.0.0.1:8000 --skip-buckal +``` + +Show help: + +```bash +python3 scripts/init_mega/init_mega.py --help +``` + +## How it works + +### 1) Wait for Mega to be ready + +The script polls: + +- `GET {base_url}/api/v1/status` + +When it returns HTTP 2xx, the service is considered ready and the script proceeds. + +### 2) Buckal Bundles workflow + +In a temporary directory, it performs: + +1. Clone `toolchains`: + - `git clone {base_url}/toolchains.git` +2. Configure the commit identity (repo-local): + - `git config user.email mega-bot@example.com` + - `git config user.name Mega Bot` +3. Clone `buckal-bundles` inside the `toolchains` repo: + - `git clone --depth 1 https://github.com/buck2hub/buckal-bundles.git` +4. Remove `buckal-bundles/.git` so it becomes a regular directory tracked by `toolchains` (vendoring). +5. Commit and push: + - `git add .` + - `git commit -m "import buckal-bundles"` + - `git push` +6. Use Mega APIs to find and merge the corresponding CL: + - `POST {base_url}/api/v1/cl/list` (paginate open CLs and match `title == "import buckal-bundles"`) + - `POST {base_url}/api/v1/cl/{link}/merge-no-auth` + +### 3) Libra workflow + +Also in a temporary directory: + +1. Clone `libra`: + - `git clone https://github.com/web3infra-foundation/libra.git .` +2. Use `third-party/` in the temporary directory as the scan root, and call the existing in-repo import script: + - `python3 scripts/import-buck2-deps/import-buck2-deps.py --scan-root /third-party --git-base-url {base_url} ...` + +## Notes + +- The script runs `git push`, so your machine must have push access/authentication to the Git service behind `{base_url}`. +- CL discovery for Buckal Bundles depends on an exact title match: `import buckal-bundles` (customize via the `COMMIT_MSG` constant in the script if needed). +- This script does not start the Mega service. Start the service first, then run the script. diff --git a/scripts/init_mega/init_mega.py b/scripts/init_mega/init_mega.py new file mode 100755 index 000000000..6cf6640cd --- /dev/null +++ b/scripts/init_mega/init_mega.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import shutil +import subprocess +import tempfile +import time +import urllib.request +from pathlib import Path + +# Constants +GIT_USER_EMAIL = "mega-bot@example.com" +GIT_USER_NAME = "Mega Bot" +BUCKAL_BUNDLES_REPO = "https://github.com/buck2hub/buckal-bundles.git" +LIBRA_REPO = "https://github.com/web3infra-foundation/libra.git" +COMMIT_MSG = "import buckal-bundles" +IMPORT_SCRIPT_PATH = "import-buck2-deps/import-buck2-deps.py" + +def run_git(cwd, args, check=True): + """Executes a git command in the specified directory.""" + cmd = ["git"] + list(args) + print(f"Running: {' '.join(cmd)} in {cwd}") + result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) + if check and result.returncode != 0: + print(f"Error: Git command failed with exit code {result.returncode}") + print(f"Stdout: {result.stdout}") + print(f"Stderr: {result.stderr}") + raise RuntimeError(f"Git command failed: {' '.join(cmd)}") + return result + +def api_request(method, url, data=None, headers=None): + """Performs an HTTP API request.""" + if headers is None: + headers = {} + + if "accept" not in headers: + headers["accept"] = "application/json" + + req_data = None + if data is not None: + req_data = json.dumps(data).encode("utf-8") + if "Content-Type" not in headers: + headers["Content-Type"] = "application/json" + + req = urllib.request.Request(url, data=req_data, headers=headers, method=method) + + try: + with urllib.request.urlopen(req, timeout=10) as response: + resp_body = response.read().decode("utf-8") + if response.status >= 200 and response.status < 300: + return json.loads(resp_body) if resp_body else {} + else: + raise RuntimeError(f"API request failed with status {response.status}: {resp_body}") + except Exception as e: + raise RuntimeError(f"API request to {url} failed: {e}") + +def wait_for_server(base_url, timeout=60): + """Waits for the Mega server to be ready.""" + status_url = f"{base_url.rstrip('/')}/api/v1/status" + start_time = time.time() + print(f"Waiting for server at {status_url}...") + + while time.time() - start_time < timeout: + try: + resp = api_request("GET", status_url) + # In Rust code, it checks if status is success. + # Here we assume if api_request doesn't raise, it's success. + print("Server is ready.") + return True + except Exception: + time.sleep(2) + + raise RuntimeError(f"Server at {base_url} did not become ready within {timeout}s") + +def find_cl_link(base_url, title, max_pages=5): + """Finds the CL link for a given title.""" + list_url = f"{base_url.rstrip('/')}/api/v1/cl/list" + + for page in range(1, max_pages + 1): + body = { + "pagination": { + "page": page, + "per_page": 20 + }, + "additional": { + "sort_by": "created_at", + "status": "open", + "asc": False + } + } + + try: + resp = api_request("POST", list_url, data=body) + if not resp.get("req_result"): + print(f"Warning: CL list request failed: {resp.get('err_message')}") + continue + + items = resp.get("data", {}).get("items", []) + for cl in items: + if cl.get("title") == title: + return cl.get("link") + except Exception as e: + print(f"Warning: Failed to fetch CL list page {page}: {e}") + + return None + +def merge_cl(base_url, link, timeout=60): + """Merges a CL by its link.""" + merge_url = f"{base_url.rstrip('/')}/api/v1/cl/{link}/merge-no-auth" + start_time = time.time() + + print(f"Attempting to merge CL: {link}") + while time.time() - start_time < timeout: + try: + resp = api_request("POST", merge_url) + if resp.get("req_result"): + print(f"Successfully merged CL: {link}") + return True + else: + print(f"Merge pending: {resp.get('err_message')}") + except Exception as e: + print(f"Merge attempt failed: {e}") + + time.sleep(2) + + raise RuntimeError(f"Failed to merge CL {link} within {timeout}s") + +def run_buckal_bundles_workflow(base_url): + """Executes the buckal-bundles import workflow.""" + print("--- Starting Buckal Bundles Workflow ---") + with tempfile.TemporaryDirectory(prefix="mega-init-buckal-") as temp_dir: + temp_path = Path(temp_dir) + + # Clone toolchains + toolchains_url = f"{base_url.rstrip('/')}/toolchains.git" + run_git(temp_path, ["clone", toolchains_url]) + + toolchains_dir = temp_path / "toolchains" + + # Config git + run_git(toolchains_dir, ["config", "user.email", GIT_USER_EMAIL]) + run_git(toolchains_dir, ["config", "user.name", GIT_USER_NAME]) + + # Clone buckal-bundles inside + print("Importing buckal-bundles...") + run_git(toolchains_dir, ["clone", "--depth", "1", BUCKAL_BUNDLES_REPO]) + + # Remove .git from buckal-bundles + buckal_git = toolchains_dir / "buckal-bundles" / ".git" + if buckal_git.exists(): + if buckal_git.is_dir(): + shutil.rmtree(buckal_git) + else: + buckal_git.unlink() + + # Commit and push + run_git(toolchains_dir, ["add", "."]) + run_git(toolchains_dir, ["commit", "-m", COMMIT_MSG]) + run_git(toolchains_dir, ["push"]) + + # Handle merge request + print("Finding CL to merge...") + # Give it a few seconds for the CL to be processed + time.sleep(5) + + link = None + start_find = time.time() + while time.time() - start_find < 90: + link = find_cl_link(base_url, COMMIT_MSG) + if link: + break + time.sleep(2) + + if not link: + raise RuntimeError(f"Could not find CL with title '{COMMIT_MSG}'") + + print(f"Found CL link: {link}") + merge_cl(base_url, link) + +def run_libra_workflow(base_url, project_root): + """Executes the libra import workflow.""" + print("--- Starting Libra Workflow ---") + with tempfile.TemporaryDirectory(prefix="mega-init-libra-") as temp_dir: + temp_path = Path(temp_dir) + + # Clone libra + print(f"Cloning libra to {temp_path}...") + run_git(temp_path, ["clone", LIBRA_REPO, "."]) + + # Resolve script path + script_path = project_root / IMPORT_SCRIPT_PATH + if not script_path.exists(): + raise RuntimeError(f"Import script not found at {script_path}") + + third_party_path = temp_path / "third-party" + + # Run import script + print("Running import-buck2-deps.py for libra...") + cmd = [ + "python3", str(script_path), + "--scan-root", str(third_party_path), + "--git-base-url", base_url, + "--no-signoff", + "--no-gpg-sign", + "--ui", "plain", + "--jobs", "6", + "--retry", "3" + ] + + result = subprocess.run(cmd, cwd=temp_path) + if result.returncode != 0: + raise RuntimeError(f"Libra import script failed with exit code {result.returncode}") + + print("Libra workflow completed successfully.") + +def main(): + parser = argparse.ArgumentParser(description="Mega Server Initialization Script") + parser.add_argument( + "--base-url", + default="https://git.gitmega.com", + help="Base URL of the Mega server (default: https://git.gitmega.com)", + ) + parser.add_argument("--skip-buckal", action="store_true", help="Skip buckal bundles workflow") + parser.add_argument("--skip-libra", action="store_true", help="Skip libra workflow") + + args = parser.parse_args() + + base_url = args.base_url + + print(f"Initializing Mega at {base_url}") + + # Resolve project root (where the script is located, one level up from scripts/) + script_dir = Path(__file__).parent.absolute() + project_root = script_dir.parent + + try: + # Wait for server + wait_for_server(base_url) + + if not args.skip_buckal: + run_buckal_bundles_workflow(base_url) + + if not args.skip_libra: + run_libra_workflow(base_url, project_root) + + print("\nAll initialization tasks completed successfully!") + + except Exception as e: + print(f"\nInitialization failed: {e}") + exit(1) + +if __name__ == "__main__": + main()