github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/tools/noms/staging.py (about) 1 #!/usr/bin/python 2 3 # Copyright 2016 Attic Labs, Inc. All rights reserved. 4 # Licensed under the Apache License, version 2.0: 5 # http://www.apache.org/licenses/LICENSE-2.0 6 7 import argparse 8 import glob 9 import hashlib 10 import os 11 import os.path 12 import re 13 import shutil 14 15 def Main(projectName, stagingFunction): 16 """Main should be called by all staging scripts when executed. 17 18 Main takes a project name and a callable. It creates a staging directory for 19 your project and then runs the callable, passing it the path to the 20 newly-created staging directory. 21 22 For the common case of simply copying a set of files into the staging 23 directory, use GlobCopier: 24 25 #!/usr/bin/python 26 27 import noms.staging as staging 28 29 if __name__ == '__main__': 30 staging.Main('nerdosphere', staging.GlobCopier('index.html', 'styles.css', '*.js')) 31 """ 32 parser = argparse.ArgumentParser(description='Stage build products from this directory.') 33 parser.add_argument('staging_dir', 34 metavar='path/to/staging/directory', 35 type=_dir_path, 36 help='top-level dir into which project build products are staged') 37 args = parser.parse_args() 38 project_staging_dir = os.path.join(args.staging_dir, projectName) 39 40 normalized = os.path.realpath(project_staging_dir) 41 if not _is_sub_dir(project_staging_dir, args.staging_dir): 42 raise Exception(project_staging_dir + ' must be a subdir of ' + args.staging_dir) 43 44 if not os.path.exists(normalized): 45 os.makedirs(normalized) 46 stagingFunction(normalized) 47 48 49 def run_globs(staging_dir, globs, exclude): 50 for pattern in globs: 51 for f in glob.glob(pattern): 52 if os.path.isdir(f): 53 continue 54 from_dir, name = os.path.split(f) 55 if name in exclude: 56 continue 57 to_dir = os.path.join(staging_dir, from_dir) 58 if not os.path.exists(to_dir): 59 os.makedirs(to_dir) 60 yield (f, to_dir) 61 62 63 def rename_with_hash(f, to_dir, rename_dict): 64 with open(f) as fh: 65 sha = hashlib.sha256() 66 sha.update(fh.read()) 67 digest = sha.hexdigest() 68 basename = os.path.basename(f) 69 name, ext = os.path.splitext(basename) 70 new_name = '%s.%s%s' % (name, digest[:20], ext) 71 rename_dict[basename] = new_name 72 shutil.move(os.path.join(to_dir, basename), os.path.join(to_dir, new_name)) 73 74 75 def GlobCopier(*globs, **kwargs): 76 ''' 77 Returns a function that copies files defined by globs into a staging dir. 78 79 Arguments: 80 - Zero or more globs used to determine which files to copy. 81 82 Keyword arguments: 83 - rename (bool) - If True then the files gets renamed to name.%%hash.ext 84 - index_file (str) - If present then this file is copied to the staging dir 85 and its content is updated where the paths to the files are updated to the 86 renamed file paths. 87 ''' 88 exclude = ('webpack.config.js',) 89 rename = 'rename' in kwargs and kwargs['rename'] 90 def stage(staging_dir): 91 if rename: 92 rename_dict = dict() 93 for f, to_dir in run_globs(staging_dir, globs, exclude): 94 shutil.copy2(f, to_dir) 95 if rename: 96 rename_with_hash(f, to_dir, rename_dict) 97 98 # Update index_file and write it to to_dir. 99 if 'index_file' not in kwargs: 100 return 101 index_file = kwargs['index_file'] 102 from_dir, name = os.path.split(index_file) 103 to_dir = os.path.join(staging_dir, from_dir) 104 with open(index_file, 'r') as f: 105 data = f.read() 106 if rename: 107 for old_name, new_name in rename_dict.iteritems(): 108 r = re.compile(r'\b%s\b' % re.escape(old_name)) 109 data = r.sub(new_name, data) 110 with open(os.path.join(to_dir, name), 'w') as f: 111 f.write(data) 112 113 return stage 114 115 116 def _dir_path(arg): 117 normalized = os.path.realpath(os.path.abspath(arg)) 118 if os.path.exists(normalized) and not os.path.isdir(normalized): 119 raise ValueError(arg + ' is not a path to a directory.') 120 return normalized 121 122 123 def _is_sub_dir(subdir, directory): 124 # Need the path-sep at the end to ensure that commonprefix returns the correct result below. 125 directory = os.path.join(os.path.realpath(directory), '') 126 subdir = os.path.realpath(subdir) 127 128 # return true, if the common prefix of both is equal to directory e.g. /a/b/c/d.rst and 129 # directory is /a/b, the common prefix is /a/b 130 return os.path.commonprefix([subdir, directory]) == directory