Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/ee_features.md')
-rw-r--r--doc/development/ee_features.md339
1 files changed, 245 insertions, 94 deletions
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 28cf6d4e1e3..777bc77875e 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -6,8 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Guidelines for implementing Enterprise Edition features
-- **Write the code and the tests.**: As with any code, EE features should have
- good test coverage to prevent regressions.
+- **Place code in `ee/`**: Put all Enterprise Edition (EE) inside the `ee/` top-level directory. The
+ rest of the code must be as close to the Community Edition (CE) files as possible.
+- **Write tests**: As with any code, EE features must have good test coverage to prevent
+ regressions. All `ee/` code must have corresponding tests in `ee/`.
- **Write documentation.**: Add documentation to the `doc/` directory. Describe
the feature and include screenshots, if applicable. Indicate [what editions](documentation/styleguide/index.md#product-tier-badges)
the feature applies to.
@@ -16,54 +18,72 @@ info: To determine the technical writer assigned to the Stage/Group associated w
[EE features list](https://about.gitlab.com/features/).
<!-- markdownlint-enable MD044 -->
-## Act as SaaS
+## Implement a new EE feature
-When developing locally, there are times when you need your instance to act like the SaaS version of the product.
-In those instances, you can simulate SaaS by exporting an environment variable as seen below:
+If you're developing a GitLab Starter, GitLab Premium, or GitLab Ultimate licensed feature, use these steps to
+add your new feature or extend it.
-```shell
-export GITLAB_SIMULATE_SAAS=1
-```
+GitLab license features are added to [`ee/app/models/gitlab_subscriptions/features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb). To determine how
+to modify this file, first discuss how your feature fits into our licensing with your Product Manager.
-There are many ways to pass an environment variable to your local GitLab instance.
-For example, you can create a `env.runit` file in the root of your GDK with the above snippet.
+Use the following questions to guide you:
-## Act as CE when unlicensed
+1. Is this a new feature, or are you extending an existing licensed feature?
+ - If your feature already exists, you don't have to modify `features.rb`, but you
+ must locate the existing feature identifier to [guard it](#guard-your-ee-feature).
+ - If this is a new feature, decide on an identifier, such as `my_feature_name`, to add to the
+ `features.rb` file.
+1. Is this a **GitLab Starter**, **GitLab Premium**, or **GitLab Ultimate** feature?
+ - Based on the plan you choose to use the feature in, add the feature identifier to `STARTER_FEATURES`,
+ `PREMIUM_FEATURES`, or `ULTIMATE_FEATURES`.
+1. Will this feature be available globally (system-wide at the GitLab instance level)?
+ - Features such as [Geo](../administration/geo/index.md) and
+ [Database Load Balancing](../administration/postgresql/database_load_balancing.md) are used by the entire instance
+ and cannot be restricted to individual user namespaces. These features are defined in the instance license.
+ Add these features to `GLOBAL_FEATURES`.
-Since the implementation of
-[GitLab CE features to work with unlicensed EE instance](https://gitlab.com/gitlab-org/gitlab/-/issues/2500)
-GitLab Enterprise Edition should work like GitLab Community Edition
-when no license is active. So EE features always should be guarded by
-`project.feature_available?` or `group.licensed_feature_available?` (or
-`License.feature_available?` if it is a system-wide feature).
+### Guard your EE feature
-Frontend features should be guarded by pushing a flag from the backend by [using `push_licensed_feature`](licensed_feature_availability.md#restricting-frontend-features), and checked using `this.glFeatures.someFeature` in the frontend. For example:
+A licensed feature can only be available to licensed users. You must add a check or guard
+to determine if users have access to the feature.
-```html
-<script>
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+To guard your licensed feature:
-export default {
- mixins: [glFeatureFlagMixin()],
- components: {
- EEComponent: () => import('ee_component/components/test.vue'),
- },
- computed: {
- shouldRenderComponent() {
- return this.glFeatures.myEEFeature;
- }
- },
-};
-</script>
+1. Locate your feature identifier in `ee/app/models/gitlab_subscriptions/features.rb`.
+1. Use the following methods, where `my_feature_name` is your feature
+ identifier:
-<template>
- <div>
- <ee-component v-if="shouldRenderComponent"/>
- </div>
-</template>
-```
+ - In a project context:
+
+ ```ruby
+ my_project.licensed_feature_available?(:my_feature_name) # true if available for my_project
+ ```
+
+ - In a group or user namespace context:
+
+ ```ruby
+ my_group.licensed_feature_available?(:my_feature_name) # true if available for my_group
+ ```
-Look in `ee/app/models/license.rb` for the names of the licensed features.
+ - For a global (system-wide) feature:
+
+ ```ruby
+ License.feature_available?(:my_feature_name) # true if available in this instance
+ ```
+
+1. Optional. If your global feature is also available to namespaces with a paid plan, combine two
+feature identifiers to allow both admins and group users. For example:
+
+ ```ruby
+ License.feature_available?(:my_feature_name) || group.licensed_feature_available?(:my_feature_name_for_namespace) # Both admins and group members can see this EE feature
+ ```
+
+### Simulate a CE instance when unlicensed
+
+After the implementation of
+[GitLab CE features to work with unlicensed EE instance](https://gitlab.com/gitlab-org/gitlab/-/issues/2500)
+GitLab Enterprise Edition works like GitLab Community Edition
+when no license is active.
CE specs should remain untouched as much as possible and extra specs
should be added for EE. Licensed features can be stubbed using the
@@ -74,7 +94,7 @@ setting the [`FOSS_ONLY` environment variable](https://gitlab.com/gitlab-org/git
to something that evaluates as `true`. The same works for running tests
(for example `FOSS_ONLY=1 yarn jest`).
-### Running feature specs as CE
+#### Run feature specs as CE
When running [feature specs](testing_guide/best_practices.md#system--feature-tests)
as CE, you should ensure that the edition of backend and frontend match.
@@ -98,7 +118,28 @@ To do so:
bin/rspec spec/features/<path_to_your_spec>
```
-## CI pipelines in a FOSS context
+### Simulate a SaaS instance
+
+If you're developing locally and need your instance to act like the SaaS version of the product,
+you can simulate SaaS by exporting an environment variable:
+
+```shell
+export GITLAB_SIMULATE_SAAS=1
+```
+
+There are many ways to pass an environment variable to your local GitLab instance.
+For example, you can create a `env.runit` file in the root of your GDK with the above snippet.
+
+#### Allow use of licensed EE feature
+
+To enable plans per namespace turn on the `Allow use of licensed EE features` option from the settings page.
+This will make licensed EE features available to projects only if the project namespace's plan includes the feature
+or if the project is public. To enable it:
+
+1. If you are developing locally, follow the steps in [Simulate a SaaS instance](#simulate-a-saas-instance) to make the option available.
+1. Visit Admin > Settings > General > "Account and limit" and enable "Allow use of licensed EE features".
+
+### Run CI pipelines in a FOSS context
By default, merge request pipelines for development run in an EE-context only. If you are
developing features that differ between FOSS and EE, you may wish to run pipelines in a
@@ -108,10 +149,7 @@ To run pipelines in both contexts, add the `~"pipeline:run-as-if-foss"` label to
See the [As-if-FOSS jobs](pipelines.md#as-if-foss-jobs) pipelines documentation for more information.
-## Separation of EE code
-
-All EE code should be put inside the `ee/` top-level directory. The
-rest of the code should be as close to the CE files as possible.
+## Separation of EE code in the backend
### EE-only features
@@ -144,7 +182,7 @@ To test an EE class that doesn't exist in CE, create the spec file as you normal
would in the `ee/spec` directory, but without the second `ee/` subdirectory.
For example, a class `ee/app/models/vulnerability.rb` would have its tests in `ee/spec/models/vulnerability_spec.rb`.
-### EE features based on CE features
+### Extend CE features with EE backend code
For features that build on existing CE features, write a module in the `EE`
namespace and inject it in the CE class, on the last line of the file that the
@@ -243,8 +281,8 @@ There are a few gotchas with it:
overriding the method, because we can't know when the overridden method
(that is, calling `super` in the overriding method) would want to stop early.
In this case, we shouldn't just override it, but update the original method
- to make it call the other method we want to extend, like a [template method
- pattern](https://en.wikipedia.org/wiki/Template_method_pattern).
+ to make it call the other method we want to extend, like a
+ [template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern).
For example, given this base:
```ruby
@@ -633,7 +671,7 @@ might need different strategies to extend it. To apply different strategies
easily, we would use `extend ActiveSupport::Concern` in the EE module.
Put the EE module files following
-[EE features based on CE features](#ee-features-based-on-ce-features).
+[Extend CE features with EE backend code](#extend-ce-features-with-ee-backend-code).
#### EE API routes
@@ -1009,9 +1047,9 @@ FactoryBot.define do
end
```
-## JavaScript code in `assets/javascripts/`
+## Separate of EE code in the frontend
-To separate EE-specific JS-files we should also move the files into an `ee` folder.
+To separate EE-specific JS-files, move the files into an `ee` folder.
For example there can be an
`app/assets/javascripts/protected_branches/protected_branches_bundle.js` and an
@@ -1032,40 +1070,123 @@ import bundle from 'ee/protected_branches/protected_branches_bundle.js';
import bundle from 'ee_else_ce/protected_branches/protected_branches_bundle.js';
```
-See the frontend guide [performance section](fe_guide/performance.md) for
-information on managing page-specific JavaScript within EE.
+### Add new EE-only features in the frontend
+
+If the feature being developed is not present in CE, add your entry point in
+`ee/`. For example:
+
+```shell
+# Add HTML element to mount
+ee/app/views/admin/geo/designs/index.html.haml
+
+# Init the application
+ee/app/assets/javascripts/pages/ee_only_feature/index.js
+
+# Mount the feature
+ee/app/assets/javascripts/ee_only_feature/index.js
+```
+
+Feature guarding `licensed_feature_available?` and `License.feature_available?` typical
+occurs in the controller, as described in the [backend guide](#ee-only-features).
+
+#### Test EE-only features
+
+Add your EE tests to `ee/spec/frontend/` following the same directory structure you use for CE.
+
+### Extend CE features with EE frontend code
+
+Use the [`push_licensed_feature`](#guard-your-ee-feature) to guard frontend features that extend
+existing views:
+
+```ruby
+# ee/app/controllers/ee/admin/my_controller.rb
+before_action do
+ push_licensed_feature(:my_feature_name) # for global features
+end
+```
+
+```ruby
+# ee/app/controllers/ee/group/my_controller.rb
+before_action do
+ push_licensed_feature(:my_feature_name, @group) # for group pages
+end
+```
+
+```ruby
+# ee/app/controllers/ee/project/my_controller.rb
+before_action do
+ push_licensed_feature(:my_feature_name, @group) # for group pages
+ push_licensed_feature(:my_feature_name, @project) # for project pages
+end
+```
+
+Verify your feature appears in `gon.licensed_features` in the browser console.
-## Vue code in `assets/javascript`
+#### Extend Vue applications with EE Vue components
-### script tag
+EE licensed features that enhance existing functionality in the UI add new
+elements or interactions to your Vue application as components.
-#### Child Component only used in EE
+To separate template differences, use a child EE component to separate Vue template differences.
+You must import the EE component [asynchronously](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components).
-To separate Vue template differences we should [import the components asynchronously](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components).
+This allows GitLab to load the correct component in EE, while in CE GitLab loads an empty component
+that renders nothing. This code **must** exist in the CE repository, in addition to the EE repository.
-Doing this allows for us to load the correct component in EE while in CE
-we can load a empty component that renders nothing. This code **should**
-exist in the CE repository as well as the EE repository.
+A CE component acts as the entry point to your EE feature. To add a EE component,
+locate it the `ee/` directory and add it with `import('ee_component/...')`:
```html
<script>
+// app/assets/javascripts/feature/components/form.vue
+
export default {
+ mixins: [glFeatureFlagMixin()],
components: {
- EEComponent: () => import('ee_component/components/test.vue'),
+ // Import an EE component from CE
+ MyEeComponent: () => import('ee_component/components/my_ee_component.vue'),
},
};
</script>
<template>
<div>
- <ee-component />
+ <!-- ... -->
+ <my-ee-component/>
+ <!-- ... -->
</div>
</template>
```
-#### For JS code that is EE only, like props, computed properties, methods, etc
+Check `glFeatures` to ensure that the Vue components are guarded. The components render only when
+the license is present.
-- Please do not use mixins unless ABSOLUTELY NECESSARY. Please try to find an alternative pattern.
+```html
+<script>
+// ee/app/assets/javascripts/feature/components/special_component.vue
+
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+
+export default {
+ mixins: [glFeatureFlagMixin()],
+ computed: {
+ shouldRenderComponent() {
+ // Comes from gon.licensed_features as a camel-case version of `my_feature_name`
+ return this.glFeatures.myFeatureName;
+ }
+ },
+};
+</script>
+
+<template>
+ <div v-if="shouldRenderComponent">
+ <!-- EE licensed feature UI -->
+ </div>
+</template>
+```
+
+NOTE:
+Do not use mixins unless ABSOLUTELY NECESSARY. Try to find an alternative pattern.
##### Recommended alternative approach (named/scoped slots)
@@ -1138,11 +1259,65 @@ export default {
**For EE components that need different results for the same computed values, we can pass in props to the CE wrapper as seen in the example.**
- **EE Child components**
- - Since we are using the asynchronous loading to check which component to load, we'd still use the component's name, check [this example](#child-component-only-used-in-ee).
+ - Since we are using the asynchronous loading to check which component to load, we'd still use the component's name, check [this example](#extend-vue-applications-with-ee-vue-components).
- **EE extra HTML**
- For the templates that have extra HTML in EE we should move it into a new component and use the `ee_else_ce` dynamic import
+#### Extend other JS code
+
+To extend JS files, complete the following steps:
+
+1. Use the `ee_else_ce` helper, where that EE only code must be inside the `ee/` folder.
+ 1. Create an EE file with only the EE, and extend the CE counterpart.
+ 1. For code inside functions that can't be extended, move the code to a new file and use `ee_else_ce` helper:
+
+```javascript
+ import eeCode from 'ee_else_ce/ee_code';
+
+ function test() {
+ const test = 'a';
+
+ eeCode();
+
+ return test;
+ }
+```
+
+In some cases, you'll need to extend other logic in your application. To extend your JS
+modules, create an EE version of the file and extend it with your custom logic:
+
+```javascript
+// app/assets/javascripts/feature/utils.js
+
+export const myFunction = () => {
+ // ...
+};
+
+// ... other CE functions ...
+```
+
+```javascript
+// ee/app/assets/javascripts/feature/utils.js
+import {
+ myFunction as ceMyFunction,
+} from '~/feature/utils';
+
+/* eslint-disable import/export */
+
+// Export same utils as CE
+export * from '~/feature/utils';
+
+// Only override `myFunction`
+export const myFunction = () => {
+ const result = ceMyFunction();
+ // add EE feature logic
+ return result;
+};
+
+/* eslint-enable import/export */
+```
+
#### Testing modules using EE/CE aliases
When writing Frontend tests, if the module under test imports other modules with `ee_else_ce/...` and these modules are also needed by the relevant test, then the relevant test **must** import these modules with `ee_else_ce/...`. This avoids unexpected EE or FOSS failures, and helps ensure the EE behaves like CE when it is unlicensed.
@@ -1185,29 +1360,7 @@ describe('ComponentUnderTest', () => {
```
-### Non Vue Files
-
-For regular JS files, the approach is similar.
-
-1. We keep using the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) helper, this means that EE only code should be inside the `ee/` folder.
- 1. An EE file should be created with the EE only code, and it should extend the CE counterpart.
- 1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper:
-
-#### Example
-
-```javascript
- import eeCode from 'ee_else_ce/ee_code';
-
- function test() {
- const test = 'a';
-
- eeCode();
-
- return test;
- }
-```
-
-## SCSS code in `assets/stylesheets`
+#### SCSS code in `assets/stylesheets`
If a component you're adding styles for is limited to EE, it is better to have a
separate SCSS file in an appropriate directory within `app/assets/stylesheets`.
@@ -1218,9 +1371,8 @@ styles are usually kept in a stylesheet that is common for both CE and EE, and i
to isolate such ruleset from rest of CE rules (along with adding comment describing the same)
to avoid conflicts during CE to EE merge.
-### Bad
-
```scss
+// Bad
.section-body {
.section-title {
background: $gl-header-color;
@@ -1234,9 +1386,8 @@ to avoid conflicts during CE to EE merge.
}
```
-### Good
-
```scss
+// Good
.section-body {
.section-title {
background: $gl-header-color;
@@ -1252,7 +1403,7 @@ to avoid conflicts during CE to EE merge.
// EE-specific end
```
-## GitLab-svgs
+### GitLab-svgs
Conflicts in `app/assets/images/icons.json` or `app/assets/images/icons.svg` can
be resolved simply by regenerating those assets with