From 7d804b9c096cef6b6a14f73c7b00d4d32e34cf0d Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sun, 31 Dec 2023 13:02:14 +0000 Subject: [PATCH] Use correct default branch on log & summary pages --- src/main.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- src/database/indexer.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- src/database/schema/mod.rs | 2 ++ src/database/schema/prefixes.rs | 5 +++++ src/database/schema/repository.rs | 14 ++++++++++++++ src/methods/repo/log.rs | 8 +++++++- src/methods/repo/summary.rs | 8 +++++++- 7 files changed, 115 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75f0f66..eea8226 100644 --- a/src/main.rs +++ a/src/main.rs @@ -1,6 +1,7 @@ #![deny(clippy::pedantic)] use std::{ + borrow::Cow, fmt::{Display, Formatter}, net::SocketAddr, path::PathBuf, @@ -9,6 +10,7 @@ time::Duration, }; +use anyhow::Context; use askama::Template; use axum::{ body::Body, @@ -20,6 +22,7 @@ }; use bat::assets::HighlightingAssets; use clap::Parser; +use database::schema::{prefixes::TreePrefix, SCHEMA_VERSION}; use once_cell::sync::{Lazy, OnceCell}; use sha2::{digest::FixedOutput, Digest}; use sled::Db; @@ -30,7 +33,7 @@ }; use tower_http::cors::CorsLayer; use tower_layer::layer_fn; -use tracing::{error, info, instrument}; +use tracing::{error, info, instrument, warn}; use crate::{git::Git, layers::logger::LoggingMiddleware}; @@ -95,7 +98,7 @@ } #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), anyhow::Error> { let args: Args = Args::parse(); let subscriber = tracing_subscriber::fmt(); @@ -103,11 +106,7 @@ let subscriber = subscriber.pretty(); subscriber.init(); - let db = sled::Config::default() - .use_compression(true) - .path(&args.db_store) - .open() - .unwrap(); + let db = open_db(&args)?; let indexer_wakeup_task = run_indexer(db.clone(), args.scan_path.clone(), args.refresh_interval); @@ -190,13 +189,40 @@ .serve(app.into_make_service_with_connect_info::()); tokio::select! { - res = server => res.map_err(Box::from), - res = indexer_wakeup_task => res.map_err(Box::from), + res = server => res.context("failed to run server"), + res = indexer_wakeup_task => res.context("failed to run indexer"), _ = tokio::signal::ctrl_c() => { info!("Received ctrl-c, shutting down"); Ok(()) } + } +} + +fn open_db(args: &Args) -> Result { + let db = sled::Config::default() + .use_compression(true) + .path(&args.db_store) + .open() + .context("Failed to open database")?; + + let needs_schema_regen = match db.get(TreePrefix::schema_version())? { + Some(v) if v != SCHEMA_VERSION.as_bytes() => Some(Some(v)), + Some(_) => None, + None => Some(None), + }; + + if let Some(version) = needs_schema_regen { + let old_version = version + .as_deref() + .map_or(Cow::Borrowed("unknown"), String::from_utf8_lossy); + + warn!("Clearing outdated database ({old_version} != {SCHEMA_VERSION})"); + + db.clear()?; + db.insert(TreePrefix::schema_version(), SCHEMA_VERSION)?; } + + Ok(db) } async fn run_indexer( diff --git a/src/database/indexer.rs b/src/database/indexer.rs index 11ba789..c79ecba 100644 --- a/src/database/indexer.rs +++ a/src/database/indexer.rs @@ -1,13 +1,13 @@ use std::{ borrow::Cow, collections::HashSet, path::{Path, PathBuf}, }; -use git2::Sort; +use git2::{ErrorCode, Sort}; use ini::Ini; use time::OffsetDateTime; -use tracing::{error, info, info_span}; +use tracing::{error, info, info_span, instrument, warn}; use crate::database::schema::{ commit::Commit, @@ -56,11 +56,19 @@ owner: find_gitweb_owner(repository_path.as_path()), last_modified: find_last_committed_time(&git_repository) .unwrap_or(OffsetDateTime::UNIX_EPOCH), + default_branch: find_default_branch(&git_repository) + .ok() + .flatten() + .map(Cow::Owned), } .insert(db, relative); } } +fn find_default_branch(repo: &git2::Repository) -> Result, git2::Error> { + Ok(repo.head()?.name().map(ToString::to_string)) +} + fn find_last_committed_time(repo: &git2::Repository) -> Result { let mut timestamp = OffsetDateTime::UNIX_EPOCH; @@ -81,9 +89,13 @@ Ok(timestamp) } +#[instrument(skip(db))] fn update_repository_reflog(scan_path: &Path, db: &sled::Db) { for (relative_path, db_repository) in Repository::fetch_all(db).unwrap() { - let git_repository = git2::Repository::open(scan_path.join(&relative_path)).unwrap(); + let Some(git_repository) = open_repo(scan_path, &relative_path, db_repository.get(), db) + else { + continue; + }; for reference in git_repository.references().unwrap() { let reference = reference.unwrap(); @@ -145,9 +157,13 @@ } } +#[instrument(skip(db))] fn update_repository_tags(scan_path: &Path, db: &sled::Db) { for (relative_path, db_repository) in Repository::fetch_all(db).unwrap() { - let git_repository = git2::Repository::open(scan_path.join(&relative_path)).unwrap(); + let Some(git_repository) = open_repo(scan_path, &relative_path, db_repository.get(), db) + else { + continue; + }; let tag_tree = db_repository.get().tag_tree(db).unwrap(); @@ -191,6 +207,31 @@ info!("Removing stale tag from index"); tag_tree.remove(tag_name); + } + } +} + +#[instrument(skip(scan_path, db_repository, db))] +fn open_repo( + scan_path: &Path, + relative_path: &str, + db_repository: &Repository<'_>, + db: &sled::Db, +) -> Option { + match git2::Repository::open(scan_path.join(relative_path)) { + Ok(v) => Some(v), + Err(e) if e.code() == ErrorCode::NotFound => { + warn!("Repository gone from disk, removing from db"); + + if let Err(error) = db_repository.delete(db, relative_path) { + warn!(%error, "Failed to delete dangling index"); + } + + None + } + Err(error) => { + warn!(%error, "Failed to reindex {relative_path}, skipping"); + None } } } diff --git a/src/database/schema/mod.rs b/src/database/schema/mod.rs index 9609e4f..d977149 100644 --- a/src/database/schema/mod.rs +++ a/src/database/schema/mod.rs @@ -9,3 +9,5 @@ pub mod tag; pub type Yoked = Yoke>; + +pub const SCHEMA_VERSION: &str = "1"; diff --git a/src/database/schema/prefixes.rs b/src/database/schema/prefixes.rs index 8cfd627..2646915 100644 --- a/src/database/schema/prefixes.rs +++ a/src/database/schema/prefixes.rs @@ -5,6 +5,7 @@ #[repr(u8)] pub enum TreePrefix { Repository = 0, + SchemaVersion = 1, Commit = 100, Tag = 101, } @@ -44,5 +45,9 @@ prefixed.extend_from_slice(&repository.to_ne_bytes()); prefixed + } + + pub fn schema_version() -> &'static [u8] { + &[TreePrefix::SchemaVersion as u8] } } diff --git a/src/database/schema/repository.rs b/src/database/schema/repository.rs index a8c8c9b..8eb48e5 100644 --- a/src/database/schema/repository.rs +++ a/src/database/schema/repository.rs @@ -25,6 +25,9 @@ pub owner: Option>, /// The last time this repository was updated, currently read from the directory mtime pub last_modified: OffsetDateTime, + /// The default branch for Git operations + #[serde(borrow)] + pub default_branch: Option>, } pub type YokedRepository = Yoked>; @@ -58,6 +61,17 @@ bincode::serialize(self).unwrap(), ) .unwrap(); + } + + pub fn delete>(&self, database: &sled::Db, path: P) -> Result<()> { + for reference in self.heads(database) { + database.drop_tree(TreePrefix::commit_id(self.id, &reference))?; + } + + database.drop_tree(TreePrefix::tag_id(self.id))?; + database.remove(TreePrefix::repository_id(path))?; + + Ok(()) } pub fn open>(database: &sled::Db, path: P) -> Result> { diff --git a/src/methods/repo/log.rs b/src/methods/repo/log.rs index 6b07a4c..e43a190 100644 --- a/src/methods/repo/log.rs +++ a/src/methods/repo/log.rs @@ -84,7 +84,13 @@ return Ok(tag_tree); } - for branch in DEFAULT_BRANCHES { + for branch in repository + .get() + .default_branch + .as_deref() + .into_iter() + .chain(DEFAULT_BRANCHES.into_iter()) + { let commit_tree = repository.get().commit_tree(database, branch)?; let commits = commit_tree.fetch_latest(amount, offset).await; diff --git a/src/methods/repo/summary.rs b/src/methods/repo/summary.rs index 4302c51..0d4bb07 100644 --- a/src/methods/repo/summary.rs +++ a/src/methods/repo/summary.rs @@ -60,7 +60,13 @@ repository: &YokedRepository, database: &sled::Db, ) -> Result> { - for branch in DEFAULT_BRANCHES { + for branch in repository + .get() + .default_branch + .as_deref() + .into_iter() + .chain(DEFAULT_BRANCHES.into_iter()) + { let commit_tree = repository.get().commit_tree(database, branch)?; let commits = commit_tree.fetch_latest(11, 0).await; -- rgit 0.1.3