Advertisement
Guest User

Untitled

a guest
Jun 18th, 2018
206
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.98 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. """
  4. git-ftp: painless, quick and easy working copy syncing over FTP
  5.  
  6. Copyright (c) 2008-2009
  7. Edward Z. Yang <ezyang@mit.edu> and Mauro Lizaur <mauro@cacavoladora.org>
  8.  
  9. Permission is hereby granted, free of charge, to any person
  10. obtaining a copy of this software and associated documentation
  11. files (the "Software"), to deal in the Software without
  12. restriction, including without limitation the rights to use,
  13. copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. copies of the Software, and to permit persons to whom the
  15. Software is furnished to do so, subject to the following
  16. conditions:
  17.  
  18. The above copyright notice and this permission notice shall be
  19. included in all copies or substantial portions of the Software.
  20.  
  21. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  23. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  25. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  26. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  27. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  28. OTHER DEALINGS IN THE SOFTWARE.
  29. """
  30.  
  31. import ftplib
  32. import cStringIO
  33. import sys
  34. import os.path
  35. import getpass
  36. import ConfigParser
  37. import optparse
  38. import logging
  39.  
  40. from git import Tree, Blob, Repo, Git
  41.  
  42. def main():
  43. Git.git_binary = 'git' # Windows doesn't like env
  44.  
  45. repo, options, args = parse_args()
  46.  
  47. if repo.is_dirty:
  48. logging.warning("Working copy is dirty; uncommitted changes will NOT be uploaded")
  49.  
  50. base = options.ftp.remotepath
  51. commit = repo.commits()[0]
  52. tree = commit.tree
  53. ftp = ftplib.FTP(options.ftp.hostname, options.ftp.username, options.ftp.password)
  54. ftp.set_pasv(1); #Set Passive
  55.  
  56.  
  57. # Check revision
  58. hash = None
  59. if not options.force:
  60. hashFile = cStringIO.StringIO()
  61. try:
  62. ftp.retrbinary('RETR ' + base + '/git-rev.txt', hashFile.write)
  63. hash = hashFile.getvalue()
  64. except ftplib.error_perm:
  65. pass
  66.  
  67. if not hash:
  68. # Perform full upload
  69. upload_all(tree, ftp, base)
  70. else:
  71. upload_diff(repo.diff(hash, commit.id).split("\n"), tree, ftp, base)
  72.  
  73. ftp.storbinary('STOR ' + base + '/git-rev.txt', cStringIO.StringIO(commit.id))
  74. ftp.quit()
  75.  
  76. def parse_args():
  77. usage = """usage: %prog [DIRECTORY]
  78.  
  79. This script uploads files in a Git repository to a
  80. website via FTP, but is smart and only uploads file
  81. that have changed."""
  82. parser = optparse.OptionParser(usage)
  83. parser.add_option('-f', '--force', dest="force", action="store_true", default=False,
  84. help="force the reupload of all files")
  85. parser.add_option('-v', '--verbose', dest="verbose", action="store_true", default=False,
  86. help="be verbose")
  87. options, args = parser.parse_args()
  88. configure_logging(options)
  89. if len(args) > 1:
  90. parser.error("too many arguments")
  91. if args: cwd = args[0]
  92. else: cwd = "."
  93. repo = Repo(cwd)
  94. get_ftp_creds(repo, options)
  95. return repo, options, args
  96.  
  97. def configure_logging(options):
  98. logger = logging.getLogger()
  99. if options.verbose: logger.setLevel(logging.INFO)
  100. ch = logging.StreamHandler(sys.stderr)
  101. formatter = logging.Formatter("%(levelname)s: %(message)s")
  102. ch.setFormatter(formatter)
  103. logger.addHandler(ch)
  104.  
  105. class FtpData:
  106. password = None
  107. username = None
  108. hostname = None
  109. remotepath = None
  110.  
  111. def get_ftp_creds(repo, options):
  112. """
  113. Retrieves the data to connect to the FTP from .git/ftpdata
  114. or interactively.
  115.  
  116. ftpdata format example::
  117.  
  118. [ftp]
  119. username=me
  120. password=s00perP4zzw0rd
  121. hostname=ftp.hostname.com
  122. remotepath=/htdocs
  123. repository=/home/me/website
  124.  
  125. Please note that it isn't necessary to have this file,
  126. you'll be asked for the data every time you upload something.
  127. """
  128.  
  129. ftpdata = os.path.join(repo.path, "ftpdata")
  130. options.ftp = FtpData()
  131. if os.path.isfile(ftpdata):
  132. logging.info("Using .git/ftpdata")
  133. cfg = ConfigParser.ConfigParser()
  134. cfg.read(ftpdata)
  135.  
  136. # just in case you do not want to store your ftp password.
  137. try:
  138. options.ftp.password = cfg.get('ftp','password')
  139. except:
  140. options.ftp.password = getpass.getpass('FTP Password: ')
  141. options.ftp.username = cfg.get('ftp','username')
  142. options.ftp.hostname = cfg.get('ftp','hostname')
  143. options.ftp.remotepath = cfg.get('ftp','remotepath')
  144. else:
  145. options.ftp.username = raw_input('FTP Username: ')
  146. options.ftp.password = getpass.getpass('FTP Password: ')
  147. options.ftp.hostname = raw_input('FTP Hostname: ')
  148. options.ftp.remotepath = raw_input('Remote Path: ')
  149.  
  150. def upload_all(tree, ftp, base):
  151. """Upload all items in a Git tree.
  152.  
  153. Keyword arguments:
  154. tree -- the git.Tree to upload contents of
  155. ftp -- the active ftplib.FTP object to upload contents to
  156. base -- the string base directory to upload contents to in ftp. For example,
  157. base = '/www/www'. base must exist and must not have a trailing
  158. slash.
  159.  
  160. """
  161. for item in tree.items():
  162. node = tree[item[0]]
  163. ftp.cwd(base)
  164. if isinstance(node, Tree):
  165. try:
  166. ftp.mkd(node.name)
  167. except ftplib.error_perm:
  168. pass
  169. upload_all(node, ftp, '/'.join((base, node.name)))
  170. else:
  171. file = cStringIO.StringIO(node.data)
  172. try:
  173. ftp.delete(node.name)
  174. except ftplib.error_perm:
  175. pass
  176. ftp.storbinary('STOR ' + node.name, file)
  177. ftp.voidcmd('SITE CHMOD 755 ' + node.name)
  178. logging.info('Uploaded ' + '/'.join((base, node.name)))
  179.  
  180. def upload_diff(diff, tree, ftp, base):
  181. """Upload and/or delete items according to a Git diff.
  182.  
  183. Keyword arguments:
  184. diff -- a unified diff split into an array by newlines. Usually generated
  185. with: repo.diff(orig_id, new_id).split("\n")
  186. tree -- root git.Tree that diff file paths can be resolved to.
  187. ftp -- the active ftplib.FTP object to upload contents to
  188. base -- the string base directory to upload contents to in ftp. For example,
  189. base = '/www/www'. base must exist and must not have a trailing
  190. slash.
  191.  
  192. """
  193. for line in diff:
  194. if line.startswith('--- ') or line.startswith('+++ '):
  195. delete = line.startswith('---')
  196. file = line.split(' ', 1)[1]
  197. if file == '/dev/null':
  198. continue
  199. # Remove bogus a or b directory git prepends to names
  200. file = line.split('/', 1)[1]
  201. target = '/'.join((base, file))
  202. if delete:
  203. try:
  204. ftp.delete(target)
  205. logging.info('Deleted ' + target)
  206. except ftplib.error_perm:
  207. pass
  208. else:
  209. components = file.split("/")
  210. subtree = tree
  211. for c in components[:-1]:
  212. subtree = subtree/c
  213. node = subtree/components[-1]
  214. if isinstance(node, Tree):
  215. try:
  216. ftp.mkd(target)
  217. logging.info('Created directory ' + target)
  218. # This holds the risk of missing files to upload if
  219. # the directory is created, but the files are not
  220. # complete.
  221. upload_all(node, ftp, target)
  222. except ftplib.error_perm:
  223. pass
  224. elif isinstance(node, Blob):
  225. file = cStringIO.StringIO(node.data)
  226. ftp.storbinary('STOR ' + target, file)
  227. ftp.voidcmd('SITE CHMOD 755 ' + target)
  228. logging.info('Uploaded ' + target)
  229. # Don't do anything if there isn't any item; maybe it
  230. # was deleted.
  231.  
  232. if __name__ == "__main__":
  233. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement