如何在 GitLab EE 的基础上开发

前面说到极狐GitLab是基于 GitLab EE 进行开发的,所以所有的功能都是从 GitLab EE 上继承过去的,而极狐GitLab时常会需要开发自己的功能,而我们不希望通过覆盖的方式去新建一个同名文件来使得 jh/ 目录下文件得到加载,而是希望通过加载 ce/ 或者 ee/ 下的文件来加载 jh/ 目录下的文件,以保持与上游 GitLab 的功能同步,并且包含有极狐GitLab独有的功能。

方法

该方法我们简单可以理解为在上游通过特定的方式开辟入口,然后在 jh/ 中使用该入口。

要做到上述的行为,我们需要做的事情就是到上游 GitLab 上游仓库 的仓库中为极狐GitLab提供一个入口,例如在 _page.html.haml 模板文件中,我们通过在该文件中添加入口代码。

添加入口

在这我们会需要在 HAML 模板中用到一个 Ruby 里定义的一个比较重要的全局函数 render_if_exists,详细用法如下:

_page.html.haml

= render_if_exists "shared/footer/global_footer"

上游代码对应位置为 _page.html.haml,对应查看

该代码便会通过上下文查找对应的 views/ 文件夹来加载我们需要的这个 _global_footer.html.haml 模板文件,上面说到:

在极狐GitLab的仓库代码中,jh/ 目录下的文件的优先级顺序为 jh/ > ee/ > ce/。在这三个目录同时拥有某个文件时,所有 jh/ 目录下的文件都会优于其他目录下的文件加载。

  • 当极狐GitLab的应用运行时,通过优先加载 jh/ 目录下的 _global_footer.html.haml 这个模板文件来渲染应用,从而达到功能定制的效果。

  • 当上游应用独立运行时,上游应用并没有 jh/ 这个目录,那么上游应用将会优先加载 ee/ 目录下的 _global_footer.html.haml 文件,假如 ee/ 下找不到该文件,则会去加载 ce/ 下的该同名文件,如果 ce/ 目录下也没有当前文件,则该方法不会有任何副作用。

使用入口

通过在对应的 jh/ 目录下添加 jh/app/views/shared/footer/_global_footer.html.haml 文件,然后添加需要渲染的内容。

该示例代码可以在 _global_footer.html.haml 查看到。

路径别名(alias)

在 JavaScript 和 Vue 代码中,我们同样需要针对 GitLab EE 或者 GitLab CE 的代码进行拓展。因此声明了如下 alias ,在不同版本中解析为不同的文件路径。

例如 any_else_ce

  • 在极狐Gitlab中会匹配到 jh/app/javascript/ 目录。
  • 在Gitlab EE中会匹配到 ee/app/javascript/ 目录。
  • 在Gitlab CE中会匹配到 app/javascript/ 目录。
Alias Path in CE Path in EE Path in JH
jh_else_ce app/javascript/xx/xx app/javascript/xx/xx jh/app/javascript/xx/xx
jh_else_ee not contained ee/app/javascript/xx/xx jh/app/javascript/xx/xx
any_else_ce app/javascript/xx/xx ee/app/javascript/xx/xx jh/app/javascript/xx/xx

注意:

  • jh_else_ee 不应该出现在 GitLab CE 的代码中,即除了 jh/ee/ 目录下的文件都不应该使用该别名。
  • 如果某个文件仅在 GitLab CE 存在,在 jh/ 目录新增同名文件后,需要在 GitLab CE 代码中将 alias 指定为 jh_else_ce
  • 如果某个文件在 GitLab CE 通过 jh_else_ce 引用后,又在 ee/ 目录新增了同名文件进行覆盖,则需要在 GitLab CE 代码中将 alias 改为 any_else_ce

覆盖上游模块

如果我们需要在极狐GitLab 中覆盖 GitLab CE 或者 GitLab EE 的 ES Module

例如:

我们想要覆盖 app/assets/javascripts/repository/components/tree_content.vue 这个组件,首先需要在上游对应引用该组件的地方,将引用路径别名改为 jh_else_ceany_else_ce,然后提交一个上游的 MR。

此时,由于上游 pipelineadd-jh-folder 会拉取极狐GitLab 的代码进行构建,但是极狐GitLab 的仓库中还没有新建对应的同名文件,jh_else_ceany_else_cejh/ 目录下找不到对应文件,从而导致上游 pipeline 失败。详情参考对应的讨论

因此,我们需要同时创建一个极狐GitLab 的分支,分支名需要严格按照上文所提 MR 的上游分支名加 -jh 后缀。

例如:

上游 MR 分支名为:1001-add-some-jihu-feature,则需要在极狐GitLab 中新建一个名为 1001-add-some-jihu-feature-jh 的分支。

然后提交我们用来覆盖的上游的 jh/app/assets/javascripts/repository/components/tree_content.vue 文件到这个分支。

提示:

为了简单处理,此时可以直接复制 app/assets/javascripts/repository/components/tree_content.vue 到这里。

最后在极狐GitLab 中将这个MR设置为 Draft 保证在上游分支合入前不合入该极狐GitLab 分支。

此时,上游 pipelineadd-jh-folder 会自动从极狐GitLab 中对应 -jh 后缀的分支拉取代码,进行构建。详情参考对应的讨论

待上游 MR 合入,极狐GitLab 同步代码成功后,我们需要合入 1001-add-some-jihu-feature-jh 分支,保证 main-jh 的构建成功。

CSS

在极狐GitLab,如果想要对特定某个页面添加样式,其方式与 GitLab EE 完全一致:

新建一个 jh/app/assets/stylesheets/pages/your_pagename.scss,在其中编写你需要的样式,此文件将会在 jh/app/assets/stylesheets/_jh/application_jh.scss 中被引入,最终被引入至 app/assets/stylesheets/application.scss

页面特定CSS

由于 jh/app/assets/stylesheets/pages/* 下的文件被会全部打包到 app/assets/stylesheets/application.scss 中,会导致用户初始化加载时的样式文件大小不断膨胀。因此,我们需要拆分不同页面特定的样式文件,尽量减少 application.scss 的体积。
GitLab 提供了 add_page_specific_style 方法,使得我们可以在 haml 文件中指定需要加载的特定样式文件。只有当我们访问该页面时,才会从 assets/page_bundles/ 目录加载对应的资源。

例如,我们需要针对 jh/app/views/groups/analytics/performance_analytics/index.html.haml 添加页面特定的样式文件。

首先,先在文件头部添加如下代码: haml // jh/app/views/groups/analytics/performance_analytics/index.html.haml - add_page_specific_style 'page_bundles/performance_analytics' 然后,在 jh/app/assets/stylesheets/page_bundles/ 目录下添加你的 scss 文件。 最后,在 jh/config/initializers/append_jh_page_bundles.rb 中将该文件添加到 precompile 数组中。(注意此处添加的文件名后缀应为 css) 。 ruby config.assets.precompile << 'page_bundles/performance_analytics.css'

注意:

  • 根据上游文档,请优先使用 GitLab UI 中的utinity classes 来完成样式需求,而不是单独编写 CSS。
  • 所有在 jh/app/assets/stylesheets/_jh/application_jh.scss 被引入的文件将直接与上游的样式进行拼接,这意味着对于页面中的同一元素,请确保你的样式比上游样式拥有更高的选择器权重。相比 GitLab CE 与 Gitlab EE,极狐GitLab会为所有页面 html 根元素添加一个额外的 jh-page-wrapper 类,您可以使用此类来提高选择器权重。
  • 随着页面的不断增加,出于加载性能考虑,应当尽量采取页面特定CSS 的方式增加样式文件,避免在 jh/app/assets/stylesheets/pages/ 目录下直接添加。

依赖

如果需要添加只会在极狐GitLab中用到的前端依赖,可以遵循以下步骤来实现: 1. 将依赖的包名和版本其添加到 package.jsondependenciesdevDependencies 字段的尾部 2. 运行 yarn install 生成新的 yarn.lock 3. 将步骤1的包同样声明一份到 jh/frontend.json 对应的字段中

原理

极狐GitLab在每次合并上游代码时,对于 package.jsonyarn.lock 会使用 ours 作为合并策略,即默认使用 main-jh 分支上的版本。再合并完成后会将上游的 package.jsonjh/frontend.json 进行合并,产生一个新的 package.json,然后执行 yarn install,生成新的 yarn.lock,如果 package.jsonyarn.lock 产生了新的 diff ,则生成一个单独的commit并推到 main-jh。相关逻辑参考 jh/bin/build_packagejson 以及 code-sync

注意:

pakcage.jsonjh/frontend.json的合并,只会合并在 jh/frontend.json 已经声明但在 pakcage.json 没有声明的依赖。对于两边都存在的依赖,会默认保留上游的版本,在相应的CI job报错后,需要手动将 jh/frontend.json 中的同名依赖改为别名,例如: "jh-jquery": "npm:jquery@^2.0.0",并将 jh/ 目录代码中原来依赖的旧名字改为新的名字。