Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • nodes/rust/duniter-v2s
  • llaq/lc-core-substrate
  • pini-gh/duniter-v2s
  • vincentux/duniter-v2s
  • mildred/duniter-v2s
  • d0p1/duniter-v2s
  • bgallois/duniter-v2s
  • Nicolas80/duniter-v2s
8 results
Show changes
......@@ -10,6 +10,7 @@ type Query {
type Mutation {
releaseCreate(input: ReleaseCreateInput!): ReleaseCreatePayload
releaseAssetLinkCreate(input: ReleaseAssetLinkInput!): ReleaseAssetLinkCreatePayload
}
type Project {
......@@ -17,6 +18,11 @@ type Project {
state: MergeRequestState
milestoneTitle: String
): MergeRequestConnection
issues(
state: IssueState
milestoneTitle: [String]
): IssueConnection
release(tagName: String!): Release
}
scalar MergeRequestID
......@@ -45,6 +51,25 @@ enum MergeRequestState {
merged
}
type IssueConnection {
count: Int!
nodes: [Issue]
}
type Issue {
id: ID!
iid: String
state: IssueState!
title: String!
}
enum IssueState {
opened
closed
locked
all
}
type Pipeline {
active: Boolean!
cancelable: Boolean!
......@@ -69,8 +94,10 @@ input ReleaseAssetsInput {
input ReleaseAssetLinkInput {
name: String!
url: String!
tagName: String!
projectPath: String!
directAssetPath: String
linkType: ReleaseAssetLinkType = OTHER
linkType: ReleaseAssetLinkType = PACKAGE
}
enum ReleaseAssetLinkType {
......@@ -83,3 +110,29 @@ enum ReleaseAssetLinkType {
type ReleaseCreatePayload {
errors: [String!]!
}
type ReleaseAssetLinkCreatePayload {
errors: [String!]!
}
type Release {
id: String!
tagName: String!
assets: ReleaseAssets
}
type ReleaseAssets {
links: ReleaseAssetLinkConnection
}
type ReleaseAssetLinkConnection {
edges: [ReleaseAssetLinkEdge]
}
type ReleaseAssetLinkEdge {
node: ReleaseAssetLink
}
type ReleaseAssetLink {
directAssetUrl: String
}
......@@ -20,7 +20,7 @@ There are **{{ calls_counter }}** {{ category_name }} calls from **{{ pallets |
</details>
{# replace markdown sytax in documentation breaking the final result #}
{{ call.documentation | replace(from="# WARNING:", to="WARNING:") }}
{{ call.documentation | replace(from="# WARNING:", to="WARNING:") | replace(from="## Complexity", to="**Complexity**") }}
{% endfor -%}
{% endfor -%}
......@@ -50,7 +50,15 @@ enum DuniterXTaskCommand {
raw_spec: PathBuf,
},
/// Release a new runtime
ReleaseRuntime { spec_version: u32 },
ReleaseRuntime { milestone: String, branch: String },
/// Update raw specs locally with the files published on a Release
UpdateRawSpecs { milestone: String },
/// Create asset in a release
CreateAssetLink {
tag: String,
asset_name: String,
asset_url: String,
},
/// Execute unit tests and integration tests
/// End2tests are skipped
Test,
......@@ -78,9 +86,17 @@ async fn main() -> Result<()> {
DuniterXTaskCommand::InjectRuntimeCode { runtime, raw_spec } => {
inject_runtime_code(&raw_spec, &runtime)
}
DuniterXTaskCommand::ReleaseRuntime { spec_version } => {
release_runtime::release_runtime(spec_version).await
DuniterXTaskCommand::ReleaseRuntime { milestone, branch } => {
release_runtime::release_runtime(milestone, branch).await
}
DuniterXTaskCommand::UpdateRawSpecs { milestone } => {
release_runtime::update_raw_specs(milestone).await
}
DuniterXTaskCommand::CreateAssetLink {
tag,
asset_name,
asset_url,
} => release_runtime::create_asset_link(tag, asset_name, asset_url).await,
DuniterXTaskCommand::Test => test(),
}
}
......
......@@ -14,13 +14,15 @@
// You should have received a copy of the GNU Affero General Public License
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
mod create_asset_link;
mod create_release;
mod get_changes;
mod get_issues;
mod get_release;
use anyhow::{anyhow, Context, Result};
use serde::Deserialize;
use std::io::Read;
use std::process::Command;
use std::fs;
#[derive(Default, Deserialize)]
struct Srtool {
......@@ -59,86 +61,134 @@ struct CoreVersion {
//transaction_version: u32,
}
pub(super) async 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!"))?;
pub(super) async fn release_runtime(milestone: String, branch: String) -> Result<()> {
// TODO: check spec_version in the code and bump if necessary (with a commit)
// TODO: create and push a git tag runtime-{spec_version}
// Create target folder for runtime build
Command::new("mkdir")
.args(["-p", "runtime/gdev/target"])
.status()?;
Command::new("chmod")
.args(["777", "runtime/gdev/target"])
.status()?;
// 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.66.1-0.9.25",
"build",
"--app",
"--json",
"-cM",
])
.output()?;
let mut release_notes = String::from(
"
# Runtimes
// Read the srtool json output
let srtool: Srtool = serde_json::from_str(
std::str::from_utf8(&output.stdout)?
.lines()
.last()
.ok_or_else(|| anyhow!("empty srtool output"))?,
)
.with_context(|| "Fail to parse srtool json output")?;
",
);
// Generate release notes
let release_notes = gen_release_notes(spec_version, srtool)
.await
.with_context(|| "Fail to generate release notes")?;
let runtimes = vec![
("ĞDev", "SRTOOL_OUTPUT_GDEV"),
("ĞTest", "SRTOOL_OUTPUT_GTEST"),
("Ğ1", "SRTOOL_OUTPUT_G1"),
];
for (currency, env_var) in runtimes {
if let Ok(sr_tool_output_file) = std::env::var(env_var) {
let read = fs::read_to_string(sr_tool_output_file);
match read {
Ok(sr_tool_output) => {
release_notes.push_str(
gen_release_notes(currency.to_string(), sr_tool_output)
.with_context(|| {
format!("Fail to generate release notes for {}", currency)
})?
.as_str(),
);
}
Err(e) => {
eprintln!("srtool JSON output could not be read ({}). Skipped.", e)
}
}
}
}
// TODO: Call gitlab API to publish the release notes (and upload the wasm)
// Get changes (list of MRs) from gitlab API
let changes = get_changes::get_changes(milestone.clone()).await?;
release_notes.push_str(
format!(
"
# Changes
{changes}
"
)
.as_str(),
);
// Get changes (list of MRs) from gitlab API
let issues = get_issues::get_issues(milestone.clone()).await?;
release_notes.push_str(
format!(
"
# Issues
{issues}
"
)
.as_str(),
);
println!("{}", release_notes);
let gitlab_token =
std::env::var("GITLAB_TOKEN").with_context(|| "missing env var GITLAB_TOKEN")?;
create_release::create_release(gitlab_token, spec_version, release_notes).await?;
create_release::create_release(gitlab_token, branch, milestone, release_notes.to_string())
.await?;
Ok(())
}
async fn gen_release_notes(spec_version: u32, srtool: Srtool) -> Result<String> {
pub(super) async fn update_raw_specs(milestone: String) -> Result<()> {
let specs = vec!["gdev-raw.json", "gtest-raw.json", "g1-raw.json"];
println!("Fetching release info…");
let assets = get_release::get_release(milestone).await?;
for spec in specs {
if let Some(gdev_raw_specs) = assets.iter().find(|asset| asset.ends_with(spec)) {
println!("Downloading {}…", spec);
let client = reqwest::Client::new();
let res = client.get(gdev_raw_specs).send().await?;
let write_to = format!("./node/specs/{}", spec);
fs::write(write_to, res.bytes().await?)?;
}
}
println!("Done.");
Ok(())
}
fn gen_release_notes(currency: String, srtool_output: String) -> Result<String> {
println!("Read srtool output… ");
// Read the srtool json output
let srtool: Srtool = serde_json::from_str(
srtool_output
.lines()
.last()
.ok_or_else(|| anyhow!("empty srtool output"))?,
)
.with_context(|| "Fail to parse srtool json output")?;
// 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)?;
let template = String::from(
"
## {currency}
```
🔨 Srtool version: {srtool_version}
🦀 Rustc version: {rustc_version}
🏋️ 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}
```
",
);
// 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;
// Get changes (list of MRs) from gitlab API
let changes = get_changes::get_changes(spec_version).await?;
// Fill template values
let mut values = std::collections::HashMap::new();
values.insert("currency".to_owned(), currency);
values.insert("srtool_version".to_owned(), srtool.gen);
values.insert("rustc_version".to_owned(), srtool.rustc);
values.insert(
......@@ -163,8 +213,19 @@ async fn gen_release_notes(spec_version: u32, srtool: Srtool) -> Result<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))
}
pub(crate) async fn create_asset_link(
tag: String,
asset_name: String,
asset_url: String,
) -> Result<()> {
let gitlab_token =
std::env::var("GITLAB_TOKEN").with_context(|| "missing env var GITLAB_TOKEN")?;
create_asset_link::create_asset_link(gitlab_token, tag, asset_name, asset_url).await?;
Ok(())
}
// Copyright 2021 Axiom-Team
//
// This file is part of Duniter-v2S.
//
// Duniter-v2S 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.
//
// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use anyhow::{anyhow, Result};
use graphql_client::{GraphQLQuery, Response};
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "res/schema.gql",
query_path = "res/create_asset_link.gql",
response_derives = "Debug"
)]
pub struct CreateReleaseAssetLinkMutation;
pub(super) async fn create_asset_link(
gitlab_token: String,
tag: String,
asset_name: String,
asset_url: String,
) -> Result<()> {
// this is the important line
let request_body = CreateReleaseAssetLinkMutation::build_query(
create_release_asset_link_mutation::Variables {
name: asset_name,
url: asset_url,
tag,
},
);
let client = reqwest::Client::new();
let res = client
.post("https://git.duniter.org/api/graphql")
.header("PRIVATE-TOKEN", gitlab_token)
.json(&request_body)
.send()
.await?;
let response_body: Response<create_release_asset_link_mutation::ResponseData> =
res.json().await?;
if let Some(data) = response_body.data {
if let Some(body) = data.release_asset_link_create {
if body.errors.is_empty() {
Ok(())
} else {
println!("{} errors:", body.errors.len());
for error in body.errors {
println!("{}", error);
}
Err(anyhow!("Logic errors"))
}
} else if let Some(errors) = response_body.errors {
Err(anyhow!("Errors: {:?}", errors))
} else {
Err(anyhow!("Invalid response: no release_asset_link_create"))
}
} else if let Some(errors) = response_body.errors {
println!("{} errors:", errors.len());
for error in errors {
println!("{}", error);
}
Err(anyhow!("GraphQL errors"))
} else {
Err(anyhow!("Invalid response: no data nor errors"))
}
}
......@@ -27,14 +27,16 @@ pub struct CreateReleaseMutation;
pub(super) async fn create_release(
gitlab_token: String,
spec_version: u32,
branch: String,
milestone: String,
release_notes: String,
) -> Result<()> {
// this is the important line
let request_body = CreateReleaseMutation::build_query(create_release_mutation::Variables {
branch: format!("release/runtime-{}", spec_version - (spec_version % 100)),
branch,
description: release_notes,
milestone: format!("runtime-{}", spec_version),
milestone,
links: vec![],
});
let client = reqwest::Client::new();
......@@ -57,6 +59,8 @@ pub(super) async fn create_release(
}
Err(anyhow!("Logic errors"))
}
} else if let Some(errors) = response_body.errors {
Err(anyhow!("Errors: {:?}", errors))
} else {
Err(anyhow!("Invalid response: no release_create"))
}
......
......@@ -25,11 +25,9 @@ use graphql_client::{GraphQLQuery, Response};
)]
pub struct GetChangesQuery;
pub(super) async fn get_changes(spec_version: u32) -> Result<String> {
pub(super) async fn get_changes(milestone: String) -> Result<String> {
// this is the important line
let request_body = GetChangesQuery::build_query(get_changes_query::Variables {
milestone: format!("runtime-{}", spec_version),
});
let request_body = GetChangesQuery::build_query(get_changes_query::Variables { milestone });
let client = reqwest::Client::new();
let res = client
......
// Copyright 2021 Axiom-Team
//
// This file is part of Duniter-v2S.
//
// Duniter-v2S 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.
//
// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use anyhow::{anyhow, Result};
use graphql_client::{GraphQLQuery, Response};
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "res/schema.gql",
query_path = "res/get_issues.gql",
response_derives = "Debug"
)]
pub struct GetIssuesQuery;
pub(super) async fn get_issues(milestone: String) -> Result<String> {
// this is the important line
let request_body = GetIssuesQuery::build_query(get_issues_query::Variables { milestone });
let client = reqwest::Client::new();
let res = client
.post("https://git.duniter.org/api/graphql")
.json(&request_body)
.send()
.await?;
let response_body: Response<get_issues_query::ResponseData> = res.json().await?;
if let Some(data) = response_body.data {
if let Some(project) = data.project {
if let Some(issues) = project.issues {
if let Some(nodes) = issues.nodes {
let mut changes = String::new();
for issue in nodes.into_iter().flatten() {
changes.push_str(&format!(
"* {issue_title} (#{issue_number} - {issue_status:?})\n",
issue_title = issue.title,
issue_number = issue.iid.expect("iid must exist"),
issue_status = issue.state
));
}
Ok(changes)
} else {
Err(anyhow!("No changes found"))
}
} else {
Err(anyhow!("No changes found"))
}
} else {
Err(anyhow!("Project not found"))
}
} else if let Some(errors) = response_body.errors {
println!("{} errors:", errors.len());
for error in errors {
println!("{}", error);
}
Err(anyhow!("GraphQL errors"))
} else {
Err(anyhow!("Invalid response: no data nor errors"))
}
}
// Copyright 2021 Axiom-Team
//
// This file is part of Duniter-v2S.
//
// Duniter-v2S 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.
//
// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use anyhow::{anyhow, Result};
use graphql_client::{GraphQLQuery, Response};
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "res/schema.gql",
query_path = "res/get_release.gql",
response_derives = "Debug"
)]
pub struct GetReleaseOfProjectQuery;
pub(super) async fn get_release(milestone: String) -> Result<Vec<String>> {
// this is the important line
let request_body =
GetReleaseOfProjectQuery::build_query(get_release_of_project_query::Variables {
milestone,
});
let client = reqwest::Client::new();
let res = client
.post("https://git.duniter.org/api/graphql")
.json(&request_body)
.send()
.await?;
let response_body: Response<get_release_of_project_query::ResponseData> = res.json().await?;
if let Some(data) = response_body.data {
Ok(data
.project
.expect("should have project")
.release
.expect("should have release")
.assets
.expect("should have assets")
.links
.expect("should have links")
.edges
.expect("should have edges")
.into_iter()
.map(|edge| {
edge.expect("should have edge")
.node
.expect("should have node")
.direct_asset_url
.expect("should have directAssetUrl")
})
.collect())
} else if let Some(errors) = response_body.errors {
println!("{} errors:", errors.len());
for error in errors {
println!("{}", error);
}
Err(anyhow!("GraphQL errors"))
} else {
Err(anyhow!("Invalid response: no data nor errors"))
}
}