From acee4edc5fb68dcc47178024180444770a21c6ff Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Thu, 2 Jun 2022 00:40:54 +0200
Subject: [PATCH] feat(xtask): add subcommand release-runtime

---
 Cargo.lock                               |  12 ++
 xtask/Cargo.toml                         |   2 +
 xtask/res/runtime_release_notes.template |  18 +++
 xtask/src/main.rs                        |   6 +
 xtask/src/release_runtime.rs             | 145 +++++++++++++++++++++++
 5 files changed, 183 insertions(+)
 create mode 100644 xtask/res/runtime_release_notes.template
 create mode 100644 xtask/src/release_runtime.rs

diff --git a/Cargo.lock b/Cargo.lock
index ed959f0a8..195167106 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5230,6 +5230,16 @@ version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 
+[[package]]
+name = "placeholder"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a488674d1157858898c95280e599d63584bb2daf3a11031ce5e76887d666c16"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
 [[package]]
 name = "platforms"
 version = "2.0.0"
@@ -9031,8 +9041,10 @@ dependencies = [
  "hex",
  "memmap2 0.5.0",
  "parity-scale-codec",
+ "placeholder",
  "run_script",
  "scale-info",
+ "serde",
  "serde_json",
  "version-compare",
  "version_check",
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index c9f4dc22c..86498acee 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -19,8 +19,10 @@ codec = { package = "parity-scale-codec", version = "2", default-features = fals
 frame-metadata = "14.0.0"
 hex = "0.4"
 memmap2 = "0.5.0"
+placeholder = "1.1.3"
 run_script = "0.6.3"
 scale-info = { version = "1.0.0", features = ["bit-vec"] }
+serde = "1"
 serde_json = "1.0"
 version_check = "0.9.2"
 version-compare = "0.0.11"
diff --git a/xtask/res/runtime_release_notes.template b/xtask/res/runtime_release_notes.template
new file mode 100644
index 000000000..c33733ede
--- /dev/null
+++ b/xtask/res/runtime_release_notes.template
@@ -0,0 +1,18 @@
+# Runtimes
+
+The runtimes have been built using [{srtool_version}](https://github.com/paritytech/srtool) and `{rustc_version}`.
+
+## ÄžDev
+
+```
+🏋️ Runtime Size:		{runtime_human_size} ({runtime_size} bytes)
+🔥 Core Version:		{core_version}
+🗜 Compressed:		Yes: {compression_percent} %
+🎁 Metadata version:		{metadata_version}
+🗳️ system.setCode hash:   {proposal_hash}
+#️⃣ Blake2-256 hash:    {blake2_256}
+```
+
+# Changes
+
+{changes}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index e569049c4..934d2f496 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -15,6 +15,7 @@
 // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
 
 mod gen_calls_doc;
+mod release_runtime;
 
 use anyhow::{Context, Result};
 use clap::Parser;
@@ -48,6 +49,8 @@ enum DuniterXTaskCommand {
         /// Raw spec filepath
         raw_spec: PathBuf,
     },
+    /// Release a new runtime
+    ReleaseRuntime { spec_version: u32 },
     /// Execute unit tests and integration tests
     /// End2tests are skipped
     Test,
@@ -74,6 +77,9 @@ fn main() -> Result<()> {
         DuniterXTaskCommand::InjectRuntimeCode { runtime, raw_spec } => {
             inject_runtime_code(&raw_spec, &runtime)
         }
+        DuniterXTaskCommand::ReleaseRuntime { spec_version } => {
+            release_runtime::release_runtime(spec_version)
+        }
         DuniterXTaskCommand::Test => test(),
     }
 }
diff --git a/xtask/src/release_runtime.rs b/xtask/src/release_runtime.rs
new file mode 100644
index 000000000..e4e20a5a7
--- /dev/null
+++ b/xtask/src/release_runtime.rs
@@ -0,0 +1,145 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Substrate-Libre-Currency is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use anyhow::{anyhow, Context, Result};
+use serde::Deserialize;
+use std::io::Read;
+use std::process::Command;
+
+#[derive(Deserialize)]
+struct Srtool {
+    gen: String,
+    rustc: String,
+    runtimes: SrtoolRuntimes,
+}
+
+#[derive(Deserialize)]
+struct SrtoolRuntimes {
+    compact: SrtoolRuntime,
+    compressed: SrtoolRuntime,
+}
+
+#[derive(Deserialize)]
+struct SrtoolRuntime {
+    subwasm: SrtoolRuntimeSubWasm,
+}
+
+#[derive(Deserialize)]
+struct SrtoolRuntimeSubWasm {
+    core_version: String,
+    metadata_version: u32,
+    size: u32,
+    blake2_256: String,
+    proposal_hash: String,
+}
+
+pub(super) fn release_runtime(_spec_version: u32) -> Result<()> {
+    // Get current dir
+    let pwd = std::env::current_dir()?
+        .into_os_string()
+        .into_string()
+        .map_err(|_| anyhow!("Fail to read current dir path: invalid utf8 string!"))?;
+
+    // TODO: check spec_version in the code and bump if necessary (with a commit)
+
+    // TODO: create and push a git tag runtime-{spec_version}
+
+    // Get current dir
+    let pwd = std::env::current_dir()?
+        .into_os_string()
+        .into_string()
+        .map_err(|_| anyhow!("Fail to read current dir path: invalid utf8 string!"))?;
+
+    // Build the new runtime
+    println!("Build gdev-runtime… (take a while)");
+    let output = Command::new("docker")
+        .args([
+            "run",
+            "-i",
+            "--rm",
+            "-e",
+            "PACKAGE=gdev-runtime",
+            "-e",
+            "RUNTIME_DIR=runtime/gdev",
+            "-v",
+            &format!("{}:/build", pwd),
+            "paritytech/srtool:1.60.0",
+            "build",
+            "--app",
+            "--json",
+            "-cM",
+        ])
+        .output()?;
+
+    // Read the srtool json output
+    let srtool: Srtool = serde_json::from_str(
+        std::str::from_utf8(&output.stdout)?
+            .lines()
+            .last()
+            .ok_or(anyhow!("empty srtool output"))?,
+    )
+    .with_context(|| "Fail to parse srtool json output")?;
+
+    // Generate release notes
+    let release_notes =
+        gen_release_notes(srtool).with_context(|| "Fail to generate release notes")?;
+
+    // TODO: Call gitlab API to publish the release notes (and upload the wasm)
+    println!("{}", release_notes);
+
+    Ok(())
+}
+
+fn gen_release_notes(srtool: Srtool) -> Result<String> {
+    // Read template file
+    const RELEASE_NOTES_TEMPLATE_FILEPATH: &str = "xtask/res/runtime_release_notes.template";
+    let mut file = std::fs::File::open(RELEASE_NOTES_TEMPLATE_FILEPATH)?;
+    let mut template = String::new();
+    file.read_to_string(&mut template)?;
+
+    // Prepare srtool values
+    let uncompressed_size = srtool.runtimes.compact.subwasm.size;
+    let wasm = srtool.runtimes.compressed.subwasm;
+    let compression_percent = (1.0 - (wasm.size as f64 / uncompressed_size as f64)) * 100.0;
+
+    // TODO: get changes (list of MRs) from gitlab API
+    let changes = String::new();
+
+    // Fill template values
+    let mut values = std::collections::HashMap::new();
+    values.insert("srtool_version".to_owned(), srtool.gen);
+    values.insert("rustc_version".to_owned(), srtool.rustc);
+    values.insert(
+        "runtime_human_size".to_owned(),
+        format!("{} KB", wasm.size / 1_024),
+    );
+    values.insert("runtime_size".to_owned(), wasm.size.to_string());
+    values.insert("core_version".to_owned(), wasm.core_version);
+    values.insert(
+        "compression_percent".to_owned(),
+        format!("{:.2}", compression_percent),
+    );
+    values.insert(
+        "metadata_version".to_owned(),
+        wasm.metadata_version.to_string(),
+    );
+    values.insert("proposal_hash".to_owned(), wasm.proposal_hash);
+    values.insert("blake2_256".to_owned(), wasm.blake2_256);
+    values.insert("changes".to_owned(), changes);
+
+    // Render template
+    placeholder::render(&template, &values).map_err(|e| anyhow!("Fail to render template: {}", e))
+}
-- 
GitLab