@@ -26,21 +26,29 @@ impl Drop for TempDirGuard<'_> {
2626 }
2727}
2828
29- /// Ensures repo is cloned/synced to cache. Returns (cache path, commit_range if updated).
30- pub fn ensure_repo ( source : & str ) -> Result < ( PathBuf , Option < String > ) > {
31- let cache_dir = get_cache_dir ( source) ?;
29+ /// Generates a cache directory path with random suffix.
30+ pub fn generate_cache_path ( source : & str ) -> PathBuf {
31+ let cache_base = dirs:: cache_dir ( )
32+ . unwrap_or_else ( || PathBuf :: from ( "/tmp" ) )
33+ . join ( "rollcron" ) ;
34+
35+ let repo_name = source
36+ . trim_end_matches ( '/' )
37+ . trim_end_matches ( ".git" )
38+ . rsplit ( '/' )
39+ . next ( )
40+ . unwrap_or ( "repo" ) ;
41+
42+ let random_suffix = generate_random_suffix ( ) ;
43+ cache_base. join ( format ! ( "{}-{}" , repo_name, random_suffix) )
44+ }
45+
46+ /// Clones repo to specified cache path.
47+ pub fn clone_to ( source : & str , cache_dir : & Path ) -> Result < ( ) > {
3248 if let Some ( parent) = cache_dir. parent ( ) {
3349 std:: fs:: create_dir_all ( parent) ?;
3450 }
35-
36- let update_info = if cache_dir. exists ( ) {
37- sync_repo ( & cache_dir) ?
38- } else {
39- clone_repo ( source, & cache_dir) ?;
40- Some ( "initial" . to_string ( ) )
41- } ;
42-
43- Ok ( ( cache_dir, update_info) )
51+ clone_repo ( source, cache_dir)
4452}
4553
4654fn clone_repo ( source : & str , dest : & Path ) -> Result < ( ) > {
@@ -59,8 +67,8 @@ fn clone_repo(source: &str, dest: &Path) -> Result<()> {
5967 Ok ( ( ) )
6068}
6169
62- /// Returns commit range (e.g. "abc123..def456") if new commits were fetched
63- fn sync_repo ( dest : & Path ) -> Result < Option < String > > {
70+ /// Syncs an existing repo. Returns commit range (e.g. "abc123..def456") if new commits were fetched.
71+ pub fn sync_repo ( dest : & Path ) -> Result < Option < String > > {
6472 // git clone sets up tracking branches for both local and remote repos
6573 let has_upstream = Command :: new ( "git" )
6674 . args ( [ "rev-parse" , "--abbrev-ref" , "@{upstream}" ] )
@@ -128,29 +136,15 @@ fn sync_repo(dest: &Path) -> Result<Option<String>> {
128136 Ok ( None )
129137}
130138
131- fn get_cache_dir ( source : & str ) -> Result < PathBuf > {
132- let cache_base = dirs:: cache_dir ( )
133- . unwrap_or_else ( || PathBuf :: from ( "/tmp" ) )
134- . join ( "rollcron" ) ;
135-
136- let repo_name = source
137- . trim_end_matches ( '/' )
138- . trim_end_matches ( ".git" )
139- . rsplit ( '/' )
140- . next ( )
141- . unwrap_or ( "repo" ) ;
142-
143- let hash = & format ! ( "{:x}" , hash_str( source) ) [ ..8 ] ;
144-
145- Ok ( cache_base. join ( format ! ( "{}-{}" , repo_name, hash) ) )
146- }
147-
148- fn hash_str ( input : & str ) -> u64 {
149- use std:: collections:: hash_map:: DefaultHasher ;
150- use std:: hash:: { Hash , Hasher } ;
151- let mut hasher = DefaultHasher :: new ( ) ;
152- input. hash ( & mut hasher) ;
153- hasher. finish ( )
139+ fn generate_random_suffix ( ) -> String {
140+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
141+ let nanos = SystemTime :: now ( )
142+ . duration_since ( UNIX_EPOCH )
143+ . map ( |d| d. as_nanos ( ) )
144+ . unwrap_or ( 0 ) ;
145+ // Use lower 32 bits of nanoseconds XOR'd with process ID for uniqueness
146+ let unique = ( nanos as u32 ) ^ std:: process:: id ( ) ;
147+ format ! ( "{:08x}" , unique)
154148}
155149
156150pub fn get_job_dir ( sot_path : & Path , job_id : & str ) -> PathBuf {
@@ -252,13 +246,44 @@ pub fn sync_to_job_dir(sot_path: &Path, job_dir: &Path) -> Result<()> {
252246 Ok ( ( ) )
253247}
254248
249+ /// Removes the sot_path and all associated job directories.
250+ pub fn cleanup_cache_dir ( sot_path : & Path , job_ids : & [ String ] ) {
251+ use tracing:: info;
252+
253+ // Remove job directories
254+ for job_id in job_ids {
255+ let job_dir = get_job_dir ( sot_path, job_id) ;
256+ if job_dir. exists ( ) {
257+ info ! ( path = %job_dir. display( ) , "Removing job directory" ) ;
258+ let _ = std:: fs:: remove_dir_all ( & job_dir) ;
259+ }
260+ // Also remove temp/old variants
261+ let _ = std:: fs:: remove_dir_all ( job_dir. with_extension ( "tmp" ) ) ;
262+ let _ = std:: fs:: remove_dir_all ( job_dir. with_extension ( "old" ) ) ;
263+ }
264+
265+ // Remove sot_path
266+ if sot_path. exists ( ) {
267+ info ! ( path = %sot_path. display( ) , "Removing cache directory" ) ;
268+ let _ = std:: fs:: remove_dir_all ( sot_path) ;
269+ }
270+ }
271+
255272#[ cfg( test) ]
256273mod tests {
257274 use super :: * ;
258275
259276 #[ test]
260- fn cache_dir_from_url ( ) {
261- let dir = get_cache_dir ( "https://github.com/user/myrepo.git" ) . unwrap ( ) ;
277+ fn cache_path_from_url ( ) {
278+ let dir = generate_cache_path ( "https://github.com/user/myrepo.git" ) ;
262279 assert ! ( dir. to_str( ) . unwrap( ) . contains( "myrepo" ) ) ;
263280 }
281+
282+ #[ test]
283+ fn cache_path_is_random ( ) {
284+ let dir1 = generate_cache_path ( "https://github.com/user/repo.git" ) ;
285+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 1 ) ) ;
286+ let dir2 = generate_cache_path ( "https://github.com/user/repo.git" ) ;
287+ assert_ne ! ( dir1, dir2) ;
288+ }
264289}
0 commit comments