squad 1.93.2__py3-none-any.whl → 1.93.5__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
squad/ci/backend/fake.py CHANGED
@@ -42,7 +42,8 @@ class Backend(object):
42
42
  tests = {test: (random.randint(1, 10) <= 8) and "pass" or 'fail' for test in TESTS}
43
43
  metrics = {metric: random.random() for metric in METRICS}
44
44
  logs = "a fake log file\ndate: " + time.strftime('%c') + "\n"
45
- return (status, completed, metadata, tests, metrics, logs)
45
+ attachments = {}
46
+ return (status, completed, metadata, tests, metrics, logs, attachments)
46
47
 
47
48
  def listen(self):
48
49
  max_id = 0
squad/ci/backend/lava.py CHANGED
@@ -718,7 +718,8 @@ class Backend(BaseBackend):
718
718
  # automatically resubmit in some cases
719
719
  if error_type in ['Infrastructure', 'Job', 'Test']:
720
720
  self.__resubmit_job__(test_job, metadata)
721
- return (data[status_key], completed, job_metadata, results, metrics, self.__parse_log__(raw_logs))
721
+ attachments = {}
722
+ return (data[status_key], completed, job_metadata, results, metrics, self.__parse_log__(raw_logs), attachments)
722
723
 
723
724
  def __resubmit_job__(self, test_job, metadata):
724
725
  infra_messages_re_list = []
squad/ci/backend/null.py CHANGED
@@ -70,8 +70,9 @@ class Backend:
70
70
  proper id.
71
71
 
72
72
  The return value must be a tuple (status, completed, metadata, tests,
73
- metrics, logs), where status and logs are strings, metadata, tests and
74
- metrics are dictionaries, and completed is a boolean.
73
+ metrics, logs, attachments), where status and logs are strings,
74
+ metadata, tests and metrics are dictionaries, and completed is a
75
+ boolean.
75
76
 
76
77
  On errors, implementations can raise two classes of exceptions:
77
78
  * squad.ci.exceptions.FetchIssue, when there is an unrecoverable
@@ -17,6 +17,8 @@ from cryptography.hazmat.primitives import (
17
17
  serialization,
18
18
  )
19
19
 
20
+ from django.core.files.base import ContentFile
21
+
20
22
  from squad.ci.backend.null import Backend as BaseBackend
21
23
  from squad.ci.exceptions import FetchIssue, TemporaryFetchIssue
22
24
  from squad.ci.models import TestJob
@@ -284,7 +286,14 @@ class Backend(BaseBackend):
284
286
  except KeyError:
285
287
  raise FetchIssue('Missing duration from build results')
286
288
 
287
- return status, completed, metadata, tests, metrics, logs
289
+ attachment_list = ["config", "tuxmake_reproducer.sh", "tux_plan.yaml"]
290
+ attachments = {}
291
+ for name in attachment_list:
292
+ response = self.fetch_url(results['download_url'], name)
293
+ if response.ok:
294
+ attachments[name] = ContentFile(response.content)
295
+
296
+ return status, completed, metadata, tests, metrics, logs, attachments
288
297
 
289
298
  def parse_oebuild_results(self, test_job, job_url, results, settings):
290
299
  required_keys = ['download_url', 'result']
@@ -301,6 +310,7 @@ class Backend(BaseBackend):
301
310
  metadata['sources'] = sources
302
311
 
303
312
  # Create tests and metrics
313
+ attachments = {}
304
314
  tests = {}
305
315
  metrics = {}
306
316
  completed = True
@@ -308,7 +318,7 @@ class Backend(BaseBackend):
308
318
  tests['build/build'] = 'pass' if results['result'] == 'pass' else 'fail'
309
319
  logs = self.fetch_url(results['download_url'], 'build.log').text
310
320
 
311
- return status, completed, metadata, tests, metrics, logs
321
+ return status, completed, metadata, tests, metrics, logs, attachments
312
322
 
313
323
  def update_metadata_from_file(self, results, metadata):
314
324
  if "download_url" in results:
@@ -327,6 +337,7 @@ class Backend(BaseBackend):
327
337
  tests = {}
328
338
  metrics = {}
329
339
  logs = ''
340
+ attachments = {}
330
341
 
331
342
  # Pick up some metadata from results
332
343
  metadata_keys = settings.get('TEST_METADATA_KEYS', [])
@@ -361,7 +372,7 @@ class Backend(BaseBackend):
361
372
 
362
373
  self.add_skip_boot_test(tests, metadata)
363
374
 
364
- return status, completed, metadata, tests, metrics, logs
375
+ return status, completed, metadata, tests, metrics, logs, attachments
365
376
 
366
377
  # Fetch results even if the job fails, but has results
367
378
  if results['result'] == 'fail':
@@ -370,12 +381,12 @@ class Backend(BaseBackend):
370
381
  elif results['result'] == 'error':
371
382
  test_job.failure = 'tuxsuite infrastructure error'
372
383
  self.add_skip_boot_test(tests, metadata)
373
- return 'Incomplete', completed, metadata, tests, metrics, logs
384
+ return 'Incomplete', completed, metadata, tests, metrics, logs, attachments
374
385
 
375
386
  elif results['result'] == 'canceled':
376
387
  test_job.failure = 'tuxsuite job canceled'
377
388
  self.add_skip_boot_test(tests, metadata)
378
- return 'Canceled', completed, metadata, tests, metrics, logs
389
+ return 'Canceled', completed, metadata, tests, metrics, logs, attachments
379
390
 
380
391
  # If boot result is unkown, a retry is needed, otherwise, it either passed or failed
381
392
  if 'unknown' == results['results']['boot']:
@@ -384,6 +395,13 @@ class Backend(BaseBackend):
384
395
  # Retrieve TuxRun log
385
396
  logs = self.fetch_url(job_url + '/', 'logs?format=txt').text
386
397
 
398
+ attachment_list = ["reproducer", "tux_plan.yaml"]
399
+ attachments = {}
400
+ for name in attachment_list:
401
+ response = self.fetch_url(job_url + '/', name)
402
+ if response.ok:
403
+ attachments[name] = ContentFile(response.content)
404
+
387
405
  # Follow up the chain and retrieve build name
388
406
  self.set_build_name(test_job, job_url, results, metadata, settings)
389
407
 
@@ -407,7 +425,7 @@ class Backend(BaseBackend):
407
425
  # test_log = self.get_test_log(log_dict, test)
408
426
  tests[test_name] = result
409
427
 
410
- return status, completed, metadata, tests, metrics, logs
428
+ return status, completed, metadata, tests, metrics, logs, attachments
411
429
 
412
430
  def fetch(self, test_job):
413
431
  url = self.job_url(test_job)
squad/ci/models.py CHANGED
@@ -109,7 +109,7 @@ class Backend(models.Model):
109
109
  test_job.fetched_at = timezone.now()
110
110
  test_job.save()
111
111
 
112
- status, completed, metadata, tests, metrics, logs = results
112
+ status, completed, metadata, tests, metrics, logs, attachments = results
113
113
 
114
114
  if not completed:
115
115
  tests = {}
@@ -136,6 +136,7 @@ class Backend(models.Model):
136
136
  metrics_file=json.dumps(metrics),
137
137
  log_file=logs,
138
138
  completed=completed,
139
+ attachments=attachments,
139
140
  )
140
141
  test_job.testrun = testrun
141
142
  except (DuplicatedTestJob, InvalidMetadata) as exception:
@@ -36,7 +36,7 @@ from squad.core.data import JSONTestDataParser, JSONMetricDataParser
36
36
  from squad.core.statistics import geomean
37
37
  from squad.core.notification import Notification
38
38
  from squad.core.plugins import apply_plugins
39
- from squad.core.utils import join_name
39
+ from squad.core.utils import join_name, split_dict
40
40
  from rest_framework import status
41
41
  from jinja2 import TemplateSyntaxError
42
42
  from . import exceptions
@@ -46,7 +46,6 @@ from .notification import maybe_notify_project_status
46
46
  from .notification import notify_patch_build_created
47
47
  from .notification import notify_delayed_report_callback, notify_delayed_report_email
48
48
 
49
-
50
49
  test_parser = JSONTestDataParser
51
50
  metric_parser = JSONMetricDataParser
52
51
 
@@ -213,50 +212,131 @@ def get_suite(test_run, suite_name):
213
212
 
214
213
  class ParseTestRunData(object):
215
214
 
215
+ @staticmethod
216
+ def create_suites(project, suites_slugs):
217
+ # Insert/Get all suites at once, not likely to be a big list
218
+ SuiteMetadata.objects.bulk_create([
219
+ SuiteMetadata(
220
+ kind='suite',
221
+ suite=suite,
222
+ name='-',
223
+ ) for suite in suites_slugs
224
+ ], ignore_conflicts=True)
225
+
226
+ suites_metadata_ids = {
227
+ m.suite: m.id for m in SuiteMetadata.objects.filter(kind='suite', name='-', suite__in=suites_slugs)
228
+ }
229
+
230
+ Suite.objects.bulk_create([
231
+ Suite(
232
+ project=project,
233
+ slug=suite,
234
+ metadata_id=suites_metadata_ids[suite],
235
+ ) for suite in suites_slugs
236
+ ], ignore_conflicts=True)
237
+
238
+ return {s.slug: s.id for s in project.suites.filter(slug__in=suites_slugs)}
239
+
240
+ @staticmethod
241
+ def create_tests_batch(testrun, tests_batch, issues_by_full_name, suites_ids):
242
+
243
+ # Create SuiteMetadata in bulk
244
+ SuiteMetadata.objects.bulk_create([
245
+ SuiteMetadata(
246
+ suite=test['suite_slug'],
247
+ name=test['test_name'],
248
+ kind='test',
249
+ ) for test in tests_batch.values()
250
+ ], ignore_conflicts=True)
251
+
252
+ # We need the extra SELECT due to `ignore_conflicts=True` above
253
+ metadata_ids = {}
254
+ metadata_names = {}
255
+ metadatas = SuiteMetadata.objects.filter(
256
+ kind='test',
257
+ name__in=[
258
+ t['test_name'] for t in tests_batch.values()
259
+ ]
260
+ ).all()
261
+
262
+ for metadata in metadatas:
263
+ full_name = join_name(metadata.suite, metadata.name)
264
+ metadata_ids[full_name] = metadata.id
265
+ metadata_names[metadata.id] = full_name
266
+
267
+ # Create tests in batch
268
+ created_tests = Test.objects.bulk_create([
269
+ Test(
270
+ test_run=testrun,
271
+ suite_id=suites_ids[test['suite_slug']],
272
+ metadata_id=metadata_ids[full_name],
273
+ result=test['result'],
274
+ log=test['log'],
275
+ has_known_issues=test['has_known_issues'],
276
+ build_id=testrun.build_id,
277
+ environment_id=testrun.environment_id,
278
+ ) for full_name, test in tests_batch.items()
279
+ ])
280
+
281
+ # Attach known issues, if any
282
+ for test in created_tests:
283
+ test_full_name = metadata_names[test.metadata_id]
284
+ test_full_name = join_name(metadata.suite, metadata.name)
285
+ issues = issues_by_full_name.get(test_full_name, [])
286
+ if len(issues) > 0:
287
+ test.known_issues.add(*issues)
288
+
216
289
  @staticmethod
217
290
  def __call__(test_run):
218
291
  if test_run.data_processed:
219
292
  return
220
293
 
221
- issues = {}
222
- for issue in KnownIssue.active_by_environment(test_run.environment):
223
- issues.setdefault(issue.test_name, [])
224
- issues[issue.test_name].append(issue)
294
+ project = test_run.build.project
225
295
 
226
296
  # Issues' test_name should be interpreted as regexes
227
297
  # so compile them prior to matching against test names
228
298
  # The * character should be replaced by .*?, which is regex for "everything"
299
+ issues = defaultdict(list)
229
300
  issues_regex = {}
301
+ for issue in KnownIssue.active_by_environment(test_run.environment):
302
+ issues[issue.test_name].append(issue)
303
+
230
304
  for test_name_regex in issues.keys():
231
305
  pattern = re.escape(test_name_regex).replace('\\*', '.*?')
232
306
  regex = re.compile(pattern)
233
307
  issues_regex[regex] = issues[test_name_regex]
234
308
 
309
+ tests_details = {}
310
+ suites_slugs = set()
311
+ issues_by_full_name = {}
235
312
  for test in test_parser()(test_run.tests_file):
236
313
  # TODO: remove check below when test_name size changes in the schema
237
314
  if len(test['test_name']) > 256:
238
315
  continue
239
- suite = get_suite(
240
- test_run,
241
- test['group_name']
242
- )
243
- metadata, _ = SuiteMetadata.objects.get_or_create(suite=suite.slug, name=test['test_name'], kind='test')
244
- full_name = join_name(suite.slug, test['test_name'])
245
316
 
246
- test_issues = list(itertools.chain(*[issue for regex, issue in issues_regex.items() if regex.match(full_name)]))
317
+ full_name = join_name(test['group_name'], test['test_name'])
318
+ test_issues = list(itertools.chain(*[
319
+ issue for regex, issue in issues_regex.items()
320
+ if regex.match(full_name)
321
+ ]))
247
322
 
248
- test_obj = Test.objects.create(
249
- test_run=test_run,
250
- suite=suite,
251
- metadata=metadata,
252
- result=test['pass'],
253
- log=test['log'],
254
- has_known_issues=bool(test_issues),
255
- build=test_run.build,
256
- environment=test_run.environment,
257
- )
258
- for issue in test_issues:
259
- test_obj.known_issues.add(issue)
323
+ suites_slugs.add(test['group_name'])
324
+ tests_details[full_name] = {
325
+ 'suite_slug': test['group_name'],
326
+ 'test_name': test['test_name'],
327
+ 'result': test['pass'],
328
+ 'log': test['log'],
329
+ 'has_known_issues': bool(test_issues),
330
+ }
331
+
332
+ issues_by_full_name[full_name] = test_issues
333
+
334
+ suites_ids = ParseTestRunData.create_suites(project, suites_slugs)
335
+
336
+ # Insert tests in batches
337
+ batch_size = 1000
338
+ for batch in split_dict(tests_details, batch_size):
339
+ ParseTestRunData.create_tests_batch(test_run, batch, issues_by_full_name, suites_ids)
260
340
 
261
341
  for metric in metric_parser()(test_run.metrics_file):
262
342
  # TODO: remove check below when test_name size changes in the schema
@@ -534,7 +614,10 @@ def remove_delayed_reports():
534
614
  @transaction.atomic
535
615
  def cleanup_build(build_id):
536
616
  build = Build.objects.get(pk=build_id)
537
- BuildPlaceholder.objects.create(project=build.project, version=build.version)
617
+ build_placeholder, created = BuildPlaceholder.objects.get_or_create(project=build.project, version=build.version)
618
+ if not created: # Update build deletion date when placeholder already exists
619
+ build_placeholder.build_deleted_at = timezone.now()
620
+ build_placeholder.save()
538
621
  build.delete()
539
622
 
540
623
 
squad/settings.py CHANGED
@@ -263,7 +263,7 @@ USE_TZ = True
263
263
  # http://whitenoise.evans.io/en/stable/django.html
264
264
  STATIC_URL = '/static/'
265
265
  STATIC_ROOT = os.getenv('SQUAD_STATIC_DIR', os.path.join(DATA_DIR, 'static'))
266
- STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
266
+ STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
267
267
 
268
268
  # Squad default storage directory
269
269
  MEDIA_ROOT = os.getenv('SQUAD_STORAGE_DIR', 'storage')
squad/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '1.93.2'
1
+ __version__ = '1.93.5'
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: squad
3
- Version: 1.93.2
3
+ Version: 1.93.5
4
4
  Summary: Software Quality Dashboard
5
5
  Home-page: https://github.com/Linaro/squad
6
6
  Author: Antonio Terceiro
@@ -12,15 +12,15 @@ 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<5,>=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
22
  Requires-Dist: django-simple-history>3.0
23
- Requires-Dist: django-filter>=2.0
23
+ Requires-Dist: django_filter>=2.0
24
24
  Requires-Dist: djangorestframework>=3.9.2
25
25
  Requires-Dist: djangorestframework-filters>=1.0.0.dev0
26
26
  Requires-Dist: drf-extensions
@@ -30,7 +30,8 @@ Requires-Dist: importlib-metadata>3
30
30
  Requires-Dist: Jinja2==3.0.3
31
31
  Requires-Dist: Markdown
32
32
  Requires-Dist: msgpack>=0.5.0
33
- Requires-Dist: python-dateutil
33
+ Requires-Dist: psycopg2-binary
34
+ Requires-Dist: python_dateutil
34
35
  Requires-Dist: PyYAML>=5.1
35
36
  Requires-Dist: PyJWT
36
37
  Requires-Dist: pyzmq
@@ -41,6 +42,14 @@ Requires-Dist: svgwrite
41
42
  Requires-Dist: whitenoise
42
43
  Provides-Extra: postgres
43
44
  Requires-Dist: psycopg2-binary; extra == "postgres"
45
+ Dynamic: author
46
+ Dynamic: author-email
47
+ Dynamic: description
48
+ Dynamic: home-page
49
+ Dynamic: license
50
+ Dynamic: platform
51
+ Dynamic: provides-extra
52
+ Dynamic: requires-dist
53
+ Dynamic: summary
44
54
 
45
55
  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=0MZ48SV_7CTrLMik2ubWf8-ROQiFju6CKnUC3iR8KAc,14800
10
+ squad/settings.py,sha256=sLSts3qmUgXmpLmzhOBrTmFcSuBRMugo0hDY0uw4z3A,14792
11
11
  squad/socialaccount.py,sha256=vySqPwQ3qVVpahuJ-Snln8K--yzRL3bw4Nx27AsB39A,789
12
12
  squad/urls.py,sha256=JiEfVW8YlzLPE52c2aHzdn5kVVKK4o22w8h5KOA6QhQ,2776
13
- squad/version.py,sha256=Ns6X_xZX0DSBTwFkFhX5az9zHmzZ77cA_N1E2Ske2c8,23
13
+ squad/version.py,sha256=gO4_Q_SHYadC2ihq6k_bt3qK83b7EcaZ1GraTBmNnp4,23
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
@@ -26,14 +26,14 @@ squad/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  squad/ci/admin.py,sha256=7yB-6F0cvt0NVvzGOTlZCyGPV_YHarmbKJZTTzataT4,2255
27
27
  squad/ci/apps.py,sha256=6OVnzTdJkxdqEJnKWYE9dZgUcc29_T1LrDw41cK4EQk,139
28
28
  squad/ci/exceptions.py,sha256=a1sccygniTYDSQi7FRn_6doapddFFiMf55AwGUh5Y80,227
29
- squad/ci/models.py,sha256=wR9FMBdjQgtEP3ga9CY6npFr5fUIeVpnfAhNa2xqM00,15897
29
+ squad/ci/models.py,sha256=_nXVB7BKAzCxSq1WGq1cXkBkhyX9CyRZcaOKQ7o4C1c,15951
30
30
  squad/ci/tasks.py,sha256=P0NYjLuyUViTpO1jZMuRVREbFDCccrMCZDw5E4pt928,3882
31
31
  squad/ci/utils.py,sha256=38zHpw8xkZDSFlkG-2BwSK6AkcddK9OkN9LXuQ3SHR0,97
32
32
  squad/ci/backend/__init__.py,sha256=yhpotXT9F4IdAOXvGQ3-17eOHAFwoaqf9SnMX17ab30,534
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=pFcNdcHpFzalHPQhbSY6ryOci_PU3LFsaNjSsgjbqGg,18676
33
+ squad/ci/backend/fake.py,sha256=MTsxGZuihJOd39X8ysJhiYp4A0R46lyhdkjoTgFm6a8,2435
34
+ squad/ci/backend/lava.py,sha256=Atb2AgV5OSejtN1LyE7xONTFvJCdDoh9vK9RO_P7L2g,34077
35
+ squad/ci/backend/null.py,sha256=oZx3OofUKSuLOYS_GZkteGaD6JOEEkdknVmi4_cxDOQ,5645
36
+ squad/ci/backend/tuxsuite.py,sha256=UP-XZnoC_QNQpPwh9uS827Xg_8Uqef_7zFSZjIq2OJM,19431
37
37
  squad/ci/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  squad/ci/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  squad/ci/management/commands/create_tuxsuite_boot_tests.py,sha256=JvjNusebLX71eyz9d-kaeCyekYSpzc1eXoeIqWK9ygo,4045
@@ -279,7 +279,7 @@ squad/core/migrations/0167_add_project_datetime.py,sha256=VUBG-qsAhh2f2NXaHOqfX9
279
279
  squad/core/migrations/0168_add_group_settings.py,sha256=5UdylfMMNavTL0KXkjPSiEMhSisGWXbhUXQSzfK29Ck,462
280
280
  squad/core/migrations/0169_userpreferences.py,sha256=FwYv9RWxMWdQ2lXJMgi-Xc6XBB5Kp-_YTAOr9GVq1To,1098
281
281
  squad/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
282
- squad/core/tasks/__init__.py,sha256=wKjyFw0JXiEDY6PEaKx3ureiSNIQFL8lHH4JIOMjlF8,18677
282
+ squad/core/tasks/__init__.py,sha256=M62agsx05m0IvCcT5Qm0ocYp3LN04lZo067zcz9Paac,21725
283
283
  squad/core/tasks/exceptions.py,sha256=n4cbmJFBdA6KWsGiTbfN9DyYGbJpk0DjR0UneEYw_W0,931
284
284
  squad/core/tasks/notification.py,sha256=6ZyTbUQZPITPP-4r9MUON7x-NbwvDBG8YeabM6fsjzA,4915
285
285
  squad/core/templates/squad/notification/base.jinja2,sha256=AbtQioEHV5DJBW4Etsu0-DQXd_8tQCnLejzgbDGDW7s,3413
@@ -436,9 +436,9 @@ squad/run/__main__.py,sha256=DOl8JOi4Yg7DdtwnUeGqtYBJ6P2k-D2psAEuYOjWr8w,66
436
436
  squad/run/listener.py,sha256=jBeOQhPGb4EdIREB1QsCzYuumsfJ-TqJPd3nR-0m59g,200
437
437
  squad/run/scheduler.py,sha256=CDJG3q5C0GuQuxwlMOfWTSSJpDdwbR6rzpbJfuA0xuw,277
438
438
  squad/run/worker.py,sha256=jtML0h5qKDuSbpJ6_rpWP4MT_rsGA7a24AhwGxBquzk,594
439
- squad-1.93.2.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
440
- squad-1.93.2.dist-info/METADATA,sha256=5w3XwKhCwVBCg7KCGYbi3CaE6yLuQRdCpp0zT5uTtF4,1280
441
- squad-1.93.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
442
- squad-1.93.2.dist-info/entry_points.txt,sha256=J_jG3qnkoOHX4RFNGC0f83eJ4BSvK3pqLFkoF3HWfmA,195
443
- squad-1.93.2.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
444
- squad-1.93.2.dist-info/RECORD,,
439
+ squad-1.93.5.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
440
+ squad-1.93.5.dist-info/METADATA,sha256=xHrEkrYBdsMg8JBOqCo8XfYKKvtUpBrqdDFJ8_RIXEw,1490
441
+ squad-1.93.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
442
+ squad-1.93.5.dist-info/entry_points.txt,sha256=apCDQydHZtvqV334ql6NhTJUAJeZRdtAm0TVcbbAi5Q,194
443
+ squad-1.93.5.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
444
+ squad-1.93.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.1)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -4,4 +4,3 @@ 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
-