Merge lp:~ricardokirkner/locolander/add-author into lp:locolander
- add-author
- Merge into trunk
Proposed by
Ricardo Kirkner
Status: | Superseded |
---|---|
Proposed branch: | lp:~ricardokirkner/locolander/add-author |
Merge into: | lp:locolander |
Diff against target: |
658 lines (+339/-108) 15 files modified
.bzrignore (+1/-0) .locolander.yml (+1/-22) README (+36/-46) TODO (+43/-0) config/supervisord.conf (+37/-0) fabfile.py (+25/-0) locolander/locolander/settings.py (+9/-10) locolander/locolander/urls.py (+4/-0) locolander/locolanderweb/migrations/0003_auto__chg_field_project_url__add_field_mergerequest_author.py (+93/-0) locolander/locolanderweb/models.py (+1/-0) locolander/locolanderweb/tests/test_base.py (+5/-2) locolander/locolanderweb/tests/test_models.py (+5/-1) locolander/repos/launchpad.py (+16/-1) locolander/repos/tests/test_launchpad.py (+42/-23) requirements.txt (+21/-3) |
To merge this branch: | bzr merge lp:~ricardokirkner/locolander/add-author |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
LocoLanderos | Pending | ||
Review via email: mp+174569@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 20. By Ricardo Kirkner
-
expose locolanderweb models in admin
- 21. By Ricardo Kirkner
-
merged trunk
- 22. By Ricardo Kirkner
-
regenerated migration
- 23. By Ricardo Kirkner
-
merged trunk
- 24. By Ricardo Kirkner
-
reverted unwanted changes
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2013-06-21 22:23:29 +0000 |
3 | +++ .bzrignore 2013-07-13 17:31:27 +0000 |
4 | @@ -1,2 +1,3 @@ |
5 | docker/files/ssh |
6 | locolander.db |
7 | +supervisord.log |
8 | |
9 | === modified file '.locolander.yml' |
10 | --- .locolander.yml 2013-06-29 22:14:32 +0000 |
11 | +++ .locolander.yml 2013-07-13 17:31:27 +0000 |
12 | @@ -1,26 +1,5 @@ |
13 | precise: |
14 | - pip: |
15 | - distribute: 0.6.34 |
16 | - Django: 1.5.1 |
17 | - github3.py: 0.7.0 |
18 | - httplib2: 0.8.0 |
19 | - keyring: 1.5.0 |
20 | - launchpadlib: 1.10.2 |
21 | - lazr.authentication: 0.1.2 |
22 | - lazr.restfulclient: 0.13.3 |
23 | - lazr.uri: 1.0.3 |
24 | - mock: 1.0.1 |
25 | - oauth: 1.0.1 |
26 | - pep8: 1.4.5 |
27 | - pyflakes: 0.7.2 |
28 | - PyYAML: 3.10.0 |
29 | - requests: 1.2.3 |
30 | - simplejson: 3.3.0 |
31 | - South: 0.8.1 |
32 | - testresources: 0.2.7 |
33 | - wadllib: 1.3.2 |
34 | - wsgi-intercept: 0.5.1 |
35 | - zope.interface: 4.0.5 |
36 | + pip: -r requirements.txt |
37 | |
38 | metadata: |
39 | test_script: ./run_tests.sh |
40 | |
41 | === modified file 'README' |
42 | --- README 2013-06-21 20:26:50 +0000 |
43 | +++ README 2013-07-13 17:31:27 +0000 |
44 | @@ -1,50 +1,40 @@ |
45 | |
46 | +========== |
47 | LocoLander |
48 | ========== |
49 | |
50 | -.. image:: locolander/locolanderweb/media/locolander.png |
51 | - |
52 | - |
53 | -To Do |
54 | -===== |
55 | - |
56 | -* automate docker bootstrap |
57 | - |
58 | - - install docker |
59 | - - install squid |
60 | - - setup squid as a global cache |
61 | - |
62 | -* generate Dockerfile |
63 | - |
64 | - - parse custom project file |
65 | - - write Dockerfile for project image |
66 | - - include "env http_proxy http://172.16.42.1:3128" in Dockerfile to use global squid cache |
67 | - |
68 | -* build project images |
69 | - |
70 | - - use generated Dockerfile to run 'docker build' |
71 | - |
72 | - - step1: install base image and system packages |
73 | - - step2: download pip dependencies from PyPI |
74 | - - step3: cut internet access |
75 | - - step4: install pip dependencies from locally downloaded cache |
76 | - |
77 | - - tag built image with project name |
78 | - |
79 | -* main script |
80 | - |
81 | - - get target branch (trunk) |
82 | - - merge source branch into target |
83 | - - run tests |
84 | - - capture output |
85 | - - commit merge and push to target upstream |
86 | - |
87 | -* main process |
88 | - |
89 | - - build project image |
90 | - - run main script in image |
91 | - - extract output from container |
92 | - |
93 | -* prevent inbound/outbound network access inside of container |
94 | - |
95 | -* implement quotas (disk, cpu, mem) |
96 | +.. image::- locolander/locolanderweb/media/locolander.png |
97 | + |
98 | + |
99 | +Installing |
100 | +========== |
101 | + |
102 | +As redis cannot be installed in a virtualenv, you'll need to install it from |
103 | +the system packages |
104 | +:: |
105 | + |
106 | + sudo apt-get install redis-server |
107 | + |
108 | +Everything else can be installed in a virtualenv |
109 | +:: |
110 | + |
111 | + sudo apt-get install fabric |
112 | + fab bootstrap |
113 | + |
114 | + |
115 | +Running |
116 | +======= |
117 | + |
118 | +Once everything is installed, you can start the full stack easily |
119 | +:: |
120 | + |
121 | + fab start_supervisor |
122 | + |
123 | + |
124 | +Developing |
125 | +========== |
126 | + |
127 | +In order to run the tests, you can just do |
128 | +:: |
129 | + |
130 | + fab test |
131 | |
132 | === added file 'TODO' |
133 | --- TODO 1970-01-01 00:00:00 +0000 |
134 | +++ TODO 2013-07-13 17:31:27 +0000 |
135 | @@ -0,0 +1,43 @@ |
136 | +To Do |
137 | +===== |
138 | + |
139 | +* automate docker bootstrap |
140 | + |
141 | + - install docker |
142 | + - install squid |
143 | + - setup squid as a global cache |
144 | + |
145 | +* generate Dockerfile |
146 | + |
147 | + - parse custom project file |
148 | + - write Dockerfile for project image |
149 | + - include "env http_proxy http://172.16.42.1:3128" in Dockerfile to use global squid cache |
150 | + |
151 | +* build project images |
152 | + |
153 | + - use generated Dockerfile to run 'docker build' |
154 | + |
155 | + - step1: install base image and system packages |
156 | + - step2: download pip dependencies from PyPI |
157 | + - step3: cut internet access |
158 | + - step4: install pip dependencies from locally downloaded cache |
159 | + |
160 | + - tag built image with project name |
161 | + |
162 | +* main script |
163 | + |
164 | + - get target branch (trunk) |
165 | + - merge source branch into target |
166 | + - run tests |
167 | + - capture output |
168 | + - commit merge and push to target upstream |
169 | + |
170 | +* main process |
171 | + |
172 | + - build project image |
173 | + - run main script in image |
174 | + - extract output from container |
175 | + |
176 | +* prevent inbound/outbound network access inside of container |
177 | + |
178 | +* implement quotas (disk, cpu, mem) |
179 | |
180 | === added directory 'config' |
181 | === added file 'config/supervisord.conf' |
182 | --- config/supervisord.conf 1970-01-01 00:00:00 +0000 |
183 | +++ config/supervisord.conf 2013-07-13 17:31:27 +0000 |
184 | @@ -0,0 +1,37 @@ |
185 | +; Sample supervisor config file. |
186 | + |
187 | +[supervisord] |
188 | +nodaemon = true |
189 | +loglevel = info |
190 | +logfile = supervisord.log |
191 | + |
192 | + |
193 | +; service configuration sections |
194 | + |
195 | +[program:gunicorn] |
196 | +command = gunicorn_django locolander/locolander/settings.py |
197 | +autostart = true |
198 | +autorestart = true |
199 | +stdout_logfile = supervisord.log |
200 | +redirect_stderr = true |
201 | + |
202 | +[program:redis] |
203 | +command = redis-server |
204 | +autostart = true |
205 | +autorestart = true |
206 | +stdout_logfile = supervisord.log |
207 | +redirect_stderr = true |
208 | + |
209 | +[program:celeryd] |
210 | +command = python locolander/manage.py celeryd |
211 | +autostart = true |
212 | +autorestart = true |
213 | +stdout_logfile = supervisord.log |
214 | +redirect_stderr = true |
215 | + |
216 | +[program:flower] |
217 | +command = python locolander/manage.py celery flower |
218 | +autostart = true |
219 | +autorestart = true |
220 | +stdout_logfile = supervisord.log |
221 | +redirect_stderr = true |
222 | |
223 | === modified file 'fabfile.py' |
224 | --- fabfile.py 2013-07-09 18:42:01 +0000 |
225 | +++ fabfile.py 2013-07-13 17:31:27 +0000 |
226 | @@ -24,3 +24,28 @@ |
227 | @virtualenv |
228 | def test(): |
229 | local('./run_tests.sh') |
230 | + |
231 | + |
232 | +@virtualenv |
233 | +def start_redis(): |
234 | + local('redis-server') |
235 | + |
236 | + |
237 | +@virtualenv |
238 | +def start_gunicorn(): |
239 | + local('gunicorn_django locolander/locolander/settings.py') |
240 | + |
241 | + |
242 | +@virtualenv |
243 | +def start_celery(): |
244 | + manage('celeryd') |
245 | + |
246 | + |
247 | +@virtualenv |
248 | +def start_flower(): |
249 | + manage('celery flower') |
250 | + |
251 | + |
252 | +@virtualenv |
253 | +def start_supervisor(): |
254 | + local('supervisord -c config/supervisord.conf -n') |
255 | |
256 | === modified file 'locolander/locolander/settings.py' |
257 | --- locolander/locolander/settings.py 2013-06-23 00:00:56 +0000 |
258 | +++ locolander/locolander/settings.py 2013-07-13 17:31:27 +0000 |
259 | @@ -2,9 +2,6 @@ |
260 | |
261 | import os |
262 | |
263 | -from django.core.urlresolvers import reverse |
264 | - |
265 | - |
266 | PROJECT_DIR = os.path.abspath(os.path.dirname(__file__)) |
267 | |
268 | DEBUG = True |
269 | @@ -134,6 +131,7 @@ |
270 | # 'django.contrib.admindocs', |
271 | 'locolanderweb', |
272 | 'south', |
273 | + 'djcelery', |
274 | ) |
275 | |
276 | # A sample logging configuration. The only tangible logging |
277 | @@ -165,10 +163,11 @@ |
278 | } |
279 | } |
280 | |
281 | -LOGIN_URL = reverse('login') |
282 | -LOGIN_REDIRECT_URL = reverse('home') |
283 | - |
284 | -try: |
285 | - from local_setting import * |
286 | -except: |
287 | - pass |
288 | +# settings for celery/redis |
289 | +import djcelery |
290 | +djcelery.setup_loader() |
291 | + |
292 | +BROKER_URL = 'redis://localhost:6379/0' |
293 | + |
294 | +LOGIN_URL = '/login/' |
295 | +LOGIN_REDIRECT_URL = '/' |
296 | |
297 | === modified file 'locolander/locolander/urls.py' |
298 | --- locolander/locolander/urls.py 2013-06-23 00:00:56 +0000 |
299 | +++ locolander/locolander/urls.py 2013-07-13 17:31:27 +0000 |
300 | @@ -1,5 +1,6 @@ |
301 | from django.conf.urls import patterns, include, url |
302 | from django.contrib import admin |
303 | +from django.contrib.staticfiles.urls import staticfiles_urlpatterns |
304 | admin.autodiscover() |
305 | |
306 | |
307 | @@ -14,3 +15,6 @@ |
308 | # Uncomment the next line to enable the admin: |
309 | url(r'^admin/', include(admin.site.urls)), |
310 | ) |
311 | + |
312 | +# enable staticfiles for gunicorn |
313 | +urlpatterns += staticfiles_urlpatterns() |
314 | |
315 | === added file 'locolander/locolanderweb/migrations/0003_auto__chg_field_project_url__add_field_mergerequest_author.py' |
316 | --- locolander/locolanderweb/migrations/0003_auto__chg_field_project_url__add_field_mergerequest_author.py 1970-01-01 00:00:00 +0000 |
317 | +++ locolander/locolanderweb/migrations/0003_auto__chg_field_project_url__add_field_mergerequest_author.py 2013-07-13 17:31:27 +0000 |
318 | @@ -0,0 +1,93 @@ |
319 | +# -*- coding: utf-8 -*- |
320 | +from south.db import db |
321 | +from south.v2 import SchemaMigration |
322 | + |
323 | + |
324 | +class Migration(SchemaMigration): |
325 | + |
326 | + def forwards(self, orm): |
327 | + |
328 | + # Changing field 'Project.url' |
329 | + db.alter_column(u'locolanderweb_project', 'url', self.gf('django.db.models.fields.CharField')(max_length=256)) |
330 | + # Adding field 'MergeRequest.author' |
331 | + db.add_column(u'locolanderweb_mergerequest', 'author', |
332 | + self.gf('django.db.models.fields.TextField')(default=''), |
333 | + keep_default=False) |
334 | + |
335 | + |
336 | + def backwards(self, orm): |
337 | + |
338 | + # Changing field 'Project.url' |
339 | + db.alter_column(u'locolanderweb_project', 'url', self.gf('django.db.models.fields.URLField')(max_length=200)) |
340 | + # Deleting field 'MergeRequest.author' |
341 | + db.delete_column(u'locolanderweb_mergerequest', 'author') |
342 | + |
343 | + |
344 | + models = { |
345 | + u'auth.group': { |
346 | + 'Meta': {'object_name': 'Group'}, |
347 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
348 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
349 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
350 | + }, |
351 | + u'auth.permission': { |
352 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
353 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
354 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
355 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
356 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
357 | + }, |
358 | + u'auth.user': { |
359 | + 'Meta': {'object_name': 'User'}, |
360 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
361 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
362 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
363 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
364 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
365 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
366 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
367 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
368 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
369 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
370 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
371 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
372 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
373 | + }, |
374 | + u'contenttypes.contenttype': { |
375 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
376 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
377 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
378 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
379 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
380 | + }, |
381 | + u'locolanderweb.mergerequest': { |
382 | + 'Meta': {'object_name': 'MergeRequest'}, |
383 | + 'author': ('django.db.models.fields.TextField', [], {'default': "''"}), |
384 | + 'commit_message': ('django.db.models.fields.TextField', [], {}), |
385 | + 'date_completed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
386 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {}), |
387 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
388 | + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['locolanderweb.Project']"}), |
389 | + 'reviewers': ('django.db.models.fields.TextField', [], {}), |
390 | + 'source': ('django.db.models.fields.TextField', [], {}), |
391 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '128'}), |
392 | + 'target': ('django.db.models.fields.TextField', [], {}) |
393 | + }, |
394 | + u'locolanderweb.project': { |
395 | + 'Meta': {'object_name': 'Project'}, |
396 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
397 | + 'name': ('django.db.models.fields.TextField', [], {}), |
398 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), |
399 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '256'}) |
400 | + }, |
401 | + u'locolanderweb.runlog': { |
402 | + 'Meta': {'object_name': 'RunLog'}, |
403 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
404 | + 'merge_request': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['locolanderweb.MergeRequest']"}), |
405 | + 'raw': ('django.db.models.fields.TextField', [], {}), |
406 | + 'return_code': ('django.db.models.fields.IntegerField', [], {}), |
407 | + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
408 | + } |
409 | + } |
410 | + |
411 | + complete_apps = ['locolanderweb'] |
412 | |
413 | === modified file 'locolander/locolanderweb/models.py' |
414 | --- locolander/locolanderweb/models.py 2013-07-06 23:19:29 +0000 |
415 | +++ locolander/locolanderweb/models.py 2013-07-13 17:31:27 +0000 |
416 | @@ -55,6 +55,7 @@ |
417 | source = models.TextField() |
418 | target = models.TextField() |
419 | commit_message = models.TextField() |
420 | + author = models.TextField(default='') |
421 | reviewers = models.TextField() |
422 | date_created = models.DateTimeField() |
423 | date_completed = models.DateTimeField(null=True, blank=True) |
424 | |
425 | === modified file 'locolander/locolanderweb/tests/test_base.py' |
426 | --- locolander/locolanderweb/tests/test_base.py 2013-07-06 23:19:29 +0000 |
427 | +++ locolander/locolanderweb/tests/test_base.py 2013-07-13 17:31:27 +0000 |
428 | @@ -46,7 +46,8 @@ |
429 | def make_merge_request( |
430 | self, project=None, status=STATUS_PENDING, |
431 | source=None, target=None, commit_message=None, |
432 | - reviewers=None, date_created=None, date_completed=None): |
433 | + author=None, reviewers=None, date_created=None, |
434 | + date_completed=None): |
435 | if project is None: |
436 | project = self.make_project() |
437 | if source is None: |
438 | @@ -55,13 +56,15 @@ |
439 | target = 'http://foo.target' |
440 | if commit_message is None: |
441 | commit_message = 'Foo bar commit message.' |
442 | + if author is None: |
443 | + author = 'Some Person <some@domain>' |
444 | if reviewers is None: |
445 | reviewers = ['mengano', 'fulano'] |
446 | if date_created is None: |
447 | date_created = datetime.utcnow() |
448 | return MergeRequest.objects.create( |
449 | project=project, status=status, source=source, target=target, |
450 | - commit_message=commit_message, reviewers=reviewers, |
451 | + commit_message=commit_message, author=author, reviewers=reviewers, |
452 | date_created=date_created, date_completed=date_completed) |
453 | |
454 | def make_run_log(self, merge_request=None, return_code=0, raw=None): |
455 | |
456 | === modified file 'locolander/locolanderweb/tests/test_models.py' |
457 | --- locolander/locolanderweb/tests/test_models.py 2013-07-06 23:19:29 +0000 |
458 | +++ locolander/locolanderweb/tests/test_models.py 2013-07-13 17:31:27 +0000 |
459 | @@ -120,7 +120,8 @@ |
460 | self.obj = MergeRequest.objects.create( |
461 | project=self.project, |
462 | source='source', target='target', commit_message='commit', |
463 | - reviewers='fulano, mengano', date_created=self.created) |
464 | + author='Some Person <some@domain>', reviewers='fulano, mengano', |
465 | + date_created=self.created) |
466 | |
467 | def test_status_default(self): |
468 | self.assertEqual(self.obj.status, STATUS_PENDING) |
469 | @@ -139,6 +140,9 @@ |
470 | def test_commit_message(self): |
471 | self.assertEqual(self.obj.commit_message, 'commit') |
472 | |
473 | + def test_author(self): |
474 | + self.assertEqual(self.obj.author, 'Some Person <some@domain>') |
475 | + |
476 | def test_reviewers(self): |
477 | self.assertEqual(self.obj.reviewers, 'fulano, mengano') |
478 | |
479 | |
480 | === modified file 'locolander/repos/launchpad.py' |
481 | --- locolander/repos/launchpad.py 2013-07-06 23:19:29 +0000 |
482 | +++ locolander/repos/launchpad.py 2013-07-13 17:31:27 +0000 |
483 | @@ -5,6 +5,10 @@ |
484 | import os |
485 | import re |
486 | |
487 | +from datetime import datetime |
488 | + |
489 | +import bzrlib.plugin |
490 | +from bzrlib.branch import Branch |
491 | from launchpadlib.launchpad import Launchpad |
492 | |
493 | from repos import errors |
494 | @@ -29,6 +33,14 @@ |
495 | self.launchpad = Launchpad.login_anonymously( |
496 | 'locolander', 'production', CACHE_DIR) |
497 | self.lp_projects = self.launchpad.projects |
498 | + # load bzr plugins so that lp: namespace is recognized |
499 | + bzrlib.plugin.load_plugins() |
500 | + |
501 | + def get_author(self, branch_url): |
502 | + branch = Branch.open(branch_url) |
503 | + tip = branch.repository.get_revision( |
504 | + branch.get_rev_id(branch.revno())) |
505 | + return tip.committer |
506 | |
507 | def approved_requests_for_project_url(self, project_url): |
508 | """Return approved merge proposals.""" |
509 | @@ -49,12 +61,15 @@ |
510 | proposals = repo.getMergeProposals() |
511 | for mp in proposals: |
512 | if mp.queue_status.lower() == 'approved': |
513 | + created = mp.date_created |
514 | approved.append({ |
515 | # XXX: needs UTC processing? |
516 | - 'date_created': mp.date_created, |
517 | + 'date_created': datetime(created.year, created.month, |
518 | + created.day), |
519 | 'source': mp.source_branch.bzr_identity, |
520 | 'target': mp.target_branch.bzr_identity, |
521 | 'reviewers': mp.reviewer.name, # csv |
522 | 'commit_message': mp.commit_message, |
523 | + 'author': self.get_author(mp.source_branch.bzr_identity), |
524 | }) |
525 | return approved |
526 | |
527 | === modified file 'locolander/repos/tests/test_launchpad.py' |
528 | --- locolander/repos/tests/test_launchpad.py 2013-07-06 22:26:19 +0000 |
529 | +++ locolander/repos/tests/test_launchpad.py 2013-07-13 17:31:27 +0000 |
530 | @@ -3,11 +3,16 @@ |
531 | from datetime import datetime |
532 | from unittest import TestCase |
533 | |
534 | +import bzrlib.branch |
535 | from mock import Mock, patch |
536 | |
537 | from repos import errors, launchpad |
538 | |
539 | |
540 | +# fake bzrlib Branch object |
541 | +FakeBranch = Mock(spec=bzrlib.branch.Branch) |
542 | + |
543 | + |
544 | class FakeMP(object): |
545 | """Fake launchpadlib Merge Proposal object.""" |
546 | |
547 | @@ -71,26 +76,40 @@ |
548 | with self.assertRaises(errors.RepositoryDoesNotExist): |
549 | self.lp.approved_requests_for_project_url('lp:not-found') |
550 | |
551 | - def test_valid_project_without_approved_mp(self): |
552 | - mps = self.lp.approved_requests_for_project_url('lp:test-project') |
553 | - self.assertEqual(len(mps), 1) |
554 | - mp = mps[0] |
555 | - self.assertIsInstance(mp, dict) |
556 | - |
557 | - expected_keys = ['date_created', 'source', 'target', 'reviewers', |
558 | - 'commit_message'] |
559 | - for key in expected_keys: |
560 | - self.assertIn(key, mp) |
561 | - self.assertEqual(mp['commit_message'], 'approved mp') |
562 | - |
563 | - def test_valid_project_return_approved_mp(self): |
564 | - mps = self.lp.approved_requests_for_project_url('lp:test-project') |
565 | - self.assertEqual(len(mps), 1) |
566 | - mp = mps[0] |
567 | - self.assertIsInstance(mp, dict) |
568 | - |
569 | - expected_keys = ['date_created', 'source', 'target', 'reviewers', |
570 | - 'commit_message'] |
571 | - for key in expected_keys: |
572 | - self.assertIn(key, mp) |
573 | - self.assertEqual(mp['commit_message'], 'approved mp') |
574 | + @patch('repos.launchpad.Branch.open') |
575 | + def test_valid_project_without_approved_mp(self, mock_open): |
576 | + mps = self.lp.approved_requests_for_project_url('lp:test-project') |
577 | + self.assertEqual(len(mps), 1) |
578 | + mp = mps[0] |
579 | + self.assertIsInstance(mp, dict) |
580 | + |
581 | + expected_keys = ['date_created', 'source', 'target', 'reviewers', |
582 | + 'commit_message', 'author'] |
583 | + for key in expected_keys: |
584 | + self.assertIn(key, mp) |
585 | + self.assertEqual(mp['commit_message'], 'approved mp') |
586 | + |
587 | + @patch('repos.launchpad.Branch.open') |
588 | + def test_valid_project_return_approved_mp(self, mock_open): |
589 | + mps = self.lp.approved_requests_for_project_url('lp:test-project') |
590 | + self.assertEqual(len(mps), 1) |
591 | + mp = mps[0] |
592 | + self.assertIsInstance(mp, dict) |
593 | + |
594 | + expected_keys = ['date_created', 'source', 'target', 'reviewers', |
595 | + 'commit_message', 'author'] |
596 | + for key in expected_keys: |
597 | + self.assertIn(key, mp) |
598 | + self.assertEqual(mp['commit_message'], 'approved mp') |
599 | + |
600 | + @patch('repos.launchpad.Branch.open') |
601 | + def test_get_author(self, mock_open): |
602 | + mock_revision = Mock() |
603 | + mock_revision.committer = 'The Author <some@email.com>' |
604 | + mock_repository = Mock() |
605 | + mock_repository.get_revision.return_value = mock_revision |
606 | + mock_open.return_value.repository = mock_repository |
607 | + |
608 | + author = self.lp.get_author('lp:test-project') |
609 | + |
610 | + self.assertEqual(author, 'The Author <some@email.com>') |
611 | |
612 | === modified file 'requirements.txt' |
613 | --- requirements.txt 2013-07-13 15:38:09 +0000 |
614 | +++ requirements.txt 2013-07-13 17:31:27 +0000 |
615 | @@ -1,22 +1,40 @@ |
616 | -distribute==0.6.34 |
617 | +amqp==1.0.12 |
618 | +anyjson==0.3.3 |
619 | +billiard==2.7.3.31 |
620 | +bzr==2.6b2 |
621 | +celery==3.0.21 |
622 | +cssselect==0.8 |
623 | Django==1.5.1 |
624 | +django-celery==3.0.17 |
625 | +flower==0.5.2 |
626 | github3.py==0.7.0 |
627 | +gunicorn==17.5 |
628 | httplib2==0.8 |
629 | keyring==1.5 |
630 | +kombu==2.5.12 |
631 | launchpadlib==1.10.2 |
632 | lazr.authentication==0.1.2 |
633 | lazr.restfulclient==0.13.3 |
634 | lazr.uri==1.0.3 |
635 | +lxml==3.2.1 |
636 | +meld3==0.6.10 |
637 | mock==1.0.1 |
638 | oauth==1.0.1 |
639 | -pep8==1.4.5 |
640 | -pyflakes==0.7.2 |
641 | +pep8==1.4.6 |
642 | +pyflakes==0.7.3 |
643 | pyquery==1.2.4 |
644 | +python-dateutil==2.1 |
645 | +pytz==2013b |
646 | PyYAML==3.10 |
647 | +redis==2.7.6 |
648 | requests==1.2.3 |
649 | simplejson==3.3.0 |
650 | +six==1.3.0 |
651 | South==0.8.1 |
652 | +supervisor==3.0b2 |
653 | testresources==0.2.7 |
654 | +tornado==3.1 |
655 | wadllib==1.3.2 |
656 | wsgi-intercept==0.5.1 |
657 | +wsgiref==0.1.2 |
658 | zope.interface==4.0.5 |