Merge ~cjwatson/launchpad:ci-build-webhooks into launchpad:master
- Git
- lp:~cjwatson/launchpad
- ci-build-webhooks
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 9ab62fd3101a6c137c41e85e514a84cbbf0fda19 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:ci-build-webhooks |
Merge into: | launchpad:master |
Diff against target: |
483 lines (+269/-11) 10 files modified
lib/lp/code/configure.zcml (+9/-1) lib/lp/code/interfaces/cibuild.py (+4/-1) lib/lp/code/model/cibuild.py (+27/-1) lib/lp/code/model/gitrepository.py (+1/-1) lib/lp/code/model/tests/test_cibuild.py (+97/-2) lib/lp/code/model/tests/test_gitrepository.py (+85/-2) lib/lp/code/subscribers/cibuild.py (+41/-0) lib/lp/services/webhooks/interfaces.py (+2/-1) lib/lp/services/webhooks/tests/test_browser.py (+2/-1) tox.ini (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ines Almeida | Approve | ||
Review via email: mp+442596@code.launchpad.net |
Commit message
Add webhooks for CI builds
Description of the change
This was mostly just cribbed from charm recipe build webhooks, with adjustments as needed for the different data model here. I expect we'll probably need to add some more fields to the payload once people start using this in practice, but this should be enough to get started.
To post a comment you must log in.
Revision history for this message
Ines Almeida (ines-almeida) wrote : | # |
One small note, we should update the webhooks documentation page
Revision history for this message
Colin Watson (cjwatson) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/code/configure.zcml b/lib/lp/code/configure.zcml |
2 | index eaf4020..cf2e52e 100644 |
3 | --- a/lib/lp/code/configure.zcml |
4 | +++ b/lib/lp/code/configure.zcml |
5 | @@ -1,4 +1,4 @@ |
6 | -<!-- Copyright 2009-2021 Canonical Ltd. This software is licensed under the |
7 | +<!-- Copyright 2009-2023 Canonical Ltd. This software is licensed under the |
8 | GNU Affero General Public License version 3 (see the file LICENSE). |
9 | --> |
10 | |
11 | @@ -1297,6 +1297,14 @@ |
12 | permission="launchpad.Admin" |
13 | interface="lp.code.interfaces.cibuild.ICIBuildAdmin" /> |
14 | </class> |
15 | + <subscriber |
16 | + for="lp.code.interfaces.cibuild.ICIBuild |
17 | + lazr.lifecycle.interfaces.IObjectCreatedEvent" |
18 | + handler="lp.code.subscribers.cibuild.ci_build_created" /> |
19 | + <subscriber |
20 | + for="lp.code.interfaces.cibuild.ICIBuild |
21 | + lazr.lifecycle.interfaces.IObjectModifiedEvent" |
22 | + handler="lp.code.subscribers.cibuild.ci_build_modified" /> |
23 | |
24 | <!-- CIBuildSet --> |
25 | <lp:securedutility |
26 | diff --git a/lib/lp/code/interfaces/cibuild.py b/lib/lp/code/interfaces/cibuild.py |
27 | index 9599b75..bac6181 100644 |
28 | --- a/lib/lp/code/interfaces/cibuild.py |
29 | +++ b/lib/lp/code/interfaces/cibuild.py |
30 | @@ -1,9 +1,10 @@ |
31 | -# Copyright 2022 Canonical Ltd. This software is licensed under the |
32 | +# Copyright 2022-2023 Canonical Ltd. This software is licensed under the |
33 | # GNU Affero General Public License version 3 (see the file LICENSE). |
34 | |
35 | """Interfaces for CI builds.""" |
36 | |
37 | __all__ = [ |
38 | + "CI_WEBHOOKS_FEATURE_FLAG", |
39 | "CannotFetchConfiguration", |
40 | "CannotParseConfiguration", |
41 | "CIBuildAlreadyRequested", |
42 | @@ -41,6 +42,8 @@ from lp.code.interfaces.gitrepository import IGitRepository |
43 | from lp.services.database.constants import DEFAULT |
44 | from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries |
45 | |
46 | +CI_WEBHOOKS_FEATURE_FLAG = "ci.webhooks.enabled" |
47 | + |
48 | |
49 | class MissingConfiguration(Exception): |
50 | """The repository for this CI build does not have a .launchpad.yaml.""" |
51 | diff --git a/lib/lp/code/model/cibuild.py b/lib/lp/code/model/cibuild.py |
52 | index ad5bbfb..e1b7450 100644 |
53 | --- a/lib/lp/code/model/cibuild.py |
54 | +++ b/lib/lp/code/model/cibuild.py |
55 | @@ -1,4 +1,4 @@ |
56 | -# Copyright 2022 Canonical Ltd. This software is licensed under the |
57 | +# Copyright 2022-2023 Canonical Ltd. This software is licensed under the |
58 | # GNU Affero General Public License version 3 (see the file LICENSE). |
59 | |
60 | """CI builds.""" |
61 | @@ -77,6 +77,7 @@ from lp.services.macaroons.interfaces import ( |
62 | ) |
63 | from lp.services.macaroons.model import MacaroonIssuerBase |
64 | from lp.services.propertycache import cachedproperty |
65 | +from lp.services.webapp.snapshot import notify_modified |
66 | from lp.soyuz.model.binarypackagename import BinaryPackageName |
67 | from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease |
68 | from lp.soyuz.model.distroarchseries import DistroArchSeries |
69 | @@ -504,6 +505,31 @@ class CIBuild(PackageBuildMixin, StormBase): |
70 | # We have no interesting checks to perform here. |
71 | return True |
72 | |
73 | + def updateStatus( |
74 | + self, |
75 | + status, |
76 | + builder=None, |
77 | + worker_status=None, |
78 | + date_started=None, |
79 | + date_finished=None, |
80 | + force_invalid_transition=False, |
81 | + ): |
82 | + """See `IBuildFarmJob`.""" |
83 | + edited_fields = set() |
84 | + with notify_modified( |
85 | + self, edited_fields, snapshot_names=("status",) |
86 | + ) as previous_obj: |
87 | + super().updateStatus( |
88 | + status, |
89 | + builder=builder, |
90 | + worker_status=worker_status, |
91 | + date_started=date_started, |
92 | + date_finished=date_finished, |
93 | + force_invalid_transition=force_invalid_transition, |
94 | + ) |
95 | + if self.status != previous_obj.status: |
96 | + edited_fields.add("status") |
97 | + |
98 | def notify(self, extra_info=None): |
99 | """See `IPackageBuild`.""" |
100 | from lp.code.mail.cibuild import CIBuildMailer |
101 | diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py |
102 | index 0ffae31..0384d58 100644 |
103 | --- a/lib/lp/code/model/gitrepository.py |
104 | +++ b/lib/lp/code/model/gitrepository.py |
105 | @@ -417,7 +417,7 @@ class GitRepository( |
106 | |
107 | @property |
108 | def valid_webhook_event_types(self): |
109 | - return ["git:push:0.1", "merge-proposal:0.1"] |
110 | + return ["ci:build:0.1", "git:push:0.1", "merge-proposal:0.1"] |
111 | |
112 | @property |
113 | def default_webhook_event_types(self): |
114 | diff --git a/lib/lp/code/model/tests/test_cibuild.py b/lib/lp/code/model/tests/test_cibuild.py |
115 | index ec452d6..ae95fbd 100644 |
116 | --- a/lib/lp/code/model/tests/test_cibuild.py |
117 | +++ b/lib/lp/code/model/tests/test_cibuild.py |
118 | @@ -1,4 +1,4 @@ |
119 | -# Copyright 2022 Canonical Ltd. This software is licensed under the |
120 | +# Copyright 2022-2023 Canonical Ltd. This software is licensed under the |
121 | # GNU Affero General Public License version 3 (see the file LICENSE). |
122 | |
123 | """Test CI builds.""" |
124 | @@ -9,13 +9,14 @@ from textwrap import dedent |
125 | from unittest.mock import Mock |
126 | from urllib.request import urlopen |
127 | |
128 | -from fixtures import MockPatchObject |
129 | +from fixtures import FakeLogger, MockPatchObject |
130 | from pymacaroons import Macaroon |
131 | from storm.locals import Store |
132 | from testtools.matchers import ( |
133 | ContainsDict, |
134 | Equals, |
135 | Is, |
136 | + MatchesDict, |
137 | MatchesListwise, |
138 | MatchesSetwise, |
139 | MatchesStructure, |
140 | @@ -35,6 +36,7 @@ from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
141 | from lp.buildmaster.model.buildqueue import BuildQueue |
142 | from lp.code.errors import GitRepositoryBlobNotFound, GitRepositoryScanFault |
143 | from lp.code.interfaces.cibuild import ( |
144 | + CI_WEBHOOKS_FEATURE_FLAG, |
145 | CannotFetchConfiguration, |
146 | CannotParseConfiguration, |
147 | CIBuildAlreadyRequested, |
148 | @@ -55,12 +57,15 @@ from lp.registry.interfaces.sourcepackage import SourcePackageType |
149 | from lp.services.authserver.xmlrpc import AuthServerAPIView |
150 | from lp.services.config import config |
151 | from lp.services.database.sqlbase import flush_database_caches |
152 | +from lp.services.features.testing import FeatureFixture |
153 | from lp.services.librarian.browser import ProxiedLibraryFileAlias |
154 | from lp.services.log.logger import BufferLogger |
155 | from lp.services.macaroons.interfaces import IMacaroonIssuer |
156 | from lp.services.macaroons.testing import MacaroonTestMixin |
157 | from lp.services.propertycache import clear_property_cache |
158 | from lp.services.webapp.interfaces import OAuthPermission |
159 | +from lp.services.webapp.publisher import canonical_url |
160 | +from lp.services.webhooks.testing import LogsScheduledWebhooks |
161 | from lp.soyuz.enums import BinaryPackageFormat |
162 | from lp.testing import ( |
163 | ANONYMOUS, |
164 | @@ -73,6 +78,7 @@ from lp.testing import ( |
165 | person_logged_in, |
166 | pop_notifications, |
167 | ) |
168 | +from lp.testing.dbuser import dbuser |
169 | from lp.testing.layers import LaunchpadFunctionalLayer, LaunchpadZopelessLayer |
170 | from lp.testing.matchers import HasQueryCount |
171 | from lp.testing.pages import webservice_for_person |
172 | @@ -320,6 +326,95 @@ class TestCIBuild(TestCaseWithFactory): |
173 | build = self.factory.makeCIBuild() |
174 | self.assertTrue(build.verifySuccessfulUpload()) |
175 | |
176 | + def test_updateStatus_triggers_webhooks(self): |
177 | + # Updating the status of a CIBuild triggers webhooks on the |
178 | + # corresponding GitRepository. |
179 | + self.useFixture(FeatureFixture({CI_WEBHOOKS_FEATURE_FLAG: "on"})) |
180 | + logger = self.useFixture(FakeLogger()) |
181 | + build = self.factory.makeCIBuild() |
182 | + hook = self.factory.makeWebhook( |
183 | + target=build.git_repository, event_types=["ci:build:0.1"] |
184 | + ) |
185 | + build.updateStatus(BuildStatus.FULLYBUILT) |
186 | + expected_payload = { |
187 | + "build": Equals(canonical_url(build, force_local_path=True)), |
188 | + "action": Equals("status-changed"), |
189 | + "git_repository": Equals( |
190 | + canonical_url(build.git_repository, force_local_path=True) |
191 | + ), |
192 | + "commit_sha1": Equals(build.commit_sha1), |
193 | + "status": Equals("Successfully built"), |
194 | + } |
195 | + delivery = hook.deliveries.one() |
196 | + self.assertThat( |
197 | + delivery, |
198 | + MatchesStructure( |
199 | + event_type=Equals("ci:build:0.1"), |
200 | + payload=MatchesDict(expected_payload), |
201 | + ), |
202 | + ) |
203 | + with dbuser(config.IWebhookDeliveryJobSource.dbuser): |
204 | + self.assertEqual( |
205 | + "<WebhookDeliveryJob for webhook %d on %r>" |
206 | + % (hook.id, hook.target), |
207 | + repr(delivery), |
208 | + ) |
209 | + self.assertThat( |
210 | + logger.output, |
211 | + LogsScheduledWebhooks( |
212 | + [(hook, "ci:build:0.1", MatchesDict(expected_payload))] |
213 | + ), |
214 | + ) |
215 | + |
216 | + def test_updateStatus_no_change_does_not_trigger_webhooks(self): |
217 | + # An updateStatus call that changes details of the worker status but |
218 | + # that doesn't change the build's status attribute does not trigger |
219 | + # webhooks. |
220 | + self.useFixture(FeatureFixture({CI_WEBHOOKS_FEATURE_FLAG: "on"})) |
221 | + logger = self.useFixture(FakeLogger()) |
222 | + build = self.factory.makeCIBuild() |
223 | + hook = self.factory.makeWebhook( |
224 | + target=build.git_repository, event_types=["ci:build:0.1"] |
225 | + ) |
226 | + builder = self.factory.makeBuilder() |
227 | + build.updateStatus(BuildStatus.BUILDING) |
228 | + expected_logs = [ |
229 | + ( |
230 | + hook, |
231 | + "ci:build:0.1", |
232 | + ContainsDict( |
233 | + { |
234 | + "action": Equals("status-changed"), |
235 | + "status": Equals("Currently building"), |
236 | + } |
237 | + ), |
238 | + ) |
239 | + ] |
240 | + self.assertEqual(1, hook.deliveries.count()) |
241 | + self.assertThat(logger.output, LogsScheduledWebhooks(expected_logs)) |
242 | + build.updateStatus( |
243 | + BuildStatus.BUILDING, |
244 | + builder=builder, |
245 | + worker_status={"revision_id": build.commit_sha1}, |
246 | + ) |
247 | + self.assertEqual(1, hook.deliveries.count()) |
248 | + self.assertThat(logger.output, LogsScheduledWebhooks(expected_logs)) |
249 | + build.updateStatus(BuildStatus.UPLOADING) |
250 | + expected_logs.append( |
251 | + ( |
252 | + hook, |
253 | + "ci:build:0.1", |
254 | + ContainsDict( |
255 | + { |
256 | + "action": Equals("status-changed"), |
257 | + "status": Equals("Uploading build"), |
258 | + } |
259 | + ), |
260 | + ) |
261 | + ) |
262 | + self.assertEqual(2, hook.deliveries.count()) |
263 | + self.assertThat(logger.output, LogsScheduledWebhooks(expected_logs)) |
264 | + |
265 | def addFakeBuildLog(self, build): |
266 | build.setLog(self.factory.makeLibraryFileAlias("mybuildlog.txt")) |
267 | |
268 | diff --git a/lib/lp/code/model/tests/test_gitrepository.py b/lib/lp/code/model/tests/test_gitrepository.py |
269 | index 2aeb2ca..ecf5663 100644 |
270 | --- a/lib/lp/code/model/tests/test_gitrepository.py |
271 | +++ b/lib/lp/code/model/tests/test_gitrepository.py |
272 | @@ -12,7 +12,7 @@ from textwrap import dedent |
273 | |
274 | import transaction |
275 | from breezy import urlutils |
276 | -from fixtures import MockPatch |
277 | +from fixtures import FakeLogger, MockPatch |
278 | from lazr.lifecycle.event import ObjectModifiedEvent |
279 | from pymacaroons import Macaroon |
280 | from storm.exceptions import LostObjectError |
281 | @@ -76,7 +76,11 @@ from lp.code.event.git import GitRefsUpdatedEvent |
282 | from lp.code.interfaces.branchmergeproposal import ( |
283 | BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES, |
284 | ) |
285 | -from lp.code.interfaces.cibuild import ICIBuild, ICIBuildSet |
286 | +from lp.code.interfaces.cibuild import ( |
287 | + CI_WEBHOOKS_FEATURE_FLAG, |
288 | + ICIBuild, |
289 | + ICIBuildSet, |
290 | +) |
291 | from lp.code.interfaces.codeimport import ICodeImportSet |
292 | from lp.code.interfaces.defaultgit import ICanHasDefaultGitRepository |
293 | from lp.code.interfaces.gitjob import ( |
294 | @@ -170,7 +174,9 @@ from lp.services.propertycache import clear_property_cache |
295 | from lp.services.utils import seconds_since_epoch |
296 | from lp.services.webapp.authorization import check_permission |
297 | from lp.services.webapp.interfaces import OAuthPermission |
298 | +from lp.services.webapp.publisher import canonical_url |
299 | from lp.services.webapp.snapshot import notify_modified |
300 | +from lp.services.webhooks.testing import LogsScheduledWebhooks |
301 | from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS |
302 | from lp.testing import ( |
303 | ANONYMOUS, |
304 | @@ -4171,6 +4177,83 @@ class TestGitRepositoryRequestCIBuilds(TestCaseWithFactory): |
305 | ) |
306 | self.assertEqual("", logger.getLogBuffer()) |
307 | |
308 | + def test_triggers_webhooks(self): |
309 | + # Requesting CI builds triggers any relevant webhooks. |
310 | + self.useFixture(FeatureFixture({CI_WEBHOOKS_FEATURE_FLAG: "on"})) |
311 | + logger = self.useFixture(FakeLogger()) |
312 | + repository = self.factory.makeGitRepository() |
313 | + hook = self.factory.makeWebhook( |
314 | + target=repository, event_types=["ci:build:0.1"] |
315 | + ) |
316 | + ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
317 | + distroseries = self.factory.makeDistroSeries(distribution=ubuntu) |
318 | + das = self.factory.makeBuildableDistroArchSeries( |
319 | + distroseries=distroseries |
320 | + ) |
321 | + configuration = dedent( |
322 | + """\ |
323 | + pipeline: [test] |
324 | + jobs: |
325 | + test: |
326 | + series: {series} |
327 | + architectures: [{architecture}] |
328 | + """.format( |
329 | + series=distroseries.name, architecture=das.architecturetag |
330 | + ) |
331 | + ).encode() |
332 | + new_commit = hashlib.sha1(self.factory.getUniqueBytes()).hexdigest() |
333 | + self.useFixture( |
334 | + GitHostingFixture( |
335 | + commits=[ |
336 | + { |
337 | + "sha1": new_commit, |
338 | + "blobs": {".launchpad.yaml": configuration}, |
339 | + }, |
340 | + ] |
341 | + ) |
342 | + ) |
343 | + with dbuser("branchscanner"): |
344 | + repository.createOrUpdateRefs( |
345 | + { |
346 | + "refs/heads/test": { |
347 | + "sha1": new_commit, |
348 | + "type": GitObjectType.COMMIT, |
349 | + } |
350 | + } |
351 | + ) |
352 | + |
353 | + [build] = getUtility(ICIBuildSet).findByGitRepository(repository) |
354 | + delivery = hook.deliveries.one() |
355 | + payload_matcher = MatchesDict( |
356 | + { |
357 | + "build": Equals(canonical_url(build, force_local_path=True)), |
358 | + "action": Equals("created"), |
359 | + "git_repository": Equals( |
360 | + canonical_url(repository, force_local_path=True) |
361 | + ), |
362 | + "commit_sha1": Equals(new_commit), |
363 | + "status": Equals("Needs building"), |
364 | + } |
365 | + ) |
366 | + self.assertThat( |
367 | + delivery, |
368 | + MatchesStructure( |
369 | + event_type=Equals("ci:build:0.1"), payload=payload_matcher |
370 | + ), |
371 | + ) |
372 | + with dbuser(config.IWebhookDeliveryJobSource.dbuser): |
373 | + self.assertEqual( |
374 | + "<WebhookDeliveryJob for webhook %d on %r>" |
375 | + % (hook.id, hook.target), |
376 | + repr(delivery), |
377 | + ) |
378 | + self.assertThat( |
379 | + logger.output, |
380 | + LogsScheduledWebhooks( |
381 | + [(hook, "ci:build:0.1", payload_matcher)] |
382 | + ), |
383 | + ) |
384 | + |
385 | |
386 | class TestGitRepositoryGetBlob(TestCaseWithFactory): |
387 | """Tests for retrieving files from a Git repository.""" |
388 | diff --git a/lib/lp/code/subscribers/cibuild.py b/lib/lp/code/subscribers/cibuild.py |
389 | new file mode 100644 |
390 | index 0000000..83d3363 |
391 | --- /dev/null |
392 | +++ b/lib/lp/code/subscribers/cibuild.py |
393 | @@ -0,0 +1,41 @@ |
394 | +# Copyright 2023 Canonical Ltd. This software is licensed under the |
395 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
396 | + |
397 | +"""Event subscribers for CI builds.""" |
398 | + |
399 | +from lazr.lifecycle.interfaces import IObjectCreatedEvent, IObjectModifiedEvent |
400 | +from zope.component import getUtility |
401 | + |
402 | +from lp.code.interfaces.cibuild import CI_WEBHOOKS_FEATURE_FLAG, ICIBuild |
403 | +from lp.services.features import getFeatureFlag |
404 | +from lp.services.webapp.publisher import canonical_url |
405 | +from lp.services.webhooks.interfaces import IWebhookSet |
406 | +from lp.services.webhooks.payload import compose_webhook_payload |
407 | + |
408 | + |
409 | +def _trigger_ci_build_webhook(build: ICIBuild, action: str) -> None: |
410 | + if getFeatureFlag(CI_WEBHOOKS_FEATURE_FLAG): |
411 | + payload = { |
412 | + "build": canonical_url(build, force_local_path=True), |
413 | + "action": action, |
414 | + } |
415 | + payload.update( |
416 | + compose_webhook_payload( |
417 | + ICIBuild, build, ["git_repository", "commit_sha1", "status"] |
418 | + ) |
419 | + ) |
420 | + getUtility(IWebhookSet).trigger( |
421 | + build.git_repository, "ci:build:0.1", payload |
422 | + ) |
423 | + |
424 | + |
425 | +def ci_build_created(build: ICIBuild, event: IObjectCreatedEvent) -> None: |
426 | + """Trigger events when a new CI build is created.""" |
427 | + _trigger_ci_build_webhook(build, "created") |
428 | + |
429 | + |
430 | +def ci_build_modified(build: ICIBuild, event: IObjectModifiedEvent) -> None: |
431 | + """Trigger events when a CI build is modified.""" |
432 | + if event.edited_fields is not None: |
433 | + if "status" in event.edited_fields: |
434 | + _trigger_ci_build_webhook(build, "status-changed") |
435 | diff --git a/lib/lp/services/webhooks/interfaces.py b/lib/lp/services/webhooks/interfaces.py |
436 | index b31f29d..4f0a1e8 100644 |
437 | --- a/lib/lp/services/webhooks/interfaces.py |
438 | +++ b/lib/lp/services/webhooks/interfaces.py |
439 | @@ -1,4 +1,4 @@ |
440 | -# Copyright 2015-2021 Canonical Ltd. This software is licensed under the |
441 | +# Copyright 2015-2023 Canonical Ltd. This software is licensed under the |
442 | # GNU Affero General Public License version 3 (see the file LICENSE). |
443 | |
444 | """Webhook interfaces.""" |
445 | @@ -50,6 +50,7 @@ from lp.services.webservice.apihelpers import ( |
446 | WEBHOOK_EVENT_TYPES = { |
447 | "bzr:push:0.1": "Bazaar push", |
448 | "charm-recipe:build:0.1": "Charm recipe build", |
449 | + "ci:build:0.1": "CI build", |
450 | "git:push:0.1": "Git push", |
451 | "livefs:build:0.1": "Live filesystem build", |
452 | "merge-proposal:0.1": "Merge proposal", |
453 | diff --git a/lib/lp/services/webhooks/tests/test_browser.py b/lib/lp/services/webhooks/tests/test_browser.py |
454 | index 65ba0c3..b3f28fe 100644 |
455 | --- a/lib/lp/services/webhooks/tests/test_browser.py |
456 | +++ b/lib/lp/services/webhooks/tests/test_browser.py |
457 | @@ -1,4 +1,4 @@ |
458 | -# Copyright 2015-2021 Canonical Ltd. This software is licensed under the |
459 | +# Copyright 2015-2023 Canonical Ltd. This software is licensed under the |
460 | # GNU Affero General Public License version 3 (see the file LICENSE). |
461 | |
462 | """Unit tests for Webhook views.""" |
463 | @@ -63,6 +63,7 @@ class GitRepositoryTestHelpers: |
464 | |
465 | event_type = "git:push:0.1" |
466 | expected_event_types = [ |
467 | + ("ci:build:0.1", "CI build"), |
468 | ("git:push:0.1", "Git push"), |
469 | ("merge-proposal:0.1", "Merge proposal"), |
470 | ] |
471 | diff --git a/tox.ini b/tox.ini |
472 | index 9054ea2..dbf21ce 100644 |
473 | --- a/tox.ini |
474 | +++ b/tox.ini |
475 | @@ -27,7 +27,7 @@ commands_pre = |
476 | {toxinidir}/scripts/update-version-info.sh |
477 | commands = |
478 | mypy --follow-imports=silent \ |
479 | - {posargs:lib/lp/answers lib/lp/app lib/lp/archivepublisher lib/lp/archiveuploader lib/lp/buildmaster lib/lp/charms/model/charmrecipebuildbehaviour.py lib/lp/code/model/cibuildbehaviour.py lib/lp/code/model/recipebuilder.py lib/lp/oci/model/ocirecipebuildbehaviour.py lib/lp/snappy/model/snapbuildbehaviour.py lib/lp/soyuz/model/binarypackagebuildbehaviour.py lib/lp/soyuz/model/livefsbuildbehaviour.py lib/lp/testing lib/lp/translations/model/translationtemplatesbuildbehaviour.py} |
480 | + {posargs:lib/lp/answers lib/lp/app lib/lp/archivepublisher lib/lp/archiveuploader lib/lp/buildmaster lib/lp/charms/model/charmrecipebuildbehaviour.py lib/lp/code/model/cibuildbehaviour.py lib/lp/code/model/recipebuilder.py lib/lp/code/subscribers lib/lp/oci/model/ocirecipebuildbehaviour.py lib/lp/snappy/model/snapbuildbehaviour.py lib/lp/soyuz/model/binarypackagebuildbehaviour.py lib/lp/soyuz/model/livefsbuildbehaviour.py lib/lp/testing lib/lp/translations/model/translationtemplatesbuildbehaviour.py} |
481 | |
482 | [testenv:docs] |
483 | basepython = python3 |
Looks good to me!
Was wondering why only trigger "status-changed" actions when the build is modified, but looking into other similar webhooks and thinking about the use case, that seems consistent.