Merge lp:~ricardokirkner/locolander/celery-tasks into lp:locolander

Proposed by Ricardo Kirkner
Status: Superseded
Proposed branch: lp:~ricardokirkner/locolander/celery-tasks
Merge into: lp:locolander
Prerequisite: lp:~ricardokirkner/locolander/add-author
Diff against target: 385 lines (+293/-33)
7 files modified
locolander/locolanderweb/migrations/0003_auto__add_field_mergerequest_author.py (+87/-0)
locolander/locolanderweb/models.py (+0/-4)
locolander/locolanderweb/tasks.py (+52/-0)
locolander/locolanderweb/tests/__init__.py (+5/-4)
locolander/locolanderweb/tests/test_base.py (+1/-1)
locolander/locolanderweb/tests/test_models.py (+10/-24)
locolander/locolanderweb/tests/test_tasks.py (+138/-0)
To merge this branch: bzr merge lp:~ricardokirkner/locolander/celery-tasks
Reviewer Review Type Date Requested Status
LocoLanderos Pending
Review via email: mp+174581@code.launchpad.net

Commit message

added celery-based tasks

- run_project: processes all outstanding merge requests for a project
- run_request: runs tests and merges a merge requests
- scan_project: finds approved requests for a project

To post a comment you must log in.
24. By Ricardo Kirkner

merged add-author

25. By Ricardo Kirkner

fixes per review

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'locolander/locolanderweb/migrations/0003_auto__add_field_mergerequest_author.py'
2--- locolander/locolanderweb/migrations/0003_auto__add_field_mergerequest_author.py 1970-01-01 00:00:00 +0000
3+++ locolander/locolanderweb/migrations/0003_auto__add_field_mergerequest_author.py 2013-07-14 15:26:27 +0000
4@@ -0,0 +1,87 @@
5+# -*- coding: utf-8 -*-
6+from south.db import db
7+from south.v2 import SchemaMigration
8+
9+
10+class Migration(SchemaMigration):
11+
12+ def forwards(self, orm):
13+ # Adding field 'MergeRequest.author'
14+ db.add_column(u'locolanderweb_mergerequest', 'author',
15+ self.gf('django.db.models.fields.TextField')(default=''),
16+ keep_default=False)
17+
18+
19+ def backwards(self, orm):
20+ # Deleting field 'MergeRequest.author'
21+ db.delete_column(u'locolanderweb_mergerequest', 'author')
22+
23+
24+ models = {
25+ u'auth.group': {
26+ 'Meta': {'object_name': 'Group'},
27+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
28+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
29+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
30+ },
31+ u'auth.permission': {
32+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
33+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
34+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
35+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
36+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
37+ },
38+ u'auth.user': {
39+ 'Meta': {'object_name': 'User'},
40+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
41+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
42+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
43+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
44+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
46+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
47+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
48+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
49+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
50+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
51+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
52+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
53+ },
54+ u'contenttypes.contenttype': {
55+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
56+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
57+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
58+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
59+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
60+ },
61+ u'locolanderweb.mergerequest': {
62+ 'Meta': {'object_name': 'MergeRequest'},
63+ 'author': ('django.db.models.fields.TextField', [], {'default': "''"}),
64+ 'commit_message': ('django.db.models.fields.TextField', [], {}),
65+ 'date_completed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
66+ 'date_created': ('django.db.models.fields.DateTimeField', [], {}),
67+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
68+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['locolanderweb.Project']"}),
69+ 'reviewers': ('django.db.models.fields.TextField', [], {}),
70+ 'source': ('django.db.models.fields.TextField', [], {}),
71+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '128'}),
72+ 'target': ('django.db.models.fields.TextField', [], {})
73+ },
74+ u'locolanderweb.project': {
75+ 'Meta': {'object_name': 'Project'},
76+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
77+ 'name': ('django.db.models.fields.TextField', [], {}),
78+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
79+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
80+ },
81+ u'locolanderweb.runlog': {
82+ 'Meta': {'object_name': 'RunLog'},
83+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84+ 'merge_request': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['locolanderweb.MergeRequest']"}),
85+ 'raw': ('django.db.models.fields.TextField', [], {}),
86+ 'return_code': ('django.db.models.fields.IntegerField', [], {}),
87+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
88+ }
89+ }
90+
91+ complete_apps = ['locolanderweb']
92
93=== modified file 'locolander/locolanderweb/models.py'
94--- locolander/locolanderweb/models.py 2013-07-14 15:26:27 +0000
95+++ locolander/locolanderweb/models.py 2013-07-14 15:26:27 +0000
96@@ -39,10 +39,6 @@
97
98 def approved_requests(self):
99 """Return approved merge proposals."""
100- mrs = self.service.approved_requests_for_project_url(self.url)
101- for mr in mrs:
102- MergeRequest.objects.get_or_create(project=self, **mr)
103-
104 return MergeRequest.objects.filter(project=self, status=STATUS_PENDING)
105
106
107
108=== added file 'locolander/locolanderweb/tasks.py'
109--- locolander/locolanderweb/tasks.py 1970-01-01 00:00:00 +0000
110+++ locolander/locolanderweb/tasks.py 2013-07-14 15:26:27 +0000
111@@ -0,0 +1,52 @@
112+import os.path
113+import subprocess
114+
115+from celery import task
116+
117+from locolanderweb.models import MergeRequest, RunLog
118+
119+
120+@task
121+def run_project(project):
122+ for request in project.approved_requests():
123+ run_request(request)
124+
125+
126+@task
127+def run_request(request):
128+ project = request.project
129+ source = request.source
130+ target = request.target
131+ author = request.author
132+ message = request.commit_message
133+
134+ cmd = [
135+ os.path.realpath(os.path.join(
136+ os.path.abspath('.'), 'docker/scripts/locolander')),
137+ project.name,
138+ source,
139+ target,
140+ message,
141+ author,
142+ ]
143+ try:
144+ output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
145+ return_code = 0
146+ error = False
147+ except subprocess.CalledProcessError as err:
148+ output = err.output
149+ return_code = err.returncode
150+ error = True
151+
152+ RunLog.objects.create(merge_request=request, raw=output,
153+ return_code=return_code)
154+
155+ # update status on merge request
156+ request.complete(error=error)
157+
158+
159+@task
160+def scan_project(project):
161+ mrs = project.service.approved_requests_for_project_url(project.url)
162+ for mr in mrs:
163+ MergeRequest.objects.get_or_create(project=project, **mr)
164
165=== modified file 'locolander/locolanderweb/tests/__init__.py'
166--- locolander/locolanderweb/tests/__init__.py 2013-06-21 15:34:22 +0000
167+++ locolander/locolanderweb/tests/__init__.py 2013-07-14 15:26:27 +0000
168@@ -1,4 +1,5 @@
169-from locolanderweb.tests.test_code_style import *
170-from locolanderweb.tests.test_forms import *
171-from locolanderweb.tests.test_models import *
172-from locolanderweb.tests.test_views import *
173+from .test_code_style import *
174+from .test_forms import *
175+from .test_models import *
176+from .test_tasks import *
177+from .test_views import *
178
179=== modified file 'locolander/locolanderweb/tests/test_base.py'
180--- locolander/locolanderweb/tests/test_base.py 2013-07-14 15:26:27 +0000
181+++ locolander/locolanderweb/tests/test_base.py 2013-07-14 15:26:27 +0000
182@@ -36,7 +36,7 @@
183
184 def make_project(self, name=None, user=None, url=None):
185 if name is None:
186- name = self.make_random_string(prefix='project-')
187+ name = self.make_random_string(prefix='project')
188 if user is None:
189 user = self.make_user()
190 if url is None:
191
192=== modified file 'locolander/locolanderweb/tests/test_models.py'
193--- locolander/locolanderweb/tests/test_models.py 2013-07-14 15:26:27 +0000
194+++ locolander/locolanderweb/tests/test_models.py 2013-07-14 15:26:27 +0000
195@@ -15,20 +15,6 @@
196 )
197 from locolanderweb.tests.test_base import BaseTestCase
198
199-MERGE_REQUESTS = [
200- dict(
201- source='source1', target='target1', reviewers='a,b,c',
202- date_created=datetime(2013, 4, 23), commit_message='something 1',
203- ),
204- dict(
205- source='source2', target='target2', reviewers='pepe',
206- date_created=datetime(2012, 2, 29), commit_message='something else',
207- ),
208- dict(
209- source='source3', target='target3', reviewers='pepe y pepa',
210- date_created=datetime.utcnow(), commit_message='',
211- ),
212-]
213
214 INVALID_URLS = (
215 'locolander',
216@@ -99,16 +85,16 @@
217 self.get_service_from_url.assert_called_once_with(self.obj.url)
218
219 def test_approved_requests(self):
220- assert MergeRequest.objects.all().count() == 0
221-
222- service = self.get_service_from_url.return_value
223- service.approved_requests_for_project_url.return_value = MERGE_REQUESTS
224-
225- result = self.obj.approved_requests()
226- self.assertEqual(MergeRequest.objects.all().count(), len(result))
227- for mr in MERGE_REQUESTS:
228- self.assertEqual(
229- MergeRequest.objects.filter(**mr).count(), 1)
230+ assert MergeRequest.objects.filter(project=self.obj).count() == 0
231+
232+ result = self.obj.approved_requests()
233+ self.assertEqual(len(result), 0)
234+
235+ # Create an approved merge request
236+ self.factory.make_merge_request(project=self.obj)
237+
238+ result = self.obj.approved_requests()
239+ self.assertEqual(len(result), 1)
240
241
242 class MergeRequestTestCase(BaseTestCase):
243
244=== added file 'locolander/locolanderweb/tests/test_tasks.py'
245--- locolander/locolanderweb/tests/test_tasks.py 1970-01-01 00:00:00 +0000
246+++ locolander/locolanderweb/tests/test_tasks.py 2013-07-14 15:26:27 +0000
247@@ -0,0 +1,138 @@
248+import subprocess
249+from datetime import datetime
250+from mock import ANY, patch
251+
252+from locolanderweb.models import (
253+ STATUS_ERROR,
254+ STATUS_MERGED,
255+ MergeRequest,
256+ RunLog,
257+)
258+from locolanderweb.tasks import run_project, run_request, scan_project
259+from locolanderweb.tests.test_base import BaseTestCase
260+
261+
262+MERGE_REQUESTS = [
263+ dict(
264+ source='source1', target='target1', reviewers='a,b,c',
265+ date_created=datetime(2013, 4, 23), commit_message='something 1',
266+ ),
267+ dict(
268+ source='source2', target='target2', reviewers='pepe',
269+ date_created=datetime(2012, 2, 29), commit_message='something else',
270+ ),
271+ dict(
272+ source='source3', target='target3', reviewers='pepe y pepa',
273+ date_created=datetime.utcnow(), commit_message='',
274+ ),
275+]
276+
277+
278+class RunProjectTestCase(BaseTestCase):
279+
280+ @patch('locolanderweb.tasks.run_request')
281+ def test_run_project(self, mock_run_request):
282+ project = self.factory.make_project()
283+ merge_request = self.factory.make_merge_request(project=project)
284+
285+ run_project(project)
286+ mock_run_request.assert_called_once_with(merge_request)
287+
288+ @patch('locolanderweb.tasks.run_request')
289+ def test_run_project_with_no_approved_requests(self, mock_run_request):
290+ project = self.factory.make_project()
291+
292+ run_project(project)
293+ self.assertFalse(mock_run_request.called)
294+
295+
296+class RunRequestTestCase(BaseTestCase):
297+
298+ @patch('locolanderweb.tasks.subprocess.check_output')
299+ def test_run_request(self, mock_check_output):
300+ mock_check_output.return_value = 'SUCCESS'
301+
302+ merge_request = self.factory.make_merge_request()
303+
304+ run_request(merge_request)
305+
306+ cmd = [
307+ ANY,
308+ merge_request.project.name,
309+ merge_request.source,
310+ merge_request.target,
311+ merge_request.commit_message,
312+ merge_request.author,
313+ ]
314+ mock_check_output.assert_called_once_with(
315+ cmd, stderr=subprocess.STDOUT)
316+ real_cmd = mock_check_output.call_args[0][0][0]
317+ self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
318+
319+ self.assertEqual(RunLog.objects.all().count(), 1)
320+ run_log = RunLog.objects.get(merge_request=merge_request)
321+ self.assertEqual(run_log.raw, 'SUCCESS')
322+ self.assertEqual(run_log.return_code, 0)
323+
324+ # refresh MR object
325+ mr = MergeRequest.objects.get(pk=merge_request.pk)
326+ self.assertEqual(mr.status, STATUS_MERGED)
327+
328+ @patch('locolanderweb.tasks.subprocess.check_output')
329+ def test_run_request_with_error(self, mock_check_output):
330+ merge_request = self.factory.make_merge_request()
331+
332+ cmd = [
333+ ANY,
334+ merge_request.project.name,
335+ merge_request.source,
336+ merge_request.target,
337+ merge_request.commit_message,
338+ merge_request.author,
339+ ]
340+ error = subprocess.CalledProcessError(1, cmd, output='FAILURE')
341+ mock_check_output.side_effect = error
342+
343+ run_request(merge_request)
344+
345+ mock_check_output.assert_called_once_with(
346+ cmd, stderr=subprocess.STDOUT)
347+ real_cmd = mock_check_output.call_args[0][0][0]
348+ self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
349+
350+ self.assertEqual(RunLog.objects.all().count(), 1)
351+ run_log = RunLog.objects.get(merge_request=merge_request)
352+ self.assertEqual(run_log.raw, 'FAILURE')
353+ self.assertEqual(run_log.return_code, 1)
354+
355+ # refresh MR object
356+ mr = MergeRequest.objects.get(pk=merge_request.pk)
357+ self.assertEqual(mr.status, STATUS_ERROR)
358+
359+
360+class ScanProjectTestCase(BaseTestCase):
361+
362+ def test_scan_project(self):
363+ project = self.factory.make_project()
364+
365+ service = self.get_service_from_url.return_value
366+ service.approved_requests_for_project_url.return_value = MERGE_REQUESTS
367+
368+ scan_project(project)
369+
370+ self.assertEqual(MergeRequest.objects.filter(project=project).count(),
371+ len(MERGE_REQUESTS))
372+ for mr in MERGE_REQUESTS:
373+ self.assertEqual(
374+ MergeRequest.objects.filter(**mr).count(), 1)
375+
376+ def test_scan_project_with_no_approved_request(self):
377+ project = self.factory.make_project()
378+
379+ service = self.get_service_from_url.return_value
380+ service.approved_requests_for_project_url.return_value = []
381+
382+ scan_project(project)
383+
384+ self.assertEqual(MergeRequest.objects.filter(project=project).count(),
385+ 0)

Subscribers

People subscribed via source and target branches

to all changes: