use anyhow::Context as _; use forgejo_api::Forgejo; mod collect; mod event_meta; mod issue; mod render; mod tree; mod wiki; async fn run() -> anyhow::Result<()> { let meta = if std::env::var("TECHTREE_FAKE").ok().is_some() { log::warn!("Fake tree!"); event_meta::fake() } else { event_meta::get_issue_event_meta_from_env().context("Failed reading issue event data")? }; log::info!( "Running due to event \"{:?}\" on issue #{} ...", meta.action, meta.issue.number ); let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); log::info!("Timestamp of this run is {timestamp}"); let token = std::env::var("GITHUB_TOKEN").context("Failed accessing GITHUB_TOKEN auth token")?; let auth = forgejo_api::Auth::Token(&token); let server_url = url::Url::parse( &std::env::var("GITHUB_SERVER_URL") .context("Failed reading GITHUB_SERVER_URL server url")?, ) .context("Failed parsing GITHUB_SERVER_URL as a url")?; let mut repo_auth_url = server_url.join(&format!("{}/{}", meta.issue.repository.owner, meta.issue.repository.name)).unwrap(); repo_auth_url.set_username("forgejo-actions").unwrap(); repo_auth_url.set_password(Some(&token)).unwrap(); let forgejo = Forgejo::new(auth, server_url).context("Could not create API access object")?; let new_comment_id = if meta.action == event_meta::IssueAction::Opened { let res = issue::make_bot_comment(&forgejo, &meta, meta.issue.number).await; match res { Ok(id) => Some(id), Err(e) => { log::warn!( "Error while creating the informational comment on issue #{}:\n{e:?}", meta.issue.number ); None } } } else { None }; let tree = collect::collect_tree(&forgejo, &meta) .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) .await .context("Failed to render and publish the techtree to git")?; let mermaid = tree.to_mermaid(); let wiki_text = format!( r##"This page is automatically updated to show the latest and greatest FAFO techtree: ```mermaid {mermaid} ``` "## ); 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")?; 'issues: for issue in tree.iter_issues() { let subtree = tree.subtree_for_issue(issue).unwrap(); let hash = subtree.stable_hash(); 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) .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."); continue 'issues; } bot_comment.id } else { log::warn!("Missing bot comment in issue #{issue}"); issue::make_bot_comment(&forgejo, &meta, issue) .await .with_context(|| { format!("Failed to create a retrospective bot comment on issue #{issue}") })? } }; let mermaid = subtree.to_mermaid(); let full_text = format!( r##"## Partial Techtree ```mermaid {mermaid} ``` Digest: {hash}; Last Updated: {timestamp}"## ); log::info!("Updating bot comment in issue #{issue} ..."); issue::update_bot_comment(&forgejo, &meta.issue.repository, comment_id, full_text) .await .with_context(|| format!("Failed to update the bot comment in issue #{issue}"))?; } Ok(()) } #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); run().await }