实现企业版特性的指南

  • 编写代码和测试。与任何代码一样,EE特性应该具有良好的测试覆盖率以防止回归。
  • 写文档。添加文档到文档/目录中。描述功能,并包括截图,如果适用的话。
  • 提交一个MR到www-gitlab-com项目。:将新功能添加到[EE功能列表][EE -features-list]。

在无牌情况下担任行政长官

自实施以来GitLab CE的特性与未授权的EE实例一起工作当没有激活许可证时,GitLab企业版应该像GitLab社区版一样工作。所以EE特征总是应该被保护的project.feature_available吗?group.feature_available吗?(或License.feature_available吗?如果它是一个系统范围的特性)。

CE规格应尽可能保持不变,而额外的规格应添加到EE。许可的特性可以使用spec helper存根stub_licensed_features情感表达:LicenseHelpers

EE码分离

我们想要一个单一代码库最终,但在我们达到目标之前,我们仍然需要将变更从GitLab CE合并到EE。为了帮助我们实现这一目标,我们应该确保不再为了实现EE特性而编辑CE文件。

相反,所有EE代码都应该放在ee /顶级目录。其余的代码应该尽可能接近CE文件。

EE-specific评论

当与之完全分离时是无法实现的ee /目录中,您可以将代码包装在EE特定的注释中,以指定与CE/EE的区别,并为解决冲突的人添加一些上下文。

# ee特定的启动stub_licensed_featuresvariable_environment_scope:真正的# EE专用端
-# EE-specific start=渲染“ci /变量/ environment_scope”form_field:form_field变量:变量-# EE-specific end

特定于ee的注释不应该反向移植到CE。

检测EE-only文件

对于每次提交(on除外)),ee-files-location-checkCI作业尝试检测是否有任何仅限ee的新文件。如果检测到任何文件,作业将失败,并解释原因以及如何使其通过。

基本上,解决方法很简单:Git mv ee/

如何命名你的分支?

对于任何EE分支,作业将尝试通过删除任何分支来检测其CE对应ee -前缀或ee从EE分支名称中提取后缀,并匹配包含该后缀的最后一个分支。

例如,来自EE分支new-shiny-feature-ee(或ee-new-shiny-feature),则会找到相应的行政长官分支机构:

  • new-shiny-feature
  • ce-new-shiny-feature
  • new-shiny-feature-ce
  • my-super-new-shiny-feature-in-ce

将一些不能移动到的EE-only文件加入白名单ee /

ee-files-location-checkCI作业提供了不能或不应该移动到的文件或文件夹的白名单ee /。请随意打开issue讨论将新文件/文件夹添加到此白名单。

例如,决定将电子文件从qa /ee / qa /会使建立gitLab - {ce、ee} qaDocker图像,它是不值得这么复杂

EE-only特性

如果正在开发的功能没有以任何形式出现在CE中,我们不需要将代码放在下面EE名称空间。例如,EE模型可以进入:ee / app /模型/ awesome.rb使用太棒了作为类名。这不仅适用于模型。下面是其他一些例子:

  • ee / app / controllers / foos_controller.rb
  • ee / app /发现者/ foos_finder.rb
  • ee / app /帮助/ foos_helper.rb
  • ee / app /邮件/ foos_mailer.rb
  • ee / app /模型/ foo.rb
  • ee /应用程序/政策/ foo_policy.rb
  • ee / app /序列化器/ foo_entity.rb
  • ee / app /序列化器/ foo_serializer.rb
  • ee /应用程序/服务/ foo / create_service.rb
  • ee / app /验证/ foo_attr_validator.rb
  • ee / app /工人/ foo_worker.rb

这之所以有效,是因为对于CE的eager-load/auto-load路径中存在的每个路径,我们都添加了相同的路径ee /- prededpath in配置/ application.rb

基于CE特性的EE特性

对于构建在现有CE特性上的特性,在EE名称空间和预谋在CE课上。这使得冲突不太可能在CE到EE合并期间发生,因为CE类中只添加了一行——预谋线。

因为该模块需要一个EE命名空间中,文件也应该放在ee /子目录。例如,我们希望在EE中扩展用户模型,因此我们有一个名为:: EE::用户放入ee / app /模型/ ee / user.rb

这也不仅仅适用于模型。下面是其他一些例子:

  • ee / app / controllers / ee / foos_controller.rb
  • ee / app /发现者/ ee / foos_finder.rb
  • ee / app /帮助/ ee / foos_helper.rb
  • ee / app /邮件/ ee / foos_mailer.rb
  • ee / app /模型/ ee / foo.rb
  • ee /应用程序/政策/ ee / foo_policy.rb
  • ee / app /序列化器/ ee / foo_entity.rb
  • ee / app /序列化器/ ee / foo_serializer.rb
  • ee /应用程序/服务/ ee / foo / create_service.rb
  • ee / app /验证/ ee / foo_attr_validator.rb
  • ee / app /工人/ ee / foo_worker.rb

重写CE方法

要覆盖CE代码库中存在的方法,请使用预谋。它允许您使用模块中的方法重写类中的方法,同时仍然可以访问类的实现超级

它有几个陷阱:

  • 你应该总是扩展:Gitlab:跑龙套::覆盖和使用覆盖来保护“overrider”方法,以确保如果该方法在CE中被重命名,EE重写不会被静默地遗忘。
  • 当“重写”将在CE实现中间添加一行时,您应该重构CE方法并将其拆分为更小的方法。或者创建一个在CE中为空的“钩子”方法,并在EE中使用特定于EE的实现。
  • 当原始实现包含一个保护子句(例如:除非条件,否则返回),我们不能通过重写方法来轻松扩展该行为,因为我们不知道何时被重写的方法(即调用超级在重写方法中)会希望尽早停止。在这种情况下,我们不应该只是重写它,而是更新原始方法,使它调用我们想要扩展的其他方法,比如模板方法模式。例如,给定这个基数:

    基地def执行返回除非启用?#……#……结束结束

    而不仅仅是重写基地#执行,我们应该更新它并将其行为提取到另一个方法中:

    基地def执行返回除非启用?do_something结束私人defdo_something#……#……结束结束

    那我们就可以推翻它do_something不用担心警卫:

    模块情感表达::Base扩展::Gitlab::跑龙套::覆盖覆盖: do_somethingdefdo_something按照上面的模式调用super并扩展它结束结束

    这需要首先更新CE,或者确保将其向后移植到CE。

添加前缀时,将它们放在ee /指定子目录,并在其中包装类或模块模块EE避免命名冲突。

的CE实现程序控制器# after_sign_out_path_for

defafter_sign_out_path_for资源current_application_settingsafter_sign_out_path存在||new_user_session_path结束

您不应该就地修改方法,而应该添加预谋到现有文件:

程序控制器<ActionController::基地预谋EE::程序控制器#……defafter_sign_out_path_for资源current_application_settingsafter_sign_out_path存在||new_user_session_path结束#……结束

创建一个新文件ee /子目录与改变的实现:

模块EE模块程序控制器扩展::Gitlab::跑龙套::覆盖覆盖: after_sign_out_path_fordefafter_sign_out_path_for资源如果Gitlab::地理二次吗?Gitlab::地理primary_nodeoauth_logout_url@geo_logout_state其他的超级结束结束结束结束

使用自描述的包装方法

当不可能/不符合逻辑地修改方法的实现时。将其包装在一个自我描述的方法中并使用该方法。

例如,在CE中只有一个管理允许访问所有私有项目/组,但在EE中也允许访问审计师具有完全的私人访问权限。的实现是不正确的用户#管理?,所以添加一个方法full_private_access吗?应用程序/模型/ users.rb。行政长官将会:

deffull_private_access吗?管理?结束

在EE中,实现ee / app /模型/ ee / users.rb是:

覆盖: full_private_access ?deffull_private_access吗?超级||审计师?结束

lib / gitlab / visibility_level.rb此方法用于返回允许的可见性级别:

deflevels_for_user用户=如果用户full_private_access吗?私人内部公共elsif#……结束

看到CE先生EE先生获取完整的实现细节。

代码app / controllers /

在控制器中,最常见的冲突类型是与before_action它在CE中有一个动作列表,但是EE向该列表添加了一些动作。

同样的问题经常发生在params.require/params.permit调用。

缓解措施

区分CE和EE动作/关键词。例如params.requireProjectsController

defproject_params参数个数需要:项目).许可证project_params_attributes结束总是返回一个符号数组,以最适合用例的方式创建。它应该按字母顺序排序。defproject_params_attributes%我描述名字路径结束

情感表达:ProjectsController模块:

defproject_params_attributes超级+project_params_attributes_ee结束defproject_params_attributes_ee%我approvals_before_mergeapprover_group_idsapprover_ids...结束

代码应用程序/模型/

ee专用模型应该延长EE:模型

例如,如果EE具有特定的Tanuki模型,你会把它放在ee / app /模型/ ee / tanuki.rb

代码app / views /

EE在CE视图中添加一些特定视图代码是一个非常常见的问题。例如,项目设置页面中的审批代码。

缓解措施

特定于ee的代码块应该移动到局部。这避免了与大块HAML代码的冲突,当您向方程式添加缩进时,这些代码很难解决。

特定于电气设备的视图应放置在ee / app / views / ee /,如果合适的话,使用额外的子目录。

代码lib /

将特定于ee的逻辑放在顶层EE模块名称空间。类下面的类命名空间EE模块,就像你通常做的那样。

例如,如果CE中有LDAP类lib / gitlab / ldap /然后将特定于ee的LDAP类放入ee / lib / ee / gitlab / ldap

代码lib / api /

通过一行。来扩展EE特性是非常棘手的预谋,每个人都不一样葡萄功能,我们可能需要不同的策略来扩展它。为了方便地应用不同的策略,我们将使用延长ActiveSupport这样::关注在EE模块。

将EE模块文件放在下面基于CE特性的EE特性

EE API路由

对于EE API路由,我们把它们放在前缀布洛克:

模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注前缀参数个数需要: id类型:字符串描述:“项目的ID”结束资源:项目要求:::API::API::PROJECT_ENDPOINT_REQUIREMENTS#……结束结束结束结束结束

注意,由于名称空间的差异,我们需要对某些常量使用完整限定符。

EE参数

我们可以定义参数个数和利用使用在另一个参数个数定义,以包含在EE中定义的参数。但是,我们需要首先在CE中定义“接口”,以便EE覆盖它。我们不需要在其他地方这样做,因为预谋但是Grape内部很复杂,我们不容易做到这一点,所以我们将遵循常规的面向对象实践,在这里首先定义接口。

例如,假设我们有更多的可选参数用于EE,给出以下CE API代码:

模块APIMergeRequests<葡萄::API# EE::API::MergeRequests将覆盖以下帮助助手参数个数: optional_params_ee结束结束预谋EE::API::MergeRequests参数个数: optional_params# CE的具体参数在这里…使用: optional_params_ee结束结束结束

然后我们可以在EE模块中重写它:

模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注前缀助手参数个数: optional_params_ee# EE的具体参数在这里…结束结束结束结束结束结束

这样,对于该API文件,CE和EE之间的唯一区别就是预谋EE:: API:: MergeRequests

EE助手

为了使EE模块能够轻松地覆盖CE帮助程序,我们需要首先定义那些我们想要扩展的帮助程序。尝试在类定义之后立即这样做,以使其简单明了:

模块APIJobArtifacts<葡萄::API# EE::API::JobArtifacts将覆盖以下帮助程序助手defauthorize_download_artifacts !authorize_read_builds !结束结束预谋EE::API::JobArtifacts结束结束

然后我们可以遵循常规的面向对象实践来覆盖它:

模块EE模块API模块JobArtifacts扩展ActiveSupport这样::关注前缀助手defauthorize_download_artifacts !超级check_cross_project_pipelines_feature !结束结束结束结束结束结束

EE-specific行为

有时我们需要在某些api中使用特定于ee的行为。通常我们可以使用EE方法来覆盖CE方法,但是API路由不是方法,因此不能简单地覆盖。我们需要将它们提取到一个独立的方法中,或者引入一些可以在CE路由中注入行为的“钩子”。像这样:

模块APIMergeRequests<葡萄::API助手# EE::API::MergeRequests将覆盖以下帮助defupdate_merge_request_eemerge_request结束结束预谋EE::API::MergeRequests”:id / merge_requests: merge_request_iid /合并的merge_request=find_project_merge_request参数个数: merge_request_iid])#……update_merge_request_eemerge_request#……结束结束结束

请注意,update_merge_request_ee在CE中不做任何事情,但我们可以在EE中重写它:

模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注前缀助手defupdate_merge_request_eemerge_request#……结束结束结束结束结束结束

EEroute_setting

在EE模块中扩展这一点是非常困难的,这只是为特定的路由存储一些元数据。鉴于此,我们可以简单地离开EEroute_setting因为它不会造成伤害,我们只是不打算在CE中使用这些元数据。

我们可以在使用的时候重新审视这个政策route_setting以及我们是否真的需要从情感表达中扩展它。现在我们不怎么用它。

利用类方法来设置特定于ee的数据

有时我们需要为特定的API路由使用不同的参数,而且我们不能轻易地用EE模块扩展它,因为Grape在不同的块中有不同的上下文。为了克服这个问题,我们可以使用API类中的类方法。

例如,在一个地方,我们需要传递一个额外的参数at_least_one_of以便API可以将仅限ee的参数视为最小参数。这不是很漂亮,但它是有效的:

模块APIMergeRequests<葡萄::APIdef自我update_params_at_least_one_of%我assignee_id描述结束预谋EE::API::MergeRequests参数个数at_least_one_of*::API::MergeRequestsupdate_params_at_least_one_of结束结束结束

然后我们可以很容易地在EE类方法中扩展这个参数:

模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注class_methodsdefupdate_params_at_least_one_of超级*%我南瓜结束结束结束结束结束

如果我们在很多路线上都需要这个,这可能会很烦人,但这可能是目前最简单的解决方案。

代码规范/

当您测试仅限ee的特性时,请避免在现有CE规范中添加示例。另外,不要更改现有的CE示例,因为当EE在没有许可证的情况下运行时,它们应该保持原样。

而是将EE规范放在ee /规范文件夹中。

JavaScript代码资产/ javascript /

为了分离特定于ee的js文件,我们还应该将这些文件移动到ee文件夹中。

例如,可以有一个应用程序/资产/ javascript / protected_branches / protected_branches_bundle.js和一个EE的对应物ee / app /资产/ javascript / protected_branches / protected_branches_bundle.js

参见前端指南性能部分获取有关在EE中管理特定于页面的javascript的信息。

SCSS代码资产/样式表

要在SCSS文件中分离特定于EE的样式,如果要为其添加样式的组件仅限于EE,那么最好在适当的目录中有一个单独的SCSS文件应用程序/资产/样式表。看到补丁的变化有关如何安全地合并更改的说明。

在某些情况下,这不是完全可能的,或者创建专用的SCSS文件是多余的,例如,某些组件的文本样式与EE不同。在这种情况下,样式通常保存在CE和EE通用的样式表中,明智的做法是将此类规则集与其他CE规则隔离开来(同时添加描述相同规则的注释),以避免在CE到EE合并期间发生冲突。

.section-body.section-title背景gl-header-color美元}&.ee-section-body.section-title背景gl-header-color-cyan美元}}}

.section-body.section-title背景gl-header-color美元}}//特定于ee的启动.section-body.ee-section-body.section-title背景gl-header-color-cyan美元}}// ee专用端

将更改从EE向后移植到CE

在处理特定于ee的特性时,可能需要调整一些非特定于ee的文件。这里有一个工作流程,可以确保这些更改也可以安全地后移植到CE中。(此方法不涉及通过csslab。)

  1. 在EE分支中进行更改。如果可能的话,保持一个单独的提交(将被压扁),以帮助后端移植和审查。
  2. 打开对EE项目的合并请求。
  3. 将您所做的更改应用于CE项目分支中的CE文件。(提示:使用补丁与您在EE分支中提交的不同)
  4. 打开对CE项目的合并请求,指的是它是EE更改的后端口,并链接到EE中的MR打开。
  5. 一旦合并了EE MR,就可以合并向CE的MR。但在此之前

注意:关于SCSS,确保文件在外面/ ee /不要在CE和EE项目之间产生分歧。

gitlab-svgs

冲突app /资产/图片/ icons.jsonapp /资产/图片/ icons.svg是否可以简单地通过使用纱线运行SVG

Baidu
map