Package coprs :: Package logic :: Module packages_logic
[hide private]
[frames] | no frames]

Source Code for Module coprs.logic.packages_logic

  1  import json 
  2  import re 
  3   
  4  from sqlalchemy import bindparam, Integer, func 
  5  from sqlalchemy.sql import true, text 
  6  from sqlalchemy.orm import selectinload 
  7   
  8  from coprs import app 
  9  from coprs import db 
 10  from coprs import exceptions 
 11  from coprs import models 
 12  from coprs import helpers 
 13   
 14  from coprs.logic import users_logic 
 15  from coprs.logic import builds_logic 
 16  from copr_common.enums import StatusEnum 
 17   
 18  log = app.logger 
19 20 21 -class PackagesLogic(object):
22 23 @classmethod
24 - def get_by_id(cls, package_id):
26 27 @classmethod
28 - def get_all(cls, copr_dir_id):
31 32 @classmethod
33 - def get_all_in_copr(cls, copr_id):
34 return (models.Package.query 35 .filter(models.Package.copr_id == copr_id))
36 37 @classmethod
38 - def get_packages_with_latest_builds_for_dir(cls, copr_dir_id):
39 packages = (models.Package.query.filter_by(copr_dir_id=copr_dir_id) 40 .order_by(models.Package.name).all()) 41 pkg_ids = [package.id for package in packages] 42 builds_ids = models.Build.query \ 43 .filter(models.Build.package_id.in_(pkg_ids)) \ 44 .with_entities(func.max(models.Build.id)) \ 45 .group_by(models.Build.package_id) 46 47 # map package.id => package object in packages array 48 packages_map = {package.id: package for package in packages} 49 50 builds = (models.Build.query.filter(models.Build.id.in_(builds_ids)) 51 .options(selectinload('build_chroots')) 52 .yield_per(1000)) 53 54 for build in builds: 55 class SmallBuild(): 56 pass
57 58 if not build.package_id: 59 continue 60 61 small_build_object = SmallBuild() 62 for param in ['state', 'status', 'pkg_version', 63 'submitted_on']: 64 # we don't want to keep all the attributes here in memory, and 65 # also we don't need any further info about assigned 66 # build_chroot(s). So we only pick the info we need, and throw 67 # the expensive objects away. 68 setattr(small_build_object, param, getattr(build, param)) 69 packages_map[build.package_id].latest_build = small_build_object 70 71 return packages
72 73 @classmethod
74 - def get_list_by_copr(cls, copr_id, package_name):
75 return models.Package.query.filter(models.Package.copr_id == copr_id, 76 models.Package.name == package_name)
77 78 @classmethod
79 - def get(cls, copr_dir_id, package_name):
80 return models.Package.query.filter(models.Package.copr_dir_id == copr_dir_id, 81 models.Package.name == package_name)
82 83 @classmethod
84 - def get_by_dir(cls, copr_dir, package_name):
85 return models.Package.query.join(models.CoprDir).filter( 86 models.CoprDir.id==copr_dir.id, 87 models.Package.name==package_name 88 )
89 90 @classmethod
91 - def get_or_create(cls, copr_dir, package_name, src_pkg):
92 package = cls.get_by_dir(copr_dir, package_name).first() 93 94 if package: 95 return package 96 97 package = models.Package( 98 name=src_pkg.name, 99 copr=src_pkg.copr, 100 source_type=src_pkg.source_type, 101 source_json=src_pkg.source_json, 102 copr_dir=copr_dir) 103 104 db.session.add(package) 105 return package
106 107 @classmethod
108 - def get_for_webhook_rebuild(cls, copr_id, webhook_secret, clone_url, commits, ref_type, ref):
109 clone_url_stripped = re.sub(r'(\.git)?/*$', '', clone_url) 110 111 packages = (models.Package.query.join(models.Copr) 112 .filter(models.Copr.webhook_secret == webhook_secret) 113 .filter(models.Package.source_type == helpers.BuildSourceEnum("scm")) 114 .filter(models.Package.copr_id == copr_id) 115 .filter(models.Package.webhook_rebuild == true()) 116 .filter(models.Package.source_json.contains(clone_url_stripped))) 117 118 result = [] 119 for package in packages: 120 package_clone_url = package.source_json_dict.get('clone_url', '') 121 package_clone_url_stripped = re.sub(r'(\.git)?/*$', '', package_clone_url) 122 123 if package_clone_url_stripped != clone_url_stripped: 124 continue 125 126 if cls.commits_belong_to_package(package, commits, ref_type, ref): 127 result += [package] 128 129 return result
130 131 @classmethod
132 - def commits_belong_to_package(cls, package, commits, ref_type, ref):
133 if ref_type == "tag": 134 matches = re.search(r'(.*)-[^-]+-[^-]+$', ref) 135 if matches and package.name != matches.group(1): 136 return False 137 else: 138 return True 139 140 committish = package.source_json_dict.get("committish") or '' 141 if committish and not ref.endswith(committish): 142 return False 143 144 for commit in commits: 145 subdir = package.source_json_dict.get('subdirectory') 146 sm = helpers.SubdirMatch(subdir) 147 changed = set() 148 for ch in ['added', 'removed', 'modified']: 149 changed |= set(commit.get(ch, [])) 150 151 for file_path in changed: 152 if sm.match(file_path): 153 return True 154 155 return False
156 157 @classmethod
158 - def add(cls, user, copr_dir, package_name, source_type=helpers.BuildSourceEnum("unset"), source_json=json.dumps({})):
159 users_logic.UsersLogic.raise_if_cant_build_in_copr( 160 user, copr_dir.copr, 161 "You don't have permissions to build in this copr.") 162 163 if cls.exists(copr_dir.id, package_name).all(): 164 raise exceptions.DuplicateException( 165 "Project dir {} already has a package '{}'" 166 .format(copr_dir.full_name, package_name)) 167 168 package = models.Package( 169 name=package_name, 170 copr=copr_dir.copr, 171 copr_dir=copr_dir, 172 source_type=source_type, 173 source_json=source_json, 174 ) 175 176 db.session.add(package) 177 return package
178 179 @classmethod
180 - def exists(cls, copr_dir_id, package_name):
181 return (models.Package.query 182 .filter(models.Package.copr_dir_id == copr_dir_id) 183 .filter(models.Package.name == package_name))
184 185 186 @classmethod
187 - def delete_package(cls, user, package):
188 if not user.can_edit(package.copr): 189 raise exceptions.InsufficientRightsException( 190 "You are not allowed to delete package `{}`.".format(package.id)) 191 192 to_delete = [] 193 for build in package.builds: 194 to_delete.append(build) 195 196 builds_logic.BuildsLogic.delete_multiple_builds(user, to_delete) 197 db.session.delete(package)
198 199 200 @classmethod
201 - def reset_package(cls, user, package):
202 if not user.can_edit(package.copr): 203 raise exceptions.InsufficientRightsException( 204 "You are not allowed to reset package `{}`.".format(package.id)) 205 206 package.source_json = json.dumps({}) 207 package.source_type = helpers.BuildSourceEnum("unset") 208 209 db.session.add(package)
210 211 212 @classmethod
213 - def build_package(cls, user, copr, package, chroot_names=None, copr_dirname=None, **build_options):
214 if not package.has_source_type_set or not package.source_json: 215 raise exceptions.NoPackageSourceException('Unset default source for package {0}'.format(package.name)) 216 return builds_logic.BuildsLogic.create_new(user, copr, package.source_type, package.source_json, 217 chroot_names, copr_dirname=copr_dirname, **build_options)
218 219 220 @classmethod
221 - def batch_build(cls, user, copr, packages, chroot_names=None, **build_options):
222 new_builds = [] 223 224 batch = models.Batch() 225 db.session.add(batch) 226 227 for package in packages: 228 git_hashes = {} 229 skip_import = False 230 source_build = None 231 232 if (package.source_type == helpers.BuildSourceEnum('upload') or 233 package.source_type == helpers.BuildSourceEnum('link')): 234 source_build = package.last_build() 235 236 if not source_build or not source_build.build_chroots[0].git_hash: 237 raise exceptions.NoPackageSourceException( 238 "Could not get latest git hash for {}".format(package.name)) 239 240 for chroot_name in chroot_names: 241 git_hashes[chroot_name] = source_build.build_chroots[0].git_hash 242 skip_import = True 243 244 new_build = builds_logic.BuildsLogic.create_new( 245 user, 246 copr, 247 package.source_type, 248 package.source_json, 249 chroot_names, 250 git_hashes=git_hashes, 251 skip_import=skip_import, 252 batch=batch, 253 **build_options) 254 255 if source_build: 256 new_build.package_id = source_build.package_id 257 new_build.pkg_version = source_build.pkg_version 258 259 new_builds.append(new_build) 260 261 return new_builds
262 263 @classmethod
264 - def delete_orphaned_packages(cls):
265 pkgs_to_delete = models.Package.query\ 266 .join(models.Copr, models.Package.copr_id == models.Copr.id)\ 267 .filter(models.Copr.deleted == True) 268 269 counter = 0 270 for pkg in pkgs_to_delete: 271 cls.delete_package(pkg.copr.user, pkg) 272 counter += 1 273 if counter >= 100: 274 db.session.commit() 275 counter = 0 276 277 if counter > 0: 278 db.session.commit()
279 280 @classmethod
281 - def last_successful_build_chroots(cls, package):
282 builds = {} 283 for chroot in package.chroots: 284 for build in reversed(package.builds): 285 try: 286 build_chroot = build.chroots_dict_by_name[chroot.name] 287 except KeyError: 288 continue 289 if build_chroot.status not in [StatusEnum("succeeded"), StatusEnum("forked")]: 290 continue 291 if build not in builds: 292 builds[build] = [build_chroot] 293 else: 294 builds[build].append(build_chroot) 295 break 296 return builds
297