squad 1.88__py3-none-any.whl → 1.90__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
squad/api/rest.py CHANGED
@@ -1190,7 +1190,13 @@ class BuildViewSet(NestedViewSetMixin, ModelViewSet):
1190
1190
  raise ValidationError('url is required.')
1191
1191
  return Response({'message': 'OK'}, status=status.HTTP_202_ACCEPTED)
1192
1192
  except (ValidationError, IntegrityError) as e:
1193
- return Response({'message': ', '.join(e.messages)}, status=status.HTTP_400_BAD_REQUEST)
1193
+ message = ""
1194
+ if hasattr(e, "messages"):
1195
+ message = ', '.join(e.messages)
1196
+ else:
1197
+ message = str(e)
1198
+
1199
+ return Response({'message': message}, status=status.HTTP_400_BAD_REQUEST)
1194
1200
 
1195
1201
  @action(detail=True, methods=['get'], suffix='compare')
1196
1202
  def compare(self, request, pk=None):
squad/api/views.py CHANGED
@@ -105,7 +105,7 @@ def add_test_run(request, group_slug, project_slug, version, environment_slug):
105
105
  if 'attachment' in request.FILES:
106
106
  attachments = {}
107
107
  for f in request.FILES.getlist('attachment'):
108
- attachments[f.name] = read_file_upload(f)
108
+ attachments[f.name] = f
109
109
  test_run_data['attachments'] = attachments
110
110
 
111
111
  receive = ReceiveTestRun(project)
squad/ci/backend/lava.py CHANGED
@@ -396,9 +396,10 @@ class Backend(BaseBackend):
396
396
 
397
397
  def __lava_job_name(self, definition):
398
398
  yaml_definition = yaml.safe_load(definition)
399
- if 'job_name' in yaml_definition.keys():
399
+ if type(yaml_definition) is dict and 'job_name' in yaml_definition.keys():
400
+ job_name = yaml_definition['job_name']
400
401
  # only return first 255 characters
401
- return yaml_definition['job_name'][:255]
402
+ return job_name[:255] if job_name else ''
402
403
  return None
403
404
 
404
405
  def __resubmit__(self, job_id):
squad/ci/tasks.py CHANGED
@@ -27,8 +27,12 @@ def poll(backend_id=None):
27
27
  @celery.task
28
28
  def fetch(job_id):
29
29
  logger.info("fetching %s" % job_id)
30
- backend = TestJob.objects.get(pk=job_id).backend
31
- backend.fetch(job_id)
30
+ try:
31
+ testjob = TestJob.objects.get(pk=job_id)
32
+ if testjob.job_id:
33
+ testjob.backend.fetch(testjob.id)
34
+ except TestJob.DoesNotExist:
35
+ return
32
36
 
33
37
 
34
38
  @celery.task
squad/core/callback.py CHANGED
@@ -56,12 +56,18 @@ def dispatch_callback(callback_object):
56
56
  else:
57
57
  args['data'] = json.loads(callback_object.payload)
58
58
 
59
- request = getattr(requests, callback_object.method)
60
- response = request(callback_object.url, **args)
59
+ try:
60
+ request = getattr(requests, callback_object.method)
61
+ response = request(callback_object.url, **args)
62
+ response_code = response.status_code
63
+ response_content = response.content
64
+ except requests.exceptions.RequestException as e:
65
+ response_code = None
66
+ response_content = str(e)
61
67
 
62
68
  if callback_object.record_response:
63
- callback_object.response_code = response.status_code
64
- callback_object.response_content = response.content
69
+ callback_object.response_code = response_code
70
+ callback_object.response_content = response_content
65
71
 
66
72
  callback_object.is_sent = True
67
73
  callback_object.save()
squad/core/history.py CHANGED
@@ -3,7 +3,7 @@ from django.core.paginator import Paginator
3
3
 
4
4
  from squad.core.queries import test_confidence
5
5
  from squad.core.utils import parse_name
6
- from squad.core.models import SuiteMetadata, KnownIssue, Environment
6
+ from squad.core.models import SuiteMetadata, KnownIssue, Environment, Build
7
7
 
8
8
 
9
9
  class TestResult(object):
@@ -49,6 +49,9 @@ class TestHistory(object):
49
49
  self.number = page
50
50
  builds = self.paginator.page(page)
51
51
 
52
+ if len(builds) == 0:
53
+ raise Build.DoesNotExist
54
+
52
55
  self.top = builds[0]
53
56
 
54
57
  issues_by_env = {}
@@ -2,6 +2,7 @@ from glob import glob
2
2
  import os
3
3
  import re
4
4
  from django.core.management.base import BaseCommand
5
+ from django.core.files import File
5
6
 
6
7
 
7
8
  from squad.core.models import Build
@@ -123,7 +124,7 @@ class Command(BaseCommand):
123
124
  for f in glob(os.path.join(directory, '*')):
124
125
  name = os.path.basename(f)
125
126
  if name not in ['metrics.json', 'metadata.json', 'tests.json']:
126
- attachments[name] = open(f, 'rb').read()
127
+ attachments[name] = File(open(f, 'rb'))
127
128
 
128
129
  if not self.options['silent']:
129
130
  print("Importing test run: %s" % directory)
squad/core/models.py CHANGED
@@ -16,7 +16,7 @@ from django.db.models.query import prefetch_related_objects
16
16
  from django.contrib.auth.models import User, AnonymousUser, Group as auth_group
17
17
  from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
18
18
  from django.contrib.contenttypes.models import ContentType
19
- from django.db.models.signals import post_save, pre_delete
19
+ from django.db.models.signals import post_save, pre_delete, pre_save
20
20
  from django.dispatch import receiver
21
21
 
22
22
  from django.conf import settings
@@ -909,8 +909,8 @@ class Attachment(models.Model):
909
909
  self.__data__ = b''
910
910
  return self.__data__
911
911
 
912
- def save_file(self, filename, contents):
913
- storage_save(self, self.storage, filename, contents)
912
+ def save_file(self, filename, file):
913
+ storage_save(self, self.storage, filename, file)
914
914
 
915
915
 
916
916
  class SuiteMetadata(models.Model):
@@ -1428,9 +1428,13 @@ class ProjectStatus(models.Model, TestSummaryBase):
1428
1428
  return None
1429
1429
 
1430
1430
  def __get_yaml_field__(self, field_value):
1431
- if field_value is not None:
1431
+ if field_value is None:
1432
+ return {}
1433
+
1434
+ try:
1432
1435
  return yaml.load(field_value, Loader=yaml.Loader)
1433
- return {}
1436
+ except yaml.YAMLError:
1437
+ return {}
1434
1438
 
1435
1439
  def get_regressions(self):
1436
1440
  return self.__get_yaml_field__(self.regressions)
@@ -1467,6 +1471,14 @@ class ProjectStatus(models.Model, TestSummaryBase):
1467
1471
  return thresholds_exceeded
1468
1472
 
1469
1473
 
1474
+ @receiver(pre_save, sender=ProjectStatus)
1475
+ def validate_project_status(sender, instance, *args, **kwargs):
1476
+ yaml_validator(instance.regressions)
1477
+ yaml_validator(instance.fixes)
1478
+ yaml_validator(instance.metric_regressions)
1479
+ yaml_validator(instance.metric_fixes)
1480
+
1481
+
1470
1482
  class NotificationDelivery(models.Model):
1471
1483
 
1472
1484
  status = models.ForeignKey('ProjectStatus', related_name='deliveries', on_delete=models.CASCADE)
@@ -172,9 +172,9 @@ class ReceiveTestRun(object):
172
172
  if log_file is not None:
173
173
  testrun.save_log_file(log_file)
174
174
 
175
- for filename, data in attachments.items():
176
- attachment = testrun.attachments.create(filename=filename, length=len(data))
177
- attachment.save_file(filename, data)
175
+ for filename, file in attachments.items():
176
+ attachment = testrun.attachments.create(filename=filename, length=file.size)
177
+ attachment.save_file(filename, file)
178
178
 
179
179
  testrun.refresh_from_db()
180
180
 
squad/core/utils.py CHANGED
@@ -67,7 +67,7 @@ def yaml_validator(value):
67
67
  if len(value) == 0:
68
68
  return
69
69
  try:
70
- if not isinstance(yaml.safe_load(value), dict):
70
+ if not isinstance(yaml.load(value, Loader=yaml.Loader), dict):
71
71
  raise ValidationError("Dictionary object expected")
72
72
  except yaml.YAMLError as e:
73
73
  raise ValidationError(e)
@@ -169,8 +169,11 @@ def log_deletion(request, object, message):
169
169
 
170
170
 
171
171
  def storage_save(obj, storage_field, filename, content):
172
- content_bytes = content or ''
173
- if type(content_bytes) is str:
174
- content_bytes = content_bytes.encode()
175
172
  filename = '%s/%s/%s' % (obj.__class__.__name__.lower(), obj.pk, filename)
176
- storage_field.save(filename, ContentFile(content_bytes))
173
+ if type(content) in [bytes, str]:
174
+ content_bytes = content or ''
175
+ if type(content_bytes) is str:
176
+ content_bytes = content_bytes.encode()
177
+ storage_field.save(filename, ContentFile(content_bytes))
178
+ else:
179
+ storage_field.save(filename, content)
@@ -3,6 +3,7 @@ from functools import reduce
3
3
  from django.shortcuts import render, get_object_or_404
4
4
  from django.core.paginator import Paginator
5
5
  from django.db.models import Q, Prefetch
6
+ from django.http import HttpResponseNotFound
6
7
 
7
8
  from squad.core.models import Project, Group, Build
8
9
  from squad.core.comparison import TestComparison, MetricComparison
@@ -107,7 +108,11 @@ def compare_builds(request):
107
108
  comparison = None
108
109
  project = None
109
110
  if project_slug:
110
- group_slug, project_slug = project_slug.split('/')
111
+ group_and_project = project_slug.split('/')
112
+ if len(group_and_project) != 2:
113
+ return HttpResponseNotFound()
114
+
115
+ group_slug, project_slug = group_and_project
111
116
  project = get_object_or_404(Project, group__slug=group_slug, slug=project_slug)
112
117
 
113
118
  baseline_build = request.GET.get('baseline')
squad/frontend/tests.py CHANGED
@@ -6,7 +6,7 @@ from django.shortcuts import render, get_object_or_404
6
6
  from django.http import Http404
7
7
 
8
8
  from squad.http import auth
9
- from squad.core.models import Test, Suite, SuiteMetadata, Environment
9
+ from squad.core.models import Build, Test, Suite, SuiteMetadata, Environment
10
10
  from squad.core.history import TestHistory
11
11
  from squad.core.queries import test_confidence
12
12
  from squad.frontend.views import get_build
@@ -234,5 +234,5 @@ def test_history(request, group_slug, project_slug, build_version=None, testrun_
234
234
  history = TestHistory(project, full_test_name, top=top, page=page)
235
235
  context.update({"history": history})
236
236
  return render(request, 'squad/test_history.jinja2', context)
237
- except (Suite.DoesNotExist, SuiteMetadata.DoesNotExist) as e:
237
+ except (Suite.DoesNotExist, SuiteMetadata.DoesNotExist, Build.DoesNotExist) as e:
238
238
  raise Http404(f"Test not found: {e}")
@@ -0,0 +1 @@
1
+ from squad.plugins import Plugin # noqa
@@ -0,0 +1,152 @@
1
+ import hashlib
2
+ import re
3
+ from collections import defaultdict
4
+
5
+ from django.template.defaultfilters import slugify
6
+
7
+ REGEX_NAME = 0
8
+ REGEX_BODY = 1
9
+ REGEX_EXTRACT_NAME = 2
10
+
11
+
12
+ class BaseLogParser:
13
+ def compile_regexes(self, regexes):
14
+ combined = [r"(%s)" % r[REGEX_BODY] for r in regexes]
15
+ return re.compile(r"|".join(combined), re.S | re.M)
16
+
17
+ def remove_numbers_and_time(self, snippet):
18
+ without_numbers = re.sub(r"(0x[a-f0-9]+|[<\[][0-9a-f]+?[>\]]|\d+)", "", snippet)
19
+ without_time = re.sub(r"^\[[^\]]+\]", "", without_numbers)
20
+
21
+ return without_time
22
+
23
+ def create_name(self, snippet, compiled_regex=None):
24
+ matches = None
25
+ if compiled_regex:
26
+ matches = compiled_regex.findall(snippet)
27
+ if not matches:
28
+ # Only extract a name if we provide a regex to extract the name and
29
+ # there is a match
30
+ return None
31
+ snippet = matches[0]
32
+ without_numbers_and_time = self.remove_numbers_and_time(snippet)
33
+
34
+ # Limit the name length to 191 characters, since the max name length
35
+ # for SuiteMetadata in SQUAD is 256 characters. The SHA and "-" take 65
36
+ # characters: 256-65=191
37
+ return slugify(without_numbers_and_time)[:191]
38
+
39
+ def create_shasum(self, snippet):
40
+ sha = hashlib.sha256()
41
+ without_numbers_and_time = self.remove_numbers_and_time(snippet)
42
+ sha.update(without_numbers_and_time.encode())
43
+ return sha.hexdigest()
44
+
45
+ def create_name_log_dict(self, test_name, lines, test_regex=None):
46
+ """
47
+ Produce a dictionary with the test names as keys and the extracted logs
48
+ for that test name as values. There will be at least one test name per
49
+ regex. If there were any matches for a given regex, then a new test
50
+ will be generated using test_name + shasum.
51
+ """
52
+ # Run the REGEX_EXTRACT_NAME regex over the log lines to sort them by
53
+ # extracted name. If no name is extracted or the log parser did not
54
+ # have any output for a particular regex, just use the default name
55
+ # (for example "check-kernel-oops").
56
+ tests_without_shas_to_create = defaultdict(set)
57
+ tests_with_shas_to_create = defaultdict(set)
58
+
59
+ # If there are no lines, use the default name and create a passing
60
+ # test. For example "check-kernel-oops"
61
+ if not lines:
62
+ tests_without_shas_to_create[test_name] = []
63
+
64
+ # If there are lines, then create the tests for these.
65
+ for line in lines:
66
+ extracted_name = self.create_name(line, test_regex)
67
+ if extracted_name:
68
+ extended_test_name = f"{test_name}-{extracted_name}"
69
+ else:
70
+ extended_test_name = test_name
71
+ tests_without_shas_to_create[extended_test_name].add(line)
72
+
73
+ for name, test_lines in tests_without_shas_to_create.items():
74
+ # Some lines of the matched regex might be the same, and we don't want to create
75
+ # multiple tests like test1-sha1, test1-sha1, etc, so we'll create a set of sha1sums
76
+ # then create only new tests for unique sha's
77
+
78
+ for line in test_lines:
79
+ sha = self.create_shasum(line)
80
+ name_with_sha = f"{name}-{sha}"
81
+ tests_with_shas_to_create[name_with_sha].add(line)
82
+
83
+ return tests_without_shas_to_create, tests_with_shas_to_create
84
+
85
+ def create_squad_tests_from_name_log_dict(
86
+ self, suite, testrun, tests_without_shas_to_create, tests_with_shas_to_create
87
+ ):
88
+ # Import SuiteMetadata from SQUAD only when required so BaseLogParser
89
+ # does not require a SQUAD to work. This makes it easier to reuse this
90
+ # class outside of SQUAD for testing and developing log parser
91
+ # patterns.
92
+ from squad.core.models import SuiteMetadata
93
+
94
+ for name, lines in tests_without_shas_to_create.items():
95
+ metadata, _ = SuiteMetadata.objects.get_or_create(
96
+ suite=suite.slug, name=name, kind="test"
97
+ )
98
+ testrun.tests.create(
99
+ suite=suite,
100
+ result=(len(lines) == 0),
101
+ log="\n".join(lines),
102
+ metadata=metadata,
103
+ build=testrun.build,
104
+ environment=testrun.environment,
105
+ )
106
+ for name_with_sha, lines in tests_with_shas_to_create.items():
107
+ metadata, _ = SuiteMetadata.objects.get_or_create(
108
+ suite=suite.slug, name=name_with_sha, kind="test"
109
+ )
110
+ testrun.tests.create(
111
+ suite=suite,
112
+ result=False,
113
+ log="\n---\n".join(lines),
114
+ metadata=metadata,
115
+ build=testrun.build,
116
+ environment=testrun.environment,
117
+ )
118
+
119
+ def create_squad_tests(self, testrun, suite, test_name, lines, test_regex=None):
120
+ """
121
+ There will be at least one test per regex. If there were any match for
122
+ a given regex, then a new test will be generated using test_name +
123
+ shasum. This helps comparing kernel logs across different builds
124
+ """
125
+ tests_without_shas_to_create, tests_with_shas_to_create = (
126
+ self.create_name_log_dict(test_name, lines, test_regex)
127
+ )
128
+ self.create_squad_tests_from_name_log_dict(
129
+ suite,
130
+ testrun,
131
+ tests_without_shas_to_create,
132
+ tests_with_shas_to_create,
133
+ )
134
+
135
+ def join_matches(self, matches, regexes):
136
+ """
137
+ group regex in python are returned as a list of tuples which each
138
+ group match in one of the positions in the tuple. Example:
139
+ regex = r'(a)|(b)|(c)'
140
+ matches = [
141
+ ('match a', '', ''),
142
+ ('', 'match b', ''),
143
+ ('match a', '', ''),
144
+ ('', '', 'match c')
145
+ ]
146
+ """
147
+ snippets = {regex_id: [] for regex_id in range(len(regexes))}
148
+ for match in matches:
149
+ for regex_id in range(len(regexes)):
150
+ if len(match[regex_id]) > 0:
151
+ snippets[regex_id].append(match[regex_id])
152
+ return snippets
@@ -1,40 +1,30 @@
1
- import hashlib
2
1
  import logging
3
2
  import re
4
- from collections import defaultdict
5
3
  from squad.plugins import Plugin as BasePlugin
6
- from squad.core.models import SuiteMetadata
7
-
4
+ from squad.plugins.lib.base_log_parser import BaseLogParser, REGEX_NAME, REGEX_EXTRACT_NAME
8
5
 
9
6
  logger = logging.getLogger()
10
7
 
11
- REGEX_NAME = 0
12
- REGEX_BODY = 1
13
-
14
8
  MULTILINERS = [
15
- ('check-kernel-exception', r'-+\[? cut here \]?-+.*?-+\[? end trace \w* \]?-+'),
16
- ('check-kernel-kasan', r'=+\n\[[\s\.\d]+\]\s+BUG: KASAN:.*?=+'),
17
- ('check-kernel-kfence', r'=+\n\[[\s\.\d]+\]\s+BUG: KFENCE:.*?=+'),
9
+ ('check-kernel-exception', r'-+\[? cut here \]?-+.*?-+\[? end trace \w* \]?-+', r"\d][^\+\n]*"),
10
+ ('check-kernel-kasan', r'=+\n\[[\s\.\d]+\]\s+BUG: KASAN:.*?=+', r"BUG: KASAN:[^\+\n]*"),
11
+ ('check-kernel-kfence', r'=+\n\[[\s\.\d]+\]\s+BUG: KFENCE:.*?=+', r"BUG: KFENCE:[^\+\n]*"),
18
12
  ]
19
13
 
20
14
  ONELINERS = [
21
- ('check-kernel-oops', r'^[^\n]+Oops(?: -|:).*?$'),
22
- ('check-kernel-fault', r'^[^\n]+Unhandled fault.*?$'),
23
- ('check-kernel-warning', r'^[^\n]+WARNING:.*?$'),
24
- ('check-kernel-bug', r'^[^\n]+(?: kernel BUG at|BUG:).*?$'),
25
- ('check-kernel-invalid-opcode', r'^[^\n]+invalid opcode:.*?$'),
26
- ('check-kernel-panic', r'Kernel panic - not syncing.*?$'),
15
+ ('check-kernel-oops', r'^[^\n]+Oops(?: -|:).*?$', r"Oops[^\+\n]*"),
16
+ ('check-kernel-fault', r'^[^\n]+Unhandled fault.*?$', r"Unhandled [^\+\n]*"),
17
+ ('check-kernel-warning', r'^[^\n]+WARNING:.*?$', r"WARNING: [^\+\n]*"),
18
+ ('check-kernel-bug', r'^[^\n]+(?: kernel BUG at|BUG:).*?$', r"BUG[^\+\n]*"),
19
+ ('check-kernel-invalid-opcode', r'^[^\n]+invalid opcode:.*?$', r"invalid opcode: [^\+\n]*"),
20
+ ('check-kernel-panic', r'Kernel panic - not syncing.*?$', r"Kernel [^\+\n]*"),
27
21
  ]
28
22
 
29
23
  # Tip: broader regexes should come first
30
24
  REGEXES = MULTILINERS + ONELINERS
31
25
 
32
26
 
33
- class Plugin(BasePlugin):
34
- def __compile_regexes(self, regexes):
35
- combined = [r'(%s)' % r[REGEX_BODY] for r in regexes]
36
- return re.compile(r'|'.join(combined), re.S | re.M)
37
-
27
+ class Plugin(BasePlugin, BaseLogParser):
38
28
  def __cutoff_boot_log(self, log):
39
29
  # Attempt to split the log in " login:"
40
30
  logs = log.split(' login:', 1)
@@ -51,68 +41,6 @@ class Plugin(BasePlugin):
51
41
  kernel_msgs = re.findall(r'(\[[ \d]+\.[ \d]+\] .*?)$', log, re.S | re.M)
52
42
  return '\n'.join(kernel_msgs)
53
43
 
54
- def __join_matches(self, matches, regexes):
55
- """
56
- group regex in python are returned as a list of tuples which each
57
- group match in one of the positions in the tuple. Example:
58
- regex = r'(a)|(b)|(c)'
59
- matches = [
60
- ('match a', '', ''),
61
- ('', 'match b', ''),
62
- ('match a', '', ''),
63
- ('', '', 'match c')
64
- ]
65
- """
66
- snippets = {regex_id: [] for regex_id in range(len(regexes))}
67
- for match in matches:
68
- for regex_id in range(len(regexes)):
69
- if len(match[regex_id]) > 0:
70
- snippets[regex_id].append(match[regex_id])
71
- return snippets
72
-
73
- def __create_tests(self, testrun, suite, test_name, lines):
74
- """
75
- There will be at least one test per regex. If there were any match for a given
76
- regex, then a new test will be generated using test_name + shasum. This helps
77
- comparing kernel logs accross different builds
78
- """
79
- metadata, _ = SuiteMetadata.objects.get_or_create(suite=suite.slug, name=test_name, kind='test')
80
- testrun.tests.create(
81
- suite=suite,
82
- result=(len(lines) == 0),
83
- log='\n'.join(lines),
84
- metadata=metadata,
85
- build=testrun.build,
86
- environment=testrun.environment,
87
- )
88
-
89
- # Some lines of the matched regex might be the same, and we don't want to create
90
- # multiple tests like test1-sha1, test1-sha1, etc, so we'll create a set of sha1sums
91
- # then create only new tests for unique sha's
92
- shas = defaultdict(set)
93
- for line in lines:
94
- sha = self.__create_shasum(line)
95
- shas[sha].add(line)
96
-
97
- for sha, lines in shas.items():
98
- name = f'{test_name}-{sha}'
99
- metadata, _ = SuiteMetadata.objects.get_or_create(suite=suite.slug, name=name, kind='test')
100
- testrun.tests.create(
101
- suite=suite,
102
- result=False,
103
- log='\n---\n'.join(lines),
104
- metadata=metadata,
105
- build=testrun.build,
106
- environment=testrun.environment,
107
- )
108
-
109
- def __create_shasum(self, snippet):
110
- sha = hashlib.sha256()
111
- without_numbers = re.sub(r'(0x[a-f0-9]+|[<\[][0-9a-f]+?[>\]]|\d+)', '', snippet)
112
- without_time = re.sub(r'^\[[^\]]+\]', '', without_numbers)
113
- sha.update(without_time.encode())
114
- return sha.hexdigest()
115
-
116
44
  def postprocess_testrun(self, testrun):
117
45
  if testrun.log_file is None:
118
46
  return
@@ -127,10 +55,14 @@ class Plugin(BasePlugin):
127
55
  log = self.__kernel_msgs_only(log)
128
56
  suite, _ = testrun.build.project.suites.get_or_create(slug=f'log-parser-{log_type}')
129
57
 
130
- regex = self.__compile_regexes(REGEXES)
58
+ regex = self.compile_regexes(REGEXES)
131
59
  matches = regex.findall(log)
132
- snippets = self.__join_matches(matches, REGEXES)
60
+ snippets = self.join_matches(matches, REGEXES)
133
61
 
134
62
  for regex_id in range(len(REGEXES)):
135
63
  test_name = REGEXES[regex_id][REGEX_NAME]
136
- self.__create_tests(testrun, suite, test_name, snippets[regex_id])
64
+ regex_pattern = REGEXES[regex_id][REGEX_EXTRACT_NAME]
65
+ test_name_regex = None
66
+ if regex_pattern:
67
+ test_name_regex = re.compile(regex_pattern, re.S | re.M)
68
+ self.create_squad_tests(testrun, suite, test_name, snippets[regex_id], test_name_regex)
squad/settings.py CHANGED
@@ -37,17 +37,21 @@ if not os.access(DATA_DIR, os.W_OK):
37
37
  # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
38
38
 
39
39
  # SECURITY WARNING: keep the secret key used in production secret!
40
- secret_key_file = os.getenv('SECRET_KEY_FILE', None)
41
- if secret_key_file is None:
42
- secret_key_file = os.path.join(DATA_DIR, 'secret.dat')
43
-
44
- if not os.path.exists(secret_key_file):
45
- from squad.core.utils import random_key
46
- fd = os.open(secret_key_file, os.O_WRONLY | os.O_CREAT, 0o600)
47
- with os.fdopen(fd, 'w') as f:
48
- f.write(random_key(64))
49
-
50
- SECRET_KEY = open(secret_key_file).read()
40
+ secret_key = os.getenv('SECRET_KEY', None)
41
+ if secret_key:
42
+ SECRET_KEY = secret_key
43
+ else:
44
+ secret_key_file = os.getenv('SECRET_KEY_FILE', None)
45
+ if secret_key_file is None:
46
+ secret_key_file = os.path.join(DATA_DIR, 'secret.dat')
47
+
48
+ if not os.path.exists(secret_key_file):
49
+ from squad.core.utils import random_key
50
+ fd = os.open(secret_key_file, os.O_WRONLY | os.O_CREAT, 0o600)
51
+ with os.fdopen(fd, 'w') as f:
52
+ f.write(random_key(64))
53
+
54
+ SECRET_KEY = open(secret_key_file).read()
51
55
 
52
56
  DEBUG = os.getenv('ENV') not in ['production', 'staging']
53
57
 
squad/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '1.88'
1
+ __version__ = '1.90'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: squad
3
- Version: 1.88
3
+ Version: 1.90
4
4
  Summary: Software Quality Dashboard
5
5
  Home-page: https://github.com/Linaro/squad
6
6
  Author: Antonio Terceiro
@@ -12,26 +12,26 @@ Requires-Dist: aiohttp
12
12
  Requires-Dist: celery
13
13
  Requires-Dist: cryptography
14
14
  Requires-Dist: coreapi
15
- Requires-Dist: django-crispy-forms ==1.14.0
16
- Requires-Dist: Django >=3
15
+ Requires-Dist: django-crispy-forms==1.14.0
16
+ Requires-Dist: Django>=3
17
17
  Requires-Dist: django-allauth
18
18
  Requires-Dist: django-bootstrap3
19
19
  Requires-Dist: django-celery-results
20
20
  Requires-Dist: django-cors-headers
21
21
  Requires-Dist: django-debug-toolbar
22
- Requires-Dist: django-simple-history >3.0
23
- Requires-Dist: django-filter >=2.0
24
- Requires-Dist: djangorestframework >=3.9.2
25
- Requires-Dist: djangorestframework-filters >=1.0.0.dev0
22
+ Requires-Dist: django-simple-history>3.0
23
+ Requires-Dist: django-filter>=2.0
24
+ Requires-Dist: djangorestframework>=3.9.2
25
+ Requires-Dist: djangorestframework-filters>=1.0.0.dev0
26
26
  Requires-Dist: drf-extensions
27
27
  Requires-Dist: future
28
28
  Requires-Dist: gunicorn
29
- Requires-Dist: importlib-metadata >3
30
- Requires-Dist: Jinja2 ==3.0.3
29
+ Requires-Dist: importlib-metadata>3
30
+ Requires-Dist: Jinja2==3.0.3
31
31
  Requires-Dist: Markdown
32
- Requires-Dist: msgpack >=0.5.0
32
+ Requires-Dist: msgpack>=0.5.0
33
33
  Requires-Dist: python-dateutil
34
- Requires-Dist: PyYAML >=5.1
34
+ Requires-Dist: PyYAML>=5.1
35
35
  Requires-Dist: PyJWT
36
36
  Requires-Dist: pyzmq
37
37
  Requires-Dist: requests
@@ -40,6 +40,7 @@ Requires-Dist: sqlparse
40
40
  Requires-Dist: svgwrite
41
41
  Requires-Dist: whitenoise
42
42
  Provides-Extra: postgres
43
- Requires-Dist: psycopg2 ; extra == 'postgres'
43
+ Requires-Dist: psycopg2; extra == "postgres"
44
44
 
45
45
  Software Quality Dashboard
46
+
@@ -7,30 +7,30 @@ squad/http.py,sha256=KuIKtpf3yOvf5fwc0T2MR0ul1l4AKxq3b0CLdk6KBhM,3667
7
7
  squad/jinja2.py,sha256=OKX-lzNz6qtTZL56HWv4UBMPuBl4WQXv0qFJztGp9zs,2541
8
8
  squad/mail.py,sha256=xH5wuIpD7u1fTN9vNOcbzByojleaffsKwp-9i3BeOD0,390
9
9
  squad/manage.py,sha256=Z-LXT67p0R-IzwJ9fLIAacEZmU0VUjqDOSg7j2ZSxJ4,1437
10
- squad/settings.py,sha256=CRmnXFDrfdspzXGUIlffRcKMQizJ4Wzyyg1cIRu-h-M,14535
10
+ squad/settings.py,sha256=nx_cXfFOf3i5z7bmIzwkRhBgVHWtDBfK40-yo5hQUzw,14663
11
11
  squad/socialaccount.py,sha256=vySqPwQ3qVVpahuJ-Snln8K--yzRL3bw4Nx27AsB39A,789
12
12
  squad/urls.py,sha256=JiEfVW8YlzLPE52c2aHzdn5kVVKK4o22w8h5KOA6QhQ,2776
13
- squad/version.py,sha256=m_t8FsqIPiv-aD7WSt66lX1Px9if2ZNoqnlCXPf8Mh8,21
13
+ squad/version.py,sha256=xCyEItfDbvgP8ttkgjhTpQfmnJCsuxMtkuIMOjALn54,21
14
14
  squad/wsgi.py,sha256=SF8T0cQ0OPVyuYjO5YXBIQzvSXQHV0M2BTmd4gP1rPs,387
15
15
  squad/api/__init__.py,sha256=CJiVakfAlHVN5mIFRVQYZQfuNUhUgWVbsdYTME4tq7U,1349
16
16
  squad/api/apps.py,sha256=Trk72p-iV1uGn0o5mdJn5HARUoHGbfgO49jwXvpkmdQ,141
17
17
  squad/api/ci.py,sha256=ymG-eMKXpJgrVUiZcqJW-dYZQKvm1LkdR3TUMe4OSoM,6943
18
18
  squad/api/data.py,sha256=obKDV0-neEvj5lPF9VED2gy_hpfhGtLJABYvSY38ing,2379
19
19
  squad/api/filters.py,sha256=Zvp8DCJmiNquFWqvfVseEAAMYYPiT95RUjqKdzcqSnw,6917
20
- squad/api/rest.py,sha256=X3WX6tkqWl2H59fCJCNc3NIEkP4GoXImQLyCmvUlrMQ,77172
20
+ squad/api/rest.py,sha256=ZtbK0c1BLPPnsX79XlKFVYONM_VJ0vacWZ2JsdCd4l0,77342
21
21
  squad/api/urls.py,sha256=rmsdaL1uOCVSZ5x1redup9RliICmijaBjRK5ObsTkG8,1343
22
22
  squad/api/utils.py,sha256=Sa8QFId3_oSqD2UOoY3Kuh54LLDLPNMq2sub5ktd6Fs,1160
23
- squad/api/views.py,sha256=kuFlbiyZiD0i9jwwmkL3Y22LwJ3bx2oJs28d1g2DPA0,3898
23
+ squad/api/views.py,sha256=WH4c10e7iRmuL5tWDxG4zEFHzvF5hxDpEVvybfvbc_E,3880
24
24
  squad/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  squad/ci/admin.py,sha256=7yB-6F0cvt0NVvzGOTlZCyGPV_YHarmbKJZTTzataT4,2255
26
26
  squad/ci/apps.py,sha256=6OVnzTdJkxdqEJnKWYE9dZgUcc29_T1LrDw41cK4EQk,139
27
27
  squad/ci/exceptions.py,sha256=a1sccygniTYDSQi7FRn_6doapddFFiMf55AwGUh5Y80,227
28
28
  squad/ci/models.py,sha256=Fm-4b3SDgMh9HXzqjOd4iZDRMJ1D9AnZ2cg7i2OR248,16018
29
- squad/ci/tasks.py,sha256=xoiOyh0HKLiKsAnQbolMRq4E9RcvP4xFgR0S9d_bgm4,3782
29
+ squad/ci/tasks.py,sha256=P0NYjLuyUViTpO1jZMuRVREbFDCccrMCZDw5E4pt928,3882
30
30
  squad/ci/utils.py,sha256=38zHpw8xkZDSFlkG-2BwSK6AkcddK9OkN9LXuQ3SHR0,97
31
31
  squad/ci/backend/__init__.py,sha256=yhpotXT9F4IdAOXvGQ3-17eOHAFwoaqf9SnMX17ab30,534
32
32
  squad/ci/backend/fake.py,sha256=9sPKndsGd5GDNPp35v-zfJWXZCbge-yXH3RBQGgTlPk,2340
33
- squad/ci/backend/lava.py,sha256=BAReftlcRo7YkzwKbtwKpehQApN7cYnkwGmI_Nbs8D4,33791
33
+ squad/ci/backend/lava.py,sha256=E4QE0XtAiqArzzx3YSv7_2qYUBs4aSw8JOz0AV0z9W8,33877
34
34
  squad/ci/backend/null.py,sha256=0CVylWELIZw3JyzCROB4XXAjgQUi15YjQz5caRfTNBo,5434
35
35
  squad/ci/backend/tuxsuite.py,sha256=-A4p5HpUWnIC-61os0vdJOfAGoO81szoLkSgzyaUt6c,17901
36
36
  squad/ci/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -76,17 +76,17 @@ squad/ci/templatetags/filter_jobs.py,sha256=IkWjfSJSeImWlpHk2Tz_aiqalJvprStl5h2J
76
76
  squad/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  squad/core/admin.py,sha256=tFyRu_rhHN-ABJkEApaT4YtRYa4qxNe-8jou2H3Q0P4,7897
78
78
  squad/core/apps.py,sha256=Bl-4Yg0joKjygdifQG0ROIz4m5bHiPqytQ3G82uyzWc,143
79
- squad/core/callback.py,sha256=QhMf3ILkR2HAnqv1B2OW1pQEhrrIp4xRA9HhD6ywe1A,3066
79
+ squad/core/callback.py,sha256=tzj5PFQFiambicGikUg4yjYJ9WW6hDbO2e1jRCgAhVQ,3282
80
80
  squad/core/comparison.py,sha256=LR3-Unv0CTmakFCDzF_h8fm2peTJzkv79mQWNau1iwI,24429
81
81
  squad/core/data.py,sha256=2zw56v7iYRTUc7wlhuUNgwIIMmK2w84hi-amR9J7EPU,2236
82
82
  squad/core/failures.py,sha256=X6lJVghM2fOrd-RfuHeLlezW2pt7owDZ8eX-Kn_Qrt0,918
83
- squad/core/history.py,sha256=APIgJ1fXAGyxoNgxVMn02kJzXhLR1x2SG4UyCcTyUEQ,3467
84
- squad/core/models.py,sha256=DEkEs72gqX32237FoY-q8GN1DZ5cBRbegtLVgSlprGY,60508
83
+ squad/core/history.py,sha256=QRSIoDOw6R6vUWMtsPMknsHGM7FaCAeuCYqASCayHTk,3541
84
+ squad/core/models.py,sha256=sXQmgPtl54IZT7rDmJEU3QK6JSPbi0hTUGRsjwL6PIo,60851
85
85
  squad/core/notification.py,sha256=rOpO6F63w7_5l9gQgWBBEk-MFBjp7x_hVzoVIVyDze0,10030
86
86
  squad/core/plugins.py,sha256=FLgyoXXKnPBYEf2MgHup9M017rHuADHivLhgzmx_cJE,6354
87
87
  squad/core/queries.py,sha256=78fhIJZWXIlDryewYAt96beK1VJad66Ufu8cg3dHh4w,7698
88
88
  squad/core/statistics.py,sha256=xyTHuhdBjcJ4AozZESjTzSD3dBmmCDgLpbg5XpeyO_M,1056
89
- squad/core/utils.py,sha256=L6c-suSBbNFBVEZF7GF8DcjD5lmXZLy7QO0T_2j9LCk,4893
89
+ squad/core/utils.py,sha256=HwCq8SsKJHbBUtF4DZt1iWCuWhqZaHRBn--Yh0O_RH4,5018
90
90
  squad/core/locale/django.pot,sha256=XycSJyEaEpozGBS9zu7QTNQbffZC0D9eSJ-AwXaVZx4,2282
91
91
  squad/core/locale/es_MX/LC_MESSAGES/django.po,sha256=bwvTWHK2KOT6zFqbIYh61_xYqRnMaQECZsMsOvNdMNw,3071
92
92
  squad/core/locale/pl/LC_MESSAGES/django.po,sha256=mI-Vo8OKWCcx4PrsoB6GiPY3lYU55tSqh0sO6fUeK2Y,3111
@@ -98,7 +98,7 @@ squad/core/management/commands/compute_build_summaries.py,sha256=dz6-3vXtFNGYOzl
98
98
  squad/core/management/commands/compute_project_statuses.py,sha256=qcm71zEP_A-XhNWrDHM55TJSgKUk_oWjewuZEu2B2KM,3134
99
99
  squad/core/management/commands/fill_test_metadata.py,sha256=EG2mqKtThY5D7nnGalM3q0XOPEVDiDnFLV7sw7YSz1U,1326
100
100
  squad/core/management/commands/fix_squadplugin_data.py,sha256=cbjPL_-AvazBsmXKd5x6LpaoP-3MGpa3uoUUxljVzdw,5072
101
- squad/core/management/commands/import_data.py,sha256=cuQchMuyMj3UhXvpx-pbMmqdDIwUKxyBp26IiCZGamY,4522
101
+ squad/core/management/commands/import_data.py,sha256=KgSTNtrQQiqzqjJdvKDHbU6IExPsdTbdMJ-yqfZY4Y4,4556
102
102
  squad/core/management/commands/import_data.rst,sha256=79tAcJ6hOVRVzW2iheQuO6o2RHZKbbFtsHM-IEr6490,1444
103
103
  squad/core/management/commands/migrate_test_runs.py,sha256=RHV06tb4gWyv_q-ooC821_QGZi0WGwxjIYaUGTboqfI,4214
104
104
  squad/core/management/commands/populate_metric_build_and_environment.py,sha256=DJP9_YLRso0RiERBVsB0GP4-GaiRtJb0rAiUQDfFNQk,3166
@@ -277,7 +277,7 @@ squad/core/migrations/0167_add_project_datetime.py,sha256=VUBG-qsAhh2f2NXaHOqfX9
277
277
  squad/core/migrations/0168_add_group_settings.py,sha256=5UdylfMMNavTL0KXkjPSiEMhSisGWXbhUXQSzfK29Ck,462
278
278
  squad/core/migrations/0169_userpreferences.py,sha256=FwYv9RWxMWdQ2lXJMgi-Xc6XBB5Kp-_YTAOr9GVq1To,1098
279
279
  squad/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
280
- squad/core/tasks/__init__.py,sha256=QoQPjAJhN8Evqb4uaUYdxqGojRTvc572abQH9N1o9y4,18567
280
+ squad/core/tasks/__init__.py,sha256=pYbEkFzNaat7iQQretRiJQPPF4Sq-5-hBykJYnBM04g,18567
281
281
  squad/core/tasks/exceptions.py,sha256=n4cbmJFBdA6KWsGiTbfN9DyYGbJpk0DjR0UneEYw_W0,931
282
282
  squad/core/tasks/notification.py,sha256=6ZyTbUQZPITPP-4r9MUON7x-NbwvDBG8YeabM6fsjzA,4915
283
283
  squad/core/templates/squad/notification/base.jinja2,sha256=AbtQioEHV5DJBW4Etsu0-DQXd_8tQCnLejzgbDGDW7s,3413
@@ -296,7 +296,7 @@ squad/frontend/apps.py,sha256=lKMd_HrIna5OZrfeWXndcGoIDR2KFmBFnMoGHtCGE4E,151
296
296
  squad/frontend/badges.py,sha256=rEgjkJQKZQT1mL9j9s47okTQO-J55eU8mDNHShijovY,5878
297
297
  squad/frontend/build_settings.py,sha256=_Hqw5npczuU01Zi6FGAiSbrtMMzK9eAXv-cX5U5btto,909
298
298
  squad/frontend/ci.py,sha256=lfglUArCj5iYRLZgC6mDgEN_k-dDqfCezXW3j2Fn_Uc,2244
299
- squad/frontend/comparison.py,sha256=tZQOcXOOTU787VbZ2ueuXpWGUjiFSeq-O7fepSM0g-I,4547
299
+ squad/frontend/comparison.py,sha256=Oy3Cffphy5qPsEGVAzICkCszb9JqHwaj-0RNMNxISLA,4721
300
300
  squad/frontend/extract.py,sha256=p88JGuBvaC4AMDkJi7lqzbj5ZGh6h2LSlV7tcXbmxDc,8491
301
301
  squad/frontend/forms.py,sha256=StPdrHsFsEoBKEOF6bBampLXbVXrZcEDkbuI4II1dCA,753
302
302
  squad/frontend/group_settings.py,sha256=mV0kJEfRo41AEbOZMxWXY1MpYNqSgWVqac0dUdwkGyk,6232
@@ -304,7 +304,7 @@ squad/frontend/metrics.py,sha256=nGrfFHLG6g_DUYVJCIDlFLvLWhMrqJxX1Z0cckPBJlg,114
304
304
  squad/frontend/project_settings.py,sha256=TtWz8h8Goeb3pccLy9jLUibeHqyqkdK8phL7_Vh_d0I,5045
305
305
  squad/frontend/queries.py,sha256=NxQF2woAf9A4Wk_ozHzZXOGmr2as-j7hqfvmsfJ-ojc,967
306
306
  squad/frontend/setup.py,sha256=NF9VunY1HJGB2HsHJss-go7EGmqr__JASddxiBCvmeQ,169
307
- squad/frontend/tests.py,sha256=q64Z6DyS6TiJTCzF6SXw4wZfvXR8c0A4t-f0ib1jJ0w,8713
307
+ squad/frontend/tests.py,sha256=PidrjaToK_Cks0s9Mc4i3Vh4UXOWoXTZlpnxQ2wWjHY,8740
308
308
  squad/frontend/urls.py,sha256=biWauxwXR5j9kOfrSUqkv1Iqz-elB2aNViS9_UFoLzQ,4882
309
309
  squad/frontend/user_settings.py,sha256=U_i59iuylg98uH98K4ezPa2NY56idslBhn7MS6FguHQ,4976
310
310
  squad/frontend/utils.py,sha256=DeH58CJUI1dovpQrj3a-DcxNzM0cxsnBDOF0mrC4Qws,1364
@@ -425,15 +425,17 @@ squad/plugins/__init__.py,sha256=9BSzy2jFIoDpWlhD7odPPrLdW4CC3btBhdFCvB651dM,152
425
425
  squad/plugins/example.py,sha256=BKpwd315lHRIuNXJPteibpwfnI6C5eXYHYdFYBtVmsI,89
426
426
  squad/plugins/gerrit.py,sha256=CqO2KnFQzu9utr_TQ-sGr1wg3ln0B-bS2-c0_i8T5-c,7009
427
427
  squad/plugins/github.py,sha256=pdtLZw_7xNuzkaFvY_zWi0f2rsMlalXjKm7sz0eADz4,2429
428
- squad/plugins/linux_log_parser.py,sha256=Ym0OBo6EurU0E_inPlf4-LwDryCtnazrTX7MaEZHRA0,4939
428
+ squad/plugins/linux_log_parser.py,sha256=9vjt3z7ZDYofR90-JZv260phKMJyRv3sdksr8satgi0,2662
429
+ squad/plugins/lib/__init__.py,sha256=jzazbAvp2_ibblAs0cKZrmo9aR2EL3hKLyRDE008r2I,40
430
+ squad/plugins/lib/base_log_parser.py,sha256=lfGoHWYTzOmWxME-qGArpJatzjUdy0uEII5eokDfP1Y,6189
429
431
  squad/run/__init__.py,sha256=ssE8GPAGFiK6V0WpZYowav6Zqsd63dfDMMYasNa1sQg,1410
430
432
  squad/run/__main__.py,sha256=DOl8JOi4Yg7DdtwnUeGqtYBJ6P2k-D2psAEuYOjWr8w,66
431
433
  squad/run/listener.py,sha256=jBeOQhPGb4EdIREB1QsCzYuumsfJ-TqJPd3nR-0m59g,200
432
434
  squad/run/scheduler.py,sha256=CDJG3q5C0GuQuxwlMOfWTSSJpDdwbR6rzpbJfuA0xuw,277
433
435
  squad/run/worker.py,sha256=jtML0h5qKDuSbpJ6_rpWP4MT_rsGA7a24AhwGxBquzk,594
434
- squad-1.88.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
435
- squad-1.88.dist-info/METADATA,sha256=AbK4kEcgX8XOcABHY9ENsLJt_J-ERLtgMJpPj-Q40GU,1281
436
- squad-1.88.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
437
- squad-1.88.dist-info/entry_points.txt,sha256=apCDQydHZtvqV334ql6NhTJUAJeZRdtAm0TVcbbAi5Q,194
438
- squad-1.88.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
439
- squad-1.88.dist-info/RECORD,,
436
+ squad-1.90.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
437
+ squad-1.90.dist-info/METADATA,sha256=wn7qXz18DRF1uk_WM5Fh_TjO8RxUg-eanVQnfmjbKVk,1271
438
+ squad-1.90.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
439
+ squad-1.90.dist-info/entry_points.txt,sha256=J_jG3qnkoOHX4RFNGC0f83eJ4BSvK3pqLFkoF3HWfmA,195
440
+ squad-1.90.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
441
+ squad-1.90.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.3.0)
2
+ Generator: bdist_wheel (0.44.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -4,3 +4,4 @@ squad-admin = squad.manage:main
4
4
  squad-listener = squad.run.listener:main
5
5
  squad-scheduler = squad.run.scheduler:main
6
6
  squad-worker = squad.run.worker:main
7
+
File without changes