]>
git.ipfire.org Git - people/jschlag/pbs.git/blob - src/buildservice/builds.py
6ef8a61c69a672396a2baa532145aae976532199
8 import pakfire
.packages
15 log
= logging
.getLogger("builds")
18 from .constants
import *
19 from .decorators
import *
21 class Builds(base
.Object
):
22 def _get_build(self
, query
, *args
):
23 res
= self
.db
.get(query
, *args
)
26 return Build(self
.backend
, res
.id, data
=res
)
28 def _get_builds(self
, query
, *args
):
29 res
= self
.db
.query(query
, *args
)
32 yield Build(self
.backend
, row
.id, data
=row
)
34 def get_by_id(self
, id, data
=None):
35 return Build(self
.backend
, id, data
=data
)
37 def get_by_uuid(self
, uuid
):
38 build
= self
.db
.get("SELECT id FROM builds WHERE uuid = %s LIMIT 1", uuid
)
41 return self
.get_by_id(build
.id)
43 def get_all(self
, limit
=50):
44 query
= "SELECT * FROM builds ORDER BY time_created DESC"
47 query
+= " LIMIT %d" % limit
49 return [self
.get_by_id(b
.id, b
) for b
in self
.db
.query(query
)]
51 def get_by_user(self
, user
, type=None):
55 if not type or type == "scratch":
56 # On scratch builds the user id equals the owner id.
57 conditions
.append("(builds.type = 'scratch' AND owner_id = %s)")
60 elif not type or type == "release":
63 query
= "SELECT builds.* AS id FROM builds \
64 JOIN packages ON builds.pkg_id = packages.id"
67 query
+= " WHERE %s" % " AND ".join(conditions
)
69 query
+= " ORDER BY builds.time_created DESC"
72 for build
in self
.db
.query(query
, *args
):
73 build
= Build(self
.backend
, build
.id, build
)
78 def get_by_name(self
, name
, type=None, user
=None, limit
=None, offset
=None):
85 conditions
.append("builds.type = %s")
89 if user
and not user
.is_admin():
90 or_conditions
.append("builds.owner_id = %s")
93 query
= "SELECT builds.* AS id FROM builds \
94 JOIN packages ON builds.pkg_id = packages.id"
97 conditions
.append(" OR ".join(or_conditions
))
100 query
+= " WHERE %s" % " AND ".join(conditions
)
102 if type == "release":
103 query
+= " ORDER BY packages.name,packages.epoch,packages.version,packages.release,id ASC"
104 elif type == "scratch":
105 query
+= " ORDER BY time_created DESC"
109 query
+= " LIMIT %s,%s"
110 args
.extend([offset
, limit
])
115 return [Build(self
.backend
, b
.id, b
) for b
in self
.db
.query(query
, *args
)]
117 def get_latest_by_name(self
, name
, type=None):
119 SELECT * FROM builds \
120 LEFT JOIN builds_latest ON builds.id = builds_latest.build_id \
121 WHERE builds_latest.package_name = %s"
125 query
+= " AND builds_latest.build_type = %s"
128 # Get the last one only.
129 # Prefer release builds over scratch builds.
132 CASE builds.type WHEN 'release' THEN 0 ELSE 1 END, \
133 builds.time_created DESC \
136 res
= self
.db
.get(query
, *args
)
139 return Build(self
.backend
, res
.id, res
)
141 def get_active_builds(self
, name
):
143 SELECT * FROM builds \
144 LEFT JOIN builds_latest ON builds.id = builds_latest.build_id \
145 WHERE builds_latest.package_name = %s AND builds.type = %s"
146 args
= [name
, "release"]
149 for row
in self
.db
.query(query
, *args
):
150 b
= Build(self
.backend
, row
.id, row
)
153 # Sort the result. Lastest build first.
154 builds
.sort(reverse
=True)
159 builds
= self
.db
.get("SELECT COUNT(*) AS count FROM builds")
163 def get_obsolete(self
, repo
=None):
165 Get all obsoleted builds.
167 If repo is True: which are in any repository.
168 If repo is some Repository object: which are in this repository.
173 query
= "SELECT id FROM builds WHERE state = 'obsolete'"
176 query
= "SELECT build_id AS id FROM repositories_builds \
177 JOIN builds ON builds.id = repositories_builds.build_id \
178 WHERE builds.state = 'obsolete'"
180 if repo
and not repo
is True:
181 query
+= " AND repositories_builds.repo_id = %s"
184 res
= self
.db
.query(query
, *args
)
188 build
= Build(self
.backend
, build
.id)
193 def create(self
, pkg
, type="release", owner
=None, distro
=None):
194 assert type in ("release", "scratch", "test")
195 assert distro
, "You need to specify the distribution of this build."
197 # Check if scratch build has an owner.
198 if type == "scratch" and not owner
:
199 raise Exception, "Scratch builds require an owner"
201 # Set the default priority of this build.
202 if type == "release":
205 elif type == "scratch":
211 # Create build in database
212 build
= self
._get
_build
("INSERT INTO builds(uuid, pkg_id, type, distro_id, priority) \
213 VALUES(%s, %s, %s, %s, %s) RETURNING *", "%s" % uuid
.uuid4(), pkg
.id, type, distro
.id, priority
)
215 # Set the owner of this build
219 # Log that the build has been created.
220 build
.log("created", user
=owner
)
222 # Create directory where the files live
223 if not os
.path
.exists(build
.path
):
224 os
.makedirs(build
.path
)
226 # Move package file to the directory of the build.
227 build
.pkg
.move(os
.path
.join(build
.path
, "src"))
229 # Generate an update id.
230 build
.generate_update_id()
232 # Obsolete all other builds with the same name to track updates.
233 build
.obsolete_others()
235 # Search for possible bug IDs in the commit message.
236 build
.search_for_bugs()
240 def create_from_source_package(self
, filename
, distro
, commit
=None, type="release",
241 arches
=None, check_for_duplicates
=True, owner
=None):
244 # Open the package file to read some basic information.
245 pkg
= pakfire
.packages
.open(None, None, filename
)
247 if check_for_duplicates
:
248 if distro
.has_package(pkg
.name
, pkg
.epoch
, pkg
.version
, pkg
.release
):
249 log
.warning("Duplicate package detected: %s. Skipping." % pkg
)
252 # Open the package and add it to the database
253 pkg
= self
.backend
.packages
.create(filename
)
255 # Associate the package to the processed commit
259 # Create a new build object from the package
260 build
= self
.create(pkg
, type=type, owner
=owner
, distro
=distro
)
262 # Create all automatic jobs
263 build
.create_autojobs(arches
=arches
)
267 def get_changelog(self
, name
, limit
=5, offset
=0):
268 query
= "SELECT builds.* FROM builds \
269 JOIN packages ON builds.pkg_id = packages.id \
274 args
= ["release", name
,]
276 query
+= " ORDER BY builds.time_created DESC"
280 query
+= " LIMIT %s,%s"
281 args
+= [offset
, limit
]
287 for b
in self
.db
.query(query
, *args
):
288 b
= Build(self
.backend
, b
.id, b
)
291 builds
.sort(reverse
=True)
295 def get_comments(self
, limit
=10, offset
=None, user
=None):
296 query
= "SELECT * FROM builds_comments \
297 JOIN users ON builds_comments.user_id = users.id"
302 wheres
.append("users.id = %s")
306 query
+= " WHERE %s" % " AND ".join(wheres
)
309 query
+= " ORDER BY time_created DESC"
314 query
+= " LIMIT %s,%s"
322 for comment
in self
.db
.query(query
, *args
):
323 comment
= logs
.CommentLogEntry(self
.backend
, comment
)
324 comments
.append(comment
)
328 def get_build_times_summary(self
, name
=None, arch
=None):
331 builds_times.arch AS arch, \
332 MAX(duration) AS maximum, \
333 MIN(duration) AS minimum, \
334 AVG(duration) AS average, \
335 SUM(duration) AS sum, \
336 STDDEV_POP(duration) AS stddev \
338 LEFT JOIN builds ON builds_times.build_id = builds.id \
339 LEFT JOIN packages ON builds.pkg_id = packages.id"
346 conditions
.append("packages.name = %s")
351 conditions
.append("builds_times.arch = %s")
356 query
+= " WHERE %s" % " AND ".join(conditions
)
358 # Grouping and sorting.
359 query
+= " GROUP BY arch ORDER BY arch DESC"
361 return self
.db
.query(query
, *args
)
363 def get_build_times_by_arch(self
, arch
, **kwargs
):
368 build_times
= self
.get_build_times_summary(**kwargs
)
370 return build_times
[0]
373 class Build(base
.DataObject
):
377 return "<%s id=%s %s>" % (self
.__class
__.__name
__, self
.id, self
.pkg
)
379 def __eq__(self
, other
):
380 if isinstance(other
, self
.__class
__):
381 return self
.id == other
.id
383 def __lt__(self
, other
):
384 if isinstance(other
, self
.__class
__):
385 return self
.pkg
< other
.pkg
388 jobs
= self
.backend
.jobs
._get
_jobs
("SELECT * FROM jobs \
389 WHERE build_id = %s", self
.id)
391 return iter(sorted(jobs
))
395 Deletes this build including all jobs,
396 packages and the source package.
398 # If the build is in a repository, we need to remove it.
400 self
.repo
.rem_build(self
)
402 # Delete all release jobs
403 for job
in self
.jobs
:
406 # Delete all test jobs
407 for job
in self
.test_jobs
:
410 # Deleted all associated bugs
411 self
.db
.execute("DELETE FROM builds_bugs WHERE build_id = %s", self
.id)
413 # Delete all comments
414 self
.db
.execute("DELETE FROM builds_comments WHERE build_id = %s", self
.id)
416 # Delete the repository history
417 self
.db
.execute("DELETE FROM repositories_history WHERE build_id = %s", self
.id)
419 # Delete all watchers
420 self
.db
.execute("DELETE FROM builds_watchers WHERE build_id = %s", self
.id)
422 # Delete build history
423 self
.db
.execute("DELETE FROM builds_history WHERE build_id = %s", self
.id)
425 # Delete the build itself.
426 self
.db
.execute("DELETE FROM builds WHERE id = %s", self
.id)
428 # Delete source package
434 A set of information that is sent to the XMLRPC client.
436 return { "uuid" : self
.uuid
}
438 def log(self
, action
, user
=None, bug_id
=None):
443 self
.db
.execute("INSERT INTO builds_history(build_id, action, user_id, time, bug_id) \
444 VALUES(%s, %s, %s, NOW(), %s)", self
.id, action
, user_id
, bug_id
)
449 The UUID of this build.
451 return self
.data
.uuid
456 Get package that is to be built in the build.
458 return self
.backend
.packages
.get_by_id(self
.data
.pkg_id
)
462 return "%s-%s" % (self
.pkg
.name
, self
.pkg
.friendly_version
)
467 The type of this build.
469 return self
.data
.type
473 The owner of this build.
475 if self
.data
.owner_id
:
476 return self
.backend
.users
.get_by_id(self
.data
.owner_id
)
478 def set_owner(self
, owner
):
480 self
._set
_attribute
("owner_id", owner
.id)
482 self
._set
_attribute
("owner_id", None)
484 owner
= lazy_property(get_owner
, set_owner
)
488 return self
.backend
.distros
.get_by_id(self
.data
.distro_id
)
492 if self
.type == "scratch":
495 def get_depends_on(self
):
496 if self
.data
.depends_on
:
497 return self
.backend
.builds
.get_by_id(self
.data
.depends_on
)
499 def set_depends_on(self
, build
):
500 self
._set
_attribute
("depends_on", build
.id)
502 depends_on
= lazy_property(get_depends_on
, set_depends_on
)
506 return self
.data
.time_created
510 return self
.created
.date()
515 Returns the size on disk of this build.
519 # Add the source package.
524 s
+= sum((j
.size
for j
in self
.jobs
))
528 def auto_update_state(self
):
530 Check if the state of this build can be updated and perform
531 the change if possible.
533 # Do not change the broken/obsolete state automatically.
534 if self
.state
in ("broken", "obsolete"):
537 if self
.repo
and self
.repo
.type == "stable":
538 self
.update_state("stable")
541 # If any of the build jobs are finished, the build will be put in testing
543 for job
in self
.jobs
:
544 if job
.state
== "finished":
545 self
.update_state("testing")
548 def update_state(self
, state
, user
=None, remove
=False):
549 assert state
in ("stable", "testing", "obsolete", "broken")
551 self
._set
_attribute
("state", state
)
553 # In broken state, the removal from the repository is forced and
554 # all jobs that are not finished yet will be aborted.
555 if state
== "broken":
558 for job
in self
.jobs
:
559 if job
.state
in ("new", "pending", "running", "dependency_error"):
560 job
.state
= "aborted"
562 # If this build is in a repository, it will leave it.
563 if remove
and self
.repo
:
564 self
.repo
.rem_build(self
)
566 # If a release build is now in testing state, we put it into the
567 # first repository of the distribution.
568 elif self
.type == "release" and state
== "testing":
569 # If the build is not in a repository, yet and if there is
570 # a first repository, we put the build there.
571 if not self
.repo
and self
.distro
.first_repo
:
572 self
.distro
.first_repo
.add_build(self
, user
=user
)
576 return self
.data
.state
579 return self
.state
== "broken"
581 def obsolete_others(self
):
582 if not self
.type == "release":
585 for build
in self
.backend
.builds
.get_by_name(self
.pkg
.name
, type="release"):
586 # Don't modify ourself.
587 if self
.id == build
.id:
590 # Don't touch broken builds.
591 if build
.state
in ("obsolete", "broken"):
594 # Obsolete the build.
595 build
.update_state("obsolete")
597 def set_severity(self
, severity
):
598 self
._set
_attribute
("severity", severity
)
600 def get_severity(self
):
601 return self
.data
.severity
603 severity
= property(get_severity
, set_severity
)
607 if self
.pkg
and self
.pkg
.commit
:
608 return self
.pkg
.commit
610 def update_message(self
, message
):
611 self
._set
_attribute
("message", message
)
613 def has_perm(self
, user
):
615 Check, if the given user has the right to perform administrative
616 operations on this build.
624 # Check if the user is allowed to manage packages from the critical path.
625 if self
.critical_path
and not user
.has_perm("manage_critical_path"):
628 # Search for maintainers...
631 if self
.type == "scratch":
632 # The owner of a scratch build has the right to do anything with it.
633 if self
.owner_id
== user
.id:
637 elif self
.type == "release":
638 # The maintainer also is allowed to manage the build.
639 if self
.pkg
.maintainer
== user
:
642 # Deny permission for all other cases.
649 if self
.data
.message
:
650 message
= self
.data
.message
653 if self
.commit
.message
:
654 message
= "\n".join((self
.commit
.subject
, self
.commit
.message
))
656 message
= self
.commit
.subject
658 prefix
= "%s: " % self
.pkg
.name
659 if message
.startswith(prefix
):
660 message
= message
[len(prefix
):]
664 def get_priority(self
):
665 return self
.data
.priority
667 def set_priority(self
, priority
):
668 assert priority
in (-2, -1, 0, 1, 2)
670 self
._set
_attribute
("priority", priority
)
672 priority
= property(get_priority
, set_priority
)
677 if self
.type == "scratch":
678 path
.append(BUILD_SCRATCH_DIR
)
679 path
.append(self
.uuid
)
681 elif self
.type == "release":
682 path
.append(BUILD_RELEASE_DIR
)
683 path
.append("%s/%s-%s-%s" % \
684 (self
.pkg
.name
, self
.pkg
.epoch
, self
.pkg
.version
, self
.pkg
.release
))
687 raise Exception, "Unknown build type: %s" % self
.type
689 return os
.path
.join(*path
)
692 def source_filename(self
):
693 return os
.path
.basename(self
.pkg
.path
)
696 def download_prefix(self
):
697 return "/".join((self
.backend
.settings
.get("download_baseurl"), "packages"))
700 def source_download(self
):
701 return "/".join((self
.download_prefix
, self
.pkg
.path
))
704 def source_hash_sha512(self
):
705 return self
.pkg
.hash_sha512
709 # XXX maybe this should rather live in a uimodule.
710 # zlib-1.2.3-2.ip3 [src, i686, blah...]
711 s
= """<a class="state_%s %s" href="/build/%s">%s</a>""" % \
712 (self
.state
, self
.type, self
.uuid
, self
.name
)
715 for job
in self
.jobs
:
716 s_jobs
.append("""<a class="state_%s %s" href="/job/%s">%s</a>""" % \
717 (job
.state
, "test" if job
.test
else "build", job
.uuid
, job
.arch
))
720 s
+= " [%s]" % ", ".join(s_jobs
)
725 def supported_arches(self
):
726 return self
.pkg
.supported_arches
729 def critical_path(self
):
730 return self
.pkg
.critical_path
732 def _get_jobs(self
, query
, *args
):
734 for job
in self
.backend
.jobs
._get
_jobs
(query
, *args
):
743 Get a list of all build jobs that are in this build.
745 return self
._get
_jobs
("SELECT * FROM jobs \
746 WHERE build_id = %s AND test IS FALSE", self
.id)
750 return self
._get
_jobs
("SELECT * FROM jobs \
751 WHERE build_id = %s AND test IS TRUE", self
.id)
754 def all_jobs_finished(self
):
757 for job
in self
.jobs
:
758 if not job
.state
== "finished":
764 def create_autojobs(self
, arches
=None, **kwargs
):
767 # Arches may be passed to this function. If not we use all arches
768 # this package supports.
770 arches
= self
.supported_arches
772 # Create a new job for every given archirecture.
773 for arch
in self
.backend
.arches
.expand(arches
):
774 # Don't create jobs for src
778 job
= self
.add_job(arch
, **kwargs
)
781 # Return all newly created jobs.
784 def add_job(self
, arch
, **kwargs
):
785 job
= self
.backend
.jobs
.create(self
, arch
, **kwargs
)
787 # Add new job to cache.
788 self
.jobs
.append(job
)
796 if not self
.type == "release":
799 # Generate an update ID if none does exist, yet.
800 self
.generate_update_id()
803 "%s" % self
.distro
.name
.replace(" ", "").upper(),
804 "%04d" % (self
.data
.update_year
or 0),
805 "%04d" % (self
.data
.update_num
or 0),
810 def generate_update_id(self
):
811 if not self
.type == "release":
814 if self
.data
.update_num
:
817 update
= self
.db
.get("SELECT update_num AS num FROM builds \
818 WHERE update_year = EXTRACT(year FROM NOW()) ORDER BY update_num DESC LIMIT 1")
821 update_num
= update
.num
+ 1
825 self
.db
.execute("UPDATE builds SET update_year = EXTRACT(year FROM NOW()), update_num = %s \
826 WHERE id = %s", update_num
, self
.id)
830 def get_comments(self
, limit
=10, offset
=0):
831 query
= "SELECT * FROM builds_comments \
832 JOIN users ON builds_comments.user_id = users.id \
833 WHERE build_id = %s ORDER BY time_created ASC"
836 for comment
in self
.db
.query(query
, self
.id):
837 comment
= logs
.CommentLogEntry(self
.backend
, comment
)
838 comments
.append(comment
)
842 def add_comment(self
, user
, text
, score
):
843 # Add the new comment to the database.
844 id = self
.db
.execute("INSERT INTO \
845 builds_comments(build_id, user_id, text, score, time_created) \
846 VALUES(%s, %s, %s, %s, NOW())",
847 self
.id, user
.id, text
, score
)
849 # Update the score cache
852 # Send the new comment to all watchers and stuff.
853 self
.send_comment_message(id)
855 # Return the ID of the newly created comment.
860 res
= self
.db
.get("SELECT SUM(score) AS score \
861 FROM builds_comments WHERE build_id = %s", self
.id)
863 return res
.score
or 0
865 def get_commenters(self
):
866 users
= self
.db
.query("SELECT DISTINCT users.id AS id FROM builds_comments \
867 JOIN users ON builds_comments.user_id = users.id \
868 WHERE builds_comments.build_id = %s AND NOT users.deleted = 'Y' \
869 AND NOT users.activated = 'Y' ORDER BY users.id", self
.id)
871 return [users
.User(self
.backend
, u
.id) for u
in users
]
873 def send_comment_message(self
, comment_id
):
874 comment
= self
.db
.get("SELECT * FROM builds_comments WHERE id = %s",
878 assert comment
.build_id
== self
.id
880 # Get user who wrote the comment.
881 user
= self
.backend
.users
.get_by_id(comment
.user_id
)
884 "build_name" : self
.name
,
885 "user_name" : user
.realname
,
888 # XXX create beautiful message
890 self
.backend
.messages
.send_to_all(self
.message_recipients
,
891 N_("%(user_name)s commented on %(build_name)s"),
892 comment
.text
, format
)
896 def get_log(self
, comments
=True, repo
=True, limit
=None):
900 created_entry
= logs
.CreatedLogEntry(self
.backend
, self
)
901 entries
.append(created_entry
)
904 entries
+= self
.get_comments(limit
=limit
)
907 entries
+= self
.get_repo_moves(limit
=limit
)
909 # Sort all entries in chronological order.
913 entries
= entries
[:limit
]
919 def get_watchers(self
):
920 query
= self
.db
.query("SELECT DISTINCT users.id AS id FROM builds_watchers \
921 JOIN users ON builds_watchers.user_id = users.id \
922 WHERE builds_watchers.build_id = %s AND NOT users.deleted = 'Y' \
923 AND users.activated = 'Y' ORDER BY users.id", self
.id)
925 return [users
.User(self
.backend
, u
.id) for u
in query
]
927 def add_watcher(self
, user
):
928 # Don't add a user twice.
929 if user
in self
.get_watchers():
932 self
.db
.execute("INSERT INTO builds_watchers(build_id, user_id) \
933 VALUES(%s, %s)", self
.id, user
.id)
936 def message_recipients(self
):
939 for watcher
in self
.get_watchers():
940 ret
.append("%s <%s>" % (watcher
.realname
, watcher
.email
))
946 if self
._update
is None:
947 update
= self
.db
.get("SELECT update_id AS id FROM updates_builds \
948 WHERE build_id = %s", self
.id)
951 self
._update
= updates
.Update(self
.backend
, update
.id)
957 res
= self
.db
.get("SELECT repo_id FROM repositories_builds \
958 WHERE build_id = %s", self
.id)
961 return self
.backend
.repos
.get_by_id(res
.repo_id
)
963 def get_repo_moves(self
, limit
=None):
964 query
= "SELECT * FROM repositories_history \
965 WHERE build_id = %s ORDER BY time ASC"
968 for action
in self
.db
.query(query
, self
.id):
969 action
= logs
.RepositoryLogEntry(self
.backend
, action
)
970 actions
.append(action
)
983 repo
= self
.db
.get("SELECT time_added FROM repositories_builds \
984 WHERE build_id = %s", self
.id)
987 return repo
.time_added
989 def get_auto_move(self
):
990 return self
.data
.auto_move
== "Y"
992 def set_auto_move(self
, state
):
993 self
._set
_attribute
("auto_move", state
)
995 auto_move
= property(get_auto_move
, set_auto_move
)
998 def can_move_forward(self
):
1002 # If there is no next repository, we cannot move anything.
1003 if not self
.repo
.next
:
1006 # If the needed amount of score is reached, we can move forward.
1007 if self
.score
>= self
.repo
.next
.score_needed
:
1010 # If the repository does not require a minimal time,
1011 # we can move forward immediately.
1012 if not self
.repo
.time_min
:
1015 query
= self
.db
.get("SELECT NOW() - time_added AS duration FROM repositories_builds \
1016 WHERE build_id = %s", self
.id)
1017 duration
= query
.duration
1019 if duration
>= self
.repo
.time_min
:
1026 def get_bug_ids(self
):
1027 query
= self
.db
.query("SELECT bug_id FROM builds_bugs \
1028 WHERE build_id = %s", self
.id)
1030 return [b
.bug_id
for b
in query
]
1032 def add_bug(self
, bug_id
, user
=None, log
=True):
1033 # Check if this bug is already in the list of bugs.
1034 if bug_id
in self
.get_bug_ids():
1037 self
.db
.execute("INSERT INTO builds_bugs(build_id, bug_id) \
1038 VALUES(%s, %s)", self
.id, bug_id
)
1042 self
.log("bug_added", user
=user
, bug_id
=bug_id
)
1044 def rem_bug(self
, bug_id
, user
=None, log
=True):
1045 self
.db
.execute("DELETE FROM builds_bugs WHERE build_id = %s AND \
1046 bug_id = %s", self
.id, bug_id
)
1050 self
.log("bug_removed", user
=user
, bug_id
=bug_id
)
1052 def search_for_bugs(self
):
1056 pattern
= re
.compile(r
"(bug\s?|#)(\d+)")
1058 for txt
in (self
.commit
.subject
, self
.commit
.message
):
1059 for bug
in re
.finditer(pattern
, txt
):
1061 bugid
= int(bug
.group(2))
1065 # Check if a bug with the given ID exists in BZ.
1066 bug
= self
.backend
.bugzilla
.get_bug(bugid
)
1074 for bug_id
in self
.get_bug_ids():
1075 bug
= self
.backend
.bugzilla
.get_bug(bug_id
)
1083 def _update_bugs_helper(self
, repo
):
1085 This function takes a new status and generates messages that
1086 are appended to all bugs.
1089 kwargs
= BUG_MESSAGES
[repo
.type].copy()
1093 baseurl
= self
.backend
.settings
.get("baseurl", "")
1095 "build_url" : "%s/build/%s" % (baseurl
, self
.uuid
),
1096 "distro_name" : self
.distro
.name
,
1097 "package_name" : self
.name
,
1098 "repo_name" : repo
.name
,
1100 kwargs
["comment"] = kwargs
["comment"] % args
1102 self
.update_bugs(**kwargs
)
1104 def _update_bug(self
, bug_id
, status
=None, resolution
=None, comment
=None):
1105 self
.db
.execute("INSERT INTO builds_bugs_updates(bug_id, status, resolution, comment, time) \
1106 VALUES(%s, %s, %s, %s, NOW())", bug_id
, status
, resolution
, comment
)
1108 def update_bugs(self
, status
, resolution
=None, comment
=None):
1109 # Update all bugs linked to this build.
1110 for bug_id
in self
.get_bug_ids():
1111 self
._update
_bug
(bug_id
, status
=status
, resolution
=resolution
, comment
=comment
)