实现企业版特性的指南
- 编写代码和测试。与任何代码一样,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_features(variable_environment_scope:真正的)# EE专用端
-# EE-specific start=渲染“ci /变量/ environment_scope”,form_field:form_field,变量:变量-# EE-specific end
特定于ee的注释不应该反向移植到CE。
检测EE-only文件
对于每次提交(on除外)主
),ee-files-location-check
CI作业尝试检测是否有任何仅限ee的新文件。如果检测到任何文件,作业将失败,并解释原因以及如何使其通过。
基本上,解决方法很简单:Git mv
。
如何命名你的分支?
对于任何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 /
将一些不能移动到的EE-only文件加入白名单的ee-files-location-check
CI作业提供了不能或不应该移动到的文件或文件夹的白名单ee /
。请随意打开issue讨论将新文件/文件夹添加到此白名单。
例如,决定将电子文件从qa /
来ee / qa /
会使建立gitLab - {ce、ee} qa
Docker图像,它是不值得这么复杂。
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_settings。after_sign_out_path。存在||new_user_session_path结束
您不应该就地修改方法,而应该添加预谋
到现有文件:
类程序控制器<ActionController::基地预谋EE::程序控制器#……defafter_sign_out_path_for(资源)current_application_settings。after_sign_out_path。存在||new_user_session_path结束#……结束
创建一个新文件ee /
子目录与改变的实现:
模块EE模块程序控制器扩展::Gitlab::跑龙套::覆盖覆盖: after_sign_out_path_fordefafter_sign_out_path_for(资源)如果Gitlab::地理。二次吗?Gitlab::地理。primary_node。oauth_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#……结束
app / controllers /
代码在控制器中,最常见的冲突类型是与before_action
它在CE中有一个动作列表,但是EE向该列表添加了一些动作。
同样的问题经常发生在params.require
/params.permit
调用。
缓解措施
区分CE和EE动作/关键词。例如params.require
在ProjectsController
:
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代码:
模块API类MergeRequests<葡萄::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帮助程序,我们需要首先定义那些我们想要扩展的帮助程序。尝试在类定义之后立即这样做,以使其简单明了:
模块API类JobArtifacts<葡萄::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路由中注入行为的“钩子”。像这样:
模块API类MergeRequests<葡萄::API助手做# EE::API::MergeRequests将覆盖以下帮助defupdate_merge_request_ee(merge_request)结束结束预谋EE::API::MergeRequests把”:id / merge_requests: merge_request_iid /合并的做merge_request=find_project_merge_request(参数个数[: merge_request_iid])#……update_merge_request_ee(merge_request)#……结束结束结束
请注意,update_merge_request_ee
在CE中不做任何事情,但我们可以在EE中重写它:
模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注前缀做助手做defupdate_merge_request_ee(merge_request)#……结束结束结束结束结束结束
route_setting
EE在EE模块中扩展这一点是非常困难的,这只是为特定的路由存储一些元数据。鉴于此,我们可以简单地离开EEroute_setting
因为它不会造成伤害,我们只是不打算在CE中使用这些元数据。
我们可以在使用的时候重新审视这个政策route_setting
以及我们是否真的需要从情感表达中扩展它。现在我们不怎么用它。
利用类方法来设置特定于ee的数据
有时我们需要为特定的API路由使用不同的参数,而且我们不能轻易地用EE模块扩展它,因为Grape在不同的块中有不同的上下文。为了克服这个问题,我们可以使用API类中的类方法。
例如,在一个地方,我们需要传递一个额外的参数at_least_one_of
以便API可以将仅限ee的参数视为最小参数。这不是很漂亮,但它是有效的:
模块API类MergeRequests<葡萄::APIdef自我。update_params_at_least_one_of%我assignee_id描述]结束预谋EE::API::MergeRequests参数个数做at_least_one_of(*::API::MergeRequests。update_params_at_least_one_of)结束结束结束
然后我们可以很容易地在EE类方法中扩展这个参数:
模块EE模块API模块MergeRequests扩展ActiveSupport这样::关注class_methods做defupdate_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。)
- 在EE分支中进行更改。如果可能的话,保持一个单独的提交(将被压扁),以帮助后端移植和审查。
- 打开对EE项目的合并请求。
- 将您所做的更改应用于CE项目分支中的CE文件。(提示:使用
补丁
与您在EE分支中提交的不同) - 打开对CE项目的合并请求,指的是它是EE更改的后端口,并链接到EE中的MR打开。
- 一旦合并了EE MR,就可以合并向CE的MR。但在此之前。
注意:关于SCSS,确保文件在外面/ ee /
不要在CE和EE项目之间产生分歧。
gitlab-svgs
冲突app /资产/图片/ icons.json
或app /资产/图片/ icons.svg
是否可以简单地通过使用纱线运行SVG
。