From b42333562d95617396d4fff1b207e641a5043799 Mon Sep 17 00:00:00 2001 From: ievgenii vdovenko Date: Fri, 20 Jun 2025 16:19:30 +0200 Subject: [PATCH] feature: adds DB model and global settings inheritance --- rhodecode/__init__.py | 2 +- .../dbmigrate/versions/116_version_5_6_0.py | 64 +++++++++++++++++++ rhodecode/model/db.py | 52 ++++++++++++--- 3 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 rhodecode/lib/dbmigrate/versions/116_version_5_6_0.py diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py index 0c62e827..aa636536 100644 --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -100,7 +100,7 @@ PYRAMID_SETTINGS = {} EXTENSIONS = {} __version__ = ".".join((str(each) for each in VERSION[:3])) -__dbversion__ = 115 # defines current db version for migrations +__dbversion__ = 116 # defines current db version for migrations __license__ = "AGPLv3, and Commercial License" __author__ = "RhodeCode GmbH" __url__ = "https://code.rhodecode.com" diff --git a/rhodecode/lib/dbmigrate/versions/116_version_5_6_0.py b/rhodecode/lib/dbmigrate/versions/116_version_5_6_0.py new file mode 100644 index 00000000..bb4fbd73 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/116_version_5_6_0.py @@ -0,0 +1,64 @@ +import json +import logging +from sqlalchemy import * +from sqlalchemy.engine import reflection + +from alembic.migration import MigrationContext +from alembic.operations import Operations + +from rhodecode.lib.dbmigrate.versions import _reset_base +from rhodecode.lib.jsonalchemy import MutationObj, JsonType +from rhodecode.model import meta, init_model_encryption + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + + from rhodecode.lib.dbmigrate.schema import db_4_20_0_0 as db + + init_model_encryption(db) + + context = MigrationContext.configure(migrate_engine.connect()) + op = Operations(context) + + table = db.PullRequest.__table__ + with op.batch_alter_table(table.name) as batch_op: + new_column = Column( + "settings_json", + MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(16384))), + ), + default=dict, + ) + batch_op.add_column(new_column) + + _inherit_settings(db, meta.Session, op) + + +def downgrade(migrate_engine): + pass + + +def _inherit_settings(models, _SESSION, op): + for pr in _SESSION.query(models.PullRequest).all(): + repo = pr.target_repo + repo_type = repo.repo_type + close_branch_before_merging = False + + if repo_type in ["git", "hg"]: + close_branch_before_merging = getattr(repo, f"{repo_type}_close_branch_before_merging", False) + + json_settings = {"close_branch_before_merging": close_branch_before_merging} + params = {"id": pr.pull_request_id, "value": json.dumps(json_settings)} + query = text( + """UPDATE pull_requests + SET settings_json = :value + WHERE pull_request_id = :id""" + ).bindparams(**params) + op.execute(query) + + _SESSION().commit() diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index 2bdb621b..87d2de47 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -77,7 +77,6 @@ from zope.cachedescriptors.property import Lazy as LazyProperty from webhelpers2.text import remove_formatting -from rhodecode import ConfigGet from rhodecode.lib.str_utils import safe_bytes from rhodecode.translation import _ from rhodecode.lib.vcs import get_vcs_instance, VCSError @@ -108,6 +107,8 @@ from rhodecode.lib.exceptions import ArtifactMetadataDuplicate, ArtifactMetadata from rhodecode.lib.pyramid_utils import get_current_request from rhodecode.model.meta import Base, Session +DEFAULT_JSON_OBJ_SIZE = 16384 + URL_SEP = "/" log = logging.getLogger(__name__) @@ -364,7 +365,7 @@ class BaseModel(object): return cls.query().all() @classmethod - def delete(cls, id_): + def delete(cls, id_): # noqa: F811 obj = cls.query().get(id_) Session().delete(obj) @@ -418,7 +419,7 @@ class RhodeCodeSetting(Base, BaseModel): @validates("_app_settings_value") def validate_settings_value(self, key, val): - assert type(val) == str + assert type(val) is str return val @hybrid_property @@ -535,7 +536,7 @@ class RepoRhodeCodeSetting(Base, BaseModel): @validates("_app_settings_value") def validate_settings_value(self, key, val): - assert type(val) == str + assert type(val) is str return val @hybrid_property @@ -4442,13 +4443,15 @@ class _PullRequestBase(BaseModel): _last_merge_target_rev = Column("last_merge_other_rev", String(40), nullable=True) _last_merge_status = Column("merge_status", Integer(), nullable=True) last_merge_metadata = Column( - "last_merge_metadata", MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + "last_merge_metadata", + MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE)))), ) merge_rev = Column("merge_rev", String(40), nullable=True) reviewer_data = Column( - "reviewer_data_json", MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + "reviewer_data_json", + MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE)))), ) @property @@ -4632,6 +4635,13 @@ class PullRequest(Base, _PullRequestBase): LATEST_VER = "latest" pull_request_id = Column("pull_request_id", Integer(), nullable=False, primary_key=True) + settings = Column( + "settings_json", + MutationObj.as_mutable( + JsonType(dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE))), + ), + default=dict, + ) def __repr__(self): if self.pull_request_id: @@ -4797,6 +4807,26 @@ class PullRequest(Base, _PullRequestBase): return self.versions_count +@event.listens_for(PullRequest, "before_insert") +def _init_pr_default_settings(mapper, connection, pull_request): + if not pull_request.settings: + pull_request.settings = { + "close_branch_before_merging": _inherit_global_settings(pull_request), + } + + +def _inherit_global_settings(pull_request): + from rhodecode.model.settings import VcsSettingsModel # handle circular dependency issue + + repo_type = pull_request.target_repo.repo_type + settings_model = VcsSettingsModel(repo=pull_request.target_repo) + settings = settings_model.get_general_settings() + key = "rhodecode_{}_close_branch_before_merging" + if repo_type in ["hg", "git"]: + return settings.get(key.format(repo_type), False) + return False + + class PullRequestVersion(Base, _PullRequestBase): __tablename__ = "pull_request_versions" __table_args__ = (base_table_args,) @@ -4859,7 +4889,9 @@ class PullRequestReviewers(Base, BaseModel): pull_requests_reviewers_id = Column("pull_requests_reviewers_id", Integer(), nullable=False, primary_key=True) pull_request_id = Column("pull_request_id", Integer(), ForeignKey("pull_requests.pull_request_id"), nullable=False) user_id = Column("user_id", Integer(), ForeignKey("users.user_id"), nullable=True) - _reasons = Column("reason", MutationList.as_mutable(JsonType("list", dialect_map=dict(mysql=UnicodeText(16384))))) + _reasons = Column( + "reason", MutationList.as_mutable(JsonType("list", dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE)))) + ) mandatory = Column("mandatory", Boolean(), nullable=False, default=False) role = Column("role", Unicode(255), nullable=True, default=ROLE_REVIEWER) @@ -4867,7 +4899,7 @@ class PullRequestReviewers(Base, BaseModel): user = relationship("User") pull_request = relationship("PullRequest", back_populates="reviewers") - rule_data = Column("rule_data_json", JsonType(dialect_map=dict(mysql=UnicodeText(16384)))) + rule_data = Column("rule_data_json", JsonType(dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE)))) def rule_user_group_data(self): """ @@ -5232,7 +5264,9 @@ class Integration(Base, BaseModel): name = Column("name", String(255), nullable=False) child_repos_only = Column("child_repos_only", Boolean(), nullable=False, default=False) - settings = Column("settings_json", MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384))))) + settings = Column( + "settings_json", MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(DEFAULT_JSON_OBJ_SIZE)))) + ) repo_id = Column("repo_id", Integer(), ForeignKey("repositories.repo_id"), nullable=True, unique=None, default=None) repo = relationship("Repository", lazy="joined", back_populates="integrations")