blob: b58a41e860f98602f80eed9ff72df3b929000922 [file] [log] [blame]
csharptest25037402011-05-19 15:42:12 -05001
2<!-- saved from url=(0068)http://support.googlecode.com/svn/trunk/scripts/googlecode_upload.py -->
3<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/env python
4#
5# Copyright 2006, 2007 Google Inc. All Rights Reserved.
6# Author: danderson@google.com (David Anderson)
7#
8# Script for uploading files to a Google Code project.
9#
10# This is intended to be both a useful script for people who want to
11# streamline project uploads and a reference implementation for
12# uploading files to Google Code projects.
13#
14# To upload a file to Google Code, you need to provide a path to the
15# file on your local machine, a small summary of what the file is, a
16# project name, and a valid account that is a member or owner of that
17# project. You can optionally provide a list of labels that apply to
18# the file. The file will be uploaded under the same name that it has
19# in your local filesystem (that is, the "basename" or last path
20# component). Run the script with '--help' to get the exact syntax
21# and available options.
22#
23# Note that the upload script requests that you enter your
24# googlecode.com password. This is NOT your Gmail account password!
25# This is the password you use on googlecode.com for committing to
26# Subversion and uploading files. You can find your password by going
27# to http://code.google.com/hosting/settings when logged in with your
28# Gmail account. If you have already committed to your project's
29# Subversion repository, the script will automatically retrieve your
30# credentials from there (unless disabled, see the output of '--help'
31# for details).
32#
33# If you are looking at this script as a reference for implementing
34# your own Google Code file uploader, then you should take a look at
35# the upload() function, which is the meat of the uploader. You
36# basically need to build a multipart/form-data POST request with the
37# right fields and send it to https://PROJECT.googlecode.com/files .
38# Authenticate the request using HTTP Basic authentication, as is
39# shown below.
40#
41# Licensed under the terms of the Apache Software License 2.0:
42# http://www.apache.org/licenses/LICENSE-2.0
43#
44# Questions, comments, feature requests and patches are most welcome.
45# Please direct all of these to the Google Code users group:
46# http://groups.google.com/group/google-code-hosting
47
48"""Google Code file uploader script.
49"""
50
51__author__ = 'danderson@google.com (David Anderson)'
52
53import httplib
54import os.path
55import optparse
56import getpass
57import base64
58import sys
59
60
61def upload(file, project_name, user_name, password, summary, labels=None):
62 """Upload a file to a Google Code project's file server.
63
64 Args:
65 file: The local path to the file.
66 project_name: The name of your project on Google Code.
67 user_name: Your Google account name.
68 password: The googlecode.com password for your account.
69 Note that this is NOT your global Google Account password!
70 summary: A small description for the file.
71 labels: an optional list of label strings with which to tag the file.
72
73 Returns: a tuple:
74 http_status: 201 if the upload succeeded, something else if an
75 error occured.
76 http_reason: The human-readable string associated with http_status
77 file_url: If the upload succeeded, the URL of the file on Google
78 Code, None otherwise.
79 """
80 # The login is the user part of user@gmail.com. If the login provided
81 # is in the full user@domain form, strip it down.
82 if user_name.endswith('@gmail.com'):
83 user_name = user_name[:user_name.index('@gmail.com')]
84
85 form_fields = [('summary', summary)]
86 if labels is not None:
87 form_fields.extend([('label', l.strip()) for l in labels])
88
89 content_type, body = encode_upload_request(form_fields, file)
90
91 upload_host = '%s.googlecode.com' % project_name
92 upload_uri = '/files'
93 auth_token = base64.b64encode('%s:%s'% (user_name, password))
94 headers = {
95 'Authorization': 'Basic %s' % auth_token,
96 'User-Agent': 'Googlecode.com uploader v0.9.4',
97 'Content-Type': content_type,
98 }
99
100 server = httplib.HTTPSConnection(upload_host)
101 server.request('POST', upload_uri, body, headers)
102 resp = server.getresponse()
103 server.close()
104
105 if resp.status == 201:
106 location = resp.getheader('Location', None)
107 else:
108 location = None
109 return resp.status, resp.reason, location
110
111
112def encode_upload_request(fields, file_path):
113 """Encode the given fields and file into a multipart form body.
114
115 fields is a sequence of (name, value) pairs. file is the path of
116 the file to upload. The file will be uploaded to Google Code with
117 the same file name.
118
119 Returns: (content_type, body) ready for httplib.HTTP instance
120 """
121 BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla'
122 CRLF = '\r\n'
123
124 body = []
125
126 # Add the metadata about the upload first
127 for key, value in fields:
128 body.extend(
129 ['--' + BOUNDARY,
130 'Content-Disposition: form-data; name="%s"' % key,
131 '',
132 value,
133 ])
134
135 # Now add the file itself
136 file_name = os.path.basename(file_path)
137 f = open(file_path, 'rb')
138 file_content = f.read()
139 f.close()
140
141 body.extend(
142 ['--' + BOUNDARY,
143 'Content-Disposition: form-data; name="filename"; filename="%s"'
144 % file_name,
145 # The upload server determines the mime-type, no need to set it.
146 'Content-Type: application/octet-stream',
147 '',
148 file_content,
149 ])
150
151 # Finalize the form body
152 body.extend(['--' + BOUNDARY + '--', ''])
153
154 return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
155
156
157def upload_find_auth(file_path, project_name, summary, labels=None,
158 user_name=None, password=None, tries=3):
159 """Find credentials and upload a file to a Google Code project's file server.
160
161 file_path, project_name, summary, and labels are passed as-is to upload.
162
163 Args:
164 file_path: The local path to the file.
165 project_name: The name of your project on Google Code.
166 summary: A small description for the file.
167 labels: an optional list of label strings with which to tag the file.
168 config_dir: Path to Subversion configuration directory, 'none', or None.
169 user_name: Your Google account name.
170 tries: How many attempts to make.
171 """
172
173 while tries &gt; 0:
174 if user_name is None:
175 # Read username if not specified or loaded from svn config, or on
176 # subsequent tries.
177 sys.stdout.write('Please enter your googlecode.com username: ')
178 sys.stdout.flush()
179 user_name = sys.stdin.readline().rstrip()
180 if password is None:
181 # Read password if not loaded from svn config, or on subsequent tries.
182 print 'Please enter your googlecode.com password.'
183 print '** Note that this is NOT your Gmail account password! **'
184 print 'It is the password you use to access Subversion repositories,'
185 print 'and can be found here: http://code.google.com/hosting/settings'
186 password = getpass.getpass()
187
188 status, reason, url = upload(file_path, project_name, user_name, password,
189 summary, labels)
190 # Returns 403 Forbidden instead of 401 Unauthorized for bad
191 # credentials as of 2007-07-17.
192 if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]:
193 # Rest for another try.
194 user_name = password = None
195 tries = tries - 1
196 else:
197 # We're done.
198 break
199
200 return status, reason, url
201
202
203def main():
204 parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY '
205 '-p PROJECT [options] FILE')
206 parser.add_option('-s', '--summary', dest='summary',
207 help='Short description of the file')
208 parser.add_option('-p', '--project', dest='project',
209 help='Google Code project name')
210 parser.add_option('-u', '--user', dest='user',
211 help='Your Google Code username')
212 parser.add_option('-w', '--password', dest='password',
213 help='Your Google Code password')
214 parser.add_option('-l', '--labels', dest='labels',
215 help='An optional list of comma-separated labels to attach '
216 'to the file')
217
218 options, args = parser.parse_args()
219
220 if not options.summary:
221 parser.error('File summary is missing.')
222 elif not options.project:
223 parser.error('Project name is missing.')
224 elif len(args) &lt; 1:
225 parser.error('File to upload not provided.')
226 elif len(args) &gt; 1:
227 parser.error('Only one file may be specified.')
228
229 file_path = args[0]
230
231 if options.labels:
232 labels = options.labels.split(',')
233 else:
234 labels = None
235
236 status, reason, url = upload_find_auth(file_path, options.project,
237 options.summary, labels,
238 options.user, options.password)
239 if url:
240 print 'The file was uploaded successfully.'
241 print 'URL: %s' % url
242 return 0
243 else:
244 print 'An error occurred. Your file was not uploaded.'
245 print 'Google Code upload server said: %s (%s)' % (reason, status)
246 return 1
247
248
249if __name__ == '__main__':
250 sys.exit(main())
251</pre></body></html>