Read about our upcoming Code of Conduct on this issue

Commit 2fc5c448 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 7535c4caaf7b
......@@ -20,6 +20,10 @@ def skip_project_check?
false
end
def can_cache_field?(field)
true
end
# Returns the default Banzai render context for the cached markdown field.
def banzai_render_context(field)
raise ArgumentError.new("Unknown field: #{field.inspect}") unless
......@@ -38,17 +42,23 @@ def banzai_render_context(field)
context
end
# Update every column in a row if any one is invalidated, as we only store
# one version per row
def refresh_markdown_cache
def rendered_field_content(markdown_field)
return unless can_cache_field?(markdown_field)
options = { skip_project_check: skip_project_check? }
Banzai::Renderer.cacheless_render_field(self, markdown_field, options)
end
# Update every applicable column in a row if any one is invalidated, as we only store
# one version per row
def refresh_markdown_cache
updates = cached_markdown_fields.markdown_fields.map do |markdown_field|
[
cached_markdown_fields.html_field(markdown_field),
Banzai::Renderer.cacheless_render_field(self, markdown_field, options)
rendered_field_content(markdown_field)
]
end.to_h
updates['cached_markdown_version'] = latest_cached_markdown_version
updates.each { |field, data| write_markdown_field(field, data) }
......
......@@ -406,11 +406,15 @@ def mattermost_team_params
end
def ci_variables_for(ref, project)
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
cache_key = "ci_variables_for:group:#{self&.id}:project:#{project&.id}:ref:#{ref}"
::Gitlab::SafeRequestStore.fetch(cache_key) do
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
end
end
def group_member(user)
......
......@@ -1963,6 +1963,14 @@ def default_environment
end
def ci_variables_for(ref:, environment: nil)
cache_key = "ci_variables_for:project:#{self&.id}:ref:#{ref}:environment:#{environment}"
::Gitlab::SafeRequestStore.fetch(cache_key) do
uncached_ci_variables_for(ref: ref, environment: environment)
end
end
def uncached_ci_variables_for(ref:, environment: nil)
result = if protected_for?(ref)
variables
else
......
......@@ -301,6 +301,10 @@ def track_snippet_repository
repository.update!(shard_name: repository_storage, disk_path: disk_path)
end
def can_cache_field?(field)
field != :content || MarkupHelper.gitlab_markdown?(file_name)
end
class << self
# Searches for snippets with a matching title or file name.
#
......
---
title: Fix Snippet content incorrectly caching
merge_request: 25985
author:
type: fixed
---
title: Add link to dependency proxy docs on the dependency proxy page
merge_request: 26092
author:
type: changed
---
title: 'Geo: Show secondary-only setting on only on secondaries'
merge_request: 26029
author:
type: fixed
---
title: Memoize loading of CI variables
merge_request: 26147
author:
type: performance
......@@ -9,10 +9,6 @@ and [deployments](../../ci/environments.md) when using [Auto DevOps](../../topic
You can install them after you
[create a cluster](../project/clusters/add_remove_clusters.md).
Interested in contributing a new GitLab managed app? Visit the
[development guidelines page](../../development/kubernetes.md#gitlab-managed-apps)
to get started.
## Installing applications
Applications managed by GitLab will be installed onto the `gitlab-managed-apps` namespace.
......@@ -466,6 +462,12 @@ The chart will deploy 5 Elasticsearch nodes: 2 masters, 2 data and 1 client node
with resource requests totalling 0.125 CPU and 4.5GB RAM. Each data node requests 1.5GB of memory,
which makes it incompatible with clusters of `f1-micro` and `g1-small` instance types.
### Future apps
Interested in contributing a new GitLab managed app? Visit the
[development guidelines page](../../development/kubernetes.md#gitlab-managed-apps)
to get started.
## Install using GitLab CI (alpha)
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20822) in GitLab 12.6.
......
......@@ -82,7 +82,7 @@ module.exports = {
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
},
transformIgnorePatterns: ['node_modules/(?!(@gitlab/ui)/)'],
transformIgnorePatterns: ['node_modules/(?!(@gitlab/ui|bootstrap-vue)/)'],
timers: 'fake',
testEnvironment: '<rootDir>/spec/frontend/environment.js',
testEnvironmentOptions: {
......
import $ from 'jquery';
import blobBundle from '~/blob_edit/blob_bundle';
jest.mock('~/blob_edit/edit_blob');
describe('BlobBundle', () => {
beforeEach(() => {
spyOnDependency(blobBundle, 'EditBlob').and.stub();
setFixtures(`
<div class="js-edit-blob-form" data-blob-filename="blah">
<button class="js-commit-button"></button>
......
export const defaultProps = {
endpoint: '/foo/bar/issues/1/related_issues',
currentNamespacePath: 'foo',
currentProjectPath: 'bar',
};
export const issuable1 = {
id: 200,
epicIssueId: 1,
confidential: false,
reference: 'foo/bar#123',
displayReference: '#123',
title: 'some title',
path: '/foo/bar/issues/123',
relationPath: '/foo/bar/issues/123/relation',
state: 'opened',
linkType: 'relates_to',
dueDate: '2010-11-22',
weight: 5,
};
export const issuable2 = {
id: 201,
epicIssueId: 2,
confidential: false,
reference: 'foo/bar#124',
displayReference: '#124',
title: 'some other thing',
path: '/foo/bar/issues/124',
relationPath: '/foo/bar/issues/124/relation',
state: 'opened',
linkType: 'blocks',
};
export const issuable3 = {
id: 202,
epicIssueId: 3,
confidential: false,
reference: 'foo/bar#125',
displayReference: '#125',
title: 'some other other thing',
path: '/foo/bar/issues/125',
relationPath: '/foo/bar/issues/125/relation',
state: 'opened',
linkType: 'is_blocked_by',
};
export const issuable4 = {
id: 203,
epicIssueId: 4,
confidential: false,
reference: 'foo/bar#126',
displayReference: '#126',
title: 'some other other other thing',
path: '/foo/bar/issues/126',
relationPath: '/foo/bar/issues/126/relation',
state: 'opened',
};
export const issuable5 = {
id: 204,
epicIssueId: 5,
confidential: false,
reference: 'foo/bar#127',
displayReference: '#127',
title: 'some other other other thing',
path: '/foo/bar/issues/127',
relationPath: '/foo/bar/issues/127/relation',
state: 'opened',
};
export const defaultMilestone = {
id: 1,
state: 'active',
title: 'Milestone title',
start_date: '2018-01-01',
due_date: '2019-12-31',
};
export const defaultAssignees = [
{
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/root`,
status_tooltip_html: null,
path: '/root',
},
{
id: 13,
name: 'Brooks Beatty',
username: 'brynn_champlin',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/brynn_champlin`,
status_tooltip_html: null,
path: '/brynn_champlin',
},
{
id: 6,
name: 'Bryce Turcotte',
username: 'melynda',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/melynda`,
status_tooltip_html: null,
path: '/melynda',
},
{
id: 20,
name: 'Conchita Eichmann',
username: 'juliana_gulgowski',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/juliana_gulgowski`,
status_tooltip_html: null,
path: '/juliana_gulgowski',
},
];
export const defaultProps = {
endpoint: '/foo/bar/issues/1/related_issues',
currentNamespacePath: 'foo',
currentProjectPath: 'bar',
};
export const issuable1 = {
id: 200,
epicIssueId: 1,
confidential: false,
reference: 'foo/bar#123',
displayReference: '#123',
title: 'some title',
path: '/foo/bar/issues/123',
state: 'opened',
linkType: 'relates_to',
dueDate: '2010-11-22',
weight: 5,
};
export const issuable2 = {
id: 201,
epicIssueId: 2,
confidential: false,
reference: 'foo/bar#124',
displayReference: '#124',
title: 'some other thing',
path: '/foo/bar/issues/124',
state: 'opened',
linkType: 'blocks',
};
export const issuable3 = {
id: 202,
epicIssueId: 3,
confidential: false,
reference: 'foo/bar#125',
displayReference: '#125',
title: 'some other other thing',
path: '/foo/bar/issues/125',
state: 'opened',
linkType: 'is_blocked_by',
};
export const issuable4 = {
id: 203,
epicIssueId: 4,
confidential: false,
reference: 'foo/bar#126',
displayReference: '#126',
title: 'some other other other thing',
path: '/foo/bar/issues/126',
state: 'opened',
};
export const issuable5 = {
id: 204,
epicIssueId: 5,
confidential: false,
reference: 'foo/bar#127',
displayReference: '#127',
title: 'some other other other thing',
path: '/foo/bar/issues/127',
state: 'opened',
};
export const defaultMilestone = {
id: 1,
state: 'active',
title: 'Milestone title',
start_date: '2018-01-01',
due_date: '2019-12-31',
};
export const defaultAssignees = [
{
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/root`,
status_tooltip_html: null,
path: '/root',
},
{
id: 13,
name: 'Brooks Beatty',
username: 'brynn_champlin',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/brynn_champlin`,
status_tooltip_html: null,
path: '/brynn_champlin',
},
{
id: 6,
name: 'Bryce Turcotte',
username: 'melynda',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/melynda`,
status_tooltip_html: null,
path: '/melynda',
},
{
id: 20,
name: 'Conchita Eichmann',
username: 'juliana_gulgowski',
state: 'active',
avatar_url: `${gl.TEST_HOST}`,
web_url: `${gl.TEST_HOST}/juliana_gulgowski`,
status_tooltip_html: null,
path: '/juliana_gulgowski',
},
];
export * from '../../../../frontend/vue_shared/components/issue/related_issuable_mock_data';
......@@ -230,6 +230,26 @@ def thing_subclass(klass, extra_attribute)
end
end
end
describe '#rendered_field_content' do
let(:thing) { klass.new(description: markdown, description_html: nil, cached_markdown_version: cache_version) }
context 'when a field can be cached' do
it 'returns the html' do
thing.description = updated_markdown
expect(thing.rendered_field_content(:description)).to eq updated_html
end
end
context 'when a field cannot be cached' do
it 'returns nil' do
allow(thing).to receive(:can_cache_field?).with(:description).and_return false
expect(thing.rendered_field_content(:description)).to eq nil
end
end
end
end
context 'for Active record classes' do
......
......@@ -911,6 +911,16 @@ def setup_group_members(group)
subject { group.ci_variables_for('ref', project) }
it 'memoizes the result by ref', :request_store do
expect(project).to receive(:protected_for?).with('ref').once.and_return(true)
expect(project).to receive(:protected_for?).with('other').once.and_return(false)
2.times do
expect(group.ci_variables_for('ref', project)).to contain_exactly(ci_variable, protected_variable)
expect(group.ci_variables_for('other', project)).to contain_exactly(ci_variable)
end
end
shared_examples 'ref is protected' do
it 'contains all the variables' do
is_expected.to contain_exactly(ci_variable, protected_variable)
......
......@@ -2930,6 +2930,19 @@
end
end
it 'memoizes the result by ref and environment', :request_store do
scoped_variable = create(:ci_variable, value: 'secret', project: project, environment_scope: 'scoped')
expect(project).to receive(:protected_for?).with('ref').once.and_return(true)
expect(project).to receive(:protected_for?).with('other').twice.and_return(false)
2.times do
expect(project.reload.ci_variables_for(ref: 'ref', environment: 'production')).to contain_exactly(ci_variable, protected_variable)
expect(project.reload.ci_variables_for(ref: 'other')).to contain_exactly(ci_variable)
expect(project.reload.ci_variables_for(ref: 'other', environment: 'scoped')).to contain_exactly(ci_variable, scoped_variable)
end
end
context 'when the ref is not protected' do
before do
allow(project).to receive(:protected_for?).with('ref').and_return(false)
......
......@@ -632,4 +632,26 @@ def to_json(params = {})
end
end
end
describe '#can_cache_field?' do
using RSpec::Parameterized::TableSyntax
let(:snippet) { create(:snippet, file_name: file_name) }
subject { snippet.can_cache_field?(field) }
where(:field, :file_name, :result) do
:title | nil | true
:title | 'foo.bar' | true
:description | nil | true
:description | 'foo.bar' | true
:content | nil | false
:content | 'bar.foo' | false
:content | 'markdown.md' | true
end
with_them do
it { is_expected.to eq result }
end
end
end
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment