Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-04 21:08:59 +00:00
parent 60bb1b9734
commit 6609e5ea75
34 changed files with 538 additions and 192 deletions

View File

@ -41,7 +41,7 @@ export default {
isMergeTrain: {
type: Boolean,
required: false,
default: true,
default: false,
},
},
data() {

View File

@ -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 });

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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'

View File

@ -0,0 +1,5 @@
---
title: Cleanup incorrect data in projects.has_external_issue_tracker
merge_request: 53936
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix 500 error for long commit messages
merge_request: 55320
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Correctly style Dark Mode application header in profile preferences
merge_request: 54575
author: Simon Stieger @sim0
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Batch-load vulnerability findings by UUID
merge_request: 55642
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Add index for pages migration
merge_request: 55757
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Include shared with groups in list of authorized groups
merge_request: 54894
author:
type: fixed

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
1ff1256d2deac0a1545ef7db30d8ba7969265d6c2df62f6bd20f9f1721a482cb

View File

@ -0,0 +1 @@
b2dad27276941e17248f86764196525bd91b088eed78ad7aa6ae2e5a2c9e82bd

View File

@ -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));

View File

@ -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.

View File

@ -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`).

View File

@ -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.

View File

@ -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**.

View File

@ -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
```

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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();

View File

@ -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();
});
});
});

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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