squad 1.66__py3-none-any.whl → 1.67__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 +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
|