summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-11 21:09:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-11 21:09:09 +0000
commit19d63dbca44d8380b8bc0d51d890137691ddf2a7 (patch)
tree534885124a1edfcf4c22f61b93f6f8d270e03ac3
parent9d195600e6266da44917f08c622a21a6d4c2317b (diff)
downloadgitlab-ce-19d63dbca44d8380b8bc0d51d890137691ddf2a7.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Experiment Successful Cleanup.md4
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--PROCESS.md4
-rw-r--r--app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js22
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue4
-rw-r--r--app/assets/javascripts/issuable/components/csv_import_export_buttons.vue1
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue8
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/index.js2
-rw-r--r--app/experiments/application_experiment.rb14
-rw-r--r--app/graphql/mutations/boards/create.rb2
-rw-r--r--app/models/packages/package_file.rb4
-rw-r--r--app/models/wiki.rb2
-rw-r--r--app/views/clusters/clusters/_advanced_settings.html.haml2
-rw-r--r--app/views/clusters/clusters/_cluster_list.html.haml4
-rw-r--r--app/views/clusters/clusters/_empty_state.html.haml2
-rw-r--r--app/views/clusters/clusters/_provider_details_form.html.haml2
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml2
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml2
-rw-r--r--changelogs/unreleased/298827-graphql-getting-mr-diff-discussions-often-returns-500.yml5
-rw-r--r--changelogs/unreleased/board_args_consolidation.yml5
-rw-r--r--changelogs/unreleased/btn-confirm-clusters.yml5
-rw-r--r--changelogs/unreleased/btn-confirm-envs.yml5
-rw-r--r--config/initializers/rack_multipart_patch.rb6
-rw-r--r--danger/feature_flag/Dangerfile2
-rw-r--r--db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb8
-rw-r--r--db/post_migrate/20210105052229_clean_up_asset_proxy_whitelist_rename_on_application_settings.rb10
-rw-r--r--doc/administration/auth/ldap/index.md4
-rw-r--r--doc/administration/feature_flags.md2
-rw-r--r--doc/administration/geo/replication/datatypes.md4
-rw-r--r--doc/development/api_graphql_styleguide.md2
-rw-r--r--doc/development/changelog.md2
-rw-r--r--doc/development/chatops_on_gitlabcom.md2
-rw-r--r--doc/development/documentation/feature_flags.md2
-rw-r--r--doc/development/documentation/styleguide/index.md11
-rw-r--r--doc/development/experiment_guide/index.md2
-rw-r--r--doc/development/fe_guide/graphql.md2
-rw-r--r--doc/development/feature_flags/controls.md6
-rw-r--r--doc/development/feature_flags/development.md605
-rw-r--r--doc/development/feature_flags/index.md657
-rw-r--r--doc/development/feature_flags/process.md177
-rw-r--r--doc/development/merge_request_performance_guidelines.md2
-rw-r--r--doc/development/new_fe_guide/tips.md8
-rw-r--r--doc/development/packages.md2
-rw-r--r--doc/development/service_measurement.md2
-rw-r--r--doc/development/testing_guide/best_practices.md2
-rw-r--r--doc/development/testing_guide/end_to_end/feature_flags.md4
-rw-r--r--doc/development/usage_ping.md3
-rw-r--r--doc/development/usage_ping/index.md2
-rw-r--r--doc/user/analytics/index.md2
-rw-r--r--doc/user/group/index.md2
-rw-r--r--doc/user/group/saml_sso/index.md1
-rw-r--r--doc/user/img/gitlab_snippet_v13_0.pngbin28925 -> 0 bytes
-rw-r--r--doc/user/img/gitlab_snippet_v13_5.pngbin20563 -> 0 bytes
-rw-r--r--doc/user/img/new_personal_snippet_from_project_v12_10.pngbin77106 -> 0 bytes
-rw-r--r--doc/user/img/new_personal_snippet_v12_10.pngbin39878 -> 0 bytes
-rw-r--r--doc/user/packages/generic_packages/index.md18
-rw-r--r--doc/user/profile/index.md2
-rw-r--r--doc/user/project/deploy_tokens/index.md16
-rw-r--r--doc/user/project/issues/index.md8
-rw-r--r--doc/user/project/settings/index.md5
-rw-r--r--doc/user/snippets.md274
-rw-r--r--lib/api/rubygem_packages.rb11
-rw-r--r--lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml3
-rw-r--r--lib/gitlab/database/migrations/background_migration_helpers.rb87
-rw-r--r--qa/qa/page/project/issue/index.rb15
-rw-r--r--spec/experiments/application_experiment_spec.rb33
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js59
-rw-r--r--spec/frontend/notes/components/noteable_note_spec.js214
-rw-r--r--spec/initializers/rack_multipart_patch_spec.rb44
-rw-r--r--spec/lib/banzai/filter/custom_emoji_filter_spec.rb35
-rw-r--r--spec/lib/banzai/filter/emoji_filter_spec.rb33
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb116
-rw-r--r--spec/models/packages/package_file_spec.rb15
-rw-r--r--spec/requests/api/rubygem_packages_spec.rb65
-rw-r--r--spec/support/shared_examples/banzai/filters/emoji_shared_examples.rb46
-rw-r--r--spec/support/shared_examples/banzai/filters/ignored_tags_emoji_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb17
79 files changed, 1478 insertions, 1308 deletions
diff --git a/.gitlab/issue_templates/Experiment Successful Cleanup.md b/.gitlab/issue_templates/Experiment Successful Cleanup.md
index b84bf1066f6..afe4793cdfc 100644
--- a/.gitlab/issue_templates/Experiment Successful Cleanup.md
+++ b/.gitlab/issue_templates/Experiment Successful Cleanup.md
@@ -9,10 +9,10 @@ The changes need to become an official part of the product.
- [ ] Determine whether the feature should apply to SaaS and/or self-managed
- [ ] Determine whether the feature should apply to EE - and which tiers - and/or Core
-- [ ] Determine if tracking should be kept as is, removed, or modified
+- [ ] Determine if tracking should be kept as is, removed, or modified.
- [ ] Ensure any relevant documentation has been updated.
- [ ] Consider changes to any `feature_category:` introduced by the experiment if ownership is changing (PM for Growth and PM for the new category as DRIs)
-- [ ] Optional: Migrate experiment to a default enabled [feature flag](https://docs.gitlab.com/ee/development/feature_flags/development.html) for one milestone and add a changelog. Converting to a feature flag can be skipped at the ICs discretion if risk is deemed low with consideration to both SaaS and (if applicable) self managed
+- [ ] Optional: Migrate experiment to a default enabled [feature flag](https://docs.gitlab.com/ee/development/feature_flags) for one milestone and add a changelog. Converting to a feature flag can be skipped at the ICs discretion if risk is deemed low with consideration to both SaaS and (if applicable) self managed
- [ ] In the next milestone, [remove the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) if applicable
- [ ] After the flag removal is deployed, [clean up the feature/experiment feature flags](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel
diff --git a/Gemfile b/Gemfile
index 916ec8a53d4..2215f88f34e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -485,7 +485,7 @@ gem 'flipper', '~> 0.17.1'
gem 'flipper-active_record', '~> 0.17.1'
gem 'flipper-active_support_cache_store', '~> 0.17.1'
gem 'unleash', '~> 0.1.5'
-gem 'gitlab-experiment', '~> 0.4.12'
+gem 'gitlab-experiment', '~> 0.5.0'
# Structured logging
gem 'lograge', '~> 0.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index ace2442e026..e637ef37e8e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -430,7 +430,7 @@ GEM
github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
- gitlab-experiment (0.4.12)
+ gitlab-experiment (0.5.0)
activesupport (>= 3.0)
scientist (~> 1.5, >= 1.5.0)
gitlab-fog-azure-rm (1.0.1)
@@ -1140,7 +1140,7 @@ GEM
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
- scientist (1.5.0)
+ scientist (1.6.0)
securecompare (1.0.0)
seed-fu (2.3.7)
activerecord (>= 3.1)
@@ -1413,7 +1413,7 @@ DEPENDENCIES
gitaly (~> 13.9.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
- gitlab-experiment (~> 0.4.12)
+ gitlab-experiment (~> 0.5.0)
gitlab-fog-azure-rm (~> 1.0.1)
gitlab-fog-google (~> 1.13)
gitlab-labkit (~> 0.16.0)
diff --git a/PROCESS.md b/PROCESS.md
index 820f19a290b..67abe2f0a98 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -77,9 +77,9 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature flags
-Overview and details of feature flag processes in development of GitLab itself is described in [feature flags process documentation](https://docs.gitlab.com/ee/development/feature_flags/process.html).
+Overview and details of feature flag processes in development of GitLab itself is described in [feature flags process documentation](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/).
-Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/development.html).
+Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags).
Getting access and how to expose the feature to users is detailed in [controlling feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/controls.html).
diff --git a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
index 36fef06eeff..88be64d0a1a 100644
--- a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
+++ b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
@@ -1,3 +1,4 @@
+import { isEmpty } from 'lodash';
import { deprecatedCreateFlash as flash } from '~/flash';
import { scrollToElement } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
@@ -88,18 +89,23 @@ export const updateDiscussionsAfterPublish = async ({ dispatch, getters, rootGet
export const updateDraft = (
{ commit, getters },
{ note, noteText, resolveDiscussion, position, callback },
-) =>
- service
- .update(getters.getNotesData.draftsPath, {
- draftId: note.id,
- note: noteText,
- resolveDiscussion,
- position: JSON.stringify(position),
- })
+) => {
+ const params = {
+ draftId: note.id,
+ note: noteText,
+ resolveDiscussion,
+ };
+ // Stringifying an empty object yields `{}` which breaks graphql queries
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/298827
+ if (!isEmpty(position)) params.position = JSON.stringify(position);
+
+ return service
+ .update(getters.getNotesData.draftsPath, params)
.then((res) => res.data)
.then((data) => commit(types.RECEIVE_DRAFT_UPDATE_SUCCESS, data))
.then(callback)
.catch(() => flash(__('An error occurred while updating the comment')));
+};
export const scrollToDraft = ({ dispatch, rootGetters }, draft) => {
const discussion = draft.discussion_id && rootGetters.getDiscussion(draft.discussion_id);
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index 3eabc10cd22..602639f09a6 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -129,7 +129,7 @@ export default {
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
- variant="success"
+ variant="confirm"
>{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
</div>
@@ -164,7 +164,7 @@ export default {
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
- variant="success"
+ variant="confirm"
>{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
</div>
diff --git a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
index 8f18902b590..d75fa1e8323 100644
--- a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
+++ b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue
@@ -66,7 +66,6 @@ export default {
<gl-dropdown
v-if="showImportButton"
v-gl-tooltip.hover="__('Import issues')"
- data-qa-selector="import_issues_dropdown"
data-testid="import-csv-dropdown"
icon="import"
>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 9bf1496f479..185f4a70367 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -1,7 +1,7 @@
<script>
import { GlSprintf, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import $ from 'jquery';
-import { escape } from 'lodash';
+import { escape, isEmpty } from 'lodash';
import { mapGetters, mapActions } from 'vuex';
import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants';
import httpStatusCodes from '~/lib/utils/http_status';
@@ -282,9 +282,13 @@ export default {
note: {
target_type: this.getNoteableData.targetType,
target_id: this.note.noteable_id,
- note: { note: noteText, position: JSON.stringify(position) },
+ note: { note: noteText },
},
};
+
+ // Stringifying an empty object yields `{}` which breaks graphql queries
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/298827
+ if (!isEmpty(position)) data.note.note.position = JSON.stringify(position);
this.isRequesting = true;
this.oldContent = this.note.note_html;
// eslint-disable-next-line vue/no-mutating-props
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/index.js b/app/assets/javascripts/pages/dashboard/todos/index/index.js
index 9d2c2f2994f..2fe90c24e77 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/index.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/index.js
@@ -1,3 +1,3 @@
import Todos from './todos';
-document.addEventListener('DOMContentLoaded', () => new Todos());
+new Todos(); // eslint-disable-line no-new
diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb
index 6ba851fbc8b..019e74bcd48 100644
--- a/app/experiments/application_experiment.rb
+++ b/app/experiments/application_experiment.rb
@@ -9,11 +9,15 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
Feature.get(feature_flag_name).state != :off # rubocop:disable Gitlab/AvoidFeatureGet
end
- def publish(_result)
+ def publish(_result = nil)
track(:assignment) # track that we've assigned a variant for this context
# push the experiment data to the client
- Gon.push({ experiment: { name => signature } }, true) if in_request_cycle?
+ begin
+ Gon.push({ experiment: { name => signature } }, true) # push the experiment data to the client
+ rescue NoMethodError
+ # means we're not in the request cycle, and can't add to Gon. Log a warning maybe?
+ end
end
def track(action, **event_args)
@@ -49,12 +53,6 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
name.tr('/', '_')
end
- def in_request_cycle?
- # Gon is only accessible when having a request. This will be fixed with
- # https://gitlab.com/gitlab-org/gitlab/-/issues/323352
- context.instance_variable_defined?(:@request)
- end
-
def resolve_variant_name
case rollout_strategy
when :round_robin
diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb
index fe324f848e7..003c4f7761b 100644
--- a/app/graphql/mutations/boards/create.rb
+++ b/app/graphql/mutations/boards/create.rb
@@ -22,7 +22,7 @@ module Mutations
response = ::Boards::CreateService.new(board_parent, current_user, args).execute
{
- board: response.payload,
+ board: response.success? ? response.payload : nil,
errors: response.errors
}
end
diff --git a/app/models/packages/package_file.rb b/app/models/packages/package_file.rb
index 9059f61b5de..23a7144e2bb 100644
--- a/app/models/packages/package_file.rb
+++ b/app/models/packages/package_file.rb
@@ -30,6 +30,10 @@ class Packages::PackageFile < ApplicationRecord
scope :preload_conan_file_metadata, -> { preload(:conan_file_metadatum) }
scope :preload_debian_file_metadata, -> { preload(:debian_file_metadatum) }
+ scope :for_rubygem_with_file_name, ->(project, file_name) do
+ joins(:package).merge(project.packages.rubygems).with_file_name(file_name)
+ end
+
scope :with_conan_file_type, ->(file_type) do
joins(:conan_file_metadatum)
.where(packages_conan_file_metadata: { conan_file_type: ::Packages::Conan::FileMetadatum.conan_file_types[file_type] })
diff --git a/app/models/wiki.rb b/app/models/wiki.rb
index 45747c0b03c..abaa4e05d74 100644
--- a/app/models/wiki.rb
+++ b/app/models/wiki.rb
@@ -159,7 +159,7 @@ class Wiki
find_page(SIDEBAR, version)
end
- def find_file(name, version = nil)
+ def find_file(name, version = 'HEAD')
wiki.file(name, version)
end
diff --git a/app/views/clusters/clusters/_advanced_settings.html.haml b/app/views/clusters/clusters/_advanced_settings.html.haml
index bbdbda40297..7f508fd0a59 100644
--- a/app/views/clusters/clusters/_advanced_settings.html.haml
+++ b/app/views/clusters/clusters/_advanced_settings.html.haml
@@ -24,7 +24,7 @@
.text-muted
= html_escape(s_('ClusterIntegration|A cluster management project can be used to run deployment jobs with Kubernetes %{code_open}cluster-admin%{code_close} privileges.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('More information'), help_page_path('user/clusters/management_project.md'), target: '_blank'
- = field.submit _('Save changes'), class: 'btn gl-button btn-success'
+ = field.submit _('Save changes'), class: 'btn gl-button btn-confirm'
.sub-section.form-group
%h4
diff --git a/app/views/clusters/clusters/_cluster_list.html.haml b/app/views/clusters/clusters/_cluster_list.html.haml
index 9627d940126..38ed7e334c9 100644
--- a/app/views/clusters/clusters/_cluster_list.html.haml
+++ b/app/views/clusters/clusters/_cluster_list.html.haml
@@ -4,9 +4,9 @@
.top-area.adjust
.gl-display-block.gl-text-right.gl-my-4.gl-w-full
- if clusterable.can_add_cluster?
- = link_to s_('ClusterIntegration|Connect cluster with certificate'), clusterable.new_path, class: 'btn gl-button btn-success js-add-cluster gl-py-2', qa_selector: :integrate_kubernetes_cluster_button
+ = link_to s_('ClusterIntegration|Connect cluster with certificate'), clusterable.new_path, class: 'btn gl-button btn-confirm js-add-cluster gl-py-2', qa_selector: :integrate_kubernetes_cluster_button
- else
- %span.btn.gl-button.btn-success.js-add-cluster.disabled.gl-py-2
+ %span.btn.gl-button.btn-confirm.js-add-cluster.disabled.gl-py-2
= s_("ClusterIntegration|Connect cluster with certificate")
#js-clusters-list-app{ data: js_clusters_list_data(clusterable.index_path(format: :json)) }
diff --git a/app/views/clusters/clusters/_empty_state.html.haml b/app/views/clusters/clusters/_empty_state.html.haml
index 257944f5e65..feef3e0027f 100644
--- a/app/views/clusters/clusters/_empty_state.html.haml
+++ b/app/views/clusters/clusters/_empty_state.html.haml
@@ -11,4 +11,4 @@
- if clusterable.can_add_cluster?
.gl-text-center
- = link_to s_('ClusterIntegration|Integrate with a cluster certificate'), clusterable.new_path, class: 'gl-button btn btn-success', data: { qa_selector: 'add_kubernetes_cluster_link' }
+ = link_to s_('ClusterIntegration|Integrate with a cluster certificate'), clusterable.new_path, class: 'gl-button btn btn-confirm', data: { qa_selector: 'add_kubernetes_cluster_link' }
diff --git a/app/views/clusters/clusters/_provider_details_form.html.haml b/app/views/clusters/clusters/_provider_details_form.html.haml
index 7fc803e9579..a936cdc04dd 100644
--- a/app/views/clusters/clusters/_provider_details_form.html.haml
+++ b/app/views/clusters/clusters/_provider_details_form.html.haml
@@ -55,4 +55,4 @@
= render('clusters/clusters/namespace', platform_field: platform_field, field: field)
.form-group
- = field.submit s_('ClusterIntegration|Save changes'), class: 'gl-button btn btn-success'
+ = field.submit s_('ClusterIntegration|Save changes'), class: 'gl-button btn btn-confirm'
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index 50e3d29f974..ee2817879be 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -85,4 +85,4 @@
.form-group.js-gke-cluster-creation-submit-container
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'),
- class: 'js-gke-cluster-creation-submit gl-button btn btn-success', disabled: true
+ class: 'js-gke-cluster-creation-submit gl-button btn btn-confirm', disabled: true
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index e2779d1b683..7d82fe06799 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -60,4 +60,4 @@
= render('clusters/clusters/namespace', platform_field: platform_kubernetes_field)
.form-group
- = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'gl-button btn btn-success', data: { qa_selector: 'add_kubernetes_cluster_button' }
+ = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'add_kubernetes_cluster_button' }
diff --git a/changelogs/unreleased/298827-graphql-getting-mr-diff-discussions-often-returns-500.yml b/changelogs/unreleased/298827-graphql-getting-mr-diff-discussions-often-returns-500.yml
new file mode 100644
index 00000000000..c994a7d11aa
--- /dev/null
+++ b/changelogs/unreleased/298827-graphql-getting-mr-diff-discussions-often-returns-500.yml
@@ -0,0 +1,5 @@
+---
+title: fix stringify empty position object
+merge_request: 56037
+author:
+type: fixed
diff --git a/changelogs/unreleased/board_args_consolidation.yml b/changelogs/unreleased/board_args_consolidation.yml
new file mode 100644
index 00000000000..e62b49fe06a
--- /dev/null
+++ b/changelogs/unreleased/board_args_consolidation.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed error handling GraphQL API when issue board creation fails
+merge_request: 55982
+author:
+type: fixed
diff --git a/changelogs/unreleased/btn-confirm-clusters.yml b/changelogs/unreleased/btn-confirm-clusters.yml
new file mode 100644
index 00000000000..fe1b0d732dc
--- /dev/null
+++ b/changelogs/unreleased/btn-confirm-clusters.yml
@@ -0,0 +1,5 @@
+---
+title: Move from btn-success to btn-confirm in clusters directory
+merge_request: 56205
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/btn-confirm-envs.yml b/changelogs/unreleased/btn-confirm-envs.yml
new file mode 100644
index 00000000000..00331aa31f6
--- /dev/null
+++ b/changelogs/unreleased/btn-confirm-envs.yml
@@ -0,0 +1,5 @@
+---
+title: Move to confirm variant from success in environments directory
+merge_request: 56203
+author: Yogi (@yo)
+type: changed
diff --git a/config/initializers/rack_multipart_patch.rb b/config/initializers/rack_multipart_patch.rb
index e664827e15e..78cf6244814 100644
--- a/config/initializers/rack_multipart_patch.rb
+++ b/config/initializers/rack_multipart_patch.rb
@@ -13,7 +13,7 @@ module Rack
def log_multipart_warning(req)
content_length = req.content_length.to_i
- return unless content_length > 500_000_000
+ return unless content_length > log_threshold
message = {
message: "Large multipart body detected",
@@ -32,6 +32,10 @@ module Rack
def log_large_multipart?
Gitlab::Utils.to_boolean(ENV['ENABLE_RACK_MULTIPART_LOGGING'], default: true) && Gitlab.com?
end
+
+ def log_threshold
+ ENV.fetch('RACK_MULTIPART_LOGGING_BYTES', 100_000_000).to_i
+ end
end
prepend MultipartPatch
diff --git a/danger/feature_flag/Dangerfile b/danger/feature_flag/Dangerfile
index c90f60640f2..e66ed35c9ab 100644
--- a/danger/feature_flag/Dangerfile
+++ b/danger/feature_flag/Dangerfile
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop:disable Style/SignalException
-SEE_DOC = "See the [feature flag documentation](https://docs.gitlab.com/ee/development/feature_flags/development.html#feature-flag-definition-and-validation)."
+SEE_DOC = "See the [feature flag documentation](https://docs.gitlab.com/ee/development/feature_flags#feature-flag-definition-and-validation)."
SUGGEST_MR_COMMENT = <<~SUGGEST_COMMENT
```suggestion
diff --git a/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb b/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb
index 8a9acd8b86e..8ac334fc6a4 100644
--- a/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb
+++ b/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb
@@ -8,6 +8,10 @@ class RenameAssetProxyAllowlistOnApplicationSettings < ActiveRecord::Migration[6
disable_ddl_transaction!
def up
+ cleanup_concurrent_column_rename :application_settings,
+ :asset_proxy_whitelist,
+ :asset_proxy_allowlist
+
rename_column_concurrently :application_settings,
:asset_proxy_allowlist,
:asset_proxy_whitelist
@@ -17,5 +21,9 @@ class RenameAssetProxyAllowlistOnApplicationSettings < ActiveRecord::Migration[6
undo_rename_column_concurrently :application_settings,
:asset_proxy_allowlist,
:asset_proxy_whitelist
+
+ undo_cleanup_concurrent_column_rename :application_settings,
+ :asset_proxy_whitelist,
+ :asset_proxy_allowlist
end
end
diff --git a/db/post_migrate/20210105052229_clean_up_asset_proxy_whitelist_rename_on_application_settings.rb b/db/post_migrate/20210105052229_clean_up_asset_proxy_whitelist_rename_on_application_settings.rb
index 8f6d312ae5e..87f391e240d 100644
--- a/db/post_migrate/20210105052229_clean_up_asset_proxy_whitelist_rename_on_application_settings.rb
+++ b/db/post_migrate/20210105052229_clean_up_asset_proxy_whitelist_rename_on_application_settings.rb
@@ -8,14 +8,12 @@ class CleanUpAssetProxyWhitelistRenameOnApplicationSettings < ActiveRecord::Migr
disable_ddl_transaction!
def up
- cleanup_concurrent_column_rename :application_settings,
- :asset_proxy_whitelist,
- :asset_proxy_allowlist
+ # This migration has been made a no-op in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56352
+ # because to revert the rename in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55419 we need
+ # to cleanup the triggers on the `asset_proxy_allowlist` column. As such, this migration would do nothing.
end
def down
- undo_cleanup_concurrent_column_rename :application_settings,
- :asset_proxy_whitelist,
- :asset_proxy_allowlist
+ # no-op
end
end
diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md
index 1ce9dd79caf..0e55efba8ae 100644
--- a/doc/administration/auth/ldap/index.md
+++ b/doc/administration/auth/ldap/index.md
@@ -476,8 +476,8 @@ be mandatory and clients cannot be authenticated with the TLS protocol.
## Multiple LDAP servers **(PREMIUM SELF)**
-With GitLab Enterprise Edition Starter, you can configure multiple LDAP servers
-that your GitLab instance connects to.
+With GitLab, you can configure multiple LDAP servers that your GitLab instance
+connects to.
To add another LDAP server:
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index 55727822654..6882797064b 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -13,7 +13,7 @@ to deploy features in an early stage of development so that they can be
incrementally rolled out.
Before making them permanently available, features can be deployed behind
-flags for a [number of reasons](../development/feature_flags/index.md#when-to-use-feature-flags), such as:
+flags for a [number of reasons](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle#when-to-use-feature-flags), such as:
- To test the feature.
- To get feedback from users and customers while in an early stage of the development of the feature.
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index 37633ee0f18..293417c9c64 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -178,8 +178,8 @@ successfully, you must replicate their data using some other means.
| [Group wiki repository](../../../user/group/index.md#group-wikis) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | |
| [Uploads](../../uploads.md) | **Yes** (10.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | No | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them. |
| [LFS objects](../../lfs/index.md) | **Yes** (10.2) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8922) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them. GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696). |
-| [Personal snippets](../../../user/snippets.md#personal-snippets) | **Yes** (10.2) | **Yes** (10.2) | No | |
-| [Project snippets](../../../user/snippets.md#project-snippets) | **Yes** (10.2) | **Yes** (10.2) | No | |
+| [Personal snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
+| [Project snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
| [CI job artifacts (other than Job Logs)](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta) . | Verified only manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them |
| [Job logs](../../job_logs.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them |
| [Object pools for forked project deduplication](../../../development/git_object_deduplication.md) | **Yes** | No | No | |
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index f7f69ac9748..8bac02c99af 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -470,7 +470,7 @@ fails. Consider this when toggling the visibility of the feature on or off on
production.
The `feature_flag` property does not allow the use of
-[feature gates based on actors](../development/feature_flags/development.md).
+[feature gates based on actors](../development/feature_flags/index.md).
This means that the feature flag cannot be toggled only for particular
projects, groups, or users, but instead can only be toggled globally for
everyone.
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index fa771f96259..98a3e75bb3c 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -57,7 +57,7 @@ the `author` field. GitLab team members **should not**.
- Any change behind an enabled feature flag **should** have a changelog entry.
- Any change that adds new usage data metrics and changes that needs to be documented in Product Intelligence [Event Dictionary](https://about.gitlab.com/handbook/product/product-intelligence-guide/#event-dictionary) **should** have a changelog entry.
- A change that adds snowplow events **should** have a changelog entry -
-- A change that [removes a feature flag](feature_flags/development.md) **must** have a changelog entry.
+- A change that [removes a feature flag](feature_flags/index.md) **must** have a changelog entry.
- A fix for a regression introduced and then fixed in the same release (i.e.,
fixing a bug introduced during a monthly release candidate) **should not**
have a changelog entry.
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
index 0341abf5eeb..4ae49103d1b 100644
--- a/doc/development/chatops_on_gitlabcom.md
+++ b/doc/development/chatops_on_gitlabcom.md
@@ -61,4 +61,4 @@ To request access to ChatOps on GitLab.com:
- [ChatOps Usage](../ci/chatops/index.md)
- [Understanding EXPLAIN plans](understanding_explain_plans.md)
-- [Feature Groups](feature_flags/development.md#feature-groups)
+- [Feature Groups](feature_flags/index.md#feature-groups)
diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md
index d318390b210..8fe3f0cbf8e 100644
--- a/doc/development/documentation/feature_flags.md
+++ b/doc/development/documentation/feature_flags.md
@@ -20,7 +20,7 @@ must be documented. For context, see the
## Criteria
-According to the process of [deploying GitLab features behind feature flags](../feature_flags/process.md):
+According to the process of [deploying GitLab features behind feature flags](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle):
> - _By default, feature flags should be off._
> - _Feature flags should remain in the codebase for a short period as possible to reduce the need for feature flag accounting._
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index db13701d5db..4831e5bbce5 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1611,11 +1611,12 @@ If all content in a section is related, add version text after the header for
the section. The version information must:
- Be surrounded by blank lines.
-- Start with `>`.
-- Version histories with more than one entry should have each entry on its own
- line (long lines are okay). Start each line with `> -` to get unordered list
- formatting.
-- Whenever possible, have a link to the completed issue, merge request, or epic
+- Start with `>`. If there are multiple bullets, each line must start with `> -`.
+- The string must include these words in this order (capitalization doesn't matter):
+ - `introduced`, `deprecated`, `moved`
+ - `in` or `to`
+ - `GitLab`
+- Whenever possible, include a link to the completed issue, merge request, or epic
that introduced the feature. An issue is preferred over a merge request, and
a merge request is preferred over an epic.
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index 652b4f8f80b..15430831f4a 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -45,7 +45,7 @@ One is built into GitLab directly and has been around for a while (this is calle
[`gitlab-experiment`](https://gitlab.com/gitlab-org/gitlab-experiment) and is referred
to as `Gitlab::Experiment` -- GLEX for short.
-Both approaches use [experiment](../feature_flags/development.md#experiment-type)
+Both approaches use [experiment](../feature_flags/index.md#experiment-type)
feature flags, and there is currently no strong suggestion to use one over the other.
| Feature | `Experimentation Module` | GLEX |
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index ec784eb3bd8..2e812d9fa0a 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -423,7 +423,7 @@ query getAuthorData($authorNameEnabled: Boolean = false) {
```
Then in the Vue (or JavaScript) call to the query we can pass in our feature flag. This feature
-flag needs to be already set up correctly. See the [feature flag documentation](../feature_flags/development.md)
+flag needs to be already set up correctly. See the [feature flag documentation](../feature_flags/index.md)
for the correct way to do this.
```javascript
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index df98996158e..fc327a2defc 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -36,7 +36,7 @@ easier to measure the impact of both separately.
The GitLab feature library (using
[Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature
-Flags process](process.md) guide) supports rolling out changes to a percentage of
+Flags process](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle) guide) supports rolling out changes to a percentage of
time to users. This in turn can be controlled using [GitLab ChatOps](../../ci/chatops/index.md).
For an up to date list of feature flag commands please see [the source
@@ -240,7 +240,7 @@ To disable a feature flag that has been enabled for a specific project you can r
/chatops run feature set --group=gitlab-org some_feature false
```
-You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](development.md#selectively-disable-by-actor) the feature flags.
+You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](index.md#selectively-disable-by-actor) the feature flags.
### Feature flag change logging
@@ -281,7 +281,7 @@ To remove a feature flag, open **one merge request** to make the changes. In the
1. Add the ~"feature flag" label so release managers are aware the changes are hidden behind a feature flag.
1. If the merge request has to be picked into a stable branch, add the
appropriate `~"Pick into X.Y"` label, for example `~"Pick into 13.0"`.
- See [the feature flag process](process.md#including-a-feature-behind-feature-flag-in-the-final-release)
+ See [the feature flag process](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle#including-a-feature-behind-feature-flag-in-the-final-release)
for further details.
1. Remove all references to the feature flag from the codebase, including tests.
1. Remove the YAML definition for the feature from the repository.
diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md
index a76c76673bf..79efd6d5502 100644
--- a/doc/development/feature_flags/development.md
+++ b/doc/development/feature_flags/development.md
@@ -1,604 +1,7 @@
---
-type: reference, dev
-stage: none
-group: Development
-info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
+redirect_to: 'index.md'
---
-# Developing with feature flags
-
-This document provides guidelines on how to use feature flags
-in the GitLab codebase to conditionally enable features
-and test them.
-
-Features that are developed and merged behind a feature flag
-should not include a changelog entry. The entry should be added either in the merge
-request removing the feature flag or the merge request where the default value of
-the feature flag is set to enabled. If the feature contains any database migrations, it
-*should* include a changelog entry for the database changes.
-
-WARNING:
-All newly-introduced feature flags should be [disabled by default](process.md#feature-flags-in-gitlab-development).
-
-NOTE:
-This document is the subject of continued work as part of an epic to [improve internal usage of Feature Flags](https://gitlab.com/groups/gitlab-org/-/epics/3551). Raise any suggestions as new issues and attach them to the epic.
-
-## Risk of a broken master (main) branch
-
-Feature flags **must** be used in the MR that introduces them. Not doing so causes a
-[broken master](https://about.gitlab.com/handbook/engineering/workflow/#broken-master) scenario due
-to the `rspec:feature-flags` job that only runs on the `master` branch.
-
-## Types of feature flags
-
-Choose a feature flag type that matches the expected usage.
-
-### `development` type
-
-`development` feature flags are short-lived feature flags,
-used so that unfinished code can be deployed in production.
-
-A `development` feature flag should have a rollout issue,
-ideally created using the [Feature Flag Roll Out template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20Flag%20Roll%20Out.md).
-
-This is the default type used when calling `Feature.enabled?`.
-
-### `ops` type
-
-`ops` feature flags are long-lived feature flags that control operational aspects
-of GitLab product behavior. For example, feature flags that disable features that might
-have a performance impact, like special Sidekiq worker behavior.
-
-`ops` feature flags likely do not have rollout issues, as it is hard to
-predict when they are enabled or disabled.
-
-To use `ops` feature flags, you must append `type: :ops` to `Feature.enabled?`
-invocations:
-
-```ruby
-# Check if feature flag is enabled
-Feature.enabled?(:my_ops_flag, project, type: :ops)
-
-# Check if feature flag is disabled
-Feature.disabled?(:my_ops_flag, project, type: :ops)
-
-# Push feature flag to Frontend
-push_frontend_feature_flag(:my_ops_flag, project, type: :ops)
-```
-
-### `experiment` type
-
-`experiment` feature flags are used for A/B testing on GitLab.com.
-
-An `experiment` feature flag should conform to the same standards as a `development` feature flag,
-although the interface has some differences. An experiment feature flag should have a rollout issue,
-ideally created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/experiment_tracking_template.md). More information can be found in the [experiment guide](../experiment_guide/index.md).
-
-## Feature flag definition and validation
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229161) in GitLab 13.3.
-
-During development (`RAILS_ENV=development`) or testing (`RAILS_ENV=test`) all feature flag usage is being strictly validated.
-
-This process is meant to ensure consistent feature flag usage in the codebase. All feature flags **must**:
-
-- Be known. Only use feature flags that are explicitly defined.
-- Not be defined twice. They have to be defined either in FOSS or EE, but not both.
-- Use a valid and consistent `type:` across all invocations.
-- Use the same `default_enabled:` across all invocations.
-- Have an owner.
-
-All feature flags known to GitLab are self-documented in YAML files stored in:
-
-- [`config/feature_flags`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/config/feature_flags)
-- [`ee/config/feature_flags`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/config/feature_flags)
-
-Each feature flag is defined in a separate YAML file consisting of a number of fields:
-
-| Field | Required | Description |
-|---------------------|----------|----------------------------------------------------------------|
-| `name` | yes | Name of the feature flag. |
-| `type` | yes | Type of feature flag. |
-| `default_enabled` | yes | The default state of the feature flag that is strictly validated, with `default_enabled:` passed as an argument. |
-| `introduced_by_url` | no | The URL to the Merge Request that introduced the feature flag. |
-| `rollout_issue_url` | no | The URL to the Issue covering the feature flag rollout. |
-| `group` | no | The [group](https://about.gitlab.com/handbook/product/categories/#devops-stages) that owns the feature flag. |
-
-NOTE:
-All validations are skipped when running in `RAILS_ENV=production`.
-
-## Create a new feature flag
-
-The GitLab codebase provides [`bin/feature-flag`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/bin/feature-flag),
-a dedicated tool to create new feature flag definitions.
-The tool asks various questions about the new feature flag, then creates
-a YAML definition in `config/feature_flags` or `ee/config/feature_flags`.
-
-Only feature flags that have a YAML definition file can be used when running the development or testing environments.
-
-```shell
-$ bin/feature-flag my_feature_flag
->> Specify the group introducing the feature flag, like `group::apm`:
-?> group::memory
-
->> URL of the MR introducing the feature flag (enter to skip):
-?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
-
->> Open this URL and fill in the rest of the details:
-https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue%5Btitle%5D=%5BFeature+flag%5D+Rollout+of+%60test-flag%60&issuable_template=Feature+Flag+Roll+Out
-
->> URL of the rollout issue (enter to skip):
-?> https://gitlab.com/gitlab-org/gitlab/-/issues/232533
-create config/feature_flags/development/my_feature_flag.yml
----
-name: my_feature_flag
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232533
-group: group::memory
-type: development
-default_enabled: false
-```
-
-NOTE:
-To create a feature flag that is only used in EE, add the `--ee` flag: `bin/feature-flag --ee`
-
-## Delete a feature flag
-
-See [cleaning up feature flags](controls.md#cleaning-up) for more information about
-deleting feature flags.
-
-## Develop with a feature flag
-
-There are two main ways of using Feature Flags in the GitLab codebase:
-
-- [Backend code (Rails)](#backend)
-- [Frontend code (VueJS)](#frontend)
-
-### Backend
-
-The feature flag interface is defined in [`lib/feature.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/feature.rb).
-This interface provides a set of methods to check if the feature flag is enabled or disabled:
-
-```ruby
-if Feature.enabled?(:my_feature_flag, project)
- # execute code if feature flag is enabled
-else
- # execute code if feature flag is disabled
-end
-
-if Feature.disabled?(:my_feature_flag, project)
- # execute code if feature flag is disabled
-end
-```
-
-In rare cases you may want to make a feature enabled by default. If so, explain the reasoning
-in the merge request. Use `default_enabled: true` when checking the feature flag state:
-
-```ruby
-if Feature.enabled?(:feature_flag, project, default_enabled: true)
- # execute code if feature flag is enabled
-else
- # execute code if feature flag is disabled
-end
-
-if Feature.disabled?(:my_feature_flag, project, default_enabled: true)
- # execute code if feature flag is disabled
-end
-```
-
-If not specified, `default_enabled` is `false`.
-
-To force reading the `default_enabled` value from the relative YAML definition file, use
-`default_enabled: :yaml`:
-
-```ruby
-if Feature.enabled?(:feature_flag, project, default_enabled: :yaml)
- # execute code if feature flag is enabled
-end
-```
-
-```ruby
-if Feature.disabled?(:feature_flag, project, default_enabled: :yaml)
- # execute code if feature flag is disabled
-end
-```
-
-This allows to use the same feature flag check across various parts of the codebase and
-maintain the status of `default_enabled` in the YAML definition file which is the SSOT.
-
-If `default_enabled: :yaml` is used, a YAML definition is expected or an error is raised
-in development or test environment, while returning `false` on production.
-
-If not specified, the default feature flag type for `Feature.enabled?` and `Feature.disabled?`
-is `type: development`. For all other feature flag types, you must specify the `type:`:
-
-```ruby
-if Feature.enabled?(:feature_flag, project, type: :ops)
- # execute code if ops feature flag is enabled
-else
- # execute code if ops feature flag is disabled
-end
-
-if Feature.disabled?(:my_feature_flag, project, type: :ops)
- # execute code if feature flag is disabled
-end
-```
-
-WARNING:
-Don't use feature flags at application load time. For example, using the `Feature` class in
-`config/initializers/*` or at the class level could cause an unexpected error. This error occurs
-because a database that a feature flag adapter might depend on doesn't exist at load time
-(especially for fresh installations). Checking for the database's existence at the caller isn't
-recommended, as some adapters don't require a database at all (for example, the HTTP adapter). The
-feature flag setup check must be abstracted in the `Feature` namespace. This approach also requires
-application reload when the feature flag changes. You must therefore ask SREs to reload the
-Web/API/Sidekiq fleet on production, which takes time to fully rollout/rollback the changes. For
-these reasons, use environment variables (for example, `ENV['YOUR_FEATURE_NAME']`) or `gitlab.yml`
-instead.
-
-Here's an example of a pattern that you should avoid:
-
-```ruby
-class MyClass
- if Feature.enabled?(:...)
- new_process
- else
- legacy_process
- end
-end
-```
-
-### Frontend
-
-Use the `push_frontend_feature_flag` method for frontend code, which is
-available to all controllers that inherit from `ApplicationController`. You can use
-this method to expose the state of a feature flag, for example:
-
-```ruby
-before_action do
- # Prefer to scope it per project or user e.g.
- push_frontend_feature_flag(:vim_bindings, project)
-end
-
-def index
- # ...
-end
-
-def edit
- # ...
-end
-```
-
-You can then check the state of the feature flag in JavaScript as follows:
-
-```javascript
-if ( gon.features.vimBindings ) {
- // ...
-}
-```
-
-The name of the feature flag in JavaScript is always camelCase,
-so checking for `gon.features.vim_bindings` would not work.
-
-See the [Vue guide](../fe_guide/vue.md#accessing-feature-flags) for details about
-how to access feature flags in a Vue component.
-
-In rare cases you may want to make a feature enabled by default. If so, explain the reasoning
-in the merge request. Use `default_enabled: true` when checking the feature flag state:
-
-```ruby
-before_action do
- # Prefer to scope it per project or user e.g.
- push_frontend_feature_flag(:vim_bindings, project, default_enabled: true)
-end
-```
-
-If not specified, the default feature flag type for `push_frontend_feature_flag`
-is `type: development`. For all other feature flag types, you must specify the `type:`:
-
-```ruby
-before_action do
- push_frontend_feature_flag(:vim_bindings, project, type: :ops)
-end
-```
-
-### Feature actors
-
-**It is strongly advised to use actors with feature flags.** Actors provide a simple
-way to enable a feature flag only for a given project, group or user. This makes debugging
-easier, as you can filter logs and errors for example, based on actors. This also makes it possible
-to enable the feature on the `gitlab-org` or `gitlab-com` groups first, while the rest of
-the users aren't impacted.
-
-Actors also provide an easy way to do a percentage rollout of a feature in a sticky way.
-If a 1% rollout enabled a feature for a specific actor, that actor will continue to have the feature enabled at
-10%, 50%, and 100%.
-
-GitLab currently supports the following models as feature flag actors:
-
-- `User`
-- `Project`
-- `Group`
-
-The actor is a second parameter of the `Feature.enabled?` call. The
-same actor type must be used consistently for all invocations of `Feature.enabled?`.
-
-```ruby
-Feature.enabled?(:feature_flag, project)
-Feature.enabled?(:feature_flag, group)
-Feature.enabled?(:feature_flag, user)
-```
-
-#### Selectively disable by actor
-
-By default you cannot selectively disable a feature flag by actor.
-
-```shell
-# This will not work how you would expect.
-/chatops run feature set some_feature true
-/chatops run feature set --project=gitlab-org/gitlab some_feature false
-```
-
-However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
-
-```ruby
-Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
-```
-
-```shell
-# This will enable a feature flag globally, except for gitlab-org/gitlab
-/chatops run feature set a_feature true
-/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
-```
-
-### Enable additional objects as actors
-
-To use feature gates based on actors, the model needs to respond to
-`flipper_id`. For example, to enable for the Foo model:
-
-```ruby
-class Foo < ActiveRecord::Base
- include FeatureGate
-end
-```
-
-Only models that `include FeatureGate` or expose `flipper_id` method can be
-used as an actor for `Feature.enabled?`.
-
-### Feature flags for licensed features
-
-You can't use a feature flag with the same name as a licensed feature name, because
-it would cause a naming collision. This was [widely discussed and removed](https://gitlab.com/gitlab-org/gitlab/-/issues/259611)
-because it is confusing.
-
-To check for licensed features, add a dedicated feature flag under a different name
-and check it explicitly, for example:
-
-```ruby
-Feature.enabled?(:licensed_feature_feature_flag, project) &&
- project.feature_available?(:licensed_feature)
-```
-
-### Feature groups
-
-Feature groups must be defined statically in `lib/feature.rb` (in the
-`.register_feature_groups` method), but their implementation can obviously be
-dynamic (querying the DB, for example).
-
-Once defined in `lib/feature.rb`, you can to activate a
-feature for a given feature group via the [`feature_group` parameter of the features API](../../api/features.md#set-or-create-a-feature)
-
-### Enabling a feature flag locally (in development)
-
-In the rails console (`rails c`), enter the following command to enable a feature flag:
-
-```ruby
-Feature.enable(:feature_flag_name)
-```
-
-Similarly, the following command disables a feature flag:
-
-```ruby
-Feature.disable(:feature_flag_name)
-```
-
-You can also enable a feature flag for a given gate:
-
-```ruby
-Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))
-```
-
-### Removing a feature flag locally (in development)
-
-Once you have manually enabled or disabled a feature flag to test in your local environment,
-the flag's default value gets overwritten and it takes precedence over the `default_enabled` value.
-This can cause confusion when changing the flag's `default_enabled` attribute.
-
-For example, flags are commonly enabled and disabled several times during the development process.
-When we finally enable the flag by default, we set `default_enabled: true` in the YAML file.
-
-- If the flag was manually enabled before setting `default_enabled: true`, the feature will be enabled.
-Not because of the `default_enabled: true` value of the flag but because it was manually enabled.
-- If the flag was manually disabled before setting `default_enabled: true`, the feature will
-remain disabled. The `default_enabled: true` value does not take precendence over the explicit `false`
-value set when disabling it manually.
-
-To reset the feature flag to the default status set in its YAML file, remove it using the Rails console
-(`rails c`) as follows:
-
-```ruby
-Feature.remove(:feature_flag_name)
-```
-
-## Feature flags in tests
-
-Introducing a feature flag into the codebase creates an additional code path that should be tested.
-It is strongly advised to test all code affected by a feature flag, both when **enabled** and **disabled**
-to ensure the feature works properly.
-
-When using the testing environment, all feature flags are enabled by default.
-
-WARNING:
-This does not apply to end-to-end (QA) tests, which [do not disable feature flags by default](#end-to-end-qa-tests). There is a different [process for using feature flags in end-to-end tests](../testing_guide/end_to_end/feature_flags.md).
-
-To disable a feature flag in a test, use the `stub_feature_flags`
-helper. For example, to globally disable the `ci_live_trace` feature
-flag in a test:
-
-```ruby
-stub_feature_flags(ci_live_trace: false)
-
-Feature.enabled?(:ci_live_trace) # => false
-```
-
-If you wish to set up a test where a feature flag is enabled only
-for some actors and not others, you can specify this in options
-passed to the helper. For example, to enable the `ci_live_trace`
-feature flag for a specific project:
-
-```ruby
-project1, project2 = build_list(:project, 2)
-
-# Feature will only be enabled for project1
-stub_feature_flags(ci_live_trace: project1)
-
-Feature.enabled?(:ci_live_trace) # => false
-Feature.enabled?(:ci_live_trace, project1) # => true
-Feature.enabled?(:ci_live_trace, project2) # => false
-```
-
-The behavior of FlipperGate is as follows:
-
-1. You can enable an override for a specified actor to be enabled.
-1. You can disable (remove) an override for a specified actor,
- falling back to the default state.
-1. There's no way to model that you explicitly disabled a specified actor.
-
-```ruby
-Feature.enable(:my_feature)
-Feature.disable(:my_feature, project1)
-Feature.enabled?(:my_feature) # => true
-Feature.enabled?(:my_feature, project1) # => true
-
-Feature.disable(:my_feature2)
-Feature.enable(:my_feature2, project1)
-Feature.enabled?(:my_feature2) # => false
-Feature.enabled?(:my_feature2, project1) # => true
-```
-
-### `have_pushed_frontend_feature_flags`
-
-Use `have_pushed_frontend_feature_flags` to test if [`push_frontend_feature_flag`](#frontend)
-has added the feature flag to the HTML.
-
-For example,
-
-```ruby
-stub_feature_flags(value_stream_analytics_path_navigation: false)
-
-visit group_analytics_cycle_analytics_path(group)
-
-expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsPathNavigation: false)
-```
-
-### `stub_feature_flags` vs `Feature.enable*`
-
-It is preferred to use `stub_feature_flags` to enable feature flags
-in the testing environment. This method provides a simple and well described
-interface for simple use cases.
-
-However, in some cases more complex behavior needs to be tested,
-like percentage rollouts of feature flags. This can be done using
-`.enable_percentage_of_time` or `.enable_percentage_of_actors`:
-
-```ruby
-# Good: feature needs to be explicitly disabled, as it is enabled by default if not defined
-stub_feature_flags(my_feature: false)
-stub_feature_flags(my_feature: true)
-stub_feature_flags(my_feature: project)
-stub_feature_flags(my_feature: [project, project2])
-
-# Bad
-Feature.enable(:my_feature_2)
-
-# Good: enable my_feature for 50% of time
-Feature.enable_percentage_of_time(:my_feature_3, 50)
-
-# Good: enable my_feature for 50% of actors/gates/things
-Feature.enable_percentage_of_actors(:my_feature_4, 50)
-```
-
-Each feature flag that has a defined state is persisted
-during test execution time:
-
-```ruby
-Feature.persisted_names.include?('my_feature') => true
-Feature.persisted_names.include?('my_feature_2') => true
-Feature.persisted_names.include?('my_feature_3') => true
-Feature.persisted_names.include?('my_feature_4') => true
-```
-
-### Stubbing actor
-
-When you want to enable a feature flag for a specific actor only,
-you can stub its representation. A gate that is passed
-as an argument to `Feature.enabled?` and `Feature.disabled?` must be an object
-that includes `FeatureGate`.
-
-In specs you can use the `stub_feature_flag_gate` method that allows you to
-quickly create a custom actor:
-
-```ruby
-gate = stub_feature_flag_gate('CustomActor')
-
-stub_feature_flags(ci_live_trace: gate)
-
-Feature.enabled?(:ci_live_trace) # => false
-Feature.enabled?(:ci_live_trace, gate) # => true
-```
-
-You can also disable a feature flag for a specific actor:
-
-```ruby
-gate = stub_feature_flag_gate('CustomActor')
-
-stub_feature_flags(ci_live_trace: false, thing: gate)
-```
-
-### Controlling feature flags engine in tests
-
-Our Flipper engine in the test environment works in a memory mode `Flipper::Adapters::Memory`.
-`production` and `development` modes use `Flipper::Adapters::ActiveRecord`.
-
-You can control whether the `Flipper::Adapters::Memory` or `ActiveRecord` mode is being used.
-
-#### `stub_feature_flags: true` (default and preferred)
-
-In this mode Flipper is configured to use `Flipper::Adapters::Memory` and mark all feature
-flags to be on-by-default and persisted on a first use. This overwrites the `default_enabled:`
-of `Feature.enabled?` and `Feature.disabled?` returning always `true` unless feature flag
-is persisted.
-
-Make sure behavior under feature flag doesn't go untested in some non-specific contexts.
-
-### `stub_feature_flags: false`
-
-This disables a memory-stubbed flipper, and uses `Flipper::Adapters::ActiveRecord`
-a mode that is used by `production` and `development`.
-
-You should use this mode only when you really want to tests aspects of Flipper
-with how it interacts with `ActiveRecord`.
-
-### End-to-end (QA) tests
-
-Toggling feature flags works differently in end-to-end (QA) tests. The end-to-end test framework does not have direct access to
-Rails or the database, so it can't use Flipper. Instead, it uses [the public API](../../api/features.md#set-or-create-a-feature). Each end-to-end test can [enable or disable a feature flag during the test](../testing_guide/end_to_end/feature_flags.md). Alternatively, you can enable or disable a feature flag before one or more tests when you [run them from your GitLab repository's `qa` directory](https://gitlab.com/gitlab-org/gitlab/tree/master/qa#running-tests-with-a-feature-flag-enabled-or-disabled), or if you [run the tests via GitLab QA](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#running-tests-with-a-feature-flag-enabled).
-
-[As noted above, feature flags are not enabled by default in end-to-end tests.](#feature-flags-in-tests)
-This means that end-to-end tests will run with feature flags in the default state implemented in the source
-code, or with the feature flag in its current state on the GitLab instance under test, unless the
-test is written to enable/disable a feature flag explicitly.
-
-When a feature flag is changed on Staging or on GitLab.com, a Slack message will be posted to the `#qa-staging` or `#qa-production` channels to inform
-the pipeline triage DRI so that they can more easily determine if any failures are related to a feature flag change. However, if you are working on a change you can
-help to avoid unexpected failures by [confirming that the end-to-end tests pass with a feature flag enabled.](../testing_guide/end_to_end/feature_flags.md#confirming-that-end-to-end-tests-pass-with-a-feature-flag-enabled)
+This document was moved to [another location](index.md).
+<!-- This redirect file can be deleted after 2021-06-01. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> \ No newline at end of file
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index 4890cc5da35..5c98dc2e473 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -1,75 +1,636 @@
---
+type: reference, dev
stage: none
group: Development
info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
---
-# Feature flags in development of GitLab
+# Developing with feature flags
**NOTE**:
The documentation below covers feature flags used by GitLab to deploy its own features, which **is not** the same
as the [feature flags offered as part of the product](../../operations/feature_flags.md).
-## When to use feature flags
+This document provides guidelines on how to use feature flags
+in the GitLab codebase to conditionally enable features
+and test them.
-Developers are required to use feature flags for changes that could affect availability of existing GitLab functionality (if it only affects the new feature you're making that is probably acceptable).
-Such changes include:
+Features that are developed and merged behind a feature flag
+should not include a changelog entry. The entry should be added either in the merge
+request removing the feature flag or the merge request where the default value of
+the feature flag is set to enabled. If the feature contains any database migrations, it
+*should* include a changelog entry for the database changes.
-1. New features in high traffic areas (e.g. a new merge request widget, new option in issues/epics, new CI functionality).
-1. Complex performance improvements that may require additional testing in production (e.g. rewriting complex queries, changes to frequently used API endpoints).
-1. Invasive changes to the user interface (e.g. introducing a new navigation bar, removal of a sidebar, UI element change in issues or MR interface).
-1. Introducing dependencies on third-party services (e.g. adding support for importing projects).
-1. Changes to features that can cause data corruption or cause data loss (e.g. features processing repository data or user uploaded content).
+WARNING:
+All newly-introduced feature flags should be [disabled by default](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#feature-flags-in-gitlab-development).
-Situations where you might consider not using a feature flag:
+NOTE:
+This document is the subject of continued work as part of an epic to [improve internal usage of Feature Flags](https://gitlab.com/groups/gitlab-org/-/epics/3551). Raise any suggestions as new issues and attach them to the epic.
-1. Adding a new API endpoint
-1. Introducing new features in low traffic areas (e.g. adding a new export functionality in the admin area/group settings/project settings)
-1. Non-invasive frontend changes (e.g. changing the color of a button, or moving a UI element in a low traffic area)
+## Feature flags in GitLab development
-In all cases, those working on the changes should ask themselves:
+The following highlights should be considered when deciding if feature flags
+should be leveraged:
-> Why do I need to add a feature flag? If I don't add one, what options do I have to control the impact on application reliability, and user experience?
+- By default, the feature flags should be **off**.
+- Feature flags should remain in the codebase for as short period as possible
+ to reduce the need for feature flag accounting.
+- The person operating with feature flags is responsible for clearly communicating
+ the status of a feature behind the feature flag with responsible stakeholders. The
+ issue description should be updated with the feature flag name and whether it is
+ defaulted on or off as soon it is evident that a feature flag is needed.
+- Merge requests that make changes hidden behind a feature flag, or remove an
+ existing feature flag because a feature is deemed stable must have the
+ ~"feature flag" label assigned.
+- When development of a feature will be spread across multiple merge
+ requests, you can use the following workflow:
-For perspective on why we limit our use of feature flags please see
-<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Feature flags only when needed](https://www.youtube.com/watch?v=DQaGqyolOd8).
+ 1. [Create a new feature flag](#create-a-new-feature-flag)
+ which is **off** by default, in the first merge request which uses the flag.
+ Flags [should not be added separately](#risk-of-a-broken-master-main-branch).
+ 1. Submit incremental changes via one or more merge requests, ensuring that any
+ new code added can only be reached if the feature flag is **on**.
+ You can keep the feature flag enabled on your local GDK during development.
+ 1. When the feature is ready to be tested, enable the feature flag for
+ a specific project and ensure that there are no issues with the implementation.
+ 1. When the feature is ready to be announced, create a merge request that adds
+ documentation about the feature, including [documentation for the feature flag itself](../documentation/feature_flags.md),
+ and a changelog entry. In the same merge request either flip the feature flag to
+ be **on by default** or remove it entirely in order to enable the new behavior.
-In case you are uncertain if a feature flag is necessary, simply ask about this in an early merge request, and those reviewing the changes will likely provide you with an answer.
+One might be tempted to think that feature flags will delay the release of a
+feature by at least one month (= one release). This is not the case. A feature
+flag does not have to stick around for a specific amount of time
+(e.g. at least one release), instead they should stick around until the feature
+is deemed stable. Stable means it works on GitLab.com without causing any
+problems, such as outages.
-When using a feature flag for UI elements, make sure to _also_ use a feature
-flag for the underlying backend code, if there is any. This ensures there is
-absolutely no way to use the feature until it is enabled.
+## Risk of a broken master (main) branch
-## How to use Feature Flags
+Feature flags **must** be used in the MR that introduces them. Not doing so causes a
+[broken master](https://about.gitlab.com/handbook/engineering/workflow/#broken-master) scenario due
+to the `rspec:feature-flags` job that only runs on the `master` branch.
-Feature flags can be used to gradually deploy changes, regardless of whether
-they are new features or performance improvements. By using feature flags,
-you can determine the impact of GitLab-directed changes, while still being able
-to disable those changes without having to revert an entire release.
+## Types of feature flags
-For an overview about starting with feature flags in GitLab development,
-use this [training template](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/.gitlab/issue_templates/feature-flag-training.md).
+Choose a feature flag type that matches the expected usage.
-Before using feature flags for GitLab development, review the following development guides:
+### `development` type
-1. [Process for using features flags](process.md): When you should use
- feature flags in the development of GitLab, what's the cost of using them,
- and how to include them in a release.
-1. [Developing with feature flags](development.md): Learn about the types of
- feature flags, their definition and validation, how to create them, frontend and
- backend details, and other information.
-1. [Documenting features deployed behind feature flags](../documentation/feature_flags.md):
- How to document features deployed behind feature flags, and how to update the
- documentation for features' flags when their states change.
-1. [Controlling feature flags](controls.md): Learn the process for deploying
- a new feature, enabling it on GitLab.com, communicating the change,
- logging, and cleaning up.
+`development` feature flags are short-lived feature flags,
+used so that unfinished code can be deployed in production.
-User guides:
+A `development` feature flag should have a rollout issue,
+ideally created using the [Feature Flag Roll Out template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20Flag%20Roll%20Out.md).
-1. [How GitLab administrators can enable and disable features behind flags](../../administration/feature_flags.md):
- An explanation for GitLab administrators about how they can
- enable or disable GitLab features behind feature flags.
-1. [What "features deployed behind flags" means to the GitLab user](../../user/feature_flags.md):
- An explanation for GitLab users regarding how certain features
- might not be available to them until they are enabled.
+This is the default type used when calling `Feature.enabled?`.
+
+### `ops` type
+
+`ops` feature flags are long-lived feature flags that control operational aspects
+of GitLab product behavior. For example, feature flags that disable features that might
+have a performance impact, like special Sidekiq worker behavior.
+
+`ops` feature flags likely do not have rollout issues, as it is hard to
+predict when they are enabled or disabled.
+
+To use `ops` feature flags, you must append `type: :ops` to `Feature.enabled?`
+invocations:
+
+```ruby
+# Check if feature flag is enabled
+Feature.enabled?(:my_ops_flag, project, type: :ops)
+
+# Check if feature flag is disabled
+Feature.disabled?(:my_ops_flag, project, type: :ops)
+
+# Push feature flag to Frontend
+push_frontend_feature_flag(:my_ops_flag, project, type: :ops)
+```
+
+### `experiment` type
+
+`experiment` feature flags are used for A/B testing on GitLab.com.
+
+An `experiment` feature flag should conform to the same standards as a `development` feature flag,
+although the interface has some differences. An experiment feature flag should have a rollout issue,
+ideally created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/experiment_tracking_template.md). More information can be found in the [experiment guide](../experiment_guide/index.md).
+
+## Feature flag definition and validation
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229161) in GitLab 13.3.
+
+During development (`RAILS_ENV=development`) or testing (`RAILS_ENV=test`) all feature flag usage is being strictly validated.
+
+This process is meant to ensure consistent feature flag usage in the codebase. All feature flags **must**:
+
+- Be known. Only use feature flags that are explicitly defined.
+- Not be defined twice. They have to be defined either in FOSS or EE, but not both.
+- Use a valid and consistent `type:` across all invocations.
+- Use the same `default_enabled:` across all invocations.
+- Have an owner.
+
+All feature flags known to GitLab are self-documented in YAML files stored in:
+
+- [`config/feature_flags`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/config/feature_flags)
+- [`ee/config/feature_flags`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/config/feature_flags)
+
+Each feature flag is defined in a separate YAML file consisting of a number of fields:
+
+| Field | Required | Description |
+|---------------------|----------|----------------------------------------------------------------|
+| `name` | yes | Name of the feature flag. |
+| `type` | yes | Type of feature flag. |
+| `default_enabled` | yes | The default state of the feature flag that is strictly validated, with `default_enabled:` passed as an argument. |
+| `introduced_by_url` | no | The URL to the Merge Request that introduced the feature flag. |
+| `rollout_issue_url` | no | The URL to the Issue covering the feature flag rollout. |
+| `group` | no | The [group](https://about.gitlab.com/handbook/product/categories/#devops-stages) that owns the feature flag. |
+
+NOTE:
+All validations are skipped when running in `RAILS_ENV=production`.
+
+## Create a new feature flag
+
+The GitLab codebase provides [`bin/feature-flag`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/bin/feature-flag),
+a dedicated tool to create new feature flag definitions.
+The tool asks various questions about the new feature flag, then creates
+a YAML definition in `config/feature_flags` or `ee/config/feature_flags`.
+
+Only feature flags that have a YAML definition file can be used when running the development or testing environments.
+
+```shell
+$ bin/feature-flag my_feature_flag
+>> Specify the group introducing the feature flag, like `group::apm`:
+?> group::memory
+
+>> URL of the MR introducing the feature flag (enter to skip):
+?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
+
+>> Open this URL and fill in the rest of the details:
+https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue%5Btitle%5D=%5BFeature+flag%5D+Rollout+of+%60test-flag%60&issuable_template=Feature+Flag+Roll+Out
+
+>> URL of the rollout issue (enter to skip):
+?> https://gitlab.com/gitlab-org/gitlab/-/issues/232533
+create config/feature_flags/development/my_feature_flag.yml
+---
+name: my_feature_flag
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232533
+group: group::memory
+type: development
+default_enabled: false
+```
+
+NOTE:
+To create a feature flag that is only used in EE, add the `--ee` flag: `bin/feature-flag --ee`
+
+## Delete a feature flag
+
+See [cleaning up feature flags](controls.md#cleaning-up) for more information about
+deleting feature flags.
+
+## Develop with a feature flag
+
+There are two main ways of using Feature Flags in the GitLab codebase:
+
+- [Backend code (Rails)](#backend)
+- [Frontend code (VueJS)](#frontend)
+
+### Backend
+
+The feature flag interface is defined in [`lib/feature.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/feature.rb).
+This interface provides a set of methods to check if the feature flag is enabled or disabled:
+
+```ruby
+if Feature.enabled?(:my_feature_flag, project)
+ # execute code if feature flag is enabled
+else
+ # execute code if feature flag is disabled
+end
+
+if Feature.disabled?(:my_feature_flag, project)
+ # execute code if feature flag is disabled
+end
+```
+
+In rare cases you may want to make a feature enabled by default. If so, explain the reasoning
+in the merge request. Use `default_enabled: true` when checking the feature flag state:
+
+```ruby
+if Feature.enabled?(:feature_flag, project, default_enabled: true)
+ # execute code if feature flag is enabled
+else
+ # execute code if feature flag is disabled
+end
+
+if Feature.disabled?(:my_feature_flag, project, default_enabled: true)
+ # execute code if feature flag is disabled
+end
+```
+
+If not specified, `default_enabled` is `false`.
+
+To force reading the `default_enabled` value from the relative YAML definition file, use
+`default_enabled: :yaml`:
+
+```ruby
+if Feature.enabled?(:feature_flag, project, default_enabled: :yaml)
+ # execute code if feature flag is enabled
+end
+```
+
+```ruby
+if Feature.disabled?(:feature_flag, project, default_enabled: :yaml)
+ # execute code if feature flag is disabled
+end
+```
+
+This allows to use the same feature flag check across various parts of the codebase and
+maintain the status of `default_enabled` in the YAML definition file which is the SSOT.
+
+If `default_enabled: :yaml` is used, a YAML definition is expected or an error is raised
+in development or test environment, while returning `false` on production.
+
+If not specified, the default feature flag type for `Feature.enabled?` and `Feature.disabled?`
+is `type: development`. For all other feature flag types, you must specify the `type:`:
+
+```ruby
+if Feature.enabled?(:feature_flag, project, type: :ops)
+ # execute code if ops feature flag is enabled
+else
+ # execute code if ops feature flag is disabled
+end
+
+if Feature.disabled?(:my_feature_flag, project, type: :ops)
+ # execute code if feature flag is disabled
+end
+```
+
+WARNING:
+Don't use feature flags at application load time. For example, using the `Feature` class in
+`config/initializers/*` or at the class level could cause an unexpected error. This error occurs
+because a database that a feature flag adapter might depend on doesn't exist at load time
+(especially for fresh installations). Checking for the database's existence at the caller isn't
+recommended, as some adapters don't require a database at all (for example, the HTTP adapter). The
+feature flag setup check must be abstracted in the `Feature` namespace. This approach also requires
+application reload when the feature flag changes. You must therefore ask SREs to reload the
+Web/API/Sidekiq fleet on production, which takes time to fully rollout/rollback the changes. For
+these reasons, use environment variables (for example, `ENV['YOUR_FEATURE_NAME']`) or `gitlab.yml`
+instead.
+
+Here's an example of a pattern that you should avoid:
+
+```ruby
+class MyClass
+ if Feature.enabled?(:...)
+ new_process
+ else
+ legacy_process
+ end
+end
+```
+
+### Frontend
+
+Use the `push_frontend_feature_flag` method for frontend code, which is
+available to all controllers that inherit from `ApplicationController`. You can use
+this method to expose the state of a feature flag, for example:
+
+```ruby
+before_action do
+ # Prefer to scope it per project or user e.g.
+ push_frontend_feature_flag(:vim_bindings, project)
+end
+
+def index
+ # ...
+end
+
+def edit
+ # ...
+end
+```
+
+You can then check the state of the feature flag in JavaScript as follows:
+
+```javascript
+if ( gon.features.vimBindings ) {
+ // ...
+}
+```
+
+The name of the feature flag in JavaScript is always camelCase,
+so checking for `gon.features.vim_bindings` would not work.
+
+See the [Vue guide](../fe_guide/vue.md#accessing-feature-flags) for details about
+how to access feature flags in a Vue component.
+
+In rare cases you may want to make a feature enabled by default. If so, explain the reasoning
+in the merge request. Use `default_enabled: true` when checking the feature flag state:
+
+```ruby
+before_action do
+ # Prefer to scope it per project or user e.g.
+ push_frontend_feature_flag(:vim_bindings, project, default_enabled: true)
+end
+```
+
+If not specified, the default feature flag type for `push_frontend_feature_flag`
+is `type: development`. For all other feature flag types, you must specify the `type:`:
+
+```ruby
+before_action do
+ push_frontend_feature_flag(:vim_bindings, project, type: :ops)
+end
+```
+
+### Feature actors
+
+**It is strongly advised to use actors with feature flags.** Actors provide a simple
+way to enable a feature flag only for a given project, group or user. This makes debugging
+easier, as you can filter logs and errors for example, based on actors. This also makes it possible
+to enable the feature on the `gitlab-org` or `gitlab-com` groups first, while the rest of
+the users aren't impacted.
+
+Actors also provide an easy way to do a percentage rollout of a feature in a sticky way.
+If a 1% rollout enabled a feature for a specific actor, that actor will continue to have the feature enabled at
+10%, 50%, and 100%.
+
+GitLab currently supports the following models as feature flag actors:
+
+- `User`
+- `Project`
+- `Group`
+
+The actor is a second parameter of the `Feature.enabled?` call. The
+same actor type must be used consistently for all invocations of `Feature.enabled?`.
+
+```ruby
+Feature.enabled?(:feature_flag, project)
+Feature.enabled?(:feature_flag, group)
+Feature.enabled?(:feature_flag, user)
+```
+
+#### Selectively disable by actor
+
+By default you cannot selectively disable a feature flag by actor.
+
+```shell
+# This will not work how you would expect.
+/chatops run feature set some_feature true
+/chatops run feature set --project=gitlab-org/gitlab some_feature false
+```
+
+However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
+
+```ruby
+Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
+```
+
+```shell
+# This will enable a feature flag globally, except for gitlab-org/gitlab
+/chatops run feature set a_feature true
+/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
+```
+
+### Enable additional objects as actors
+
+To use feature gates based on actors, the model needs to respond to
+`flipper_id`. For example, to enable for the Foo model:
+
+```ruby
+class Foo < ActiveRecord::Base
+ include FeatureGate
+end
+```
+
+Only models that `include FeatureGate` or expose `flipper_id` method can be
+used as an actor for `Feature.enabled?`.
+
+### Feature flags for licensed features
+
+You can't use a feature flag with the same name as a licensed feature name, because
+it would cause a naming collision. This was [widely discussed and removed](https://gitlab.com/gitlab-org/gitlab/-/issues/259611)
+because it is confusing.
+
+To check for licensed features, add a dedicated feature flag under a different name
+and check it explicitly, for example:
+
+```ruby
+Feature.enabled?(:licensed_feature_feature_flag, project) &&
+ project.feature_available?(:licensed_feature)
+```
+
+### Feature groups
+
+Feature groups must be defined statically in `lib/feature.rb` (in the
+`.register_feature_groups` method), but their implementation can obviously be
+dynamic (querying the DB, for example).
+
+Once defined in `lib/feature.rb`, you can to activate a
+feature for a given feature group via the [`feature_group` parameter of the features API](../../api/features.md#set-or-create-a-feature)
+
+### Enabling a feature flag locally (in development)
+
+In the rails console (`rails c`), enter the following command to enable a feature flag:
+
+```ruby
+Feature.enable(:feature_flag_name)
+```
+
+Similarly, the following command disables a feature flag:
+
+```ruby
+Feature.disable(:feature_flag_name)
+```
+
+You can also enable a feature flag for a given gate:
+
+```ruby
+Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))
+```
+
+### Removing a feature flag locally (in development)
+
+When manually enabling or disabling a feature flag from the Rails console, its default value gets overwritten.
+This can cause confusion when changing the flag's `default_enabled` attribute.
+
+To reset the feature flag to the default status, you can remove it in the rails console (`rails c`)
+as follows:
+
+```ruby
+Feature.remove(:feature_flag_name)
+```
+
+## Feature flags in tests
+
+Introducing a feature flag into the codebase creates an additional code path that should be tested.
+It is strongly advised to test all code affected by a feature flag, both when **enabled** and **disabled**
+to ensure the feature works properly.
+
+When using the testing environment, all feature flags are enabled by default.
+
+WARNING:
+This does not apply to end-to-end (QA) tests, which [do not disable feature flags by default](#end-to-end-qa-tests). There is a different [process for using feature flags in end-to-end tests](../testing_guide/end_to_end/feature_flags.md).
+
+To disable a feature flag in a test, use the `stub_feature_flags`
+helper. For example, to globally disable the `ci_live_trace` feature
+flag in a test:
+
+```ruby
+stub_feature_flags(ci_live_trace: false)
+
+Feature.enabled?(:ci_live_trace) # => false
+```
+
+If you wish to set up a test where a feature flag is enabled only
+for some actors and not others, you can specify this in options
+passed to the helper. For example, to enable the `ci_live_trace`
+feature flag for a specific project:
+
+```ruby
+project1, project2 = build_list(:project, 2)
+
+# Feature will only be enabled for project1
+stub_feature_flags(ci_live_trace: project1)
+
+Feature.enabled?(:ci_live_trace) # => false
+Feature.enabled?(:ci_live_trace, project1) # => true
+Feature.enabled?(:ci_live_trace, project2) # => false
+```
+
+The behavior of FlipperGate is as follows:
+
+1. You can enable an override for a specified actor to be enabled.
+1. You can disable (remove) an override for a specified actor,
+ falling back to the default state.
+1. There's no way to model that you explicitly disabled a specified actor.
+
+```ruby
+Feature.enable(:my_feature)
+Feature.disable(:my_feature, project1)
+Feature.enabled?(:my_feature) # => true
+Feature.enabled?(:my_feature, project1) # => true
+
+Feature.disable(:my_feature2)
+Feature.enable(:my_feature2, project1)
+Feature.enabled?(:my_feature2) # => false
+Feature.enabled?(:my_feature2, project1) # => true
+```
+
+### `have_pushed_frontend_feature_flags`
+
+Use `have_pushed_frontend_feature_flags` to test if [`push_frontend_feature_flag`](#frontend)
+has added the feature flag to the HTML.
+
+For example,
+
+```ruby
+stub_feature_flags(value_stream_analytics_path_navigation: false)
+
+visit group_analytics_cycle_analytics_path(group)
+
+expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsPathNavigation: false)
+```
+
+### `stub_feature_flags` vs `Feature.enable*`
+
+It is preferred to use `stub_feature_flags` to enable feature flags
+in the testing environment. This method provides a simple and well described
+interface for simple use cases.
+
+However, in some cases more complex behavior needs to be tested,
+like percentage rollouts of feature flags. This can be done using
+`.enable_percentage_of_time` or `.enable_percentage_of_actors`:
+
+```ruby
+# Good: feature needs to be explicitly disabled, as it is enabled by default if not defined
+stub_feature_flags(my_feature: false)
+stub_feature_flags(my_feature: true)
+stub_feature_flags(my_feature: project)
+stub_feature_flags(my_feature: [project, project2])
+
+# Bad
+Feature.enable(:my_feature_2)
+
+# Good: enable my_feature for 50% of time
+Feature.enable_percentage_of_time(:my_feature_3, 50)
+
+# Good: enable my_feature for 50% of actors/gates/things
+Feature.enable_percentage_of_actors(:my_feature_4, 50)
+```
+
+Each feature flag that has a defined state is persisted
+during test execution time:
+
+```ruby
+Feature.persisted_names.include?('my_feature') => true
+Feature.persisted_names.include?('my_feature_2') => true
+Feature.persisted_names.include?('my_feature_3') => true
+Feature.persisted_names.include?('my_feature_4') => true
+```
+
+### Stubbing actor
+
+When you want to enable a feature flag for a specific actor only,
+you can stub its representation. A gate that is passed
+as an argument to `Feature.enabled?` and `Feature.disabled?` must be an object
+that includes `FeatureGate`.
+
+In specs you can use the `stub_feature_flag_gate` method that allows you to
+quickly create a custom actor:
+
+```ruby
+gate = stub_feature_flag_gate('CustomActor')
+
+stub_feature_flags(ci_live_trace: gate)
+
+Feature.enabled?(:ci_live_trace) # => false
+Feature.enabled?(:ci_live_trace, gate) # => true
+```
+
+You can also disable a feature flag for a specific actor:
+
+```ruby
+gate = stub_feature_flag_gate('CustomActor')
+
+stub_feature_flags(ci_live_trace: false, thing: gate)
+```
+
+### Controlling feature flags engine in tests
+
+Our Flipper engine in the test environment works in a memory mode `Flipper::Adapters::Memory`.
+`production` and `development` modes use `Flipper::Adapters::ActiveRecord`.
+
+You can control whether the `Flipper::Adapters::Memory` or `ActiveRecord` mode is being used.
+
+#### `stub_feature_flags: true` (default and preferred)
+
+In this mode Flipper is configured to use `Flipper::Adapters::Memory` and mark all feature
+flags to be on-by-default and persisted on a first use. This overwrites the `default_enabled:`
+of `Feature.enabled?` and `Feature.disabled?` returning always `true` unless feature flag
+is persisted.
+
+Make sure behavior under feature flag doesn't go untested in some non-specific contexts.
+
+### `stub_feature_flags: false`
+
+This disables a memory-stubbed flipper, and uses `Flipper::Adapters::ActiveRecord`
+a mode that is used by `production` and `development`.
+
+You should use this mode only when you really want to tests aspects of Flipper
+with how it interacts with `ActiveRecord`.
+
+### End-to-end (QA) tests
+
+Toggling feature flags works differently in end-to-end (QA) tests. The end-to-end test framework does not have direct access to
+Rails or the database, so it can't use Flipper. Instead, it uses [the public API](../../api/features.md#set-or-create-a-feature). Each end-to-end test can [enable or disable a feature flag during the test](../testing_guide/end_to_end/feature_flags.md). Alternatively, you can enable or disable a feature flag before one or more tests when you [run them from your GitLab repository's `qa` directory](https://gitlab.com/gitlab-org/gitlab/tree/master/qa#running-tests-with-a-feature-flag-enabled-or-disabled), or if you [run the tests via GitLab QA](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#running-tests-with-a-feature-flag-enabled).
+
+[As noted above, feature flags are not enabled by default in end-to-end tests.](#feature-flags-in-tests)
+This means that end-to-end tests will run with feature flags in the default state implemented in the source
+code, or with the feature flag in its current state on the GitLab instance under test, unless the
+test is written to enable/disable a feature flag explicitly.
+
+When a feature flag is changed on Staging or on GitLab.com, a Slack message will be posted to the `#qa-staging` or `#qa-production` channels to inform
+the pipeline triage DRI so that they can more easily determine if any failures are related to a feature flag change. However, if you are working on a change you can
+help to avoid unexpected failures by [confirming that the end-to-end tests pass with a feature flag enabled.](../testing_guide/end_to_end/feature_flags.md#confirming-that-end-to-end-tests-pass-with-a-feature-flag-enabled)
diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md
index 65a6d01e0a2..247dafe9f0b 100644
--- a/doc/development/feature_flags/process.md
+++ b/doc/development/feature_flags/process.md
@@ -1,177 +1,8 @@
---
-type: reference, dev
-stage: none
-group: Development
-info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
+redirect_to: 'https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/'
---
-# Feature flags process
+This document was moved to [another location](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/).
-## Feature flags for user applications
-
-This document only covers feature flags used in the development of GitLab
-itself. Feature flags in deployed user applications can be found at
-[Feature Flags feature documentation](../../operations/feature_flags.md).
-
-## Feature flags in GitLab development
-
-The following highlights should be considered when deciding if feature flags
-should be leveraged:
-
-- By default, the feature flags should be **off**.
-- Feature flags should remain in the codebase for as short period as possible
- to reduce the need for feature flag accounting.
-- The person operating with feature flags is responsible for clearly communicating
- the status of a feature behind the feature flag with responsible stakeholders. The
- issue description should be updated with the feature flag name and whether it is
- defaulted on or off as soon it is evident that a feature flag is needed.
-- Merge requests that make changes hidden behind a feature flag, or remove an
- existing feature flag because a feature is deemed stable must have the
- ~"feature flag" label assigned.
-- When development of a feature will be spread across multiple merge
- requests, you can use the following workflow:
-
- 1. [Create a new feature flag](development.md#create-a-new-feature-flag)
- which is **off** by default, in the first merge request which uses the flag.
- Flags [should not be added separately](development.md#risk-of-a-broken-master-main-branch).
- 1. Submit incremental changes via one or more merge requests, ensuring that any
- new code added can only be reached if the feature flag is **on**.
- You can keep the feature flag enabled on your local GDK during development.
- 1. When the feature is ready to be tested, enable the feature flag for
- a specific project and ensure that there are no issues with the implementation.
- 1. When the feature is ready to be announced, create a merge request that adds
- documentation about the feature, including [documentation for the feature flag itself](../documentation/feature_flags.md),
- and a changelog entry. In the same merge request either flip the feature flag to
- be **on by default** or remove it entirely in order to enable the new behavior.
-
-One might be tempted to think that feature flags will delay the release of a
-feature by at least one month (= one release). This is not the case. A feature
-flag does not have to stick around for a specific amount of time
-(e.g. at least one release), instead they should stick around until the feature
-is deemed stable. Stable means it works on GitLab.com without causing any
-problems, such as outages.
-
-Please also read the [development guide for feature flags](development.md).
-
-### Including a feature behind feature flag in the final release
-
-In order to build a final release and present the feature for self-managed
-users, the feature flag should be at least defaulted to **on**. If the feature
-is deemed stable and there is confidence that removing the feature flag is safe,
-consider removing the feature flag altogether. It's _strongly_ recommended that
-the feature flag is [enabled **globally** on **production**](controls.md#enabling-a-feature-for-gitlabcom) for **at least one day**
-before making this decision. Unexpected bugs are sometimes discovered during this period.
-
-The process for enabling features that are disabled by default can take 5-6 days
-from when the merge request is first reviewed to when the change is deployed to
-GitLab.com. However, it is recommended to allow 10-14 days for this activity to
-account for unforeseen problems.
-
-Feature flags must be [documented according to their state (enabled/disabled)](../documentation/feature_flags.md),
-and when the state changes, docs **must** be updated accordingly.
-
-NOTE:
-Take into consideration that such action can make the feature available on
-GitLab.com shortly after the change to the feature flag is merged.
-
-Changing the default state or removing the feature flag has to be done before
-the 22nd of the month, _at least_ 3-4 working days before, in order for the change
-to be included in the final self-managed release.
-
-In addition to this, the feature behind feature flag should:
-
-- Run in all GitLab.com environments for a sufficient period of time. This time
- period depends on the feature behind the feature flag, but as a general rule of
- thumb 2-4 working days should be sufficient to gather enough feedback.
-- The feature should be exposed to all users within the GitLab.com plan during
- the above mentioned period of time. Exposing the feature to a smaller percentage
- or only a group of users might not expose a sufficient amount of information to aid in
- making a decision on feature stability.
-
-While rare, release managers may decide to reject picking or revert a change in
-a stable branch, even when feature flags are used. This might be necessary if
-the changes are deemed problematic, too invasive, or there simply isn't enough
-time to properly measure how the changes behave on GitLab.com.
-
-### The cost of feature flags
-
-When reading the above, one might be tempted to think this procedure is going to
-add a lot of work. Fortunately, this is not the case, and we'll show why. For
-this example we'll specify the cost of the work to do as a number, ranging from
-0 to infinity. The greater the number, the more expensive the work is. The cost
-does _not_ translate to time, it's just a way of measuring complexity of one
-change relative to another.
-
-Let's say we are building a new feature, and we have determined that the cost of
-this is 10. We have also determined that the cost of adding a feature flag check
-in a variety of places is 1. If we do not use feature flags, and our feature
-works as intended, our total cost is 10. This however is the best case scenario.
-Optimizing for the best case scenario is guaranteed to lead to trouble, whereas
-optimizing for the worst case scenario is almost always better.
-
-To illustrate this, let's say our feature causes an outage, and there's no
-immediate way to resolve it. This means we'd have to take the following steps to
-resolve the outage:
-
-1. Revert the release.
-1. Perform any cleanups that might be necessary, depending on the changes that
- were made.
-1. Revert the commit, ensuring the "master" branch remains stable. This is
- especially necessary if solving the problem can take days or even weeks.
-1. Pick the revert commit into the appropriate stable branches, ensuring we
- don't block any future releases until the problem is resolved.
-
-As history has shown, these steps are time consuming, complex, often involve
-many developers, and worst of all: our users will have a bad experience using
-GitLab.com until the problem is resolved.
-
-Now let's say that all of this has an associated cost of 10. This means that in
-the worst case scenario, which we should optimize for, our total cost is now 20.
-
-If we had used a feature flag, things would have been very different. We don't
-need to revert a release, and because feature flags are disabled by default we
-don't need to revert and pick any Git commits. In fact, all we have to do is
-disable the feature, and in the worst case, perform cleanup. Let's say that
-the cost of this is 2. In this case, our best case cost is 11: 10 to build the
-feature, and 1 to add the feature flag. The worst case cost is now 13:
-
-- 10 to build the feature.
-- 1 to add the feature flag.
-- 2 to disable and clean up.
-
-Here we can see that in the best case scenario the work necessary is only a tiny
-bit more compared to not using a feature flag. Meanwhile, the process of
-reverting our changes has been made significantly and reliably cheaper.
-
-In other words, feature flags do not slow down the development process. Instead,
-they speed up the process as managing incidents now becomes _much_ easier. Once
-continuous deployments are easier to perform, the time to iterate on a feature
-is reduced even further, as you no longer need to wait weeks before your changes
-are available on GitLab.com.
-
-### The benefits of feature flags
-
-It may seem like feature flags are configuration, which goes against our [convention-over-configuration](https://about.gitlab.com/handbook/product/product-principles/#convention-over-configuration)
-principle. However, configuration is by definition something that is user-manageable.
-Feature flags are not intended to be user-editable. Instead, they are intended as a tool for Engineers
-and Site Reliability Engineers to use to de-risk their changes. Feature flags are the shim that gets us
-to Continuous Delivery with our monorepo and without having to deploy the entire codebase on every change.
-Feature flags are created to ensure that we can safely rollout our work on our terms.
-If we use Feature Flags as a configuration, we are doing it wrong and are indeed in violation of our
-principles. If something needs to be configured, we should intentionally make it configuration from the
-first moment.
-
-Some of the benefits of using development-type feature flags are:
-
-1. It enables Continuous Delivery for GitLab.com.
-1. It significantly reduces Mean-Time-To-Recovery.
-1. It helps engineers to monitor and reduce the impact of their changes gradually, at any scale,
- allowing us to be more metrics-driven and execute good DevOps practices, [shifting some responsibility "left"](https://devops.com/why-its-time-for-site-reliability-engineering-to-shift-left/).
-1. Controlled feature rollout timing: without feature flags, we would need to wait until a specific
- deployment was complete (which at GitLab could be at any time).
-1. Increased psychological safety: when a feature flag is used, an engineer has the confidence that if anything goes wrong they can quickly disable the code and minimize the impact of a change that might be risky.
-1. Improved throughput: when a change is less risky because a flag exists, theoretical tests about
- scalability can potentially become unnecessary or less important. This allows an engineer to
- potentially test a feature on a small project, monitor the impact, and proceed. The alternative might
- be to build complex benchmarks locally, or on staging, or on another GitLab deployment, which has a
- large impact on the time it can take to build and release a feature.
+<!-- This redirect file can be deleted after 2021-06-01. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> \ No newline at end of file
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index 8d5b2db828e..50362269c1b 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -459,7 +459,7 @@ Performance deficiencies should be addressed right away after we merge initial
changes.
Read more about when and how feature flags should be used in
-[Feature flags in GitLab development](feature_flags/process.md#feature-flags-in-gitlab-development).
+[Feature flags in GitLab development](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle#feature-flags-in-gitlab-development).
## Storage
diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md
index d38e261b99f..c60d70b3b16 100644
--- a/doc/development/new_fe_guide/tips.md
+++ b/doc/development/new_fe_guide/tips.md
@@ -16,18 +16,18 @@ yarn clean
## Creating feature flags in development
-The process for creating a feature flag is the same as [enabling a feature flag in development](../feature_flags/development.md#enabling-a-feature-flag-locally-in-development).
+The process for creating a feature flag is the same as [enabling a feature flag in development](../feature_flags/index.md#enabling-a-feature-flag-locally-in-development).
Your feature flag can now be:
-- [Made available to the frontend](../feature_flags/development.md#frontend) via the `gon`
-- Queried in [tests](../feature_flags/development.md#feature-flags-in-tests)
+- [Made available to the frontend](../feature_flags/index.md#frontend) via the `gon`
+- Queried in [tests](../feature_flags/index.md#feature-flags-in-tests)
- Queried in HAML templates and Ruby files via the `Feature.enabled?(:my_shiny_new_feature_flag)` method
### More on feature flags
- [Deleting a feature flag](../../api/features.md#delete-a-feature)
-- [Manage feature flags](../feature_flags/process.md)
+- [Manage feature flags](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle)
- [Feature flags API](../../api/features.md)
## Running tests locally
diff --git a/doc/development/packages.md b/doc/development/packages.md
index 5a3ca79f992..e8c326da974 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -122,7 +122,7 @@ There are usually 2 phases for the MVC:
When implementing a new package manager, it is tempting to create one large merge request containing all of the
necessary endpoints and services necessary to support basic usage. Instead, put the
-API endpoints behind a [feature flag](feature_flags/development.md) and
+API endpoints behind a [feature flag](feature_flags/index.md) and
submit each endpoint or behavior (download, upload, etc) in a different merge request to shorten the review
process.
diff --git a/doc/development/service_measurement.md b/doc/development/service_measurement.md
index 91fb8248db4..895ac540838 100644
--- a/doc/development/service_measurement.md
+++ b/doc/development/service_measurement.md
@@ -75,7 +75,7 @@ To actually use it, you need to enable measuring for the desired service by enab
### Enabling measurement using feature flags
In the following example, the `:gitlab_service_measuring_projects_import_service`
-[feature flag](feature_flags/development.md#enabling-a-feature-flag-locally-in-development) is used to enable the measuring feature
+[feature flag](feature_flags/index.md#enabling-a-feature-flag-locally-in-development) is used to enable the measuring feature
for `Projects::ImportService`.
From ChatOps:
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index ad16f3201e2..ee8401e08d4 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -545,7 +545,7 @@ end
### Feature flags in tests
-This section was moved to [developing with feature flags](../feature_flags/development.md).
+This section was moved to [developing with feature flags](../feature_flags/index.md).
### Pristine test environments
diff --git a/doc/development/testing_guide/end_to_end/feature_flags.md b/doc/development/testing_guide/end_to_end/feature_flags.md
index 1bc33b79c7c..e3719393d41 100644
--- a/doc/development/testing_guide/end_to_end/feature_flags.md
+++ b/doc/development/testing_guide/end_to_end/feature_flags.md
@@ -18,8 +18,8 @@ Please be sure to include the tag `:requires_admin` so that the test can be skip
where admin access is not available.
WARNING:
-You are strongly advised to [enable feature flags only for a group, project, user](../../feature_flags/development.md#feature-actors),
-or [feature group](../../feature_flags/development.md#feature-groups). This makes it possible to
+You are strongly advised to [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors),
+or [feature group](../../feature_flags/index.md#feature-groups). This makes it possible to
test a feature in a shared environment without affecting other users.
For example, the code below would enable a feature flag named `:feature_flag_name` for the project
diff --git a/doc/development/usage_ping.md b/doc/development/usage_ping.md
index c340f1eb10d..b8f08caaebd 100644
--- a/doc/development/usage_ping.md
+++ b/doc/development/usage_ping.md
@@ -3,6 +3,5 @@ redirect_to: 'usage_ping/index.md'
---
This document was moved to [another location](usage_ping/index.md).
-
<!-- This redirect file can be deleted after <2021-05-23>. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> \ No newline at end of file
diff --git a/doc/development/usage_ping/index.md b/doc/development/usage_ping/index.md
index 40635e20495..604d59f42e1 100644
--- a/doc/development/usage_ping/index.md
+++ b/doc/development/usage_ping/index.md
@@ -947,7 +947,7 @@ Each aggregate definition includes following parts:
relay on the same data source. Additional data source requirements are described in the
[Database sourced aggregated metrics](#database-sourced-aggregated-metrics) and
[Redis sourced aggregated metrics](#redis-sourced-aggregated-metrics) sections.
-- `feature_flag`: Name of [development feature flag](../feature_flags/development.md#development-type)
+- `feature_flag`: Name of [development feature flag](../feature_flags/index.md#development-type)
that is checked before metrics aggregation is performed. Corresponding feature flag
should have `default_enabled` attribute set to `false`. The `feature_flag` attribute
is optional and can be omitted. When `feature_flag` is missing, no feature flag is checked.
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index f5da373ee6d..fe35d575373 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -61,6 +61,6 @@ The following analytics features are available at the project level:
- [Insights](../project/insights/index.md). **(ULTIMATE)**
- [Issue](../group/issues_analytics/index.md). **(PREMIUM)**
- [Merge Request](merge_request_analytics.md), enabled with the `project_merge_request_analytics`
- [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-locally-in-development). **(PREMIUM)**
+ [feature flag](../../development/feature_flags/index.md#enabling-a-feature-flag-locally-in-development). **(PREMIUM)**
- [Repository](repository_analytics.md). **(FREE)**
- [Value Stream](value_stream_analytics.md). **(FREE)**
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index d632fc47249..655a0dcb00f 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -242,7 +242,7 @@ a [beta feature](https://about.gitlab.com/handbook/product/#beta).
For a group, you can view how many merge requests, issues, and members were created in the last 90 days.
-These Group Activity Analytics can be enabled with the `group_activity_analytics` [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-locally-in-development).
+These Group Activity Analytics can be enabled with the `group_activity_analytics` [feature flag](../../development/feature_flags/index.md#enabling-a-feature-flag-locally-in-development).
![Recent Group Activity](img/group_activity_analytics_v13_10.png)
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 61da664f134..5079b927fbe 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -103,7 +103,6 @@ With this option enabled, users must go through your group's GitLab single sign-
However, users are not prompted to sign in through SSO on each visit. GitLab checks whether a user
has authenticated through SSO. If it's been more than 1 day since the last sign-in, GitLab
prompts the user to sign in again through SSO.
-You can see more information about how long a session is valid in our [user profile documentation](../../profile/#why-do-i-keep-getting-signed-out).
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
diff --git a/doc/user/img/gitlab_snippet_v13_0.png b/doc/user/img/gitlab_snippet_v13_0.png
deleted file mode 100644
index 33194c512df..00000000000
--- a/doc/user/img/gitlab_snippet_v13_0.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/gitlab_snippet_v13_5.png b/doc/user/img/gitlab_snippet_v13_5.png
deleted file mode 100644
index 3fce1d25c3d..00000000000
--- a/doc/user/img/gitlab_snippet_v13_5.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/new_personal_snippet_from_project_v12_10.png b/doc/user/img/new_personal_snippet_from_project_v12_10.png
deleted file mode 100644
index af8cce01208..00000000000
--- a/doc/user/img/new_personal_snippet_from_project_v12_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/new_personal_snippet_v12_10.png b/doc/user/img/new_personal_snippet_v12_10.png
deleted file mode 100644
index a995e4d4b40..00000000000
--- a/doc/user/img/new_personal_snippet_v12_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md
index 8d5a41a6e7d..b9186e99357 100644
--- a/doc/user/packages/generic_packages/index.md
+++ b/doc/user/packages/generic_packages/index.md
@@ -53,7 +53,7 @@ PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name?sta
Provide the file context in the request body.
-Example request:
+Example request using a personal access token:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
@@ -69,6 +69,22 @@ Example response:
}
```
+Example request using a deploy token:
+
+```shell
+curl --header "DEPLOY-TOKEN: <deploy_token>" \
+ --upload-file path/to/file.txt \
+ "https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt?status=hidden"
+```
+
+Example response:
+
+```json
+{
+ "message":"201 Created"
+}
+```
+
## Download package file
Download a package file.
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 6a13ec4311c..65d83c2b413 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -40,7 +40,7 @@ On your profile page, you can see the following information:
- Contributed projects: [projects](../project/index.md) you contributed to
- Personal projects: your personal projects (respecting the project's visibility level)
- Starred projects: projects you starred
-- Snippets: your personal code [snippets](../snippets.md#personal-snippets)
+- Snippets: your personal code [snippets](../snippets.md)
- Followers: people [following](../index.md#user-activity) you
- Following: people you are [following](../index.md#user-activity)
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 3c846db7b47..8606ce36a82 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -130,6 +130,22 @@ To pull packages in the GitLab package registry, you must:
1. For the [package type of your choice](../../packages/index.md), follow the
authentication instructions for deploy tokens.
+Example request publishing a generic package using a deploy token:
+
+```shell
+curl --header "DEPLOY-TOKEN: <deploy_token>" \
+ --upload-file path/to/file.txt \
+ "https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt?status=hidden"
+```
+
+Example response:
+
+```json
+{
+ "message":"201 Created"
+}
+```
+
### Push or upload packages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213566) in GitLab 13.0.
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 7a7c3578d42..c97196ff861 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -137,7 +137,7 @@ You can mark two issues as related, so that when viewing one, the other is alway
listed in its [Related Issues](related_issues.md) section. This can help display important
context, such as past work, dependencies, or duplicates.
-Users on [GitLab Starter, GitLab Bronze, and higher tiers](https://about.gitlab.com/pricing/), can
+Users of [GitLab Premium](https://about.gitlab.com/pricing/) or higher can
also mark issues as blocking or blocked by another issue.
### Crosslinking issues
@@ -162,9 +162,9 @@ Up to five similar issues, sorted by most recently updated, are displayed below
### Health status **(ULTIMATE)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36427) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
-> - Health status of closed issues [can't be edited](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4 and later.
-> - Issue health status visible in issue lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45141) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.6.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36427) in GitLab Ultimate 12.10.
+> - Health status of closed issues [can't be edited](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in GitLab Ultimate 13.4 and later.
+> - Issue health status visible in issue lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45141) in GitLab Ultimate 13.6.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/213567) in GitLab 13.7.
To help you track issue statuses, you can assign a status to each issue.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index c3427830426..785618a862a 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -44,6 +44,7 @@ Compliance framework labels do not affect your project settings.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
+> - It can be enabled or disabled for a single group
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-custom-compliance-frameworks). **(PREMIUM)**
@@ -329,11 +330,11 @@ can enable it.
To enable it:
```ruby
-Feature.enable(:ff_custom_compliance_frameworks)
+Feature.enable(:ff_custom_compliance_frameworks, Group.find(<group id>))
```
To disable it:
```ruby
-Feature.disable(:ff_custom_compliance_frameworks)
+Feature.disable(:ff_custom_compliance_frameworks, Group.find(<group id>))
```
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 30da6eceaf4..a2a18c0e8de 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -7,83 +7,98 @@ type: reference
# Snippets **(FREE)**
-With GitLab Snippets you can store and share bits of code and text with other users.
+With GitLab snippets, you can store and share bits of code and text with other users.
+You can [comment on](#comment-on-snippets), [clone](#clone-snippets), and
+[use version control](#versioned-snippets) in snippets. They can
+[contain multiple files](#add-or-remove-multiple-files). They also support
+[syntax highlighting](#filenames), [embedding](#embed-snippets), [downloading](#download-snippets),
+and you can maintain your snippets with the [snippets API](../api/snippets.md).
+
+GitLab provides two types of snippets:
+
+- **Personal snippets**: Created independent of any project.
+ You can set a [visibility level](../public_access/public_access.md)
+ for your snippet: public, internal, or private.
+- **Project snippets**: Always related to a specific project.
+ Project snippets can be visible publicly or to only group members.
+
+## Create snippets
+
+You can create snippets in multiple ways, depending on whether you want to create a personal or project snippet:
+
+1. Select the kind of snippet you want to create:
+ - **To create a personal snippet**:
+ - *If you're on a project's page,* select the plus icon (**{plus-square-o}**)
+ in the top navigation bar, then select **New snippet** from the **GitLab** (for GitLab.com)
+ or **Your Instance** (self-managed) section of the same dropdown menu.
+ - *For all other pages,* select the plus icon (**{plus-square-o}**)
+ in the top navigation bar, then select **New snippet** from the dropdown menu.
+ - **To create a project snippet**: Go to your project's page. Select the plus icon
+ (**{plus-square-o}**), then select **New snippet** from the **This project** section
+ of the dropdown menu.
+1. Add a **Title** and **Description**.
+1. Name your **File** with an appropriate extension, such as `example.rb` or `index.html`.
+ Filenames with appropriate extensions display [syntax highlighting](#filenames).
+ Failure to add a filename can cause a known
+ [copy-pasting bug](https://gitlab.com/gitlab-org/gitlab/-/issues/22870). If you don't provide a filename, GitLab [creates a name for you](#filenames).
+1. (Optional) Add [multiple files](#add-or-remove-multiple-files) to your snippet.
+1. Select a visibility level, and select **Create snippet**.
+
+After you create a snippet, you can still [add more files to it](#add-or-remove-multiple-files).
+In GitLab versions 13.0 and later, snippets are [versioned by default](#versioned-snippets).
-![GitLab Snippet](img/gitlab_snippet_v13_0.png)
-
-Snippets can be maintained using [snippets API](../api/snippets.md).
-
-There are two types of snippets:
-
-- Personal snippets.
-- Project snippets.
-
-## Personal snippets
-
-Personal snippets are not related to any project and can be created completely
-independently. There are 3 visibility levels that can be set, public, internal
-and private. See [Public access](../public_access/public_access.md) for more information.
-
-## Project snippets
-
-Project snippets are always related to a specific project.
-See [Project features](project/index.md#project-features) for more information.
-
-## Create a snippet
-
-To create a personal snippet, click the plus icon (**{plus-square-o}**)
-on the top navigation and select **New snippet** from the dropdown menu:
-
-![New personal snippet from non-project pages](img/new_personal_snippet_v12_10.png)
-
-If you're on a project's page but you want to create a new personal snippet,
-click the plus icon (**{plus-square-o}**) and select **New snippet** from the
-lower part of the dropdown (**GitLab** on GitLab.com; **Your Instance** on
-self-managed instances):
+## Discover snippets
-![New personal snippet from project pages](img/new_personal_snippet_from_project_v12_10.png)
+To discover all snippets visible to you in GitLab, you can:
-To create a project snippet, navigate to your project's page and click the
-plus icon (**{plus-square-o}**), then select **New snippet** from the upper
-part of the dropdown (**This project**).
+- **View all snippets visible to you**: In the top navigation bar of your GitLab
+ instance, go to **More > Snippets** to view your snippets dashboard.
+- **Visit [GitLab snippets](http://snippets.gitlab.com/)** for your snippets on GitLab.com.
+- **Explore all public snippets**: In the top navigation bar of your GitLab
+ instance, go to **More > Snippets** and select **Explore snippets** to view
+ [all public snippets](https://gitlab.com/explore/snippets).
+- **View a project's snippets**: In your project,
+ go to **Snippets**.
-![New personal snippet from project pages](img/new_project_snippet_from_project_v12_10.png)
+## Change default visibility of snippets
-From there, add the **Title**, **Description**, and a **File** name with the
-appropriate extension (for example, `example.rb`, `index.html`).
+Project snippets are enabled and available by default. To change their
+default visibility:
-WARNING:
-Make sure to add the filename to get code highlighting and to avoid this
-[copy-pasting bug](https://gitlab.com/gitlab-org/gitlab/-/issues/22870).
+1. In your project,
+ go to **Settings**.
+1. Expand the **Visibility, project features, permissions** section, and scroll to **Snippets**.
+1. Toggle the default visibility, and select whether snippets can be viewed by
+ everyone, or only project members.
+1. Select **Save changes**.
-## Versioned Snippets
+## Versioned snippets
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/239) in GitLab 13.0.
-Starting in 13.0, snippets (both personal and project snippets)
+In GitLab versions 13.0 and later, snippets (both personal and project snippets)
have version control enabled by default.
This means that all snippets get their own underlying repository initialized with
-a `master` branch at the moment the snippet is created. Whenever a change to the snippet is saved, a
-new commit to the `master` branch is recorded. Commit messages are automatically
-generated. The snippet's repository has only one branch (`master`) by default, deleting
-it or creating other branches is not supported.
+a default branch at the moment the snippet is created. Whenever a change to the snippet is saved, a
+new commit to the default branch is recorded. Commit messages are automatically
+generated. The snippet's repository has only one branch. You can't delete this branch,
+or create other branches.
-Existing snippets are automatically migrated in 13.0. Their current
-content is saved as the initial commit to the snippets' repository.
+Existing snippets were automatically migrated in 13.0. Their current
+content was saved as the initial commit to the snippets' repository.
-### Filenames
+## Filenames
Snippets support syntax highlighting based on the filename and
extension provided for them. While you can submit a snippet
-without specifying a filename and extension, it needs a valid name so the
+without a filename and extension, it needs a valid name so the
content can be created as a file in the snippet's repository.
-If you don't attribute a filename and extension to a snippet,
+If you don't give a snippet a filename and extension,
GitLab adds a filename in the format `snippetfile<x>.txt`
where `<x>` represents a number added to the file, starting with 1. This
-number increments when more snippets without an attributed
-filename are added.
+number increments if you add more unnamed snippets.
When upgrading from an earlier version of GitLab to 13.0, existing snippets
without a supported filename are renamed to a compatible format. For
@@ -92,139 +107,110 @@ changed to `http-a-weird-filename-me` to be included in the snippet's
repository. As snippets are stored by ID, changing their filenames breaks
direct or embedded links to the snippet.
-### Multiple files by Snippet
+## Add or remove multiple files
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2829) in GitLab 13.5.
-GitLab Snippets support multiple files in one single snippet. This is helpful
-when your code snippet is composed of multiple parts or when they relate
-to a certain context. For example:
+A single snippet can support up to 10 files, which helps keep related files together, such as:
- A snippet that includes a script and its output.
-- A snippet that includes HTML, CSS, and JS code.
+- A snippet that includes HTML, CSS, and JavaScript code.
- A snippet with a `docker-compose.yml` file and its associated `.env` file.
-- A `gulpfile.js` file coupled with a `package.json` file, which together can be
+- A `gulpfile.js` file and a `package.json` file, which together can be
used to bootstrap a project and manage its dependencies.
-Snippets support between 1 and 10 files. They can be managed via Git (because they're [versioned](#versioned-snippets)
-by a Git repository), through the [Snippets API](../api/snippets.md), or in the GitLab UI.
-
-![Multi-file Snippet](img/gitlab_snippet_v13_5.png)
+You can manage these by using Git (because they're [versioned](#versioned-snippets)
+by a Git repository), through the [Snippets API](../api/snippets.md), and in the GitLab UI.
To add a new file to your snippet through the GitLab UI:
1. Go to your snippet in the GitLab UI.
-1. Click **Edit** in the top right.
+1. Select **Edit** in the top right corner.
1. Select **Add another file**.
1. Add your content to the file in the form fields provided.
-1. Click **Save changes**.
+1. Select **Save changes**.
To delete a file from your snippet through the GitLab UI:
1. Go to your snippet in the GitLab UI.
-1. Click **Edit** in the top right.
+1. Select **Edit** in the top right corner.
1. Select **Delete file** alongside the filename of each file
you wish to delete.
-1. Click **Save changes**.
-
-### Cloning snippets
-
-Snippets can be cloned as a regular Git repository using SSH or HTTPS. Click the **Clone**
-button above the snippet content to copy the URL of your choice.
+1. Select **Save changes**.
-![Clone Snippet](img/snippet_clone_button_v13_0.png)
+## Clone snippets
-This allows you to have a local copy of the snippet's repository and make
-changes as needed. You can commit those changes and push them to the remote
-`master` branch.
+Instead of copying a snippet to a local file, you may want to clone a snippet to
+preserve its relationship with the repository, so you can receive or make updates
+as needed. Select the **Clone** button on a snippet to display the URLs to clone with SSH or HTTPS:
-### Reduce snippets repository size
+![Clone snippet](img/snippet_clone_button_v13_0.png)
-Because versioned Snippets are considered as part of the [namespace storage size](../user/admin_area/settings/account_and_limit_settings.md),
-it's recommended to keep snippets' repositories as compact as possible.
+You can commit changes to a cloned snippet, and push the changes to GitLab.
-For more information about tools to compact repositories,
-see the documentation on [reducing repository size](../user/project/repository/reducing_the_repo_size_using_git.md).
+## Embed snippets
-### Limitations
+Public snippets can be shared and embedded on any website. You can reuse a GitLab snippet in multiple places, and any change to the source
+is reflected in the embedded snippets. When embedded, users can download it, or view the snippet in raw format.
-- Binary files are not supported.
-- Creating or deleting branches is not supported. Only a default `master` branch is used.
-- Git tags are not supported in snippet repositories.
-- Snippets' repositories are limited to 10 files. Attempting to push more
- than 10 files results in an error.
-- Revisions are not visible to the user on the GitLab UI, but this feature is planned
- in future iterations. See the [revisions tab issue](https://gitlab.com/gitlab-org/gitlab/-/issues/39271)
- for updates.
-- The [maximum size for a snippet](../administration/snippets/index.md#snippets-content-size-limit)
- is 50 MB, by default.
-- Git LFS is not supported.
+To embed a snippet:
-## Discover snippets
+1. Confirm your snippet is publicly visible:
+ - *If it's a project snippet*, the project must be public.
+ - The snippet is publicly visible.
+ - In **Project > Settings > Permissions**, the snippets
+ permissions are set to **Everyone with access**.
+1. In your snippet's **Embed** section, select **Copy** to copy a one-line script you can add to any website or blog post. For example:
-There are two main ways of how you can discover snippets in GitLab.
+ ```html
+ <script src="https://gitlab.com/namespace/project/snippets/SNIPPET_ID.js"></script>
+ ```
-For exploring all snippets that are visible to you, you can go to the Snippets
-dashboard of your GitLab instance via the top navigation. For GitLab.com you can
-visit [GitLab Snippets](http://snippets.gitlab.com/) or navigate to an [overview]((https://gitlab.com/dashboard/snippets)) that shows snippets
-you created and allows you to explore all snippets.
+1. Add your script to your file.
-To discover snippets that belong to a specific project, navigate
-to the Snippets page via the left side navigation on the project page.
-Project snippets are enabled and available by default. You can
-disable them by navigating to your project's **Settings**, expanding
-**Visibility, project features, permissions** and scrolling down to
-**Snippets**. From there, you can toggle to disable them or select a
-different visibility level from the dropdown menu.
+Embedded snippets display a header that shows:
-## Snippet comments
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/12910) in GitLab 9.2.
+- The filename, if defined.
+- The snippet size.
+- A link to GitLab.
+- The actual snippet content.
-With GitLab Snippets you engage in a conversation about that piece of code,
-encouraging user collaboration.
+For example:
-## Downloading snippets
+<script src="https://gitlab.com/gitlab-org/gitlab-foss/snippets/1717978.js"></script>
-You can download the raw content of a snippet.
+## Download snippets
-By default snippets are downloaded with Linux-style line endings (`LF`). If
+You can download the raw content of a snippet. By default, they download with Linux-style line endings (`LF`). If
you want to preserve the original line endings you need to add a parameter `line_ending=raw`
(For example: `https://gitlab.com/snippets/SNIPPET_ID/raw?line_ending=raw`). In case a
snippet was created using the GitLab web interface the original line ending is Windows-like (`CRLF`).
-## Embedded snippets
-
-> Introduced in GitLab 10.8.
-
-Public snippets can not only be shared, but also embedded on any website. With
-this, you can reuse a GitLab snippet in multiple places and any change to the source
-is automatically reflected in the embedded snippet.
+## Comment on snippets
-To embed a snippet, first make sure that:
+With snippets, you engage in a conversation about that piece of code,
+which can encourage user collaboration.
-- The project is public (if it's a project snippet)
-- The snippet is public
-- In **Project > Settings > Permissions**, the snippets permissions are
- set to **Everyone with access**
+## Troubleshooting
-After the above conditions are met, the **Embed** section appears in your
-snippet. Click the **Copy** button to copy a one-line
-script that you can add to any website or blog post. For example:
+### Snippet limitations
-```html
-<script src="https://gitlab.com/namespace/project/snippets/SNIPPET_ID.js"></script>
-```
-
-Here's what an embedded snippet looks like:
-
-<script src="https://gitlab.com/gitlab-org/gitlab-foss/snippets/1717978.js"></script>
+- Binary files are not supported.
+- Creating or deleting branches is not supported. Only the default branch is used.
+- Git tags are not supported in snippet repositories.
+- Snippets' repositories are limited to 10 files. Attempting to push more
+ than 10 files results in an error.
+- Revisions are not visible to the user on the GitLab UI, but this feature is planned
+ in future iterations. See the [revisions tab issue](https://gitlab.com/gitlab-org/gitlab/-/issues/39271)
+ for updates.
+- The [maximum size for a snippet](../administration/snippets/index.md#snippets-content-size-limit)
+ is 50 MB, by default.
+- Git LFS is not supported.
-Embedded snippets are displayed with a header that shows:
+### Reduce snippets repository size
-- The filename, if defined.
-- The snippet size.
-- A link to GitLab.
-- The actual snippet content.
+Because versioned snippets are considered as part of the [namespace storage size](../user/admin_area/settings/account_and_limit_settings.md),
+it's recommended to keep snippets' repositories as compact as possible.
-Actions in the header enable users to see the snippet in raw format, and download it.
+For more information about tools to compact repositories,
+see the documentation on [reducing repository size](../user/project/repository/reducing_the_repo_size_using_git.md).
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index 136e217b982..8d2d4586d8d 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -64,8 +64,15 @@ module API
requires :file_name, type: String, desc: 'Package file name'
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
- # To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299283
- not_found!
+ authorize!(:read_package, user_project)
+
+ package_file = ::Packages::PackageFile.for_rubygem_with_file_name(
+ user_project, params[:file_name]
+ ).last!
+
+ track_package_event('pull_package', :rubygems)
+
+ present_carrierwave_file!(package_file.file)
end
namespace 'api/v1' do
diff --git a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
index c2db0fc44f1..200388a274c 100644
--- a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
@@ -52,7 +52,8 @@ cache:
- gitlab-terraform apply
when: manual
only:
- - master
+ variables:
+ - $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
.destroy: &destroy
stage: cleanup
diff --git a/lib/gitlab/database/migrations/background_migration_helpers.rb b/lib/gitlab/database/migrations/background_migration_helpers.rb
index 12dcf68da2f..e8cbea72887 100644
--- a/lib/gitlab/database/migrations/background_migration_helpers.rb
+++ b/lib/gitlab/database/migrations/background_migration_helpers.rb
@@ -4,8 +4,12 @@ module Gitlab
module Database
module Migrations
module BackgroundMigrationHelpers
- BACKGROUND_MIGRATION_BATCH_SIZE = 1_000 # Number of rows to process per job
- BACKGROUND_MIGRATION_JOB_BUFFER_SIZE = 1_000 # Number of jobs to bulk queue at a time
+ BATCH_SIZE = 1_000 # Number of rows to process per job
+ SUB_BATCH_SIZE = 100 # Number of rows to process per sub-batch
+ JOB_BUFFER_SIZE = 1_000 # Number of jobs to bulk queue at a time
+ BATCH_CLASS_NAME = 'PrimaryKeyBatchingStrategy' # Default batch class for batched migrations
+ BATCH_MIN_VALUE = 1 # Default minimum value for batched migrations
+ BATCH_MIN_DELAY = 2.minutes.freeze # Minimum delay between batched migrations
# Bulk queues background migration jobs for an entire table, batched by ID range.
# "Bulk" meaning many jobs will be pushed at a time for efficiency.
@@ -31,7 +35,7 @@ module Gitlab
# # do something
# end
# end
- def bulk_queue_background_migration_jobs_by_range(model_class, job_class_name, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE)
+ def bulk_queue_background_migration_jobs_by_range(model_class, job_class_name, batch_size: BATCH_SIZE)
raise "#{model_class} does not have an ID to use for batch ranges" unless model_class.column_names.include?('id')
jobs = []
@@ -40,7 +44,7 @@ module Gitlab
model_class.each_batch(of: batch_size) do |relation|
start_id, end_id = relation.pluck("MIN(#{table_name}.id)", "MAX(#{table_name}.id)").first
- if jobs.length >= BACKGROUND_MIGRATION_JOB_BUFFER_SIZE
+ if jobs.length >= JOB_BUFFER_SIZE
# Note: This code path generally only helps with many millions of rows
# We push multiple jobs at a time to reduce the time spent in
# Sidekiq/Redis operations. We're using this buffer based approach so we
@@ -89,7 +93,7 @@ module Gitlab
# # do something
# end
# end
- def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false, primary_column_name: :id)
+ def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false, primary_column_name: :id)
raise "#{model_class} does not have an ID column of #{primary_column_name} to use for batch ranges" unless model_class.column_names.include?(primary_column_name.to_s)
raise "#{primary_column_name} is not an integer column" unless model_class.columns_hash[primary_column_name.to_s].type == :integer
@@ -127,6 +131,79 @@ module Gitlab
final_delay
end
+ # Creates a batched background migration for the given table. A batched migration runs one job
+ # at a time, computing the bounds of the next batch based on the current migration settings and the previous
+ # batch bounds. Each job's execution status is tracked in the database as the migration runs. The given job
+ # class must be present in the Gitlab::BackgroundMigration module, and the batch class (if specified) must be
+ # present in the Gitlab::BackgroundMigration::BatchingStrategies module.
+ #
+ # job_class_name - The background migration job class as a string
+ # batch_table_name - The name of the table the migration will batch over
+ # batch_column_name - The name of the column the migration will batch over
+ # job_arguments - Extra arguments to pass to the job instance when the migration runs
+ # job_interval - The pause interval between each job's execution, minimum of 2 minutes
+ # batch_min_value - The value in the column the batching will begin at
+ # batch_max_value - The value in the column the batching will end at, defaults to `SELECT MAX(batch_column)`
+ # batch_class_name - The name of the class that will be called to find the range of each next batch
+ # batch_size - The maximum number of rows per job
+ # sub_batch_size - The maximum number of rows processed per "iteration" within the job
+ #
+ #
+ # *Returns the created BatchedMigration record*
+ #
+ # Example:
+ #
+ # queue_batched_background_migration(
+ # 'CopyColumnUsingBackgroundMigrationJob',
+ # :events,
+ # :id,
+ # job_interval: 2.minutes,
+ # other_job_arguments: ['column1', 'column2'])
+ #
+ # Where the the background migration exists:
+ #
+ # class Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob
+ # def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, *other_args)
+ # # do something
+ # end
+ # end
+ def queue_batched_background_migration( # rubocop:disable Metrics/ParameterLists
+ job_class_name,
+ batch_table_name,
+ batch_column_name,
+ *job_arguments,
+ job_interval:,
+ batch_min_value: BATCH_MIN_VALUE,
+ batch_max_value: nil,
+ batch_class_name: BATCH_CLASS_NAME,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+
+ job_interval = BATCH_MIN_DELAY if job_interval < BATCH_MIN_DELAY
+
+ batch_max_value ||= connection.select_value(<<~SQL)
+ SELECT MAX(#{connection.quote_column_name(batch_column_name)})
+ FROM #{connection.quote_table_name(batch_table_name)}
+ SQL
+
+ migration_status = batch_max_value.nil? ? :finished : :active
+ batch_max_value ||= batch_min_value
+
+ Gitlab::Database::BackgroundMigration::BatchedMigration.create!(
+ job_class_name: job_class_name,
+ table_name: batch_table_name,
+ column_name: batch_column_name,
+ interval: job_interval,
+ min_value: batch_min_value,
+ max_value: batch_max_value,
+ batch_class_name: batch_class_name,
+ batch_size: batch_size,
+ sub_batch_size: sub_batch_size,
+ job_arguments: job_arguments,
+ status: migration_status)
+ end
+
def perform_background_migration_inline?
Rails.env.test? || Rails.env.development?
end
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index bd1e44a9d82..032f02268bc 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -15,18 +15,21 @@ module QA
element :avatar_counter_content
end
+ view 'app/assets/javascripts/issuable/components/csv_export_modal.vue' do
+ element :export_issuable_modal
+ end
+
view 'app/assets/javascripts/issuable/components/csv_import_export_buttons.vue' do
element :export_as_csv_button
- element :import_issues_dropdown
element :import_from_jira_link
end
- view 'app/views/shared/issuable/csv_export/_modal.html.haml' do
- element :export_issues_button
+ view 'app/views/projects/issues/import_csv/_button.html.haml' do
+ element :import_issues_button
end
- view 'app/assets/javascripts/issuable/components/csv_export_modal.vue' do
- element :export_issuable_modal
+ view 'app/views/shared/issuable/csv_export/_modal.html.haml' do
+ element :export_issues_button
end
view 'app/views/shared/issuable/_nav.html.haml' do
@@ -60,7 +63,7 @@ module QA
def click_import_issues_dropdown
# When there are no issues, the image that loads causes the buttons to jump
has_loaded_all_images?
- click_element(:import_issues_dropdown)
+ click_element(:import_issues_button)
end
def export_issues_modal
diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb
index a0fe9f0f310..1a0f35846fe 100644
--- a/spec/experiments/application_experiment_spec.rb
+++ b/spec/experiments/application_experiment_spec.rb
@@ -61,38 +61,19 @@ RSpec.describe ApplicationExperiment, :experiment do
it "tracks the assignment" do
expect(subject).to receive(:track).with(:assignment)
- subject.publish(nil)
+ subject.publish
end
- context "when inside a request cycle" do
- before do
- subject.context.instance_variable_set(:@request, double('Request', headers: 'true'))
- end
+ it "pushes the experiment knowledge into the client using Gon" do
+ expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => subject.signature } }, true)
- it "pushes the experiment knowledge into the client using Gon" do
- expect(Gon).to receive(:push).with(
- {
- experiment: {
- 'namespaced/stub' => { # string key because it can be namespaced
- experiment: 'namespaced/stub',
- key: '86208ac54ca798e11f127e8b23ec396a',
- variant: 'control'
- }
- }
- },
- true
- )
-
- subject.publish(nil)
- end
+ subject.publish
end
- context "when outside a request cycle" do
- it "does not push to gon when outside request cycle" do
- expect(Gon).not_to receive(:push)
+ it "handles when Gon raises exceptions (like when it can't be pushed into)" do
+ expect(Gon).to receive(:push).and_raise(NoMethodError)
- subject.publish(nil)
- end
+ expect { subject.publish }.not_to raise_error
end
end
diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
index 885e02ef60f..da19265ce82 100644
--- a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
+++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
@@ -1,6 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
+import service from '~/batch_comments/services/drafts_service';
import * as actions from '~/batch_comments/stores/modules/batch_comments/actions';
import axios from '~/lib/utils/axios_utils';
@@ -201,6 +202,12 @@ describe('Batch comments store actions', () => {
describe('updateDraft', () => {
let getters;
+ service.update = jest.fn();
+ service.update.mockResolvedValue({ data: { id: 1 } });
+
+ const commit = jest.fn();
+ let context;
+ let params;
beforeEach(() => {
getters = {
@@ -208,43 +215,43 @@ describe('Batch comments store actions', () => {
draftsPath: TEST_HOST,
},
};
- });
- it('commits RECEIVE_DRAFT_UPDATE_SUCCESS with returned data', (done) => {
- const commit = jest.fn();
- const context = {
+ context = {
getters,
commit,
};
res = { id: 1 };
mock.onAny().reply(200, res);
+ params = { note: { id: 1 }, noteText: 'test' };
+ });
- actions
- .updateDraft(context, { note: { id: 1 }, noteText: 'test', callback() {} })
- .then(() => {
- expect(commit).toHaveBeenCalledWith('RECEIVE_DRAFT_UPDATE_SUCCESS', { id: 1 });
- })
- .then(done)
- .catch(done.fail);
+ afterEach(() => jest.clearAllMocks());
+
+ it('commits RECEIVE_DRAFT_UPDATE_SUCCESS with returned data', () => {
+ return actions.updateDraft(context, { ...params, callback() {} }).then(() => {
+ expect(commit).toHaveBeenCalledWith('RECEIVE_DRAFT_UPDATE_SUCCESS', { id: 1 });
+ });
});
- it('calls passed callback', (done) => {
- const commit = jest.fn();
- const context = {
- getters,
- commit,
- };
+ it('calls passed callback', () => {
const callback = jest.fn();
- res = { id: 1 };
- mock.onAny().reply(200, res);
+ return actions.updateDraft(context, { ...params, callback }).then(() => {
+ expect(callback).toHaveBeenCalled();
+ });
+ });
- actions
- .updateDraft(context, { note: { id: 1 }, noteText: 'test', callback })
- .then(() => {
- expect(callback).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ it('does not stringify empty position', () => {
+ return actions.updateDraft(context, { ...params, position: {}, callback() {} }).then(() => {
+ expect(service.update.mock.calls[0][1].position).toBeUndefined();
+ });
+ });
+
+ it('stringifies a non-empty position', () => {
+ const position = { test: true };
+ const expectation = JSON.stringify(position);
+ return actions.updateDraft(context, { ...params, position, callback() {} }).then(() => {
+ expect(service.update.mock.calls[0][1].position).toBe(expectation);
+ });
});
});
diff --git a/spec/frontend/notes/components/noteable_note_spec.js b/spec/frontend/notes/components/noteable_note_spec.js
index fe78e086403..112983f3ac2 100644
--- a/spec/frontend/notes/components/noteable_note_spec.js
+++ b/spec/frontend/notes/components/noteable_note_spec.js
@@ -1,5 +1,6 @@
import { mount, createLocalVue } from '@vue/test-utils';
import { escape } from 'lodash';
+import waitForPromises from 'helpers/wait_for_promises';
import NoteActions from '~/notes/components/note_actions.vue';
import NoteBody from '~/notes/components/note_body.vue';
import NoteHeader from '~/notes/components/note_header.vue';
@@ -13,7 +14,7 @@ describe('issue_note', () => {
let wrapper;
const findMultilineComment = () => wrapper.find('[data-testid="multiline-comment"]');
- beforeEach(() => {
+ const createWrapper = (props = {}) => {
store = createStore();
store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
@@ -23,6 +24,7 @@ describe('issue_note', () => {
store,
propsData: {
note,
+ ...props,
},
localVue,
stubs: [
@@ -33,14 +35,18 @@ describe('issue_note', () => {
'multiline-comment-form',
],
});
- });
+ };
afterEach(() => {
wrapper.destroy();
});
describe('mutiline comments', () => {
- it('should render if has multiline comment', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('should render if has multiline comment', async () => {
const position = {
line_range: {
start: {
@@ -69,9 +75,8 @@ describe('issue_note', () => {
line,
});
- return wrapper.vm.$nextTick().then(() => {
- expect(findMultilineComment().text()).toEqual('Comment on lines 1 to 2');
- });
+ await wrapper.vm.$nextTick();
+ expect(findMultilineComment().text()).toBe('Comment on lines 1 to 2');
});
it('should only render if it has everything it needs', () => {
@@ -147,108 +152,151 @@ describe('issue_note', () => {
});
});
- it('should render user information', () => {
- const { author } = note;
- const avatar = wrapper.find(UserAvatarLink);
- const avatarProps = avatar.props();
+ describe('rendering', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
- expect(avatarProps.linkHref).toBe(author.path);
- expect(avatarProps.imgSrc).toBe(author.avatar_url);
- expect(avatarProps.imgAlt).toBe(author.name);
- expect(avatarProps.imgSize).toBe(40);
- });
+ it('should render user information', () => {
+ const { author } = note;
+ const avatar = wrapper.findComponent(UserAvatarLink);
+ const avatarProps = avatar.props();
- it('should render note header content', () => {
- const noteHeader = wrapper.find(NoteHeader);
- const noteHeaderProps = noteHeader.props();
+ expect(avatarProps.linkHref).toBe(author.path);
+ expect(avatarProps.imgSrc).toBe(author.avatar_url);
+ expect(avatarProps.imgAlt).toBe(author.name);
+ expect(avatarProps.imgSize).toBe(40);
+ });
- expect(noteHeaderProps.author).toEqual(note.author);
- expect(noteHeaderProps.createdAt).toEqual(note.created_at);
- expect(noteHeaderProps.noteId).toEqual(note.id);
- });
+ it('should render note header content', () => {
+ const noteHeader = wrapper.findComponent(NoteHeader);
+ const noteHeaderProps = noteHeader.props();
- it('should render note actions', () => {
- const { author } = note;
- const noteActions = wrapper.find(NoteActions);
- const noteActionsProps = noteActions.props();
-
- expect(noteActionsProps.authorId).toBe(author.id);
- expect(noteActionsProps.noteId).toBe(note.id);
- expect(noteActionsProps.noteUrl).toBe(note.noteable_note_url);
- expect(noteActionsProps.accessLevel).toBe(note.human_access);
- expect(noteActionsProps.canEdit).toBe(note.current_user.can_edit);
- expect(noteActionsProps.canAwardEmoji).toBe(note.current_user.can_award_emoji);
- expect(noteActionsProps.canDelete).toBe(note.current_user.can_edit);
- expect(noteActionsProps.canReportAsAbuse).toBe(true);
- expect(noteActionsProps.canResolve).toBe(false);
- expect(noteActionsProps.reportAbusePath).toBe(note.report_abuse_path);
- expect(noteActionsProps.resolvable).toBe(false);
- expect(noteActionsProps.isResolved).toBe(false);
- expect(noteActionsProps.isResolving).toBe(false);
- expect(noteActionsProps.resolvedBy).toEqual({});
- });
+ expect(noteHeaderProps.author).toBe(note.author);
+ expect(noteHeaderProps.createdAt).toBe(note.created_at);
+ expect(noteHeaderProps.noteId).toBe(note.id);
+ });
- it('should render issue body', () => {
- const noteBody = wrapper.find(NoteBody);
- const noteBodyProps = noteBody.props();
+ it('should render note actions', () => {
+ const { author } = note;
+ const noteActions = wrapper.findComponent(NoteActions);
+ const noteActionsProps = noteActions.props();
- expect(noteBodyProps.note).toEqual(note);
- expect(noteBodyProps.line).toBe(null);
- expect(noteBodyProps.canEdit).toBe(note.current_user.can_edit);
- expect(noteBodyProps.isEditing).toBe(false);
- expect(noteBodyProps.helpPagePath).toBe('');
- });
+ expect(noteActionsProps.authorId).toBe(author.id);
+ expect(noteActionsProps.noteId).toBe(note.id);
+ expect(noteActionsProps.noteUrl).toBe(note.noteable_note_url);
+ expect(noteActionsProps.accessLevel).toBe(note.human_access);
+ expect(noteActionsProps.canEdit).toBe(note.current_user.can_edit);
+ expect(noteActionsProps.canAwardEmoji).toBe(note.current_user.can_award_emoji);
+ expect(noteActionsProps.canDelete).toBe(note.current_user.can_edit);
+ expect(noteActionsProps.canReportAsAbuse).toBe(true);
+ expect(noteActionsProps.canResolve).toBe(false);
+ expect(noteActionsProps.reportAbusePath).toBe(note.report_abuse_path);
+ expect(noteActionsProps.resolvable).toBe(false);
+ expect(noteActionsProps.isResolved).toBe(false);
+ expect(noteActionsProps.isResolving).toBe(false);
+ expect(noteActionsProps.resolvedBy).toEqual({});
+ });
- it('prevents note preview xss', (done) => {
- const imgSrc = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
- const noteBody = `<img src="${imgSrc}" onload="alert(1)" />`;
- const alertSpy = jest.spyOn(window, 'alert');
- store.hotUpdate({
- actions: {
- updateNote() {},
- setSelectedCommentPositionHover() {},
- },
+ it('should render issue body', () => {
+ const noteBody = wrapper.findComponent(NoteBody);
+ const noteBodyProps = noteBody.props();
+
+ expect(noteBodyProps.note).toBe(note);
+ expect(noteBodyProps.line).toBe(null);
+ expect(noteBodyProps.canEdit).toBe(note.current_user.can_edit);
+ expect(noteBodyProps.isEditing).toBe(false);
+ expect(noteBodyProps.helpPagePath).toBe('');
});
- const noteBodyComponent = wrapper.find(NoteBody);
- noteBodyComponent.vm.$emit('handleFormUpdate', noteBody, null, () => {});
+ it('prevents note preview xss', async () => {
+ const noteBody =
+ '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert(1)" />';
+ const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {});
+ const noteBodyComponent = wrapper.findComponent(NoteBody);
+
+ store.hotUpdate({
+ actions: {
+ updateNote() {},
+ setSelectedCommentPositionHover() {},
+ },
+ });
+
+ noteBodyComponent.vm.$emit('handleFormUpdate', noteBody, null, () => {});
- setImmediate(() => {
+ await waitForPromises();
expect(alertSpy).not.toHaveBeenCalled();
- expect(wrapper.vm.note.note_html).toEqual(escape(noteBody));
- done();
+ expect(wrapper.vm.note.note_html).toBe(escape(noteBody));
});
});
describe('cancel edit', () => {
- it('restores content of updated note', (done) => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('restores content of updated note', async () => {
const updatedText = 'updated note text';
store.hotUpdate({
actions: {
updateNote() {},
},
});
- const noteBody = wrapper.find(NoteBody);
+ const noteBody = wrapper.findComponent(NoteBody);
noteBody.vm.resetAutoSave = () => {};
noteBody.vm.$emit('handleFormUpdate', updatedText, null, () => {});
- wrapper.vm
- .$nextTick()
- .then(() => {
- const noteBodyProps = noteBody.props();
-
- expect(noteBodyProps.note.note_html).toBe(updatedText);
- noteBody.vm.$emit('cancelForm');
- })
- .then(() => wrapper.vm.$nextTick())
- .then(() => {
- const noteBodyProps = noteBody.props();
-
- expect(noteBodyProps.note.note_html).toBe(note.note_html);
- })
- .then(done)
- .catch(done.fail);
+ await wrapper.vm.$nextTick();
+ let noteBodyProps = noteBody.props();
+
+ expect(noteBodyProps.note.note_html).toBe(updatedText);
+
+ noteBody.vm.$emit('cancelForm');
+ await wrapper.vm.$nextTick();
+
+ noteBodyProps = noteBody.props();
+
+ expect(noteBodyProps.note.note_html).toBe(note.note_html);
+ });
+ });
+
+ describe('formUpdateHandler', () => {
+ const updateNote = jest.fn();
+ const params = ['', null, jest.fn(), ''];
+
+ const updateActions = () => {
+ store.hotUpdate({
+ actions: {
+ updateNote,
+ setSelectedCommentPositionHover() {},
+ },
+ });
+ };
+
+ afterEach(() => updateNote.mockReset());
+
+ it('responds to handleFormUpdate', () => {
+ createWrapper();
+ updateActions();
+ wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params);
+ expect(wrapper.emitted('handleUpdateNote')).toBeTruthy();
+ });
+
+ it('does not stringify empty position', () => {
+ createWrapper();
+ updateActions();
+ wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params);
+ expect(updateNote.mock.calls[0][1].note.note.position).toBeUndefined();
+ });
+
+ it('stringifies populated position', () => {
+ const position = { test: true };
+ const expectation = JSON.stringify(position);
+ createWrapper({ note: { ...note, position } });
+ updateActions();
+ wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params);
+ expect(updateNote.mock.calls[0][1].note.note.position).toBe(expectation);
});
});
});
diff --git a/spec/initializers/rack_multipart_patch_spec.rb b/spec/initializers/rack_multipart_patch_spec.rb
index e51d7bbeb2d..862fdc7901b 100644
--- a/spec/initializers/rack_multipart_patch_spec.rb
+++ b/spec/initializers/rack_multipart_patch_spec.rb
@@ -42,20 +42,38 @@ EOF
end
context 'with Content-Length over the limit' do
- it 'extracts multipart message' do
- env = Rack::MockRequest.env_for("/", multipart_fixture(:text, 500_000_001))
-
- expect(described_class).to receive(:log_large_multipart?).and_return(true)
- expect(described_class).to receive(:log_multipart_warning).and_call_original
- expect(described_class).to receive(:log_warn).with({
- message: 'Large multipart body detected',
- path: '/',
- content_length: anything,
- correlation_id: anything
- })
- params = described_class.parse_multipart(env)
+ shared_examples 'logs multipart message' do
+ it 'extracts multipart message' do
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:text, length))
- expect(params.keys).to include(*%w(reply fileupload))
+ expect(described_class).to receive(:log_large_multipart?).and_return(true)
+ expect(described_class).to receive(:log_multipart_warning).and_call_original
+ expect(described_class).to receive(:log_warn).with({
+ message: 'Large multipart body detected',
+ path: '/',
+ content_length: anything,
+ correlation_id: anything
+ })
+ params = described_class.parse_multipart(env)
+
+ expect(params.keys).to include(*%w(reply fileupload))
+ end
+ end
+
+ context 'from environment' do
+ let(:length) { 1001 }
+
+ before do
+ stub_env('RACK_MULTIPART_LOGGING_BYTES', 1000)
+ end
+
+ it_behaves_like 'logs multipart message'
+ end
+
+ context 'default limit' do
+ let(:length) { 100_000_001 }
+
+ it_behaves_like 'logs multipart message'
end
end
end
diff --git a/spec/lib/banzai/filter/custom_emoji_filter_spec.rb b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb
index 82e399fff9b..5e76e8164dd 100644
--- a/spec/lib/banzai/filter/custom_emoji_filter_spec.rb
+++ b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb
@@ -10,6 +10,10 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter do
let_it_be(:custom_emoji) { create(:custom_emoji, name: 'tanuki', group: group) }
let_it_be(:custom_emoji2) { create(:custom_emoji, name: 'happy_tanuki', group: group, file: 'https://foo.bar/happy.png') }
+ it_behaves_like 'emoji filter' do
+ let(:emoji_name) { ':tanuki:' }
+ end
+
it 'replaces supported name custom emoji' do
doc = filter('<p>:tanuki:</p>', project: project)
@@ -17,25 +21,12 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter do
expect(doc.css('gl-emoji img').size).to eq 1
end
- it 'ignores non existent custom emoji' do
- exp = '<p>:foo:</p>'
- doc = filter(exp)
-
- expect(doc.to_html).to eq(exp)
- end
-
it 'correctly uses the custom emoji URL' do
doc = filter('<p>:tanuki:</p>')
expect(doc.css('img').first.attributes['src'].value).to eq(custom_emoji.file)
end
- it 'matches with adjacent text' do
- doc = filter('tanuki (:tanuki:)')
-
- expect(doc.css('img').size).to eq 1
- end
-
it 'matches multiple same custom emoji' do
doc = filter(':tanuki: :tanuki:')
@@ -54,18 +45,6 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter do
expect(doc.css('img').size).to be 0
end
- it 'keeps whitespace intact' do
- doc = filter('This deserves a :tanuki:, big time.')
-
- expect(doc.to_html).to match(/^This deserves a <gl-emoji.+>, big time\.\z/)
- end
-
- it 'does not match emoji in a string' do
- doc = filter("'2a00:tanuki:100::1'")
-
- expect(doc.css('gl-emoji').size).to eq 0
- end
-
it 'does not do N+1 query' do
create(:custom_emoji, name: 'party-parrot', group: group)
@@ -77,10 +56,4 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter do
filter('<p>:tanuki: :party-parrot:</p>')
end.not_to exceed_all_query_limit(control_count.count)
end
-
- context 'ancestor tags' do
- let(:emoji_name) { ':tanuki:' }
-
- it_behaves_like 'ignored ancestor tags'
- end
end
diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb
index d76a36b24e1..cb0b470eaa1 100644
--- a/spec/lib/banzai/filter/emoji_filter_spec.rb
+++ b/spec/lib/banzai/filter/emoji_filter_spec.rb
@@ -5,6 +5,10 @@ require 'spec_helper'
RSpec.describe Banzai::Filter::EmojiFilter do
include FilterSpecHelper
+ it_behaves_like 'emoji filter' do
+ let(:emoji_name) { ':+1:' }
+ end
+
it 'replaces supported name emoji' do
doc = filter('<p>:heart:</p>')
expect(doc.css('gl-emoji').first.text).to eq '❤'
@@ -15,12 +19,6 @@ RSpec.describe Banzai::Filter::EmojiFilter do
expect(doc.css('gl-emoji').first.text).to eq '❤'
end
- it 'ignores unsupported emoji' do
- exp = act = '<p>:foo:</p>'
- doc = filter(act)
- expect(doc.to_html).to match Regexp.escape(exp)
- end
-
it 'ignores unicode versions of trademark, copyright, and registered trademark' do
exp = act = '<p>™ © ®</p>'
doc = filter(act)
@@ -65,11 +63,6 @@ RSpec.describe Banzai::Filter::EmojiFilter do
expect(doc.css('gl-emoji').size).to eq 1
end
- it 'matches with adjacent text' do
- doc = filter('+1 (:+1:)')
- expect(doc.css('gl-emoji').size).to eq 1
- end
-
it 'unicode matches with adjacent text' do
doc = filter('+1 (👍)')
expect(doc.css('gl-emoji').size).to eq 1
@@ -90,12 +83,6 @@ RSpec.describe Banzai::Filter::EmojiFilter do
expect(doc.css('gl-emoji').size).to eq 6
end
- it 'does not match emoji in a string' do
- doc = filter("'2a00:a4c0:100::1'")
-
- expect(doc.css('gl-emoji').size).to eq 0
- end
-
it 'has a data-name attribute' do
doc = filter(':-1:')
expect(doc.css('gl-emoji').first.attr('data-name')).to eq 'thumbsdown'
@@ -106,21 +93,9 @@ RSpec.describe Banzai::Filter::EmojiFilter do
expect(doc.css('gl-emoji').first.attr('data-unicode-version')).to eq '6.0'
end
- it 'keeps whitespace intact' do
- doc = filter('This deserves a :+1:, big time.')
-
- expect(doc.to_html).to match(/^This deserves a <gl-emoji.+>, big time\.\z/)
- end
-
it 'unicode keeps whitespace intact' do
doc = filter('This deserves a 🎱, big time.')
expect(doc.to_html).to match(/^This deserves a <gl-emoji.+>, big time\.\z/)
end
-
- context 'ancestor tags' do
- let(:emoji_name) { ':see_no_evil:' }
-
- it_behaves_like 'ignored ancestor tags'
- end
end
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index 3e8563376ce..e25e4af2e86 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
context 'with enough rows to bulk queue jobs more than once' do
before do
- stub_const('Gitlab::Database::Migrations::BackgroundMigrationHelpers::BACKGROUND_MIGRATION_JOB_BUFFER_SIZE', 1)
+ stub_const('Gitlab::Database::Migrations::BackgroundMigrationHelpers::JOB_BUFFER_SIZE', 1)
end
it 'queues jobs correctly' do
@@ -262,6 +262,120 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
+ describe '#queue_batched_background_migration' do
+ it 'creates the database record for the migration' do
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ job_interval: 5.minutes,
+ batch_min_value: 5,
+ batch_max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
+ job_class_name: 'MyJobClass',
+ table_name: 'projects',
+ column_name: 'id',
+ interval: 300,
+ min_value: 5,
+ max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10,
+ job_arguments: %w[],
+ status: 'active')
+ end
+
+ context 'when the job interval is lower than the minimum' do
+ let(:minimum_delay) { described_class::BATCH_MIN_DELAY }
+
+ it 'sets the job interval to the minimum value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: minimum_delay - 1.minute)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.interval).to eq(minimum_delay)
+ end
+ end
+
+ context 'when additional arguments are passed to the method' do
+ it 'saves the arguments on the database record' do
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ 'my',
+ 'arguments',
+ job_interval: 5.minutes,
+ batch_max_value: 1000)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
+ job_class_name: 'MyJobClass',
+ table_name: 'projects',
+ column_name: 'id',
+ interval: 300,
+ min_value: 1,
+ max_value: 1000,
+ job_arguments: %w[my arguments])
+ end
+ end
+
+ context 'when the max_value is not given' do
+ context 'when records exist in the database' do
+ let!(:event1) { create(:event) }
+ let!(:event2) { create(:event) }
+ let!(:event3) { create(:event) }
+
+ it 'creates the record with the current max value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.max_value).to eq(event3.id)
+ end
+
+ it 'creates the record with an active status' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_active
+ end
+ end
+
+ context 'when the database is empty' do
+ it 'sets the max value to the min value' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
+
+ expect(created_migration.max_value).to eq(created_migration.min_value)
+ end
+
+ it 'creates the record with a finished status' do
+ expect do
+ model.queue_batched_background_migration('MyJobClass', :projects, :id, job_interval: 5.minutes)
+ end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_finished
+ end
+ end
+ end
+ end
+
describe '#migrate_async' do
it 'calls BackgroundMigrationWorker.perform_async' do
expect(BackgroundMigrationWorker).to receive(:perform_async).with("Class", "hello", "world")
diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb
index ebb10e991ad..9cf998a0639 100644
--- a/spec/models/packages/package_file_spec.rb
+++ b/spec/models/packages/package_file_spec.rb
@@ -62,6 +62,21 @@ RSpec.describe Packages::PackageFile, type: :model do
end
end
+ describe '.for_rubygem_with_file_name' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:non_ruby_package) { create(:nuget_package, project: project, package_type: :nuget) }
+ let_it_be(:ruby_package) { create(:rubygems_package, project: project, package_type: :rubygems) }
+ let_it_be(:file_name) { 'other.gem' }
+
+ let_it_be(:non_ruby_file) { create(:package_file, :nuget, package: non_ruby_package, file_name: file_name) }
+ let_it_be(:gem_file1) { create(:package_file, :gem, package: ruby_package) }
+ let_it_be(:gem_file2) { create(:package_file, :gem, package: ruby_package, file_name: file_name) }
+
+ it 'returns the matching gem file only for ruby packages' do
+ expect(described_class.for_rubygem_with_file_name(project, file_name)).to contain_exactly(gem_file2)
+ end
+ end
+
describe '#update_file_store callback' do
let_it_be(:package_file) { build(:package_file, :nuget, size: nil) }
diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb
index 75aa7954ca3..d6ad8186063 100644
--- a/spec/requests/api/rubygem_packages_spec.rb
+++ b/spec/requests/api/rubygem_packages_spec.rb
@@ -108,11 +108,68 @@ RSpec.describe API::RubygemPackages do
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/gems/:file_name' do
- let(:url) { api("/projects/#{project.id}/packages/rubygems/gems/my_gem-1.0.0.gem") }
+ let_it_be(:package_name) { 'package' }
+ let_it_be(:version) { '0.0.1' }
+ let_it_be(:package) { create(:rubygems_package, project: project, name: package_name, version: version) }
+ let_it_be(:file_name) { "#{package_name}-#{version}.gem" }
+
+ let(:url) { api("/projects/#{project.id}/packages/rubygems/gems/#{file_name}") }
subject { get(url, headers: headers) }
- it_behaves_like 'an unimplemented route'
+ context 'with valid project' do
+ where(:visibility, :user_role, :member, :token_type, :valid_token, :shared_examples_name, :expected_status) do
+ :public | :developer | true | :personal_access_token | true | 'Rubygems gem download' | :success
+ :public | :guest | true | :personal_access_token | true | 'Rubygems gem download' | :success
+ :public | :developer | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :guest | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :developer | false | :personal_access_token | true | 'Rubygems gem download' | :success
+ :public | :guest | false | :personal_access_token | true | 'Rubygems gem download' | :success
+ :public | :developer | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :guest | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :anonymous | false | :personal_access_token | true | 'Rubygems gem download' | :success
+ :private | :developer | true | :personal_access_token | true | 'Rubygems gem download' | :success
+ :private | :guest | true | :personal_access_token | true | 'rejects rubygems packages access' | :forbidden
+ :private | :developer | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :guest | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :developer | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
+ :private | :guest | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
+ :private | :developer | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :guest | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :anonymous | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
+ :public | :developer | true | :job_token | true | 'Rubygems gem download' | :success
+ :public | :guest | true | :job_token | true | 'Rubygems gem download' | :success
+ :public | :developer | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :guest | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :developer | false | :job_token | true | 'Rubygems gem download' | :success
+ :public | :guest | false | :job_token | true | 'Rubygems gem download' | :success
+ :public | :developer | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :guest | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :developer | true | :job_token | true | 'Rubygems gem download' | :success
+ :private | :guest | true | :job_token | true | 'rejects rubygems packages access' | :forbidden
+ :private | :developer | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :guest | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :developer | false | :job_token | true | 'rejects rubygems packages access' | :not_found
+ :private | :guest | false | :job_token | true | 'rejects rubygems packages access' | :not_found
+ :private | :developer | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :guest | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
+ :public | :developer | true | :deploy_token | true | 'Rubygems gem download' | :success
+ :public | :developer | true | :deploy_token | false | 'rejects rubygems packages access' | :unauthorized
+ :private | :developer | true | :deploy_token | true | 'Rubygems gem download' | :success
+ :private | :developer | true | :deploy_token | false | 'rejects rubygems packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
+ let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
end
describe 'POST /api/v4/projects/:project_id/packages/rubygems/api/v1/gems/authorize' do
@@ -171,7 +228,7 @@ RSpec.describe API::RubygemPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
- project.update!(visibility: visibility.to_s)
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
@@ -249,7 +306,7 @@ RSpec.describe API::RubygemPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
- project.update!(visibility: visibility.to_s)
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
diff --git a/spec/support/shared_examples/banzai/filters/emoji_shared_examples.rb b/spec/support/shared_examples/banzai/filters/emoji_shared_examples.rb
new file mode 100644
index 00000000000..da305f5ccaa
--- /dev/null
+++ b/spec/support/shared_examples/banzai/filters/emoji_shared_examples.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'emoji filter' do
+ it 'keeps whitespace intact' do
+ doc = filter("This deserves a #{emoji_name}, big time.")
+
+ expect(doc.to_html).to match(/^This deserves a <gl-emoji.+>, big time\.\z/)
+ end
+
+ it 'does not match emoji in a string' do
+ doc = filter("'2a00:a4c0#{emoji_name}:1'")
+
+ expect(doc.css('gl-emoji')).to be_empty
+ end
+
+ it 'ignores non existent/unsupported emoji' do
+ exp = '<p>:foo:</p>'
+ doc = filter(exp)
+
+ expect(doc.to_html).to eq(exp)
+ end
+
+ it 'matches with adjacent text' do
+ doc = filter("#{emoji_name.delete(':')} (#{emoji_name})")
+
+ expect(doc.css('gl-emoji').size).to eq 1
+ end
+
+ it 'does not match emoji in a pre tag' do
+ doc = filter("<p><pre>#{emoji_name}</pre></p>")
+
+ expect(doc.css('img')).to be_empty
+ end
+
+ it 'does not match emoji in code tag' do
+ doc = filter("<p><code>#{emoji_name} wow</code></p>")
+
+ expect(doc.css('img')).to be_empty
+ end
+
+ it 'does not match emoji in tt tag' do
+ doc = filter("<p><tt>#{emoji_name} yes!</tt></p>")
+
+ expect(doc.css('img')).to be_empty
+ end
+end
diff --git a/spec/support/shared_examples/banzai/filters/ignored_tags_emoji_shared_examples.rb b/spec/support/shared_examples/banzai/filters/ignored_tags_emoji_shared_examples.rb
deleted file mode 100644
index 89d7cdc4bba..00000000000
--- a/spec/support/shared_examples/banzai/filters/ignored_tags_emoji_shared_examples.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'ignored ancestor tags' do
- it 'does not match emoji in a pre tag' do
- doc = filter("<p><pre>#{emoji_name}</pre></p>")
-
- expect(doc.css('img').size).to eq 0
- end
-
- it 'does not match emoji in code tag' do
- doc = filter("<p><code>#{emoji_name} wow</code></p>")
-
- expect(doc.css('img').size).to eq 0
- end
-
- it 'does not match emoji in tt tag' do
- doc = filter("<p><tt>#{emoji_name} yes!</tt></p>")
-
- expect(doc.css('img').size).to eq 0
- end
-end
diff --git a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
index 2b93d174653..2e3a3ce6b41 100644
--- a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
@@ -66,9 +66,7 @@ RSpec.shared_examples 'boards create mutation' do
context 'when the Boards::CreateService returns an error response' do
before do
- allow_next_instance_of(Boards::CreateService) do |service|
- allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'There was an error.'))
- end
+ params[:name] = ''
end
it 'does not create a board' do
@@ -80,7 +78,7 @@ RSpec.shared_examples 'boards create mutation' do
expect(mutation_response).to have_key('board')
expect(mutation_response['board']).to be_nil
- expect(mutation_response['errors'].first).to eq('There was an error.')
+ expect(mutation_response['errors'].first).to eq('There was an error when creating a board.')
end
end
end
diff --git a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
index cd4cb99d8a5..15fb6611b90 100644
--- a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
@@ -175,3 +175,20 @@ RSpec.shared_examples 'dependency endpoint success' do |user_type, status, add_m
end
end
end
+
+RSpec.shared_examples 'Rubygems gem download' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it 'returns the gem', :aggregate_failures do
+ subject
+
+ expect(response.media_type).to eq('application/octet-stream')
+ expect(response).to have_gitlab_http_status(status)
+ end
+
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ end
+end