fix(git/hook): make sync_tags Send-safe by collecting git2 data synchronously
sync_tags held git2 types (StringArray, Repository) across .await points on sea-orm DB operations. Applied the same pattern as sync_refs: - Added collect_tag_refs() sync helper that collects all tag metadata (name, oid, description, tagger) into owned TagTip structs. - sync_tags now calls collect_tag_refs() before any .await, so no git2 types cross the async boundary. - Removed #[allow(dead_code)] from TagTip now that it's consumed.
This commit is contained in:
parent
01b18c97df
commit
7a2a3c51c4
@ -22,8 +22,7 @@ pub(crate) struct BranchTip {
|
||||
}
|
||||
|
||||
/// Owned tag data collected from git2 (no git2 types after this).
|
||||
/// Used by sync_tags; currently populated by collect_git_refs but not yet consumed.
|
||||
#[allow(dead_code)]
|
||||
/// Consumed by sync_tags via collect_tag_refs().
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct TagTip {
|
||||
pub name: String,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::hook::sync::commit::TagTip;
|
||||
use crate::GitError;
|
||||
use crate::hook::sync::HookMetaDataSync;
|
||||
use db::database::AppTransaction;
|
||||
@ -7,21 +8,15 @@ use sea_orm::*;
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl HookMetaDataSync {
|
||||
pub async fn sync_tags(&self, txn: &AppTransaction) -> Result<(), GitError> {
|
||||
let repo_id = self.repo.id;
|
||||
/// Collect all tag metadata from git2 into owned structs.
|
||||
/// This is sync and must be called from a `spawn_blocking` context.
|
||||
pub(crate) fn collect_tag_refs(&self) -> Result<Vec<TagTip>, GitError> {
|
||||
let repo = self.domain.repo();
|
||||
|
||||
let existing: Vec<repo_tag::Model> = repo_tag::Entity::find()
|
||||
.filter(repo_tag::Column::Repo.eq(repo_id))
|
||||
.all(txn)
|
||||
.await
|
||||
.map_err(|e| GitError::IoError(format!("failed to query tags: {}", e)))?;
|
||||
let mut existing_names: HashSet<String> = existing.iter().map(|t| t.name.clone()).collect();
|
||||
|
||||
let tag_names = repo
|
||||
.tag_names(None)
|
||||
.map_err(|e| GitError::Internal(e.to_string()))?;
|
||||
|
||||
let mut tags = Vec::new();
|
||||
for tag_name in tag_names.iter().flatten() {
|
||||
let full_ref = format!("refs/tags/{}", tag_name);
|
||||
let reference = match repo.find_reference(&full_ref) {
|
||||
@ -53,28 +48,53 @@ impl HookMetaDataSync {
|
||||
(None, String::new(), String::new())
|
||||
};
|
||||
|
||||
if existing_names.contains(tag_name) {
|
||||
existing_names.remove(tag_name);
|
||||
tags.push(TagTip {
|
||||
name: tag_name.to_string(),
|
||||
target_oid,
|
||||
description,
|
||||
tagger_name,
|
||||
tagger_email,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(tags)
|
||||
}
|
||||
|
||||
pub async fn sync_tags(&self, txn: &AppTransaction) -> Result<(), GitError> {
|
||||
let repo_id = self.repo.id;
|
||||
|
||||
let existing: Vec<repo_tag::Model> = repo_tag::Entity::find()
|
||||
.filter(repo_tag::Column::Repo.eq(repo_id))
|
||||
.all(txn)
|
||||
.await
|
||||
.map_err(|e| GitError::IoError(format!("failed to query tags: {}", e)))?;
|
||||
let mut existing_names: HashSet<String> = existing.iter().map(|t| t.name.clone()).collect();
|
||||
|
||||
let tags = self.collect_tag_refs()?;
|
||||
|
||||
for tag in tags {
|
||||
if existing_names.contains(&tag.name) {
|
||||
existing_names.remove(&tag.name);
|
||||
repo_tag::Entity::update_many()
|
||||
.filter(repo_tag::Column::Repo.eq(repo_id))
|
||||
.filter(repo_tag::Column::Name.eq(tag_name))
|
||||
.col_expr(repo_tag::Column::Oid, Expr::value(&target_oid))
|
||||
.col_expr(repo_tag::Column::Description, Expr::value(description))
|
||||
.col_expr(repo_tag::Column::TaggerName, Expr::value(&tagger_name))
|
||||
.col_expr(repo_tag::Column::TaggerEmail, Expr::value(&tagger_email))
|
||||
.filter(repo_tag::Column::Name.eq(&tag.name))
|
||||
.col_expr(repo_tag::Column::Oid, Expr::value(&tag.target_oid))
|
||||
.col_expr(repo_tag::Column::Description, Expr::value(tag.description.clone()))
|
||||
.col_expr(repo_tag::Column::TaggerName, Expr::value(&tag.tagger_name))
|
||||
.col_expr(repo_tag::Column::TaggerEmail, Expr::value(&tag.tagger_email))
|
||||
.exec(txn)
|
||||
.await
|
||||
.map_err(|e| GitError::IoError(format!("failed to update tag: {}", e)))?;
|
||||
} else {
|
||||
let new_tag = repo_tag::ActiveModel {
|
||||
repo: Set(repo_id),
|
||||
name: Set(tag_name.to_string()),
|
||||
oid: Set(target_oid),
|
||||
name: Set(tag.name.clone()),
|
||||
oid: Set(tag.target_oid),
|
||||
color: Set(None),
|
||||
description: Set(description),
|
||||
description: Set(tag.description.clone()),
|
||||
created_at: Set(chrono::Utc::now()),
|
||||
tagger_name: Set(tagger_name),
|
||||
tagger_email: Set(tagger_email),
|
||||
tagger_name: Set(tag.tagger_name.clone()),
|
||||
tagger_email: Set(tag.tagger_email.clone()),
|
||||
tagger: Set(None),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user