Commit 80af17cb authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-09-06

# Conflicts:
#	app/assets/javascripts/pages/projects/project.js
#	app/assets/stylesheets/framework/variables.scss
#	app/finders/template_finder.rb
#	app/helpers/button_helper.rb
#	app/views/projects/_home_panel.html.haml
#	doc/user/markdown.md
#	locale/gitlab.pot

[ci skip]
parents 32e77114 f965bc9e
...@@ -71,7 +71,7 @@ gem 'u2f', '~> 0.2.1' ...@@ -71,7 +71,7 @@ gem 'u2f', '~> 0.2.1'
gem 'validates_hostname', '~> 1.0.6' gem 'validates_hostname', '~> 1.0.6'
# Browser detection # Browser detection
gem 'browser', '~> 2.2' gem 'browser', '~> 2.5'
# GPG # GPG
gem 'gpgme' gem 'gpgme'
......
...@@ -98,7 +98,7 @@ GEM ...@@ -98,7 +98,7 @@ GEM
msgpack (~> 1.0) msgpack (~> 1.0)
bootstrap_form (2.7.0) bootstrap_form (2.7.0)
brakeman (4.2.1) brakeman (4.2.1)
browser (2.2.0) browser (2.5.3)
builder (3.2.3) builder (3.2.3)
bullet (5.5.1) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -1021,7 +1021,7 @@ DEPENDENCIES ...@@ -1021,7 +1021,7 @@ DEPENDENCIES
bootsnap (~> 1.3) bootsnap (~> 1.3)
bootstrap_form (~> 2.7.0) bootstrap_form (~> 2.7.0)
brakeman (~> 4.2) brakeman (~> 4.2)
browser (~> 2.2) browser (~> 2.5)
bullet (~> 5.5.0) bullet (~> 5.5.0)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 2.15) capybara (~> 2.15)
......
...@@ -101,7 +101,7 @@ GEM ...@@ -101,7 +101,7 @@ GEM
msgpack (~> 1.0) msgpack (~> 1.0)
bootstrap_form (2.7.0) bootstrap_form (2.7.0)
brakeman (4.2.1) brakeman (4.2.1)
browser (2.2.0) browser (2.5.3)
builder (3.2.3) builder (3.2.3)
bullet (5.5.1) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -1030,7 +1030,7 @@ DEPENDENCIES ...@@ -1030,7 +1030,7 @@ DEPENDENCIES
bootsnap (~> 1.3) bootsnap (~> 1.3)
bootstrap_form (~> 2.7.0) bootstrap_form (~> 2.7.0)
brakeman (~> 4.2) brakeman (~> 4.2)
browser (~> 2.2) browser (~> 2.5)
bullet (~> 5.5.0) bullet (~> 5.5.0)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 2.15) capybara (~> 2.15)
......
...@@ -37,11 +37,14 @@ export default class Project { ...@@ -37,11 +37,14 @@ export default class Project {
$label.text(activeText); $label.text(activeText);
}); });
<<<<<<< HEAD
$('#modal-geo-info').data({ $('#modal-geo-info').data({
cloneUrlSecondary: $this.attr('href'), cloneUrlSecondary: $this.attr('href'),
cloneUrlPrimary: $this.data('primaryUrl') || '', cloneUrlPrimary: $this.data('primaryUrl') || '',
}); });
=======
>>>>>>> upstream/master
$projectCloneField.val(url); $projectCloneField.val(url);
$('.js-git-empty .js-clone').text(url); $('.js-git-empty .js-clone').text(url);
}); });
......
...@@ -275,12 +275,15 @@ $flash-height: 52px; ...@@ -275,12 +275,15 @@ $flash-height: 52px;
$context-header-height: 60px; $context-header-height: 60px;
$breadcrumb-min-height: 48px; $breadcrumb-min-height: 48px;
$project-title-row-height: 24px; $project-title-row-height: 24px;
<<<<<<< HEAD
// EE-only CSS variables START // EE-only CSS variables START
$system-header-height: 35px; $system-header-height: 35px;
$issue-box-upcoming-bg: #8f8f8f; $issue-box-upcoming-bg: #8f8f8f;
$pages-group-name-color: #4c4e54; $pages-group-name-color: #4c4e54;
// EE-only CSS variables END // EE-only CSS variables END
=======
>>>>>>> upstream/master
/* /*
* Common component specific colors * Common component specific colors
......
...@@ -110,6 +110,7 @@ class ApplicationController < ActionController::Base ...@@ -110,6 +110,7 @@ class ApplicationController < ActionController::Base
def append_info_to_payload(payload) def append_info_to_payload(payload)
super super
payload[:ua] = request.env["HTTP_USER_AGENT"]
payload[:remote_ip] = request.remote_ip payload[:remote_ip] = request.remote_ip
logged_user = auth_user logged_user = auth_user
......
class TemplateFinder class TemplateFinder
<<<<<<< HEAD
prepend ::EE::TemplateFinder prepend ::EE::TemplateFinder
=======
>>>>>>> upstream/master
VENDORED_TEMPLATES = { VENDORED_TEMPLATES = {
dockerfiles: ::Gitlab::Template::DockerfileTemplate, dockerfiles: ::Gitlab::Template::DockerfileTemplate,
gitignores: ::Gitlab::Template::GitignoreTemplate, gitignores: ::Gitlab::Template::GitignoreTemplate,
......
...@@ -92,6 +92,7 @@ module ButtonHelper ...@@ -92,6 +92,7 @@ module ButtonHelper
class: "#{title.downcase}-selector", class: "#{title.downcase}-selector",
href: (href if href), href: (href if href),
data: (data if data) data: (data if data)
<<<<<<< HEAD
end end
def kerberos_clone_button(project) def kerberos_clone_button(project)
...@@ -117,5 +118,7 @@ module ButtonHelper ...@@ -117,5 +118,7 @@ module ButtonHelper
data: data, data: data,
type: :button, type: :button,
title: 'See Geo-specific instructions' title: 'See Geo-specific instructions'
=======
>>>>>>> upstream/master
end end
end end
...@@ -107,23 +107,23 @@ module MarkupHelper ...@@ -107,23 +107,23 @@ module MarkupHelper
def markup(file_name, text, context = {}) def markup(file_name, text, context = {})
context[:project] ||= @project context[:project] ||= @project
context[:markdown_engine] ||= :redcarpet context[:markdown_engine] ||= :redcarpet unless commonmark_for_repositories_enabled?
html = context.delete(:rendered) || markup_unsafe(file_name, text, context) html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context) prepare_for_rendering(html, context)
end end
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page, context = {})
text = wiki_page.content text = wiki_page.content
return '' unless text.present? return '' unless text.present?
context = { context.merge!(
pipeline: :wiki, pipeline: :wiki,
project: @project, project: @project,
project_wiki: @project_wiki, project_wiki: @project_wiki,
page_slug: wiki_page.slug, page_slug: wiki_page.slug,
issuable_state_filter_enabled: true, issuable_state_filter_enabled: true
markdown_engine: :redcarpet )
} context[:markdown_engine] ||= :redcarpet unless commonmark_for_repositories_enabled?
html = html =
case wiki_page.format case wiki_page.format
...@@ -178,6 +178,10 @@ module MarkupHelper ...@@ -178,6 +178,10 @@ module MarkupHelper
end end
end end
def commonmark_for_repositories_enabled?
Feature.enabled?(:commonmark_for_repositories, default_enabled: true)
end
private private
# Return +text+, truncated to +max_chars+ characters, excluding any HTML # Return +text+, truncated to +max_chars+ characters, excluding any HTML
......
...@@ -254,6 +254,10 @@ module ProjectsHelper ...@@ -254,6 +254,10 @@ module ProjectsHelper
"xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}" "xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}"
end end
def legacy_render_context(params)
params[:legacy_render] ? { markdown_engine: :redcarpet } : {}
end
private private
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
......
...@@ -90,7 +90,9 @@ module Ci ...@@ -90,7 +90,9 @@ module Ci
end end
def hashed_path? def hashed_path?
super || self.try(:file_location).nil? return true if trace? # ArchiveLegacyTraces background migration might not have `file_location` column
super || self.file_location.nil?
end end
def expire_in def expire_in
......
...@@ -585,7 +585,12 @@ class Repository ...@@ -585,7 +585,12 @@ class Repository
end end
def rendered_readme def rendered_readme
MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme return unless readme
context = { project: project }
context[:markdown_engine] = :redcarpet unless MarkupHelper.commonmark_for_repositories_enabled?
MarkupHelper.markup_unsafe(readme.name, readme.data, context)
end end
cache_method :rendered_readme cache_method :rendered_readme
......
...@@ -43,6 +43,10 @@ class PreviewMarkdownService < BaseService ...@@ -43,6 +43,10 @@ class PreviewMarkdownService < BaseService
end end
def markdown_engine def markdown_engine
CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i) if params[:legacy_render]
:redcarpet
else
CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
end
end end
end end
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)') - deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) } = deleted_message % { project_name: fork_source_name(@project) }
<<<<<<< HEAD
- if @project.mirror? - if @project.mirror?
- import_url = @project.safe_import_url - import_url = @project.safe_import_url
%p %p
...@@ -51,6 +52,8 @@ ...@@ -51,6 +52,8 @@
%br %br
= render "shared/mirror_status" = render "shared/mirror_status"
=======
>>>>>>> upstream/master
- if @project.badges.present? - if @project.badges.present?
.project-badges.prepend-top-default.append-bottom-default .project-badges.prepend-top-default.append-bottom-default
- @project.badges.each do |badge| - @project.badges.each do |badge|
...@@ -69,6 +72,7 @@ ...@@ -69,6 +72,7 @@
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
.project-clone-holder.d-inline-flex.d-sm-none .project-clone-holder.d-inline-flex.d-sm-none
= render "shared/mobile_clone_panel" = render "shared/mobile_clone_panel"
<<<<<<< HEAD
.project-clone-holder.d-none.d-sm-inline-flex .project-clone-holder.d-none.d-sm-inline-flex
= render "shared/clone_panel" = render "shared/clone_panel"
...@@ -77,6 +81,16 @@ ...@@ -77,6 +81,16 @@
.project-action-button.project-xcode.inline .project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link" = render "projects/buttons/xcode_link"
=======
.project-clone-holder.d-none.d-sm-inline-flex
= render "shared/clone_panel"
- if show_xcode_link?(@project)
.project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link"
>>>>>>> upstream/master
- if current_user - if current_user
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
.d-none.d-sm-inline-flex .d-none.d-sm-inline-flex
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%div{ class: container_class } %div{ class: container_class }
.prepend-top-default.append-bottom-default .prepend-top-default.append-bottom-default
.wiki .wiki
= render_wiki_content(@wiki_home) = render_wiki_content(@wiki_home, legacy_render_context(params))
- else - else
- can_create_wiki = can?(current_user, :create_wiki, @project) - can_create_wiki = can?(current_user, :create_wiki, @project)
.project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] } .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
Write Write
%li %li
= link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id, legacy_render: params[:legacy_render]) do
= editing_preview_title(@blob.name) = editing_preview_title(@blob.name)
= form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do = form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.diff-content .diff-content
- if markup?(@blob.name) - if markup?(@blob.name)
.file-content.wiki .file-content.wiki
= markup(@blob.name, @content) = markup(@blob.name, @content, legacy_render_context(params))
- else - else
.file-content.code.js-syntax-highlight .file-content.code.js-syntax-highlight
- unless @diff_lines.empty? - unless @diff_lines.empty?
......
- blob = viewer.blob - blob = viewer.blob
- rendered_markup = blob.rendered_markup if blob.respond_to?(:rendered_markup) - context = legacy_render_context(params)
- unless context[:markdown_engine] == :redcarpet
- context[:rendered] = blob.rendered_markup if blob.respond_to?(:rendered_markup)
.file-content.wiki .file-content.wiki
= markup(blob.name, blob.data, rendered: rendered_markup) = markup(blob.name, blob.data, context)
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}") - commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
- commit_message = commit_message % { page_title: @page.title } - commit_message = commit_message % { page_title: @page.title }
- if params[:legacy_render] || !commonmark_for_repositories_enabled?
- markdown_version = CacheMarkdownField::CACHE_REDCARPET_VERSION
- else
- markdown_version = 0
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' }, html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' },
data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f| data: { markdown_version: markdown_version } do |f|
= form_errors(@page) = form_errors(@page)
- if @page.persisted? - if @page.persisted?
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
.blocks-container .blocks-container
.block.block-first .block.block-first
- if @sidebar_page - if @sidebar_page
= render_wiki_content(@sidebar_page) = render_wiki_content(@sidebar_page, legacy_render_context(params))
- else - else
%ul.wiki-pages %ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar' = render @sidebar_wiki_entries, context: 'sidebar'
......
...@@ -26,6 +26,6 @@ ...@@ -26,6 +26,6 @@
.prepend-top-default.append-bottom-default .prepend-top-default.append-bottom-default
.wiki .wiki
= render_wiki_content(@page) = render_wiki_content(@page, legacy_render_context(params))
= render 'sidebar' = render 'sidebar'
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.file-content.wiki .file-content.wiki
- snippet_chunks.each do |chunk| - snippet_chunks.each do |chunk|
- unless chunk[:data].empty? - unless chunk[:data].empty?
= markup(snippet.file_name, chunk[:data]) = markup(snippet.file_name, chunk[:data], legacy_render_context(params))
- else - else
.file-content.code .file-content.code
.nothing-here-block Empty file .nothing-here-block Empty file
......
---
title: Render files (`.md`) and wikis using CommonMark
merge_request: 21228
author:
type: changed
---
title: Upgrade Monaco editor
merge_request:
author:
type: other
---
title: Explicit hashed path check for trace, prevents background migration from accessing
file_location column that doesn't exist
merge_request: 21533
author: Jasper Maes
type: other
---
title: Add User-Agent to production_json.log
merge_request: 21546
author:
type: other
...@@ -22,7 +22,8 @@ unless Sidekiq.server? ...@@ -22,7 +22,8 @@ unless Sidekiq.server?
params: params, params: params,
remote_ip: event.payload[:remote_ip], remote_ip: event.payload[:remote_ip],
user_id: event.payload[:user_id], user_id: event.payload[:user_id],
username: event.payload[:username] username: event.payload[:username],
ua: event.payload[:ua]
} }
gitaly_calls = Gitlab::GitalyClient.get_request_count gitaly_calls = Gitlab::GitalyClient.get_request_count
......
...@@ -58,13 +58,20 @@ Features that are developed and are intended to be merged behind a feature flag ...@@ -58,13 +58,20 @@ Features that are developed and are intended to be merged behind a feature flag
should not include a changelog entry. The entry should be added in the merge should not include a changelog entry. The entry should be added in the merge
request removing the feature flags. request removing the feature flags.
In the rare case that you need the feature flag to be on automatically, use
`default_enabled: true` when checking:
```ruby
Feature.enabled?(:feature_flag, project, default_enabled: true)
```
### Specs ### Specs
In the test environment `Feature.enabled?` is stubbed to always respond to `true`, In the test environment `Feature.enabled?` is stubbed to always respond to `true`,
so we make sure behavior under feature flag doesn't go untested in some non-specific so we make sure behavior under feature flag doesn't go untested in some non-specific
contexts. contexts.
If you need to test the feature flag in a different state, you need to stub it with: If you need to test the feature flag in a different state, you need to stub it with:
```ruby ```ruby
stub_feature_flags(my_feature_flag: false) stub_feature_flags(my_feature_flag: false)
......
...@@ -24,7 +24,10 @@ You can use GFM in the following areas: ...@@ -24,7 +24,10 @@ You can use GFM in the following areas:
- snippets (the snippet must be named with a `.md` extension) - snippets (the snippet must be named with a `.md` extension)
- wiki pages - wiki pages
- markdown documents inside the repository - markdown documents inside the repository
<<<<<<< HEAD
- epics - epics
=======
>>>>>>> upstream/master
You can also use other rich text files in GitLab. You might have to install a You can also use other rich text files in GitLab. You might have to install a
dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information. dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
......
...@@ -42,13 +42,21 @@ class Feature ...@@ -42,13 +42,21 @@ class Feature
persisted_names.include?(feature.name.to_s) persisted_names.include?(feature.name.to_s)
end end
def enabled?(key, thing = nil) # use `default_enabled: true` to default the flag to being `enabled`
get(key).enabled?(thing) # unless set explicitly. The default is `disabled`
def enabled?(key, thing = nil, default_enabled: false)
feature = Feature.get(key)
# If we're not default enabling the flag or the feature has been set, always evaluate.
# `persisted?` can potentially generate DB queries and also checks for inclusion
# in an array of feature names (177 at last count), possibly reducing performance by half.
# So we only perform the `persisted` check if `default_enabled: true`
!default_enabled || Feature.persisted?(feature) ? feature.enabled?(thing) : true
end end
def disabled?(key, thing = nil) def disabled?(key, thing = nil, default_enabled: false)
# we need to make different method calls to make it easy to mock / define expectations in test mode # we need to make different method calls to make it easy to mock / define expectations in test mode
thing.nil? ? !enabled?(key) : !enabled?(key, thing) thing.nil? ? !enabled?(key, default_enabled: default_enabled) : !enabled?(key, thing, default_enabled: default_enabled)
end end
def enable(key, thing = true) def enable(key, thing = true)
......
...@@ -399,10 +399,14 @@ msgstr "" ...@@ -399,10 +399,14 @@ msgstr ""
msgid "Add Group Webhooks and GitLab Enterprise Edition." msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Add Kubernetes cluster" msgid "Add Kubernetes cluster"
=======
msgid "Add Readme"
>>>>>>> upstream/master
msgstr "" msgstr ""
msgid "Add Readme" msgid "Add license"
msgstr "" msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit" msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
...@@ -2235,9 +2239,12 @@ msgstr "" ...@@ -2235,9 +2239,12 @@ msgstr ""
msgid "Copy SSH clone URL" msgid "Copy SSH clone URL"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Copy SSH public key to clipboard" msgid "Copy SSH public key to clipboard"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Copy URL to clipboard" msgid "Copy URL to clipboard"
msgstr "" msgstr ""
...@@ -4968,9 +4975,12 @@ msgstr "" ...@@ -4968,9 +4975,12 @@ msgstr ""
msgid "No license. All rights reserved" msgid "No license. All rights reserved"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "No merge requests for the selected time period." msgid "No merge requests for the selected time period."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "No merge requests found" msgid "No merge requests found"
msgstr "" msgstr ""
...@@ -8176,12 +8186,15 @@ msgstr "" ...@@ -8176,12 +8186,15 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock" msgid "You must have maintainer access to force delete a lock"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "You need a different license to enable FileLocks feature" msgid "You need a different license to enable FileLocks feature"
msgstr "" msgstr ""
msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com" msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "You need permission." msgid "You need permission."
msgstr "" msgstr ""
......
...@@ -77,7 +77,8 @@ module RuboCop ...@@ -77,7 +77,8 @@ module RuboCop
start_clause_line?(previous_line(node)) || start_clause_line?(previous_line(node)) ||
block_start?(previous_line(node)) || block_start?(previous_line(node)) ||
begin_line?(previous_line(node)) || begin_line?(previous_line(node)) ||
assignment_line?(previous_line(node)) assignment_line?(previous_line(node)) ||
rescue_line?(previous_line(node))
end end
def last_line_valid?(node) def last_line_valid?(node)
...@@ -111,6 +112,10 @@ module RuboCop ...@@ -111,6 +112,10 @@ module RuboCop
line =~ /^\s*.*=/ line =~ /^\s*.*=/
end end
def rescue_line?(line)
line =~ /^\s*rescue/
end
def block_start?(line) def block_start?(line)
line.match(/ (do|{)( \|.*?\|)?\s?$/) line.match(/ (do|{)( \|.*?\|)?\s?$/)
end end
......
...@@ -5,8 +5,8 @@ describe 'File blob', :js do ...@@ -5,8 +5,8 @@ describe 'File blob', :js do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
def visit_blob(path, anchor: nil, ref: 'master') def visit_blob(path, anchor: nil, ref: 'master', legacy_render: nil)
visit project_blob_path(project, File.join(ref, path), anchor: anchor) visit project_blob_path(project, File.join(ref, path), anchor: anchor, legacy_render: legacy_render)
wait_for_requests wait_for_requests
end end
...@@ -142,6 +142,52 @@ describe 'File blob', :js do ...@@ -142,6 +142,52 @@ describe 'File blob', :js do
end end
end end
context 'Markdown rendering' do
before do
project.add_maintainer(project.creator)
Files::CreateService.new(
project,
project.creator,
start_branch: 'master',
branch_name: 'master',
commit_message: "Add RedCarpet and CommonMark Markdown ",
file_path: 'files/commonmark/file.md',
file_content: "1. one\n - sublist\n"
).execute
end
context 'when rendering default markdown' do
before do
visit_blob('files/commonmark/file.md')
wait_for_requests
end
it 'renders using CommonMark' do
aggregate_failures do
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
end
end
context 'when rendering legacy markdown' do
before do
visit_blob('files/commonmark/file.md', legacy_render: 1)
wait_for_requests
end
it 'renders using RedCarpet' do
aggregate_failures do
expect(page).to have_content("sublist")
expect(page).to have_xpath("//ol//li//ul")
end
end
end
end
context 'Markdown file (stored in LFS)' do context 'Markdown file (stored in LFS)' do
before do before do
project.add_maintainer(project.creator) project.add_maintainer(project.creator)
......
...@@ -7,6 +7,7 @@ describe 'Editing file blob', :js do ...@@ -7,6 +7,7 @@ describe 'Editing file blob', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
let(:branch) { 'master' } let(:branch) { 'master' }
let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] } let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] }
let(:readme_file_path) { 'README.md' }
context 'as a developer' do context 'as a developer' do
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -20,14 +21,19 @@ describe 'Editing file blob', :js do ...@@ -20,14 +21,19 @@ describe 'Editing file blob', :js do
def edit_and_commit(commit_changes: true) def edit_and_commit(commit_changes: true)
wait_for_requests wait_for_requests
find('.js-edit-blob').click find('.js-edit-blob').click
find('#editor') fill_editor(content: "class NextFeature\\nend\\n")
execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
if commit_changes if commit_changes
click_button 'Commit changes' click_button 'Commit changes'
end end
end end
def fill_editor(content: "class NextFeature\\nend\\n")
wait_for_requests
find('#editor')
execute_script("ace.edit('editor').setValue('#{content}')")
end
context 'from MR diff' do context 'from MR diff' do
before do before do
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
...@@ -63,6 +69,30 @@ describe 'Editing file blob', :js do ...@@ -63,6 +69,30 @@ describe 'Editing file blob', :js do
expect(new_line_count).to be > 0 expect(new_line_count).to be > 0
end end
end end
context 'when rendering the preview' do
it 'renders content with CommonMark' do
visit project_edit_blob_path(project, tree_join(branch, readme_file_path))
fill_editor(content: "1. one\\n - sublist\\n")
click_link 'Preview'
wait_for_requests
# the above generates two seperate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
it 'renders content with RedCarpet when legacy_render is set' do
visit project_edit_blob_path(project, tree_join(branch, readme_file_path), legacy_render: 1)
fill_editor(content: "1. one\\n - sublist\\n")
click_link 'Preview'
wait_for_requests
# the above generates a sublist list in RedCarpet
expect(page).to have_content("sublist")
expect(page).to have_xpath("//ol//li//ul")
end
end
end end
context 'visit blob edit' do context 'visit blob edit' do
......
...@@ -162,6 +162,34 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -162,6 +162,34 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end end
end end
context 'when rendering the preview' do
it 'renders content with CommonMark' do
create_wiki_page 'a-page/b-page/c-page/common-mark'
click_link 'Edit'
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
# the above generates two seperate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
it 'renders content with RedCarpet when legacy_render is set' do
wiki_page = create(:wiki_page,
wiki: project.wiki,
attrs: { title: 'home', content: "Empty content" })
visit(project_wiki_edit_path(project, wiki_page, legacy_render: 1))
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
# the above generates a sublist list in RedCarpet
expect(page).to have_content("sublist")
expect(page).to have_xpath("//ol//li//ul")
end
end
end end
it "does not linkify double brackets inside code blocks as expected" do it "does not linkify double brackets inside code blocks as expected" do
......
...@@ -68,23 +68,45 @@ describe 'Snippet', :js do ...@@ -68,23 +68,45 @@ describe 'Snippet', :js do
end end
end end
context 'with cached Redcarpet html' do context 'Markdown rendering' do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) } let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) }
let(:file_name) { 'test.md' } let(:file_name) { 'test.md' }
let(:content) { "1. one\n - sublist\n" } let(:content) { "1. one\n - sublist\n" }
it 'renders correctly' do context 'when rendering default markdown' do
expect(page).to have_xpath("//ol//li//ul") it 'renders using CommonMark' do
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
end end
end
context 'with cached CommonMark html' do context 'when rendering legacy markdown' do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) } before do
let(:file_name) { 'test.md' } visit snippet_path(snippet, legacy_render: 1)
let(:content) { "1. one\n - sublist\n" }
it 'renders correctly' do wait_for_requests
expect(page).not_to have_xpath("//ol//li//ul") end
it 'renders using RedCarpet' do
expect(page).to have_content("sublist")
expect(page).to have_xpath("//ol//li//ul")
end
end
context 'with cached CommonMark html' do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
it 'renders correctly' do
expect(page).not_to have_xpath("//ol//li//ul")
end
end
context 'with cached Redcarpet html' do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
it 'renders correctly' do
expect(page).to have_xpath("//ol//li//ul")
end
end end
end end
......
...@@ -25,17 +25,17 @@ describe MarkupHelper do ...@@ -25,17 +25,17 @@ describe MarkupHelper do
let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" } let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" }
it "links to the merge request" do it "links to the merge request" do
expected = project_merge_request_path(project, merge_request) expected = urls.project_merge_request_path(project, merge_request)
expect(helper.markdown(actual)).to match(expected) expect(helper.markdown(actual)).to match(expected)
end end
it "links to the commit" do it "links to the commit" do
expected = project_commit_path(project, commit) expected = urls.project_commit_path(project, commit)
expect(helper.markdown(actual)).to match(expected) expect(helper.markdown(actual)).to match(expected)
end end
it "links to the issue" do it "links to the issue" do
expected = project_issue_path(project, issue) expected = urls.project_issue_path(project, issue)
expect(helper.markdown(actual)).to match(expected) expect(helper.markdown(actual)).to match(expected)
end end
end end
...@@ -46,7 +46,7 @@ describe MarkupHelper do ...@@ -46,7 +46,7 @@ describe MarkupHelper do
let(:second_issue) { create(:issue, project: second_project) } let(:second_issue) { create(:issue, project: second_project) }
it 'links to the issue' do it 'links to the issue' do
expected = project_issue_path(second_project, second_issue) expected = urls.project_issue_path(second_project, second_issue)
expect(markdown(actual, project: second_project)).to match(expected) expect(markdown(actual, project: second_project)).to match(expected)
end end
end end
...@@ -93,7 +93,7 @@ describe MarkupHelper do ...@@ -93,7 +93,7 @@ describe MarkupHelper do
# First issue link # First issue link
expect(doc.css('a')[1].attr('href')) expect(doc.css('a')[1].attr('href'))
.to eq project_issue_path(project, issues[0]) .to eq urls.project_issue_path(project, issues[0])
expect(doc.css('a')[1].text).to eq issues[0].to_reference expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link # Internal commit link
...@@ -102,7 +102,7 @@ describe MarkupHelper do ...@@ -102,7 +102,7 @@ describe MarkupHelper do
# Second issue link # Second issue link
expect(doc.css('a')[3].attr('href')) expect(doc.css('a')[3].attr('href'))
.to eq project_issue_path(project, issues[1]) .to eq urls.project_issue_path(project, issues[1])
expect(doc.css('a')[3].text).to eq issues[1].to_reference expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link # Trailing commit link
...@@ -128,7 +128,7 @@ describe MarkupHelper do ...@@ -128,7 +128,7 @@ describe MarkupHelper do
# First issue link # First issue link
expect(doc.css('a')[1].attr('href')) expect(doc.css('a')[1].attr('href'))
.to eq project_issue_path(project, issues[0]) .to eq urls.project_issue_path(project, issues[0])
expect(doc.css('a')[1].text).to eq issues[0].to_reference expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link # Internal commit link
...@@ -137,7 +137,7 @@ describe MarkupHelper do ...@@ -137,7 +137,7 @@ describe MarkupHelper do
# Second issue link # Second issue link
expect(doc.css('a')[3].attr('href')) expect(doc.css('a')[3].attr('href'))
.to eq project_issue_path(project, issues[1]) .to eq urls.project_issue_path(project, issues[1])
expect(doc.css('a')[3].text).to eq issues[1].to_reference expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link # Trailing commit link
...@@ -183,7 +183,7 @@ describe MarkupHelper do ...@@ -183,7 +183,7 @@ describe MarkupHelper do
doc = Nokogiri::HTML.parse(rendered) doc = Nokogiri::HTML.parse(rendered)
expect(doc.css('a')[0].attr('href')) expect(doc.css('a')[0].attr('href'))
.to eq project_issue_path(project, issue) .to eq urls.project_issue_path(project, issue)
expect(doc.css('a')[0].text).to eq issue.to_reference expect(doc.css('a')[0].text).to eq issue.to_reference
wrapped = helper.link_to_html(rendered, link) wrapped = helper.link_to_html(rendered, link)
...@@ -205,6 +205,17 @@ describe MarkupHelper do ...@@ -205,6 +205,17 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown) allow(@wiki).to receive(:format).and_return(:markdown)
expect(helper).to receive(:markdown_unsafe).with('wiki content',
pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
issuable_state_filter_enabled: true)
helper.render_wiki_content(@wiki)
end
it 'uses Wiki pipeline for markdown files with RedCarpet if feature disabled' do
stub_feature_flags(commonmark_for_repositories: false)
allow(@wiki).to receive(:format).and_return(:markdown)
expect(helper).to receive(:markdown_unsafe).with('wiki content', expect(helper).to receive(:markdown_unsafe).with('wiki content',
pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
issuable_state_filter_enabled: true, markdown_engine: :redcarpet) issuable_state_filter_enabled: true, markdown_engine: :redcarpet)
...@@ -259,10 +270,18 @@ describe MarkupHelper do ...@@ -259,10 +270,18 @@ describe MarkupHelper do
expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>') expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
end end
it 'defaults to Redcarpet' do it 'defaults to CommonMark' do
expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL') expect(helper.markup('foo.md', 'x^2')).to include('x^2')
end
expect(helper.markup('foo.md', content)).to eq('NOEL') it 'honors markdown_engine for RedCarpet' do
expect(helper.markup('foo.md', 'x^2', { markdown_engine: :redcarpet })).to include('x<sup>2</sup>')
end
it 'uses RedCarpet if feature disabled' do
stub_feature_flags(commonmark_for_repositories: false)
expect(helper.markup('foo.md', 'x^2', { markdown_engine: :redcarpet })).to include('x<sup>2</sup>')
end end
end end
...@@ -414,4 +433,8 @@ describe MarkupHelper do ...@@ -414,4 +433,8 @@ describe MarkupHelper do
expect(helper.cross_project_reference(project, issue)).to include(project.full_path) expect(helper.cross_project_reference(project, issue)).to include(project.full_path)
end end
end end
def urls
Gitlab::Routing.url_helpers
end
end end
...@@ -479,4 +479,16 @@ describe ProjectsHelper do ...@@ -479,4 +479,16 @@ describe ProjectsHelper do
end end
end end
end end
describe '#legacy_render_context' do
it 'returns the redcarpet engine' do
params = { legacy_render: '1' }
expect(helper.legacy_render_context(params)).to include(markdown_engine: :redcarpet)
end
it 'returns nothing' do
expect(helper.legacy_render_context({})).to be_empty
end
end
end end
...@@ -121,6 +121,13 @@ describe Banzai::Pipeline::WikiPipeline do ...@@ -121,6 +121,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"") expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
end end
it 'rewrites non-file links (with spaces) to be at the scope of the wiki root' do
markdown = "[Link to Page](page slug)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page%20slug\"")
end
it "rewrites file links to be at the scope of the current directory" do it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](page.md)" markdown = "[Link to Page](page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug) output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
...@@ -134,6 +141,13 @@ describe Banzai::Pipeline::WikiPipeline do ...@@ -134,6 +141,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"") expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
end end
it 'rewrites links (with spaces) with anchor' do
markdown = '[Link to Header](start page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start%20page#title\"")
end
end end
describe "when creating root links" do describe "when creating root links" do
......
...@@ -119,6 +119,10 @@ describe Feature do ...@@ -119,6 +119,10 @@ describe Feature do
expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey
end end
it 'returns true for undefined feature with default_enabled' do
expect(described_class.enabled?(:some_random_feature_flag, default_enabled: true)).to be_truthy
end
it 'returns false for existing disabled feature in the database' do it 'returns false for existing disabled feature in the database' do
described_class.disable(:disabled_feature_flag) described_class.disable(:disabled_feature_flag)
...@@ -160,6 +164,10 @@ describe Feature do ...@@ -160,6 +164,10 @@ describe Feature do
expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy
end end
it 'returns false for undefined feature with default_enabled' do
expect(described_class.disabled?(:some_random_feature_flag, default_enabled: true)).to be_falsey
end
it 'returns true for existing disabled feature in the database' do it 'returns true for existing disabled feature in the database' do
described_class.disable(:disabled_feature_flag) described_class.disable(:disabled_feature_flag)
......
...@@ -328,6 +328,22 @@ describe RuboCop::Cop::LineBreakAroundConditionalBlock do ...@@ -328,6 +328,22 @@ describe RuboCop::Cop::LineBreakAroundConditionalBlock do
expect(cop.offenses).to be_empty expect(cop.offenses).to be_empty
end end
it "doesn't flag violation for #{conditional} preceded by a rescue" do
source = <<~RUBY
def a_method
do_something
rescue
#{conditional} condition
do_something
end
end
RUBY
inspect_source(source)
expect(cop.offenses).to be_empty
end
it "doesn't flag violation for #{conditional} followed by a rescue" do it "doesn't flag violation for #{conditional} followed by a rescue" do
source = <<~RUBY source = <<~RUBY
def a_method def a_method
......
...@@ -101,4 +101,11 @@ describe PreviewMarkdownService do ...@@ -101,4 +101,11 @@ describe PreviewMarkdownService do
expect(result[:markdown_engine]).to eq :common_mark expect(result[:markdown_engine]).to eq :common_mark
end end
it 'honors the legacy_render parameter' do
service = described_class.new(project, user, { legacy_render: '1' })
result = service.execute
expect(result[:markdown_engine]).to eq :redcarpet
end
end end
...@@ -4,8 +4,8 @@ module StubFeatureFlags ...@@ -4,8 +4,8 @@ module StubFeatureFlags
# @param [Hash] features where key is feature name and value is boolean whether enabled or not # @param [Hash] features where key is feature name and value is boolean whether enabled or not
def stub_feature_flags(features) def stub_feature_flags(features)
features.each do |feature_name, enabled| features.each do |feature_name, enabled|
allow(Feature).to receive(:enabled?).with(feature_name) { enabled } allow(Feature).to receive(:enabled?).with(feature_name, any_args) { enabled }
allow(Feature).to receive(:enabled?).with(feature_name.to_s) { enabled } allow(Feature).to receive(:enabled?).with(feature_name.to_s, any_args) { enabled }
end end
end end
end end
...@@ -5274,13 +5274,13 @@ moment@2.x, moment@^2.18.1: ...@@ -5274,13 +5274,13 @@ moment@2.x, moment@^2.18.1:
version "2.19.2" version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
monaco-editor-webpack-plugin@^1.4.0: monaco-editor-webpack-plugin@^1.5.2:
version "1.4.0" version "1.5.2"
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f" resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.5.2.tgz#e113fa1d5759ede6fd776eb620cdd5930203b55a"
monaco-editor@0.13.1: monaco-editor@^0.14.3:
version "0.13.1" version "0.14.3"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.13.1.tgz#6b9ce20e4d1c945042d256825eb133cb23315a52" resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.14.3.tgz#7cc4a4096a3821f52fea9b10489b527ef3034e22"
mousetrap@^1.4.6: mousetrap@^1.4.6:
version "1.4.6" version "1.4.6"
......
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