diff --git a/xtask/res/get_issue.gql b/xtask/res/get_issue.gql new file mode 100644 index 0000000000000000000000000000000000000000..07bfb7afc43d1f51610b63a357593115edcb59e4 --- /dev/null +++ b/xtask/res/get_issue.gql @@ -0,0 +1,16 @@ +query GetIssueQuery($id: String!) { + project(fullPath: "nodes/rust/duniter-v2s") { + issue(iid: $id) { + iid + state + title + assignees { + edges { + node { + username + } + } + } + } + } +} diff --git a/xtask/res/schema.gql b/xtask/res/schema.gql index e20d88d968b0a4217686bfe4f25b5e35b5ea7589..6b95026033794293c7bd77b251914d44d428b9c4 100644 --- a/xtask/res/schema.gql +++ b/xtask/res/schema.gql @@ -22,6 +22,9 @@ type Project { state: IssueState milestoneTitle: [String] ): IssueConnection + issue( + iid: String! + ): Issue release(tagName: String!): Release } diff --git a/xtask/src/release_runtime.rs b/xtask/src/release_runtime.rs index d1d1cfaf8c7a63a062158f8446b033359ab5279e..8137566ee8f7d5eb5efb5c2526f5d21b662e9d3b 100644 --- a/xtask/src/release_runtime.rs +++ b/xtask/src/release_runtime.rs @@ -17,6 +17,7 @@ mod create_asset_link; mod create_release; pub mod get_changes; +pub mod get_issue; pub mod get_issues; mod get_release; diff --git a/xtask/src/release_runtime/get_issue.rs b/xtask/src/release_runtime/get_issue.rs new file mode 100644 index 0000000000000000000000000000000000000000..b300d489462218832722e10a6fb775b58849722f --- /dev/null +++ b/xtask/src/release_runtime/get_issue.rs @@ -0,0 +1,90 @@ +// 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}; +use std::fmt::Display; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/schema.gql", + query_path = "res/get_issue.gql", + response_derives = "Debug" +)] +pub struct GetIssueQuery; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +pub struct GitlabIssue { + pub title: String, + pub number: u32, + pub status: String, + pub assignees: Vec<String>, +} + +impl Display for GitlabIssue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = format!( + "#{issue_number} — {issue_title}", + issue_title = self.title, + issue_number = self.number + ); + write!(f, "{}", str) + } +} + +pub(crate) async fn get_issue(id: String) -> Result<GitlabIssue> { + // this is the important line + let request_body = GetIssueQuery::build_query(get_issue_query::Variables { id }); + + 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_issue_query::ResponseData> = res.json().await?; + + if let Some(data) = response_body.data { + if let Some(project) = data.project { + if let Some(issue) = project.issue { + Ok(GitlabIssue { + title: issue.title, + number: issue.iid.expect("iid must exist").parse()?, + status: format!("{:?}", issue.state), + assignees: issue + .assignees + .edges + .into_iter() + .flatten() + .map(|edge| edge.node.username) + .collect(), + }) + } 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")) + } +} diff --git a/xtask/src/show_milestone.rs b/xtask/src/show_milestone.rs index 7614e06769b1d7747cf4b225b455a5573f420d5b..e503b634197b7a2a4d8d5b42962103b17534e238 100644 --- a/xtask/src/show_milestone.rs +++ b/xtask/src/show_milestone.rs @@ -32,6 +32,7 @@ struct MilestoneDump { unchanged_issues: IssueCounter, open_issues: IssueCounter, closed_issues: IssueCounter, + moved_issues: IssueCounter, } #[derive(Debug, serde::Serialize, serde::Deserialize)] @@ -71,6 +72,7 @@ impl MilestoneDump { unstarted_issues: IssueCounter::new(), open_issues: IssueCounter::new(), closed_issues: IssueCounter::new(), + moved_issues: IssueCounter::new(), } } } @@ -142,6 +144,13 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin .filter(|issue| issue.status == "closed") .collect::<Vec<GitlabIssue>>() .into(), + moved_issues: previous_dump + .issues + .clone() + .into_iter() + .filter(|issue| !issues.iter().find(|p| p.number == issue.number).is_some()) + .collect::<Vec<GitlabIssue>>() + .into(), }; let mut release_notes = format!(""); @@ -150,6 +159,7 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin // Dump issues ordered by ID (for raw changelog — allows to compare with previous changelog to see new issues) let mut issues_str = "## Issues\n\n".to_string(); + issues_str.push_str(format!("Total : {}\n\n", dump.issues.len()).as_str()); issues_str.push_str( print_issues_section( "\n### Ouvertes", @@ -166,7 +176,7 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin // &mut dump.unstarted_issues.issues.iter().map(|i| get_issue(&issues, *i)).collect()).as_str()); issues_str.push_str( print_issues_section( - "\n#### Dont assignées depuis le dernier point", + "\n### Assignées depuis le dernier point", &mut dump .taken_issues .issues @@ -179,7 +189,7 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin ); issues_str.push_str( print_issues_section( - "\n#### Dont stagnantes depuis le dernier point", + "\n### Stagnantes depuis le dernier point", &mut dump .unchanged_issues .issues @@ -194,7 +204,7 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin // &mut dump.closed_issues.issues.iter().map(|i| get_issue(&issues, *i)).collect()).as_str()); issues_str.push_str( print_issues_section( - "\n#### Dont fermées depuis le dernier point", + "\n### Fermées depuis le dernier point", &mut dump .taken_issues .issues @@ -212,6 +222,17 @@ pub(super) async fn show_milestone(milestone: String, compare_with: Option<Strin ) .as_str(), ); + let mut moved: Vec<GitlabIssue> = vec![]; + for issue_number in dump.moved_issues.issues.iter() { + moved.push(fetch_issue(*issue_number).await); + } + issues_str.push_str( + print_issues_section( + "\n### Retirées depuis le dernier point (remises à plus tard)", + &mut moved, + ) + .as_str(), + ); release_notes.push_str(issues_str.as_str()); println!("{}", release_notes); Ok(()) @@ -247,3 +268,15 @@ fn get_issue(issues: &Vec<GitlabIssue>, number: u32) -> GitlabIssue { .unwrap() .clone() } + +async fn fetch_issue(number: u32) -> GitlabIssue { + let issue = crate::release_runtime::get_issue::get_issue(format!("{}", number)) + .await + .unwrap(); + GitlabIssue { + title: issue.title, + number: issue.number, + status: issue.status, + assignees: issue.assignees, + } +}