squad 1.90__py3-none-any.whl → 1.92__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/ci.py +11 -6
- squad/api/prometheus.py +52 -0
- squad/api/urls.py +2 -0
- squad/ci/backend/fake.py +3 -0
- squad/ci/backend/lava.py +6 -1
- squad/ci/backend/null.py +6 -0
- squad/ci/backend/tuxsuite.py +39 -8
- squad/frontend/templates/squad/project.jinja2 +8 -0
- squad/frontend/urls.py +1 -0
- squad/plugins/lib/base_log_parser.py +11 -6
- squad/plugins/linux_log_parser.py +14 -11
- squad/settings.py +2 -0
- squad/version.py +1 -1
- {squad-1.90.dist-info → squad-1.92.dist-info}/METADATA +2 -2
- {squad-1.90.dist-info → squad-1.92.dist-info}/RECORD +19 -18
- {squad-1.90.dist-info → squad-1.92.dist-info}/COPYING +0 -0
- {squad-1.90.dist-info → squad-1.92.dist-info}/WHEEL +0 -0
- {squad-1.90.dist-info → squad-1.92.dist-info}/entry_points.txt +0 -0
- {squad-1.90.dist-info → squad-1.92.dist-info}/top_level.txt +0 -0
squad/api/ci.py
CHANGED
@@ -73,15 +73,26 @@ def submit_job(request, group_slug, project_slug, version, environment_slug):
|
|
73
73
|
@csrf_exempt
|
74
74
|
@auth_privileged
|
75
75
|
def watch_job(request, group_slug, project_slug, version, environment_slug):
|
76
|
+
|
77
|
+
# testjob_id points to the backend's test job
|
78
|
+
testjob_id = request.POST.get('testjob_id', None)
|
79
|
+
if testjob_id is None:
|
80
|
+
return HttpResponseBadRequest("testjob_id is required")
|
81
|
+
|
76
82
|
backend_name = request.POST.get('backend')
|
77
83
|
if backend_name is None:
|
78
84
|
return HttpResponseBadRequest("backend field is required")
|
85
|
+
|
79
86
|
backend = None
|
80
87
|
try:
|
81
88
|
backend = Backend.objects.get(name=request.POST.get('backend'))
|
82
89
|
except Backend.DoesNotExist:
|
83
90
|
return HttpResponseBadRequest("requested backend does not exist")
|
84
91
|
|
92
|
+
check = backend.get_implementation().check_job_id(testjob_id)
|
93
|
+
if check is not True:
|
94
|
+
return HttpResponseBadRequest(check)
|
95
|
+
|
85
96
|
# project has to exist or request will result with 400
|
86
97
|
project = request.project
|
87
98
|
if backend is None or project is None:
|
@@ -90,12 +101,6 @@ def watch_job(request, group_slug, project_slug, version, environment_slug):
|
|
90
101
|
# create Build object
|
91
102
|
build, _ = project.builds.get_or_create(version=version)
|
92
103
|
|
93
|
-
# testjob_id points to the backend's test job
|
94
|
-
testjob_id = request.POST.get('testjob_id', None)
|
95
|
-
|
96
|
-
if testjob_id is None:
|
97
|
-
return HttpResponseBadRequest("testjob_id is required")
|
98
|
-
|
99
104
|
# create TestJob object
|
100
105
|
test_job = TestJob(
|
101
106
|
backend=backend,
|
squad/api/prometheus.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
import re
|
2
|
+
import requests
|
3
|
+
import celery
|
4
|
+
|
5
|
+
from django.conf import settings
|
6
|
+
from django.http import HttpResponse, HttpResponseForbidden
|
7
|
+
from django.views.decorators.csrf import csrf_exempt
|
8
|
+
from django.views.decorators.http import require_http_methods
|
9
|
+
|
10
|
+
from squad.http import auth_user_from_request
|
11
|
+
|
12
|
+
|
13
|
+
@csrf_exempt
|
14
|
+
@require_http_methods(['GET'])
|
15
|
+
def metrics(request):
|
16
|
+
user = auth_user_from_request(request, request.user)
|
17
|
+
if not user.is_authenticated:
|
18
|
+
return HttpResponseForbidden()
|
19
|
+
|
20
|
+
output = ''
|
21
|
+
available_queues = None
|
22
|
+
|
23
|
+
active_queues = celery.current_app.control.inspect().active_queues()
|
24
|
+
if active_queues is not None:
|
25
|
+
active_workers = set()
|
26
|
+
available_queues = set()
|
27
|
+
for worker_name, queues in active_queues.items():
|
28
|
+
active_workers.add(worker_name)
|
29
|
+
available_queues |= set([q['name'] for q in queues])
|
30
|
+
|
31
|
+
output += '# TYPE workers_count counter\n'
|
32
|
+
output += f'workers_count {len(active_workers)}\n'
|
33
|
+
|
34
|
+
# TODO: check how to get metrics for non-RabbitMQ brokers
|
35
|
+
if settings.CELERY_BROKER_URL:
|
36
|
+
rabbitmq_url = settings.CELERY_BROKER_URL.replace('amqps://', 'https://').replace('amqp://', 'http://')
|
37
|
+
rabbitmq_url = re.sub(r':\d+$', '', rabbitmq_url)
|
38
|
+
rabbitmq_url += '/api/queues'
|
39
|
+
|
40
|
+
response = requests.get(rabbitmq_url)
|
41
|
+
queues = response.json()
|
42
|
+
available_queues = {r["queue"] for r in settings.CELERY_TASK_ROUTES.values()}
|
43
|
+
|
44
|
+
for queue in queues:
|
45
|
+
if queue['name'] in available_queues:
|
46
|
+
metric_name = f'queue_{queue["name"]}_count'
|
47
|
+
length = queue['messages_ready']
|
48
|
+
|
49
|
+
output += f'\n# TYPE {metric_name} counter'
|
50
|
+
output += f'\n{metric_name} {length}'
|
51
|
+
|
52
|
+
return HttpResponse(output, status=200, content_type="text/plain;")
|
squad/api/urls.py
CHANGED
@@ -5,6 +5,7 @@ from rest_framework.schemas import get_schema_view
|
|
5
5
|
from . import views
|
6
6
|
from . import data
|
7
7
|
from . import ci
|
8
|
+
from . import prometheus
|
8
9
|
from . import rest
|
9
10
|
|
10
11
|
|
@@ -26,4 +27,5 @@ urlpatterns = [
|
|
26
27
|
url(r'^resubmit/([0-9]+)', ci.resubmit_job),
|
27
28
|
url(r'^forceresubmit/([0-9]+)', ci.force_resubmit_job),
|
28
29
|
url(r'^version/', views.version),
|
30
|
+
url(r'^prometheus/', prometheus.metrics),
|
29
31
|
]
|
squad/ci/backend/fake.py
CHANGED
squad/ci/backend/lava.py
CHANGED
@@ -683,7 +683,7 @@ class Backend(BaseBackend):
|
|
683
683
|
if clone_measurements_to_tests:
|
684
684
|
res_value = result['result']
|
685
685
|
results.update({res_name: res_value})
|
686
|
-
elif result['name']
|
686
|
+
elif 'login-action' in result['name'] and handle_lava_boot:
|
687
687
|
# add artificial 'boot' test result for each test job
|
688
688
|
# by default the boot test is named after the device_type
|
689
689
|
boot = "boot-%s" % test_job.name
|
@@ -785,6 +785,11 @@ class Backend(BaseBackend):
|
|
785
785
|
except yaml.YAMLError as e:
|
786
786
|
return str(e)
|
787
787
|
|
788
|
+
def check_job_id(self, job_id):
|
789
|
+
if re.match(r"^\d+$", str(job_id)) is not None:
|
790
|
+
return True
|
791
|
+
return "LAVA job id should be an integer"
|
792
|
+
|
788
793
|
def get_job_definition(self, job_id):
|
789
794
|
if self.use_xml_rpc:
|
790
795
|
return self.proxy.scheduler.jobs.definition(job_id)
|
squad/ci/backend/null.py
CHANGED
@@ -143,6 +143,12 @@ class Backend:
|
|
143
143
|
"""
|
144
144
|
raise NotImplementedError
|
145
145
|
|
146
|
+
def check_job_id(self, job_id):
|
147
|
+
"""
|
148
|
+
Returns True if job id matches what the backend expect, else returns the error message
|
149
|
+
"""
|
150
|
+
raise NotImplementedError
|
151
|
+
|
146
152
|
def format_message(self, msg):
|
147
153
|
if self.data and hasattr(self.data, "name"):
|
148
154
|
return self.data.name + ': ' + msg
|
squad/ci/backend/tuxsuite.py
CHANGED
@@ -120,6 +120,13 @@ class Backend(BaseBackend):
|
|
120
120
|
# The regex below is supposed to find only one match
|
121
121
|
return matches[0]
|
122
122
|
|
123
|
+
def check_job_id(self, job_id):
|
124
|
+
try:
|
125
|
+
self.parse_job_id(job_id)
|
126
|
+
return True
|
127
|
+
except FetchIssue as e:
|
128
|
+
return str(e)
|
129
|
+
|
123
130
|
def generate_job_id(self, result_type, result):
|
124
131
|
"""
|
125
132
|
The job id for TuxSuite results is generated using 3 pieces of info:
|
@@ -144,7 +151,11 @@ class Backend(BaseBackend):
|
|
144
151
|
url = reduce(urljoin, urlbits)
|
145
152
|
|
146
153
|
try:
|
147
|
-
|
154
|
+
headers = {}
|
155
|
+
if hasattr(self, 'auth_token') and self.auth_token is not None:
|
156
|
+
headers = {'Authorization': self.auth_token}
|
157
|
+
|
158
|
+
response = Backend.get_session().request("GET", url, headers=headers)
|
148
159
|
except Exception as e:
|
149
160
|
raise TemporaryFetchIssue(f"Can't retrieve from {url}: {e}")
|
150
161
|
|
@@ -224,6 +235,11 @@ class Backend(BaseBackend):
|
|
224
235
|
if 'toolchain' in build_metadata_keys and 'kconfig' in build_metadata_keys and metadata['build_name'] in [None, '']:
|
225
236
|
metadata['build_name'] = self.generate_test_name(build_metadata)
|
226
237
|
|
238
|
+
def add_skip_boot_test(self, tests, metadata):
|
239
|
+
# Create an artificial boot test and mark it as skip
|
240
|
+
boot_test_name = 'boot/' + (metadata.get('build_name') or 'boot')
|
241
|
+
tests[boot_test_name] = None
|
242
|
+
|
227
243
|
def parse_build_results(self, test_job, job_url, results, settings):
|
228
244
|
required_keys = ['build_status', 'warnings_count', 'download_url', 'retry']
|
229
245
|
self.__check_required_keys__(required_keys, results)
|
@@ -233,9 +249,6 @@ class Backend(BaseBackend):
|
|
233
249
|
test_job.name = test_name
|
234
250
|
|
235
251
|
build_status = results['build_status']
|
236
|
-
if build_status == 'error' and results['retry'] < 2:
|
237
|
-
# SQUAD should retry fetching the build until retry == 2
|
238
|
-
raise TemporaryFetchIssue(results.get('status_message', 'TuxSuite Error'))
|
239
252
|
|
240
253
|
# Make metadata
|
241
254
|
metadata_keys = settings.get('BUILD_METADATA_KEYS', [])
|
@@ -254,7 +267,7 @@ class Backend(BaseBackend):
|
|
254
267
|
metrics = {}
|
255
268
|
|
256
269
|
completed = True
|
257
|
-
if
|
270
|
+
if build_status == 'error':
|
258
271
|
# This indicates that TuxSuite gave up trying to work on this build
|
259
272
|
status = 'Incomplete'
|
260
273
|
tests[f'build/{test_name}'] = 'skip'
|
@@ -318,6 +331,11 @@ class Backend(BaseBackend):
|
|
318
331
|
metadata_keys = settings.get('TEST_METADATA_KEYS', [])
|
319
332
|
metadata = {k: results.get(k) for k in metadata_keys}
|
320
333
|
|
334
|
+
# Change environment name
|
335
|
+
if 'test_name' in results and results.get('test_name') is not None:
|
336
|
+
test_job.environment = results.get('test_name')
|
337
|
+
test_job.save()
|
338
|
+
|
321
339
|
# Add extra metadata from metadata file if it exists
|
322
340
|
self.update_metadata_from_file(results=results, metadata=metadata)
|
323
341
|
|
@@ -340,6 +358,8 @@ class Backend(BaseBackend):
|
|
340
358
|
else:
|
341
359
|
test_job.failure = 'sanity test failed'
|
342
360
|
|
361
|
+
self.add_skip_boot_test(tests, metadata)
|
362
|
+
|
343
363
|
return status, completed, metadata, tests, metrics, logs
|
344
364
|
|
345
365
|
# Fetch results even if the job fails, but has results
|
@@ -348,8 +368,14 @@ class Backend(BaseBackend):
|
|
348
368
|
|
349
369
|
elif results['result'] == 'error':
|
350
370
|
test_job.failure = 'tuxsuite infrastructure error'
|
371
|
+
self.add_skip_boot_test(tests, metadata)
|
351
372
|
return 'Incomplete', completed, metadata, tests, metrics, logs
|
352
373
|
|
374
|
+
elif results['result'] == 'canceled':
|
375
|
+
test_job.failure = 'tuxsuite job canceled'
|
376
|
+
self.add_skip_boot_test(tests, metadata)
|
377
|
+
return 'Canceled', completed, metadata, tests, metrics, logs
|
378
|
+
|
353
379
|
# If boot result is unkown, a retry is needed, otherwise, it either passed or failed
|
354
380
|
if 'unknown' == results['results']['boot']:
|
355
381
|
return None
|
@@ -384,6 +410,10 @@ class Backend(BaseBackend):
|
|
384
410
|
|
385
411
|
def fetch(self, test_job):
|
386
412
|
url = self.job_url(test_job)
|
413
|
+
|
414
|
+
settings = self.__resolve_settings__(test_job)
|
415
|
+
self.auth_token = settings.get('TUXSUITE_TOKEN', None)
|
416
|
+
|
387
417
|
if test_job.input:
|
388
418
|
results = self.fetch_from_results_input(test_job)
|
389
419
|
test_job.input = None
|
@@ -393,11 +423,12 @@ class Backend(BaseBackend):
|
|
393
423
|
if results.get('state') != 'finished':
|
394
424
|
return None
|
395
425
|
|
396
|
-
settings = self.__resolve_settings__(test_job)
|
397
|
-
|
398
426
|
result_type = self.parse_job_id(test_job.job_id)[0]
|
399
427
|
parse_results = getattr(self, f'parse_{result_type.lower()}_results')
|
400
|
-
|
428
|
+
parsed = parse_results(test_job, url, results, settings)
|
429
|
+
|
430
|
+
self.auth_token = None
|
431
|
+
return parsed
|
401
432
|
|
402
433
|
def job_url(self, test_job):
|
403
434
|
result_type, tux_project, tux_uid = self.parse_job_id(test_job.job_id)
|
@@ -6,6 +6,14 @@
|
|
6
6
|
|
7
7
|
{% include "squad/project-nav.jinja2" %}
|
8
8
|
|
9
|
+
|
10
|
+
{% if project.description%}
|
11
|
+
<h2>{{ _('Description') }}</h2>
|
12
|
+
<div class='description-{{project.id}}'>
|
13
|
+
{{project.description}}
|
14
|
+
</div>
|
15
|
+
{% endif %}
|
16
|
+
|
9
17
|
{% if last_build %}
|
10
18
|
<div>
|
11
19
|
<h2>
|
squad/frontend/urls.py
CHANGED
@@ -52,6 +52,7 @@ urlpatterns = [
|
|
52
52
|
url(r'^(%s)/(%s)/build/([^/]+)/attachments/testrun/([^/]+)/([^/]+)$' % group_and_project, views.build_attachment, name='build_attachments'),
|
53
53
|
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/tests/$' % group_and_project, views.test_run_suite_tests, name='testrun_suite_tests'),
|
54
54
|
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/test/([^/]+)/history/$' % group_and_project, tests.test_history, name='test_history'),
|
55
|
+
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/test/([^/]+)/$' % group_and_project, views.test_run_suite_test_details, name='testrun_suite_test_details'),
|
55
56
|
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/test/([^/]+)/details/$' % group_and_project, views.test_run_suite_test_details, name='testrun_suite_test_details'),
|
56
57
|
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/metrics/$' % group_and_project, views.test_run_suite_metrics, name='testrun_suite_metrics'),
|
57
58
|
url(r'^(%s)/(%s)/build/([^/]+)/testrun/([^/]+)/suite/([^/]+)/test/([^/]+)/log$' % group_and_project, views.test_details_log, name='test_details_log'),
|
@@ -8,6 +8,11 @@ REGEX_NAME = 0
|
|
8
8
|
REGEX_BODY = 1
|
9
9
|
REGEX_EXTRACT_NAME = 2
|
10
10
|
|
11
|
+
tstamp = r"\[[ \d]+\.[ \d]+\]"
|
12
|
+
pid = r"(?:\s*?\[\s*?[CT]\d+\s*?\])"
|
13
|
+
not_newline_or_plus = r"[^\+\n]"
|
14
|
+
square_brackets_and_contents = r"\[[^\]]+\]"
|
15
|
+
|
11
16
|
|
12
17
|
class BaseLogParser:
|
13
18
|
def compile_regexes(self, regexes):
|
@@ -15,8 +20,13 @@ class BaseLogParser:
|
|
15
20
|
return re.compile(r"|".join(combined), re.S | re.M)
|
16
21
|
|
17
22
|
def remove_numbers_and_time(self, snippet):
|
23
|
+
# [ 1067.461794][ T132] BUG: KCSAN: data-race in do_page_fault spectre_v4_enable_task_mitigation
|
24
|
+
# -> [ .][ T] BUG: KCSAN: data-race in do_page_fault spectre_v_enable_task_mitigation
|
18
25
|
without_numbers = re.sub(r"(0x[a-f0-9]+|[<\[][0-9a-f]+?[>\]]|\d+)", "", snippet)
|
19
|
-
|
26
|
+
|
27
|
+
# [ .][ T] BUG: KCSAN: data-race in do_page_fault spectre_v_enable_task_mitigation
|
28
|
+
# -> BUG: KCSAN: data-race in do_page_fault spectre_v_enable_task_mitigation
|
29
|
+
without_time = re.sub(f"^{square_brackets_and_contents}({square_brackets_and_contents})?", "", without_numbers) # noqa
|
20
30
|
|
21
31
|
return without_time
|
22
32
|
|
@@ -56,11 +66,6 @@ class BaseLogParser:
|
|
56
66
|
tests_without_shas_to_create = defaultdict(set)
|
57
67
|
tests_with_shas_to_create = defaultdict(set)
|
58
68
|
|
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
69
|
# If there are lines, then create the tests for these.
|
65
70
|
for line in lines:
|
66
71
|
extracted_name = self.create_name(line, test_regex)
|
@@ -1,23 +1,26 @@
|
|
1
1
|
import logging
|
2
2
|
import re
|
3
3
|
from squad.plugins import Plugin as BasePlugin
|
4
|
-
from squad.plugins.lib.base_log_parser import BaseLogParser, REGEX_NAME, REGEX_EXTRACT_NAME
|
4
|
+
from squad.plugins.lib.base_log_parser import BaseLogParser, REGEX_NAME, REGEX_EXTRACT_NAME, tstamp, pid, not_newline_or_plus
|
5
5
|
|
6
6
|
logger = logging.getLogger()
|
7
7
|
|
8
8
|
MULTILINERS = [
|
9
|
-
('
|
10
|
-
('
|
11
|
-
('
|
9
|
+
('exception', fr'-+\[? cut here \]?-+.*?{tstamp}{pid}?\s+-+\[? end trace \w* \]?-+', fr"\n{tstamp}{not_newline_or_plus}*"), # noqa
|
10
|
+
('kasan', fr'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KASAN:.*?\n*?{tstamp}{pid}?\s+=+', fr"BUG: KASAN:{not_newline_or_plus}*"), # noqa
|
11
|
+
('kcsan', fr'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KCSAN:.*?=+', fr"BUG: KCSAN:{not_newline_or_plus}*"), # noqa
|
12
|
+
('kfence', fr'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KFENCE:.*?{tstamp}{pid}?\s+=+', fr"BUG: KFENCE:{not_newline_or_plus}*"), # noqa
|
13
|
+
('panic-multiline', fr'{tstamp}{pid}?\s+Kernel panic - [^\n]+\n.*?-+\[? end Kernel panic - [^\n]+ \]?-*', fr"Kernel {not_newline_or_plus}*"), # noqa
|
14
|
+
('internal-error-oops', fr'{tstamp}{pid}?\s+Internal error: Oops.*?-+\[? end trace \w+ \]?-+', fr"Oops{not_newline_or_plus}*"), # noqa
|
12
15
|
]
|
13
16
|
|
14
17
|
ONELINERS = [
|
15
|
-
('
|
16
|
-
('
|
17
|
-
('
|
18
|
-
('
|
19
|
-
('
|
20
|
-
('
|
18
|
+
('oops', r'^[^\n]+Oops(?: -|:).*?$', fr"Oops{not_newline_or_plus}*"), # noqa
|
19
|
+
('fault', r'^[^\n]+Unhandled fault.*?$', fr"Unhandled {not_newline_or_plus}*"), # noqa
|
20
|
+
('warning', r'^[^\n]+WARNING:.*?$', fr"WARNING:{not_newline_or_plus}*"), # noqa
|
21
|
+
('bug', r'^[^\n]+(?: kernel BUG at|BUG:).*?$', fr"BUG{not_newline_or_plus}*"), # noqa
|
22
|
+
('invalid-opcode', r'^[^\n]+invalid opcode:.*?$', fr"invalid opcode:{not_newline_or_plus}*"), # noqa
|
23
|
+
('panic', r'Kernel panic - not syncing.*?$', fr"Kernel {not_newline_or_plus}*"), # noqa
|
21
24
|
]
|
22
25
|
|
23
26
|
# Tip: broader regexes should come first
|
@@ -38,7 +41,7 @@ class Plugin(BasePlugin, BaseLogParser):
|
|
38
41
|
return boot_log, test_log
|
39
42
|
|
40
43
|
def __kernel_msgs_only(self, log):
|
41
|
-
kernel_msgs = re.findall(
|
44
|
+
kernel_msgs = re.findall(f'({tstamp}{pid}? .*?)$', log, re.S | re.M) # noqa
|
42
45
|
return '\n'.join(kernel_msgs)
|
43
46
|
|
44
47
|
def postprocess_testrun(self, testrun):
|
squad/settings.py
CHANGED
@@ -422,6 +422,7 @@ CRISPY_TEMPLATE_PACK = 'bootstrap3'
|
|
422
422
|
|
423
423
|
# Sentry support
|
424
424
|
SENTRY_DSN = os.getenv('SENTRY_DSN')
|
425
|
+
SENTRY_TRACES_SAMPLE_RATE = os.getenv('SENTRY_TRACES_SAMPLE_RATE', '0')
|
425
426
|
if SENTRY_DSN:
|
426
427
|
try:
|
427
428
|
import sentry_sdk
|
@@ -432,6 +433,7 @@ if SENTRY_DSN:
|
|
432
433
|
dsn=SENTRY_DSN,
|
433
434
|
integrations=[DjangoIntegration(), CeleryIntegration()],
|
434
435
|
release='%s@%s' % (os.getenv('ENV', 'squad'), squad_version),
|
436
|
+
traces_sample_rate=float(SENTRY_TRACES_SAMPLE_RATE),
|
435
437
|
)
|
436
438
|
except ImportError:
|
437
439
|
pass
|
squad/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '1.
|
1
|
+
__version__ = '1.92'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: squad
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.92
|
4
4
|
Summary: Software Quality Dashboard
|
5
5
|
Home-page: https://github.com/Linaro/squad
|
6
6
|
Author: Antonio Terceiro
|
@@ -40,7 +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-binary; extra == "postgres"
|
44
44
|
|
45
45
|
Software Quality Dashboard
|
46
46
|
|
@@ -7,18 +7,19 @@ 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=
|
10
|
+
squad/settings.py,sha256=0MZ48SV_7CTrLMik2ubWf8-ROQiFju6CKnUC3iR8KAc,14800
|
11
11
|
squad/socialaccount.py,sha256=vySqPwQ3qVVpahuJ-Snln8K--yzRL3bw4Nx27AsB39A,789
|
12
12
|
squad/urls.py,sha256=JiEfVW8YlzLPE52c2aHzdn5kVVKK4o22w8h5KOA6QhQ,2776
|
13
|
-
squad/version.py,sha256=
|
13
|
+
squad/version.py,sha256=WHUX3wrk23jPrWzNUft2dot0QtSqj7kO0c3Q3yKJiDM,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
|
-
squad/api/ci.py,sha256=
|
17
|
+
squad/api/ci.py,sha256=QjGIhSpm8gmIjH4Nd2NAWtJItSVleg3QOLxBU_p9h1E,7082
|
18
18
|
squad/api/data.py,sha256=obKDV0-neEvj5lPF9VED2gy_hpfhGtLJABYvSY38ing,2379
|
19
19
|
squad/api/filters.py,sha256=Zvp8DCJmiNquFWqvfVseEAAMYYPiT95RUjqKdzcqSnw,6917
|
20
|
+
squad/api/prometheus.py,sha256=MEzSZtYYx6PXreIwZzcVHLp-1vVQ9IKhi9hb8b0vjUk,1839
|
20
21
|
squad/api/rest.py,sha256=ZtbK0c1BLPPnsX79XlKFVYONM_VJ0vacWZ2JsdCd4l0,77342
|
21
|
-
squad/api/urls.py,sha256=
|
22
|
+
squad/api/urls.py,sha256=c-o27_RP0ynOtxuyRKUl274fFMWWrzoii31Mr2saxSQ,1414
|
22
23
|
squad/api/utils.py,sha256=Sa8QFId3_oSqD2UOoY3Kuh54LLDLPNMq2sub5ktd6Fs,1160
|
23
24
|
squad/api/views.py,sha256=WH4c10e7iRmuL5tWDxG4zEFHzvF5hxDpEVvybfvbc_E,3880
|
24
25
|
squad/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -29,10 +30,10 @@ squad/ci/models.py,sha256=Fm-4b3SDgMh9HXzqjOd4iZDRMJ1D9AnZ2cg7i2OR248,16018
|
|
29
30
|
squad/ci/tasks.py,sha256=P0NYjLuyUViTpO1jZMuRVREbFDCccrMCZDw5E4pt928,3882
|
30
31
|
squad/ci/utils.py,sha256=38zHpw8xkZDSFlkG-2BwSK6AkcddK9OkN9LXuQ3SHR0,97
|
31
32
|
squad/ci/backend/__init__.py,sha256=yhpotXT9F4IdAOXvGQ3-17eOHAFwoaqf9SnMX17ab30,534
|
32
|
-
squad/ci/backend/fake.py,sha256=
|
33
|
-
squad/ci/backend/lava.py,sha256=
|
34
|
-
squad/ci/backend/null.py,sha256=
|
35
|
-
squad/ci/backend/tuxsuite.py,sha256
|
33
|
+
squad/ci/backend/fake.py,sha256=7Rl-JXnBYThDomOBzBsN9XuVkSjSHTZjtZOURdowZbA,2397
|
34
|
+
squad/ci/backend/lava.py,sha256=WeOJJNxv42geGf3Y6r-I0WnhWinxpSSgZAFAwfkiXGY,34039
|
35
|
+
squad/ci/backend/null.py,sha256=htEd4NbrXLKdPgFfTS0Ixm8PdT6Ghat3BCYi2zjfuv0,5624
|
36
|
+
squad/ci/backend/tuxsuite.py,sha256=dTQNgHs5HNrY2LUaSH6jXiBLVjgyMNTqJ6CUWU3yPK4,18914
|
36
37
|
squad/ci/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
38
|
squad/ci/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
39
|
squad/ci/management/commands/create_tuxsuite_boot_tests.py,sha256=JvjNusebLX71eyz9d-kaeCyekYSpzc1eXoeIqWK9ygo,4045
|
@@ -305,7 +306,7 @@ squad/frontend/project_settings.py,sha256=TtWz8h8Goeb3pccLy9jLUibeHqyqkdK8phL7_V
|
|
305
306
|
squad/frontend/queries.py,sha256=NxQF2woAf9A4Wk_ozHzZXOGmr2as-j7hqfvmsfJ-ojc,967
|
306
307
|
squad/frontend/setup.py,sha256=NF9VunY1HJGB2HsHJss-go7EGmqr__JASddxiBCvmeQ,169
|
307
308
|
squad/frontend/tests.py,sha256=PidrjaToK_Cks0s9Mc4i3Vh4UXOWoXTZlpnxQ2wWjHY,8740
|
308
|
-
squad/frontend/urls.py,sha256
|
309
|
+
squad/frontend/urls.py,sha256=-rxbsUlMyxldzoVKiVAOMAREqU8SOipy4CqBTlULuMQ,5055
|
309
310
|
squad/frontend/user_settings.py,sha256=U_i59iuylg98uH98K4ezPa2NY56idslBhn7MS6FguHQ,4976
|
310
311
|
squad/frontend/utils.py,sha256=DeH58CJUI1dovpQrj3a-DcxNzM0cxsnBDOF0mrC4Qws,1364
|
311
312
|
squad/frontend/views.py,sha256=4ld9n8pOS35sUKPifgCY4rLHL_Dmybfo_Jg0_Bo5sxs,27031
|
@@ -387,7 +388,7 @@ squad/frontend/templates/squad/knownissues.jinja2,sha256=RdQZ2AKgV97953eIuP-4IwN
|
|
387
388
|
squad/frontend/templates/squad/login.jinja2,sha256=NPp20MpmgoGxWOschCUxcZMJKdnkVhUmy8kpCgogPs0,2775
|
388
389
|
squad/frontend/templates/squad/metrics.jinja2,sha256=7oFBkTiGi3k1UtfR5x0RS961u6rsRCe_YcEXklA0iLA,3277
|
389
390
|
squad/frontend/templates/squad/project-nav.jinja2,sha256=AHN7r5TMvJ-NwEo_u3vlJg34J1njsuII32SgQuTfiwA,1526
|
390
|
-
squad/frontend/templates/squad/project.jinja2,sha256=
|
391
|
+
squad/frontend/templates/squad/project.jinja2,sha256=k2orc5C6Fxp_74utQAN1sa6XPOFMF2sC2D2y671pgSg,887
|
391
392
|
squad/frontend/templates/squad/test_history.jinja2,sha256=g_pHD4yQdfXK1D8-tTAPG4yzoqbDVUYm6ml2hANffp8,5869
|
392
393
|
squad/frontend/templates/squad/test_run.jinja2,sha256=smxFEC7XnHu28Wj7iC2WQrGjpuPiqsxASpflbyYGG_A,1176
|
393
394
|
squad/frontend/templates/squad/test_run_suite_metrics.jinja2,sha256=WGjlObw7ZTGoomTmON0O2QRHHdmEBOYf9xMSTWP83F4,1780
|
@@ -425,17 +426,17 @@ squad/plugins/__init__.py,sha256=9BSzy2jFIoDpWlhD7odPPrLdW4CC3btBhdFCvB651dM,152
|
|
425
426
|
squad/plugins/example.py,sha256=BKpwd315lHRIuNXJPteibpwfnI6C5eXYHYdFYBtVmsI,89
|
426
427
|
squad/plugins/gerrit.py,sha256=CqO2KnFQzu9utr_TQ-sGr1wg3ln0B-bS2-c0_i8T5-c,7009
|
427
428
|
squad/plugins/github.py,sha256=pdtLZw_7xNuzkaFvY_zWi0f2rsMlalXjKm7sz0eADz4,2429
|
428
|
-
squad/plugins/linux_log_parser.py,sha256=
|
429
|
+
squad/plugins/linux_log_parser.py,sha256=MB8ScFZycq70Rrm7IM_Cw95rMtqVS9w4RhS5HhBSpcE,3292
|
429
430
|
squad/plugins/lib/__init__.py,sha256=jzazbAvp2_ibblAs0cKZrmo9aR2EL3hKLyRDE008r2I,40
|
430
|
-
squad/plugins/lib/base_log_parser.py,sha256=
|
431
|
+
squad/plugins/lib/base_log_parser.py,sha256=OW6JkZ3PM5RiDkt9UZ7OFFpUIArCxFUaqovynzwBL1Y,6573
|
431
432
|
squad/run/__init__.py,sha256=ssE8GPAGFiK6V0WpZYowav6Zqsd63dfDMMYasNa1sQg,1410
|
432
433
|
squad/run/__main__.py,sha256=DOl8JOi4Yg7DdtwnUeGqtYBJ6P2k-D2psAEuYOjWr8w,66
|
433
434
|
squad/run/listener.py,sha256=jBeOQhPGb4EdIREB1QsCzYuumsfJ-TqJPd3nR-0m59g,200
|
434
435
|
squad/run/scheduler.py,sha256=CDJG3q5C0GuQuxwlMOfWTSSJpDdwbR6rzpbJfuA0xuw,277
|
435
436
|
squad/run/worker.py,sha256=jtML0h5qKDuSbpJ6_rpWP4MT_rsGA7a24AhwGxBquzk,594
|
436
|
-
squad-1.
|
437
|
-
squad-1.
|
438
|
-
squad-1.
|
439
|
-
squad-1.
|
440
|
-
squad-1.
|
441
|
-
squad-1.
|
437
|
+
squad-1.92.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
438
|
+
squad-1.92.dist-info/METADATA,sha256=_lF0F4lPQq88zJhPQ7M38zYoiWXjPWDKCOXr10WTKfg,1278
|
439
|
+
squad-1.92.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
440
|
+
squad-1.92.dist-info/entry_points.txt,sha256=J_jG3qnkoOHX4RFNGC0f83eJ4BSvK3pqLFkoF3HWfmA,195
|
441
|
+
squad-1.92.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
|
442
|
+
squad-1.92.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|