@@ -49,12 +49,7 @@ function create_artifact(f::Function)
4949 # as something that was foolishly overridden. This should be virtually impossible
5050 # unless the user has been very unwise, but let's be cautious.
5151 new_path = artifact_path (artifact_hash; honor_overrides= false )
52- if ! isdir (new_path)
53- # Move this generated directory to its final destination, set it to read-only
54- mv (temp_dir, new_path)
55- chmod (new_path, filemode (dirname (new_path)))
56- set_readonly (new_path)
57- end
52+ _mv_temp_artifact_dir (temp_dir, new_path)
5853
5954 # Give the people what they want
6055 return artifact_hash
@@ -64,6 +59,28 @@ function create_artifact(f::Function)
6459 end
6560end
6661
62+ """
63+ _mv_temp_artifact_dir(temp_dir::String, new_path::String)::Nothing
64+ Either rename the directory at `temp_dir` to `new_path` and set it to read-only
65+ or if `new_path` artifact already exists try to do nothing.
66+ """
67+ function _mv_temp_artifact_dir (temp_dir:: String , new_path:: String ):: Nothing
68+ if ! isdir (new_path)
69+ # This next step is like
70+ # `mv(temp_dir, new_path)`.
71+ # However, `mv` defaults to `cp` if `rename` returns an error.
72+ # `cp` is not atomic, so avoid the potential of calling it.
73+ err = ccall (:jl_fs_rename , Int32, (Cstring, Cstring), temp_dir, new_path)
74+ # Ignore rename error, but ensure `new_path` exists.
75+ if ! isdir (new_path)
76+ error (" $(repr (new_path)) could not be made" )
77+ end
78+ chmod (new_path, filemode (dirname (new_path)))
79+ set_readonly (new_path)
80+ end
81+ nothing
82+ end
83+
6784"""
6885 remove_artifact(hash::SHA1; honor_overrides::Bool=false)
6986
@@ -289,68 +306,75 @@ function download_artifact(
289306 return true
290307 end
291308
292- # We download by using `create_artifact()`. We do this because the download may
309+ # Ensure the `artifacts` directory exists in our default depot
310+ artifacts_dir = first (artifacts_dirs ())
311+ mkpath (artifacts_dir)
312+ # expected artifact path
313+ dst = joinpath (artifacts_dir, bytes2hex (tree_hash. bytes))
314+
315+ # We download by using a temporary directory. We do this because the download may
293316 # be corrupted or even malicious; we don't want to clobber someone else's artifact
294317 # by trusting the tree hash that has been given to us; we will instead download it
295318 # to a temporary directory, calculate the true tree hash, then move it to the proper
296319 # location only after knowing what it is, and if something goes wrong in the process,
297- # everything should be cleaned up. Luckily, that is precisely what our
298- # `create_artifact()` wrapper does, so we use that here.
299- calc_hash = try
300- create_artifact () do dir
301- download_verify_unpack (tarball_url, tarball_hash, dir, ignore_existence= true , verbose= verbose,
320+ # everything should be cleaned up.
321+
322+ # Temporary directory where we'll do our creation business
323+ temp_dir = mktempdir (artifacts_dir)
324+
325+ try
326+ download_verify_unpack (tarball_url, tarball_hash, temp_dir, ignore_existence= true , verbose= verbose,
302327 quiet_download= quiet_download, io= io)
328+ calc_hash = SHA1 (GitTools. tree_hash (temp_dir))
329+
330+ # Did we get what we expected? If not, freak out.
331+ if calc_hash. bytes != tree_hash. bytes
332+ msg = """
333+ Tree Hash Mismatch!
334+ Expected git-tree-sha1: $(bytes2hex (tree_hash. bytes))
335+ Calculated git-tree-sha1: $(bytes2hex (calc_hash. bytes))
336+ """
337+ # Since tree hash calculation is rather fragile and file system dependent,
338+ # we allow setting JULIA_PKG_IGNORE_HASHES=1 to ignore the error and move
339+ # the artifact to the expected location and return true
340+ ignore_hash_env_set = get (ENV , " JULIA_PKG_IGNORE_HASHES" , " " ) != " "
341+ if ignore_hash_env_set
342+ ignore_hash = Base. get_bool_env (" JULIA_PKG_IGNORE_HASHES" , false )
343+ ignore_hash === nothing && @error (
344+ " Invalid ENV[\" JULIA_PKG_IGNORE_HASHES\" ] value" ,
345+ ENV [" JULIA_PKG_IGNORE_HASHES" ],
346+ )
347+ ignore_hash = something (ignore_hash, false )
348+ else
349+ # default: false except Windows users who can't symlink
350+ ignore_hash = Sys. iswindows () &&
351+ ! mktempdir (can_symlink, artifacts_dir)
352+ end
353+ if ignore_hash
354+ desc = ignore_hash_env_set ?
355+ " Environment variable \$ JULIA_PKG_IGNORE_HASHES is true" :
356+ " System is Windows and user cannot create symlinks"
357+ msg *= " \n $desc : \
358+ ignoring hash mismatch and moving \
359+ artifact to the expected location"
360+ @error (msg)
361+ else
362+ error (msg)
363+ end
303364 end
365+ # Move it to the location we expected
366+ _mv_temp_artifact_dir (temp_dir, dst)
304367 catch err
305368 @debug " download_artifact error" tree_hash tarball_url tarball_hash err
306369 if isa (err, InterruptException)
307370 rethrow (err)
308371 end
309372 # If something went wrong during download, return the error
310373 return err
374+ finally
375+ # Always attempt to cleanup
376+ rm (temp_dir; recursive= true , force= true )
311377 end
312-
313- # Did we get what we expected? If not, freak out.
314- if calc_hash. bytes != tree_hash. bytes
315- msg = """
316- Tree Hash Mismatch!
317- Expected git-tree-sha1: $(bytes2hex (tree_hash. bytes))
318- Calculated git-tree-sha1: $(bytes2hex (calc_hash. bytes))
319- """
320- # actual and expected artifiact paths
321- src = artifact_path (calc_hash; honor_overrides= false )
322- dst = artifact_path (tree_hash; honor_overrides= false )
323- # Since tree hash calculation is rather fragile and file system dependent,
324- # we allow setting JULIA_PKG_IGNORE_HASHES=1 to ignore the error and move
325- # the artifact to the expected location and return true
326- ignore_hash_env_set = get (ENV , " JULIA_PKG_IGNORE_HASHES" , " " ) != " "
327- if ignore_hash_env_set
328- ignore_hash = Base. get_bool_env (" JULIA_PKG_IGNORE_HASHES" , false )
329- ignore_hash === nothing && @error (
330- " Invalid ENV[\" JULIA_PKG_IGNORE_HASHES\" ] value" ,
331- ENV [" JULIA_PKG_IGNORE_HASHES" ],
332- )
333- ignore_hash = something (ignore_hash, false )
334- else
335- # default: false except Windows users who can't symlink
336- ignore_hash = Sys. iswindows () &&
337- ! mktempdir (can_symlink, dirname (src))
338- end
339- if ignore_hash
340- desc = ignore_hash_env_set ?
341- " Environment variable \$ JULIA_PKG_IGNORE_HASHES is true" :
342- " System is Windows and user cannot create symlinks"
343- msg *= " \n $desc : \
344- ignoring hash mismatch and moving \
345- artifact to the expected location"
346- @error (msg)
347- # Move it to the location we expected
348- mv (src, dst; force= true )
349- return true
350- end
351- return ErrorException (msg)
352- end
353-
354378 return true
355379end
356380
0 commit comments