We allow strings with context that represent paths (since they qualify
as `pathLike`). While store path (names) may not contain any characters
that are meaningful in shell, they may contain directories and/or files
with such names since it's permissible in POSIX.
To fix this, we convert the given value `v` to a shell argument in two
stages:
1. Use `${v}` to coerce the value to a string while importing any
   necessary paths to store.
2. Escape the resulting string for use as an argument.
Change-Id: Ib989b50df2a921c2abcd1ebc7ca0ff6e2bb79088
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12898
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
Autosubmit: sterni <sternenseemann@systemli.org>
		
	
			
		
			
				
	
	
		
			43 lines
		
	
	
	
		
			1.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			43 lines
		
	
	
	
		
			1.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ depot, lib, pkgs, ... }:
 | 
						|
let
 | 
						|
  inherit (lib) fix pipe mapAttrsToList isAttrs concatLines isString isDerivation isPath;
 | 
						|
 | 
						|
  # TODO(sterni): move to //nix/utils with clearer naming and alternative similar to lib.types.path
 | 
						|
  isPathLike = value:
 | 
						|
    isPath value
 | 
						|
    || isDerivation value
 | 
						|
    || (isString value && builtins.hasContext value);
 | 
						|
 | 
						|
  esc = s: lib.escapeShellArg /* ensure paths import into store */ "${s}";
 | 
						|
 | 
						|
  writeTreeAtPath = path: tree:
 | 
						|
    ''
 | 
						|
      mkdir -p "$out/"${esc path}
 | 
						|
    ''
 | 
						|
    + pipe tree [
 | 
						|
      (mapAttrsToList (k: v:
 | 
						|
        if isPathLike v then
 | 
						|
          "cp -R --reflink=auto ${esc "${v}"} \"$out/\"${esc path}/${esc k}"
 | 
						|
        else if lib.isAttrs v then
 | 
						|
          writeTreeAtPath (path + "/" + k) v
 | 
						|
        else
 | 
						|
          throw "invalid type (expected path, derivation, string with context, or attrs)"))
 | 
						|
      concatLines
 | 
						|
    ];
 | 
						|
 | 
						|
  /* Create a directory tree specified by a Nix attribute set structure.
 | 
						|
 | 
						|
     Each value in `tree` should either be a file, a directory, or another tree
 | 
						|
     attribute set. Those paths will be written to a directory tree
 | 
						|
     corresponding to the structure of the attribute set.
 | 
						|
 | 
						|
     Type: string -> attrSet -> derivation
 | 
						|
  */
 | 
						|
  writeTree = name: tree:
 | 
						|
    pkgs.runCommandLocal name { } (writeTreeAtPath "" tree);
 | 
						|
in
 | 
						|
 | 
						|
# __functor trick so readTree can add the tests attribute
 | 
						|
{
 | 
						|
  __functor = _: writeTree;
 | 
						|
}
 |