squad 1.66__py3-none-any.whl → 1.67__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 +42 -0
- squad/api/urls.py +1 -0
- squad/ci/backend/fake.py +3 -0
- squad/ci/backend/null.py +19 -0
- squad/ci/backend/tuxsuite.py +91 -2
- squad/ci/migrations/0029_create_testjob_results_input.py +22 -0
- squad/ci/models.py +29 -0
- squad/plugins/github.py +6 -5
- squad/version.py +1 -1
- {squad-1.66.dist-info → squad-1.67.dist-info}/METADATA +1 -1
- {squad-1.66.dist-info → squad-1.67.dist-info}/RECORD +15 -14
- {squad-1.66.dist-info → squad-1.67.dist-info}/COPYING +0 -0
- {squad-1.66.dist-info → squad-1.67.dist-info}/WHEEL +0 -0
- {squad-1.66.dist-info → squad-1.67.dist-info}/entry_points.txt +0 -0
- {squad-1.66.dist-info → squad-1.67.dist-info}/top_level.txt +0 -0
squad/api/ci.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import json
|
2
|
+
|
1
3
|
from django.http import HttpResponse, HttpResponseBadRequest
|
2
4
|
from django.shortcuts import get_object_or_404
|
3
5
|
from django.views.decorators.csrf import csrf_exempt
|
@@ -8,6 +10,7 @@ from squad.ci.exceptions import SubmissionIssue
|
|
8
10
|
from squad.ci.tasks import submit, fetch
|
9
11
|
from squad.ci.models import Backend, TestJob
|
10
12
|
from squad.core.utils import log_addition
|
13
|
+
from squad.core.models import Project
|
11
14
|
|
12
15
|
|
13
16
|
@require_http_methods(["POST"])
|
@@ -149,3 +152,42 @@ def resubmit_job(request, test_job_id, method='resubmit'):
|
|
149
152
|
@csrf_exempt
|
150
153
|
def force_resubmit_job(request, test_job_id):
|
151
154
|
return resubmit_job(request, test_job_id, method='force_resubmit')
|
155
|
+
|
156
|
+
|
157
|
+
@require_http_methods(["POST"])
|
158
|
+
@csrf_exempt
|
159
|
+
def fetch_job(request, group_slug, project_slug, version, environment_slug, backend_name):
|
160
|
+
try:
|
161
|
+
backend = Backend.objects.get(name=backend_name)
|
162
|
+
except Backend.DoesNotExist:
|
163
|
+
return HttpResponseBadRequest("requested backend does not exist")
|
164
|
+
|
165
|
+
if not backend.supports_callbacks():
|
166
|
+
return HttpResponseBadRequest("requested backend does not support callbacks")
|
167
|
+
|
168
|
+
try:
|
169
|
+
project = Project.objects.get(slug=project_slug, group__slug=group_slug)
|
170
|
+
except Project.DoesNotExist:
|
171
|
+
return HttpResponseBadRequest("group/project does not exist")
|
172
|
+
|
173
|
+
try:
|
174
|
+
backend.validate_callback(request, project)
|
175
|
+
except Exception as e:
|
176
|
+
return HttpResponseBadRequest(f"request is not valid for this backend: {e}")
|
177
|
+
|
178
|
+
environment, _ = project.environments.get_or_create(slug=environment_slug)
|
179
|
+
build, _ = project.builds.get_or_create(version=version)
|
180
|
+
|
181
|
+
try:
|
182
|
+
payload = json.loads(request.body)
|
183
|
+
except Exception as e:
|
184
|
+
return HttpResponseBadRequest(f"payload failed to parse as json: {e}")
|
185
|
+
|
186
|
+
try:
|
187
|
+
test_job = backend.process_callback(payload, build, environment)
|
188
|
+
except Exception as e:
|
189
|
+
return HttpResponseBadRequest(f"malformed callback payload: {e}")
|
190
|
+
|
191
|
+
fetch.delay(test_job.id)
|
192
|
+
|
193
|
+
return HttpResponse(test_job.id, status=201)
|
squad/api/urls.py
CHANGED
@@ -21,6 +21,7 @@ urlpatterns = [
|
|
21
21
|
url(r'^submit/(%s)/(%s)/(%s)/(%s)' % (group_slug_pattern, slug_pattern, slug_pattern, slug_pattern), views.add_test_run),
|
22
22
|
url(r'^submitjob/(%s)/(%s)/(%s)/(%s)' % (group_slug_pattern, slug_pattern, slug_pattern, slug_pattern), ci.submit_job),
|
23
23
|
url(r'^watchjob/(%s)/(%s)/(%s)/(%s)' % (group_slug_pattern, slug_pattern, slug_pattern, slug_pattern), ci.watch_job),
|
24
|
+
url(r'^fetchjob/(%s)/(%s)/(%s)/(%s)/(%s)' % (group_slug_pattern, slug_pattern, slug_pattern, slug_pattern, slug_pattern), ci.fetch_job),
|
24
25
|
url(r'^data/(%s)/(%s)' % (group_slug_pattern, slug_pattern), data.get),
|
25
26
|
url(r'^resubmit/([0-9]+)', ci.resubmit_job),
|
26
27
|
url(r'^forceresubmit/([0-9]+)', ci.force_resubmit_job),
|
squad/ci/backend/fake.py
CHANGED
squad/ci/backend/null.py
CHANGED
@@ -104,6 +104,25 @@ class Backend(object):
|
|
104
104
|
"""
|
105
105
|
raise NotImplementedError
|
106
106
|
|
107
|
+
def supports_callbacks(self):
|
108
|
+
"""
|
109
|
+
Returns True if this backend supports callbacks, False otherwise
|
110
|
+
"""
|
111
|
+
return False
|
112
|
+
|
113
|
+
def validate_callback(self, request, project):
|
114
|
+
"""
|
115
|
+
Raises an exception in case the request does not pass the validation
|
116
|
+
"""
|
117
|
+
raise NotImplementedError
|
118
|
+
|
119
|
+
def process_callback(self, json_payload, build, environment, backend):
|
120
|
+
"""
|
121
|
+
Returns a test_job if processing callback's payload fine, or raise exceptions
|
122
|
+
if something isn't right
|
123
|
+
"""
|
124
|
+
raise NotImplementedError
|
125
|
+
|
107
126
|
def format_message(self, msg):
|
108
127
|
if self.data and hasattr(self.data, "name"):
|
109
128
|
return self.data.name + ': ' + msg
|
squad/ci/backend/tuxsuite.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import base64
|
1
2
|
import hashlib
|
2
3
|
import logging
|
3
4
|
import re
|
@@ -8,8 +9,15 @@ import json
|
|
8
9
|
from functools import reduce
|
9
10
|
from urllib.parse import urljoin
|
10
11
|
|
12
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
13
|
+
from cryptography.hazmat.primitives import (
|
14
|
+
hashes,
|
15
|
+
serialization,
|
16
|
+
)
|
17
|
+
|
11
18
|
from squad.ci.backend.null import Backend as BaseBackend
|
12
19
|
from squad.ci.exceptions import FetchIssue, TemporaryFetchIssue
|
20
|
+
from squad.ci.models import TestJob
|
13
21
|
|
14
22
|
|
15
23
|
logger = logging.getLogger('squad.ci.backend.tuxsuite')
|
@@ -83,16 +91,43 @@ class Backend(BaseBackend):
|
|
83
91
|
# The regex below is supposed to find only one match
|
84
92
|
return matches[0]
|
85
93
|
|
94
|
+
def generate_job_id(self, result_type, result):
|
95
|
+
"""
|
96
|
+
The job id for TuxSuite results is generated using 3 pieces of info:
|
97
|
+
1. If it's either "BUILD" or "TEST" result;
|
98
|
+
2. The TuxSuite project. Ex: "linaro/anders"
|
99
|
+
3. The ksuid of the object. Ex: "1yPYGaOEPNwr2pfqBgONY43zORp"
|
100
|
+
|
101
|
+
A couple examples for job_id are:
|
102
|
+
- BUILD:linaro@anders#1yPYGaOEPNwr2pCqBgONY43zORq
|
103
|
+
- TEST:arm@bob#1yPYGaOEPNwr2pCqBgONY43zORp
|
104
|
+
|
105
|
+
Then it's up to SQUAD's TuxSuite backend to parse the job_id
|
106
|
+
and fetch results properly.
|
107
|
+
"""
|
108
|
+
_type = "TEST" if result_type == "test" else "BUILD"
|
109
|
+
project = result["project"].replace("/", "@")
|
110
|
+
uid = result["uid"]
|
111
|
+
return f"{_type}:{project}#{uid}"
|
112
|
+
|
86
113
|
def fetch_url(self, *urlbits):
|
87
114
|
url = reduce(urljoin, urlbits)
|
88
115
|
|
89
116
|
try:
|
90
117
|
response = requests.get(url)
|
91
118
|
except Exception as e:
|
92
|
-
raise TemporaryFetchIssue(f"Can't retrieve from {url}:
|
119
|
+
raise TemporaryFetchIssue(f"Can't retrieve from {url}: {e}")
|
93
120
|
|
94
121
|
return response
|
95
122
|
|
123
|
+
def fetch_from_results_input(self, test_job):
|
124
|
+
try:
|
125
|
+
return json.loads(test_job.input)
|
126
|
+
except Exception as e:
|
127
|
+
logger.error(f"Can't parse results from job's input: {e}")
|
128
|
+
|
129
|
+
return None
|
130
|
+
|
96
131
|
def parse_build_results(self, test_job, job_url, results, settings):
|
97
132
|
required_keys = ['build_status', 'warnings_count', 'download_url', 'retry']
|
98
133
|
self.__check_required_keys__(required_keys, results)
|
@@ -179,6 +214,9 @@ class Backend(BaseBackend):
|
|
179
214
|
_, _, test_id = self.parse_job_id(test_job.job_id)
|
180
215
|
build_id = results['waiting_for']
|
181
216
|
build_url = job_url.replace(test_id, build_id).replace('tests', 'builds')
|
217
|
+
|
218
|
+
# TODO: check if we can save a few seconds by querying a testjob that
|
219
|
+
# already contains build results
|
182
220
|
build_metadata = self.fetch_url(build_url).json()
|
183
221
|
|
184
222
|
build_metadata_keys = settings.get('TEST_BUILD_METADATA_KEYS', [])
|
@@ -211,7 +249,12 @@ class Backend(BaseBackend):
|
|
211
249
|
|
212
250
|
def fetch(self, test_job):
|
213
251
|
url = self.job_url(test_job)
|
214
|
-
|
252
|
+
if test_job.input:
|
253
|
+
results = self.fetch_from_results_input(test_job)
|
254
|
+
test_job.input = None
|
255
|
+
else:
|
256
|
+
results = self.fetch_url(url).json()
|
257
|
+
|
215
258
|
if results.get('state') != 'finished':
|
216
259
|
return None
|
217
260
|
|
@@ -253,3 +296,49 @@ class Backend(BaseBackend):
|
|
253
296
|
url = urljoin(self.data.url, endpoint)
|
254
297
|
response = requests.post(url)
|
255
298
|
return response.status_code == 200
|
299
|
+
|
300
|
+
def supports_callbacks(self):
|
301
|
+
return True
|
302
|
+
|
303
|
+
def validate_callback(self, request, project):
|
304
|
+
signature = request.headers.get("x-tux-payload-signature", None)
|
305
|
+
if signature is None:
|
306
|
+
raise Exception("tuxsuite request is missing signature headers")
|
307
|
+
|
308
|
+
public_key = project.get_setting("TUXSUITE_PUBLIC_KEY")
|
309
|
+
if public_key is None:
|
310
|
+
raise Exception("missing tuxsuite public key for this project")
|
311
|
+
|
312
|
+
payload = request.body
|
313
|
+
signature = base64.urlsafe_b64decode(signature)
|
314
|
+
key = serialization.load_ssh_public_key(public_key.encode("ascii"))
|
315
|
+
key.verify(
|
316
|
+
signature,
|
317
|
+
payload,
|
318
|
+
ec.ECDSA(hashes.SHA256()),
|
319
|
+
)
|
320
|
+
|
321
|
+
def process_callback(self, json_payload, build, environment, backend):
|
322
|
+
if "kind" not in json_payload or "status" not in json_payload:
|
323
|
+
raise Exception("`kind` and `status` are required in the payload")
|
324
|
+
|
325
|
+
kind = json_payload["kind"]
|
326
|
+
status = json_payload["status"]
|
327
|
+
job_id = self.generate_job_id(kind, status)
|
328
|
+
try:
|
329
|
+
# Tuxsuite's job id DO NOT repeat, like ever
|
330
|
+
testjob = TestJob.objects.get(job_id=job_id, target_build=build, environment=environment.slug)
|
331
|
+
except TestJob.DoesNotExist:
|
332
|
+
testjob = TestJob.objects.create(
|
333
|
+
backend=backend,
|
334
|
+
target=build.project,
|
335
|
+
target_build=build,
|
336
|
+
environment=environment.slug,
|
337
|
+
submitted=True,
|
338
|
+
job_id=job_id
|
339
|
+
)
|
340
|
+
|
341
|
+
# Saves the input so it can be processed by the queue
|
342
|
+
testjob.input = json.dumps(status)
|
343
|
+
|
344
|
+
return testjob
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Generated by Django 4.2 on 2023-04-26 16:50
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('ci', '0028_create_testjob_indexes'),
|
11
|
+
]
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.CreateModel(
|
15
|
+
name='ResultsInput',
|
16
|
+
fields=[
|
17
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
18
|
+
('text', models.TextField(blank=True, null=True)),
|
19
|
+
('test_job', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='results_input', to='ci.testjob')),
|
20
|
+
],
|
21
|
+
),
|
22
|
+
]
|
squad/ci/models.py
CHANGED
@@ -164,6 +164,15 @@ class Backend(models.Model):
|
|
164
164
|
def check_job_definition(self, definition):
|
165
165
|
return self.get_implementation().check_job_definition(definition)
|
166
166
|
|
167
|
+
def supports_callbacks(self):
|
168
|
+
return self.get_implementation().supports_callbacks()
|
169
|
+
|
170
|
+
def validate_callback(self, request, project):
|
171
|
+
self.get_implementation().validate_callback(request, project)
|
172
|
+
|
173
|
+
def process_callback(self, payload, build, environment):
|
174
|
+
return self.get_implementation().process_callback(payload, build, environment, self)
|
175
|
+
|
167
176
|
def __str__(self):
|
168
177
|
return '%s (%s)' % (self.name, self.implementation_type)
|
169
178
|
|
@@ -241,6 +250,21 @@ class TestJob(models.Model):
|
|
241
250
|
return self.backend.get_implementation().job_url(self)
|
242
251
|
return None
|
243
252
|
|
253
|
+
@property
|
254
|
+
def input(self):
|
255
|
+
try:
|
256
|
+
return self.results_input.text
|
257
|
+
except ResultsInput.DoesNotExist:
|
258
|
+
return None
|
259
|
+
|
260
|
+
@input.setter
|
261
|
+
def input(self, value):
|
262
|
+
if value:
|
263
|
+
self.results_input = ResultsInput(text=value)
|
264
|
+
self.results_input.save()
|
265
|
+
else:
|
266
|
+
self.results_input.delete()
|
267
|
+
|
244
268
|
def resubmit(self):
|
245
269
|
ret_value = False
|
246
270
|
if self.can_resubmit:
|
@@ -311,3 +335,8 @@ class TestJob(models.Model):
|
|
311
335
|
indexes = [
|
312
336
|
models.Index(fields=['submitted', 'fetched']),
|
313
337
|
]
|
338
|
+
|
339
|
+
|
340
|
+
class ResultsInput(models.Model):
|
341
|
+
test_job = models.OneToOneField(TestJob, related_name='results_input', on_delete=models.CASCADE, null=True)
|
342
|
+
text = models.TextField(null=True, blank=True)
|
squad/plugins/github.py
CHANGED
@@ -4,6 +4,7 @@ from django.conf import settings
|
|
4
4
|
from squad.core.models import ProjectStatus
|
5
5
|
from squad.core.plugins import Plugin as BasePlugin
|
6
6
|
from squad.frontend.templatetags.squad import project_url
|
7
|
+
from urllib.parse import urljoin
|
7
8
|
|
8
9
|
|
9
10
|
def build_url(build):
|
@@ -11,7 +12,6 @@ def build_url(build):
|
|
11
12
|
|
12
13
|
|
13
14
|
class Plugin(BasePlugin):
|
14
|
-
|
15
15
|
@staticmethod
|
16
16
|
def __github_post__(build, endpoint, payload):
|
17
17
|
api_url = build.patch_source.url
|
@@ -22,10 +22,11 @@ class Plugin(BasePlugin):
|
|
22
22
|
"Authorization": "token %s" % api_token,
|
23
23
|
}
|
24
24
|
|
25
|
-
url =
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
url = urljoin(
|
26
|
+
api_url, endpoint.format(
|
27
|
+
owner=owner,
|
28
|
+
repository=repository,
|
29
|
+
commit=commit)
|
29
30
|
)
|
30
31
|
return requests.post(url, headers=headers, json=payload)
|
31
32
|
|
squad/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '1.
|
1
|
+
__version__ = '1.67'
|
@@ -10,29 +10,29 @@ squad/manage.py,sha256=Z-LXT67p0R-IzwJ9fLIAacEZmU0VUjqDOSg7j2ZSxJ4,1437
|
|
10
10
|
squad/settings.py,sha256=EzUgd8Egzp6GrZd-Vx6OlAorhlVavT84bxvsqD58oZg,14051
|
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=cyth8cQRyIgeHOgLyFVNnADaoVSn0Rrc5rl9Qs7F8gs,21
|
14
14
|
squad/wsgi.py,sha256=SF8T0cQ0OPVyuYjO5YXBIQzvSXQHV0M2BTmd4gP1rPs,387
|
15
15
|
squad/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
squad/api/apps.py,sha256=Trk72p-iV1uGn0o5mdJn5HARUoHGbfgO49jwXvpkmdQ,141
|
17
|
-
squad/api/ci.py,sha256=
|
17
|
+
squad/api/ci.py,sha256=7eJvUwUbQ7sJzUxcxYURDV1tOT1ouqo0M3L7ojWVAFE,6536
|
18
18
|
squad/api/data.py,sha256=obKDV0-neEvj5lPF9VED2gy_hpfhGtLJABYvSY38ing,2379
|
19
19
|
squad/api/filters.py,sha256=Zvp8DCJmiNquFWqvfVseEAAMYYPiT95RUjqKdzcqSnw,6917
|
20
20
|
squad/api/rest.py,sha256=aVHqq-mXLctPjro29miLqWmR1gMEAIMBeXdbZX-7rQI,74991
|
21
|
-
squad/api/urls.py,sha256=
|
21
|
+
squad/api/urls.py,sha256=rmsdaL1uOCVSZ5x1redup9RliICmijaBjRK5ObsTkG8,1343
|
22
22
|
squad/api/utils.py,sha256=Sa8QFId3_oSqD2UOoY3Kuh54LLDLPNMq2sub5ktd6Fs,1160
|
23
23
|
squad/api/views.py,sha256=yGLUp6RtNI5vuae6cOStMuUpSia46LcEVam3eMXmEqY,3885
|
24
24
|
squad/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
25
|
squad/ci/admin.py,sha256=7yB-6F0cvt0NVvzGOTlZCyGPV_YHarmbKJZTTzataT4,2255
|
26
26
|
squad/ci/apps.py,sha256=6OVnzTdJkxdqEJnKWYE9dZgUcc29_T1LrDw41cK4EQk,139
|
27
27
|
squad/ci/exceptions.py,sha256=a1sccygniTYDSQi7FRn_6doapddFFiMf55AwGUh5Y80,227
|
28
|
-
squad/ci/models.py,sha256=
|
28
|
+
squad/ci/models.py,sha256=RoXJY4Xgu3-YA4FtcK-kem8HR8paqOb5648NByAlzDo,12325
|
29
29
|
squad/ci/tasks.py,sha256=yrtxfPuYEqqGCDYRwLz2XyIp9a7LJ-K6Zm3VS1ymdZk,2728
|
30
30
|
squad/ci/utils.py,sha256=38zHpw8xkZDSFlkG-2BwSK6AkcddK9OkN9LXuQ3SHR0,97
|
31
31
|
squad/ci/backend/__init__.py,sha256=yhpotXT9F4IdAOXvGQ3-17eOHAFwoaqf9SnMX17ab30,534
|
32
|
-
squad/ci/backend/fake.py,sha256=
|
32
|
+
squad/ci/backend/fake.py,sha256=zzOXGesDCW9xiMQvXGD_jqCQF32yEd7hPM8DgfZxUok,2159
|
33
33
|
squad/ci/backend/lava.py,sha256=InqmLjf_txjKpMDpmsYbkPJEhOVADBGnTzwaA76fr1Y,33076
|
34
|
-
squad/ci/backend/null.py,sha256=
|
35
|
-
squad/ci/backend/tuxsuite.py,sha256=
|
34
|
+
squad/ci/backend/null.py,sha256=vP77Xj7roruehSjX8fJs7xK2aWxgaUA2id3P8nHNrEY,4949
|
35
|
+
squad/ci/backend/tuxsuite.py,sha256=m4Hgpb0twM4icOrPdEHUgvezmVq9DDvtEloojit_2pg,12759
|
36
36
|
squad/ci/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
squad/ci/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
squad/ci/management/commands/create_tuxsuite_boot_tests.py,sha256=JvjNusebLX71eyz9d-kaeCyekYSpzc1eXoeIqWK9ygo,4045
|
@@ -66,6 +66,7 @@ squad/ci/migrations/0025_backend_listen_enabled.py,sha256=t7Tx7URhsz-Q4GGuoKYNJY
|
|
66
66
|
squad/ci/migrations/0026_job_start_end_time.py,sha256=18swRRnDXIhHpr2ykMFns04VS1_Fbs7Zdc0HOrikSSY,543
|
67
67
|
squad/ci/migrations/0027_add_tuxsuite_implementation_type.py,sha256=5Max0wE_jnU5FXdJzbrPGr73N0pdZlA321pZKE-yDtE,502
|
68
68
|
squad/ci/migrations/0028_create_testjob_indexes.py,sha256=VYT2wmrvjHJLOazBXZb15vKzGJn8jgKCVNNSSj4Nct4,612
|
69
|
+
squad/ci/migrations/0029_create_testjob_results_input.py,sha256=Ax0jBSthCEDWUp7tLfdCgwa0nXgC2JDXOdQ26Kl9_fA,712
|
69
70
|
squad/ci/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
71
|
squad/ci/templates/squad/ci/testjob_resubmit.html.jinja2,sha256=ys8zb4E2CJSsi4-uP57glkOFhWVK5x8-yUZcDdi81sY,1693
|
71
72
|
squad/ci/templates/squad/ci/testjob_resubmit.txt.jinja2,sha256=yKYtixlO5zWh6ChD_mk8yDQly_iQae5slB7a0FwNPv0,610
|
@@ -422,16 +423,16 @@ squad/frontend/templatetags/squad.py,sha256=YSDgZu8e6e99rFhHudzVKh-6cIfoeq5fC0lW
|
|
422
423
|
squad/plugins/__init__.py,sha256=9BSzy2jFIoDpWlhD7odPPrLdW4CC3btBhdFCvB651dM,152
|
423
424
|
squad/plugins/example.py,sha256=BKpwd315lHRIuNXJPteibpwfnI6C5eXYHYdFYBtVmsI,89
|
424
425
|
squad/plugins/gerrit.py,sha256=CqO2KnFQzu9utr_TQ-sGr1wg3ln0B-bS2-c0_i8T5-c,7009
|
425
|
-
squad/plugins/github.py,sha256=
|
426
|
+
squad/plugins/github.py,sha256=4ZXhR-eBVdmLUK-dBJVMRHOyjue9oknNrUfkuUqkhY0,2413
|
426
427
|
squad/plugins/linux_log_parser.py,sha256=Dfcvh1-t5380QS_tvNIpzW_UD-4gptT9I4FUHVbb0PU,5503
|
427
428
|
squad/run/__init__.py,sha256=ssE8GPAGFiK6V0WpZYowav6Zqsd63dfDMMYasNa1sQg,1410
|
428
429
|
squad/run/__main__.py,sha256=DOl8JOi4Yg7DdtwnUeGqtYBJ6P2k-D2psAEuYOjWr8w,66
|
429
430
|
squad/run/listener.py,sha256=jBeOQhPGb4EdIREB1QsCzYuumsfJ-TqJPd3nR-0m59g,200
|
430
431
|
squad/run/scheduler.py,sha256=CDJG3q5C0GuQuxwlMOfWTSSJpDdwbR6rzpbJfuA0xuw,277
|
431
432
|
squad/run/worker.py,sha256=jtML0h5qKDuSbpJ6_rpWP4MT_rsGA7a24AhwGxBquzk,594
|
432
|
-
squad-1.
|
433
|
-
squad-1.
|
434
|
-
squad-1.
|
435
|
-
squad-1.
|
436
|
-
squad-1.
|
437
|
-
squad-1.
|
433
|
+
squad-1.67.dist-info/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
434
|
+
squad-1.67.dist-info/METADATA,sha256=PA2cyjXXMPX2KVIyIzo6xcLtGugZFDHfLmqvpN8TEvU,1267
|
435
|
+
squad-1.67.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
436
|
+
squad-1.67.dist-info/entry_points.txt,sha256=apCDQydHZtvqV334ql6NhTJUAJeZRdtAm0TVcbbAi5Q,194
|
437
|
+
squad-1.67.dist-info/top_level.txt,sha256=_x9uqE1XppiiytmVTl_qNgpnXus6Gsef69HqfliE7WI,6
|
438
|
+
squad-1.67.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|