pub struct GitHub<R: ProcessRunner = JobRunner> { /* private fields */ }Expand description
The real GitHub client. Generic over the ProcessRunner so tests can inject
a fake process executor; GitHub::new uses the real job-backed runner.
Wraps a ManagedClient. By default it authenticates through gh’s own
ambient login; attach a CredentialProvider with
with_credentials to supply a token per operation
— it is injected as GH_TOKEN on every gh invocation.
Implementations§
Source§impl<R: ProcessRunner> GitHub<R>
impl<R: ProcessRunner> GitHub<R>
Sourcepub fn with_runner(runner: R) -> Self
pub fn with_runner(runner: R) -> Self
Create a client driving runner — inject a fake in tests.
Sourcepub fn default_timeout(self, timeout: Duration) -> Self
pub fn default_timeout(self, timeout: Duration) -> Self
Apply a default timeout to every command this client builds.
Sourcepub fn default_env(
self,
key: impl AsRef<OsStr>,
value: impl AsRef<OsStr>,
) -> Self
pub fn default_env( self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>, ) -> Self
Set an environment variable on every command this client builds.
Sourcepub fn default_env_remove(self, key: impl AsRef<OsStr>) -> Self
pub fn default_env_remove(self, key: impl AsRef<OsStr>) -> Self
Remove an inherited environment variable on every command this client builds.
Sourcepub fn default_cancel_on(self, token: CancellationToken) -> Self
pub fn default_cancel_on(self, token: CancellationToken) -> Self
Cancel every command this client builds when token fires.
Source§impl<R: ProcessRunner> GitHub<R>
impl<R: ProcessRunner> GitHub<R>
Sourcepub fn with_credentials(self, provider: Arc<dyn CredentialProvider>) -> Self
pub fn with_credentials(self, provider: Arc<dyn CredentialProvider>) -> Self
Supply credentials per operation via a CredentialProvider — opt-in, off
by default (ambient gh auth). The resolved token is injected as GH_TOKEN
on every gh invocation, overriding the ambient login for this client.
Sourcepub fn with_token(self, token: impl Into<Secret>) -> Self
pub fn with_token(self, token: impl Into<Secret>) -> Self
Convenience for the common case: authenticate with a single static token,
injected as GH_TOKEN. Shorthand for
with_credentials(Arc::new(StaticCredential::token(token))).
Sourcepub fn with_env_token(self, var: impl Into<String>) -> Self
pub fn with_env_token(self, var: impl Into<String>) -> Self
Convenience: read the token from environment variable var at request time
(injected as GH_TOKEN); if var is unset/empty, fall back to ambient auth.
Shorthand for with_credentials(Arc::new(EnvToken::new(var))).
Source§impl<R: ProcessRunner> GitHub<R>
impl<R: ProcessRunner> GitHub<R>
Sourcepub async fn run_args(&self, args: &[&str]) -> Result<String>
pub async fn run_args(&self, args: &[&str]) -> Result<String>
Run gh <args> over string slices — gh.run_args(&["pr", "list"])
without allocating a Vec<String>. Inherent (not on the object-safe
trait), so it can take &[&str]; forwards to the same path as
GitHubApi::run.
Sourcepub async fn run_raw_args(&self, args: &[&str]) -> Result<ProcessResult<String>>
pub async fn run_raw_args(&self, args: &[&str]) -> Result<ProcessResult<String>>
Like run_args but never errors on a non-zero exit
(mirrors GitHubApi::run_raw).
Trait Implementations§
Source§impl<R: ProcessRunner> GitHubApi for GitHub<R>
impl<R: ProcessRunner> GitHubApi for GitHub<R>
Source§fn run<'life0, 'life1, 'async_trait>(
&'life0 self,
args: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn run<'life0, 'life1, 'async_trait>(
&'life0 self,
args: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh <args> in the process’s current directory, returning trimmed
stdout (throws on a non-zero exit). A raw escape hatch — you supply the whole
argv, so pass -R owner/repo to target a specific repo; the at(dir) bound
view does not re-bind it (unlike api, which is dir-bound).Source§fn run_raw<'life0, 'life1, 'async_trait>(
&'life0 self,
args: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<ProcessResult<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn run_raw<'life0, 'life1, 'async_trait>(
&'life0 self,
args: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<ProcessResult<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
GitHubApi::run but never errors on a non-zero exit — returns the
captured ProcessResult.Source§fn version<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn version<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
gh --version).Source§fn auth_status<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn auth_status<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
gh auth status exits zero). Reflects
the exit code as a bool — any non-zero exit reads as false, never an
error; only a spawn failure or timeout errors.Source§fn repo_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<RepoView>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn repo_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<RepoView>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
dir (gh repo view --json …).Source§fn pr_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<PullRequest>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<PullRequest>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
dir (gh pr list --limit 100 --json …). Returns up to
100 open PRs; use run for more.Source§fn pr_list_for_branch<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
head: &'life2 str,
base: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<PullRequest>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn pr_list_for_branch<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
head: &'life2 str,
base: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<PullRequest>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
head into base, in any state — open, closed,
or merged (gh pr list --head <head> --base <base> --state all --limit 100 --json …). Each carries its title, URL, and state. Empty when none
match; returns up to 100 (use run for more).Source§fn pr_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<PullRequest>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<PullRequest>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr view <n> --json …).Source§fn issue_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<Issue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn issue_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<Issue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
dir (gh issue list --limit 100 --json …). Returns up to 100
open issues; use run for more.Source§fn pr_create<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
spec: PrCreate,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_create<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
spec: PrCreate,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr create) — see
PrCreate for the title/body and the optional head (source branch;
None = current branch) / base (target; None = repo default).Source§fn api<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
endpoint: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn api<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
endpoint: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
gh api <endpoint>), run in dir so
a relative endpoint’s {owner}/{repo} placeholder resolves against the bound
repository — not whatever repo the process’s current directory happens to be in.Source§fn pr_merge<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
merge: PrMerge,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_merge<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
merge: PrMerge,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr merge <n> --merge|--squash|--rebase [--auto] [--delete-branch]) — see PrMerge.Source§fn pr_mark_ready<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_mark_ready<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr ready <n>).Source§fn pr_close<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
delete_branch: bool,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_close<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
delete_branch: bool,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr close <n> [--delete-branch]).Source§fn pr_checks<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<Vec<CheckRun>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_checks<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<Vec<CheckRun>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr checks <n> --json …). gh signals the overall
outcome through its exit code — 0 all passed, 8 still pending, 1 some
failed — and emits the same JSON either way, so all three return the
parsed list; branch on each entry’s bucket. A PR
with no checks at all yields an empty list (gh’s “no checks reported”
exit). Any other exit (no such PR, auth required, …) errors.Source§fn pr_review<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
action: ReviewAction,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_review<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
action: ReviewAction,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr review <n> --approve|--request-changes|--comment [--body <body>]) — see ReviewAction (request-changes/comment carry a
required body by construction).Source§fn pr_comment<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
body: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn pr_comment<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
body: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
gh pr comment <n> --body <body>).Source§fn pr_edit<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
edit: PrEdit,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_edit<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
edit: PrEdit,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr edit <n> [--title <title>] [--body <body>]). At least one of
title or body must be Some — the facade rejects both-None
before reaching the wrapper, so the default implementation is
unreachable in normal use. Defaulted to Error::Unsupported so
external implementers of the trait keep compiling when the crate
bumps.Source§fn pr_feedback<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<PrFeedback>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn pr_feedback<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<PrFeedback>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh pr view <n> --json reviews,comments).Source§fn run_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
limit: u64,
branch: Option<String>,
) -> Pin<Box<dyn Future<Output = Result<Vec<WorkflowRun>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn run_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
limit: u64,
branch: Option<String>,
) -> Pin<Box<dyn Future<Output = Result<Vec<WorkflowRun>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh run list --limit <n> [--branch <b>] --json …). branch is an owned Option<String> to keep
the trait mockall-friendly.Source§fn run_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
id: u64,
) -> Pin<Box<dyn Future<Output = Result<WorkflowRun>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn run_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
id: u64,
) -> Pin<Box<dyn Future<Output = Result<WorkflowRun>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh run view <id> --json …); the id is
WorkflowRun::database_id.Source§fn run_watch<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
id: u64,
) -> Pin<Box<dyn Future<Output = Result<WorkflowRun>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn run_watch<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
id: u64,
) -> Pin<Box<dyn Future<Output = Result<WorkflowRun>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh run watch <id>, then a run view). Inspect
conclusion for the outcome — exit codes
can’t distinguish a failed run from a cancelled one. Read moreSource§fn issue_create<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
title: &'life2 str,
body: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn issue_create<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
title: &'life2 str,
body: &'life3 str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
gh issue create --title <title> --body <body>).Source§fn issue_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<Issue>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn issue_view<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
number: u64,
) -> Pin<Box<dyn Future<Output = Result<Issue>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
body/url filled
(gh issue view <n> --json …).Source§fn release_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<Release>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn release_list<'life0, 'life1, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
) -> Pin<Box<dyn Future<Output = Result<Vec<Release>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
gh release list --limit 100 --json …); body/url
are not fetched here — use release_view.
Returns up to 100 releases; use run for more.Source§fn release_view<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
tag: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<Release>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn release_view<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
dir: &'life1 Path,
tag: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<Release>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
body/url filled
(gh release view <tag> --json …). gh reports is_latest only from
release_list; here it defaults to false.