class LegalFlag(db.Model, helpers.Serializer): id = db.Column(db.Integer, primary_key=True) # message from user who raised the flag (what he thinks is wrong) raise_message = db.Column(db.Text) # time of raising the flag as returned by int(time.time()) raised_on = db.Column(db.Integer) # time of resolving the flag by admin as returned by int(time.time()) resolved_on = db.Column(db.Integer) # relations copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), nullable=True) # cascade="all" means that we want to keep these even if copr is deleted copr = db.relationship("Copr", backref=db.backref("legal_flags", cascade="all")) # user who reported the problem reporter_id = db.Column(db.Integer, db.ForeignKey("user.id")) reporter = db.relationship("User", backref=db.backref("legal_flags_raised"), foreign_keys=[reporter_id], primaryjoin="LegalFlag.reporter_id==User.id") # admin who resolved the problem resolver_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=True) resolver = db.relationship("User", backref=db.backref("legal_flags_resolved"), foreign_keys=[resolver_id], primaryjoin="LegalFlag.resolver_id==User.id")
class CoprPermission(db.Model, helpers.Serializer): """ Association class for Copr<->Permission relation """ # see helpers.PermissionEnum for possible values of the fields below # can this user build in the copr? copr_builder = db.Column(db.SmallInteger, default=0) # can this user serve as an admin? (-> edit and approve permissions) copr_admin = db.Column(db.SmallInteger, default=0) # relations user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True) user = db.relationship("User", backref=db.backref("copr_permissions")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True) copr = db.relationship("Copr", backref=db.backref("copr_permissions"))
class CoprChroot(db.Model, helpers.Serializer): """ Representation of Copr<->MockChroot relation """ buildroot_pkgs = db.Column(db.Text) mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("copr_chroots")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True) copr = db.relationship("Copr", backref=db.backref( "copr_chroots", single_parent=True, cascade="all,delete,delete-orphan")) comps_zlib = db.Column(db.LargeBinary(), nullable=True) comps_name = db.Column(db.String(127), nullable=True) def update_comps(self, comps_xml): self.comps_zlib = zlib.compress(comps_xml.encode("utf-8")) @property def buildroot_pkgs_list(self): return self.buildroot_pkgs.split() @property def comps(self): if self.comps_zlib: return zlib.decompress(self.comps_zlib).decode("utf-8") @property def comps_len(self): if self.comps_zlib: return len(zlib.decompress(self.comps_zlib)) else: return 0 @property def name(self): return self.mock_chroot.name @property def is_active(self): return self.mock_chroot.is_active
class CoprChroot(db.Model, helpers.Serializer): """ Representation of Copr<->MockChroot relation """ buildroot_pkgs = db.Column(db.Text) mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("copr_chroots")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True) copr = db.relationship("Copr", backref=db.backref( "copr_chroots", single_parent=True, cascade="all,delete,delete-orphan"))
class BuildChroot(db.Model, helpers.Serializer): """ Representation of Build<->MockChroot relation """ mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("builds")) build_id = db.Column(db.Integer, db.ForeignKey("build.id"), primary_key=True) build = db.relationship("Build", backref=db.backref("build_chroots")) status = db.Column(db.Integer, default=helpers.StatusEnum("pending")) @property def name(self): """ Textual representation of name of this chroot """ return self.mock_chroot.name @property def state(self): """ Return text representation of status of this build chroot """ if self.status is not None: return helpers.StatusEnum(self.status) return "unknown" def __str__(self): return "<BuildChroot: {}>".format(self.to_dict())
class Krb5Login(db.Model, helpers.Serializer): """ Represents additional user information for kerberos authentication. """ __tablename__ = "krb5_login" # FK to User table user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) # 'string' from 'copr.conf' from KRB5_LOGIN[string] config_name = db.Column(db.String(30), nullable=False, primary_key=True) # krb's primary, i.e. 'username' from '*****@*****.**' primary = db.Column(db.String(80), nullable=False, primary_key=True) user = db.relationship("User", backref=db.backref("krb5_logins"))
class Package(db.Model, helpers.Serializer): """ Represents a single package in a project. """ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) # Source of the build: type identifier source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset")) # Source of the build: description in json, example: git link, srpm url, etc. source_json = db.Column(db.Text) # True if the package is built automatically via webhooks webhook_rebuild = db.Column(db.Boolean, default=False) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # relations copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("packages")) @property def dist_git_repo(self): if self.copr.is_a_group_project: return "@{}/{}/{}".format(self.copr.group.name, self.copr.name, self.name) else: return "{}/{}/{}".format(self.copr.owner.name, self.copr.name, self.name) @property def source_json_dict(self): return json.loads(self.source_json) @property def source_type_text(self): return helpers.BuildSourceEnum(self.source_type) @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git".format(app.config["DIST_GIT_URL"], self.dist_git_repo) return None
class Build(db.Model, helpers.Serializer): """ Representation of one build in one copr """ id = db.Column(db.Integer, primary_key=True) # single url to the source rpm, should not contain " ", "\n", "\t" pkgs = db.Column(db.Text) # built packages built_packages = db.Column(db.Text) # version of the srpm package got by rpm pkg_version = db.Column(db.Text) # was this build canceled by user? canceled = db.Column(db.Boolean, default=False) # list of space separated additional repos repos = db.Column(db.Text) # the three below represent time of important events for this build # as returned by int(time.time()) submitted_on = db.Column(db.Integer, nullable=False) started_on = db.Column(db.Integer) ended_on = db.Column(db.Integer) # url of the build results results = db.Column(db.Text) # memory requirements for backend builder memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY) # maximum allowed time of build, build will fail if exceeded timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # relations user_id = db.Column(db.Integer, db.ForeignKey("user.id")) user = db.relationship("User", backref=db.backref("builds")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("builds")) chroots = association_proxy("build_chroots", "mock_chroot") @property def chroot_states(self): return map(lambda chroot: chroot.status, self.build_chroots) def get_chroots_by_status(self, statuses=None): """ Get build chroots with states which present in `states` list If states == None, function returns build_chroots """ chroot_states_map = dict(zip(self.build_chroots, self.chroot_states)) if statuses is not None: statuses = set(statuses) else: return self.build_chroots return [ chroot for chroot, status in chroot_states_map.items() if status in statuses ] @property def has_pending_chroot(self): # FIXME bad name # used when checking if the repo is initialized and results can be set # i think this is the only purpose - check return helpers.StatusEnum("pending") in self.chroot_states or \ helpers.StatusEnum("starting") in self.chroot_states @property def has_unfinished_chroot(self): return helpers.StatusEnum("pending") in self.chroot_states or \ helpers.StatusEnum("starting") in self.chroot_states or \ helpers.StatusEnum("running") in self.chroot_states @property def status(self): """ Return build status according to build status of its chroots """ if self.canceled: return helpers.StatusEnum("canceled") for state in [ "failed", "running", "starting", "pending", "succeeded", "skipped" ]: if helpers.StatusEnum(state) in self.chroot_states: return helpers.StatusEnum(state) @property def state(self): """ Return text representation of status of this build """ if self.status is not None: return helpers.StatusEnum(self.status) return "unknown" @property def cancelable(self): """ Find out if this build is cancelable. Build is cancelabel only when it's pending (not started) """ return self.status == helpers.StatusEnum("pending") @property def repeatable(self): """ Find out if this build is repeatable. Build is repeatable only if it's not pending, starting or running """ return self.status not in [ helpers.StatusEnum("pending"), helpers.StatusEnum("starting"), helpers.StatusEnum("running"), ] @property def deletable(self): """ Find out if this build is deletable. Build is deletable only when it's finished. (also means cancelled) It is important to remember that "failed" state doesn't ultimately mean it's finished - so we need to check whether the "ended_on" property has been set. """ if self.state == "failed" and self.ended_on is not None: return True return self.state in ["succeeded", "canceled", "skipped"] @property def src_pkg_name(self): """ Extract source package name from URL """ src_rpm_name = self.pkgs.split("/")[-1] if src_rpm_name.endswith(".src.rpm"): return src_rpm_name[:-8] else: return src_rpm_name
class Copr(db.Model, helpers.Serializer): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations owner_id = db.Column(db.Integer, db.ForeignKey("user.id")) owner = db.relationship("User", backref=db.backref("coprs")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) __mapper_args__ = {"order_by": created_on.desc()} @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) def check_copr_chroot(self, chroot): """ Return object of chroot, if is related to our copr or None """ result = None # there will be max ~10 chroots per build, iteration will be probably # faster than sql query for copr_chroot in self.copr_chroots: if copr_chroot.mock_chroot_id == chroot.id: result = copr_chroot break return result def buildroot_pkgs(self, chroot): """ Return packages in minimal buildroot for given chroot. """ result = "" # this is ugly as user can remove chroot after he submit build, but # lets call this feature copr_chroot = self.check_copr_chroot(chroot) if copr_chroot: result = copr_chroot.buildroot_pkgs return result @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.active_chroots: if self.buildroot_pkgs(chroot): modified_chroots.append(chroot) return modified_chroots
class BuildChroot(db.Model, helpers.Serializer): """ Representation of Build<->MockChroot relation """ mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("builds")) build_id = db.Column(db.Integer, db.ForeignKey("build.id"), primary_key=True) build = db.relationship("Build", backref=db.backref("build_chroots")) git_hash = db.Column(db.String(40)) status = db.Column(db.Integer, default=StatusEnum("importing")) started_on = db.Column(db.Integer) ended_on = db.Column(db.Integer) last_deferred = db.Column(db.Integer) @property def name(self): """ Textual representation of name of this chroot """ return self.mock_chroot.name @property def state(self): """ Return text representation of status of this build chroot """ if self.status is not None: return StatusEnum(self.status) return "unknown" @property def task_id(self): return "{}-{}".format(self.build_id, self.name) @property def import_task_id(self): return "{}-{}".format(self.build_id, helpers.chroot_to_branch(self.name)) @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git/commit/?id={}".format( app.config["DIST_GIT_URL"], self.build.package.dist_git_repo, self.git_hash) return None @property def import_log_url(self): if app.config["COPR_DIST_GIT_LOGS_URL"]: return "{}/{}.log".format(app.config["COPR_DIST_GIT_LOGS_URL"], self.import_task_id) return None @property def result_dir_url(self): return "/".join( [app.config["BACKEND_BASE_URL"], u"results", self.result_dir]) @property def result_dir(self): # hide changes occurred after migration to dist-git # if build has defined dist-git, it means that new schema should be used # otherwise use older structure # old: results/valtri/ruby/fedora-rawhide-x86_64/rubygem-aws-sdk-resources-2.1.11-1.fc24/ # new: results/asamalik/rh-perl520/epel-7-x86_64/00000187-rh-perl520/ parts = [self.build.copr.owner_name] parts.extend([ self.build.copr.name, self.name, ]) if self.git_hash is not None and self.build.package: parts.append(self.build.result_dir_name) else: parts.append(self.build.src_pkg_name) return os.path.join(*parts) def __str__(self): return "<BuildChroot: {}>".format(self.to_dict())
class Build(db.Model, helpers.Serializer): """ Representation of one build in one copr """ __table_args__ = (db.Index('build_canceled', "canceled"), ) id = db.Column(db.Integer, primary_key=True) # single url to the source rpm, should not contain " ", "\n", "\t" pkgs = db.Column(db.Text) # built packages built_packages = db.Column(db.Text) # version of the srpm package got by rpm pkg_version = db.Column(db.Text) # was this build canceled by user? canceled = db.Column(db.Boolean, default=False) # list of space separated additional repos repos = db.Column(db.Text) # the three below represent time of important events for this build # as returned by int(time.time()) submitted_on = db.Column(db.Integer, nullable=False) # url of the build results results = db.Column(db.Text) # memory requirements for backend builder memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY) # maximum allowed time of build, build will fail if exceeded timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # Source of the build: type identifier source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset")) # Source of the build: description in json, example: git link, srpm url, etc. source_json = db.Column(db.Text) # Type of failure: type identifier fail_type = db.Column(db.Integer, default=helpers.FailTypeEnum("unset")) # background builds has lesser priority than regular builds. is_background = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # relations user_id = db.Column(db.Integer, db.ForeignKey("user.id")) user = db.relationship("User", backref=db.backref("builds")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("builds")) package_id = db.Column(db.Integer, db.ForeignKey("package.id")) package = db.relationship("Package") chroots = association_proxy("build_chroots", "mock_chroot") @property def user_name(self): return self.user.name @property def group_name(self): return self.copr.group.name @property def copr_name(self): return self.copr.name @property def fail_type_text(self): return helpers.FailTypeEnum(self.fail_type) @property def is_older_results_naming_used(self): # we have changed result directory naming together with transition to dist-git # that's why we use so strange criterion return self.build_chroots[0].git_hash is None @property def repos_list(self): if self.repos is None: return list() else: return self.repos.split() @property def result_dir_name(self): # We can remove this ugly condition after migrating Copr to new machines # It is throw-back from era before dist-git if self.is_older_results_naming_used: return self.src_pkg_name return "{:08d}-{}".format(self.id, self.package.name) @property def source_json_dict(self): if not self.source_json: return {} return json.loads(self.source_json) @property def started_on(self): return self.min_started_on @property def min_started_on(self): mb_list = [ chroot.started_on for chroot in self.build_chroots if chroot.started_on ] if len(mb_list) > 0: return min(mb_list) else: return None @property def ended_on(self): return self.max_ended_on @property def max_ended_on(self): if not self.build_chroots: return None if any(chroot.ended_on is None for chroot in self.build_chroots): return None return max(chroot.ended_on for chroot in self.build_chroots) @property def chroots_started_on(self): return { chroot.name: chroot.started_on for chroot in self.build_chroots } @property def chroots_ended_on(self): return {chroot.name: chroot.ended_on for chroot in self.build_chroots} @property def source_type_text(self): return helpers.BuildSourceEnum(self.source_type) @property def source_metadata(self): if self.source_json is None: return None try: return json.loads(self.source_json) except (TypeError, ValueError): return None @property def chroot_states(self): return map(lambda chroot: chroot.status, self.build_chroots) def get_chroots_by_status(self, statuses=None): """ Get build chroots with states which present in `states` list If states == None, function returns build_chroots """ chroot_states_map = dict(zip(self.build_chroots, self.chroot_states)) if statuses is not None: statuses = set(statuses) else: return self.build_chroots return [ chroot for chroot, status in chroot_states_map.items() if status in statuses ] @property def chroots_dict_by_name(self): return {b.name: b for b in self.build_chroots} @property def has_pending_chroot(self): # FIXME bad name # used when checking if the repo is initialized and results can be set # i think this is the only purpose - check return StatusEnum("pending") in self.chroot_states or \ StatusEnum("starting") in self.chroot_states @property def has_unfinished_chroot(self): return StatusEnum("pending") in self.chroot_states or \ StatusEnum("starting") in self.chroot_states or \ StatusEnum("running") in self.chroot_states @property def has_importing_chroot(self): return StatusEnum("importing") in self.chroot_states @property def status(self): """ Return build status according to build status of its chroots """ if self.canceled: return StatusEnum("canceled") for state in [ "running", "starting", "importing", "pending", "failed", "succeeded", "skipped" ]: if StatusEnum(state) in self.chroot_states: return StatusEnum(state) @property def state(self): """ Return text representation of status of this build """ if self.status is not None: return StatusEnum(self.status) return "unknown" @property def cancelable(self): """ Find out if this build is cancelable. Build is cancelabel only when it's pending (not started) """ return self.status == StatusEnum("pending") or \ self.status == StatusEnum("importing") @property def repeatable(self): """ Find out if this build is repeatable. Build is repeatable only if it's not pending, starting or running """ return self.status not in [ StatusEnum("pending"), StatusEnum("starting"), StatusEnum("running"), ] @property def finished(self): """ Find out if this build is in finished state. Build is finished only if all its build_chroots are in finished state. """ return all([(chroot.state in ["succeeded", "canceled", "skipped", "failed"]) for chroot in self.build_chroots]) @property def persistent(self): """ Find out if this build is persistent. This property is inherited from the project. """ return self.copr.persistent @property def src_pkg_name(self): """ Extract source package name from source name or url todo: obsolete """ try: src_rpm_name = self.pkgs.split("/")[-1] except: return None if src_rpm_name.endswith(".src.rpm"): return src_rpm_name[:-8] else: return src_rpm_name @property def package_name(self): try: return self.package.name except: return None def to_dict(self, options=None, with_chroot_states=False): result = super(Build, self).to_dict(options) result["src_pkg"] = result["pkgs"] del result["pkgs"] del result["copr_id"] result['source_type'] = helpers.BuildSourceEnum(result['source_type']) result["state"] = self.state if with_chroot_states: result["chroots"] = {b.name: b.state for b in self.build_chroots} return result
class Package(db.Model, helpers.Serializer, CoprSearchRelatedData): """ Represents a single package in a project. """ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) # Source of the build: type identifier source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset")) # Source of the build: description in json, example: git link, srpm url, etc. source_json = db.Column(db.Text) # True if the package is built automatically via webhooks webhook_rebuild = db.Column(db.Boolean, default=False) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # @TODO Remove me few weeks after Copr migration # Contain status of the Package before migration # Normally the `status` is not stored in `Package`. It is computed from `status` variable of `BuildChroot`, # but `old_status` has to be stored here, because we migrate whole `package` table, but only succeeded builds. # Therefore if `old_status` was in `BuildChroot` we wouldn't be able to know old state of non-succeeded packages # even though it would be known before migration. old_status = db.Column(db.Integer) builds = db.relationship("Build", order_by="Build.id") # relations copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("packages")) @property def dist_git_repo(self): return "{}/{}".format(self.copr.full_name, self.name) @property def source_json_dict(self): if not self.source_json: return {} return json.loads(self.source_json) @property def source_type_text(self): return helpers.BuildSourceEnum(self.source_type) @property def has_source_type_set(self): """ Package's source type (and source_json) is being derived from its first build, which works except for "srpm_link" and "srpm_upload" cases. Consider these being equivalent to source_type being unset. """ return self.source_type and self.source_type_text != "srpm_link" and self.source_type_text != "srpm_upload" @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git".format(app.config["DIST_GIT_URL"], self.dist_git_repo) return None def last_build(self, successful=False): for build in reversed(self.builds): if not successful or build.state == "succeeded": return build return None def to_dict(self, with_latest_build=False, with_latest_succeeded_build=False, with_all_builds=False): package_dict = super(Package, self).to_dict() package_dict['source_type'] = helpers.BuildSourceEnum( package_dict['source_type']) if with_latest_build: build = self.last_build(successful=False) package_dict['latest_build'] = build.to_dict( with_chroot_states=True) if build else None if with_latest_succeeded_build: build = self.last_build(successful=True) package_dict['latest_succeeded_build'] = build.to_dict( with_chroot_states=True) if build else None if with_all_builds: package_dict['builds'] = [ build.to_dict(with_chroot_states=True) for build in reversed(self.builds) ] return package_dict def get_search_related_copr_id(self): return self.copr.id
class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) homepage = db.Column(db.Text) contact = db.Column(db.Text) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations user_id = db.Column(db.Integer, db.ForeignKey("user.id")) user = db.relationship("User", backref=db.backref("coprs")) group_id = db.Column(db.Integer, db.ForeignKey("group.id")) group = db.relationship("Group", backref=db.backref("groups")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id")) forked_from = db.relationship("Copr", remote_side=id, backref=db.backref("forks")) # a secret to be used for webhooks authentication webhook_secret = db.Column(db.String(100)) # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) unlisted_on_hp = db.Column(db.Boolean, default=False, nullable=False) # information for search index updating latest_indexed_data_update = db.Column(db.Integer) # builds and the project are immune against deletion persistent = db.Column(db.Boolean, default=False, nullable=False, server_default="0") __mapper_args__ = {"order_by": created_on.desc()} @property def is_a_group_project(self): """ Return True if copr belongs to a group """ return self.group_id is not None @property def owner(self): """ Return owner (user or group) of this copr """ return self.group if self.is_a_group_project else self.user @property def owner_name(self): """ Return @group.name for a copr owned by a group and user.name otherwise """ return self.group.at_name if self.is_a_group_project else self.user.name @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def active_copr_chroots(self): """ :rtype: list of CoprChroot """ return [c for c in self.copr_chroots if c.is_active] @property def active_chroots_sorted(self): """ Return list of active mock_chroots of this copr """ return sorted(self.active_chroots, key=lambda ch: ch.name) @property def active_chroots_grouped(self): """ Return list of active mock_chroots of this copr """ chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted] output = [] for os, chs in itertools.groupby(chroots, operator.itemgetter(0)): output.append((os, [ch[1] for ch in chs])) return output @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.copr_chroots: if chroot.buildroot_pkgs and chroot.is_active: modified_chroots.append(chroot) return modified_chroots def is_release_arch_modified(self, name_release, arch): if "{}-{}".format(name_release, arch) in \ [chroot.name for chroot in self.modified_chroots]: return True return False @property def full_name(self): return "{}/{}".format(self.owner_name, self.name) @property def repo_name(self): return "{}-{}".format(self.owner_name, self.name) @property def repo_url(self): return "/".join( [app.config["BACKEND_BASE_URL"], u"results", self.full_name]) @property def modules_url(self): return "/".join([self.repo_url, "modules"]) @property def repo_id(self): if self.is_a_group_project: return "group_{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.user.name, self.name) def to_dict(self, private=False, show_builds=True, show_chroots=True): result = {} for key in ["id", "name", "description", "instructions"]: result[key] = str(copy.copy(getattr(self, key))) result["owner"] = self.owner_name return result @property def still_forking(self): return bool( Action.query.filter( Action.result == helpers.BackendResultEnum("waiting")).filter( Action.action_type == helpers.ActionTypeEnum("fork")). filter(Action.new_value == self.full_name).all()) def get_search_related_copr_id(self): return self.id
class Copr(db.Model, helpers.Serializer): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) homepage = db.Column(db.Text) contact = db.Column(db.Text) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations owner_id = db.Column(db.Integer, db.ForeignKey("user.id")) owner = db.relationship("User", backref=db.backref("coprs")) group_id = db.Column(db.Integer, db.ForeignKey("group.id")) group = db.relationship("Group", backref=db.backref("groups")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") # a secret to be used for webhooks authentication webhook_secret = db.Column(db.String(100)) # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) __mapper_args__ = {"order_by": created_on.desc()} @property def is_a_group_project(self): return self.group_id is not None @property def owner_name(self): return self.owner.name @property def group_name(self): return self.group.name @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def active_copr_chroots(self): """ :rtype: list of CoprChroot """ return [c for c in self.copr_chroots if c.is_active] @property def active_chroots_sorted(self): """ Return list of active mock_chroots of this copr """ return sorted(self.active_chroots, key=lambda ch: ch.name) @property def active_chroots_grouped(self): """ Return list of active mock_chroots of this copr """ chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted] output = [] for os, chs in itertools.groupby(chroots, operator.itemgetter(0)): output.append((os, [ch[1] for ch in chs])) return output @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.copr_chroots: if chroot.buildroot_pkgs and chroot.is_active: modified_chroots.append(chroot) return modified_chroots def is_release_arch_modified(self, name_release, arch): if "{}-{}".format(name_release, arch) in \ [chroot.name for chroot in self.modified_chroots]: return True return False @property def full_name(self): if self.is_a_group_project: return "@{}/{}".format(self.group.name, self.name) else: return "{}/{}".format(self.owner.username, self.name) @property def repo_name(self): if self.is_a_group_project: return "@{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.owner.username, self.name) @property def repo_id(self): if self.is_a_group_project: return "group_{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.owner.username, self.name) def to_dict(self, private=False, show_builds=True, show_chroots=True): result = {} for key in ["id", "name", "description", "instructions"]: result[key] = str(copy.copy(getattr(self, key))) result["owner"] = self.owner.name return result