Trees | Indices | Help |
---|
|
1 import os 2 import re 3 from six.moves.urllib.parse import urlparse 4 5 import flask 6 import wtforms 7 import json 8 9 from flask_wtf.file import FileRequired, FileField 10 from fnmatch import fnmatch 11 12 try: # get rid of deprecation warning with newer flask_wtf 13 from flask_wtf import FlaskForm 14 except ImportError: 15 from flask_wtf import Form as FlaskForm 16 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.logic.dist_git_logic import DistGitLogic 23 from coprs.logic.complex_logic import ComplexLogic 24 from coprs import exceptions 25 26 from wtforms import ValidationError 27 28 FALSE_VALUES = {False, "false", ""}32 """ 33 Params 34 ------ 35 source_type_text : str 36 name of the source type (scm/pypi/rubygems/git_and_tito/mock_scm) 37 38 Returns 39 ------- 40 BasePackageForm child 41 based on source_type_text input 42 """ 43 # pylint: disable=too-many-return-statements 44 if source_type_text == 'scm': 45 return PackageFormScm 46 elif source_type_text == 'pypi': 47 return PackageFormPyPI 48 elif source_type_text == 'rubygems': 49 return PackageFormRubyGems 50 elif source_type_text == 'git_and_tito': 51 return PackageFormTito # deprecated 52 elif source_type_text == 'mock_scm': 53 return PackageFormMock # deprecated 54 elif source_type_text == "custom": 55 return PackageFormCustom 56 elif source_type_text == "distgit": 57 return PackageFormDistGitSimple 58 else: 59 raise exceptions.UnknownSourceTypeException("Invalid source type")6063 """ 64 Select-box for the bootstrap configuration in chroot/project form 65 """ 66 67 choices = [] 68 default_choices = [ 69 ('default', 'Use default configuration from mock-core-configs.rpm'), 70 ('off', 'Disable'), 71 ('on', 'Enable'), 72 ('image', 'Initialize by default pre-configured container image'), 73 ] 74 75 if level == 'chroot': 76 choices.append(("unchanged", "Use project settings")) 77 choices.extend(default_choices) 78 choices.append(('custom_image', 79 'Initialize by custom bootstrap image (specified ' 80 'in the "Mock bootstrap image" field below)')) 81 82 elif level == 'build': 83 choices.append(("unchanged", "Use project/chroot settings")) 84 choices.extend(default_choices) 85 86 else: 87 choices.extend(default_choices) 88 89 return wtforms.SelectField( 90 "Mock bootstrap", 91 choices=choices, 92 validators=[wtforms.validators.Optional()], 93 # Replace "None" with None (needed on Fedora <= 32) 94 filters=[NoneFilter(None)], 95 )9699 """ 100 Mandatory bootstrap-image field when the bootstrap select-box is set to a 101 custom image option. 102 """ 103 return wtforms.TextField( 104 "Mock bootstrap image", 105 validators=[ 106 wtforms.validators.Optional(), 107 wtforms.validators.Regexp( 108 r"^\w+(:\w+)?$", 109 message=("Enter valid bootstrap image id " 110 "(<name>[:<tag>], e.g. fedora:33)."))], 111 filters=[ 112 lambda x: None if not x else x 113 ], 114 )115118 widget = wtforms.widgets.ListWidget(prefix_label=False) 119 option_widget = wtforms.widgets.CheckboxInput()120123143125 if not message: 126 message = ("A list of http[s] URLs separated by whitespace characters" 127 " is needed ('{0}' doesn't seem to be a valid URL).") 128 self.message = message129131 urls = field.data.split() 132 for u in urls: 133 if not self.is_url(u): 134 raise wtforms.ValidationError(self.message.format(u))135146 """ Allows also `repo://` schema"""162148 parsed = urlparse(url) 149 if parsed.scheme not in ["http", "https", "copr"]: 150 return False 151 if not parsed.netloc: 152 return False 153 # copr://username/projectname 154 # ^^ schema ^^ netlock ^^ path 155 if parsed.scheme == "copr": 156 # check if projectname missed 157 path_split = parsed.path.split("/") 158 if len(path_split) < 2 or path_split[1] == "": 159 return False 160 161 return True176166 if not message: 167 message = ("URLs must end with .src.rpm, .nosrc.rpm, or .spec" 168 " ('{0}' doesn't seem to be a valid URL).") 169 super(UrlSrpmListValidator, self).__init__(message)170188180 if not message: 181 message = "You can upload only .src.rpm, .nosrc.rpm, and .spec files" 182 self.message = message183191214193 if not message: 194 if group is None: 195 message = "You already have project named '{}'." 196 else: 197 message = "Group {} ".format(group) + "already have project named '{}'." 198 self.message = message 199 if not user: 200 user = flask.g.user 201 self.user = user 202 self.group = group203205 if self.group: 206 existing = CoprsLogic.exists_for_group( 207 self.group, field.data).first() 208 else: 209 existing = CoprsLogic.exists_for_user( 210 self.user, field.data).first() 211 212 if existing and str(existing.id) != form.id.data: 213 raise wtforms.ValidationError(self.message.format(field.data))227218 if not message: 219 message = "Name must contain only letters, digits, underscores, dashes and dots." 220 self.message = message221229261231 already_enabled = {} 232 for module in form.module_toggle.data.split(","): 233 if module == "": 234 return True 235 236 try: 237 module_name, stream = module.strip().split(":") 238 except ValueError: 239 raise ValidationError( 240 message=( 241 "Module name '{0}' must consist of two parts separated " 242 "with colon, e.g. module:stream" 243 ).format(module)) 244 245 pattern = re.compile(re.compile(r"^([a-zA-Z0-9-_!][^\ ]*)$")) 246 if pattern.match(module_name) is None: 247 raise ValidationError(message=( 248 "Module name '{0}' must contain only letters, digits, " 249 "dashes, underscores.").format(module_name)) 250 251 if module_name in already_enabled: 252 raise ValidationError("Module name '{0}' specified multiple " 253 "times".format(module_name)) 254 else: 255 already_enabled[module_name] = True 256 257 if pattern.match(stream) is None: 258 raise ValidationError(message=( 259 "Stream part of module name '{0}' must contain only " 260 "letters, digits, dashes, underscores.").format(stream))273264 # Allow it to be truly optional and has None value 265 if not field.data: 266 return 267 268 selected = set(field.data.split()) 269 enabled = set(MockChrootsLogic.active_names()) 270 271 if selected - enabled: 272 raise wtforms.ValidationError("Such chroot is not available: {}".format(", ".join(selected - enabled)))276285 302278 if not message: 279 message = "Project's name can not be just number." 280 self.message = message281305315 327 334307 if not value: 308 return '' 309 # Replace every whitespace string with one newline 310 # Formats ideally for html form filling, use replace('\n', ' ') 311 # to get space-separated values or split() to get list 312 result = value.strip() 313 regex = re.compile(r"\s+") 314 return regex.sub(lambda x: '\n', result)336 if data in [True, 'true']: 337 return True 338 if data in [False, 'false']: 339 return False 340 return None341343 344 @staticmethod494 495 @property 496 def errors(self): 497 """ 498 Current stable version of WTForms's `Form` doesn't allow to set 499 form-level errors. Let's workaround it in a way, that is 500 implemented in the development branch. 501 502 2.2.1 (Fedora 31/32) 503 `form.errors["whatever"] = ["Some message"]` could be done 504 505 2.3.1 (Fedora 33) 506 The previous solution does nothing and there is no way to 507 have form-level errors. The only way to set errors is via 508 `form.some_field.errors.append("Some message")`. We are 509 reimplementing `errors` property to behave like in 3.0.0 510 511 3.0.0 (Fedora ??) 512 The `form.form_errors` field can be set. This list will be 513 added to the resulting `errors` value and accessible as 514 `form.errors[None]`. 515 516 RFE: https://github.com/wtforms/wtforms/issues/55 517 PR: https://github.com/wtforms/wtforms/pull/595 518 Release notes: https://github.com/wtforms/wtforms/blob/master/CHANGES.rst#version-300 519 """ 520 errors = super().errors.copy() 521 if hasattr(self, "form_errors"): 522 errors[None] = self.form_errors 523 return errors 524 525 def validate_mock_chroots_not_empty(self): 526 have_any = False 527 for c in self.chroots_list: 528 if getattr(self, c).data: 529 have_any = True 530 return have_any 531 532 F.chroots_list = MockChrootsLogic.active_names() 533 F.chroots_list.sort() 534 # sets of chroots according to how we should print them in columns 535 F.chroots_sets = {} 536 for ch in F.chroots_list: 537 checkbox_default = False 538 if mock_chroots and ch in [x.name for x in mock_chroots]: 539 checkbox_default = True 540 541 setattr(F, ch, wtforms.BooleanField(ch, default=checkbox_default, false_values=FALSE_VALUES)) 542 if ch[0] in F.chroots_sets: 543 F.chroots_sets[ch[0]].append(ch) 544 else: 545 F.chroots_sets[ch[0]] = [ch] 546 547 return F 548346 class F(FlaskForm): 347 # also use id here, to be able to find out whether user 348 # is updating a copr if so, we don't want to shout 349 # that name already exists 350 id = wtforms.HiddenField() 351 group_id = wtforms.HiddenField() 352 353 name = wtforms.StringField( 354 "Name", 355 validators=[ 356 wtforms.validators.DataRequired(), 357 NameCharactersValidator(), 358 CoprUniqueNameValidator(user=user, group=group), 359 NameNotNumberValidator() 360 ]) 361 362 homepage = wtforms.StringField( 363 "Homepage", 364 validators=[ 365 wtforms.validators.Optional(), 366 wtforms.validators.URL()]) 367 368 contact = wtforms.StringField( 369 "Contact", 370 validators=[ 371 wtforms.validators.Optional(), 372 EmailOrURL()]) 373 374 description = wtforms.TextAreaField("Description") 375 376 instructions = wtforms.TextAreaField("Instructions") 377 378 delete_after_days = wtforms.IntegerField( 379 "Delete after days", 380 validators=[ 381 wtforms.validators.Optional(), 382 wtforms.validators.NumberRange(min=0, max=60), 383 ], 384 render_kw={'disabled': bool(copr and copr.persistent)}) 385 386 repos = wtforms.TextAreaField( 387 "External Repositories", 388 validators=[UrlRepoListValidator()], 389 filters=[StringListFilter()]) 390 391 runtime_dependencies = wtforms.TextAreaField( 392 "Runtime dependencies", 393 validators=[UrlRepoListValidator()], 394 filters=[StringListFilter()]) 395 396 initial_pkgs = wtforms.TextAreaField( 397 "Initial packages to build", 398 validators=[ 399 UrlListValidator(), 400 UrlSrpmListValidator()], 401 filters=[StringListFilter()]) 402 403 disable_createrepo = wtforms.BooleanField(default=False, 404 label="Create repositories manually", 405 description="""Repository meta data is normally refreshed 406 after each build. If you want to do this manually, turn 407 this option on.""", 408 false_values=FALSE_VALUES) 409 410 unlisted_on_hp = wtforms.BooleanField( 411 "Project will not be listed on home page", 412 default=False, 413 false_values=FALSE_VALUES) 414 415 persistent = wtforms.BooleanField( 416 "Protect project and its builds against deletion", 417 description="""Project's builds and the project itself 418 cannot be deleted by any means. This option is set once and 419 for all (this option can not be changed after project is 420 created).""", 421 render_kw={'disabled': bool(copr)}, 422 default=False, false_values=FALSE_VALUES) 423 424 auto_prune = wtforms.BooleanField( 425 "Old builds will be deleted automatically", 426 default=True, false_values=FALSE_VALUES, 427 description="""Build will be deleted only if there is a 428 newer build (with respect to package version) and it is 429 older than 14 days""") 430 431 use_bootstrap_container = wtforms.StringField( 432 "backward-compat-only: old bootstrap", 433 validators=[wtforms.validators.Optional()], 434 filters=[_optional_checkbox_filter]) 435 436 bootstrap = create_mock_bootstrap_field("project") 437 438 follow_fedora_branching = wtforms.BooleanField( 439 "Follow Fedora branching", 440 description="""When Fedora is branched from rawhide, the 441 respective chroots for the new branch are automatically 442 created for you (as soon as they are available) as rawhide 443 chroot forks.""", 444 default=True, 445 false_values=FALSE_VALUES) 446 447 multilib = wtforms.BooleanField( 448 "Multilib support", 449 description="""When users enable this copr repository on 450 64bit variant of multilib capable architecture (e.g. 451 x86_64), they will be able to install 32bit variants of the 452 packages (e.g. i386 for x86_64 arch)""", 453 default=False, 454 false_values=FALSE_VALUES) 455 456 # Deprecated, use `enable_net` instead 457 build_enable_net = wtforms.BooleanField( 458 "Enable internet access during builds", 459 default=False, false_values=FALSE_VALUES) 460 461 enable_net = wtforms.BooleanField( 462 "Enable internet access during builds", 463 default=False, false_values=FALSE_VALUES) 464 465 module_hotfixes = wtforms.BooleanField( 466 "This repository contains module hotfixes", 467 description="""This will make packages from this project 468 available on along with packages from the active module 469 streams.""", 470 default=False, false_values=FALSE_VALUES) 471 472 @property 473 def selected_chroots(self): 474 selected = [] 475 for ch in self.chroots_list: 476 if getattr(self, ch).data: 477 selected.append(ch) 478 return selected479 480 def validate(self): 481 if not super(F, self).validate(): 482 return False 483 484 if not self.validate_mock_chroots_not_empty(): 485 self.form_errors = ["At least one chroot must be selected"] 486 return False 487 488 if self.persistent.data and self.delete_after_days.data: 489 self.delete_after_days.errors.append( 490 "'delete after' can not be combined with persistent") 491 return False 492 493 return True551 verify = wtforms.StringField( 552 "Confirm deleting by typing 'yes'", 553 validators=[ 554 wtforms.validators.DataRequired(), 555 wtforms.validators.Regexp( 556 r"^yes$", 557 message="Type 'yes' - without the quotes, lowercase.") 558 ])559 563565 minutes = round(sec / 60) 566 hours = minutes // 60 567 minutes = minutes % 60 568 return hours if not minutes else "{}:{:02d}".format(hours, minutes)569 576579 @staticmethod587581 form = BuildFormRebuildFactory.create_form_cls(active_chroots) 582 # pylint: disable=attribute-defined-outside-init 583 form.package_name = wtforms.StringField( 584 "Package name", 585 validators=[wtforms.validators.DataRequired()]) 586 return form590 if not string: 591 return string 592 fields = [x.lstrip().rstrip() for x in string.split(',')] 593 return ', '.join(fields)594597 if field.data: 598 string = field.data 599 fields = [x.lstrip().rstrip() for x in string.split(',')] 600 for field in fields: 601 pattern = r'^[a-z0-9-*]+$' 602 if not re.match(pattern, field): 603 raise wtforms.ValidationError('Pattern "{0}" does not match "{1}"'.format(field, pattern)) 604 605 matched = set() 606 all_chroots = MockChrootsLogic.active_names() 607 for chroot in all_chroots: 608 if fnmatch(chroot, field): 609 matched.add(chroot) 610 611 if not matched: 612 raise wtforms.ValidationError('no chroot matched by pattern "{0}"'.format(field)) 613 614 if matched == all_chroots: 615 raise wtforms.ValidationError('patterns are black-listing all chroots')616619 package_name_regex = r"^[-+_.a-zA-Z0-9]+$" 620 621 package_name = wtforms.StringField( 622 "Package name", 623 validators=[ 624 wtforms.validators.Regexp( 625 re.compile(package_name_regex), 626 message="Please enter a valid package name in " \ 627 + package_name_regex)] 628 ) 629 630 webhook_rebuild = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 631 chroot_blacklist = wtforms.StringField( 632 "Chroot blacklist", 633 filters=[cleanup_chroot_blacklist], 634 validators=[ 635 wtforms.validators.Optional(), 636 validate_chroot_blacklist, 637 ], 638 ) 639 max_builds = wtforms.IntegerField( 640 "Max number of builds", 641 description="""Keep only the specified number of the newest-by-id builds 642 (garbage collector is run daily)""", 643 render_kw={'placeholder': 'Optional - integer, e.g. 10, zero/empty disables'}, 644 validators=[ 645 wtforms.validators.Optional(), 646 wtforms.validators.NumberRange(min=0, max=100)], 647 default=None, 648 )649652 scm_type = wtforms.SelectField( 653 "Type", 654 choices=[("git", "Git"), ("svn", "SVN")], 655 default="git") 656 657 clone_url = wtforms.StringField( 658 "Clone url", 659 validators=[ 660 wtforms.validators.DataRequired(), 661 wtforms.validators.URL()]) 662 663 committish = wtforms.StringField( 664 "Committish", 665 validators=[ 666 wtforms.validators.Optional()]) 667 668 subdirectory = wtforms.StringField( 669 "Subdirectory", 670 validators=[ 671 wtforms.validators.Optional()]) 672 673 spec = wtforms.StringField( 674 "Spec File", 675 validators=[ 676 wtforms.validators.Optional(), 677 wtforms.validators.Regexp( 678 r"^.+\.spec$", 679 message="RPM spec file must end with .spec")]) 680 681 srpm_build_method = wtforms.SelectField( 682 "SRPM build method", 683 choices=[(x, x) for x in ["rpkg", "tito", "tito_test", "make_srpm"]], 684 default="rpkg") 685 686 @property696688 return json.dumps({ 689 "type": self.scm_type.data, 690 "clone_url": self.clone_url.data, 691 "subdirectory": self.subdirectory.data, 692 "committish": self.committish.data, 693 "spec": self.spec.data, 694 "srpm_build_method": self.srpm_build_method.data, 695 })699 pypi_package_name = wtforms.StringField( 700 "PyPI package name", 701 validators=[wtforms.validators.DataRequired()]) 702 703 pypi_package_version = wtforms.StringField( 704 "PyPI package version", 705 validators=[ 706 wtforms.validators.Optional(), 707 ]) 708 709 spec_template = wtforms.SelectField( 710 "Spec template", 711 choices=[ 712 ("", "default"), 713 ("fedora", "fedora"), 714 ("epel7", "epel7"), 715 ("mageia", "mageia"), 716 ("pld", "pld"), 717 ], default="") 718 719 python_versions = MultiCheckboxField( 720 'Build for Python', 721 choices=[ 722 ('3', 'python3'), 723 ('2', 'python2') 724 ], 725 default=['3', '2']) 726 727 @property735729 return json.dumps({ 730 "pypi_package_name": self.pypi_package_name.data, 731 "pypi_package_version": self.pypi_package_version.data, 732 "spec_template": self.spec_template.data, 733 "python_versions": self.python_versions.data 734 })738 gem_name = wtforms.StringField( 739 "Gem Name", 740 validators=[wtforms.validators.DataRequired()]) 741 742 @property747750 """ 751 @deprecated 752 """ 753 git_url = wtforms.StringField( 754 "Git URL", 755 validators=[ 756 wtforms.validators.DataRequired(), 757 wtforms.validators.URL()]) 758 759 git_directory = wtforms.StringField( 760 "Git Directory", 761 validators=[ 762 wtforms.validators.Optional()]) 763 764 git_branch = wtforms.StringField( 765 "Git Branch", 766 validators=[ 767 wtforms.validators.Optional()]) 768 769 tito_test = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 770 771 @property781773 return json.dumps({ 774 "type": 'git', 775 "clone_url": self.git_url.data, 776 "committish": self.git_branch.data, 777 "subdirectory": self.git_directory.data, 778 "spec": '', 779 "srpm_build_method": 'tito_test' if self.tito_test.data else 'tito', 780 })784 """ 785 @deprecated 786 """ 787 scm_type = wtforms.SelectField( 788 "SCM Type", 789 choices=[("git", "Git"), ("svn", "SVN")]) 790 791 scm_url = wtforms.StringField( 792 "SCM URL", 793 validators=[ 794 wtforms.validators.DataRequired(), 795 wtforms.validators.URL()]) 796 797 scm_branch = wtforms.StringField( 798 "Git Branch", 799 validators=[ 800 wtforms.validators.Optional()]) 801 802 scm_subdir = wtforms.StringField( 803 "Subdirectory", 804 validators=[ 805 wtforms.validators.Optional()]) 806 807 spec = wtforms.StringField( 808 "Spec File", 809 validators=[ 810 wtforms.validators.Optional(), 811 wtforms.validators.Regexp( 812 r"^.+\.spec$", 813 message="RPM spec file must end with .spec")]) 814 815 @property825828 """ 829 @deprecated 830 """ 831 clone_url = wtforms.StringField( 832 "Clone Url", 833 validators=[wtforms.validators.DataRequired()]) 834 835 branch = wtforms.StringField( 836 "Branch", 837 validators=[wtforms.validators.Optional()]) 838 839 @property849852 if not string: 853 return string 854 855 if string.split('\n')[0].endswith('\r'): 856 # This script is most probably coming from the web-UI, where 857 # web-browsers mistakenly put '\r\n' as EOL; and that would just 858 # mean that the script is not executable (any line can mean 859 # syntax error, but namely shebang would cause 100% fail) 860 string = string.replace('\r\n', '\n') 861 862 # And append newline to have a valid unix file. 863 if not string.endswith('\n'): 864 string += '\n' 865 866 return string867870 script = wtforms.TextAreaField( 871 "Script", 872 validators=[ 873 wtforms.validators.DataRequired(), 874 wtforms.validators.Length( 875 max=4096, 876 message="Maximum script size is 4kB"), 877 ], 878 filters=[cleanup_script], 879 ) 880 881 builddeps = wtforms.StringField( 882 "Build dependencies", 883 validators=[wtforms.validators.Optional()]) 884 885 chroot = wtforms.SelectField( 886 'Mock chroot', 887 choices=[], 888 default='fedora-latest-x86_64', 889 ) 890 891 resultdir = wtforms.StringField( 892 "Result directory", 893 validators=[wtforms.validators.Optional()]) 894922 930 940896 super(PackageFormCustom, self).__init__(*args, **kwargs) 897 chroot_objects = models.MockChroot.query.filter(models.MockChroot.is_active).all() 898 899 chroots = [c.name for c in chroot_objects] 900 chroots.sort() 901 chroots = [(name, name) for name in chroots] 902 903 arches = set() 904 for ch in chroot_objects: 905 if ch.os_release == 'fedora': 906 arches.add(ch.arch) 907 908 self.chroot.choices = [] 909 if arches: 910 self.chroot.choices += [('fedora-latest-' + l, 'fedora-latest-' + l) for l in arches] 911 912 self.chroot.choices += chroots913 914 @property943 """ Select-box for picking (default) dist git instance """ 944 945 # pylint: disable=too-few-public-methods965947 if not validators: 948 validators = [] 949 if not filters: 950 filters = [] 951 952 self.distgit_choices = [x.name for x in DistGitLogic.ordered().all()] 953 self.distgit_default = self.distgit_choices[0] 954 955 validators.append(DistGitValidator()) 956 filters.append(NoneFilter(self.distgit_default)) 957 958 super().__init__( 959 label="DistGit instance", 960 validators=validators, 961 filters=filters, 962 choices=[(x, x) for x in self.distgit_choices], 963 **kwargs, 964 )968 """ 969 This represents basically a variant of the SCM method, but with a very 970 trivial user interface. 971 """ 972 distgit = DistGitSelectField() 973 974 committish = wtforms.StringField( 975 "Committish", 976 validators=[wtforms.validators.Optional()], 977 render_kw={ 978 "placeholder": "Optional - Specific branch, tag, or commit that " 979 "you want to build from"}, 980 ) 981 982 namespace = wtforms.StringField( 983 "DistGit namespace", 984 validators=[wtforms.validators.Optional()], 985 default=None, 986 filters=[lambda x: None if not x else os.path.normpath(x)], 987 description=( 988 "Some dist-git instances have the git repositories " 989 "namespaced - e.g. you need to specify '@copr/copr' for " 990 "the <a href='https://copr-dist-git.fedorainfracloud.org/" 991 "cgit/@copr/copr/copr-cli.git/tree/copr-cli.spec'>" 992 "@copr/copr/copr-cli</a> Fedora Copr package"), 993 render_kw={ 994 "placeholder": "Optional - string, e.g. '@copr/copr'"}, 995 ) 996 997 build_requires_package_name = True 998 999 @property10391001 """ Source json stored in DB in Package.source_json """ 1002 data = { 1003 "clone_url": self.clone_url(), 1004 } 1005 1006 if self.distgit.data: 1007 data["distgit"] = self.distgit.data 1008 1009 for field_name in ["distgit", "namespace", "committish"]: 1010 field = getattr(self, field_name) 1011 if field.data: 1012 data[field_name] = field.data 1013 1014 return json.dumps(data)10151017 """ One-time generate the clone_url from the form data """ 1018 return DistGitLogic.get_clone_url(self.distgit.data, 1019 self.package_name.data, 1020 self.namespace.data)10211023 """ 1024 Try to check that we can generate clone_url from distgit, namespace and 1025 package. This can not be done by single-field-context validator. 1026 """ 1027 if not super().validate(): 1028 return False 1029 1030 try: 1031 self.clone_url() 1032 except Exception as e: # pylint: disable=broad-except 1033 self.distgit.errors.append( 1034 "Can not validate DistGit input: {}".format(str(e)) 1035 ) 1036 return False 1037 1038 return True10501043 form_cls = BaseBuildFormFactory(active_chroots, FlaskForm) 1044 form_cls.packages = MultiCheckboxField( 1045 "Packages", 1046 choices=[(name, name) for name in package_names], 1047 default=package_names, 1048 validators=[wtforms.validators.DataRequired()]) 1049 return form_cls1053 # TODO: Change this to just 'def get_build_form(...)'. The __new__ 1054 # hack confuses not only PyLint (on each calling place it claims 1055 # that the return value is not callable. __new__ isn't supposed 1056 # to return classes, but instances.1066 1067 F.timeout = wtforms.IntegerField( 1068 "Timeout", 1069 description="Optional - number of seconds we allow the builds to run, default is {0} ({1}h)".format( 1070 app.config["DEFAULT_BUILD_TIMEOUT"], seconds_to_pretty_hours(app.config["DEFAULT_BUILD_TIMEOUT"])), 1071 validators=[ 1072 wtforms.validators.Optional(), 1073 wtforms.validators.NumberRange( 1074 min=app.config["MIN_BUILD_TIMEOUT"], 1075 max=app.config["MAX_BUILD_TIMEOUT"])], 1076 default=app.config["DEFAULT_BUILD_TIMEOUT"]) 1077 1078 F.enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 1079 F.background = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 1080 F.project_dirname = wtforms.StringField(default=None) 1081 F.bootstrap = create_mock_bootstrap_field("build") 1082 1083 # Overrides BasePackageForm.package_name, it is usually unused for 1084 # building 1085 if not getattr(F, "build_requires_package_name", None): 1086 F.package_name = wtforms.StringField() 1087 1088 # fill chroots based on project settings 1089 F.chroots_list = [x.name for x in active_chroots] 1090 F.chroots_list.sort() 1091 F.chroots_sets = {} 1092 1093 package_chroots = set(F.chroots_list) 1094 if package: 1095 package_chroots = set([ch.name for ch in package.chroots]) 1096 1097 for ch in F.chroots_list: 1098 default = ch in package_chroots 1099 setattr(F, ch, wtforms.BooleanField(ch, default=default, false_values=FALSE_VALUES)) 1100 if ch[0] in F.chroots_sets: 1101 F.chroots_sets[ch[0]].append(ch) 1102 else: 1103 F.chroots_sets[ch[0]] = [ch] 1104 1105 F.after_build_id = wtforms.IntegerField( 1106 "Batch-build after", 1107 description=( 1108 "Optional - Build after the batch containing " 1109 "the Build ID build." 1110 ), 1111 validators=[ 1112 wtforms.validators.Optional()], 1113 render_kw={'placeholder': 'Build ID'}, 1114 filters=[NoneFilter(None)], 1115 ) 1116 1117 F.with_build_id = wtforms.IntegerField( 1118 "Batch-build with", 1119 description=( 1120 "Optional - Build in the same batch with the Build ID build" 1121 ), 1122 render_kw={'placeholder': 'Build ID'}, 1123 validators=[ 1124 wtforms.validators.Optional()], 1125 filters=[NoneFilter(None)], 1126 ) 1127 1128 def _validate_batch_opts(form, field): 1129 counterpart = form.with_build_id 1130 modifies = False 1131 if counterpart == field: 1132 counterpart = form.after_build_id 1133 modifies = True 1134 1135 if counterpart.data: 1136 raise wtforms.ValidationError( 1137 "Only one batch option can be specified") 1138 1139 build_id = field.data 1140 if not build_id: 1141 return 1142 1143 build_id = int(build_id) 1144 build = models.Build.query.get(build_id) 1145 if not build: 1146 raise wtforms.ValidationError( 1147 "Build {} not found".format(build_id)) 1148 batch_error = build.batching_user_error(flask.g.user, modifies) 1149 if batch_error: 1150 raise wtforms.ValidationError(batch_error) 1151 1152 F.validate_with_build_id = _validate_batch_opts 1153 F.validate_after_build_id = _validate_batch_opts 1154 1155 return F 1156 1161 1169 1177 1182 1187 11921058 class F(form): 1059 @property 1060 def selected_chroots(self): 1061 selected = [] 1062 for ch in self.chroots_list: 1063 if getattr(self, ch).data: 1064 selected.append(ch) 1065 return selected1201 1206 12151196 form = BaseBuildFormFactory(active_chroots, FlaskForm) 1197 form.pkgs = FileField('srpm', validators=[ 1198 FileRequired(), 1199 SrpmValidator()]) 1200 return form12271218 form = BaseBuildFormFactory(active_chroots, FlaskForm) 1219 form.pkgs = wtforms.TextAreaField( 1220 "Pkgs", 1221 validators=[ 1222 wtforms.validators.DataRequired(message="URLs to packages are required"), 1223 UrlListValidator(), 1224 UrlSrpmListValidator()], 1225 filters=[StringListFilter()]) 1226 return form1230 modulemd = FileField("modulemd", validators=[ 1231 FileRequired(), 1232 # @TODO Validate modulemd.yaml file 1233 ]) 1234 1235 create = wtforms.BooleanField("create", default=True, false_values=FALSE_VALUES) 1236 build = wtforms.BooleanField("build", default=True, false_values=FALSE_VALUES)12371240 class ModuleBuildForm(FlaskForm): 1241 modulemd = FileField("modulemd") 1242 scmurl = wtforms.StringField() 1243 branch = wtforms.StringField() 1244 1245 distgit = DistGitSelectField()1246 1247 return ModuleBuildForm(*args, **kwargs) 12481251 repo_url = wtforms.StringField("repo_url", default='') 1252 api_key = wtforms.StringField("api_key", default='') 125312601263 1264 """ 1265 Validator for editing chroots in project 1266 (adding packages to minimal chroot) 1267 """ 1268 1269 buildroot_pkgs = wtforms.StringField("Packages") 1270 1271 repos = wtforms.TextAreaField('Repos', 1272 validators=[UrlRepoListValidator(), 1273 wtforms.validators.Optional()], 1274 filters=[StringListFilter()]) 1275 1276 comps = FileField("comps_xml") 1277 1278 module_toggle = wtforms.StringField("Enable module", 1279 validators=[ModuleEnableNameValidator()], 1280 filters=[StringWhiteCharactersFilter()] 1281 ) 1282 1283 with_opts = wtforms.StringField("With options") 1284 without_opts = wtforms.StringField("Without options") 1285 1286 bootstrap = create_mock_bootstrap_field("chroot") 1287 bootstrap_image = create_mock_bootstrap_image_field() 1288 1290 """ We need to special-case custom_image configuration """ 1291 result = super().validate(*args, **kwargs) 1292 if self.bootstrap.data != "custom_image": 1293 return result 1294 if not self.bootstrap_image.data: 1295 self.bootstrap_image.errors.append( 1296 "Custom image is selected, but not specified") 1297 return False 1298 return result12991302 extend = wtforms.StringField("Chroot name") 1303 expire = wtforms.StringField("Chroot name") 1304 ownername = wtforms.HiddenField("Owner name") 1305 projectname = wtforms.HiddenField("Project name")13061309 comment = wtforms.TextAreaField("Comment")13101313 1314 @staticmethod 1318 1319 builder_default = False 1320 admin_default = False 1321 1322 if permission: 1323 if permission.copr_builder != helpers.PermissionEnum("nothing"): 1324 builder_default = True 1325 if permission.copr_admin != helpers.PermissionEnum("nothing"): 1326 admin_default = True 1327 1328 setattr(F, "copr_builder", 1329 wtforms.BooleanField( 1330 default=builder_default, 1331 false_values=FALSE_VALUES, 1332 filters=[ValueToPermissionNumberFilter()])) 1333 1334 setattr(F, "copr_admin", 1335 wtforms.BooleanField( 1336 default=admin_default, 1337 false_values=FALSE_VALUES, 1338 filters=[ValueToPermissionNumberFilter()])) 1339 1340 return F13411344 1345 """Creates a dynamic form for given set of copr permissions""" 1346 @staticmethod 1350 1351 for perm in permissions: 1352 builder_choices = helpers.PermissionEnum.choices_list() 1353 admin_choices = helpers.PermissionEnum.choices_list() 1354 1355 builder_default = perm.copr_builder 1356 admin_default = perm.copr_admin 1357 1358 setattr(F, "copr_builder_{0}".format(perm.user.id), 1359 wtforms.SelectField( 1360 choices=builder_choices, 1361 default=builder_default, 1362 coerce=int)) 1363 1364 setattr(F, "copr_admin_{0}".format(perm.user.id), 1365 wtforms.SelectField( 1366 choices=admin_choices, 1367 default=admin_default, 1368 coerce=int)) 1369 1370 return F13711374 description = wtforms.TextAreaField('Description', 1375 validators=[wtforms.validators.Optional()]) 1376 1377 instructions = wtforms.TextAreaField('Instructions', 1378 validators=[wtforms.validators.Optional()]) 1379 1380 chroots = wtforms.TextAreaField('Chroots', 1381 validators=[wtforms.validators.Optional(), ChrootsValidator()]) 1382 1383 repos = wtforms.TextAreaField('External Repositories', 1384 validators=[UrlRepoListValidator(), 1385 wtforms.validators.Optional()], 1386 filters=[StringListFilter()]) 1387 1388 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1389 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1390 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1391 bootstrap = create_mock_bootstrap_field("project") 1392 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1393 follow_fedora_branching = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1394 follow_fedora_branching = wtforms.BooleanField(default=True, false_values=FALSE_VALUES) 1395 delete_after_days = wtforms.IntegerField( 1396 validators=[wtforms.validators.Optional(), 1397 wtforms.validators.NumberRange(min=-1, max=60)], 1398 filters=[(lambda x : -1 if x is None else x)]) 1399 1400 # Deprecated, use `enable_net` instead 1401 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1402 enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1403 multilib = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1404 module_hotfixes = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES)14051408 @staticmethod14311410 class F(FlaskForm): 1411 source = wtforms.StringField( 1412 "Source", 1413 default=copr.full_name) 1414 1415 owner = wtforms.SelectField( 1416 "Fork owner", 1417 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 1418 default=user.name, 1419 validators=[wtforms.validators.DataRequired()]) 1420 1421 name = wtforms.StringField( 1422 "Fork name", 1423 default=copr.name, 1424 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 1425 1426 confirm = wtforms.BooleanField( 1427 "Confirm", 1428 false_values=FALSE_VALUES, 1429 default=False)1430 return F1434 buildroot_pkgs = wtforms.StringField('Additional packages to be always present in minimal buildroot') 1435 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 1436 validators=[UrlRepoListValidator(), 1437 wtforms.validators.Optional()], 1438 filters=[StringListFilter()]) 1439 comps = None 1440 upload_comps = FileField("Upload comps.xml") 1441 delete_comps = wtforms.BooleanField("Delete comps.xml", false_values=FALSE_VALUES)14421445 """ 1446 Otherwise choices are required and in some cases we don't know them beforehand 1447 """14501453 copr_ids = SelectMultipleFieldNoValidation(wtforms.IntegerField("Pinned Copr ID")) 1454 145814771460 super().validate() 1461 1462 choices = [str(c.id) for c in ComplexLogic.get_coprs_pinnable_by_owner(self.owner)] 1463 if any([i and i not in choices for i in self.copr_ids.data]): 1464 self.copr_ids.errors.append("Unexpected value selected") 1465 return False 1466 1467 limit = app.config["PINNED_PROJECTS_LIMIT"] 1468 if len(self.copr_ids.data) > limit: 1469 self.copr_ids.errors.append("Too many pinned projects. Limit is {}!".format(limit)) 1470 return False 1471 1472 if len(list(filter(None, self.copr_ids.data))) != len(set(filter(None, self.copr_ids.data))): 1473 self.copr_ids.errors.append("You can pin a particular project only once") 1474 return False 1475 1476 return True1480 """ 1481 Form for upvoting and downvoting projects 1482 """ 1483 upvote = wtforms.SubmitField("Upvote") 1484 downvote = wtforms.SubmitField("Downvote") 1485 reset = wtforms.SubmitField("Reset vote")1486 14901493 project = wtforms.StringField("Project")1494149715061499 if not message: 1500 message = "Group with the alias '{}' already exists." 1501 self.message = message15021504 if UsersLogic.group_alias_exists(field.data): 1505 raise wtforms.ValidationError(self.message.format(field.data))1509 1510 name = wtforms.StringField( 1511 validators=[ 1512 wtforms.validators.Regexp( 1513 re.compile(r"^[\w.-]+$"), 1514 message="Name must contain only letters," 1515 "digits, underscores, dashes and dots."), 1516 GroupUniqueNameValidator() 1517 ] 1518 )15191522 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 1523 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 1524 filter = wtforms.FieldList(wtforms.StringField("Package Filter"), 1525 validators=[wtforms.validators.DataRequired("You must select some packages from this project")]) 1526 api = wtforms.FieldList(wtforms.StringField("Module API")) 1527 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 1528 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 1529 153315541535 if not FlaskForm.validate(self): 1536 return False 1537 1538 # Profile names should be unique 1539 names = [x for x in self.profile_names.data if x] 1540 if len(set(names)) < len(names): 1541 self.profile_names.errors.append("Profile names must be unique") 1542 return False 1543 1544 # WORKAROUND 1545 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 1546 # profile_pkgs in seconds box, it is sorted and validated correctly 1547 for i in range(0, len(self.profile_names.data)): 1548 # If profile name is not set, then there should not be any packages in this profile 1549 if not flask.request.form["profile_names-{}".format(i)]: 1550 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 1551 self.profile_names.errors.append("Missing profile name") 1552 return False 1553 return True1557 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 1558 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 1559 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 1560 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 1561 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 1562 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])1563
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |