141 lines
4.4 KiB
Rust
141 lines
4.4 KiB
Rust
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}
|
|
```
|
|
|
|
<small>Digest: {hash}; Last Updated: {timestamp}</small>"##
|
|
);
|
|
|
|
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
|
|
}
|