From bc54cd91fdcf8184bcf35127f8db87172ddda2dc Mon Sep 17 00:00:00 2001 From: Rahix Date: Thu, 22 May 2025 18:47:31 +0200 Subject: [PATCH] manager: Factor out a common context object Clean up the global data a bit by bundling it in a context object. --- techtree-manager/src/collect.rs | 27 +++++------- techtree-manager/src/issue.rs | 40 ++++++++---------- techtree-manager/src/main.rs | 75 +++++++++++++++++++++------------ techtree-manager/src/render.rs | 7 ++- techtree-manager/src/wiki.rs | 12 +++--- 5 files changed, 84 insertions(+), 77 deletions(-) diff --git a/techtree-manager/src/collect.rs b/techtree-manager/src/collect.rs index 81a0971..79b23af 100644 --- a/techtree-manager/src/collect.rs +++ b/techtree-manager/src/collect.rs @@ -1,14 +1,12 @@ use anyhow::Context as _; /// Read all issues to generate the full techtree -pub async fn collect_tree( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::IssueEventMeta, -) -> anyhow::Result { - let issues = forgejo +pub async fn collect_tree(ctx: &crate::Context) -> anyhow::Result { + let issues = ctx + .forgejo .issue_list_issues( - &meta.issue.repository.owner, - &meta.issue.repository.name, + &ctx.owner, + &ctx.repo, forgejo_api::structs::IssueListIssuesQuery { // We also want the closed issues state: Some(forgejo_api::structs::IssueListIssuesQueryState::All), @@ -43,10 +41,12 @@ pub async fn collect_tree( } for issue in issue_numbers.into_iter() { - let dependencies = forgejo + let dependencies = ctx + .forgejo .issue_list_issue_dependencies( - &meta.issue.repository.owner, - &meta.issue.repository.name, + &ctx.owner, + &ctx.repo, + // Why the hell is the issue number a string here? &issue.to_string(), forgejo_api::structs::IssueListIssueDependenciesQuery { limit: Some(10000), @@ -54,12 +54,7 @@ pub async fn collect_tree( }, ) .await - .with_context(|| { - format!( - "Failed to fetch issue dependencies for #{}", - meta.issue.number - ) - })?; + .with_context(|| format!("Failed to fetch issue dependencies for #{issue}",))?; for dep in dependencies { let dep_number = dep.number.context("Missing issue number in dependency")?; diff --git a/techtree-manager/src/issue.rs b/techtree-manager/src/issue.rs index 9f1f5b7..d87b852 100644 --- a/techtree-manager/src/issue.rs +++ b/techtree-manager/src/issue.rs @@ -8,17 +8,16 @@ pub struct BotCommentInfo { } pub async fn make_bot_comment( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::IssueEventMeta, + ctx: &crate::Context, issue_number: u64, ) -> anyhow::Result { let initial_message = "_Please be patient, this issue is currently being integrated into the techtree..._"; - let res = forgejo + let res = ctx.forgejo .issue_create_comment( - &meta.issue.repository.owner, - &meta.issue.repository.name, + &ctx.owner, + &ctx.repo, issue_number, forgejo_api::structs::CreateIssueCommentOption { body: initial_message.to_owned(), @@ -31,14 +30,13 @@ pub async fn make_bot_comment( } pub async fn find_bot_comment( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::RepoMeta, + ctx: &crate::Context, issue_number: u64, ) -> anyhow::Result> { - let mut comments = forgejo + let mut comments = ctx.forgejo .issue_get_comments( - &meta.owner, - &meta.name, + &ctx.owner, + &ctx.repo, issue_number, forgejo_api::structs::IssueGetCommentsQuery { ..Default::default() @@ -61,15 +59,14 @@ pub async fn find_bot_comment( } pub async fn update_bot_comment( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::RepoMeta, + ctx: &crate::Context, id: CommentId, new_body: String, ) -> anyhow::Result<()> { - forgejo + ctx.forgejo .issue_edit_comment( - &meta.owner, - &meta.name, + &ctx.owner, + &ctx.repo, id, forgejo_api::structs::EditIssueCommentOption { body: new_body, @@ -83,12 +80,11 @@ pub async fn update_bot_comment( } pub async fn remove_stale_label( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::RepoMeta, + ctx: &crate::Context, issue_number: u64, ) -> anyhow::Result<()> { - let labels = forgejo - .issue_get_labels(&meta.owner, &meta.name, issue_number) + let labels = ctx.forgejo + .issue_get_labels(&ctx.owner, &ctx.repo, issue_number) .await .context("Failed fetching issue labels")?; @@ -101,10 +97,10 @@ pub async fn remove_stale_label( if let Some(stale_label_id) = stale_label_id { log::info!("Removing `Stale` label from issue #{issue_number}..."); - let res = forgejo + let res = ctx.forgejo .issue_remove_label( - &meta.owner, - &meta.name, + &ctx.owner, + &ctx.repo, issue_number, stale_label_id, forgejo_api::structs::DeleteLabelsOption { diff --git a/techtree-manager/src/main.rs b/techtree-manager/src/main.rs index 5b7866a..4902e7a 100644 --- a/techtree-manager/src/main.rs +++ b/techtree-manager/src/main.rs @@ -10,6 +10,21 @@ mod render; mod tree; mod wiki; +pub struct Context { + /// API Accessor object + pub forgejo: forgejo_api::Forgejo, + /// Repository Owner + pub owner: String, + /// Repository Name + pub repo: String, + /// URL of the repository page + pub repo_url: url::Url, + /// URL of the repository with authentication information attached + pub repo_auth_url: url::Url, + /// Human readable timestamp of this manager run + pub timestamp: String, +} + async fn run() -> anyhow::Result<()> { let meta = if std::env::var("TECHTREE_FAKE").ok().is_some() { log::warn!("Fake tree!"); @@ -48,21 +63,29 @@ async fn run() -> anyhow::Result<()> { let forgejo = Forgejo::new(auth, server_url).context("Could not create API access object")?; + let ctx = Context { + forgejo, + owner: meta.issue.repository.owner.clone(), + repo: meta.issue.repository.name.clone(), + repo_url, + repo_auth_url, + timestamp, + }; + let new_comment_id = if meta.action == event_meta::IssueAction::Opened { - let bot_comment = - issue::find_bot_comment(&forgejo, &meta.issue.repository, meta.issue.number) - .await - .with_context(|| { - format!( - "Failed searching for bot comment for issue #{}", - meta.issue.number - ) - })?; + let bot_comment = issue::find_bot_comment(&ctx, meta.issue.number) + .await + .with_context(|| { + format!( + "Failed searching for bot comment for issue #{}", + meta.issue.number + ) + })?; let id = match bot_comment { Some(comment) => Some(comment.id), None => { - let res = issue::make_bot_comment(&forgejo, &meta, meta.issue.number).await; + let res = issue::make_bot_comment(&ctx, meta.issue.number).await; match res { Ok(id) => Some(id), Err(e) => { @@ -86,17 +109,17 @@ async fn run() -> anyhow::Result<()> { None }; - let tree = collect::collect_tree(&forgejo, &meta) + let tree = collect::collect_tree(&ctx) .await .context("Failed to collect the techtree from issue metadata")?; log::info!("Rendering and publishing techtree to git repository..."); - render::render_and_publish(&tree, ×tamp, &repo_auth_url) + render::render_and_publish(&ctx, &tree) .await .context("Failed to render and publish the techtree to git")?; if false { - let mermaid = tree.to_mermaid(&repo_url.to_string()); + let mermaid = tree.to_mermaid(&ctx.repo_url.to_string()); let wiki_text = format!( r##"This page is automatically updated to show the latest and greatest FAFO techtree: @@ -106,14 +129,9 @@ async fn run() -> anyhow::Result<()> { "## ); log::info!("Updating the wiki overview..."); - wiki::update_wiki_overview( - &forgejo, - &meta.issue.repository, - timestamp.to_string(), - wiki_text, - ) - .await - .context("Failed to update the techtree wiki page")?; + wiki::update_wiki_overview(&ctx, wiki_text) + .await + .context("Failed to update the techtree wiki page")?; } 'issues: for issue in tree.iter_issues() { @@ -123,14 +141,14 @@ async fn run() -> anyhow::Result<()> { let comment_id = if new_comment_id.is_some() && issue == meta.issue.number { new_comment_id.unwrap() } else { - let bot_comment = issue::find_bot_comment(&forgejo, &meta.issue.repository, issue) + let bot_comment = issue::find_bot_comment(&ctx, issue) .await .with_context(|| format!("Failed searching for bot comment for issue #{issue}"))?; if let Some(bot_comment) = bot_comment { if bot_comment.body.contains(&hash) { log::info!("Issue #{issue} is up-to-date, not editing comment."); - issue::remove_stale_label(&forgejo, &meta.issue.repository, issue) + issue::remove_stale_label(&ctx, issue) .await .with_context(|| { format!("Failed to remove `Stale` label from issue #{issue}") @@ -142,7 +160,7 @@ async fn run() -> anyhow::Result<()> { } else { log::warn!("Missing bot comment in issue #{issue}"); - issue::make_bot_comment(&forgejo, &meta, issue) + issue::make_bot_comment(&ctx, issue) .await .with_context(|| { format!("Failed to create a retrospective bot comment on issue #{issue}") @@ -150,7 +168,7 @@ async fn run() -> anyhow::Result<()> { } }; - let mermaid = subtree.to_mermaid(&repo_url.to_string()); + let mermaid = subtree.to_mermaid(&ctx.repo_url.to_string()); let full_text = format!( r##"## Partial Techtree @@ -158,15 +176,16 @@ async fn run() -> anyhow::Result<()> { {mermaid} ``` -Digest: {hash}; Last Updated: {timestamp}"## +Digest: {hash}; Last Updated: {timestamp}"##, + timestamp = ctx.timestamp, ); log::info!("Updating bot comment in issue #{issue} ..."); - issue::update_bot_comment(&forgejo, &meta.issue.repository, comment_id, full_text) + issue::update_bot_comment(&ctx, comment_id, full_text) .await .with_context(|| format!("Failed to update the bot comment in issue #{issue}"))?; - issue::remove_stale_label(&forgejo, &meta.issue.repository, issue) + issue::remove_stale_label(&ctx, issue) .await .with_context(|| format!("Failed to remove `Stale` label from issue #{issue}"))?; } diff --git a/techtree-manager/src/render.rs b/techtree-manager/src/render.rs index 40cefad..69682e9 100644 --- a/techtree-manager/src/render.rs +++ b/techtree-manager/src/render.rs @@ -16,9 +16,8 @@ impl SuccessExt for Command { } pub async fn render_and_publish( + ctx: &crate::Context, tree: &crate::tree::Tree, - timestamp: &str, - repo_auth_url: &url::Url, ) -> anyhow::Result<()> { let render_repo = std::path::PathBuf::from("render-git"); @@ -81,7 +80,7 @@ pub async fn render_and_publish( .arg("-C") .arg(&render_repo) .arg("commit") - .args(["-m", &format!("Updated techtree at {timestamp}")]) + .args(["-m", &format!("Updated techtree at {}", ctx.timestamp)]) .success() .context("Failed to add generated graph files to git index")?; @@ -91,7 +90,7 @@ pub async fn render_and_publish( .arg("push") .arg("--force") .arg("--quiet") - .arg(repo_auth_url.to_string()) + .arg(ctx.repo_auth_url.to_string()) .arg("HEAD:refs/heads/render") .status() .context("Failed to push rendered graph to forgejo repository")?; diff --git a/techtree-manager/src/wiki.rs b/techtree-manager/src/wiki.rs index 647aa17..0f1de44 100644 --- a/techtree-manager/src/wiki.rs +++ b/techtree-manager/src/wiki.rs @@ -2,20 +2,18 @@ use anyhow::Context as _; use base64::prelude::*; pub async fn update_wiki_overview( - forgejo: &forgejo_api::Forgejo, - meta: &crate::event_meta::RepoMeta, - timestamp: String, + ctx: &crate::Context, new_body: String, ) -> anyhow::Result<()> { // TODO: Figure out why we get a 404 when the edit was successfull... - let _ = forgejo + let _ = ctx.forgejo .repo_edit_wiki_page( - &meta.owner, - &meta.name, + &ctx.owner, + &ctx.repo, "Home", forgejo_api::structs::CreateWikiPageOptions { content_base64: Some(BASE64_STANDARD.encode(new_body.as_bytes())), - message: Some(format!("Updated to latest model at {timestamp}")), + message: Some(format!("Updated to latest model at {}", ctx.timestamp)), title: Some("Home".to_owned()), }, )