Skip to content

Commit 40d38ee

Browse files
committed
merge tobami/master into branch
2 parents 34a6055 + 263860b commit 40d38ee

15 files changed

Lines changed: 493 additions & 62 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,18 @@ COMP_EXECUTABLES = [
224224
('myexe', 'L'),
225225
]
226226
```
227+
* `COMPARISON_COMMIT_TAGS: Defines a list of tags to display on the comparison page. This comes
228+
handy when there are a lot of tags. It defaults to ``None`` which means display all the available
229+
tags.
230+
231+
### VCS Provider Specific Settings
232+
233+
#### Github
234+
235+
* ``GITHUB_OAUTH_TOKEN`` - Github oAuth token to use for authenticating against
236+
the Github API. If not provided, it will default to unauthenticated API requests
237+
which have low rate limits so an exception may be thrown when retrieving info
238+
from the Github API due to the rate limit being reached.
227239

228240
## Getting help
229241

codespeed/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ProjectAdmin(admin.ModelAdmin):
3737

3838
@admin.register(Branch)
3939
class BranchAdmin(admin.ModelAdmin):
40-
list_display = ('name', 'project')
40+
list_display = ('name', 'project', 'display_on_comparison_page')
4141
list_filter = ('project',)
4242

4343

codespeed/commits/git.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
import datetime
22
import logging
33
import os
4-
from string import strip
5-
from subprocess import Popen, PIPE
64

5+
from subprocess import Popen, PIPE
76
from django.conf import settings
8-
97
from .exceptions import CommitLogError
108

119
logger = logging.getLogger(__name__)
1210

1311

12+
def execute_command(cmd, cwd):
13+
p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=cwd)
14+
stdout, stderr = p.communicate()
15+
stdout = stdout.decode('utf8') if stdout is not None else stdout
16+
stderr = stderr.decode('utf8') if stderr is not None else stderr
17+
return (p, stdout, stderr)
18+
19+
1420
def updaterepo(project, update=True):
1521
if os.path.exists(project.working_copy):
1622
if not update:
1723
return
1824

19-
p = Popen(['git', 'pull'], stdout=PIPE, stderr=PIPE,
20-
cwd=project.working_copy)
25+
p, _, stderr = execute_command(['git', 'pull'], cwd=project.working_copy)
2126

22-
stdout, stderr = p.communicate()
2327
if p.returncode != 0:
2428
raise CommitLogError("git pull returned %s: %s" % (p.returncode,
2529
stderr))
2630
else:
2731
return [{'error': False}]
2832
else:
2933
cmd = ['git', 'clone', project.repo_path, project.repo_name]
30-
p = Popen(cmd, stdout=PIPE, stderr=PIPE,
31-
cwd=settings.REPOSITORY_BASE_PATH)
34+
p, stdout, stderr = execute_command(cmd, settings.REPOSITORY_BASE_PATH)
3235
logger.debug('Cloning Git repo {0} for project {1}'.format(
3336
project.repo_path, project))
34-
stdout, stderr = p.communicate()
3537

3638
if p.returncode != 0:
3739
raise CommitLogError("%s returned %s: %s" % (
@@ -59,32 +61,24 @@ def getlogs(endrev, startrev):
5961
cmd.append(endrev.commitid)
6062

6163
working_copy = endrev.branch.project.working_copy
62-
p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=working_copy)
63-
64-
stdout, stderr = p.communicate()
64+
p, stdout, stderr = execute_command(cmd, working_copy)
6565

6666
if p.returncode != 0:
6767
raise CommitLogError("%s returned %s: %s" % (
6868
" ".join(cmd), p.returncode, stderr))
6969
logs = []
70-
for log in filter(None, stdout.split(b'\x1e')):
70+
for log in filter(None, stdout.split('\x1e')):
7171
(short_commit_id, commit_id, date_t, author_name, author_email,
72-
subject, body) = map(strip, log.split(b'\x00', 7))
73-
74-
tag = ""
72+
subject, body) = map(lambda s: s.strip(), log.split('\x00', 7))
7573

7674
cmd = ["git", "tag", "--points-at", commit_id]
77-
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=working_copy)
7875

7976
try:
80-
stdout, stderr = proc.communicate()
81-
except ValueError:
82-
stdout = b''
83-
stderr = b''
84-
85-
if proc.returncode == 0:
86-
tag = stdout.strip()
77+
p, stdout, stderr = execute_command(cmd, working_copy)
78+
except Exception:
79+
logger.debug('Failed to get tag', exc_info=True)
8780

81+
tag = stdout.strip() if p.returncode == 0 else ""
8882
date = datetime.datetime.fromtimestamp(
8983
int(date_t)).strftime("%Y-%m-%d %H:%M:%S")
9084

codespeed/commits/github.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@
1111
try:
1212
# Python 3
1313
from urllib.request import urlopen
14+
from urllib.request import Request
1415
except ImportError:
1516
# Python 2
16-
from urllib import urlopen
17+
from urllib2 import urlopen
18+
from urllib2 import Request
1719
import re
1820
import json
1921

2022
import isodate
2123
from django.core.cache import cache
24+
from django.conf import settings
2225

2326
from .exceptions import CommitLogError
2427

@@ -42,8 +45,17 @@ def fetch_json(url):
4245
json_obj = cache.get(url)
4346

4447
if json_obj is None:
48+
github_oauth_token = getattr(settings, 'GITHUB_OAUTH_TOKEN', None)
49+
50+
if github_oauth_token:
51+
headers = {'Authorization': 'token %s' % (github_oauth_token)}
52+
else:
53+
headers = {}
54+
55+
request = Request(url=url, headers=headers)
56+
4557
try:
46-
json_obj = json.load(urlopen(url))
58+
json_obj = json.load(urlopen(request))
4759
except IOError as e:
4860
logger.exception("Unable to load %s: %s",
4961
url, e, exc_info=True)
@@ -91,11 +103,13 @@ def retrieve_revision(commit_id, username, project, revision=None):
91103
if revision:
92104
# Overwrite any existing data we might have for this revision since
93105
# we never want our records to be out of sync with the actual VCS:
94-
95-
# We need to convert the timezone-aware date to a naive (i.e.
96-
# timezone-less) date in UTC to avoid killing MySQL:
97-
revision.date = date.astimezone(
98-
isodate.tzinfo.Utc()).replace(tzinfo=None)
106+
if not getattr(settings, 'USE_TZ_AWARE_DATES', False):
107+
# We need to convert the timezone-aware date to a naive (i.e.
108+
# timezone-less) date in UTC to avoid killing MySQL:
109+
logger.debug('USE_TZ_AWARE_DATES setting is set to False, '
110+
'converting datetime object to a naive one')
111+
revision.date = date.astimezone(
112+
isodate.tzinfo.Utc()).replace(tzinfo=None)
99113
revision.author = commit_json['author']['name']
100114
revision.message = commit_json['message']
101115
revision.full_clean()

codespeed/commits/tests/__init__.py

Whitespace-only changes.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from datetime import datetime
2+
from django.test import TestCase, override_settings
3+
from mock import Mock, patch
4+
5+
from codespeed.commits.git import getlogs
6+
from codespeed.models import Project, Revision, Branch, Environment
7+
8+
9+
@override_settings(ALLOW_ANONYMOUS_POST=True)
10+
class GitTest(TestCase):
11+
def setUp(self):
12+
self.env = Environment.objects.create(name='env')
13+
self.project = Project.objects.create(name='project',
14+
repo_path='path',
15+
repo_type=Project.GIT)
16+
self.branch = Branch.objects.create(name='default',
17+
project_id=self.project.id)
18+
self.revision = Revision.objects.create(
19+
**{
20+
'commitid': 'id1',
21+
'date': datetime.now(),
22+
'project_id': self.project.id,
23+
'branch_id': self.branch.id,
24+
}
25+
)
26+
27+
@patch("codespeed.commits.git.Popen")
28+
def test_git_output_parsing(self, popen):
29+
# given
30+
outputs = {
31+
"log": b"id\x00long_id\x001583489681\x00author\x00email\x00msg\x00\x1e",
32+
"tag": b'tag',
33+
}
34+
35+
def side_effect(cmd, *args, **kwargs):
36+
ret = Mock()
37+
ret.returncode = 0
38+
git_command = cmd[1] if len(cmd) > 0 else None
39+
output = outputs.get(git_command, b'')
40+
ret.communicate.return_value = (output, b'')
41+
return ret
42+
43+
popen.side_effect = side_effect
44+
45+
# when
46+
# revision doesn't matter here, git commands are mocked
47+
logs = getlogs(self.revision, self.revision)
48+
49+
# then
50+
expected = {
51+
'date': '2020-03-06 04:14:41',
52+
'message': 'msg',
53+
'commitid': 'long_id',
54+
'author': 'author',
55+
'author_email': 'email',
56+
'body': '',
57+
'short_commit_id': 'id',
58+
'tag': 'tag',
59+
}
60+
self.assertEquals([expected], logs)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.1.15 on 2020-02-24 11:26
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('codespeed', '0003_project_default_branch'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='branch',
15+
name='display_on_comparison_page',
16+
field=models.BooleanField(default=True, verbose_name='True to display this branch on the comparison page'),
17+
),
18+
]

codespeed/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ class Branch(models.Model):
107107
name = models.CharField(max_length=32)
108108
project = models.ForeignKey(
109109
Project, on_delete=models.CASCADE, related_name="branches")
110+
display_on_comparison_page = models.BooleanField(
111+
"True to display this branch on the comparison page",
112+
default=True)
110113

111114
def __str__(self):
112115
return self.project.name + ":" + self.name

codespeed/settings.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,29 @@
7878
# ('myexe', '21df2423ra'),
7979
# ('myexe', 'L'),]
8080

81+
COMPARISON_COMMIT_TAGS = None # List of tag names which should be included in the executables list
82+
# on the comparision page.
83+
# This comes handy where project contains a lot of tags, but you only want
84+
# to list subset of them on the comparison page.
85+
# If this value is set to None (default value), all the available tags will
86+
# be included.
87+
88+
TIMELINE_EXECUTABLE_NAME_MAX_LEN = 22 # Maximum length of the executable name used in the
89+
# Changes and Timeline view. If the name is longer, the name
90+
# will be truncated and "..." will be added at the end.
91+
92+
COMPARISON_EXECUTABLE_NAME_MAX_LEN = 20 # Maximum length of the executable name used in the
93+
# Coomparison view. If the name is longer, the name
94+
8195
USE_MEDIAN_BANDS = True # True to enable median bands on Timeline view
8296

8397

8498
ALLOW_ANONYMOUS_POST = True # Whether anonymous users can post results
8599
REQUIRE_SECURE_AUTH = True # Whether auth needs to be over a secure channel
100+
101+
US_TZ_AWARE_DATES = False # True to use timezone aware datetime objects with Github provider.
102+
# NOTE: Some database backends may not support tz aware dates.
103+
104+
GITHUB_OAUTH_TOKEN = None # Github oAuth token to use when using Github repo type. If not
105+
# specified, it will utilize unauthenticated requests which have
106+
# low rate limits.

codespeed/templates/codespeed/timeline.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
{% block extra_head %}
88
{{ block.super }}
9-
<link rel="stylesheet" type="text/css" href="{% static '/css/timeline.css' %}" />
9+
<link rel="stylesheet" type="text/css" href="{% static 'css/timeline.css' %}" />
1010
<link rel="stylesheet" type="text/css" href="{% static 'js/jqplot/jquery.jqplot.min.css' %}" />
1111
{% endblock %}
1212

0 commit comments

Comments
 (0)