You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

googlecode_upload.py 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2006, 2007 Google Inc. All Rights Reserved.
  4. # Author: danderson@google.com (David Anderson)
  5. #
  6. # Script for uploading files to a Google Code project.
  7. #
  8. # This is intended to be both a useful script for people who want to
  9. # streamline project uploads and a reference implementation for
  10. # uploading files to Google Code projects.
  11. #
  12. # To upload a file to Google Code, you need to provide a path to the
  13. # file on your local machine, a small summary of what the file is, a
  14. # project name, and a valid account that is a member or owner of that
  15. # project. You can optionally provide a list of labels that apply to
  16. # the file. The file will be uploaded under the same name that it has
  17. # in your local filesystem (that is, the "basename" or last path
  18. # component). Run the script with '--help' to get the exact syntax
  19. # and available options.
  20. #
  21. # Note that the upload script requests that you enter your
  22. # googlecode.com password. This is NOT your Gmail account password!
  23. # This is the password you use on googlecode.com for committing to
  24. # Subversion and uploading files. You can find your password by going
  25. # to http://code.google.com/hosting/settings when logged in with your
  26. # Gmail account. If you have already committed to your project's
  27. # Subversion repository, the script will automatically retrieve your
  28. # credentials from there (unless disabled, see the output of '--help'
  29. # for details).
  30. #
  31. # If you are looking at this script as a reference for implementing
  32. # your own Google Code file uploader, then you should take a look at
  33. # the upload() function, which is the meat of the uploader. You
  34. # basically need to build a multipart/form-data POST request with the
  35. # right fields and send it to https://PROJECT.googlecode.com/files .
  36. # Authenticate the request using HTTP Basic authentication, as is
  37. # shown below.
  38. #
  39. # Licensed under the terms of the Apache Software License 2.0:
  40. # http://www.apache.org/licenses/LICENSE-2.0
  41. #
  42. # Questions, comments, feature requests and patches are most welcome.
  43. # Please direct all of these to the Google Code users group:
  44. # http://groups.google.com/group/google-code-hosting
  45. """Google Code file uploader script.
  46. """
  47. __author__ = 'danderson@google.com (David Anderson) [Modified for DMDirc by Shane McCormack: shane@dmdirc.com]'
  48. import httplib
  49. import os.path
  50. import optparse
  51. import getpass
  52. import base64
  53. import sys
  54. def upload(file, project_name, user_name, password, summary, labels=None):
  55. """Upload a file to a Google Code project's file server.
  56. Args:
  57. file: The local path to the file.
  58. project_name: The name of your project on Google Code.
  59. user_name: Your Google account name.
  60. password: The googlecode.com password for your account.
  61. Note that this is NOT your global Google Account password!
  62. summary: A small description for the file.
  63. labels: an optional list of label strings with which to tag the file.
  64. Returns: a tuple:
  65. http_status: 201 if the upload succeeded, something else if an
  66. error occured.
  67. http_reason: The human-readable string associated with http_status
  68. file_url: If the upload succeeded, the URL of the file on Google
  69. Code, None otherwise.
  70. """
  71. # The login is the user part of user@gmail.com. If the login provided
  72. # is in the full user@domain form, strip it down.
  73. if '@' in user_name:
  74. user_name = user_name[:user_name.index('@')]
  75. form_fields = [('summary', summary)]
  76. if labels is not None:
  77. form_fields.extend([('label', l.strip()) for l in labels])
  78. content_type, body = encode_upload_request(form_fields, file)
  79. upload_host = '%s.googlecode.com' % project_name
  80. upload_uri = '/files'
  81. auth_token = base64.b64encode('%s:%s'% (user_name, password))
  82. headers = {
  83. 'Authorization': 'Basic %s' % auth_token,
  84. 'User-Agent': 'Googlecode.com uploader v0.9.4 (DMDirc)',
  85. 'Content-Type': content_type,
  86. }
  87. server = httplib.HTTPSConnection(upload_host)
  88. server.request('POST', upload_uri, body, headers)
  89. resp = server.getresponse()
  90. server.close()
  91. if resp.status == 201:
  92. location = resp.getheader('Location', None)
  93. else:
  94. location = None
  95. return resp.status, resp.reason, location
  96. def encode_upload_request(fields, file_path):
  97. """Encode the given fields and file into a multipart form body.
  98. fields is a sequence of (name, value) pairs. file is the path of
  99. the file to upload. The file will be uploaded to Google Code with
  100. the same file name.
  101. Returns: (content_type, body) ready for httplib.HTTP instance
  102. """
  103. BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla'
  104. CRLF = '\r\n'
  105. body = []
  106. # Add the metadata about the upload first
  107. for key, value in fields:
  108. body.extend(
  109. ['--' + BOUNDARY,
  110. 'Content-Disposition: form-data; name="%s"' % key,
  111. '',
  112. value,
  113. ])
  114. # Now add the file itself
  115. file_name = os.path.basename(file_path)
  116. f = open(file_path, 'rb')
  117. file_content = f.read()
  118. f.close()
  119. body.extend(
  120. ['--' + BOUNDARY,
  121. 'Content-Disposition: form-data; name="filename"; filename="%s"'
  122. % file_name,
  123. # The upload server determines the mime-type, no need to set it.
  124. 'Content-Type: application/octet-stream',
  125. '',
  126. file_content,
  127. ])
  128. # Finalize the form body
  129. body.extend(['--' + BOUNDARY + '--', ''])
  130. return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
  131. def main():
  132. parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY '
  133. '[options] FILE')
  134. parser.add_option('-s', '--summary', dest='summary',
  135. help='Short description of the file')
  136. parser.add_option('-u', '--user', dest='user',
  137. help='Your Google Code username')
  138. parser.add_option('--pass', dest='password',
  139. help='Your Google Code password')
  140. parser.add_option('-l', '--labels', dest='labels',
  141. help='An optional list of labels to attach to the file')
  142. options, args = parser.parse_args()
  143. if not options.summary:
  144. parser.error('File summary is missing.')
  145. elif not options.user:
  146. parser.error('Username is missing.')
  147. elif not options.password:
  148. parser.error('Password is missing.')
  149. elif len(args) < 1:
  150. parser.error('File to upload not provided.')
  151. elif len(args) > 1:
  152. parser.error('Only one file may be specified.')
  153. file_path = args[0]
  154. if options.labels:
  155. labels = options.labels.split(',')
  156. else:
  157. labels = None
  158. # print 'Summary: %s' % options.summary
  159. # print 'Labels: %s' % options.labels
  160. # print 'Path: %s' % file_path
  161. status, reason, url = upload(file_path, 'dmdirc', options.user, options.password, options.summary, labels)
  162. if url:
  163. print 'The file was uploaded successfully.'
  164. print 'URL: %s' % url
  165. return 0
  166. else:
  167. print 'An error occurred. Your file was not uploaded.'
  168. print 'Google Code upload server said: %s (%s)' % (reason, status)
  169. return 1
  170. if __name__ == '__main__':
  171. sys.exit(main())