Skip to content

Commit

Permalink
rework the git facade
Browse files Browse the repository at this point in the history
  • Loading branch information
DenysVuika committed Jun 23, 2023
1 parent 5e2b20e commit 8b33650
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 128 deletions.
12 changes: 6 additions & 6 deletions src/db.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::git::{AuthorInfo, RepositoryInfo};
use crate::git::{AuthorInfo, GitProject};
use crate::models::PackageJsonFile;
use anyhow::Result;
use chrono::{DateTime, Utc};
Expand Down Expand Up @@ -161,7 +161,7 @@ pub fn create_project(
conn: &Connection,
name: &String,
version: &String,
origin: &String,
origin: &str,
) -> Result<i64> {
conn.execute(
"INSERT INTO projects (name, version, origin) VALUES (?1, ?2, ?3)",
Expand Down Expand Up @@ -231,10 +231,10 @@ pub fn get_project_by_snapshot(conn: &Connection, sid: i64) -> Result<ProjectInf
Ok(project_info)
}

pub fn create_snapshot(conn: &Connection, pid: i64, repo: &RepositoryInfo) -> Result<i64> {
let branch = &repo.branch;
let sha = &repo.sha;
let timestamp = &repo.timestamp;
pub fn create_snapshot(conn: &Connection, pid: i64, project: &GitProject) -> Result<i64> {
let branch = project.branch()?;
let sha = project.sha()?;
let timestamp = project.timestamp()?;

conn.execute(
"INSERT INTO snapshots (pid, branch, sha, timestamp) VALUES (?1, ?2, ?3, ?4)",
Expand Down
226 changes: 127 additions & 99 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,117 +11,145 @@ pub struct AuthorInfo {
pub commits: i64,
}

#[derive(Debug, Serialize)]
pub struct RepositoryInfo {
pub remote_url: String,
pub branch: String,
pub sha: String,
pub tags: Vec<String>,
/// last commit timestamp
pub timestamp: DateTime<Utc>,
pub struct GitProject {
repository: Repository,
working_dir: PathBuf,
}

pub fn checkout_branch(path: &PathBuf, branch: &str) -> Result<()> {
let repo = Repository::open(path)?;
let refname = branch; // or a tag (v0.1.1) or a commit (8e8128)
let (object, reference) = repo.revparse_ext(refname).expect("Object not found");
impl GitProject {
pub fn open(working_dir: &PathBuf) -> Result<Self> {
let repository = Repository::open(working_dir)?;

Ok(GitProject {
repository,
working_dir: working_dir.clone(),
})
}

/// Get the remote url
pub fn remote_url(&self) -> Result<String> {
let remote = &self.repository.find_remote("origin")?;
let url = remote.url().unwrap().to_owned();

repo.checkout_tree(&object, None)
.expect("Failed to checkout");
let remote_url = match url.strip_suffix(".git") {
Some(value) => value.to_owned(),
None => url,
};

match reference {
// gref is an actual reference like branches or tags
Some(gref) => repo.set_head(gref.name().unwrap()),
// this is a commit, not a reference
None => repo.set_head_detached(object.id()),
Ok(remote_url)
}
.expect("Failed to set HEAD");
Ok(())
}

pub fn get_repository_info(path: &PathBuf) -> Result<RepositoryInfo> {
let repo = Repository::open(path)?;
let head = repo.head()?;
let remote = repo.find_remote("origin")?;
let url = remote.url().unwrap();

let remote_url = match url.strip_suffix(".git") {
Some(value) => value,
None => url,
};

let mut tags = vec![];
let tag_names = &repo.tag_names(None)?;
for name in tag_names.into_iter().flatten() {
tags.push(name.to_owned());
/// Get the branch name
pub fn branch(&self) -> Result<String> {
let head = &self.repository.head()?;
let branch = head.shorthand().unwrap().to_owned();
Ok(branch)
}

let commit = head.peel_to_commit()?;
display_commit(&commit);
/// Get the head sha
pub fn sha(&self) -> Result<String> {
let head = &self.repository.head()?;
let sha = head.target().unwrap().to_string();
Ok(sha)
}

let timestamp = Utc.timestamp_opt(commit.time().seconds(), 0).unwrap();
pub fn authors(&self) -> Result<Vec<AuthorInfo>> {
let repo = &self.repository;
let mut rev_walker = repo.revwalk()?;
rev_walker.push_head()?;

let mut authors: Vec<AuthorInfo> = rev_walker
.map(|r| {
let oid = r?;
repo.find_commit(oid)
})
.filter_map(|c| match c {
Ok(commit) => Some(commit),
Err(e) => {
log::error!("Error walking the revisions {}, skipping", e);
None
}
})
.fold(
HashMap::new(),
|mut result: HashMap<String, AuthorInfo>, cur| {
if let Some(name) = cur.author().name() {
let author_name = name.to_string();
let mut author = result.entry(author_name).or_insert(AuthorInfo {
name: name.to_string(),
commits: 0,
});
author.commits += 1;
}
result
},
)
.into_values()
.collect();

authors.sort_by(|a, b| b.commits.cmp(&a.commits));
Ok(authors)
}

Ok(RepositoryInfo {
remote_url: remote_url.to_owned(),
branch: head.shorthand().unwrap().to_owned(),
sha: head.target().unwrap().to_string(),
tags,
timestamp,
})
}
/// Checkout a branch (main), or a tag (v0.1.1) or a commit (8e8128)
pub fn checkout(&self, ref_name: &str) -> Result<()> {
if ref_name == self.branch()? {
log::info!("Branch {} already checked out", ref_name);
return Ok(());
}

fn display_commit(commit: &Commit) {
let timestamp = commit.time().seconds();
let tm = Utc.timestamp_opt(timestamp, 0).unwrap();

log::info!(
"commit {}\nAuthor: {}\nDate: {}\n\n {}",
commit.id(),
commit.author(),
tm,
commit.message().unwrap_or("no commit message")
);
}
let repo = &self.repository;
let (object, reference) = repo.revparse_ext(ref_name).expect("Object not found");

pub fn get_repository_authors(path: &PathBuf) -> Result<Vec<AuthorInfo>> {
let repo = Repository::open(path)?;
let authors = get_commit_authors(&repo)?;
Ok(authors)
}
repo.checkout_tree(&object, None)
.expect("Failed to checkout");

fn get_commit_authors(repo: &Repository) -> Result<Vec<AuthorInfo>> {
let mut rev_walker = repo.revwalk()?;
rev_walker.push_head()?;
match reference {
// gref is an actual reference like branches or tags
Some(gref) => repo.set_head(gref.name().unwrap()),
// this is a commit, not a reference
None => repo.set_head_detached(object.id()),
}
.expect("Failed to set HEAD");

let mut authors: Vec<AuthorInfo> = rev_walker
.map(|r| {
let oid = r?;
repo.find_commit(oid)
})
.filter_map(|c| match c {
Ok(commit) => Some(commit),
Err(e) => {
log::error!("Error walking the revisions {}, skipping", e);
None
}
})
.fold(
HashMap::new(),
|mut result: HashMap<String, AuthorInfo>, cur| {
if let Some(name) = cur.author().name() {
let author_name = name.to_string();
let mut author = result.entry(author_name).or_insert(AuthorInfo {
name: name.to_string(),
commits: 0,
});
author.commits += 1;
}
result
},
)
.into_values()
.collect();

authors.sort_by(|a, b| b.commits.cmp(&a.commits));
Ok(authors)
Ok(())
}

pub fn tags(&self) -> Vec<String> {
let mut tags = vec![];
let tag_names = &self.repository.tag_names(None).unwrap();

for name in tag_names.into_iter().flatten() {
tags.push(name.to_owned());
// let obj = repo.revparse_single(name)?;
// if let Some(tag) = obj.as_tag() {
// println!("tag: {}, {}", tag.name().unwrap(), tag.message().unwrap())
// } else if let Some(commit) = obj.as_commit() {
// println!("commit: {}, {}", name, commit.author().to_string())
// }
}
tags
}

pub fn timestamp(&self) -> Result<DateTime<Utc>> {
let head = &self.repository.head()?;
let commit = head.peel_to_commit()?;
self.display_commit(&commit);

let timestamp = Utc.timestamp_opt(commit.time().seconds(), 0).unwrap();
Ok(timestamp)
}

pub fn display_commit(&self, commit: &Commit) {
let timestamp = commit.time().seconds();
let tm = Utc.timestamp_opt(timestamp, 0).unwrap();

log::info!(
"commit {}\nAuthor: {}\nDate: {}\n\n {}",
commit.id(),
commit.author(),
tm,
commit.message().unwrap_or("no commit message")
);
}
}
47 changes: 25 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub mod models;
pub mod server;

use crate::config::Config;
use crate::git::{get_repository_authors, get_repository_info, RepositoryInfo};
use crate::git::GitProject;
use crate::inspectors::*;
use crate::models::PackageJsonFile;
use crate::server::run_server;
Expand All @@ -24,7 +24,16 @@ pub async fn run(config: &Config) -> Result<()> {
panic!("Cannot find package.json file");
}

let repo = get_repository_info(&config.working_dir)?;
let git_project = GitProject::open(&config.working_dir)?;

log::info!("Current branch: {}", git_project.branch()?);
log::info!("Checking out develop branch");
git_project.checkout("develop")?;

let branch = git_project.branch()?;
let sha = git_project.sha()?;
let remote_url = git_project.remote_url()?;

let conn = db::create_connection(&config.output_dir)?;
let package = PackageJsonFile::from_file(package_json_path)?;

Expand All @@ -36,12 +45,12 @@ pub async fn run(config: &Config) -> Result<()> {
log::info!("Found the project `{}`", &name);
log::info!("Verifying snapshots...");

if let Some(snapshot) = db::get_snapshot_by_sha(&conn, &repo.sha) {
if let Some(snapshot) = db::get_snapshot_by_sha(&conn, &sha) {
log::info!(
"Snapshot {} for branch `{}` ({}) is already created.",
snapshot.oid,
&repo.branch,
&repo.sha
&branch,
&sha
);

if config.open {
Expand All @@ -56,27 +65,21 @@ pub async fn run(config: &Config) -> Result<()> {
project.id
}
Err(_) => {
// log::info!("Checking out develop branch");
// git::checkout_branch(&config.working_dir, "develop")?;

log::info!("Creating project `{}`", &name);
let pid = db::create_project(&conn, &name, &version, &repo.remote_url)?;
let pid = db::create_project(&conn, &name, &version, &remote_url)?;

log::info!("Creating tags");
db::create_tags(&conn, pid, &repo.tags)?;
let tags = git_project.tags();
db::create_tags(&conn, pid, &tags)?;

pid
}
};

log::info!(
"Creating new snapshot for branch `{}`({})",
&repo.branch,
&repo.sha
);
let sid = db::create_snapshot(&conn, pid, &repo)?;
let authors = get_repository_authors(&config.working_dir)?;
db::create_authors(&conn, sid, &authors)?;
log::info!("Creating new snapshot for branch `{}`({})", &branch, &sha);
let sid = db::create_snapshot(&conn, pid, &git_project)?;
let authors = &git_project.authors()?;
db::create_authors(&conn, sid, authors)?;

if let Some(dependencies) = package.dependencies {
if let Some(version) = dependencies.get("@angular/core") {
Expand All @@ -90,7 +93,7 @@ pub async fn run(config: &Config) -> Result<()> {
Box::new(AngularInspector {}),
];

run_inspectors(config, &conn, sid, inspectors, config.verbose, &repo)?;
run_inspectors(config, &conn, sid, inspectors, config.verbose, &git_project)?;

log::info!("Inspection complete");

Expand All @@ -109,7 +112,7 @@ fn run_inspectors(
sid: i64,
inspectors: Vec<Box<dyn FileInspector>>,
verbose: bool,
repo: &RepositoryInfo,
project: &GitProject,
) -> Result<()> {
let working_dir = &config.working_dir;
let mut types: HashMap<String, i64> = HashMap::new();
Expand All @@ -131,8 +134,8 @@ fn run_inspectors(
}

if inspector.supports_file(entry_path) {
let remote = &repo.remote_url;
let target = &repo.sha;
let remote = &project.remote_url()?;
let target = &project.sha()?;
let url = format!("{remote}/blob/{target}/{rel_path}");

let options = FileInspectorOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use env_logger::{
use log::{Level, Record};
use std::io::Write;

fn colored_level<'a>(style: &'a mut Style, level: Level) -> StyledValue<'a, &'static str> {
fn colored_level(style: &mut Style, level: Level) -> StyledValue<&'static str> {
match level {
Level::Trace => style.set_color(Color::Magenta).value("TRACE"),
Level::Debug => style.set_color(Color::Blue).value("DEBUG"),
Expand Down

0 comments on commit 8b33650

Please sign in to comment.