pub(crate) mod args;
pub(crate) mod dispatch;
pub(crate) mod github;
pub(crate) mod listen;
#[cfg(test)]
#[path = "tests.rs"]
mod tests;
use trusty_mpm::runtime::RuntimeKind;
use crate::commands::ticket::runner::{CommandRunner, RealCommandRunner};
use crate::gh_identity::{clone_url, load_gh_env};
use trusty_mpm::core::trusty_tools_config::{GithubConfig, TrustyToolsConfig};
use args::{RawWatchArgs, ResolvedWatch, resolve};
use dispatch::{DispatchMode, dispatch_issue};
use github::{GhIssueLister, IssueLister};
use listen::run_listen_loop;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct BoardRepo {
pub(crate) clone_url: String,
pub(crate) default_branch: String,
}
pub(crate) fn dispatch_mode(execute: bool, dry_run: bool) -> DispatchMode {
if execute && !dry_run {
DispatchMode::Execute
} else {
DispatchMode::DryRun
}
}
pub(crate) fn resolve_board_repo<R: CommandRunner>(
runner: &R,
repo: &str,
github: Option<&GithubConfig>,
) -> anyhow::Result<BoardRepo> {
let clone_url = clone_url(github, repo);
let out = runner.run(
"gh",
&[
"repo",
"view",
repo,
"--json",
"defaultBranchRef",
"--jq",
".defaultBranchRef.name",
],
)?;
let default_branch = out.ok_or_stderr("gh repo view")?;
let default_branch = if default_branch.is_empty() {
"main".to_string()
} else {
default_branch
};
Ok(BoardRepo {
clone_url,
default_branch,
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn poll(
client: &reqwest::Client,
url: &str,
raw: RawWatchArgs,
execute: bool,
dry_run: bool,
runtime: RuntimeKind,
) -> anyhow::Result<()> {
let config = TrustyToolsConfig::load();
let github = config.github.clone();
let settings = resolve(&raw, &config.watch.clone().unwrap_or_default())?;
let mode = dispatch_mode(execute, dry_run);
let gh_env = load_gh_env()?;
let runner = RealCommandRunner::with_env(gh_env.vars().to_vec());
let lister = GhIssueLister::new(RealCommandRunner::with_env(gh_env.vars().to_vec()));
run_poll_once(
client,
url,
&runner,
&lister,
&settings,
github.as_ref(),
mode,
runtime,
)
.await
}
#[allow(clippy::too_many_arguments)]
async fn run_poll_once<R: CommandRunner, L: IssueLister>(
client: &reqwest::Client,
url: &str,
runner: &R,
lister: &L,
settings: &ResolvedWatch,
github: Option<&GithubConfig>,
mode: DispatchMode,
runtime: RuntimeKind,
) -> anyhow::Result<()> {
let board = resolve_board_repo(runner, &settings.repo, github)?;
let issues = lister.list(&settings.repo, &settings.label, settings.state)?;
eprintln!(
"tm watch poll: {} issue(s) on {} carry label `{}` ({})",
issues.len(),
settings.repo,
settings.label,
match mode {
DispatchMode::DryRun => "dry-run",
DispatchMode::Execute => "EXECUTE",
}
);
let mut dispatched = 0usize;
for issue in &issues {
if dispatch_issue(
client,
url,
&board.clone_url,
&board.default_branch,
issue,
mode,
runtime,
)
.await?
{
dispatched += 1;
}
}
eprintln!(
"tm watch poll: done — {dispatched} dispatched, {} considered",
issues.len()
);
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn listen(
client: &reqwest::Client,
url: &str,
raw: RawWatchArgs,
execute: bool,
dry_run: bool,
runtime: RuntimeKind,
) -> anyhow::Result<()> {
let config = TrustyToolsConfig::load();
let github = config.github.clone();
let settings = resolve(&raw, &config.watch.clone().unwrap_or_default())?;
let mode = dispatch_mode(execute, dry_run);
let gh_env = load_gh_env()?;
let runner = RealCommandRunner::with_env(gh_env.vars().to_vec());
let board = resolve_board_repo(&runner, &settings.repo, github.as_ref())?;
let lister = GhIssueLister::new(RealCommandRunner::with_env(gh_env.vars().to_vec()));
run_listen_loop(
client,
url,
&lister,
&settings,
&board.clone_url,
&board.default_branch,
mode,
runtime,
)
.await
}