Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-02-28 09:08:25 +00:00
parent d3eb1e90a5
commit 5eab6dcdd9
25 changed files with 185 additions and 93 deletions

View File

@ -373,7 +373,7 @@ gem 'prometheus-client-mmap', '~> 0.17', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'
group :development do
gem 'lefthook', '~> 1.2.9', require: false
gem 'lefthook', '~> 1.3.2', require: false
gem 'rubocop'
gem 'solargraph', '~> 0.47.2', require: false

View File

@ -314,7 +314,7 @@
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
{"name":"lefthook","version":"1.2.9","platform":"ruby","checksum":"1fd4a768e08fc624e756597fc628b3c7991267325974a7a5cc169595b425701d"},
{"name":"lefthook","version":"1.3.2","platform":"ruby","checksum":"38607be9d670af5bfbbcb2159459f4403bc8e1b10885a923b9e512b3b72b3dec"},
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},

View File

@ -845,7 +845,7 @@ GEM
kramdown (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
lefthook (1.2.9)
lefthook (1.3.2)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (2.0.0)
@ -1738,7 +1738,7 @@ DEPENDENCIES
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
kubeclient (~> 4.9.3)!
lefthook (~> 1.2.9)
lefthook (~> 1.3.2)
letter_opener_web (~> 2.0.0)
license_finder (~> 7.0)
licensee (~> 9.15)
@ -1893,4 +1893,4 @@ DEPENDENCIES
yajl-ruby (~> 1.4.3)
BUNDLED WITH
2.4.6
2.4.7

View File

@ -22,15 +22,6 @@ export default {
type: String,
required: true,
},
markdownDocsPath: {
type: String,
required: true,
},
quickActionsDocsPath: {
type: String,
required: false,
default: '',
},
uploadsPath: {
type: String,
required: false,
@ -41,21 +32,6 @@ export default {
required: false,
default: true,
},
enablePreview: {
type: Boolean,
required: false,
default: true,
},
autocompleteDataSources: {
type: Object,
required: false,
default: () => ({}),
},
enableAutocomplete: {
type: Boolean,
required: false,
default: true,
},
formFieldProps: {
type: Object,
required: true,
@ -76,14 +52,10 @@ export default {
required: false,
default: false,
},
drawioEnabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
markdown: this.value || '',
editingMode: EDITING_MODE_MARKDOWN_FIELD,
autofocused: false,
};
@ -97,14 +69,21 @@ export default {
return this.autofocus && !this.autofocused ? 'end' : false;
},
},
watch: {
value(val) {
this.markdown = val;
},
},
mounted() {
this.autofocusTextarea();
},
methods: {
updateMarkdownFromContentEditor({ markdown }) {
this.markdown = markdown;
this.$emit('input', markdown);
},
updateMarkdownFromMarkdownField({ target }) {
this.markdown = target.value;
this.$emit('input', target.value);
},
renderMarkdown(markdown) {
@ -143,16 +122,12 @@ export default {
/>
<markdown-field
v-if="!isContentEditorActive"
v-bind="$attrs"
data-testid="markdown-field"
:markdown-preview-path="renderMarkdownPath"
can-attach-file
:enable-autocomplete="enableAutocomplete"
:textarea-value="value"
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:autocomplete-data-sources="autocompleteDataSources"
:textarea-value="markdown"
:uploads-path="uploadsPath"
:enable-preview="enablePreview"
:drawio-enabled="drawioEnabled"
show-content-editor-switcher
class="bordered-box"
@enableContentEditor="onEditingModeChange('contentEditor')"
@ -161,7 +136,7 @@ export default {
<textarea
v-bind="formFieldProps"
ref="textarea"
:value="value"
:value="markdown"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
:data-supports-quick-actions="supportsQuickActions"
@ -176,7 +151,7 @@ export default {
<content-editor
:render-markdown="renderMarkdown"
:uploads-path="uploadsPath"
:markdown="value"
:markdown="markdown"
:autofocus="contentEditorAutofocused"
:use-bottom-toolbar="useBottomToolbar"
@initialized="setEditorAsAutofocused"
@ -186,7 +161,7 @@ export default {
/>
<input
v-bind="formFieldProps"
:value="value"
:value="markdown"
data-qa-selector="markdown_editor_form_field"
type="hidden"
/>

View File

@ -453,7 +453,7 @@ module Ci
new_version = values[:version]
schedule_runner_version_update(new_version) if new_version && values[:version] != version
cache_attributes(values)
merge_cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at`
update_columns(values) if persist_cached_data?

View File

@ -10,7 +10,7 @@ module Ci
ignore_column :machine_xid, remove_with: '15.11', remove_after: '2022-03-22'
# The `UPDATE_CONTACT_COLUMN_EVERY` defines how often the Runner Machine DB entry can be updated
UPDATE_CONTACT_COLUMN_EVERY = 40.minutes..55.minutes
UPDATE_CONTACT_COLUMN_EVERY = (40.minutes)..(55.minutes)
belongs_to :runner
@ -60,7 +60,7 @@ module Ci
new_version = values[:version]
schedule_runner_version_update(new_version) if new_version && values[:version] != version
cache_attributes(values)
merge_cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at`
update_columns(values) if persist_cached_data?

View File

@ -33,6 +33,14 @@ module RedisCacheable
clear_memoization(:cached_attributes)
end
def merge_cache_attributes(values)
existing_attributes = Hash(cached_attributes)
merged_attributes = existing_attributes.merge(values.symbolize_keys)
return if merged_attributes == existing_attributes
cache_attributes(merged_attributes)
end
private
def cache_attribute_key

View File

@ -102,8 +102,6 @@ class User < ApplicationRecord
MINIMUM_DAYS_CREATED = 7
ignore_columns %i[linkedin twitter skype website_url location organization], remove_with: '15.10', remove_after: '2023-02-22'
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
# rubocop: disable CodeReuse/ServiceClass

View File

@ -79,6 +79,7 @@ If you are running GitLab 12.0, enable the `graphql`
GraphQL queries can be run in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session). For example, to search projects:
```ruby
current_user = User.find_by_id(1)
query = <<~EOQ
query securityGetProjects($search: String!) {
projects(search: $search) {

View File

@ -311,9 +311,9 @@ The following table shows the possible return codes for API requests.
| Return values | Description |
|--------------------------|-------------|
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, and the resource itself is returned as JSON. |
| `201 Created` | The `POST` request was successful, and the resource is returned as JSON. |
| `202 Accepted` | The `GET`, `PUT` or `DELETE` request was successful, and the resource is scheduled for processing. |
| `204 No Content` | The server has successfully fulfilled the request, and there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful, and the resource is returned as JSON. |
| `304 Not Modified` | The resource hasn't been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing. For example, the title of an issue is not given. |
| `401 Unauthorized` | The user isn't authenticated. A valid [user token](#authentication) is necessary. |
@ -321,7 +321,7 @@ The following table shows the possible return codes for API requests.
| `404 Not Found` | A resource couldn't be accessed. For example, an ID for a resource couldn't be found. |
| `405 Method Not Allowed` | The request isn't supported. |
| `409 Conflict` | A conflicting resource already exists. For example, creating a project with a name that already exists. |
| `412` | The request was denied. This can happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
| `412 Precondition Failed`| The request was denied. This can happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
| `422 Unprocessable` | The entity couldn't be processed. |
| `429 Too Many Requests` | The user exceeded the [application rate limits](../../administration/instance_limits.md#rate-limits). |
| `500 Server Error` | While handling the request, something went wrong on the server. |

View File

@ -82,10 +82,6 @@ To remove a metric:
1. Create an issue for removing the metric if none exists yet. The issue needs to outline why the metric should be deleted. You can use this issue to document the removal process.
1. Check the following YAML files and verify the metric is not used in an aggregate:
- [`config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/)
- [`ee/config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/aggregates/)
1. Verify the metric is not used to calculate the conversational index. The
conversational index is a measure that reports back to self-managed instances
to inform administrators of the progress of DevOps adoption for the instance.

View File

@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# External issue tracker **(FREE)**
# External issue trackers **(FREE)**
GitLab has an [issue tracker](../user/project/issues/index.md), but you can
configure an external issue tracker per GitLab project.

View File

@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Configure the Jira integration **(FREE)**
# Configure Jira **(FREE)**
You can set up the [Jira integration](index.md#jira-integration)
by configuring your project settings in GitLab.

View File

@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Jira integrations **(FREE)**
# Jira **(FREE)**
If your organization uses [Jira](https://www.atlassian.com/software/jira) issues,
you can [migrate your issues from Jira](../../user/project/import/jira.md) and work

View File

@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Jira integration issue management **(FREE)**
# Jira issue management **(FREE)**
To integrate issue management with Jira, [configure Jira](index.md#jira-integration)
and [enable the integration](configure.md) in GitLab.

View File

@ -4,7 +4,7 @@ group: Commerce Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Marketplace partner integration
# Marketplace partners
GitLab supports automation for selected distribution marketplaces to process sales of GitLab products to authorized
channel partners. Marketplace partners can use the GitLab Marketplace APIs to integrate their systems with GitLab to

View File

@ -5237,6 +5237,11 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
msgid "Approve a pending member"
msgid_plural "Approve %d pending members"
msgstr[0] ""
msgstr[1] ""
msgid "Approve merge request"
msgstr ""
@ -5249,6 +5254,16 @@ msgstr ""
msgid "Approved MRs"
msgstr ""
msgid "Approved members will use an additional seat in your subscription, which may override your user cap."
msgid_plural "Approved members will use an additional %d seats in your subscription, which may override your user cap."
msgstr[0] ""
msgstr[1] ""
msgid "Approved members will use an additional seat in your subscription."
msgid_plural "Approved members will use an additional %d seats in your subscription."
msgstr[0] ""
msgstr[1] ""
msgid "Approved the current merge request."
msgstr ""
@ -5324,9 +5339,6 @@ msgstr ""
msgid "Are you sure you want to approve %{user}?"
msgstr ""
msgid "Are you sure you want to approve all users?"
msgstr ""
msgid "Are you sure you want to attempt to merge?"
msgstr ""

View File

@ -80,17 +80,18 @@ describe('Description field component', () => {
});
it('uses the MarkdownEditor component to edit markdown', () => {
expect(findMarkdownEditor().props()).toEqual(
expect.objectContaining({
value: 'test',
renderMarkdownPath: '/',
markdownDocsPath: '/',
quickActionsDocsPath: expect.any(String),
autofocus: true,
supportsQuickActions: true,
enableAutocomplete: true,
}),
);
expect(findMarkdownEditor().props()).toMatchObject({
value: 'test',
renderMarkdownPath: '/',
autofocus: true,
supportsQuickActions: true,
});
expect(findMarkdownEditor().vm.$attrs).toMatchObject({
'enable-autocomplete': true,
'markdown-docs-path': '/',
'quick-actions-docs-path': expect.any(String),
});
});
it('triggers update with meta+enter', () => {

View File

@ -116,7 +116,6 @@ describe('WikiForm', () => {
expect.objectContaining({
value: pageInfoPersisted.content,
renderMarkdownPath: pageInfoPersisted.markdownPreviewPath,
markdownDocsPath: pageInfoPersisted.markdownHelpPath,
uploadsPath: pageInfoPersisted.uploadsPath,
autofocus: pageInfoPersisted.persisted,
}),
@ -126,6 +125,10 @@ describe('WikiForm', () => {
id: 'wiki_content',
name: 'wiki[content]',
});
expect(markdownEditor.vm.$attrs['markdown-docs-path']).toEqual(
pageInfoPersisted.markdownHelpPath,
);
});
it.each`
@ -172,7 +175,7 @@ describe('WikiForm', () => {
nextTick();
expect(findMarkdownEditor().props('enablePreview')).toBe(enabled);
expect(findMarkdownEditor().vm.$attrs['enable-preview']).toBe(enabled);
});
it.each`

View File

@ -83,8 +83,28 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
});
});
it('passes down any additional props to markdown field component', () => {
const propsData = {
line: { text: 'hello world', richText: 'hello world' },
lines: [{ text: 'hello world', richText: 'hello world' }],
canSuggest: true,
};
buildWrapper({
propsData: { ...propsData, myCustomProp: 'myCustomValue', 'data-testid': 'custom id' },
});
expect(findMarkdownField().props()).toMatchObject(propsData);
expect(findMarkdownField().vm.$attrs).toMatchObject({
myCustomProp: 'myCustomValue',
// data-testid isn't copied over
'data-testid': 'markdown-field',
});
});
it('renders markdown field textarea', () => {
buildWrapper();
buildWrapper({ propsData: { supportsQuickActions: true } });
expect(findTextarea().attributes()).toEqual(
expect.objectContaining({
@ -92,6 +112,7 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
name: formFieldName,
placeholder: formFieldPlaceholder,
'aria-label': formFieldAriaLabel,
'data-supports-quick-actions': 'true',
}),
);

View File

@ -117,10 +117,13 @@ describe('WorkItemDescription', () => {
await createComponent({ isEditing: true });
expect(findMarkdownEditor().props()).toMatchObject({
autocompleteDataSources: autocompleteDataSources(fullPath, iid),
supportsQuickActions: true,
renderMarkdownPath: markdownPreviewPath(fullPath, iid),
quickActionsDocsPath: wrapper.vm.$options.quickActionsDocsPath,
});
expect(findMarkdownEditor().vm.$attrs).toMatchObject({
'autocomplete-data-sources': autocompleteDataSources(fullPath, iid),
'quick-actions-docs-path': wrapper.vm.$options.quickActionsDocsPath,
});
});
});

View File

@ -69,21 +69,15 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new { rewriter.rewrite.load } }
let(:relation) { User.where(state: :active, user_type: %i(support_bot alert_bot)).limit(2) }
let(:users_default_select_fields) do
User.default_select_columns
.map { |field| "\"users\".\"#{field.name}\"" }
.join(',')
end
let(:expected_query) do
<<~SQL
SELECT
#{users_default_select_fields}
"users".*
FROM
unnest('{1,2}'::smallint[]) AS "user_types"("user_type"),
LATERAL (
SELECT
#{users_default_select_fields}
"users".*
FROM
"users"
WHERE
@ -107,13 +101,13 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
#{users_default_select_fields}
"users".*
FROM
unnest(ARRAY(SELECT "users"."state" FROM "users")::character varying[]) AS "states"("state"),
unnest('{1,2}'::smallint[]) AS "user_types"("user_type"),
LATERAL (
SELECT
#{users_default_select_fields}
"users".*
FROM
"users"
WHERE
@ -135,12 +129,12 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
#{users_default_select_fields}
"users".*
FROM
unnest('{active,blocked,banned}'::charactervarying[]) AS "states"("state"),
LATERAL (
SELECT
#{users_default_select_fields}
"users".*
FROM
"users"
WHERE
@ -187,6 +181,8 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
"users".*
FROM
"users"
WHERE
@ -221,7 +217,7 @@ RSpec.describe UnnestedInFilters::Rewriter do
end
it 'changes the query' do
expect(issued_query.gsub(/\s/, '')).to include(expected_query.gsub(/\s/, ''))
expect(issued_query.gsub(/\s/, '')).to start_with(expected_query.gsub(/\s/, ''))
end
end
@ -230,6 +226,8 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
"users".*
FROM
"users"
WHERE
@ -259,7 +257,7 @@ RSpec.describe UnnestedInFilters::Rewriter do
end
it 'does not rewrite the in statement for the joined table' do
expect(issued_query.gsub(/\s/, '')).to include(expected_query.gsub(/\s/, ''))
expect(issued_query.gsub(/\s/, '')).to start_with(expected_query.gsub(/\s/, ''))
end
end

View File

@ -107,6 +107,18 @@ RSpec.describe Ci::RunnerMachine, feature_category: :runner_fleet, type: :model
heartbeat
end
context 'with new version having been cached' do
let(:version) { '15.0.1' }
before do
runner_machine.cache_attributes(version: version)
end
it 'does not lose cached version value' do
expect { heartbeat }.not_to change { runner_machine.version }.from(version)
end
end
end
end

View File

@ -1122,6 +1122,18 @@ RSpec.describe Ci::Runner, type: :model, feature_category: :runner do
heartbeat
end
context 'with new version having been cached' do
let(:version) { '15.0.1' }
before do
runner.cache_attributes(version: version)
end
it 'does not lose cached version value' do
expect { heartbeat }.not_to change { runner.version }.from(version)
end
end
end
end

View File

@ -50,6 +50,58 @@ RSpec.describe RedisCacheable do
subject
end
context 'with existing cached attributes' do
before do
instance.cache_attributes({ existing_attr: 'value' })
end
it 'sets the cache attributes' do
Gitlab::Redis::Cache.with do |redis|
expect(redis).to receive(:set).with(cache_key, payload.to_json, anything).and_call_original
end
expect { subject }.to change { instance.cached_attribute(:existing_attr) }.from('value').to(nil)
end
end
end
describe '#merge_cache_attributes' do
subject { instance.merge_cache_attributes(payload) }
let(:existing_attributes) { { existing_attr: 'value', name: 'value' } }
before do
instance.cache_attributes(existing_attributes)
end
context 'with different attribute values' do
let(:payload) { { name: 'new_value' } }
it 'merges the cache attributes with existing values' do
Gitlab::Redis::Cache.with do |redis|
expect(redis).to receive(:set).with(cache_key, existing_attributes.merge(payload).to_json, anything)
.and_call_original
end
subject
expect(instance.cached_attribute(:existing_attr)).to eq 'value'
expect(instance.cached_attribute(:name)).to eq 'new_value'
end
end
context 'with no new or changed attribute values' do
let(:payload) { { name: 'value' } }
it 'does not try to set Redis key' do
Gitlab::Redis::Cache.with do |redis|
expect(redis).not_to receive(:set)
end
subject
end
end
end
describe '#cached_attr_reader', :clean_gitlab_redis_cache do