Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
60bb1b9734
commit
6609e5ea75
|
@ -41,7 +41,7 @@ export default {
|
|||
isMergeTrain: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
|
@ -44,6 +44,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
isSubmitEnabled: true,
|
||||
darkModeOnCreate: null,
|
||||
darkModeOnSubmit: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -58,6 +60,7 @@ export default {
|
|||
this.formEl.addEventListener('ajax:beforeSend', this.handleLoading);
|
||||
this.formEl.addEventListener('ajax:success', this.handleSuccess);
|
||||
this.formEl.addEventListener('ajax:error', this.handleError);
|
||||
this.darkModeOnCreate = this.darkModeSelected();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.formEl.removeEventListener('ajax:beforeSend', this.handleLoading);
|
||||
|
@ -65,16 +68,27 @@ export default {
|
|||
this.formEl.removeEventListener('ajax:error', this.handleError);
|
||||
},
|
||||
methods: {
|
||||
darkModeSelected() {
|
||||
const theme = this.getSelectedTheme();
|
||||
return theme ? theme.css_class === 'gl-dark' : null;
|
||||
},
|
||||
getSelectedTheme() {
|
||||
const themeId = new FormData(this.formEl).get('user[theme_id]');
|
||||
return this.applicationThemes[themeId] ?? null;
|
||||
},
|
||||
handleLoading() {
|
||||
this.isSubmitEnabled = false;
|
||||
this.darkModeOnSubmit = this.darkModeSelected();
|
||||
},
|
||||
handleSuccess(customEvent) {
|
||||
const formData = new FormData(this.formEl);
|
||||
updateClasses(
|
||||
this.bodyClasses,
|
||||
this.applicationThemes[formData.get('user[theme_id]')].css_class,
|
||||
this.selectedLayout,
|
||||
);
|
||||
// Reload the page if the theme has changed from light to dark mode or vice versa
|
||||
// to correctly load all required styles.
|
||||
const modeChanged = this.darkModeOnCreate ? !this.darkModeOnSubmit : this.darkModeOnSubmit;
|
||||
if (modeChanged) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
updateClasses(this.bodyClasses, this.getSelectedTheme().css_class, this.selectedLayout);
|
||||
const { message = this.$options.i18n.defaultSuccess, type = FLASH_TYPES.NOTICE } =
|
||||
customEvent?.detail?.[0] || {};
|
||||
createFlash({ message, type });
|
||||
|
|
|
@ -84,11 +84,11 @@ body {
|
|||
|
||||
&.gl-dark {
|
||||
.logo-text svg {
|
||||
fill: $gl-text-color;
|
||||
fill: var(--gl-text-color);
|
||||
}
|
||||
|
||||
.navbar-gitlab {
|
||||
background-color: $gray-50;
|
||||
background-color: var(--gray-50);
|
||||
|
||||
.navbar-sub-nav,
|
||||
.navbar-nav {
|
||||
|
@ -97,8 +97,8 @@ body {
|
|||
> a:focus,
|
||||
> button:hover,
|
||||
> button:focus {
|
||||
color: $gl-text-color;
|
||||
background-color: $gray-200;
|
||||
color: var(--gl-text-color);
|
||||
background-color: var(--gray-200);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,21 +106,21 @@ body {
|
|||
li.dropdown.show {
|
||||
> a,
|
||||
> button {
|
||||
color: $gl-text-color;
|
||||
background-color: $gray-200;
|
||||
color: var(--gl-text-color);
|
||||
background-color: var(--gray-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
form {
|
||||
background-color: $gray-100;
|
||||
box-shadow: inset 0 0 0 1px $border-color;
|
||||
background-color: var(--gray-100);
|
||||
box-shadow: inset 0 0 0 1px var(--border-color);
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
background-color: $gray-100;
|
||||
box-shadow: inset 0 0 0 1px $blue-200;
|
||||
background-color: var(--gray-100);
|
||||
box-shadow: inset 0 0 0 1px var(--blue-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -940,11 +940,7 @@ class User < ApplicationRecord
|
|||
# Returns the groups a user has access to, either through a membership or a project authorization
|
||||
def authorized_groups
|
||||
Group.unscoped do
|
||||
if Feature.enabled?(:shared_group_membership_auth, self)
|
||||
authorized_groups_with_shared_membership
|
||||
else
|
||||
authorized_groups_without_shared_membership
|
||||
end
|
||||
authorized_groups_with_shared_membership
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
|
||||
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
|
||||
.form-actions
|
||||
= f.submit _('Save changes'), class: 'btn gl-button btn-success'
|
||||
= link_to _('Cancel'), admin_deploy_keys_path, class: 'btn gl-button btn-cancel'
|
||||
= f.submit _('Save changes'), class: 'btn gl-button btn-confirm'
|
||||
= link_to _('Cancel'), admin_deploy_keys_path, class: 'btn gl-button btn-default btn-cancel'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
- if @deploy_keys.any?
|
||||
%h3.page-title.deploy-keys-title
|
||||
= _('Public deploy keys (%{deploy_keys_count})') % { deploy_keys_count: @deploy_keys.load.size }
|
||||
= link_to _('New deploy key'), new_admin_deploy_key_path, class: 'float-right btn gl-button btn-success btn-md gl-button'
|
||||
= link_to _('New deploy key'), new_admin_deploy_key_path, class: 'float-right btn gl-button btn-confirm btn-md gl-button'
|
||||
.table-holder.deploy-keys-list
|
||||
%table.table
|
||||
%thead
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
|
||||
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
|
||||
.form-actions
|
||||
= f.submit 'Create', class: 'btn gl-button btn-success'
|
||||
= link_to 'Cancel', admin_deploy_keys_path, class: 'btn gl-button btn-cancel'
|
||||
= f.submit 'Create', class: 'btn gl-button btn-confirm'
|
||||
= link_to 'Cancel', admin_deploy_keys_path, class: 'btn gl-button btn-default btn-cancel'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Cleanup incorrect data in projects.has_external_issue_tracker
|
||||
merge_request: 53936
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix 500 error for long commit messages
|
||||
merge_request: 55320
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Correctly style Dark Mode application header in profile preferences
|
||||
merge_request: 54575
|
||||
author: Simon Stieger @sim0
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Batch-load vulnerability findings by UUID
|
||||
merge_request: 55642
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add index for pages migration
|
||||
merge_request: 55757
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Include shared with groups in list of authorized groups
|
||||
merge_request: 54894
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move to btn-confirm from btn-success in admin/deploy_keys directory
|
||||
merge_request: 55267
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: shared_group_membership_auth
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46412
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/224771
|
||||
milestone: '13.6'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: false
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDeployedDeploymentIdIndexToProjectPagesMetadata < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_on_pages_metadata_not_migrated'
|
||||
|
||||
def up
|
||||
add_concurrent_index :project_pages_metadata, :project_id, where: "deployed = TRUE AND pages_deployment_id is NULL", name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :project_pages_metadata, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupProjectsWithBadHasExternalIssueTrackerData < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
TMP_INDEX_NAME = 'tmp_idx_projects_on_id_where_has_external_issue_tracker_is_true'.freeze
|
||||
BATCH_SIZE = 100
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Service < ActiveRecord::Base
|
||||
include EachBatch
|
||||
belongs_to :project
|
||||
|
||||
self.table_name = 'services'
|
||||
self.inheritance_column = :_type_disabled
|
||||
end
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
include EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
end
|
||||
|
||||
def up
|
||||
update_projects_with_active_external_issue_trackers
|
||||
update_projects_without_active_external_issue_trackers
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op : can't go back to incorrect data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_projects_with_active_external_issue_trackers
|
||||
scope = Service.where(active: true, category: 'issue_tracker').where.not(project_id: nil).distinct(:project_id)
|
||||
|
||||
scope.each_batch(of: BATCH_SIZE) do |relation|
|
||||
scope_with_projects = relation
|
||||
.joins(:project)
|
||||
.select('project_id')
|
||||
.merge(Project.where(has_external_issue_tracker: false).where(pending_delete: false))
|
||||
|
||||
execute(<<~SQL)
|
||||
WITH project_ids_to_update (id) AS (
|
||||
#{scope_with_projects.to_sql}
|
||||
)
|
||||
UPDATE projects SET has_external_issue_tracker = true WHERE id IN (SELECT id FROM project_ids_to_update)
|
||||
SQL
|
||||
end
|
||||
end
|
||||
|
||||
def update_projects_without_active_external_issue_trackers
|
||||
# Add a temporary index to speed up the scoping of projects.
|
||||
index_where = <<~SQL
|
||||
"projects"."has_external_issue_tracker" = TRUE
|
||||
AND "projects"."pending_delete" = FALSE
|
||||
SQL
|
||||
|
||||
add_concurrent_index(:projects, :id, where: index_where, name: TMP_INDEX_NAME)
|
||||
|
||||
services_sub_query = Service
|
||||
.select('1')
|
||||
.where('services.project_id = projects.id')
|
||||
.where(category: 'issue_tracker')
|
||||
.where(active: true)
|
||||
|
||||
# 322 projects are scoped in this query on GitLab.com.
|
||||
Project.where(index_where).each_batch(of: BATCH_SIZE) do |relation|
|
||||
relation_with_exists_query = relation.where('NOT EXISTS (?)', services_sub_query)
|
||||
execute(<<~SQL)
|
||||
WITH project_ids_to_update (id) AS (
|
||||
#{relation_with_exists_query.select(:id).to_sql}
|
||||
)
|
||||
UPDATE projects SET has_external_issue_tracker = false WHERE id IN (SELECT id FROM project_ids_to_update)
|
||||
SQL
|
||||
end
|
||||
|
||||
# Drop the temporary index.
|
||||
remove_concurrent_index_by_name(:projects, TMP_INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
1ff1256d2deac0a1545ef7db30d8ba7969265d6c2df62f6bd20f9f1721a482cb
|
|
@ -0,0 +1 @@
|
|||
b2dad27276941e17248f86764196525bd91b088eed78ad7aa6ae2e5a2c9e82bd
|
|
@ -22909,6 +22909,8 @@ CREATE INDEX index_on_namespaces_lower_name ON namespaces USING btree (lower((na
|
|||
|
||||
CREATE INDEX index_on_namespaces_lower_path ON namespaces USING btree (lower((path)::text));
|
||||
|
||||
CREATE INDEX index_on_pages_metadata_not_migrated ON project_pages_metadata USING btree (project_id) WHERE ((deployed = true) AND (pages_deployment_id IS NULL));
|
||||
|
||||
CREATE INDEX index_on_projects_lower_path ON projects USING btree (lower((path)::text));
|
||||
|
||||
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
|
||||
|
|
|
@ -245,3 +245,9 @@ Upload.find_each do |upload|
|
|||
end
|
||||
p "#{uploads_deleted} remote objects were destroyed."
|
||||
```
|
||||
|
||||
### Delete references to missing LFS objects
|
||||
|
||||
If `gitlab-rake gitlab:lfs:check VERBOSE=1` detects LFS objects that exist in the database
|
||||
but not on disk, [follow the procedure in the LFS documentation](../../topics/git/lfs/index.md#missing-lfs-objects)
|
||||
to remove the database entries.
|
||||
|
|
|
@ -237,6 +237,9 @@ Each Vue component has a unique output. This output is always present in the ren
|
|||
Although each method of a Vue component can be tested individually, our goal is to test the output
|
||||
of the render function, which represents the state at all times.
|
||||
|
||||
Visit the [Vue testing guide](https://vuejs.org/v2/guide/testing.html#Unit-Testing) for help
|
||||
testing the rendered output.
|
||||
|
||||
Here's an example of a well structured unit test for [this Vue component](#appendix---vue-component-subject-under-test):
|
||||
|
||||
```javascript
|
||||
|
@ -331,11 +334,6 @@ describe('~/todos/app.vue', () => {
|
|||
});
|
||||
```
|
||||
|
||||
### Test the component's output
|
||||
|
||||
The main return value of a Vue component is the rendered output. In order to test the component we
|
||||
need to test the rendered output. Visit the [Vue testing guide](https://vuejs.org/v2/guide/testing.html#Unit-Testing).
|
||||
|
||||
### Child components
|
||||
|
||||
1. Test any directive that defines if/how child component is rendered (for example, `v-if` and `v-for`).
|
||||
|
|
|
@ -9,110 +9,49 @@ type: index
|
|||
|
||||
# Installation **(FREE SELF)**
|
||||
|
||||
GitLab can be installed in most GNU/Linux distributions and with several
|
||||
GitLab can be installed in most GNU/Linux distributions, and with several
|
||||
cloud providers. To get the best experience from GitLab, you must balance
|
||||
performance, reliability, ease of administration (backups, upgrades, and
|
||||
troubleshooting), and the cost of hosting.
|
||||
|
||||
Depending on your platform, select from the following available methods to
|
||||
install GitLab:
|
||||
|
||||
- [_Omnibus GitLab_](#installing-gitlab-on-linux-using-the-omnibus-gitlab-package-recommended):
|
||||
The official deb/rpm packages that contain a bundle of GitLab and the
|
||||
components it depends on, including PostgreSQL, Redis, and Sidekiq.
|
||||
- [_GitLab Helm chart_](#installing-gitlab-on-kubernetes-via-the-gitlab-helm-charts):
|
||||
The cloud native Helm chart for installing GitLab and all of its components
|
||||
on Kubernetes.
|
||||
- [_Docker_](#installing-gitlab-with-docker): The Omnibus GitLab packages,
|
||||
Dockerized.
|
||||
- [_Source_](#installing-gitlab-from-source): Install GitLab and all of its
|
||||
components from scratch.
|
||||
- [_Cloud provider_](#installing-gitlab-on-cloud-providers): Install directly
|
||||
from platforms like AWS, Azure, and GCP.
|
||||
|
||||
If you're not sure which installation method to use, we recommend you use
|
||||
Omnibus GitLab. The Omnibus GitLab packages are mature,
|
||||
[scalable](../administration/reference_architectures/index.md), and are used
|
||||
today on GitLab.com. The Helm charts are recommended for those who are familiar
|
||||
with Kubernetes.
|
||||
|
||||
## Requirements
|
||||
|
||||
Before you install GitLab, be sure to review the [system requirements](requirements.md).
|
||||
The system requirements include details about the minimum hardware, software,
|
||||
database, and additional requirements to support GitLab.
|
||||
|
||||
## Installing GitLab on Linux using the Omnibus GitLab package (recommended)
|
||||
## Choose the installation method
|
||||
|
||||
The Omnibus GitLab package uses our official deb/rpm repositories, and is
|
||||
recommended for most users.
|
||||
Depending on your platform, select from the following available methods to
|
||||
install GitLab:
|
||||
|
||||
If you need additional scale or resilience, we recommend deploying
|
||||
GitLab as described in our [reference architecture documentation](../administration/reference_architectures/index.md).
|
||||
| Installation method | Description | When to choose |
|
||||
|----------------------------------------------------------------|-------------|----------------|
|
||||
| [Linux package](https://docs.gitlab.com/omnibus/installation/) | The official deb/rpm packages (also known as Omnibus GitLab) that contains a bundle of GitLab and the components it depends on, including PostgreSQL, Redis, and Sidekiq. | This is the recommended method for getting started. The Linux packages are mature, scalable, and are used today on GitLab.com. If you need additional flexibility and resilience, we recommend deploying GitLab as described in the [reference architecture documentation](../administration/reference_architectures/index.md). |
|
||||
| [Helm charts](https://docs.gitlab.com/charts/) | The cloud native Helm chart for installing GitLab and all of its components on Kubernetes. | When installing GitLab on Kubernetes, there are some trade-offs that you need to be aware of: <br/>- Administration and troubleshooting requires Kubernetes knowledge.<br/>- It can be more expensive for smaller installations. The default installation requires more resources than a single node Linux package deployment, as most services are deployed in a redundant fashion.<br/>- There are some feature [limitations to be aware of](https://docs.gitlab.com/charts/#limitations).<br/><br/> Use this method if your infrastructure is built on Kubernetes and you're familiar with how it works. The methods for management, observability, and some concepts are different than traditional deployments. |
|
||||
| [Docker](https://docs.gitlab.com/omnibus/docker/) | The GitLab packages, Dockerized. | Use this method if you're familiar with Docker. |
|
||||
| [Source](installation.md) | Install GitLab and all of its components from scratch. | Use this method if none of the previous methods are available for your platform. Useful for unsupported systems like \*BSD.|
|
||||
| [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit#documentation) | The GitLab Environment toolkit provides a set of automation tools to deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers. | Since GET is in beta and not yet recommended for production use, use this method if you want to test deploying GitLab in scalable environment. |
|
||||
|
||||
[**> Install GitLab using the Omnibus GitLab package.**](https://about.gitlab.com/install/)
|
||||
## Install GitLab on cloud providers
|
||||
|
||||
### GitLab Environment Toolkit (GET)
|
||||
Regardless of the installation method, you can install GitLab on several cloud
|
||||
providers, assuming the cloud provider supports it. Here are several possible installation
|
||||
methods, the majority which use the Linux packages:
|
||||
|
||||
The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit) (GET) provides a set of automation tools to easily deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers.
|
||||
|
||||
It is currently in beta, and is not yet recommended for production use.
|
||||
|
||||
[**> Install a GitLab reference architecture using the GitLab Environment Toolkit.**](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit#documentation)
|
||||
|
||||
## Installing GitLab on Kubernetes via the GitLab Helm charts
|
||||
|
||||
When installing GitLab on Kubernetes, there are some trade-offs that you
|
||||
need to be aware of:
|
||||
|
||||
- Administration and troubleshooting requires Kubernetes knowledge.
|
||||
- It can be more expensive for smaller installations. The default installation
|
||||
requires more resources than a single node Omnibus deployment, as most services
|
||||
are deployed in a redundant fashion.
|
||||
- There are some feature [limitations to be aware of](https://docs.gitlab.com/charts/#limitations).
|
||||
|
||||
Due to these trade-offs, having Kubernetes experience is a requirement for
|
||||
using this method. We recommend being familiar with Kubernetes before using it
|
||||
to deploy GitLab in production. The methods for management, observability, and
|
||||
some concepts are different than traditional deployments.
|
||||
|
||||
[**> Install GitLab on Kubernetes using the GitLab Helm charts.**](https://docs.gitlab.com/charts/)
|
||||
|
||||
## Installing GitLab with Docker
|
||||
|
||||
GitLab maintains a set of official Docker images based on the Omnibus GitLab
|
||||
package.
|
||||
|
||||
[**> Install GitLab using the official GitLab Docker images.**](docker.md)
|
||||
|
||||
## Installing GitLab from source
|
||||
|
||||
If the Omnibus GitLab package isn't available for your distribution, you can
|
||||
install GitLab from source. This can be useful with unsupported systems, like
|
||||
\*BSD. For an overview of the directory structure, see the
|
||||
[structure documentation](installation.md#gitlab-directory-structure).
|
||||
|
||||
[**> Install GitLab from source.**](installation.md)
|
||||
|
||||
## Installing GitLab on cloud providers
|
||||
|
||||
GitLab can be installed on a variety of cloud providers by using any of
|
||||
the above methods, provided the cloud provider supports it.
|
||||
|
||||
- [Install on AWS](aws/index.md): Install Omnibus GitLab on AWS using the community AMIs that GitLab provides.
|
||||
- [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md): Install Omnibus GitLab on a VM in GCP.
|
||||
- [Install GitLab on Azure](azure/index.md): Install Omnibus GitLab from Azure Marketplace.
|
||||
- [Install GitLab on OpenShift](https://docs.gitlab.com/charts/installation/cloud/openshift.html): Install GitLab on OpenShift by using the GitLab Helm charts.
|
||||
- [Install GitLab on DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-gitlab-on-ubuntu-18-04): Install Omnibus GitLab on DigitalOcean.
|
||||
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md):
|
||||
Quickly test any version of GitLab on DigitalOcean using Docker Machine.
|
||||
| Cloud provider | Description |
|
||||
|---------------------------------------------------------------|-------------|
|
||||
| [AWS (HA)](aws/index.md) | Install GitLab on AWS using the community AMIs provided by GitLab. |
|
||||
| [Google Cloud Platform (GCP)](google_cloud_platform/index.md) | Install GitLab on a VM in GCP. |
|
||||
| [Azure](azure/index.md) | Install GitLab from Azure Marketplace. |
|
||||
| [DigitalOcean](https://about.gitlab.com/blog/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Install GitLab on DigitalOcean. You can also [test GitLab on DigitalOcean using Docker Machine](digitaloceandocker.md). |
|
||||
|
||||
## Next steps
|
||||
|
||||
Here are a few resources you might want to check out after completing the
|
||||
installation:
|
||||
|
||||
- [Upload a license](../user/admin_area/license.md) or [start a free trial](https://about.gitlab.com/free-trial/):
|
||||
- [Upload a license](../user/admin_area/license.md) or [start a free trial](https://about.gitlab.com/free-trial/):
|
||||
Activate all GitLab Enterprise Edition functionality with a license.
|
||||
- [Set up runners](https://docs.gitlab.com/runner/): Set up one or more GitLab
|
||||
Runners, the agents that are responsible for all of the GitLab CI/CD features.
|
||||
|
|
|
@ -23,14 +23,12 @@ you never miss a page.
|
|||
|
||||
## Email notifications
|
||||
|
||||
Email notifications are available in projects that have been
|
||||
[configured to create incidents automatically](incidents.md#create-incidents-automatically)
|
||||
for triggered alerts. Project members with the **Owner** or **Maintainer** roles are
|
||||
sent an email notification automatically. (This is not configurable.) To optionally
|
||||
send additional email notifications to project members with the **Developer** role:
|
||||
Email notifications are available in projects for triggered alerts. Project
|
||||
members with the **Owner** or **Maintainer** roles have the option to receive
|
||||
a single email notification for new alerts.
|
||||
|
||||
1. Navigate to **Settings > Operations**.
|
||||
1. Expand the **Incidents** section.
|
||||
1. In the **Alert Integration** tab, select the **Send a separate email notification to Developers**
|
||||
check box.
|
||||
1. In the **Alert Integration** tab, select the checkbox
|
||||
**Send a single email notification to Owners and Maintainers for new alerts**.
|
||||
1. Select **Save changes**.
|
||||
|
|
|
@ -269,3 +269,46 @@ You might choose to do this if you are using an appliance like a <!-- vale gitla
|
|||
GitLab can't verify LFS objects. Pushes then fail if you have GitLab LFS support enabled.
|
||||
|
||||
To stop push failure, LFS support can be disabled in the [Project settings](../../../user/project/settings/index.md), which also disables GitLab LFS value-adds (Verifying LFS objects, UI integration for LFS).
|
||||
|
||||
### Missing LFS objects
|
||||
|
||||
An error about a missing LFS object may occur in either of these situations:
|
||||
|
||||
- When migrating LFS objects from disk to object storage, with error messages like:
|
||||
|
||||
```plaintext
|
||||
ERROR -- : Failed to transfer LFS object
|
||||
006622269c61b41bf14a22bbe0e43be3acf86a4a446afb4250c3794ea47541a7
|
||||
with error: No such file or directory @ rb_sysopen -
|
||||
/var/opt/gitlab/gitlab-rails/shared/lfs-objects/00/66/22269c61b41bf14a22bbe0e43be3acf86a4a446afb4250c3794ea47541a7
|
||||
```
|
||||
|
||||
(Line breaks have been added for legibility.)
|
||||
|
||||
- When running the
|
||||
[integrity check for LFS objects](../../../administration/raketasks/check.md#uploaded-files-integrity)
|
||||
with the `VERBOSE=1` parameter.
|
||||
|
||||
The database can have records for LFS objects which are not on disk. The database entry may
|
||||
[prevent a new copy of the object being pushed](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/49241).
|
||||
To delete these references:
|
||||
|
||||
1. [Start a rails console](../../../administration/operations/rails_console.md).
|
||||
1. Query the object that's reported as missing in the rails console, to return a file path:
|
||||
|
||||
```ruby
|
||||
lfs_object = LfsObject.find_by(oid: '006622269c61b41bf14a22bbe0e43be3acf86a4a446afb4250c3794ea47541a7')
|
||||
lfs_object.file.path
|
||||
```
|
||||
|
||||
1. Check on disk if it exists:
|
||||
|
||||
```shell
|
||||
ls -al /var/opt/gitlab/gitlab-rails/shared/lfs-objects/00/66/22269c61b41bf14a22bbe0e43be3acf86a4a446afb4250c3794ea47541a7
|
||||
```
|
||||
|
||||
1. If the file is not present, remove the database record via the rails console:
|
||||
|
||||
```ruby
|
||||
lfs_object.destroy
|
||||
```
|
||||
|
|
|
@ -102,8 +102,7 @@ as shown in the following table:
|
|||
Secret Detection is performed by a [specific analyzer](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml)
|
||||
during the `secret-detection` job. It runs regardless of your app's programming language.
|
||||
|
||||
The Secret Detection analyzer includes [Gitleaks](https://github.com/zricethezav/gitleaks) and
|
||||
[TruffleHog](https://github.com/dxa4481/truffleHog) checks.
|
||||
The Secret Detection analyzer includes [Gitleaks](https://github.com/zricethezav/gitleaks) checks.
|
||||
|
||||
Note that the Secret Detection analyzer ignores Password-in-URL vulnerabilities if the password
|
||||
begins with a dollar sign (`$`), as this likely indicates the password is an environment variable.
|
||||
|
@ -200,7 +199,7 @@ Secret Detection can be customized by defining available CI/CD variables:
|
|||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211387) in GitLab 13.5.
|
||||
|
||||
You can customize the default secret detection rules provided with GitLab.
|
||||
Customization allows you to exclude rules and add new rules.
|
||||
Customization allows replace the default secret detection rules with rules that you define.
|
||||
|
||||
To create a custom ruleset:
|
||||
|
||||
|
|
|
@ -104,12 +104,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def fetch_last_cached_commits_list
|
||||
cache_key = ['projects', project.id, 'last_commits_list', commit.id, ensured_path, offset, limit]
|
||||
cache_key = ['projects', project.id, 'last_commits', commit.id, ensured_path, offset, limit]
|
||||
|
||||
commits = Rails.cache.fetch(cache_key, expires_in: CACHE_EXPIRE_IN) do
|
||||
repository
|
||||
.list_last_commits_for_tree(commit.id, ensured_path, offset: offset, limit: limit, literal_pathspec: true)
|
||||
.transform_values!(&:to_hash)
|
||||
.transform_values! { |commit| commit_to_hash(commit) }
|
||||
end
|
||||
|
||||
commits.transform_values! { |value| Commit.from_hash(value, project) }
|
||||
|
@ -121,6 +121,12 @@ module Gitlab
|
|||
resolved_commits[commit.id] ||= commit
|
||||
end
|
||||
|
||||
def commit_to_hash(commit)
|
||||
commit.to_hash.tap do |hash|
|
||||
hash[:message] = hash[:message].to_s.truncate_bytes(1.kilobyte, omission: '...')
|
||||
end
|
||||
end
|
||||
|
||||
def commit_path(commit)
|
||||
Gitlab::Routing.url_helpers.project_commit_path(project, commit)
|
||||
end
|
||||
|
|
|
@ -16,11 +16,11 @@ gitlab:
|
|||
gitaly:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1200m
|
||||
memory: 245M
|
||||
cpu: 2400m
|
||||
memory: 1000M
|
||||
limits:
|
||||
cpu: 1800m
|
||||
memory: 367M
|
||||
cpu: 3600m
|
||||
memory: 1500M
|
||||
persistence:
|
||||
size: 10G
|
||||
gitlab-exporter:
|
||||
|
@ -38,10 +38,10 @@ gitlab:
|
|||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 25M
|
||||
memory: 100M
|
||||
limits:
|
||||
cpu: 750m
|
||||
memory: 37.5M
|
||||
memory: 150M
|
||||
maxReplicas: 3
|
||||
hpa:
|
||||
targetAverageValue: 500m
|
||||
|
@ -52,10 +52,10 @@ gitlab:
|
|||
resources:
|
||||
requests:
|
||||
cpu: 855m
|
||||
memory: 1285M
|
||||
memory: 1927M
|
||||
limits:
|
||||
cpu: 1282m
|
||||
memory: 1927M
|
||||
memory: 2890M
|
||||
hpa:
|
||||
targetAverageValue: 650m
|
||||
task-runner:
|
||||
|
@ -138,10 +138,10 @@ postgresql:
|
|||
resources:
|
||||
requests:
|
||||
cpu: 550m
|
||||
memory: 250M
|
||||
memory: 1000M
|
||||
limits:
|
||||
cpu: 825m
|
||||
memory: 375M
|
||||
memory: 1500M
|
||||
prometheus:
|
||||
install: false
|
||||
redis:
|
||||
|
|
|
@ -195,9 +195,7 @@ describe('Pipelines stage component', () => {
|
|||
describe('With merge trains disabled', () => {
|
||||
beforeEach(async () => {
|
||||
mock.onGet(dropdownPath).reply(200, stageReply);
|
||||
createComponent({
|
||||
isMergeTrain: false,
|
||||
});
|
||||
createComponent();
|
||||
|
||||
await openStageDropdown();
|
||||
await axios.waitForAll();
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import IntegrationView from '~/profile/preferences/components/integration_view.vue';
|
||||
import ProfilePreferences from '~/profile/preferences/components/profile_preferences.vue';
|
||||
import { i18n } from '~/profile/preferences/constants';
|
||||
import { integrationViews, userFields, bodyClasses } from '../mock_data';
|
||||
import {
|
||||
integrationViews,
|
||||
userFields,
|
||||
bodyClasses,
|
||||
themes,
|
||||
lightModeThemeId1,
|
||||
darkModeThemeId,
|
||||
lightModeThemeId2,
|
||||
} from '../mock_data';
|
||||
|
||||
const expectedUrl = '/foo';
|
||||
|
||||
|
@ -14,7 +23,7 @@ describe('ProfilePreferences component', () => {
|
|||
integrationViews: [],
|
||||
userFields,
|
||||
bodyClasses,
|
||||
themes: [{ id: 1, css_class: 'foo' }],
|
||||
themes,
|
||||
profilePreferencesPath: '/update-profile',
|
||||
formEl: document.createElement('form'),
|
||||
};
|
||||
|
@ -49,6 +58,30 @@ describe('ProfilePreferences component', () => {
|
|||
return document.querySelector('.flash-container .flash-text');
|
||||
}
|
||||
|
||||
function createThemeInput(themeId = lightModeThemeId1) {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('name', 'user[theme_id]');
|
||||
input.setAttribute('type', 'radio');
|
||||
input.setAttribute('value', themeId.toString());
|
||||
input.setAttribute('checked', 'checked');
|
||||
return input;
|
||||
}
|
||||
|
||||
function createForm(themeInput = createThemeInput()) {
|
||||
const form = document.createElement('form');
|
||||
form.setAttribute('url', expectedUrl);
|
||||
form.setAttribute('method', 'put');
|
||||
form.appendChild(themeInput);
|
||||
return form;
|
||||
}
|
||||
|
||||
function setupBody() {
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('container-fluid');
|
||||
document.body.appendChild(div);
|
||||
document.body.classList.add('content-wrapper');
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setFixtures('<div class="flash-container"></div>');
|
||||
});
|
||||
|
@ -84,30 +117,15 @@ describe('ProfilePreferences component', () => {
|
|||
let form;
|
||||
|
||||
beforeEach(() => {
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('container-fluid');
|
||||
document.body.appendChild(div);
|
||||
document.body.classList.add('content-wrapper');
|
||||
|
||||
form = document.createElement('form');
|
||||
form.setAttribute('url', expectedUrl);
|
||||
form.setAttribute('method', 'put');
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('name', 'user[theme_id]');
|
||||
input.setAttribute('type', 'radio');
|
||||
input.setAttribute('value', '1');
|
||||
input.setAttribute('checked', 'checked');
|
||||
form.appendChild(input);
|
||||
|
||||
setupBody();
|
||||
form = createForm();
|
||||
wrapper = createComponent({ provide: { formEl: form }, attachTo: document.body });
|
||||
|
||||
const beforeSendEvent = new CustomEvent('ajax:beforeSend');
|
||||
form.dispatchEvent(beforeSendEvent);
|
||||
});
|
||||
|
||||
it('disables the submit button', async () => {
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
const button = findSubmitButton();
|
||||
expect(button.props('disabled')).toBe(true);
|
||||
});
|
||||
|
@ -116,7 +134,7 @@ describe('ProfilePreferences component', () => {
|
|||
const successEvent = new CustomEvent('ajax:success');
|
||||
form.dispatchEvent(successEvent);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
const button = findSubmitButton();
|
||||
expect(button.props('disabled')).toBe(false);
|
||||
});
|
||||
|
@ -125,7 +143,7 @@ describe('ProfilePreferences component', () => {
|
|||
const errorEvent = new CustomEvent('ajax:error');
|
||||
form.dispatchEvent(errorEvent);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
const button = findSubmitButton();
|
||||
expect(button.props('disabled')).toBe(false);
|
||||
});
|
||||
|
@ -160,4 +178,89 @@ describe('ProfilePreferences component', () => {
|
|||
expect(findFlashError().innerText.trim()).toEqual(message);
|
||||
});
|
||||
});
|
||||
|
||||
describe('theme changes', () => {
|
||||
const { location } = window;
|
||||
|
||||
let themeInput;
|
||||
let form;
|
||||
|
||||
function setupWrapper() {
|
||||
wrapper = createComponent({ provide: { formEl: form }, attachTo: document.body });
|
||||
}
|
||||
|
||||
function selectThemeId(themeId) {
|
||||
themeInput.setAttribute('value', themeId.toString());
|
||||
}
|
||||
|
||||
function dispatchBeforeSendEvent() {
|
||||
const beforeSendEvent = new CustomEvent('ajax:beforeSend');
|
||||
form.dispatchEvent(beforeSendEvent);
|
||||
}
|
||||
|
||||
function dispatchSuccessEvent() {
|
||||
const successEvent = new CustomEvent('ajax:success');
|
||||
form.dispatchEvent(successEvent);
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
...location,
|
||||
reload: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
window.location = location;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setupBody();
|
||||
themeInput = createThemeInput();
|
||||
form = createForm(themeInput);
|
||||
});
|
||||
|
||||
it('reloads the page when switching from light to dark mode', async () => {
|
||||
selectThemeId(lightModeThemeId1);
|
||||
setupWrapper();
|
||||
|
||||
selectThemeId(darkModeThemeId);
|
||||
dispatchBeforeSendEvent();
|
||||
await nextTick();
|
||||
|
||||
dispatchSuccessEvent();
|
||||
await nextTick();
|
||||
|
||||
expect(window.location.reload).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('reloads the page when switching from dark to light mode', async () => {
|
||||
selectThemeId(darkModeThemeId);
|
||||
setupWrapper();
|
||||
|
||||
selectThemeId(lightModeThemeId1);
|
||||
dispatchBeforeSendEvent();
|
||||
await nextTick();
|
||||
|
||||
dispatchSuccessEvent();
|
||||
await nextTick();
|
||||
|
||||
expect(window.location.reload).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not reload the page when switching between light mode themes', async () => {
|
||||
selectThemeId(lightModeThemeId1);
|
||||
setupWrapper();
|
||||
|
||||
selectThemeId(lightModeThemeId2);
|
||||
dispatchBeforeSendEvent();
|
||||
await nextTick();
|
||||
|
||||
dispatchSuccessEvent();
|
||||
await nextTick();
|
||||
|
||||
expect(window.location.reload).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,3 +18,15 @@ export const userFields = {
|
|||
};
|
||||
|
||||
export const bodyClasses = 'ui-light-indigo ui-light gl-dark';
|
||||
|
||||
export const themes = [
|
||||
{ id: 1, css_class: 'foo' },
|
||||
{ id: 2, css_class: 'bar' },
|
||||
{ id: 3, css_class: 'gl-dark' },
|
||||
];
|
||||
|
||||
export const lightModeThemeId1 = 1;
|
||||
|
||||
export const lightModeThemeId2 = 2;
|
||||
|
||||
export const darkModeThemeId = 3;
|
||||
|
|
|
@ -57,14 +57,12 @@ RSpec.describe Gitlab::TreeSummary do
|
|||
context 'with caching', :use_clean_rails_memory_store_caching do
|
||||
subject { Rails.cache.fetch(key) }
|
||||
|
||||
before do
|
||||
summarized
|
||||
end
|
||||
|
||||
context 'Repository tree cache' do
|
||||
let(:key) { ['projects', project.id, 'content', commit.id, path] }
|
||||
|
||||
it 'creates a cache for repository content' do
|
||||
summarized
|
||||
|
||||
is_expected.to eq([{ file_name: 'a.txt', type: :blob }])
|
||||
end
|
||||
end
|
||||
|
@ -72,11 +70,34 @@ RSpec.describe Gitlab::TreeSummary do
|
|||
context 'Commits list cache' do
|
||||
let(:offset) { 0 }
|
||||
let(:limit) { 25 }
|
||||
let(:key) { ['projects', project.id, 'last_commits_list', commit.id, path, offset, limit] }
|
||||
let(:key) { ['projects', project.id, 'last_commits', commit.id, path, offset, limit] }
|
||||
|
||||
it 'creates a cache for commits list' do
|
||||
summarized
|
||||
|
||||
is_expected.to eq('a.txt' => commit.to_hash)
|
||||
end
|
||||
|
||||
context 'when commit has a very long message' do
|
||||
before do
|
||||
repo.create_file(
|
||||
project.creator,
|
||||
'long.txt',
|
||||
'',
|
||||
message: message,
|
||||
branch_name: project.default_branch_or_master
|
||||
)
|
||||
end
|
||||
|
||||
let(:message) { 'a' * 1025 }
|
||||
let(:expected_message) { message[0...1021] + '...' }
|
||||
|
||||
it 'truncates commit message to 1 kilobyte' do
|
||||
summarized
|
||||
|
||||
is_expected.to include('long.txt' => a_hash_including(message: expected_message))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
require_migration!
|
||||
|
||||
RSpec.describe CleanupProjectsWithBadHasExternalIssueTrackerData, :migration do
|
||||
let(:namespace) { table(:namespaces).create!(name: 'foo', path: 'bar') }
|
||||
let(:projects) { table(:projects) }
|
||||
let(:services) { table(:services) }
|
||||
|
||||
def create_projects!(num)
|
||||
Array.new(num) do
|
||||
projects.create!(namespace_id: namespace.id)
|
||||
end
|
||||
end
|
||||
|
||||
def create_active_external_issue_tracker_integrations!(*projects)
|
||||
projects.each do |project|
|
||||
services.create!(category: 'issue_tracker', project_id: project.id, active: true)
|
||||
end
|
||||
end
|
||||
|
||||
def create_disabled_external_issue_tracker_integrations!(*projects)
|
||||
projects.each do |project|
|
||||
services.create!(category: 'issue_tracker', project_id: project.id, active: false)
|
||||
end
|
||||
end
|
||||
|
||||
def create_active_other_integrations!(*projects)
|
||||
projects.each do |project|
|
||||
services.create!(category: 'not_an_issue_tracker', project_id: project.id, active: true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets `projects.has_external_issue_tracker` correctly' do
|
||||
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
|
||||
|
||||
project_with_an_external_issue_tracker_1,
|
||||
project_with_an_external_issue_tracker_2,
|
||||
project_with_only_a_disabled_external_issue_tracker_1,
|
||||
project_with_only_a_disabled_external_issue_tracker_2,
|
||||
project_without_any_external_issue_trackers_1,
|
||||
project_without_any_external_issue_trackers_2 = create_projects!(6)
|
||||
|
||||
create_active_external_issue_tracker_integrations!(
|
||||
project_with_an_external_issue_tracker_1,
|
||||
project_with_an_external_issue_tracker_2
|
||||
)
|
||||
|
||||
create_disabled_external_issue_tracker_integrations!(
|
||||
project_with_an_external_issue_tracker_1,
|
||||
project_with_an_external_issue_tracker_2,
|
||||
project_with_only_a_disabled_external_issue_tracker_1,
|
||||
project_with_only_a_disabled_external_issue_tracker_2
|
||||
)
|
||||
|
||||
create_active_other_integrations!(
|
||||
project_with_an_external_issue_tracker_1,
|
||||
project_with_an_external_issue_tracker_2,
|
||||
project_without_any_external_issue_trackers_1,
|
||||
project_without_any_external_issue_trackers_2
|
||||
)
|
||||
|
||||
# PG triggers on the services table added in
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51852 will have set
|
||||
# the `has_external_issue_tracker` columns to correct data when the services
|
||||
# records were created above.
|
||||
#
|
||||
# We set the `has_external_issue_tracker` columns for projects to incorrect
|
||||
# data manually below to emulate projects in a state before the PG
|
||||
# triggers were added.
|
||||
project_with_an_external_issue_tracker_2.update!(has_external_issue_tracker: false)
|
||||
project_with_only_a_disabled_external_issue_tracker_2.update!(has_external_issue_tracker: true)
|
||||
project_without_any_external_issue_trackers_2.update!(has_external_issue_tracker: true)
|
||||
|
||||
migrate!
|
||||
|
||||
expected_true = [
|
||||
project_with_an_external_issue_tracker_1,
|
||||
project_with_an_external_issue_tracker_2
|
||||
].each(&:reload).map(&:has_external_issue_tracker)
|
||||
|
||||
expected_not_true = [
|
||||
project_without_any_external_issue_trackers_1,
|
||||
project_without_any_external_issue_trackers_2,
|
||||
project_with_only_a_disabled_external_issue_tracker_1,
|
||||
project_with_only_a_disabled_external_issue_tracker_2
|
||||
].each(&:reload).map(&:has_external_issue_tracker)
|
||||
|
||||
expect(expected_true).to all(eq(true))
|
||||
expect(expected_not_true).to all(be_falsey)
|
||||
end
|
||||
end
|
|
@ -3266,23 +3266,8 @@ RSpec.describe User do
|
|||
create(:group_group_link, shared_group: private_group, shared_with_group: other_group)
|
||||
end
|
||||
|
||||
context 'when shared_group_membership_auth is enabled' do
|
||||
before do
|
||||
stub_feature_flags(shared_group_membership_auth: user)
|
||||
end
|
||||
|
||||
it { is_expected.to include shared_group }
|
||||
it { is_expected.not_to include other_group }
|
||||
end
|
||||
|
||||
context 'when shared_group_membership_auth is disabled' do
|
||||
before do
|
||||
stub_feature_flags(shared_group_membership_auth: false)
|
||||
end
|
||||
|
||||
it { is_expected.not_to include shared_group }
|
||||
it { is_expected.not_to include other_group }
|
||||
end
|
||||
it { is_expected.to include shared_group }
|
||||
it { is_expected.not_to include other_group }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue