11use std:: {
2- fs:: { self , File } ,
3- io:: Read ,
4- os:: unix:: fs:: PermissionsExt ,
2+ fs:: { self , create_dir_all, rename} ,
3+ os:: unix:: fs:: { PermissionsExt , symlink} ,
54 path:: Path ,
65} ;
76
7+ use rand:: RngCore ;
8+
89use crate :: { artifacts:: get_artifact, compression:: decompress_file} ;
910
1011mod artifacts;
@@ -64,6 +65,7 @@ pub fn build(input_dir: &Path, repo_dir: &Path, artifact_name: &String) -> Resul
6465 {
6566 use std:: os:: unix:: fs:: PermissionsExt ;
6667
68+ let root_path = input_dir. to_string_lossy ( ) . to_string ( ) ;
6769 let path = entry. path ( ) . to_string_lossy ( ) . to_string ( ) ;
6870 let raw = fs:: read ( & path) . expect ( "Couldn't read file for chunking!" ) ;
6971 let compressed = compression:: compress_file ( & raw , 3 ) ;
@@ -75,7 +77,7 @@ pub fn build(input_dir: &Path, repo_dir: &Path, artifact_name: &String) -> Resul
7577 // Save the chunk
7678 fs:: write ( chunk_dir. join ( & hash) , compressed) . expect ( "Couldn't write chunk file!" ) ;
7779
78- files. push ( ( path, hash, is_executable) ) ;
80+ files. push ( ( path. replacen ( & root_path , "" , 1 ) , hash, is_executable) ) ;
7981 }
8082
8183 let manifest = Manifest {
@@ -102,28 +104,64 @@ pub fn build(input_dir: &Path, repo_dir: &Path, artifact_name: &String) -> Resul
102104}
103105
104106pub fn install_artifact ( artifact_name : & String , store_path : & Path , repo_cache_path : & Path ) {
105- let chunk_dir = repo_cache_path. join ( "chunks" ) ;
106- let manifest_dir = repo_cache_path. join ( "manifests" ) ;
107- let artifacts_path = repo_cache_path. join ( "artifacts" ) ;
107+ assert ! ( store_path. is_absolute( ) , "Store path must be absolute!" ) ;
108+ assert ! (
109+ repo_cache_path. is_absolute( ) ,
110+ "Repo cache path must be absolute!"
111+ ) ;
112+
113+ let repo_manifest_dir = repo_cache_path. join ( "manifests" ) ;
114+ let repo_artifacts_path = repo_cache_path. join ( "artifacts" ) ;
115+
116+ let store_chunk_dir = store_path. join ( "chunks" ) ;
117+ let store_manifest_dir = store_path. join ( "manifests" ) ;
118+ let store_artifacts_path = store_path. join ( "artifacts" ) ;
108119
109- let manifest_hash = get_artifact ( artifact_name, & artifacts_path) . unwrap ( ) ;
120+ let manifest_hash =
121+ get_artifact ( artifact_name, & repo_artifacts_path) . expect ( "Artifact not found" ) ;
110122
111123 let manifest: Manifest = serde_json:: from_str (
112- fs:: read_to_string ( manifest_dir . join ( manifest_hash) )
124+ fs:: read_to_string ( repo_manifest_dir . join ( & manifest_hash) )
113125 . unwrap ( )
114126 . as_str ( ) ,
115127 )
116128 . unwrap ( ) ;
117129
118- for ( path , hash, executable) in manifest. files {
130+ for ( _path , hash, executable) in & manifest. files {
119131 // Install chunks
120132 install_chunk ( & hash, store_path, repo_cache_path) . unwrap ( ) ;
121133
122134 // Make sure it's executable if it needs to be
123- if executable {
135+ if * executable {
124136 make_chunk_executable ( & hash, store_path) ;
125137 }
126138 }
139+
140+ // Seperate to ensure chunks have been installed prior to linked
141+ for ( manifest_defined_path, hash, _executable) in & manifest. files {
142+ let manifest_defined_path = manifest_defined_path. trim_start_matches ( '/' ) . to_string ( ) ;
143+
144+ let path = store_manifest_dir
145+ . join ( & manifest_hash)
146+ . join ( manifest_defined_path) ;
147+
148+ if !path. parent ( ) . unwrap ( ) . exists ( ) {
149+ create_dir_all ( & path. parent ( ) . unwrap ( ) ) . unwrap ( ) ;
150+ }
151+
152+ if !fs:: exists ( & path) . unwrap ( ) {
153+ symlink ( store_chunk_dir. as_path ( ) . join ( hash) , path) . unwrap ( ) ;
154+ }
155+ }
156+
157+ // Create a temporary symlink for atomic update
158+ let tmp_file_name = format ! ( ".tmp_{}" , rand:: rng( ) . next_u64( ) ) ;
159+
160+ let tmp_symlink = store_artifacts_path. join ( & tmp_file_name) ;
161+ let final_symlink = store_artifacts_path. join ( artifact_name) ;
162+
163+ symlink ( store_manifest_dir. join ( & manifest_hash) , & tmp_symlink) . unwrap ( ) ;
164+ rename ( & tmp_symlink, & final_symlink) . unwrap ( ) ;
127165}
128166
129167fn make_chunk_executable ( chunk_hash : & String , store_path : & Path ) {
@@ -160,7 +198,9 @@ fn install_chunk(
160198 }
161199
162200 fs:: write ( store_chunk_path, decompressed_chunk) . unwrap ( ) ;
163- }
164201
165- Err ( "Couldn't find chunk" . to_string ( ) )
202+ Ok ( ( ) )
203+ } else {
204+ Err ( "Couldn't find chunk" . to_string ( ) )
205+ }
166206}
0 commit comments