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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of squad might be problematic. Click here for more details.

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