提交 adfb0e18 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 08b3b980
......@@ -12,7 +12,7 @@ Actionable insights always have a follow-up action that needs to take place as a
#### Description
- [ ] Provide some brief detials on the actionable insight and the action to take
- [ ] Provide some brief details on the actionable insight and the action to take
-------------------------------------------------------------------------------
......@@ -28,4 +28,4 @@ Actionable insights always have a follow-up action that needs to take place as a
~"Actionable Insight"
/label ~"Actionable Insight"
......@@ -16,9 +16,8 @@
## Author's checklist (required)
- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
- If you have **Developer** permissions or higher:
- [ ] Ensure that the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) is added to doc's `h1`.
- [ ] Ensure that the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) is added to doc's `h1`.
- [ ] Apply the ~documentation label, plus:
- The corresponding DevOps stage and group labels, if applicable.
- ~"development guidelines" when changing docs under `doc/development/*`, `CONTRIBUTING.md`, or `README.md`.
......
......@@ -334,17 +334,32 @@ export function getWebSocketUrl(path) {
* Convert search query into an object
*
* @param {String} query from "document.location.search"
* @param {Object} options
* @param {Boolean} options.gatherArrays - gather array values into an Array
* @returns {Object}
*
* ex: "?one=1&two=2" into {one: 1, two: 2}
*/
export function queryToObject(query) {
export function queryToObject(query, options = {}) {
const { gatherArrays = false } = options;
const removeQuestionMarkFromQuery = String(query).startsWith('?') ? query.slice(1) : query;
return removeQuestionMarkFromQuery.split('&').reduce((accumulator, curr) => {
const [key, value] = curr.split('=');
if (value !== undefined) {
accumulator[decodeURIComponent(key)] = decodeURIComponent(value);
if (value === undefined) {
return accumulator;
}
const decodedValue = decodeURIComponent(value);
if (gatherArrays && key.endsWith('[]')) {
const decodedKey = decodeURIComponent(key.slice(0, -2));
if (!Array.isArray(accumulator[decodedKey])) {
accumulator[decodedKey] = [];
}
accumulator[decodedKey].push(decodedValue);
} else {
accumulator[decodeURIComponent(key)] = decodedValue;
}
return accumulator;
}, {});
}
......
......@@ -427,6 +427,8 @@ class ApplicationSetting < ApplicationRecord
end
def self.create_from_defaults
check_schema!
transaction(requires_new: true) do
super
end
......@@ -435,6 +437,22 @@ class ApplicationSetting < ApplicationRecord
current_without_cache
end
# Due to the frequency with which settings are accessed, it is
# likely that during a backup restore a running GitLab process
# will insert a new `application_settings` row before the
# constraints have been added to the table. This would add an
# extra row with ID 1 and prevent the primary key constraint from
# being added, which made ActiveRecord throw a
# IrreversibleOrderError anytime the settings were accessed
# (https://gitlab.com/gitlab-org/gitlab/-/issues/36405). To
# prevent this from happening, we do a sanity check that the
# primary key constraint is present before inserting a new entry.
def self.check_schema!
return if ActiveRecord::Base.connection.primary_key(self.table_name).present?
raise "The `#{self.table_name}` table is missing a primary key constraint in the database schema"
end
# By default, the backend is Rails.cache, which uses
# ActiveSupport::Cache::RedisStore. Since loading ApplicationSetting
# can cause a significant amount of load on Redis, let's cache it in
......
......@@ -199,7 +199,14 @@ class CommitStatus < ApplicationRecord
end
def group_name
name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
# 'rspec:linux: 1/10' => 'rspec:linux'
common_name = name.to_s.gsub(%r{\d+[\s:\/\\]+\d+\s*}, '')
# 'rspec:linux: [aws, max memory]' => 'rspec:linux'
common_name.gsub!(%r{: \[.*, .*\]\s*\z}, '') if Gitlab::Ci::Features.new_matrix_job_names_enabled?
common_name.strip!
common_name
end
def failed_but_allowed?
......
---
title: Upgrade pages to v1.23.0
merge_request: 40915
author:
type: added
---
title: Fix ActiveRecord::IrreversibleOrderError during restore from backup
merge_request: 40789
author:
type: fixed
---
title: Update Workhorse to v8.44.0
merge_request: 40970
author:
type: other
# Import bare repositories **(CORE ONLY)**
Rake tasks are available to import bare repositories into a GitLab instance.
When migrating from an existing GitLab instance,
and to preserve ownership by users and their namespaces,
please use [our project-based import/export](../user/project/settings/import_export.md).
Note that:
......@@ -14,11 +17,14 @@ Note that:
To import bare repositories into a GitLab instance:
1. Create a new folder to import your Git repositories from. The new folder needs to have Git user
ownership and read/write/execute access for Git user and its group:
1. Create a new folder to import your Git repositories from.
You can also import projects into a (sub)group's namespace,
instead of the administrator's namespace. To do so, create subfolders and
give ownership and read/write/execute permissions of those subfolders to the
`git` user and its group:
```shell
sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-<date>/new_group
sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-$(date "+%Y-%m-%d")/<optional_groupname>/<optional_subgroup>
```
1. Copy your bare repositories inside this newly created folder. Note:
......@@ -26,15 +32,15 @@ To import bare repositories into a GitLab instance:
- Any `.git` repositories found on any of the subfolders will be imported as projects.
- Groups will be created as needed, these could be nested folders.
For example, if we copy the repositories to `/var/opt/gitlab/git-data/repository-import-<date>`,
For example, if we copy the repositories to `/var/opt/gitlab/git-data/repository-import-2020-08-22`,
and repository `A` needs to be under the groups `G1` and `G2`, it must be created under those folders:
`/var/opt/gitlab/git-data/repository-import-<date>/G1/G2/A.git`.
`/var/opt/gitlab/git-data/repository-import-2020-08-22/G1/G2/A.git`.
```shell
sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-<date>/new_group/
sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-$(date "+%Y-%m-%d")/<optional_groupname>/<optional_subgroup>
# Do this once when you are done copying git repositories
sudo chown -R git:git /var/opt/gitlab/git-data/repository-import-<date>
sudo chown -R git:git /var/opt/gitlab/git-data/repository-import-$(date "+%Y-%m-%d")
```
`foo.git` needs to be owned by the `git` user and `git` users group.
......@@ -46,7 +52,7 @@ To import bare repositories into a GitLab instance:
- Omnibus Installation
```shell
sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>']
sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-$(date "+%Y-%m-%d")']
```
- Installation from source. Before running this command you need to change to the directory where
......@@ -54,7 +60,7 @@ To import bare repositories into a GitLab instance:
```shell
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>'] RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-$(date "+%Y-%m-%d")'] RAILS_ENV=production
```
## Example output
......
......@@ -13,6 +13,9 @@ For more details, see [Bulk editing issues and merge requests at the project lev
If you want to update attributes across multiple issues, epics, or merge requests in a group, you
can do it by bulk editing them, that is, editing them together.
NOTE: **Note:**
Only the items visible on the current page are selected for bulk editing (up to 20).
![Bulk editing](img/bulk-editing_v13_2.png)
## Bulk edit issues at the group level
......
......@@ -14,6 +14,9 @@ For more details, see
If you want to update attributes across multiple issues or merge requests, you can do it
by bulk editing them, that is, editing them together.
NOTE: **Note:**
Only the items visible on the current page are selected for bulk editing (up to 20).
![Bulk editing](img/bulk-editing_v13_2.png)
## Bulk edit issues at the project level
......
......@@ -56,6 +56,8 @@ module QA
def fabricate_via_api!
populate(:upstream, :user)
@api_client = Runtime::API::Client.new(:gitlab, is_new_session: false, user: user)
Runtime::Logger.debug("Forking project #{upstream.name} to namespace #{user.username}...")
super
wait_until_forked
......
# frozen_string_literal: true
module QA
RSpec.configure do |rspec|
# This config option will be enabled by default on RSpec 4,
# but for reasons of backwards compatibility, you have to
# set it on RSpec 3.
#
# It causes the host group and examples to inherit metadata
# from the shared context.
rspec.shared_context_metadata_behavior = :apply_to_host_groups
end
RSpec.shared_context "cluster with Prometheus installed", shared_context: :metadata do
RSpec.shared_context "cluster with Prometheus installed" do
before :all do
@cluster = Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create!
@project = Resource::Project.fabricate_via_api! do |project|
......@@ -71,7 +61,7 @@ module QA
end
after :all do
@cluster.remove!
@cluster&.remove!
end
end
end
......@@ -616,6 +616,35 @@ describe('URL utility', () => {
expect(urlUtils.queryToObject(searchQuery)).toEqual({ one: '1', two: '2' });
});
describe('with gatherArrays=false', () => {
it('overwrites values with the same array-key and does not change the key', () => {
const searchQuery = '?one[]=1&one[]=2&two=2&two=3';
expect(urlUtils.queryToObject(searchQuery)).toEqual({ 'one[]': '2', two: '3' });
});
});
describe('with gatherArrays=true', () => {
const options = { gatherArrays: true };
it('gathers only values with the same array-key and strips `[]` from the key', () => {
const searchQuery = '?one[]=1&one[]=2&two=2&two=3';
expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: ['1', '2'], two: '3' });
});
it('overwrites values with the same array-key name', () => {
const searchQuery = '?one=1&one[]=2&two=2&two=3';
expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: ['2'], two: '3' });
});
it('overwrites values with the same key name', () => {
const searchQuery = '?one[]=1&one=2&two=2&two=3';
expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: '2', two: '3' });
});
});
});
describe('objectToQuery', () => {
......
// Node spec helpers
export const buildMockTextNode = literal => {
return {
firstChild: null,
literal,
type: 'text',
};
};
export const buildMockTextNode = literal => ({ literal, type: 'text' });
export const normalTextNode = buildMockTextNode('This is just normal text.');
......@@ -23,17 +17,20 @@ const buildMockUneditableOpenToken = type => {
};
};
const buildMockUneditableCloseToken = type => {
return { type: 'closeTag', tagName: type };
const buildMockTextToken = content => {
return {
type: 'text',
tagName: null,
content,
};
};
export const originToken = {
type: 'text',
tagName: null,
content: '{:.no_toc .hidden-md .hidden-lg}',
};
const buildMockUneditableCloseToken = type => ({ type: 'closeTag', tagName: type });
export const originToken = buildMockTextToken('{:.no_toc .hidden-md .hidden-lg}');
const uneditableOpenToken = buildMockUneditableOpenToken('div');
export const uneditableOpenTokens = [uneditableOpenToken, originToken];
export const uneditableCloseToken = buildMockUneditableCloseToken('div');
export const uneditableOpenTokens = [buildMockUneditableOpenToken('div'), originToken];
export const uneditableCloseTokens = [originToken, uneditableCloseToken];
export const uneditableTokens = [...uneditableOpenTokens, uneditableCloseToken];
......@@ -41,6 +38,7 @@ export const originInlineToken = {
type: 'text',
content: '<i>Inline</i> content',
};
export const uneditableInlineTokens = [
buildMockUneditableOpenToken('a'),
originInlineToken,
......@@ -48,13 +46,9 @@ export const uneditableInlineTokens = [
];
export const uneditableBlockTokens = [
buildMockUneditableOpenToken('div'),
{
type: 'text',
tagName: null,
content: '<div><h1>Some header</h1><p>Some paragraph</p></div>',
},
buildMockUneditableCloseToken('div'),
uneditableOpenToken,
buildMockTextToken('<div><h1>Some header</h1><p>Some paragraph</p></div>'),
uneditableCloseToken,
];
export const attributeDefinition = '{:.no_toc .hidden-md .hidden-lg}';
......@@ -115,6 +115,16 @@ RSpec.describe Gitlab::CurrentSettings do
expect(settings).to have_attributes(settings_from_defaults)
end
context 'when ApplicationSettings does not have a primary key' do
before do
allow(ActiveRecord::Base.connection).to receive(:primary_key).with('application_settings').and_return(nil)
end
it 'raises an exception if ApplicationSettings does not have a primary key' do
expect { described_class.current_application_settings }.to raise_error(/table is missing a primary key constraint/)
end
end
context 'with pending migrations' do
let(:current_settings) { described_class.current_application_settings }
......
......@@ -650,6 +650,16 @@ RSpec.describe ApplicationSetting do
end
end
context 'when ApplicationSettings does not have a primary key' do
before do
allow(ActiveRecord::Base.connection).to receive(:primary_key).with(described_class.table_name).and_return(nil)
end
it 'raises an exception' do
expect { described_class.create_from_defaults }.to raise_error(/table is missing a primary key constraint/)
end
end
describe '#disabled_oauth_sign_in_sources=' do
before do
allow(Devise).to receive(:omniauth_providers).and_return([:github])
......
......@@ -494,6 +494,10 @@ RSpec.describe CommitStatus do
end
describe '#group_name' do
let(:commit_status) do
build(:commit_status, pipeline: pipeline, stage: 'test')
end
subject { commit_status.group_name }
tests = {
......@@ -510,7 +514,19 @@ RSpec.describe CommitStatus do
'rspec:windows 0 : / 1' => 'rspec:windows',
'rspec:windows 0 : / 1 name' => 'rspec:windows name',
'0 1 name ruby' => 'name ruby',
'0 :/ 1 name ruby' => 'name ruby'
'0 :/ 1 name ruby' => 'name ruby',
'rspec: [aws]' => 'rspec: [aws]',
'rspec: [aws] 0/1' => 'rspec: [aws]',
'rspec: [aws, max memory]' => 'rspec',
'rspec:linux: [aws, max memory, data]' => 'rspec:linux',
'rspec: [inception: [something, other thing], value]' => 'rspec',
'rspec:windows 0/1: [name, other]' => 'rspec:windows',
'rspec:windows: [name, other] 0/1' => 'rspec:windows',
'rspec:windows: [name, 0/1] 0/1' => 'rspec:windows',
'rspec:windows: [0/1, name]' => 'rspec:windows',
'rspec:windows: [, ]' => 'rspec:windows',
'rspec:windows: [name]' => 'rspec:windows: [name]',
'rspec:windows: [name,other]' => 'rspec:windows: [name,other]'
}
tests.each do |name, group_name|
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册