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

Sign up to get free protection for your applications and to get access to all the features.
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,
@@ -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"]}_length'
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
@@ -72,6 +72,9 @@ class Backend(object):
72
72
  def check_job_definition(self, definition):
73
73
  return True
74
74
 
75
+ def check_job_id(self, job_id):
76
+ return True
77
+
75
78
  def get_job_definition(self, test_job):
76
79
  return "sample job definition"
77
80
 
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'] == 'auto-login-action' and handle_lava_boot:
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
@@ -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
- response = Backend.get_session().request("GET", url)
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)
@@ -318,6 +334,11 @@ class Backend(BaseBackend):
318
334
  metadata_keys = settings.get('TEST_METADATA_KEYS', [])
319
335
  metadata = {k: results.get(k) for k in metadata_keys}
320
336
 
337
+ # Change environment name
338
+ if 'test_name' in results and results.get('test_name') is not None:
339
+ test_job.environment = results.get('test_name')
340
+ test_job.save()
341
+
321
342
  # Add extra metadata from metadata file if it exists
322
343
  self.update_metadata_from_file(results=results, metadata=metadata)
323
344
 
@@ -340,6 +361,8 @@ class Backend(BaseBackend):
340
361
  else:
341
362
  test_job.failure = 'sanity test failed'
342
363
 
364
+ self.add_skip_boot_test(tests, metadata)
365
+
343
366
  return status, completed, metadata, tests, metrics, logs
344
367
 
345
368
  # Fetch results even if the job fails, but has results
@@ -348,8 +371,14 @@ class Backend(BaseBackend):
348
371
 
349
372
  elif results['result'] == 'error':
350
373
  test_job.failure = 'tuxsuite infrastructure error'
374
+ self.add_skip_boot_test(tests, metadata)
351
375
  return 'Incomplete', completed, metadata, tests, metrics, logs
352
376
 
377
+ elif results['result'] == 'canceled':
378
+ test_job.failure = 'tuxsuite job canceled'
379
+ self.add_skip_boot_test(tests, metadata)
380
+ return 'Canceled', completed, metadata, tests, metrics, logs
381
+
353
382
  # If boot result is unkown, a retry is needed, otherwise, it either passed or failed
354
383
  if 'unknown' == results['results']['boot']:
355
384
  return None
@@ -384,6 +413,10 @@ class Backend(BaseBackend):
384
413
 
385
414
  def fetch(self, test_job):
386
415
  url = self.job_url(test_job)
416
+
417
+ settings = self.__resolve_settings__(test_job)
418
+ self.auth_token = settings.get('TUXSUITE_TOKEN', None)
419
+
387
420
  if test_job.input:
388
421
  results = self.fetch_from_results_input(test_job)
389
422
  test_job.input = None
@@ -393,11 +426,12 @@ class Backend(BaseBackend):
393
426
  if results.get('state') != 'finished':
394
427
  return None
395
428
 
396
- settings = self.__resolve_settings__(test_job)
397
-
398
429
  result_type = self.parse_job_id(test_job.job_id)[0]
399
430
  parse_results = getattr(self, f'parse_{result_type.lower()}_results')
400
- return parse_results(test_job, url, results, settings)
431
+ parsed = parse_results(test_job, url, results, settings)
432
+
433
+ self.auth_token = None
434
+ return parsed
401
435
 
402
436
  def job_url(self, test_job):
403
437
  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
- without_time = re.sub(r"^\[[^\]]+\]", "", without_numbers)
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
- ('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]*"),
9
+ ('exception', f'-+\[? cut here \]?-+.*?{tstamp}{pid}?\s+-+\[? end trace \w* \]?-+', f"\n{tstamp}{not_newline_or_plus}*"), # noqa
10
+ ('kasan', f'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KASAN:.*?\n*?{tstamp}{pid}?\s+=+', f"BUG: KASAN:{not_newline_or_plus}*"), # noqa
11
+ ('kcsan', f'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KCSAN:.*?=+', f"BUG: KCSAN:{not_newline_or_plus}*"), # noqa
12
+ ('kfence', f'{tstamp}{pid}?\s+=+\n{tstamp}{pid}?\s+BUG: KFENCE:.*?{tstamp}{pid}?\s+=+', f"BUG: KFENCE:{not_newline_or_plus}*"), # noqa
13
+ ('panic-multiline', f'{tstamp}{pid}?\s+Kernel panic - [^\n]+\n.*?-+\[? end Kernel panic - [^\n]+ \]?-*', f"Kernel {not_newline_or_plus}*"), # noqa
14
+ ('internal-error-oops', f'{tstamp}{pid}?\s+Internal error: Oops.*?-+\[? end trace \w+ \]?-+', f"Oops{not_newline_or_plus}*"), # noqa
12
15
  ]
13
16
 
14
17
  ONELINERS = [
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]*"),
18
+ ('oops', r'^[^\n]+Oops(?: -|:).*?$', f"Oops{not_newline_or_plus}*"), # noqa
19
+ ('fault', r'^[^\n]+Unhandled fault.*?$', f"Unhandled {not_newline_or_plus}*"), # noqa
20
+ ('warning', r'^[^\n]+WARNING:.*?$', f"WARNING:{not_newline_or_plus}*"), # noqa
21
+ ('bug', r'^[^\n]+(?: kernel BUG at|BUG:).*?$', f"BUG{not_newline_or_plus}*"), # noqa
22
+ ('invalid-opcode', r'^[^\n]+invalid opcode:.*?$', f"invalid opcode:{not_newline_or_plus}*"), # noqa
23
+ ('panic', r'Kernel panic - not syncing.*?$', f"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(r'(\[[ \d]+\.[ \d]+\] .*?)$', log, re.S | re.M)
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.90'
1
+ __version__ = '1.91'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: squad
3
- Version: 1.90
3
+ Version: 1.91
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=nx_cXfFOf3i5z7bmIzwkRhBgVHWtDBfK40-yo5hQUzw,14663
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=xCyEItfDbvgP8ttkgjhTpQfmnJCsuxMtkuIMOjALn54,21
13
+ squad/version.py,sha256=S8qXLXebPToWZIRCvA5VIWk_5c_MVZR49-YdwQ0ypzc,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=ymG-eMKXpJgrVUiZcqJW-dYZQKvm1LkdR3TUMe4OSoM,6943
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=0usJgOz14g1a71sdfjM-cOC8IGXkpE-5-TqpvJj-Oyk,1840
20
21
  squad/api/rest.py,sha256=ZtbK0c1BLPPnsX79XlKFVYONM_VJ0vacWZ2JsdCd4l0,77342
21
- squad/api/urls.py,sha256=rmsdaL1uOCVSZ5x1redup9RliICmijaBjRK5ObsTkG8,1343
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=9sPKndsGd5GDNPp35v-zfJWXZCbge-yXH3RBQGgTlPk,2340
33
- squad/ci/backend/lava.py,sha256=E4QE0XtAiqArzzx3YSv7_2qYUBs4aSw8JOz0AV0z9W8,33877
34
- squad/ci/backend/null.py,sha256=0CVylWELIZw3JyzCROB4XXAjgQUi15YjQz5caRfTNBo,5434
35
- squad/ci/backend/tuxsuite.py,sha256=-A4p5HpUWnIC-61os0vdJOfAGoO81szoLkSgzyaUt6c,17901
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=HTYLyJvtraHnkMKOjYix66bq1QV4m8bamNBahV5SZZw,19129
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=biWauxwXR5j9kOfrSUqkv1Iqz-elB2aNViS9_UFoLzQ,4882
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=JUmiYPlAo5hlUd-M32QmyjWmqe49NILySVFQ2a39Ftc,737
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=9vjt3z7ZDYofR90-JZv260phKMJyRv3sdksr8satgi0,2662
429
+ squad/plugins/linux_log_parser.py,sha256=WrDbyfupEcP1-E4ke9wjHiddio8sD5BFuEtF4AH0aXA,3274
429
430
  squad/plugins/lib/__init__.py,sha256=jzazbAvp2_ibblAs0cKZrmo9aR2EL3hKLyRDE008r2I,40
430
- squad/plugins/lib/base_log_parser.py,sha256=lfGoHWYTzOmWxME-qGArpJatzjUdy0uEII5eokDfP1Y,6189
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.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,,
437
+ squad-1.91.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
438
+ squad-1.91.dist-info/METADATA,sha256=U074123n6U3T9v0BY5GTaFCynFxFjL4HA7GEoLQjXX4,1278
439
+ squad-1.91.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
440
+ squad-1.91.dist-info/entry_points.txt,sha256=J_jG3qnkoOHX4RFNGC0f83eJ4BSvK3pqLFkoF3HWfmA,195
441
+ squad-1.91.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
442
+ squad-1.91.dist-info/RECORD,,
File without changes
File without changes