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/database/indexer.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++---- src/database/schema/mod.rs | 2 ++ src/database/schema/prefixes.rs | 5 +++++ src/database/schema/repository.rs | 14 ++++++++++++++ src/main.rs | 44 +++++++++++++++++++++++++++++++++++--------- src/methods/repo/log.rs | 8 +++++++- src/methods/repo/summary.rs | 8 +++++++- 7 files changed, 115 insertions(+), 15 deletions(-) diff --git a/src/database/indexer.rs b/src/database/indexer.rs index 11ba789..c79ecba 100644 --- a/src/database/indexer.rs +++ b/src/database/indexer.rs @@ -4,10 +4,10 @@ use std::{ 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 @@ fn update_repository_metadata(scan_path: &Path, db: &sled::Db) { 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 @@ fn find_last_committed_time(repo: &git2::Repository) -> Result, + 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 + } + } +} + fn get_relative_path<'a>(relative_to: &Path, full_path: &'a Path) -> &'a Path { full_path.strip_prefix(relative_to).unwrap() } diff --git a/src/database/schema/mod.rs b/src/database/schema/mod.rs index 9609e4f..d977149 100644 --- a/src/database/schema/mod.rs +++ b/src/database/schema/mod.rs @@ -9,3 +9,5 @@ pub mod repository; 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 +++ b/src/database/schema/prefixes.rs @@ -5,6 +5,7 @@ use crate::database::schema::repository::RepositoryId; #[repr(u8)] pub enum TreePrefix { Repository = 0, + SchemaVersion = 1, Commit = 100, Tag = 101, } @@ -45,4 +46,8 @@ impl TreePrefix { 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 +++ b/src/database/schema/repository.rs @@ -25,6 +25,9 @@ pub struct Repository<'a> { 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>; @@ -60,6 +63,17 @@ impl Repository<'_> { .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> { database .get(TreePrefix::repository_id(path)) diff --git a/src/main.rs b/src/main.rs index 75f0f66..eea8226 100644 --- a/src/main.rs +++ b/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 @@ use std::{ time::Duration, }; +use anyhow::Context; use askama::Template; use axum::{ body::Body, @@ -20,6 +22,7 @@ use axum::{ }; 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 tokio::{ }; 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 @@ impl FromStr for RefreshInterval { } #[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 @@ async fn main() -> Result<(), Box> { 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,8 +189,8 @@ async fn main() -> Result<(), Box> { .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(()) @@ -199,6 +198,33 @@ async fn main() -> Result<(), Box> { } } +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( db: Db, scan_path: PathBuf, diff --git a/src/methods/repo/log.rs b/src/methods/repo/log.rs index 6b07a4c..e43a190 100644 --- a/src/methods/repo/log.rs +++ b/src/methods/repo/log.rs @@ -84,7 +84,13 @@ pub async fn get_branch_commits( 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 +++ b/src/methods/repo/summary.rs @@ -60,7 +60,13 @@ pub async fn get_default_branch_commits( 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; -- libgit2 1.7.2