Package coprs :: Module forms
[hide private]
[frames] | no frames]

Source Code for Module coprs.forms

   1  import re 
   2  from six.moves.urllib.parse import urlparse 
   3   
   4  import flask 
   5  import wtforms 
   6  import json 
   7   
   8  from flask_wtf.file import FileRequired, FileField 
   9  from fnmatch import fnmatch 
  10   
  11  try: # get rid of deprecation warning with newer flask_wtf 
  12      from flask_wtf import FlaskForm 
  13  except ImportError: 
  14      from flask_wtf import Form as FlaskForm 
  15   
  16  from coprs import constants 
  17  from coprs import app 
  18  from coprs import helpers 
  19  from coprs import models 
  20  from coprs.logic.coprs_logic import CoprsLogic, MockChrootsLogic 
  21  from coprs.logic.users_logic import UsersLogic 
  22  from coprs import exceptions 
  23   
  24   
  25  FALSE_VALUES = {False, "false", ""} 
26 27 28 -def get_package_form_cls_by_source_type_text(source_type_text):
29 """ 30 Params 31 ------ 32 source_type_text : str 33 name of the source type (scm/pypi/rubygems/git_and_tito/mock_scm) 34 35 Returns 36 ------- 37 BasePackageForm child 38 based on source_type_text input 39 """ 40 if source_type_text == 'scm': 41 return PackageFormScm 42 elif source_type_text == 'pypi': 43 return PackageFormPyPI 44 elif source_type_text == 'rubygems': 45 return PackageFormRubyGems 46 elif source_type_text == 'git_and_tito': 47 return PackageFormTito # deprecated 48 elif source_type_text == 'mock_scm': 49 return PackageFormMock # deprecated 50 elif source_type_text == "custom": 51 return PackageFormCustom 52 else: 53 raise exceptions.UnknownSourceTypeException("Invalid source type")
54
55 56 -class MultiCheckboxField(wtforms.SelectMultipleField):
57 widget = wtforms.widgets.ListWidget(prefix_label=False) 58 option_widget = wtforms.widgets.CheckboxInput()
59
60 61 -class UrlListValidator(object):
62
63 - def __init__(self, message=None):
64 if not message: 65 message = ("A list of http[s] URLs separated by whitespace characters" 66 " is needed ('{0}' doesn't seem to be a valid URL).") 67 self.message = message
68
69 - def __call__(self, form, field):
70 urls = field.data.split() 71 for u in urls: 72 if not self.is_url(u): 73 raise wtforms.ValidationError(self.message.format(u))
74
75 - def is_url(self, url):
76 parsed = urlparse(url) 77 if not parsed.scheme.startswith("http"): 78 return False 79 if not parsed.netloc: 80 return False 81 return True
82
83 84 -class UrlRepoListValidator(UrlListValidator):
85 """ Allows also `repo://` schema"""
86 - def is_url(self, url):
87 parsed = urlparse(url) 88 if parsed.scheme not in ["http", "https", "copr"]: 89 return False 90 if not parsed.netloc: 91 return False 92 # copr://username/projectname 93 # ^^ schema ^^ netlock ^^ path 94 if parsed.scheme == "copr": 95 # check if projectname missed 96 path_split = parsed.path.split("/") 97 if len(path_split) < 2 or path_split[1] == "": 98 return False 99 100 return True
101
102 103 -class UrlSrpmListValidator(UrlListValidator):
104 - def __init__(self, message=None):
105 if not message: 106 message = ("URLs must end with .src.rpm, .nosrc.rpm, or .spec" 107 " ('{0}' doesn't seem to be a valid URL).") 108 super(UrlSrpmListValidator, self).__init__(message)
109
110 - def is_url(self, url):
111 parsed = urlparse(url) 112 if not parsed.path.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 113 return False 114 return True
115
116 117 -class SrpmValidator(object):
118 - def __init__(self, message=None):
119 if not message: 120 message = "You can upload only .src.rpm, .nosrc.rpm, and .spec files" 121 self.message = message
122
123 - def __call__(self, form, field):
124 filename = field.data.filename.lower() 125 if not filename.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 126 raise wtforms.ValidationError(self.message)
127
128 129 -class CoprUniqueNameValidator(object):
130
131 - def __init__(self, message=None, user=None, group=None):
132 if not message: 133 if group is None: 134 message = "You already have project named '{}'." 135 else: 136 message = "Group {} ".format(group) + "already have project named '{}'." 137 self.message = message 138 if not user: 139 user = flask.g.user 140 self.user = user 141 self.group = group
142
143 - def __call__(self, form, field):
144 if self.group: 145 existing = CoprsLogic.exists_for_group( 146 self.group, field.data).first() 147 else: 148 existing = CoprsLogic.exists_for_user( 149 self.user, field.data).first() 150 151 if existing and str(existing.id) != form.id.data: 152 raise wtforms.ValidationError(self.message.format(field.data))
153
154 155 -class NameCharactersValidator(object):
156 - def __init__(self, message=None):
157 if not message: 158 message = "Name must contain only letters, digits, underscores, dashes and dots." 159 self.message = message
160
161 - def __call__(self, form, field):
162 validator = wtforms.validators.Regexp( 163 re.compile(r"^[\w.-]+$"), 164 message=self.message) 165 validator(form, field)
166
167 168 -class ChrootsValidator(object):
169 - def __call__(self, form, field):
170 # Allow it to be truly optional and has None value 171 if not field.data: 172 return 173 174 selected = set(field.data.split()) 175 enabled = set(MockChrootsLogic.active_names()) 176 177 if selected - enabled: 178 raise wtforms.ValidationError("Such chroot is not available: {}".format(", ".join(selected - enabled)))
179
180 181 -class NameNotNumberValidator(object):
182
183 - def __init__(self, message=None):
184 if not message: 185 message = "Project's name can not be just number." 186 self.message = message
187
188 - def __call__(self, form, field):
189 if field.data.isdigit(): 190 raise wtforms.ValidationError(self.message.format(field.data))
191
192 193 -class EmailOrURL(object):
194
195 - def __init__(self, message=None):
196 if not message: 197 message = "{} must be email address or URL" 198 self.message = message
199
200 - def __call__(self, form, field):
201 for validator in [wtforms.validators.Email(), wtforms.validators.URL()]: 202 try: 203 validator(form, field) 204 return True 205 except wtforms.ValidationError: 206 pass 207 raise wtforms.ValidationError(self.message.format(field.name.capitalize()))
208
209 210 -class StringListFilter(object):
211
212 - def __call__(self, value):
213 if not value: 214 return '' 215 # Replace every whitespace string with one newline 216 # Formats ideally for html form filling, use replace('\n', ' ') 217 # to get space-separated values or split() to get list 218 result = value.strip() 219 regex = re.compile(r"\s+") 220 return regex.sub(lambda x: '\n', result)
221
222 223 -class ValueToPermissionNumberFilter(object):
224
225 - def __call__(self, value):
226 if value: 227 return helpers.PermissionEnum("request") 228 return helpers.PermissionEnum("nothing")
229
230 231 -class CoprFormFactory(object):
232 233 @staticmethod
234 - def create_form_cls(mock_chroots=None, user=None, group=None, copr=None):
235 class F(FlaskForm): 236 # also use id here, to be able to find out whether user 237 # is updating a copr if so, we don't want to shout 238 # that name already exists 239 id = wtforms.HiddenField() 240 group_id = wtforms.HiddenField() 241 242 name = wtforms.StringField( 243 "Name", 244 validators=[ 245 wtforms.validators.DataRequired(), 246 NameCharactersValidator(), 247 CoprUniqueNameValidator(user=user, group=group), 248 NameNotNumberValidator() 249 ]) 250 251 homepage = wtforms.StringField( 252 "Homepage", 253 validators=[ 254 wtforms.validators.Optional(), 255 wtforms.validators.URL()]) 256 257 contact = wtforms.StringField( 258 "Contact", 259 validators=[ 260 wtforms.validators.Optional(), 261 EmailOrURL()]) 262 263 description = wtforms.TextAreaField("Description") 264 265 instructions = wtforms.TextAreaField("Instructions") 266 267 delete_after_days = wtforms.IntegerField( 268 "Delete after days", 269 validators=[ 270 wtforms.validators.Optional(), 271 wtforms.validators.NumberRange(min=0, max=60), 272 ], 273 render_kw={'disabled': bool(copr and copr.persistent)}) 274 275 repos = wtforms.TextAreaField( 276 "External Repositories", 277 validators=[UrlRepoListValidator()], 278 filters=[StringListFilter()]) 279 280 initial_pkgs = wtforms.TextAreaField( 281 "Initial packages to build", 282 validators=[ 283 UrlListValidator(), 284 UrlSrpmListValidator()], 285 filters=[StringListFilter()]) 286 287 disable_createrepo = wtforms.BooleanField(default=False, 288 label="Create repositories manually", 289 description="""Repository meta data is normally refreshed 290 after each build. If you want to do this manually, turn 291 this option on.""", 292 false_values=FALSE_VALUES) 293 294 unlisted_on_hp = wtforms.BooleanField( 295 "Project will not be listed on home page", 296 default=False, 297 false_values=FALSE_VALUES) 298 299 persistent = wtforms.BooleanField( 300 "Protect project and its builds against deletion", 301 description="""Project's builds and the project itself 302 cannot be deleted by any means. This option is set once and 303 for all (this option can not be changed after project is 304 created).""", 305 render_kw={'disabled': bool(copr)}, 306 default=False, false_values=FALSE_VALUES) 307 308 auto_prune = wtforms.BooleanField( 309 "Old builds will be deleted automatically", 310 default=True, false_values=FALSE_VALUES, 311 description="""Build will be deleted only if there is a 312 newer build (with respect to package version) and it is 313 older than 14 days""") 314 315 use_bootstrap_container = wtforms.BooleanField( 316 "Enable mock's use_bootstrap_container experimental feature", 317 description="""This will make the build slower but it has an 318 advantage that the dnf _from_ the given chroot will be used 319 to setup the chroot (otherwise host system dnf and rpm is 320 used)""", 321 default=False, 322 false_values=FALSE_VALUES) 323 324 follow_fedora_branching = wtforms.BooleanField( 325 "Follow Fedora branching", 326 description="""When Fedora is branched from rawhide, the 327 respective chroots for the new branch are automatically 328 created for you (as soon as they are available) as rawhide 329 chroot forks.""", 330 default=True, 331 false_values=FALSE_VALUES) 332 333 multilib = wtforms.BooleanField( 334 "Multilib support", 335 description="""When users enable this copr repository on 336 64bit variant of multilib capable architecture (e.g. 337 x86_64), they will be able to install 32bit variants of the 338 packages (e.g. i386 for x86_64 arch)""", 339 default=False, 340 false_values=FALSE_VALUES) 341 342 # Deprecated, use `enable_net` instead 343 build_enable_net = wtforms.BooleanField( 344 "Enable internet access during builds", 345 default=False, false_values=FALSE_VALUES) 346 347 enable_net = wtforms.BooleanField( 348 "Enable internet access during builds", 349 default=False, false_values=FALSE_VALUES) 350 351 module_hotfixes = wtforms.BooleanField( 352 "This repository contains module hotfixes", 353 description="""This will make packages from this project 354 available on along with packages from the active module 355 streams.""", 356 default=False, false_values=FALSE_VALUES) 357 358 @property 359 def selected_chroots(self): 360 selected = [] 361 for ch in self.chroots_list: 362 if getattr(self, ch).data: 363 selected.append(ch) 364 return selected
365 366 def validate(self): 367 if not super(F, self).validate(): 368 return False 369 370 if not self.validate_mock_chroots_not_empty(): 371 self.errors["chroots"] = ["At least one chroot must be selected"] 372 return False 373 374 if self.persistent.data and self.delete_after_days.data: 375 self.delete_after_days.errors.append( 376 "'delete after' can not be combined with persistent") 377 return False 378 379 return True
380 381 def validate_mock_chroots_not_empty(self): 382 have_any = False 383 for c in self.chroots_list: 384 if getattr(self, c).data: 385 have_any = True 386 return have_any 387 388 F.chroots_list = MockChrootsLogic.active_names() 389 F.chroots_list.sort() 390 # sets of chroots according to how we should print them in columns 391 F.chroots_sets = {} 392 for ch in F.chroots_list: 393 checkbox_default = False 394 if mock_chroots and ch in [x.name for x in mock_chroots]: 395 checkbox_default = True 396 397 setattr(F, ch, wtforms.BooleanField(ch, default=checkbox_default, false_values=FALSE_VALUES)) 398 if ch[0] in F.chroots_sets: 399 F.chroots_sets[ch[0]].append(ch) 400 else: 401 F.chroots_sets[ch[0]] = [ch] 402 403 return F 404
405 406 -class CoprDeleteForm(FlaskForm):
407 verify = wtforms.StringField( 408 "Confirm deleting by typing 'yes'", 409 validators=[ 410 wtforms.validators.DataRequired(), 411 wtforms.validators.Regexp( 412 r"^yes$", 413 message="Type 'yes' - without the quotes, lowercase.") 414 ])
415
416 417 -class APICoprDeleteForm(CoprDeleteForm):
418 verify = wtforms.BooleanField("Confirm deleting", false_values=FALSE_VALUES)
419
420 421 # @TODO jkadlcik - rewrite via BaseBuildFormFactory after fe-dev-cloud is back online 422 -class BuildFormRebuildFactory(object):
423 @staticmethod
424 - def create_form_cls(active_chroots):
425 class F(FlaskForm): 426 @property 427 def selected_chroots(self): 428 selected = [] 429 for ch in self.chroots_list: 430 if getattr(self, ch).data: 431 selected.append(ch) 432 return selected
433 434 memory_reqs = wtforms.IntegerField( 435 "Memory requirements", 436 validators=[ 437 wtforms.validators.NumberRange( 438 min=constants.MIN_BUILD_MEMORY, 439 max=constants.MAX_BUILD_MEMORY)], 440 default=constants.DEFAULT_BUILD_MEMORY) 441 442 timeout = wtforms.IntegerField( 443 "Timeout", 444 validators=[ 445 wtforms.validators.NumberRange( 446 min=constants.MIN_BUILD_TIMEOUT, 447 max=constants.MAX_BUILD_TIMEOUT)], 448 default=constants.DEFAULT_BUILD_TIMEOUT) 449 450 enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 451 background = wtforms.BooleanField(false_values=FALSE_VALUES) 452 project_dirname = wtforms.StringField(default=None)
453 454 F.chroots_list = list(map(lambda x: x.name, active_chroots)) 455 F.chroots_list.sort() 456 F.chroots_sets = {} 457 for ch in F.chroots_list: 458 setattr(F, ch, wtforms.BooleanField(ch, default=True, false_values=FALSE_VALUES)) 459 if ch[0] in F.chroots_sets: 460 F.chroots_sets[ch[0]].append(ch) 461 else: 462 F.chroots_sets[ch[0]] = [ch] 463 464 return F 465
466 467 -class RebuildPackageFactory(object):
468 @staticmethod
469 - def create_form_cls(active_chroots):
470 form = BuildFormRebuildFactory.create_form_cls(active_chroots) 471 form.package_name = wtforms.StringField( 472 "Package name", 473 validators=[wtforms.validators.DataRequired()]) 474 return form
475
476 477 -def cleanup_chroot_blacklist(string):
478 if not string: 479 return string 480 fields = [x.lstrip().rstrip() for x in string.split(',')] 481 return ', '.join(fields)
482
483 484 -def validate_chroot_blacklist(form, field):
485 if field.data: 486 string = field.data 487 fields = [x.lstrip().rstrip() for x in string.split(',')] 488 for field in fields: 489 pattern = r'^[a-z0-9-*]+$' 490 if not re.match(pattern, field): 491 raise wtforms.ValidationError('Pattern "{0}" does not match "{1}"'.format(field, pattern)) 492 493 matched = set() 494 all_chroots = MockChrootsLogic.active_names() 495 for chroot in all_chroots: 496 if fnmatch(chroot, field): 497 matched.add(chroot) 498 499 if not matched: 500 raise wtforms.ValidationError('no chroot matched by pattern "{0}"'.format(field)) 501 502 if matched == all_chroots: 503 raise wtforms.ValidationError('patterns are black-listing all chroots')
504
505 506 -class BasePackageForm(FlaskForm):
507 package_name = wtforms.StringField( 508 "Package name", 509 validators=[wtforms.validators.DataRequired()]) 510 webhook_rebuild = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 511 chroot_blacklist = wtforms.StringField( 512 "Chroot blacklist", 513 filters=[cleanup_chroot_blacklist], 514 validators=[ 515 wtforms.validators.Optional(), 516 validate_chroot_blacklist, 517 ], 518 ) 519 max_builds = wtforms.IntegerField( 520 "Max number of builds", 521 description="""Keep only the specified number of the newest-by-id builds 522 (garbage collector is run daily)""", 523 render_kw={'placeholder': 'Optional - integer, e.g. 10, zero/empty disables'}, 524 validators=[ 525 wtforms.validators.Optional(), 526 wtforms.validators.NumberRange(min=0, max=100)], 527 default=None, 528 )
529
530 531 -class PackageFormScm(BasePackageForm):
532 scm_type = wtforms.SelectField( 533 "Type", 534 choices=[("git", "Git"), ("svn", "SVN")], 535 default="git") 536 537 clone_url = wtforms.StringField( 538 "Clone url", 539 validators=[ 540 wtforms.validators.DataRequired(), 541 wtforms.validators.URL()]) 542 543 committish = wtforms.StringField( 544 "Committish", 545 validators=[ 546 wtforms.validators.Optional()]) 547 548 subdirectory = wtforms.StringField( 549 "Subdirectory", 550 validators=[ 551 wtforms.validators.Optional()]) 552 553 spec = wtforms.StringField( 554 "Spec File", 555 validators=[ 556 wtforms.validators.Optional(), 557 wtforms.validators.Regexp( 558 r"^.+\.spec$", 559 message="RPM spec file must end with .spec")]) 560 561 srpm_build_method = wtforms.SelectField( 562 "SRPM build method", 563 choices=[(x, x) for x in ["rpkg", "tito", "tito_test", "make_srpm"]], 564 default="rpkg") 565 566 @property
567 - def source_json(self):
568 return json.dumps({ 569 "type": self.scm_type.data, 570 "clone_url": self.clone_url.data, 571 "subdirectory": self.subdirectory.data, 572 "committish": self.committish.data, 573 "spec": self.spec.data, 574 "srpm_build_method": self.srpm_build_method.data, 575 })
576
577 578 -class PackageFormPyPI(BasePackageForm):
579 pypi_package_name = wtforms.StringField( 580 "PyPI package name", 581 validators=[wtforms.validators.DataRequired()]) 582 583 pypi_package_version = wtforms.StringField( 584 "PyPI package version", 585 validators=[ 586 wtforms.validators.Optional(), 587 ]) 588 589 spec_template = wtforms.SelectField( 590 "Spec template", 591 choices=[ 592 ("", "default"), 593 ("fedora", "fedora"), 594 ("epel7", "epel7"), 595 ("mageia", "mageia"), 596 ("pld", "pld"), 597 ], default="") 598 599 python_versions = MultiCheckboxField( 600 'Build for Python', 601 choices=[ 602 ('3', 'python3'), 603 ('2', 'python2') 604 ], 605 default=['3', '2']) 606 607 @property
608 - def source_json(self):
609 return json.dumps({ 610 "pypi_package_name": self.pypi_package_name.data, 611 "pypi_package_version": self.pypi_package_version.data, 612 "spec_template": self.spec_template.data, 613 "python_versions": self.python_versions.data 614 })
615
616 617 -class PackageFormRubyGems(BasePackageForm):
618 gem_name = wtforms.StringField( 619 "Gem Name", 620 validators=[wtforms.validators.DataRequired()]) 621 622 @property
623 - def source_json(self):
624 return json.dumps({ 625 "gem_name": self.gem_name.data 626 })
627
628 629 -class PackageFormTito(BasePackageForm):
630 """ 631 @deprecated 632 """ 633 git_url = wtforms.StringField( 634 "Git URL", 635 validators=[ 636 wtforms.validators.DataRequired(), 637 wtforms.validators.URL()]) 638 639 git_directory = wtforms.StringField( 640 "Git Directory", 641 validators=[ 642 wtforms.validators.Optional()]) 643 644 git_branch = wtforms.StringField( 645 "Git Branch", 646 validators=[ 647 wtforms.validators.Optional()]) 648 649 tito_test = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 650 651 @property
652 - def source_json(self):
653 return json.dumps({ 654 "type": 'git', 655 "clone_url": self.git_url.data, 656 "committish": self.git_branch.data, 657 "subdirectory": self.git_directory.data, 658 "spec": '', 659 "srpm_build_method": 'tito_test' if self.tito_test.data else 'tito', 660 })
661
662 663 -class PackageFormMock(BasePackageForm):
664 """ 665 @deprecated 666 """ 667 scm_type = wtforms.SelectField( 668 "SCM Type", 669 choices=[("git", "Git"), ("svn", "SVN")]) 670 671 scm_url = wtforms.StringField( 672 "SCM URL", 673 validators=[ 674 wtforms.validators.DataRequired(), 675 wtforms.validators.URL()]) 676 677 scm_branch = wtforms.StringField( 678 "Git Branch", 679 validators=[ 680 wtforms.validators.Optional()]) 681 682 scm_subdir = wtforms.StringField( 683 "Subdirectory", 684 validators=[ 685 wtforms.validators.Optional()]) 686 687 spec = wtforms.StringField( 688 "Spec File", 689 validators=[ 690 wtforms.validators.Optional(), 691 wtforms.validators.Regexp( 692 r"^.+\.spec$", 693 message="RPM spec file must end with .spec")]) 694 695 @property
696 - def source_json(self):
697 return json.dumps({ 698 "type": self.scm_type.data, 699 "clone_url": self.scm_url.data, 700 "committish": self.scm_branch.data, 701 "subdirectory": self.scm_subdir.data, 702 "spec": self.spec.data, 703 "srpm_build_method": 'rpkg', 704 })
705
706 707 -class PackageFormDistGit(BasePackageForm):
708 """ 709 @deprecated 710 """ 711 clone_url = wtforms.StringField( 712 "Clone Url", 713 validators=[wtforms.validators.DataRequired()]) 714 715 branch = wtforms.StringField( 716 "Branch", 717 validators=[wtforms.validators.Optional()]) 718 719 @property
720 - def source_json(self):
721 return json.dumps({ 722 "type": 'git', 723 "clone_url": self.clone_url.data, 724 "committish": self.branch.data, 725 "subdirectory": '', 726 "spec": '', 727 "srpm_build_method": 'rpkg', 728 })
729
730 731 -def cleanup_script(string):
732 if not string: 733 return string 734 735 if string.split('\n')[0].endswith('\r'): 736 # This script is most probably coming from the web-UI, where 737 # web-browsers mistakenly put '\r\n' as EOL; and that would just 738 # mean that the script is not executable (any line can mean 739 # syntax error, but namely shebang would cause 100% fail) 740 string = string.replace('\r\n', '\n') 741 742 # And append newline to have a valid unix file. 743 if not string.endswith('\n'): 744 string += '\n' 745 746 return string
747
748 749 -class PackageFormCustom(BasePackageForm):
750 script = wtforms.TextAreaField( 751 "Script", 752 validators=[ 753 wtforms.validators.DataRequired(), 754 wtforms.validators.Length( 755 max=4096, 756 message="Maximum script size is 4kB"), 757 ], 758 filters=[cleanup_script], 759 ) 760 761 builddeps = wtforms.StringField( 762 "Build dependencies", 763 validators=[wtforms.validators.Optional()]) 764 765 chroot = wtforms.SelectField( 766 'Mock chroot', 767 choices=[], 768 default='fedora-latest-x86_64', 769 ) 770 771 resultdir = wtforms.StringField( 772 "Result directory", 773 validators=[wtforms.validators.Optional()]) 774
775 - def __init__(self, *args, **kwargs):
776 super(PackageFormCustom, self).__init__(*args, **kwargs) 777 chroot_objects = models.MockChroot.query.filter(models.MockChroot.is_active).all() 778 779 chroots = [c.name for c in chroot_objects] 780 chroots.sort() 781 chroots = [(name, name) for name in chroots] 782 783 arches = set() 784 for ch in chroot_objects: 785 if ch.os_release == 'fedora': 786 arches.add(ch.arch) 787 788 self.chroot.choices = [] 789 if arches: 790 self.chroot.choices += [('fedora-latest-' + l, 'fedora-latest-' + l) for l in arches] 791 792 self.chroot.choices += chroots
793 794 @property
795 - def source_json(self):
796 return json.dumps({ 797 "script": self.script.data, 798 "chroot": self.chroot.data, 799 "builddeps": self.builddeps.data, 800 "resultdir": self.resultdir.data, 801 })
802
803 804 -class RebuildAllPackagesFormFactory(object):
805 - def __new__(cls, active_chroots, package_names):
806 form_cls = BaseBuildFormFactory(active_chroots, FlaskForm) 807 form_cls.packages = MultiCheckboxField( 808 "Packages", 809 choices=[(name, name) for name in package_names], 810 default=package_names, 811 validators=[wtforms.validators.DataRequired()]) 812 return form_cls
813
814 815 -class BaseBuildFormFactory(object):
816 - def __new__(cls, active_chroots, form, package=None):
817 class F(form): 818 @property 819 def selected_chroots(self): 820 selected = [] 821 for ch in self.chroots_list: 822 if getattr(self, ch).data: 823 selected.append(ch) 824 return selected
825 826 F.memory_reqs = wtforms.IntegerField( 827 "Memory requirements", 828 validators=[ 829 wtforms.validators.Optional(), 830 wtforms.validators.NumberRange( 831 min=constants.MIN_BUILD_MEMORY, 832 max=constants.MAX_BUILD_MEMORY)], 833 default=constants.DEFAULT_BUILD_MEMORY) 834 835 F.timeout = wtforms.IntegerField( 836 "Timeout", 837 validators=[ 838 wtforms.validators.Optional(), 839 wtforms.validators.NumberRange( 840 min=constants.MIN_BUILD_TIMEOUT, 841 max=constants.MAX_BUILD_TIMEOUT)], 842 default=constants.DEFAULT_BUILD_TIMEOUT) 843 844 F.enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 845 F.background = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 846 F.project_dirname = wtforms.StringField(default=None) 847 848 # overrides BasePackageForm.package_name and is unused for building 849 F.package_name = wtforms.StringField() 850 851 # fill chroots based on project settings 852 F.chroots_list = [x.name for x in active_chroots] 853 F.chroots_list.sort() 854 F.chroots_sets = {} 855 856 package_chroots = set(F.chroots_list) 857 if package: 858 package_chroots = set([ch.name for ch in package.chroots]) 859 860 for ch in F.chroots_list: 861 default = ch in package_chroots 862 setattr(F, ch, wtforms.BooleanField(ch, default=default, false_values=FALSE_VALUES)) 863 if ch[0] in F.chroots_sets: 864 F.chroots_sets[ch[0]].append(ch) 865 else: 866 F.chroots_sets[ch[0]] = [ch] 867 return F 868
869 870 -class BuildFormScmFactory(object):
871 - def __new__(cls, active_chroots, package=None):
873
874 875 -class BuildFormTitoFactory(object):
876 """ 877 @deprecated 878 """
879 - def __new__(cls, active_chroots):
881
882 883 -class BuildFormMockFactory(object):
884 """ 885 @deprecated 886 """
887 - def __new__(cls, active_chroots):
889
890 891 -class BuildFormPyPIFactory(object):
892 - def __new__(cls, active_chroots, package=None):
894
895 896 -class BuildFormRubyGemsFactory(object):
897 - def __new__(cls, active_chroots, package=None):
899
900 901 -class BuildFormDistGitFactory(object):
902 - def __new__(cls, active_chroots):
904
905 906 -class BuildFormUploadFactory(object):
907 - def __new__(cls, active_chroots):
908 form = BaseBuildFormFactory(active_chroots, FlaskForm) 909 form.pkgs = FileField('srpm', validators=[ 910 FileRequired(), 911 SrpmValidator()]) 912 return form
913
914 915 -class BuildFormCustomFactory(object):
916 - def __new__(cls, active_chroots, package=None):
918
919 920 -class BuildFormUrlFactory(object):
921 - def __new__(cls, active_chroots):
922 form = BaseBuildFormFactory(active_chroots, FlaskForm) 923 form.pkgs = wtforms.TextAreaField( 924 "Pkgs", 925 validators=[ 926 wtforms.validators.DataRequired(message="URLs to packages are required"), 927 UrlListValidator(), 928 UrlSrpmListValidator()], 929 filters=[StringListFilter()]) 930 return form
931
932 933 -class ModuleFormUploadFactory(FlaskForm):
934 modulemd = FileField("modulemd", validators=[ 935 FileRequired(), 936 # @TODO Validate modulemd.yaml file 937 ]) 938 939 create = wtforms.BooleanField("create", default=True, false_values=FALSE_VALUES) 940 build = wtforms.BooleanField("build", default=True, false_values=FALSE_VALUES)
941
942 943 -class ModuleBuildForm(FlaskForm):
944 modulemd = FileField("modulemd") 945 scmurl = wtforms.StringField() 946 branch = wtforms.StringField()
947
948 949 -class PagureIntegrationForm(FlaskForm):
950 repo_url = wtforms.StringField("repo_url", default='') 951 api_key = wtforms.StringField("api_key", default='') 952
953 - def __init__(self, api_key=None, repo_url=None, *args, **kwargs):
954 super(PagureIntegrationForm, self).__init__(*args, **kwargs) 955 if api_key != None: 956 self.api_key.data = api_key 957 if repo_url != None: 958 self.repo_url.data = repo_url
959
960 961 -class ChrootForm(FlaskForm):
962 963 """ 964 Validator for editing chroots in project 965 (adding packages to minimal chroot) 966 """ 967 968 buildroot_pkgs = wtforms.StringField("Packages") 969 970 repos = wtforms.TextAreaField('Repos', 971 validators=[UrlRepoListValidator(), 972 wtforms.validators.Optional()], 973 filters=[StringListFilter()]) 974 975 comps = FileField("comps_xml") 976 977 with_opts = wtforms.StringField("With options") 978 without_opts = wtforms.StringField("Without options")
979
980 981 -class CoprChrootExtend(FlaskForm):
982 extend = wtforms.StringField("Chroot name") 983 expire = wtforms.StringField("Chroot name")
984
985 986 -class CoprLegalFlagForm(FlaskForm):
987 comment = wtforms.TextAreaField("Comment")
988
989 990 -class PermissionsApplierFormFactory(object):
991 992 @staticmethod
993 - def create_form_cls(permission=None):
994 class F(FlaskForm): 995 pass
996 997 builder_default = False 998 admin_default = False 999 1000 if permission: 1001 if permission.copr_builder != helpers.PermissionEnum("nothing"): 1002 builder_default = True 1003 if permission.copr_admin != helpers.PermissionEnum("nothing"): 1004 admin_default = True 1005 1006 setattr(F, "copr_builder", 1007 wtforms.BooleanField( 1008 default=builder_default, 1009 false_values=FALSE_VALUES, 1010 filters=[ValueToPermissionNumberFilter()])) 1011 1012 setattr(F, "copr_admin", 1013 wtforms.BooleanField( 1014 default=admin_default, 1015 false_values=FALSE_VALUES, 1016 filters=[ValueToPermissionNumberFilter()])) 1017 1018 return F
1019
1020 1021 -class PermissionsFormFactory(object):
1022 1023 """Creates a dynamic form for given set of copr permissions""" 1024 @staticmethod
1025 - def create_form_cls(permissions):
1026 class F(FlaskForm): 1027 pass
1028 1029 for perm in permissions: 1030 builder_choices = helpers.PermissionEnum.choices_list() 1031 admin_choices = helpers.PermissionEnum.choices_list() 1032 1033 builder_default = perm.copr_builder 1034 admin_default = perm.copr_admin 1035 1036 setattr(F, "copr_builder_{0}".format(perm.user.id), 1037 wtforms.SelectField( 1038 choices=builder_choices, 1039 default=builder_default, 1040 coerce=int)) 1041 1042 setattr(F, "copr_admin_{0}".format(perm.user.id), 1043 wtforms.SelectField( 1044 choices=admin_choices, 1045 default=admin_default, 1046 coerce=int)) 1047 1048 return F
1049
1050 1051 -class CoprModifyForm(FlaskForm):
1052 description = wtforms.TextAreaField('Description', 1053 validators=[wtforms.validators.Optional()]) 1054 1055 instructions = wtforms.TextAreaField('Instructions', 1056 validators=[wtforms.validators.Optional()]) 1057 1058 chroots = wtforms.TextAreaField('Chroots', 1059 validators=[wtforms.validators.Optional(), ChrootsValidator()]) 1060 1061 repos = wtforms.TextAreaField('External Repositories', 1062 validators=[UrlRepoListValidator(), 1063 wtforms.validators.Optional()], 1064 filters=[StringListFilter()]) 1065 1066 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1067 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1068 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1069 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1070 follow_fedora_branching = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1071 follow_fedora_branching = wtforms.BooleanField(default=True, false_values=FALSE_VALUES) 1072 delete_after_days = wtforms.IntegerField( 1073 validators=[wtforms.validators.Optional(), 1074 wtforms.validators.NumberRange(min=-1, max=60)], 1075 filters=[(lambda x : -1 if x is None else x)]) 1076 1077 # Deprecated, use `enable_net` instead 1078 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1079 enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1080 multilib = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1081 module_hotfixes = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES)
1082
1083 1084 -class CoprForkFormFactory(object):
1085 @staticmethod
1086 - def create_form_cls(copr, user, groups):
1087 class F(FlaskForm): 1088 source = wtforms.StringField( 1089 "Source", 1090 default=copr.full_name) 1091 1092 owner = wtforms.SelectField( 1093 "Fork owner", 1094 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 1095 default=user.name, 1096 validators=[wtforms.validators.DataRequired()]) 1097 1098 name = wtforms.StringField( 1099 "Fork name", 1100 default=copr.name, 1101 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 1102 1103 confirm = wtforms.BooleanField( 1104 "Confirm", 1105 false_values=FALSE_VALUES, 1106 default=False)
1107 return F
1108
1109 1110 -class ModifyChrootForm(ChrootForm):
1111 buildroot_pkgs = wtforms.StringField('Additional packages to be always present in minimal buildroot') 1112 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 1113 validators=[UrlRepoListValidator(), 1114 wtforms.validators.Optional()], 1115 filters=[StringListFilter()]) 1116 comps = None 1117 upload_comps = FileField("Upload comps.xml") 1118 delete_comps = wtforms.BooleanField("Delete comps.xml", false_values=FALSE_VALUES)
1119
1120 1121 -class PinnedCoprsForm(FlaskForm):
1122 copr_ids = wtforms.SelectMultipleField(wtforms.IntegerField("Pinned Copr ID")) 1123
1124 - def validate(self):
1125 if any([i and not i.isnumeric() for i in self.copr_ids.data]): 1126 self.errors["coprs"] = ["Unexpected value selected"] 1127 return False 1128 1129 limit = app.config["PINNED_PROJECTS_LIMIT"] 1130 if len(self.copr_ids.data) > limit: 1131 self.errors["coprs"] = ["Too many pinned projects. Limit is {}!".format(limit)] 1132 return False 1133 1134 if len(list(filter(None, self.copr_ids.data))) != len(set(filter(None, self.copr_ids.data))): 1135 self.errors["coprs"] = ["You can pin a particular project only once"] 1136 return False 1137 1138 return True
1139
1140 1141 -class AdminPlaygroundForm(FlaskForm):
1142 playground = wtforms.BooleanField("Playground", false_values=FALSE_VALUES)
1143
1144 1145 -class AdminPlaygroundSearchForm(FlaskForm):
1146 project = wtforms.StringField("Project")
1147
1148 1149 -class GroupUniqueNameValidator(object):
1150
1151 - def __init__(self, message=None):
1152 if not message: 1153 message = "Group with the alias '{}' already exists." 1154 self.message = message
1155
1156 - def __call__(self, form, field):
1157 if UsersLogic.group_alias_exists(field.data): 1158 raise wtforms.ValidationError(self.message.format(field.data))
1159
1160 1161 -class ActivateFasGroupForm(FlaskForm):
1162 1163 name = wtforms.StringField( 1164 validators=[ 1165 wtforms.validators.Regexp( 1166 re.compile(r"^[\w.-]+$"), 1167 message="Name must contain only letters," 1168 "digits, underscores, dashes and dots."), 1169 GroupUniqueNameValidator() 1170 ] 1171 )
1172
1173 1174 -class CreateModuleForm(FlaskForm):
1175 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 1176 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 1177 filter = wtforms.FieldList(wtforms.StringField("Package Filter")) 1178 api = wtforms.FieldList(wtforms.StringField("Module API")) 1179 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 1180 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 1181
1182 - def __init__(self, copr=None, *args, **kwargs):
1183 self.copr = copr 1184 super(CreateModuleForm, self).__init__(*args, **kwargs)
1185
1186 - def validate(self):
1187 if not FlaskForm.validate(self): 1188 return False 1189 1190 # Profile names should be unique 1191 names = [x for x in self.profile_names.data if x] 1192 if len(set(names)) < len(names): 1193 self.errors["profiles"] = ["Profile names must be unique"] 1194 return False 1195 1196 # WORKAROUND 1197 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 1198 # profile_pkgs in seconds box, it is sorted and validated correctly 1199 for i in range(0, len(self.profile_names.data)): 1200 # If profile name is not set, then there should not be any packages in this profile 1201 if not flask.request.form["profile_names-{}".format(i)]: 1202 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 1203 self.errors["profiles"] = ["Missing profile name"] 1204 return False 1205 return True
1206
1207 1208 -class ModuleRepo(FlaskForm):
1209 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 1210 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 1211 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 1212 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 1213 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 1214 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])
1215