Commit 6593f1f6 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 2f369bd9
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
*.rake @gitlab-org/maintainers/rails-backend *.rake @gitlab-org/maintainers/rails-backend
# Technical writing team are the default reviewers for everything in `doc/` # Technical writing team are the default reviewers for everything in `doc/`
/doc/* @gl-docsteam *.md @gl-docsteam
doc/ @gl-docsteam
# Frontend maintainers should see everything in `app/assets/` # Frontend maintainers should see everything in `app/assets/`
app/assets/ @gitlab-org/maintainers/frontend app/assets/ @gitlab-org/maintainers/frontend
......
...@@ -183,7 +183,7 @@ export class CopyAsGFM { ...@@ -183,7 +183,7 @@ export class CopyAsGFM {
} }
// Export CopyAsGFM as a global for rspec to access // Export CopyAsGFM as a global for rspec to access
// see /spec/features/copy_as_gfm_spec.rb // see /spec/features/markdown/copy_as_gfm_spec.rb
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
window.CopyAsGFM = CopyAsGFM; window.CopyAsGFM = CopyAsGFM;
} }
......
...@@ -53,7 +53,7 @@ import InlineHTML from './marks/inline_html'; ...@@ -53,7 +53,7 @@ import InlineHTML from './marks/inline_html';
// The nodes and marks referenced here transform that same HTML to GFM to be copied to the clipboard. // The nodes and marks referenced here transform that same HTML to GFM to be copied to the clipboard.
// Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML // Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML
// from GFM should have a node or mark here. // from GFM should have a node or mark here.
// The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. // The GFM-to-HTML-to-GFM cycle is tested in spec/features/markdown/copy_as_gfm_spec.rb.
export default [ export default [
new Doc(), new Doc(),
......
...@@ -12,6 +12,7 @@ function cleanSuggestionLine(line = {}) { ...@@ -12,6 +12,7 @@ function cleanSuggestionLine(line = {}) {
return { return {
...line, ...line,
text: trimFirstCharOfLineContent(line.text), text: trimFirstCharOfLineContent(line.text),
rich_text: trimFirstCharOfLineContent(line.rich_text),
}; };
} }
......
...@@ -24,7 +24,8 @@ export default { ...@@ -24,7 +24,8 @@ export default {
{{ line.new_line }} {{ line.new_line }}
</td> </td>
<td class="line_content" :class="lineType"> <td class="line_content" :class="lineType">
<span v-if="line.text">{{ line.text }}</span> <span v-if="line.rich_text" v-html="line.rich_text"></span>
<span v-else-if="line.text">{{ line.text }}</span>
<!-- TODO: replace this hack with zero-width whitespace when we have rich_text from BE --> <!-- TODO: replace this hack with zero-width whitespace when we have rich_text from BE -->
<span v-else>&#8203;</span> <span v-else>&#8203;</span>
</td> </td>
......
...@@ -4,7 +4,9 @@ class SuggestionEntity < API::Entities::Suggestion ...@@ -4,7 +4,9 @@ class SuggestionEntity < API::Entities::Suggestion
include RequestAwareEntity include RequestAwareEntity
unexpose :from_line, :to_line, :from_content, :to_content unexpose :from_line, :to_line, :from_content, :to_content
expose :diff_lines, using: DiffLineEntity expose :diff_lines, using: DiffLineEntity do |suggestion|
Gitlab::Diff::Highlight.new(suggestion.diff_lines).highlight
end
expose :current_user do expose :current_user do
expose :can_apply do |suggestion| expose :can_apply do |suggestion|
Ability.allowed?(current_user, :apply_suggestion, suggestion) Ability.allowed?(current_user, :apply_suggestion, suggestion)
......
...@@ -84,7 +84,9 @@ module QuickActions ...@@ -84,7 +84,9 @@ module QuickActions
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def find_milestones(project, params = {}) def find_milestones(project, params = {})
MilestonesFinder.new(params.merge(project_ids: [project.id], group_ids: [project.group&.id])).execute group_ids = project.group.self_and_ancestors.select(:id) if project.group
MilestonesFinder.new(params.merge(project_ids: [project.id], group_ids: group_ids)).execute
end end
def parent def parent
......
---
title: Fix milestone quick action to handle ancestor group milestones
merge_request: 22231
author:
type: fixed
---
title: Apply word-diff highlighting to Suggestions
merge_request: 22182
author:
type: changed
---
title: Copy repository route under - scope
merge_request: 22092
author:
type: changed
---
title: Add back feature flag for cache invalidator
merge_request: 22106
author:
type: changed
...@@ -274,8 +274,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -274,8 +274,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
# The wiki routing contains wildcard characters so # The wiki and repository routing contains wildcard characters so
# its preferable to keep it below all other project routes # its preferable to keep it below all other project routes
draw :repository_scoped
draw :wiki draw :wiki
end end
# End of the /-/ scope. # End of the /-/ scope.
...@@ -481,6 +482,13 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -481,6 +482,13 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# its preferable to keep it below all other project routes # its preferable to keep it below all other project routes
draw :repository draw :repository
# To ensure an old unscoped routing is used for the UI we need to
# add prefix 'as' to the scope routing and place it below original routing.
# Issue https://gitlab.com/gitlab-org/gitlab/issues/118849
scope '-', as: 'scoped' do
draw :repository
end
# All new routes should go under /-/ scope. # All new routes should go under /-/ scope.
# Look for scope '-' at the top of the file. # Look for scope '-' at the top of the file.
# rubocop: enable Cop/PutProjectRoutesUnderScope # rubocop: enable Cop/PutProjectRoutesUnderScope
......
...@@ -39,32 +39,6 @@ scope format: false do ...@@ -39,32 +39,6 @@ scope format: false do
end end
end end
scope path: '-', constraints: { id: Gitlab::PathRegex.git_reference_regex } do
resources :network, only: [:show]
resources :graphs, only: [:show] do
member do
get :charts
get :commits
get :ci
get :languages
end
end
get '/branches/:state', to: 'branches#index', as: :branches_filtered, constraints: { state: /active|stale|all/ }
resources :branches, only: [:index, :new, :create, :destroy] do
get :diverging_commit_counts, on: :collection
end
delete :merged_branches, controller: 'branches', action: :destroy_all_merged
resources :tags, only: [:index, :show, :new, :create, :destroy] do
resource :release, controller: 'tags/releases', only: [:edit, :update]
end
resources :protected_branches, only: [:index, :show, :create, :update, :destroy, :patch], constraints: { id: Gitlab::PathRegex.git_reference_regex }
resources :protected_tags, only: [:index, :show, :create, :update, :destroy]
end
scope constraints: { id: /[^\0]+/ } do scope constraints: { id: /[^\0]+/ } do
scope controller: :blob do scope controller: :blob do
get '/new/*id', action: :new, as: :new_blob get '/new/*id', action: :new, as: :new_blob
......
# frozen_string_literal: true
# All routing related to repository browsing
# that is already under /-/ scope only
# Don't use format parameter as file extension (old 3.0.x behavior)
# See http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments
scope format: false do
scope constraints: { id: Gitlab::PathRegex.git_reference_regex } do
resources :network, only: [:show]
resources :graphs, only: [:show] do
member do
get :charts
get :commits
get :ci
get :languages
end
end
get '/branches/:state', to: 'branches#index', as: :branches_filtered, constraints: { state: /active|stale|all/ }
resources :branches, only: [:index, :new, :create, :destroy] do
get :diverging_commit_counts, on: :collection
end
delete :merged_branches, controller: 'branches', action: :destroy_all_merged
resources :tags, only: [:index, :show, :new, :create, :destroy] do
resource :release, controller: 'tags/releases', only: [:edit, :update]
end
resources :protected_branches, only: [:index, :show, :create, :update, :destroy, :patch], constraints: { id: Gitlab::PathRegex.git_reference_regex }
resources :protected_tags, only: [:index, :show, :create, :update, :destroy]
end
end
...@@ -10,7 +10,7 @@ to perform various actions. ...@@ -10,7 +10,7 @@ to perform various actions.
All statistics are opt-out, you can enable/disable them from the admin panel All statistics are opt-out, you can enable/disable them from the admin panel
under **Admin area > Settings > Metrics and profiling > Usage statistics**. under **Admin area > Settings > Metrics and profiling > Usage statistics**.
## Version check **(CORE ONLY)** ## Version Check **(CORE ONLY)**
If enabled, version check will inform you if a new version is available and the If enabled, version check will inform you if a new version is available and the
importance of it through a status. This is shown on the help page (i.e. `/help`) importance of it through a status. This is shown on the help page (i.e. `/help`)
...@@ -33,7 +33,23 @@ secure. ...@@ -33,7 +33,23 @@ secure.
If you disable version check, this information will not be collected. Enable or If you disable version check, this information will not be collected. Enable or
disable the version check at **Admin area > Settings > Metrics and profiling > Usage statistics**. disable the version check at **Admin area > Settings > Metrics and profiling > Usage statistics**.
## Usage ping **(CORE ONLY)** ### Request flow example
The following example shows a basic request/response flow between the self-managed GitLab instance
and the GitLab Version Application:
```mermaid
sequenceDiagram
participant GitLab instance
participant Version Application
GitLab instance->>Version Application: Is there a version update?
loop Version Check
Version Application->>Version Application: Record version info
end
Version Application->>GitLab instance: Response (PNG/SVG)
```
## Usage Ping **(CORE ONLY)**
> [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics > [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics
[were added][ee-735] in GitLab Enterprise Edition [were added][ee-735] in GitLab Enterprise Edition
...@@ -52,7 +68,32 @@ You can view the exact JSON payload in the administration panel. To view the pay ...@@ -52,7 +68,32 @@ You can view the exact JSON payload in the administration panel. To view the pay
1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**. 1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**.
1. Expand **Usage statistics** and click on the **Preview payload** button. 1. Expand **Usage statistics** and click on the **Preview payload** button.
You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv). You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/version_usage_stats_to_stage_mappings.csv).
### Request flow example
The following example shows a basic request/response flow between the self-managed GitLab instance, GitLab Version Application,
GitLab License Application and Salesforce:
```mermaid
sequenceDiagram
participant GitLab instance
participant Version Application
participant License Application
participant Salesforce
GitLab instance->>Version Application: Usage Ping data
loop Process Usage Data
Version Application->>Version Application: Parse Usage Data
Version Application->>Version Application: Record Usage Data
Version Application->>Version Application: Update license ping time
end
Version Application-xLicense Application: Request Zuora subscription id
License Application-xVersion Application: Zuora subscription id
Version Application-xSalesforce: Request Zuora account id by Zuora subscription id
Salesforce-xVersion Application: Zuora account id
Version Application-xSalesforce: Usage data for the Zuora account
Version Application->>GitLab instance: Conversational Development Index
```
### Deactivate the usage ping ### Deactivate the usage ping
......
...@@ -7,6 +7,7 @@ class Feature ...@@ -7,6 +7,7 @@ class Feature
# Server feature flags should use '_' to separate words. # Server feature flags should use '_' to separate words.
SERVER_FEATURE_FLAGS = SERVER_FEATURE_FLAGS =
%w[ %w[
cache_invalidator
inforef_uploadpack_cache inforef_uploadpack_cache
get_tag_messages_go get_tag_messages_go
filter_shas_with_signatures_go filter_shas_with_signatures_go
......
...@@ -7,8 +7,8 @@ const oldLine = { ...@@ -7,8 +7,8 @@ const oldLine = {
meta_data: null, meta_data: null,
new_line: null, new_line: null,
old_line: 5, old_line: 5,
rich_text: '-oldtext', rich_text: 'oldrichtext',
text: '-oldtext', text: 'oldplaintext',
type: 'old', type: 'old',
}; };
...@@ -18,8 +18,8 @@ const newLine = { ...@@ -18,8 +18,8 @@ const newLine = {
meta_data: null, meta_data: null,
new_line: 6, new_line: 6,
old_line: null, old_line: null,
rich_text: '-newtext', rich_text: 'newrichtext',
text: '-newtext', text: 'newplaintext',
type: 'new', type: 'new',
}; };
...@@ -42,14 +42,46 @@ describe('SuggestionDiffRow', () => { ...@@ -42,14 +42,46 @@ describe('SuggestionDiffRow', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it('renders correctly', () => { describe('renders correctly', () => {
factory({ it('has the right classes on the wrapper', () => {
propsData: { factory({
line: oldLine, propsData: {
}, line: oldLine,
},
});
expect(wrapper.is('.line_holder')).toBe(true);
});
it('renders the rich text when it is available', () => {
factory({
propsData: {
line: newLine,
},
});
expect(wrapper.find('td.line_content').text()).toEqual('newrichtext');
}); });
expect(wrapper.is('.line_holder')).toBe(true); it('renders the plain text when it is available but rich text is not', () => {
factory({
propsData: {
line: Object.assign({}, newLine, { rich_text: undefined }),
},
});
expect(wrapper.find('td.line_content').text()).toEqual('newplaintext');
});
it('renders a zero-width space when it has no plain or rich texts', () => {
factory({
propsData: {
line: Object.assign({}, newLine, { rich_text: undefined, text: undefined }),
},
});
expect(wrapper.find('td.line_content').text()).toEqual('\u200B');
});
}); });
describe('when passed line has type old', () => { describe('when passed line has type old', () => {
......
...@@ -4,17 +4,19 @@ require 'spec_helper' ...@@ -4,17 +4,19 @@ require 'spec_helper'
describe SafeUrl do describe SafeUrl do
describe '#safe_url' do describe '#safe_url' do
class SafeUrlTestClass let(:safe_url_test_class) do
include SafeUrl Class.new do
include SafeUrl
attr_reader :url attr_reader :url
def initialize(url) def initialize(url)
@url = url @url = url
end
end end
end end
let(:test_class) { SafeUrlTestClass.new(url) } let(:test_class) { safe_url_test_class.new(url) }
let(:url) { 'http://example.com' } let(:url) { 'http://example.com' }
subject { test_class.safe_url } subject { test_class.safe_url }
......
...@@ -326,7 +326,7 @@ describe API::Internal::Base do ...@@ -326,7 +326,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true')
expect(user.reload.last_activity_on).to eql(Date.today) expect(user.reload.last_activity_on).to eql(Date.today)
end end
end end
...@@ -346,7 +346,7 @@ describe API::Internal::Base do ...@@ -346,7 +346,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true')
expect(user.reload.last_activity_on).to be_nil expect(user.reload.last_activity_on).to be_nil
end end
end end
...@@ -588,7 +588,7 @@ describe API::Internal::Base do ...@@ -588,7 +588,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true')
end end
end end
......
...@@ -579,6 +579,10 @@ describe 'project routing' do ...@@ -579,6 +579,10 @@ describe 'project routing' do
namespace_id: 'gitlab', project_id: 'gitlabhq', namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "blob/master/blob/#{newline_file}" }) id: "blob/master/blob/#{newline_file}" })
end end
it 'to #show from scope routing' do
expect(get('/gitlab/gitlabhq/-/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
end
end end
# project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /[^\0]+/, project_id: /[^\/]+/} # project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /[^\0]+/, project_id: /[^\/]+/}
...@@ -596,6 +600,10 @@ describe 'project routing' do ...@@ -596,6 +600,10 @@ describe 'project routing' do
namespace_id: 'gitlab', project_id: 'gitlabhq', namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/#{newline_file}" }) id: "master/#{newline_file}" })
end end
it 'to #show from scope routing' do
expect(get('/gitlab/gitlabhq/-/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
end
end end
# project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/[^\0]+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/} # project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/[^\0]+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/}
......
...@@ -856,9 +856,10 @@ describe QuickActions::InterpretService do ...@@ -856,9 +856,10 @@ describe QuickActions::InterpretService do
end end
context 'only group milestones available' do context 'only group milestones available' do
let(:group) { create(:group) } let(:ancestor_group) { create(:group) }
let(:group) { create(:group, parent: ancestor_group) }
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
let(:milestone) { create(:milestone, group: group, title: '10.0') } let(:milestone) { create(:milestone, group: ancestor_group, title: '10.0') }
it_behaves_like 'milestone command' do it_behaves_like 'milestone command' do
let(:content) { "/milestone %#{milestone.title}" } let(:content) { "/milestone %#{milestone.title}" }
......
...@@ -3,18 +3,19 @@ ...@@ -3,18 +3,19 @@
require 'spec_helper' require 'spec_helper'
describe QualifiedDomainArrayValidator do describe QualifiedDomainArrayValidator do
class QualifiedDomainArrayValidatorTestClass let(:qualified_domain_array_validator_test_class) do
include ActiveModel::Validations Class.new do
include ActiveModel::Validations
attr_accessor :domain_array attr_accessor :domain_array
def initialize(domain_array) def initialize(domain_array)
self.domain_array = domain_array self.domain_array = domain_array
end
end end
end end
let!(:record) do let!(:record) do
QualifiedDomainArrayValidatorTestClass.new(['gitlab.com']) qualified_domain_array_validator_test_class.new(['gitlab.com'])
end end
subject { validator.validate(record) } subject { validator.validate(record) }
......
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