squad 1.93.2__py3-none-any.whl → 1.93.5__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/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
-