squad 1.89__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/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)
@@ -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
@@ -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):
@@ -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
@@ -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)
@@ -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,18 +1,10 @@
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
- from django.template.defaultfilters import slugify
8
-
4
+ from squad.plugins.lib.base_log_parser import BaseLogParser, REGEX_NAME, REGEX_EXTRACT_NAME
9
5
 
10
6
  logger = logging.getLogger()
11
7
 
12
- REGEX_NAME = 0
13
- REGEX_BODY = 1
14
- REGEX_EXTRACT_NAME = 2
15
-
16
8
  MULTILINERS = [
17
9
  ('check-kernel-exception', r'-+\[? cut here \]?-+.*?-+\[? end trace \w* \]?-+', r"\d][^\+\n]*"),
18
10
  ('check-kernel-kasan', r'=+\n\[[\s\.\d]+\]\s+BUG: KASAN:.*?=+', r"BUG: KASAN:[^\+\n]*"),
@@ -32,11 +24,7 @@ ONELINERS = [
32
24
  REGEXES = MULTILINERS + ONELINERS
33
25
 
34
26
 
35
- class Plugin(BasePlugin):
36
- def __compile_regexes(self, regexes):
37
- combined = [r'(%s)' % r[REGEX_BODY] for r in regexes]
38
- return re.compile(r'|'.join(combined), re.S | re.M)
39
-
27
+ class Plugin(BasePlugin, BaseLogParser):
40
28
  def __cutoff_boot_log(self, log):
41
29
  # Attempt to split the log in " login:"
42
30
  logs = log.split(' login:', 1)
@@ -53,109 +41,6 @@ class Plugin(BasePlugin):
53
41
  kernel_msgs = re.findall(r'(\[[ \d]+\.[ \d]+\] .*?)$', log, re.S | re.M)
54
42
  return '\n'.join(kernel_msgs)
55
43
 
56
- def __join_matches(self, matches, regexes):
57
- """
58
- group regex in python are returned as a list of tuples which each
59
- group match in one of the positions in the tuple. Example:
60
- regex = r'(a)|(b)|(c)'
61
- matches = [
62
- ('match a', '', ''),
63
- ('', 'match b', ''),
64
- ('match a', '', ''),
65
- ('', '', 'match c')
66
- ]
67
- """
68
- snippets = {regex_id: [] for regex_id in range(len(regexes))}
69
- for match in matches:
70
- for regex_id in range(len(regexes)):
71
- if len(match[regex_id]) > 0:
72
- snippets[regex_id].append(match[regex_id])
73
- return snippets
74
-
75
- def __create_tests(self, testrun, suite, test_name, lines, test_regex=None):
76
- """
77
- There will be at least one test per regex. If there were any match for a given
78
- regex, then a new test will be generated using test_name + shasum. This helps
79
- comparing kernel logs accross different builds
80
- """
81
- # Run the REGEX_EXTRACT_NAME regex over the log lines to sort them by
82
- # extracted name. If no name is extracted or the log parser did not
83
- # have any output for a particular regex, just use the default name
84
- # (for example "check-kernel-oops").
85
- tests_to_create = defaultdict(set)
86
- shas = defaultdict(set)
87
-
88
- # If there are no lines, use the default name and create a passing
89
- # test. For example "check-kernel-oops"
90
- if not lines:
91
- tests_to_create[test_name] = []
92
-
93
- # If there are lines, then create the tests for these.
94
- for line in lines:
95
- extracted_name = self.__create_name(line, test_regex)
96
- if extracted_name:
97
- extended_test_name = f"{test_name}-{extracted_name}"
98
- else:
99
- extended_test_name = test_name
100
- tests_to_create[extended_test_name].add(line)
101
-
102
- for name, lines in tests_to_create.items():
103
- metadata, _ = SuiteMetadata.objects.get_or_create(suite=suite.slug, name=name, kind='test')
104
- testrun.tests.create(
105
- suite=suite,
106
- result=(len(lines) == 0),
107
- log='\n'.join(lines),
108
- metadata=metadata,
109
- build=testrun.build,
110
- environment=testrun.environment,
111
- )
112
-
113
- # Some lines of the matched regex might be the same, and we don't want to create
114
- # multiple tests like test1-sha1, test1-sha1, etc, so we'll create a set of sha1sums
115
- # then create only new tests for unique sha's
116
-
117
- for line in lines:
118
- sha = self.__create_shasum(line)
119
- name_with_sha = f"{name}-{sha}"
120
- shas[name_with_sha].add(line)
121
-
122
- for name_with_sha, lines in shas.items():
123
- metadata, _ = SuiteMetadata.objects.get_or_create(suite=suite.slug, name=name_with_sha, kind='test')
124
- testrun.tests.create(
125
- suite=suite,
126
- result=False,
127
- log='\n---\n'.join(lines),
128
- metadata=metadata,
129
- build=testrun.build,
130
- environment=testrun.environment,
131
- )
132
-
133
- def __remove_numbers_and_time(self, snippet):
134
- without_numbers = re.sub(r"(0x[a-f0-9]+|[<\[][0-9a-f]+?[>\]]|\d+)", "", snippet)
135
- without_time = re.sub(r"^\[[^\]]+\]", "", without_numbers)
136
-
137
- return without_time
138
-
139
- def __create_name(self, snippet, regex=None):
140
- matches = None
141
- if regex:
142
- matches = regex.findall(snippet)
143
- if not matches:
144
- return None
145
- snippet = matches[0]
146
- without_numbers_and_time = self.__remove_numbers_and_time(snippet)
147
-
148
- # Limit the name length to 191 characters, since the max name length
149
- # for SuiteMetadata in SQUAD is 256 characters. The SHA and "-" take 65
150
- # characters: 256-65=191
151
- return slugify(without_numbers_and_time)[:191]
152
-
153
- def __create_shasum(self, snippet):
154
- sha = hashlib.sha256()
155
- without_numbers_and_time = self.__remove_numbers_and_time(snippet)
156
- sha.update(without_numbers_and_time.encode())
157
- return sha.hexdigest()
158
-
159
44
  def postprocess_testrun(self, testrun):
160
45
  if testrun.log_file is None:
161
46
  return
@@ -170,9 +55,9 @@ class Plugin(BasePlugin):
170
55
  log = self.__kernel_msgs_only(log)
171
56
  suite, _ = testrun.build.project.suites.get_or_create(slug=f'log-parser-{log_type}')
172
57
 
173
- regex = self.__compile_regexes(REGEXES)
58
+ regex = self.compile_regexes(REGEXES)
174
59
  matches = regex.findall(log)
175
- snippets = self.__join_matches(matches, REGEXES)
60
+ snippets = self.join_matches(matches, REGEXES)
176
61
 
177
62
  for regex_id in range(len(REGEXES)):
178
63
  test_name = REGEXES[regex_id][REGEX_NAME]
@@ -180,4 +65,4 @@ class Plugin(BasePlugin):
180
65
  test_name_regex = None
181
66
  if regex_pattern:
182
67
  test_name_regex = re.compile(regex_pattern, re.S | re.M)
183
- self.__create_tests(testrun, suite, test_name, snippets[regex_id], test_name_regex)
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.89'
1
+ __version__ = '1.90'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: squad
3
- Version: 1.89
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,10 +7,10 @@ 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=DHemnt4WEI99t3epDIiaS6j6mPKaFVroxzlr9RNdMCU,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
@@ -20,7 +20,7 @@ squad/api/filters.py,sha256=Zvp8DCJmiNquFWqvfVseEAAMYYPiT95RUjqKdzcqSnw,6917
20
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
@@ -81,12 +81,12 @@ squad/core/comparison.py,sha256=LR3-Unv0CTmakFCDzF_h8fm2peTJzkv79mQWNau1iwI,2442
81
81
  squad/core/data.py,sha256=2zw56v7iYRTUc7wlhuUNgwIIMmK2w84hi-amR9J7EPU,2236
82
82
  squad/core/failures.py,sha256=X6lJVghM2fOrd-RfuHeLlezW2pt7owDZ8eX-Kn_Qrt0,918
83
83
  squad/core/history.py,sha256=QRSIoDOw6R6vUWMtsPMknsHGM7FaCAeuCYqASCayHTk,3541
84
- squad/core/models.py,sha256=jAQpsCo4uNPF2f93LI-_Wfpu5TjUhHFf6LfwXA8grU0,60859
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=jinGlLEnr8nL84OzBvm0VFGt6EzWWR0r4dOabDrbXfM,4908
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
@@ -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=8GgIM4skM06uZX1d-15nZ0D4lxg5jOp9XYRvMtUtjA0,7195
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.89.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
435
- squad-1.89.dist-info/METADATA,sha256=Kb687CzcwB57kqLaoR1vJ9H6ESKXgDx5UToU4yOcZFg,1281
436
- squad-1.89.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
437
- squad-1.89.dist-info/entry_points.txt,sha256=apCDQydHZtvqV334ql6NhTJUAJeZRdtAm0TVcbbAi5Q,194
438
- squad-1.89.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
439
- squad-1.89.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 (71.1.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