···11pub mod git_protocol;22pub mod if_none_match;33+pub mod repository;34pub mod request_id;
+41
crates/gordian-knot/src/extract/repository.rs
···11+use axum::extract::FromRequestParts;22+use axum::http::request::Parts;33+use gix::ThreadSafeRepository;44+use serde::Deserialize;55+66+use crate::model::knot_state::RepositoryProvider;77+use crate::public::xrpc::XrpcError;88+use crate::public::xrpc::XrpcQuery;99+use crate::types::repository_spec::RepositoryKey;1010+1111+/// Extract a repository from the `repo` parameter of a `sh.tangled.repo.*` XRPC1212+/// request.1313+pub struct XrpcRepository(pub ThreadSafeRepository);1414+1515+#[derive(Deserialize)]1616+struct Param {1717+ repo: String,1818+}1919+2020+impl<S: Send + Sync> FromRequestParts<S> for XrpcRepository2121+where2222+ S: RepositoryProvider,2323+{2424+ type Rejection = XrpcError;2525+2626+ async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {2727+ let XrpcQuery(Param { repo }) = XrpcQuery::from_request_parts(parts, state).await?;2828+2929+ // Appview should always give use "did/name", let's just pretend its a3030+ // "did/rkey".3131+ let key: RepositoryKey = repo.parse()?;3232+3333+ // @review cardinality may be an issue.3434+ let labels = [("did", key.owner.to_string()), ("name", key.rkey.clone())];3535+ metrics::counter!("knot_repo_requests_total", &labels).increment(1);3636+3737+ let repo = state.open_repository(key).await?;3838+3939+ Ok(Self(repo))4040+ }4141+}
+2
crates/gordian-knot/src/lib.rs
···1414#[cfg(test)]1515pub(crate) mod mock;1616#[cfg(test)]1717+mod test_helpers;1818+#[cfg(test)]1719mod tests;18201921pub use gordian_lexicon as lexicon;
···11-use std::borrow::Cow;22-use std::io::Write as _;33-use std::process::Stdio;44-55-use axum::Json;66-use gordian_lexicon::sh_tangled::repo::merge_check::ConflictInfo;77-use gordian_lexicon::sh_tangled::repo::merge_check::Output;88-99-use crate::model::errors;1010-use crate::model::repository::ResolveRevspec as _;1111-use crate::model::repository::ResolvedRevspec;1212-use crate::model::repository::TempWorktree;1313-use crate::public::xrpc::XrpcError;1414-use crate::public::xrpc::XrpcResponse;1515-1616-impl super::TangledRepository {1717- pub fn merge_check(1818- &self,1919- patch: String,2020- branch: &str,2121- ) -> Result<XrpcResponse<Json<Output>>, XrpcError> {2222- let ResolvedRevspec { commit, immutable } =2323- self.repository.resolve_revspec(&Some(branch.as_ref()))?;2424-2525- let worktree = TempWorktree::builder()2626- .prefix("merge-check")2727- .config(&self.knot.git_config_path())2828- .commit(&commit.id)2929- .build(&self.repository)3030- .map_err(errors::Internal)?;3131-3232- let mut child = self3333- .git()3434- .arg("-C")3535- .arg(worktree.path())3636- .arg("apply")3737- .arg("--check")3838- .arg("--verbose")3939- .arg("-")4040- .stdin(Stdio::piped())4141- .stderr(Stdio::piped())4242- .spawn()4343- .map_err(errors::Internal)?;4444-4545- let mut stdin = child.stdin.take().expect("handle present");4646- let writer = std::thread::spawn(move || stdin.write_all(patch.as_bytes()));4747- let output = child.wait_with_output().map_err(errors::Internal)?;4848-4949- writer5050- .join()5151- .expect("thread should not panic")5252- .map_err(errors::Internal)?;5353-5454- let errors = std::str::from_utf8(&output.stderr).map_err(errors::Internal)?;5555- let conflicts = parse_git_apply_check_errors(errors);5656- let is_conflicted = !output.status.success() && !conflicts.is_empty();5757- let message = is_conflicted.then_some(Cow::Borrowed("patch cannot be applied cleanly"));5858-5959- Ok(XrpcResponse {6060- response: Json(Output {6161- is_conflicted,6262- conflicts,6363- message,6464- error: None,6565- }),6666- immutable,6767- })6868- }6969-}7070-7171-fn parse_git_apply_check_errors(stderr: &str) -> Vec<ConflictInfo> {7272- let mut hunk_name = None;7373- stderr7474- .lines()7575- .filter_map(|line| {7676- let mut parts = line.splitn(3, ':').map(|s| s.trim());7777- match (parts.next(), parts.next(), parts.next()) {7878- (Some("error"), Some("patch failed"), Some(hunk)) => {7979- hunk_name = Some(hunk);8080- None8181- }8282- (Some("error"), Some(filename), Some("already exists in working directory")) => {8383- Some(ConflictInfo {8484- filename: hunk_name.unwrap_or(filename).to_owned(),8585- reason: Cow::Borrowed("file already exists"),8686- })8787- }8888- (Some("error"), Some(filename), Some("does not exist in working tree")) => {8989- Some(ConflictInfo {9090- filename: hunk_name.unwrap_or(filename).to_owned(),9191- reason: Cow::Borrowed("file does not exist"),9292- })9393- }9494- (Some("error"), Some(filename), Some("patch does not apply")) => {9595- Some(ConflictInfo {9696- filename: hunk_name.unwrap_or(filename).to_owned(),9797- reason: Cow::Borrowed("patch does not apply"),9898- })9999- }100100- _ => None,101101- }102102- })103103- .collect()104104-}
+6-8
crates/gordian-knot/src/private.rs
···2121use serde::Deserialize;2222use serde::Serialize;2323use time::OffsetDateTime;2424-use tokio_rayon::AsyncThreadPool as _;25242625use crate::model::Knot;2726use crate::model::errors;···237238238239 let commits = {239240 let repo = repo.to_thread_local();240240- knot.pool()241241- .spawn_fifo_async(move || repo.count_commits(&new_sha, &old_sha))241241+ tokio_rayon::spawn_fifo(move || repo.count_commits(&new_sha, &old_sha))242242 };243243244244 let languages = {245245 let repo = repo.to_thread_local();246246- knot.pool()247247- .spawn_fifo_async(move || repo.language_breakdown(&new_sha))246246+ tokio_rayon::spawn_fifo(move || repo.language_breakdown(&new_sha))248247 };249248250249 let (commits, languages) = tokio::join!(commits, languages);···310313 let repository: TangledRepository = (knot.clone(), repo_key.clone(), repo).into();311314312315 // Schedule a maintenance run.313313- knot.pool().spawn(move || {314314- // We can do anything if this fails.315315- let _ = run_maintenance(repo_key, repository).inspect_err(|error| tracing::error!(?error));316316+ //317317+ // We cannot do anything if this fails.318318+ let _ = tokio_rayon::spawn(move || {319319+ run_maintenance(repo_key, repository).inspect_err(|error| tracing::error!(?error))316320 });317321318322 Ok(StatusCode::NO_CONTENT)