Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130//! `atgc logs`: reading atgc's own local logs back out.//!//! Each log atgc keeps gets one reader here, named for the log it reads://! [`mod@oauth`] is `atgc logs oauth`, [`mod@pds`] is `atgc logs pds`,//! [`mod@git`] is `atgc logs git`. They are three implementations of one//! thing, and [`mod@render`] is that thing: the filters, the palette, the//! invocation grouping and the follow loop, generic over what a log's events//! happen to be. A person who has learned to read one has learned to read the//! others, which is the whole reason the rendering is shared rather than//! merely similar.//!//! These readers are the halves of the logging that cannot write. The writers//! live beside the code they instrument: [`crate::logging::oauth`] under jacquard's//! OAuth transport, [`crate::logging::pds`] under the same transport a layer out,//! [`crate::logging::git`] under the one function that spawns a git process://! for the reason those modules give: a writer's promise about what it never//! records is only auditable while the writer is small enough to read in one//! sitting, and several hundred lines of formatting and filtering would bury//! it. Nothing in this directory opens a file for writing.//!//! What both writers share is in [`crate::logging::file`]: the envelope, the size//! cap, the rotation, and the invocation id that lets a line from one log be//! laid beside a line from the other.
pub(crate) mod git;pub(crate) mod oauth;pub(crate) mod pds;pub(crate) mod render;
/// The `atgc logs` readers.#[derive(clap::Subcommand, Debug)]pub(crate) enum Command { /// Read the OAuth event log at ~/.config/atgc/oauth.jsonl (also: /// ATGC_OAUTH_LOG to change the path; ATGC_OAUTH_LOG=off disables logging) /// /// Events are grouped by invocation, newest last, and every row carries /// the invocation that wrote it and, on token events, the client_id /// fingerprint that actually went on the wire. Both generations of the /// log are read, so a rotation does not hide history. /// /// Examples: /// atgc logs oauth --failures --since 2h /// atgc logs oauth --inv 3f2a1b0c /// atgc logs oauth --event 'token_*' --json | jq .client_id.fp /// atgc logs oauth -f -n 0 /// atgc logs oauth --incident // Without this clap reflows the example block into one paragraph, and a // command line that has been wrapped is not an example of anything. #[command(verbatim_doc_comment)] Oauth { #[command(flatten)] args: OauthArgs, }, /// Read the PDS write log at ~/.config/atgc/pds.jsonl (also: ATGC_PDS_LOG /// to change the path; ATGC_PDS_LOG=off disables logging) /// /// One line per write leaving atgc and one per answer coming back: /// which collection and record key, the swapRecord/swapCommit /// precondition that was sent, and the at-uri and CID the PDS returned. /// Record content is never written here: a value appears only as an /// eight-character fingerprint, which is enough to tell two writes apart /// and not enough to reconstruct either. /// /// Same flags, same rendering and the same invocation ids as `logs /// oauth`, so a line from each can be read side by side. /// /// Examples: /// atgc logs pds --failures --since 2h /// atgc logs pds --event write_refused /// atgc logs pds --json | jq -c '{ts, op, cid}' /// atgc logs pds -f -n 0 #[command(verbatim_doc_comment)] Pds { #[command(flatten)] args: render::Args, }, /// Read the git subprocess log at ~/.config/atgc/git.jsonl (also: /// ATGC_GIT_LOG to change the path; ATGC_GIT_LOG=off disables logging) /// /// One line per git subprocess starting and one per exit: the argv as /// git received it, the directory, the exit status, and, for the /// commands that can move a ref: where HEAD stood before and after. /// This is what `git reflog` cannot say: that atgc was the thing that /// moved your branch, and with which arguments. Credentials in a remote /// URL, `-c` overrides and config values are fingerprinted, never /// written; stdin is recorded only as a length. /// /// A non-zero exit is often the answer rather than a fault here: /// `merge-base --is-ancestor` replies by exit status, so --failures /// selects everything that did not return 0. /// /// Examples: /// atgc logs git --since 30m /// atgc logs git --event exit --json | jq -c '{verb, before, after}' /// atgc logs git --inv 3f2a1b0c /// atgc logs git -f -n 0 #[command(verbatim_doc_comment)] Git { #[command(flatten)] args: render::Args, },}
/// The OAuth log's flags: the shared set, plus the one view only it has.////// `--incident` is not in [`render::Args`] because it answers a question/// about OAuth sessions specifically, and a flag the other reader would have/// to refuse is worse than a flag it never offers.#[derive(clap::Args, Debug)]pub(crate) struct OauthArgs { #[command(flatten)] pub common: render::Args, /// Compare client_ids across authorization and refresh, and look for /// overlapping invocations around a failure #[arg(long, conflicts_with_all = ["follow", "json"])] pub incident: bool,}
/// Run whichever `logs` reader was parsed.////// Not `async`: both readers only open a local file, and `--follow` blocks on/// a sleep loop rather than on a socket.pub(crate) fn run(command: Command) -> anyhow::Result<()> { match command { Command::Oauth { args } => oauth::log(args.common, args.incident), Command::Pds { args } => pds::log(args), Command::Git { args } => git::log(args), }}