Skip to content

Commit 03a226d

Browse files
Merge pull request #77 from reddevilmidzy/default-branch
디폴트 브랜치 요청 처리
2 parents a183d36 + 14864f6 commit 03a226d

File tree

2 files changed

+89
-65
lines changed

2 files changed

+89
-65
lines changed

rook/src/git/mod.rs

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use serde::{Deserialize, Serialize};
44
use std::collections::HashSet;
55
use std::env;
66
use std::fs;
7+
use std::path::PathBuf;
78

89
const REGEX_URL: &str = r"https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)";
910

1011
/// Generate a unique directory name using repo owner and name
11-
fn generate_dir_name(repo_url: &str, branch: Option<String>) -> String {
12+
fn generate_dir_name(repo_url: &str, branch: Option<&str>) -> String {
1213
let parts: Vec<&str> = repo_url
1314
.trim_start_matches("https://github.com/")
1415
.split('/')
@@ -18,7 +19,7 @@ fn generate_dir_name(repo_url: &str, branch: Option<String>) -> String {
1819
"queensac_temp_repo/{}/{}/{}",
1920
user_name,
2021
repo_name,
21-
branch.unwrap_or("default".to_string())
22+
branch.unwrap_or("default")
2223
)
2324
}
2425

@@ -49,42 +50,33 @@ impl std::hash::Hash for LinkInfo {
4950

5051
/// Checkout a specific branch in the repository
5152
fn checkout_branch(repo: &Repository, branch_name: &str) -> Result<(), git2::Error> {
52-
let remote_branch_name = format!("origin/{}", branch_name);
53-
let mut remote = repo.find_remote("origin")?;
54-
55-
// 특정 브랜치만 fetch
56-
let refspec = format!(
57-
"refs/heads/{}:refs/remotes/origin/{}",
58-
branch_name, branch_name
59-
);
60-
remote.fetch(&[&refspec], None, None)?;
61-
62-
let remote_ref = format!("refs/remotes/{}", remote_branch_name);
53+
let remote_branch = format!("origin/{}", branch_name);
6354
let reference = repo
64-
.find_reference(&remote_ref)
55+
.find_reference(&format!("refs/remotes/{}", remote_branch))
6556
.map_err(|_| git2::Error::from_str(&format!("Branch not found: {}", branch_name)))?;
6657

67-
// Create a local branch tracking the remote branch
6858
let commit = reference.peel_to_commit()?;
69-
let branch = repo.branch(branch_name, &commit, false)?;
70-
repo.set_head(branch.get().name().unwrap())?;
71-
repo.checkout_head(None)?;
72-
59+
repo.checkout_tree(commit.as_object(), None)?;
60+
repo.set_head(&format!("refs/heads/{}", branch_name))?;
7361
Ok(())
7462
}
7563

7664
pub fn extract_links_from_repo_url(
7765
repo_url: &str,
7866
branch: Option<String>,
7967
) -> Result<HashSet<LinkInfo>, git2::Error> {
80-
let temp_dir = env::temp_dir().join(generate_dir_name(repo_url, branch.clone()));
68+
let temp_dir = env::temp_dir().join(generate_dir_name(repo_url, branch.as_deref()));
8169
let _temp_dir_guard = TempDirGuard::new(temp_dir.clone()).map_err(|e| {
8270
git2::Error::from_str(&format!("Failed to create temporary directory: {}", e))
8371
})?;
72+
73+
// Clone repository
8474
let repo = Repository::clone(repo_url, &temp_dir)?;
8575

86-
// 체크아웃 브랜치
76+
// 브랜치가 지정된 경우에만 fetch와 체크아웃 수행
8777
if let Some(branch_name) = branch {
78+
let mut remote = repo.find_remote("origin")?;
79+
remote.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None)?;
8880
checkout_branch(&repo, &branch_name)?;
8981
}
9082

@@ -118,9 +110,7 @@ pub fn extract_links_from_repo_url(
118110
}
119111

120112
fn find_link_in_content(content: &str, file_path: String) -> HashSet<LinkInfo> {
121-
// TODO 정규표현식 캐싱
122113
let url_regex = Regex::new(REGEX_URL).unwrap();
123-
124114
let mut result = HashSet::new();
125115

126116
for (line_num, line) in content.lines().enumerate() {
@@ -133,19 +123,19 @@ fn find_link_in_content(content: &str, file_path: String) -> HashSet<LinkInfo> {
133123
result.insert(LinkInfo {
134124
url,
135125
file_path: file_path.clone(),
136-
line_number: line_num + 1, // 1-based line number
126+
line_number: line_num + 1,
137127
});
138128
}
139129
}
140130
result
141131
}
142132

143133
struct TempDirGuard {
144-
path: std::path::PathBuf,
134+
path: PathBuf,
145135
}
146136

147137
impl TempDirGuard {
148-
fn new(path: std::path::PathBuf) -> std::io::Result<Self> {
138+
fn new(path: PathBuf) -> std::io::Result<Self> {
149139
if path.exists() {
150140
fs::remove_dir_all(&path)?;
151141
}
@@ -284,4 +274,58 @@ mod tests {
284274
);
285275
}
286276
}
277+
278+
#[test]
279+
#[serial]
280+
fn test_checkout_branch_with_valid_branch() {
281+
let repo_url = "https://github.com/reddevilmidzy/woowalog";
282+
let temp_dir = env::temp_dir().join("test_checkout_branch");
283+
let _temp_dir_guard = TempDirGuard::new(temp_dir.clone()).unwrap();
284+
285+
// Clone repository
286+
let repo = Repository::clone(repo_url, &temp_dir).unwrap();
287+
288+
// Fetch all branches
289+
let mut remote = repo.find_remote("origin").unwrap();
290+
remote
291+
.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None)
292+
.unwrap();
293+
294+
// Test checkout with a known existing branch
295+
let result = checkout_branch(&repo, "main");
296+
assert!(
297+
result.is_ok(),
298+
"Should successfully checkout existing branch"
299+
);
300+
}
301+
302+
#[test]
303+
#[serial]
304+
fn test_checkout_branch_with_invalid_branch() {
305+
let repo_url = "https://github.com/reddevilmidzy/woowalog";
306+
let temp_dir = env::temp_dir().join("test_checkout_invalid_branch");
307+
let _temp_dir_guard = TempDirGuard::new(temp_dir.clone()).unwrap();
308+
309+
// Clone repository
310+
let repo = Repository::clone(repo_url, &temp_dir).unwrap();
311+
312+
// Fetch all branches
313+
let mut remote = repo.find_remote("origin").unwrap();
314+
remote
315+
.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None)
316+
.unwrap();
317+
318+
// Test checkout with a non-existent branch
319+
let result = checkout_branch(&repo, "non-existent-branch");
320+
assert!(
321+
result.is_err(),
322+
"Should fail to checkout non-existent branch"
323+
);
324+
if let Err(e) = result {
325+
assert!(
326+
e.message().contains("non-existent-branch"),
327+
"Error message should contain the branch name"
328+
);
329+
}
330+
}
287331
}

rook/src/schedule/mod.rs

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use tracing::{error, info, instrument, warn};
1010
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
1111
struct RepoKey {
1212
repo_url: String,
13-
branch: String,
13+
branch: Option<String>,
1414
}
1515

1616
static REPO_TASKS: Lazy<Mutex<HashMap<RepoKey, CancellationToken>>> =
@@ -22,7 +22,6 @@ pub async fn check_repository_links(
2222
branch: Option<String>,
2323
interval_duration: Duration,
2424
) -> Result<(), String> {
25-
let branch = branch.unwrap_or_else(|| "default".to_string());
2625
let repo_key = RepoKey {
2726
repo_url: repo_url.to_string(),
2827
branch: branch.clone(),
@@ -33,7 +32,7 @@ pub async fn check_repository_links(
3332
let mut map = REPO_TASKS.lock().unwrap();
3433
if map.contains_key(&repo_key) {
3534
return Err(format!(
36-
"Repository {} (branch: {}) is already being monitored",
35+
"Repository {} (branch: {:?}) is already being monitored",
3736
repo_url, branch
3837
));
3938
}
@@ -43,17 +42,21 @@ pub async fn check_repository_links(
4342
};
4443

4544
info!(
46-
"Starting repository link checker for {} (branch: {})",
45+
"Starting repository link checker for {} (branch: {:?})",
4746
repo_url, branch
4847
);
4948

5049
let mut interval = tokio::time::interval(interval_duration);
5150
loop {
5251
tokio::select! {
5352
_ = interval.tick() => {
54-
info!("Checking links for repository: {} (branch: {})", repo_url, branch);
53+
info!(
54+
"Checking links for repository: {} (branch: {:?})",
55+
repo_url,
56+
branch
57+
);
5558

56-
match git::extract_links_from_repo_url(repo_url, Some(branch.clone())) {
59+
match git::extract_links_from_repo_url(repo_url, branch.clone()) {
5760
Ok(links) => {
5861
info!("Found {} links to check", links.len());
5962
let mut handles = Vec::new();
@@ -78,12 +81,17 @@ pub async fn check_repository_links(
7881
}
7982
Err(e) => error!("Error processing repository: {}", e),
8083
}
81-
info!("Link check completed for {} (branch: {}). Waiting for next interval...", repo_url, branch);
84+
info!(
85+
"Link check completed for {} (branch: {:?}). Waiting for next interval...",
86+
repo_url,
87+
branch
88+
);
8289
},
8390
_ = token.cancelled() => {
8491
info!(
85-
"Repository checker cancelled for: {} (branch: {})",
86-
repo_url, branch
92+
"Repository checker cancelled for: {} (branch: {:?})",
93+
repo_url,
94+
branch
8795
);
8896
break;
8997
}
@@ -98,7 +106,6 @@ pub async fn cancel_repository_checker(
98106
repo_url: &str,
99107
branch: Option<String>,
100108
) -> Result<(), String> {
101-
let branch = branch.unwrap_or_else(|| "default".to_string());
102109
let repo_key = RepoKey {
103110
repo_url: repo_url.to_string(),
104111
branch: branch.clone(),
@@ -111,13 +118,13 @@ pub async fn cancel_repository_checker(
111118
if let Some(token) = token {
112119
token.cancel();
113120
info!(
114-
"Cancellation requested for repository: {} (branch: {})",
121+
"Cancellation requested for repository: {} (branch: {:?})",
115122
repo_url, branch
116123
);
117124
Ok(())
118125
} else {
119126
Err(format!(
120-
"No active checker found for repository: {} (branch: {})",
127+
"No active checker found for repository: {} (branch: {:?})",
121128
repo_url, branch
122129
))
123130
}
@@ -130,33 +137,6 @@ mod tests {
130137
use std::sync::atomic::{AtomicUsize, Ordering};
131138
use tokio::time::timeout;
132139

133-
// #[tokio::test]
134-
// FIXME: 미션 mock 사용하여 테스트를 가능케하라.
135-
// async fn test_duplicate_repository() {
136-
// let repo_url = "https://github.com/reddevilmidzy/woowalog";
137-
// let interval = Duration::from_millis(100);
138-
139-
// // First call should succeed
140-
// let result1 = check_repository_links(repo_url, None, interval).await;
141-
// assert!(result1.is_ok(), "First call should succeed");
142-
143-
// // Second call should fail (same repo, default branch)
144-
// let result2 = check_repository_links(repo_url, None, interval).await;
145-
// assert!(result2.is_err(), "Second call should fail");
146-
// assert!(result2.unwrap_err().contains("already being monitored"));
147-
148-
// // Call with different branch should succeed
149-
// let result3 = check_repository_links(repo_url, Some("main".to_string()), interval).await;
150-
// assert!(result3.is_ok(), "Call with different branch should succeed");
151-
152-
// // Cancel the first checker
153-
// cancel_repository_checker(repo_url, None).await.unwrap();
154-
155-
// // Third call should succeed again
156-
// let result4 = check_repository_links(repo_url, None, interval).await;
157-
// assert!(result4.is_ok(), "Third call should succeed after cancellation");
158-
// }
159-
160140
#[tokio::test]
161141
async fn test_scheduled_execution() {
162142
let counter = Arc::new(AtomicUsize::new(0));

0 commit comments

Comments
 (0)