#![feature(let_chains)]
mod gen_doc;
mod release_runtime;
use anyhow::{Context, Result};
use clap::Parser;
use std::{
io::{BufReader, BufWriter},
path::{Path, PathBuf},
process::Command,
};
const MIN_RUST_VERSION: &str = "1.58.0";
#[derive(Debug, clap::Parser)]
struct DuniterXTask {
#[clap(subcommand)]
command: DuniterXTaskCommand,
}
#[derive(Debug, clap::Subcommand)]
enum DuniterXTaskCommand {
Build {
#[clap(long)]
production: bool,
},
GenDoc,
InjectRuntimeCode {
#[clap(short, long)]
runtime: PathBuf,
#[clap(short = 's', long)]
raw_spec: PathBuf,
},
ReleaseNetwork { network: String, branch: String },
ReleaseRuntime {
name: String,
network: String,
branch: String,
milestone: String,
},
PrintSpec { network: String },
CreateAssetLink {
tag: String,
asset_name: String,
asset_url: String,
},
Test,
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let args = DuniterXTask::parse();
if !version_check::is_min_version(MIN_RUST_VERSION).unwrap_or(false)
&& exec_should_success(Command::new("rustup").args(["update", "stable"])).is_err()
{
eprintln!(
"Duniter requires stable Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
MIN_RUST_VERSION
);
std::process::exit(1);
}
match &args.command {
DuniterXTaskCommand::PrintSpec { .. } => { }
_ => {
Command::new("rustc").arg("--version").status()?;
Command::new("cargo").arg("--version").status()?;
}
}
match args.command {
DuniterXTaskCommand::Build { production } => build(production),
DuniterXTaskCommand::GenDoc => gen_doc::gen_doc(),
DuniterXTaskCommand::InjectRuntimeCode { runtime, raw_spec } => {
inject_runtime_code(&raw_spec, &runtime)
}
DuniterXTaskCommand::ReleaseNetwork { network, branch } => {
release_runtime::release_network(network, branch).await
}
DuniterXTaskCommand::ReleaseRuntime {
name,
network,
branch,
milestone,
} => release_runtime::release_runtime(name, network, branch, milestone).await,
DuniterXTaskCommand::PrintSpec { network } => release_runtime::print_spec(network).await,
DuniterXTaskCommand::CreateAssetLink {
tag,
asset_name,
asset_url,
} => release_runtime::create_asset_link(tag, asset_name, asset_url).await,
DuniterXTaskCommand::Test => test(),
}
}
fn inject_runtime_code(raw_spec: &Path, runtime: &Path) -> Result<()> {
let file = std::fs::File::open(runtime).with_context(|| "Failed to open runtime wasm file")?;
let runtime_code =
unsafe { memmap2::Mmap::map(&file).with_context(|| "Failed to read runtime wasm file")? };
let file = std::fs::File::open(raw_spec).with_context(|| "Failed to open raw spec file")?;
let reader = BufReader::new(file);
let mut json: serde_json::Value =
serde_json::from_reader(reader).with_context(|| "Failed to read raw spec file")?;
println!("json raw specs loaded!");
let mut hex_runtime_code = String::with_capacity(2 + (runtime_code.len() * 2));
hex_runtime_code.push('0');
hex_runtime_code.push('x');
hex_runtime_code.push_str(&hex::encode(runtime_code));
const CODE_KEY: &str = "0x3a636f6465";
json.as_object_mut()
.with_context(|| "invalid raw spec file")?
.get_mut("genesis")
.with_context(|| "invalid raw spec file: missing field genesis")?
.as_object_mut()
.with_context(|| "invalid raw spec file")?
.get_mut("raw")
.with_context(|| "invalid raw spec file: missing field raw")?
.as_object_mut()
.with_context(|| "invalid raw spec file")?
.get_mut("top")
.with_context(|| "invalid raw spec file: missing field top")?
.as_object_mut()
.with_context(|| "invalid raw spec file")?
.insert(
CODE_KEY.to_owned(),
serde_json::Value::String(hex_runtime_code),
);
let file = std::fs::File::create(raw_spec)?;
serde_json::to_writer_pretty(BufWriter::new(file), &json)
.with_context(|| "fail to write raw specs")?;
Ok(())
}
fn build(_production: bool) -> Result<()> {
exec_should_success(Command::new("cargo").args(["clean", "-p", "duniter"]))?;
exec_should_success(Command::new("cargo").args(["build", "--locked"]))?;
exec_should_success(Command::new("mkdir").args(["build"]))?;
exec_should_success(Command::new("mv").args(["target/debug/duniter", "build/duniter"]))?;
Ok(())
}
fn test() -> Result<()> {
exec_should_success(Command::new("cargo").args([
"test",
"--workspace",
"--exclude",
"duniter-end2end-tests",
]))?;
Ok(())
}
fn exec_should_success(command: &mut Command) -> Result<()> {
if !command.status()?.success() {
std::process::exit(1);
} else {
Ok(())
}
}