1#!/usr/bin/python 2# 3# This Source Code Form is subject to the terms of the Mozilla Public 4# License, v. 2.0. If a copy of the MPL was not distributed with this 5# file, You can obtain one at http://mozilla.org/MPL/2.0/. 6# 7# When run directly, this script expects the following environment variables 8# to be set: 9# UPLOAD_PATH : path on that host to put the files in 10# 11# Files are simply copied to UPLOAD_PATH. 12# 13# All files to be uploaded should be passed as commandline arguments to this 14# script. The script takes one other parameter, --base-path, which you can use 15# to indicate that files should be uploaded including their paths relative 16# to the base path. 17 18import sys 19import os 20import shutil 21from optparse import OptionParser 22 23 24def OptionalEnvironmentVariable(v): 25 """Return the value of the environment variable named v, or None 26 if it's unset (or empty).""" 27 if v in os.environ and os.environ[v] != "": 28 return os.environ[v] 29 return None 30 31 32def FixupMsysPath(path): 33 """MSYS helpfully translates absolute pathnames in environment variables 34 and commandline arguments into Windows native paths. This sucks if you're 35 trying to pass an absolute path on a remote server. This function attempts 36 to un-mangle such paths.""" 37 if "OSTYPE" in os.environ and os.environ["OSTYPE"] == "msys": 38 # sort of awful, find out where our shell is (should be in msys/bin) 39 # and strip the first part of that path out of the other path 40 if "SHELL" in os.environ: 41 sh = os.environ["SHELL"] 42 msys = sh[: sh.find("/bin")] 43 if path.startswith(msys): 44 path = path[len(msys) :] 45 return path 46 47 48def GetBaseRelativePath(path, local_file, base_path): 49 """Given a remote path to upload to, a full path to a local file, and an 50 optional full path that is a base path of the local file, construct the 51 full remote path to place the file in. If base_path is not None, include 52 the relative path from base_path to file.""" 53 if base_path is None or not local_file.startswith(base_path): 54 return path 55 56 dir = os.path.dirname(local_file) 57 # strip base_path + extra slash and make it unixy 58 dir = dir[len(base_path) + 1 :].replace("\\", "/") 59 return path + dir 60 61 62def CopyFilesLocally(path, files, verbose=False, base_path=None): 63 """Copy each file in the list of files to `path`. The `base_path` argument is treated 64 as it is by UploadFiles.""" 65 if not path.endswith("/"): 66 path += "/" 67 if base_path is not None: 68 base_path = os.path.abspath(base_path) 69 for file in files: 70 file = os.path.abspath(file) 71 if not os.path.isfile(file): 72 raise IOError("File not found: %s" % file) 73 # first ensure that path exists remotely 74 target_path = GetBaseRelativePath(path, file, base_path) 75 if not os.path.exists(target_path): 76 os.makedirs(target_path) 77 if verbose: 78 print("Copying " + file + " to " + target_path) 79 shutil.copy(file, target_path) 80 81 82if __name__ == "__main__": 83 path = OptionalEnvironmentVariable("UPLOAD_PATH") 84 85 if sys.platform == "win32": 86 if path is not None: 87 path = FixupMsysPath(path) 88 89 parser = OptionParser(usage="usage: %prog [options] <files>") 90 parser.add_option( 91 "-b", 92 "--base-path", 93 action="store", 94 help="Preserve file paths relative to this path when uploading. " 95 "If unset, all files will be uploaded directly to UPLOAD_PATH.", 96 ) 97 (options, args) = parser.parse_args() 98 if len(args) < 1: 99 print("You must specify at least one file to upload") 100 sys.exit(1) 101 102 try: 103 CopyFilesLocally(path, args, base_path=options.base_path, verbose=True) 104 except IOError as strerror: 105 print(strerror) 106 sys.exit(1) 107