🏡 index : ~doyle/rgit.git

author Jordan Doyle <jordan@doyle.la> 2023-12-31 13:02:14.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2023-12-31 13:02:14.0 +00:00:00
commit
7d804b9c096cef6b6a14f73c7b00d4d32e34cf0d [patch]
tree
a61e6fcd924f1d31b12f4a19cb8d973d6519c47a
parent
d964566b7d2c62adee90ca1d0dc22ef42bb4c50d
download
7d804b9c096cef6b6a14f73c7b00d4d32e34cf0d.tar.gz

Use correct default branch on log & summary pages



Diff

 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<Option<String>, git2::Error> {
    Ok(repo.head()?.name().map(ToString::to_string))
}

fn find_last_committed_time(repo: &git2::Repository) -> Result<OffsetDateTime, git2::Error> {
    let mut timestamp = OffsetDateTime::UNIX_EPOCH;

@@ -81,9 +89,13 @@ fn find_last_committed_time(repo: &git2::Repository) -> Result<OffsetDateTime, g
    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 @@ fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
    }
}

#[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();

@@ -195,6 +211,31 @@ fn update_repository_tags(scan_path: &Path, db: &sled::Db) {
    }
}

#[instrument(skip(scan_path, db_repository, db))]
fn open_repo(
    scan_path: &Path,
    relative_path: &str,
    db_repository: &Repository<'_>,
    db: &sled::Db,
) -> Option<git2::Repository> {
    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<T> = Yoke<T, Box<IVec>>;

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<Cow<'a, str>>,
    /// 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<Cow<'a, str>>,
}

pub type YokedRepository = Yoked<Repository<'static>>;
@@ -60,6 +63,17 @@ impl Repository<'_> {
            .unwrap();
    }

    pub fn delete<P: AsRef<Path>>(&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<P: AsRef<Path>>(database: &sled::Db, path: P) -> Result<Option<YokedRepository>> {
        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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
    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<dyn std::error::Error>> {
        .serve(app.into_make_service_with_connect_info::<SocketAddr>());

    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<dyn std::error::Error>> {
    }
}

fn open_db(args: &Args) -> Result<Db, anyhow::Error> {
    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<Vec<YokedCommit>> {
    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;