-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Download raw build log #3585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Download raw build log #3585
Changes from all commits
ae03632
fea2137
200b358
a7cfd45
717d70e
a414abb
8b6e266
33af7f2
18545db
9f6da49
a1f22a7
1c46ce0
d4eaa37
31d3999
73b7a12
9be631d
16aa10b
3cd340d
d91dd60
4ca3c47
47315c8
aca0ef8
75a8042
5c7629c
949b42d
d9f229f
ce7e16c
40f259d
a029a08
c3b02f4
03e642b
39b3607
7bfd92c
2af04a8
59af03a
990145c
52aafc5
dc2cbdd
86d7bde
29696ed
c8dc506
a037062
bd03a78
1415270
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Read the Docs build information | ||
Build id: {{ build.id }} | ||
Project: {{ build.project_slug }} | ||
Version: {{ build.version_slug }} | ||
Commit: {{ build.commit }} | ||
Date: {{ build.date }} | ||
State: {{ build.state }} | ||
Success: {% if build.state == 'finished' %}{{ build.success }}{% else %}Unknown{% endif %} | ||
|
||
{% for command in build.commands %} | ||
[rtd-command-info] start-time: {{ command.start_time }}, end-time: {{ command.end_time }}, duration: {{ command.run_time }}, exit-code: {{ command.exit_code }} | ||
{{ command.command|safe }} | ||
{{ command.output|safe }} | ||
{% endfor %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,8 +9,9 @@ | |
from allauth.socialaccount.models import SocialAccount | ||
from django.shortcuts import get_object_or_404 | ||
from rest_framework import decorators, permissions, status, viewsets | ||
from django.template.loader import render_to_string | ||
from rest_framework.decorators import detail_route | ||
from rest_framework.renderers import JSONRenderer | ||
from rest_framework.renderers import BaseRenderer, JSONRenderer | ||
from rest_framework.response import Response | ||
|
||
from readthedocs.builds.constants import BRANCH, TAG | ||
|
@@ -34,6 +35,28 @@ | |
log = logging.getLogger(__name__) | ||
|
||
|
||
class PlainTextBuildRenderer(BaseRenderer): | ||
|
||
""" | ||
Custom renderer for text/plain format. | ||
|
||
charset is 'utf-8' by default. | ||
""" | ||
|
||
media_type = 'text/plain' | ||
format = 'txt' | ||
|
||
def render(self, data, accepted_media_type=None, renderer_context=None): | ||
renderer_context = renderer_context or {} | ||
response = renderer_context.get('response') | ||
if not response or response.exception: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is no response probably is something wrong, anyway, this is just an extra protection since |
||
return data.get('detail', '').encode(self.charset) | ||
data = render_to_string( | ||
'restapi/log.txt', {'build': data} | ||
) | ||
return data.encode(self.charset) | ||
|
||
|
||
class UserSelectViewSet(viewsets.ModelViewSet): | ||
|
||
""" | ||
|
@@ -213,7 +236,7 @@ class VersionViewSet(UserSelectViewSet): | |
|
||
class BuildViewSetBase(UserSelectViewSet): | ||
permission_classes = [APIRestrictedPermission] | ||
renderer_classes = (JSONRenderer,) | ||
renderer_classes = (JSONRenderer, PlainTextBuildRenderer) | ||
serializer_class = BuildSerializer | ||
admin_serializer_class = BuildAdminSerializer | ||
model = Build | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
from rest_framework import status | ||
from rest_framework.test import APIClient | ||
|
||
from readthedocs.builds.models import Build, Version | ||
from readthedocs.builds.models import Build, BuildCommandResult, Version | ||
from readthedocs.integrations.models import Integration | ||
from readthedocs.oauth.models import RemoteOrganization, RemoteRepository | ||
from readthedocs.projects.models import Feature, Project | ||
|
@@ -277,6 +277,140 @@ def test_make_build_commands(self): | |
self.assertEqual(build['commands'][0]['run_time'], 5) | ||
self.assertEqual(build['commands'][0]['description'], 'foo') | ||
|
||
def test_get_raw_log_success(self): | ||
build = get(Build, project_id=1, version_id=1, builder='foo') | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='python setup.py install', | ||
output='Installing dependencies...' | ||
) | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='git checkout master', | ||
output='Switched to branch "master"' | ||
) | ||
client = APIClient() | ||
|
||
api_user = get(User, user='test', password='test') | ||
client.force_authenticate(user=api_user) | ||
resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) | ||
self.assertEqual(resp.status_code, 200) | ||
|
||
self.assertIn('Read the Docs build information', resp.content.decode()) | ||
self.assertIn('Build id: {}'.format(build.id), resp.content.decode()) | ||
self.assertIn('Project: {}'.format(build.project.slug), resp.content.decode()) | ||
self.assertIn('Version: {}'.format(build.version.slug), resp.content.decode()) | ||
self.assertIn('Commit: {}'.format(build.commit), resp.content.decode()) | ||
self.assertIn('Date: ', resp.content.decode()) | ||
self.assertIn('State: finished', resp.content.decode()) | ||
self.assertIn('Success: True', resp.content.decode()) | ||
self.assertIn('[rtd-command-info]', resp.content.decode()) | ||
self.assertIn( | ||
'python setup.py install\nInstalling dependencies...', | ||
resp.content.decode() | ||
) | ||
self.assertIn( | ||
'git checkout master\nSwitched to branch "master"', | ||
resp.content.decode() | ||
) | ||
|
||
def test_get_raw_log_building(self): | ||
build = get( | ||
Build, project_id=1, version_id=1, | ||
builder='foo', success=False, | ||
exit_code=1, state='building', | ||
) | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='python setup.py install', | ||
output='Installing dependencies...', | ||
exit_code=1, | ||
) | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='git checkout master', | ||
output='Switched to branch "master"' | ||
) | ||
client = APIClient() | ||
|
||
api_user = get(User, user='test', password='test') | ||
client.force_authenticate(user=api_user) | ||
resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) | ||
self.assertEqual(resp.status_code, 200) | ||
|
||
self.assertIn('Read the Docs build information', resp.content.decode()) | ||
self.assertIn('Build id: {}'.format(build.id), resp.content.decode()) | ||
self.assertIn('Project: {}'.format(build.project.slug), resp.content.decode()) | ||
self.assertIn('Version: {}'.format(build.version.slug), resp.content.decode()) | ||
self.assertIn('Commit: {}'.format(build.commit), resp.content.decode()) | ||
self.assertIn('Date: ', resp.content.decode()) | ||
self.assertIn('State: building', resp.content.decode()) | ||
self.assertIn('Success: Unknow', resp.content.decode()) | ||
self.assertIn('[rtd-command-info]', resp.content.decode()) | ||
self.assertIn( | ||
'python setup.py install\nInstalling dependencies...', | ||
resp.content.decode() | ||
) | ||
self.assertIn( | ||
'git checkout master\nSwitched to branch "master"', | ||
resp.content.decode() | ||
) | ||
|
||
def test_get_raw_log_failure(self): | ||
build = get( | ||
Build, project_id=1, version_id=1, | ||
builder='foo', success=False, exit_code=1 | ||
) | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='python setup.py install', | ||
output='Installing dependencies...', | ||
exit_code=1, | ||
) | ||
get( | ||
BuildCommandResult, | ||
build=build, | ||
command='git checkout master', | ||
output='Switched to branch "master"' | ||
) | ||
client = APIClient() | ||
|
||
api_user = get(User, user='test', password='test') | ||
client.force_authenticate(user=api_user) | ||
resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) | ||
self.assertEqual(resp.status_code, 200) | ||
|
||
self.assertIn('Read the Docs build information', resp.content.decode()) | ||
self.assertIn('Build id: {}'.format(build.id), resp.content.decode()) | ||
self.assertIn('Project: {}'.format(build.project.slug), resp.content.decode()) | ||
self.assertIn('Version: {}'.format(build.version.slug), resp.content.decode()) | ||
self.assertIn('Commit: {}'.format(build.commit), resp.content.decode()) | ||
self.assertIn('Date: ', resp.content.decode()) | ||
self.assertIn('State: finished', resp.content.decode()) | ||
self.assertIn('Success: False', resp.content.decode()) | ||
self.assertIn('[rtd-command-info]', resp.content.decode()) | ||
self.assertIn( | ||
'python setup.py install\nInstalling dependencies...', | ||
resp.content.decode() | ||
) | ||
self.assertIn( | ||
'git checkout master\nSwitched to branch "master"', | ||
resp.content.decode() | ||
) | ||
|
||
def test_get_invalid_raw_log(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One extra test to see if the response.exception works :) |
||
client = APIClient() | ||
|
||
api_user = get(User, user='test', password='test') | ||
client.force_authenticate(user=api_user) | ||
resp = client.get('/api/v2/build/{0}.txt'.format(404)) | ||
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) | ||
|
||
|
||
class APITests(TestCase): | ||
fixtures = ['eric.json', 'test_data.json'] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed to add this new fields here since we can't access this inside the template as an object but as the result of the serializer.