Commit 3275f76c authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-08-10

# Conflicts:
#	app/assets/javascripts/reports/components/issues_list.vue
#	app/assets/javascripts/reports/components/report_issues.vue
#	app/models/project_services/hipchat_service.rb
#	app/serializers/environment_entity.rb
#	app/services/ci/enqueue_build_service.rb
#	app/views/projects/mirrors/_instructions.html.haml
#	app/views/projects/mirrors/_mirror_repos.html.haml
#	app/views/projects/mirrors/_mirror_repos_form.html.haml
#	locale/gitlab.pot

[ci skip]
parents d7318ba3 9964b678
...@@ -5,9 +5,12 @@ import { ...@@ -5,9 +5,12 @@ import {
STATUS_FAILED, STATUS_FAILED,
STATUS_NEUTRAL, STATUS_NEUTRAL,
} from '~/reports/constants'; } from '~/reports/constants';
<<<<<<< HEAD
import { componentNames } from 'ee/vue_shared/components/reports/issue_body'; import { componentNames } from 'ee/vue_shared/components/reports/issue_body';
import SastContainerInfo from 'ee/vue_shared/security_reports/components/sast_container_info.vue'; import SastContainerInfo from 'ee/vue_shared/security_reports/components/sast_container_info.vue';
=======
>>>>>>> upstream/master
/** /**
* Renders block of issues * Renders block of issues
......
<script> <script>
import IssueStatusIcon from '~/reports/components/issue_status_icon.vue'; import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
<<<<<<< HEAD
import { components, componentNames } from 'ee/vue_shared/components/reports/issue_body'; import { components, componentNames } from 'ee/vue_shared/components/reports/issue_body';
=======
import { components, componentNames } from '~/reports/components/issue_body';
>>>>>>> upstream/master
export default { export default {
name: 'ReportIssues', name: 'ReportIssues',
......
module MirrorHelper
def mirrors_form_data_attributes
{ project_mirror_endpoint: project_mirror_path(@project) }
end
end
...@@ -230,7 +230,7 @@ module Ci ...@@ -230,7 +230,7 @@ module Ci
end end
def cancelable? def cancelable?
active? active? || created?
end end
def retryable? def retryable?
......
# frozen_string_literal: true
class ProgrammingLanguage < ActiveRecord::Base class ProgrammingLanguage < ActiveRecord::Base
validates :name, presence: true validates :name, presence: true
validates :color, allow_blank: false, color: true validates :color, allow_blank: false, color: true
......
...@@ -477,6 +477,24 @@ class Project < ActiveRecord::Base ...@@ -477,6 +477,24 @@ class Project < ActiveRecord::Base
}x }x
end end
def reference_postfix
'>'
end
def reference_postfix_escaped
'&gt;'
end
# Pattern used to extract `namespace/project>` project references from text.
# '>' or its escaped form ('&gt;') are checked for because '>' is sometimes escaped
# when the reference comes from an external source.
def markdown_reference_pattern
%r{
#{reference_pattern}
(#{reference_postfix}|#{reference_postfix_escaped})
}x
end
def trending def trending
joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id') joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id')
.reorder('trending_projects.id ASC') .reorder('trending_projects.id ASC')
...@@ -925,6 +943,10 @@ class Project < ActiveRecord::Base ...@@ -925,6 +943,10 @@ class Project < ActiveRecord::Base
end end
end end
def to_reference_with_postfix
"#{to_reference(full: true)}#{self.class.reference_postfix}"
end
# `from` argument can be a Namespace or Project. # `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false) def to_reference(from = nil, full: false)
if full || cross_namespace_reference?(from) if full || cross_namespace_reference?(from)
......
# frozen_string_literal: true
require 'asana' require 'asana'
class AsanaService < Service class AsanaService < Service
......
# frozen_string_literal: true
class AssemblaService < Service class AssemblaService < Service
prop_accessor :token, :subdomain prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
......
# frozen_string_literal: true
class BambooService < CiService class BambooService < CiService
include ReactiveService include ReactiveService
......
# frozen_string_literal: true
class BugzillaService < IssueTrackerService class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
......
# frozen_string_literal: true
require "addressable/uri" require "addressable/uri"
class BuildkiteService < CiService class BuildkiteService < CiService
......
# frozen_string_literal: true
# This class is to be removed with 9.1 # This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database # We should also by then remove BuildsEmailService from database
class BuildsEmailService < Service class BuildsEmailService < Service
......
# frozen_string_literal: true
class CampfireService < Service class CampfireService < Service
prop_accessor :token, :subdomain, :room prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
...@@ -82,7 +84,7 @@ class CampfireService < Service ...@@ -82,7 +84,7 @@ class CampfireService < Service
before = push[:before] before = push[:before]
after = push[:after] after = push[:after]
message = "" message = []
message << "[#{project.full_name}] " message << "[#{project.full_name}] "
message << "#{push[:user_name]} " message << "#{push[:user_name]} "
...@@ -95,6 +97,6 @@ class CampfireService < Service ...@@ -95,6 +97,6 @@ class CampfireService < Service
message << "#{project.web_url}/compare/#{before}...#{after}" message << "#{project.web_url}/compare/#{before}...#{after}"
end end
message message.join
end end
end end
# frozen_string_literal: true
require 'slack-notifier' require 'slack-notifier'
module ChatMessage module ChatMessage
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class IssueMessage < BaseMessage class IssueMessage < BaseMessage
attr_reader :title attr_reader :title
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class MergeMessage < BaseMessage class MergeMessage < BaseMessage
attr_reader :merge_request_iid attr_reader :merge_request_iid
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class NoteMessage < BaseMessage class NoteMessage < BaseMessage
attr_reader :note attr_reader :note
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class PipelineMessage < BaseMessage class PipelineMessage < BaseMessage
attr_reader :ref_type attr_reader :ref_type
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class PushMessage < BaseMessage class PushMessage < BaseMessage
attr_reader :after attr_reader :after
......
# frozen_string_literal: true
module ChatMessage module ChatMessage
class WikiPageMessage < BaseMessage class WikiPageMessage < BaseMessage
attr_reader :title attr_reader :title
......
# frozen_string_literal: true
# Base class for Chat notifications services # Base class for Chat notifications services
# This class is not meant to be used directly, but only to inherit from. # This class is not meant to be used directly, but only to inherit from.
class ChatNotificationService < Service class ChatNotificationService < Service
......
# frozen_string_literal: true
# Base class for CI services # Base class for CI services
# List methods you need to implement to get your CI service # List methods you need to implement to get your CI service
# working with GitLab Merge Requests # working with GitLab Merge Requests
......
# frozen_string_literal: true
class CustomIssueTrackerService < IssueTrackerService class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
......
# frozen_string_literal: true
# Base class for deployment services # Base class for deployment services
# #
# These services integrate with a deployment solution like Kubernetes/OpenShift, # These services integrate with a deployment solution like Kubernetes/OpenShift,
......
# frozen_string_literal: true
class DroneCiService < CiService class DroneCiService < CiService
include ReactiveService include ReactiveService
......
# frozen_string_literal: true
class EmailsOnPushService < Service class EmailsOnPushService < Service
boolean_accessor :send_from_committer_email boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs boolean_accessor :disable_diffs
......
# frozen_string_literal: true
class ExternalWikiService < Service class ExternalWikiService < Service
prop_accessor :external_wiki_url prop_accessor :external_wiki_url
......
# frozen_string_literal: true
require "flowdock-git-hook" require "flowdock-git-hook"
# Flow dock depends on Grit to compute the number of commits between two given # Flow dock depends on Grit to compute the number of commits between two given
......
# frozen_string_literal: true
require "gemnasium/gitlab_service" require "gemnasium/gitlab_service"
class GemnasiumService < Service class GemnasiumService < Service
......
# frozen_string_literal: true
class GitlabIssueTrackerService < IssueTrackerService class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing include Gitlab::Routing
......
# frozen_string_literal: true
require 'hangouts_chat' require 'hangouts_chat'
class HangoutsChatService < ChatNotificationService class HangoutsChatService < ChatNotificationService
......
# frozen_string_literal: true
class HipchatService < Service class HipchatService < Service
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
...@@ -108,7 +110,7 @@ class HipchatService < Service ...@@ -108,7 +110,7 @@ class HipchatService < Service
before = push[:before] before = push[:before]
after = push[:after] after = push[:after]
message = "" message = []
message << "#{push[:user_name]} " message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before) if Gitlab::Git.blank_ref?(before)
...@@ -132,7 +134,7 @@ class HipchatService < Service ...@@ -132,7 +134,7 @@ class HipchatService < Service
end end
end end
message message.join
end end
def markdown(text, options = {}) def markdown(text, options = {})
...@@ -165,11 +167,11 @@ class HipchatService < Service ...@@ -165,11 +167,11 @@ class HipchatService < Service
description = obj_attr[:description] description = obj_attr[:description]
issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>" issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
message = "#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"
message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
message << "<pre>#{markdown(description)}</pre>" message << "<pre>#{markdown(description)}</pre>"
message message.join
end end
def create_merge_request_message(data) def create_merge_request_message(data)
...@@ -181,16 +183,23 @@ class HipchatService < Service ...@@ -181,16 +183,23 @@ class HipchatService < Service
state = obj_attr[:state] state = obj_attr[:state]
description = obj_attr[:description] description = obj_attr[:description]
title = render_line(obj_attr[:title]) title = render_line(obj_attr[:title])
<<<<<<< HEAD
action = obj_attr[:action] action = obj_attr[:action]
state_or_action_text = action == 'approved' ? action : state state_or_action_text = action == 'approved' ? action : state
merge_request_url = "#{project_url}/merge_requests/#{merge_request_iid}" merge_request_url = "#{project_url}/merge_requests/#{merge_request_iid}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_iid}</a>" merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_iid}</a>"
message = "#{user_name} #{state_or_action_text} #{merge_request_link} in " \ message = "#{user_name} #{state_or_action_text} #{merge_request_link} in " \
"#{project_link}: <b>#{title}</b>" "#{project_link}: <b>#{title}</b>"
=======
message << "<pre>#{markdown(description)}</pre>" merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
message = ["#{user_name} #{state} #{merge_request_link} in " \
"#{project_link}: <b>#{title}</b>"]
>>>>>>> upstream/master
message message << "<pre>#{markdown(description)}</pre>"
message.join
end end
def format_title(title) def format_title(title)
...@@ -236,12 +245,11 @@ class HipchatService < Service ...@@ -236,12 +245,11 @@ class HipchatService < Service
end end
subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>" subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
message = "#{user_name} commented on #{subject_html} in #{project_link}: " message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
message << title message << title
message << "<pre>#{markdown(note, ref: commit_id)}</pre>" message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
message.join
message
end end
def create_pipeline_message(data) def create_pipeline_message(data)
......
# frozen_string_literal: true
require 'uri' require 'uri'
class IrkerService < Service class IrkerService < Service
......
# frozen_string_literal: true
class IssueTrackerService < Service class IssueTrackerService < Service
validate :one_issue_tracker, if: :activated?, on: :manual_change validate :one_issue_tracker, if: :activated?, on: :manual_change
......
# frozen_string_literal: true
class JiraService < IssueTrackerService class JiraService < IssueTrackerService
include Gitlab::Routing include Gitlab::Routing
include ApplicationHelper include ApplicationHelper
......
# frozen_string_literal: true
## ##
# NOTE: # NOTE:
# We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic. # We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic.
......
# frozen_string_literal: true
class MattermostService < ChatNotificationService class MattermostService < ChatNotificationService
def title def title
'Mattermost notifications' 'Mattermost notifications'
......
# frozen_string_literal: true
class MattermostSlashCommandsService < SlashCommandsService class MattermostSlashCommandsService < SlashCommandsService
include TriggersHelper include TriggersHelper
......
# frozen_string_literal: true
class MicrosoftTeamsService < ChatNotificationService class MicrosoftTeamsService < ChatNotificationService
def title def title
'Microsoft Teams Notification' 'Microsoft Teams Notification'
......
# frozen_string_literal: true
# For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service # For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service
class MockCiService < CiService class MockCiService < CiService
ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze
......
# frozen_string_literal: true
class MockDeploymentService < DeploymentService class MockDeploymentService < DeploymentService
def title def title
'Mock deployment' 'Mock deployment'
......
# frozen_string_literal: true
class MockMonitoringService < MonitoringService class MockMonitoringService < MonitoringService
def title def title
'Mock monitoring' 'Mock monitoring'
......
# frozen_string_literal: true
# Base class for monitoring services # Base class for monitoring services
# #
# These services integrate with a deployment solution like Prometheus # These services integrate with a deployment solution like Prometheus
......
# frozen_string_literal: true
class PackagistService < Service class PackagistService < Service
prop_accessor :username, :token, :server prop_accessor :username, :token, :server
......
# frozen_string_literal: true
class PipelinesEmailService < Service class PipelinesEmailService < Service
prop_accessor :recipients prop_accessor :recipients
boolean_accessor :notify_only_broken_pipelines boolean_accessor :notify_only_broken_pipelines
......
# frozen_string_literal: true
class PivotaltrackerService < Service class PivotaltrackerService < Service
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
......
# frozen_string_literal: true
class PrometheusService < MonitoringService class PrometheusService < MonitoringService
include PrometheusAdapter include PrometheusAdapter
......
# frozen_string_literal: true
class PushoverService < Service class PushoverService < Service
BASE_URI = 'https://api.pushover.net/1'.freeze BASE_URI = 'https://api.pushover.net/1'.freeze
...@@ -79,7 +81,7 @@ class PushoverService < Service ...@@ -79,7 +81,7 @@ class PushoverService < Service
end end
if data[:total_commits_count] > 0 if data[:total_commits_count] > 0
message << "\nTotal commits count: #{data[:total_commits_count]}" message = [message, "Total commits count: #{data[:total_commits_count]}"].join("\n")
end end
pushover_data = { pushover_data = {
......
# frozen_string_literal: true
class RedmineService < IssueTrackerService class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
......
# frozen_string_literal: true
class SlackService < ChatNotificationService class SlackService < ChatNotificationService
def title def title
'Slack notifications' 'Slack notifications'
......
# frozen_string_literal: true
class SlackSlashCommandsService < SlashCommandsService class SlackSlashCommandsService < SlashCommandsService
prepend EE::SlackSlashCommandsService prepend EE::SlackSlashCommandsService
include TriggersHelper include TriggersHelper
......
# frozen_string_literal: true
# Base class for Chat services # Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from. # This class is not meant to be used directly, but only to inherrit from.
class SlashCommandsService < Service class SlashCommandsService < Service
......
# frozen_string_literal: true
class TeamcityService < CiService class TeamcityService < CiService
include ReactiveService include ReactiveService
......
# frozen_string_literal: true
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
end end
# frozen_string_literal: true
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
end end
# frozen_string_literal: true
class ProtectedTag::CreateAccessLevel < ActiveRecord::Base class ProtectedTag::CreateAccessLevel < ActiveRecord::Base
include ProtectedTagAccess include ProtectedTagAccess
......
# frozen_string_literal: true
class RepositoryLanguage < ActiveRecord::Base class RepositoryLanguage < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :programming_language belongs_to :programming_language
......
# frozen_string_literal: true
class SiteStatistic < ActiveRecord::Base class SiteStatistic < ActiveRecord::Base
# prevents the creation of multiple rows # prevents the creation of multiple rows
default_value_for :id, 1 default_value_for :id, 1
......
# frozen_string_literal: true
module Storage module Storage
class HashedProject class HashedProject
attr_accessor :project attr_accessor :project
......
# frozen_string_literal: true
module Storage module Storage
class LegacyProject class LegacyProject
attr_accessor :project attr_accessor :project
......
...@@ -47,10 +47,13 @@ class EnvironmentEntity < Grape::Entity ...@@ -47,10 +47,13 @@ class EnvironmentEntity < Grape::Entity
request.current_user request.current_user
end end
<<<<<<< HEAD
def can_read_deploy_board? def can_read_deploy_board?
can?(current_user, :read_deploy_board, environment.project) can?(current_user, :read_deploy_board, environment.project)
end end
=======
>>>>>>> upstream/master
def can_access_terminal? def can_access_terminal?
can?(request.current_user, :create_environment_terminal, environment) can?(request.current_user, :create_environment_terminal, environment)
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module Ci module Ci
class EnqueueBuildService < BaseService class EnqueueBuildService < BaseService
<<<<<<< HEAD
prepend EE::Ci::EnqueueBuildService prepend EE::Ci::EnqueueBuildService
=======
>>>>>>> upstream/master
def execute(build) def execute(build)
build.enqueue build.enqueue
end end
......
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
= _('The repository must be accessible over <code>http://</code>, = _('The repository must be accessible over <code>http://</code>,
<code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe <code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe
%li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe %li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe
<<<<<<< HEAD
%li= _("The update action will time out after #{import_will_timeout_message(Gitlab.config.gitlab_shell.git_timeout)} minutes. For big repositories, use a clone/push combination.") %li= _("The update action will time out after #{import_will_timeout_message(Gitlab.config.gitlab_shell.git_timeout)} minutes. For big repositories, use a clone/push combination.")
=======
%li= _('The update action will time out after 15 minutes. For big repositories, use a clone/push combination.')
>>>>>>> upstream/master
%li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe %li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe
%li %li
= _('This user will be the author of all events in the activity feed that are the result of an update, = _('This user will be the author of all events in the activity feed that are the result of an update,
......
...@@ -12,15 +12,26 @@ ...@@ -12,15 +12,26 @@
.settings-content .settings-content
= form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'false', data: mirrors_form_data_attributes } do |f| = form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'false', data: mirrors_form_data_attributes } do |f|
<<<<<<< HEAD
.card .card
.card-header .card-header
%h3.card-title= _('Mirror a repository') %h3.card-title= _('Mirror a repository')
.card-body .card-body
=======
.panel.panel-default
.panel-heading
%h3.panel-title= _('Mirror a repository')
.panel-body
>>>>>>> upstream/master
%div= form_errors(@project) %div= form_errors(@project)
.form-group.has-feedback .form-group.has-feedback
= label_tag :url, _('Git repository URL'), class: 'label-light' = label_tag :url, _('Git repository URL'), class: 'label-light'
<<<<<<< HEAD
= text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", title: 'A valid repository URL is required' = text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", title: 'A valid repository URL is required'
=======
= text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
>>>>>>> upstream/master
= render 'projects/mirrors/instructions' = render 'projects/mirrors/instructions'
...@@ -29,12 +40,21 @@ ...@@ -29,12 +40,21 @@
.form-check.append-bottom-10 .form-check.append-bottom-10
= check_box_tag :only_protected_branches, '1', false, class: 'js-mirror-protected form-check-input' = check_box_tag :only_protected_branches, '1', false, class: 'js-mirror-protected form-check-input'
= label_tag :only_protected_branches, _('Only mirror protected branches'), class: 'form-check-label' = label_tag :only_protected_branches, _('Only mirror protected branches'), class: 'form-check-label'
<<<<<<< HEAD
= link_to icon('question-circle'), help_page_path('user/project/protected_branches'), target: '_blank' = link_to icon('question-circle'), help_page_path('user/project/protected_branches'), target: '_blank'
.card-footer .card-footer
= f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror = f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror
.card .card
=======
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
.panel-footer
= f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror
.panel.panel-default
>>>>>>> upstream/master
.table-responsive .table-responsive
%table.table.push-pull-table %table.table.push-pull-table
%thead %thead
......
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
.form-group .form-group
= label_tag :auth_method, _('Authentication method'), class: 'label-bold' = label_tag :auth_method, _('Authentication method'), class: 'label-bold'
<<<<<<< HEAD
= select_tag :auth_method, options_for_select([[[_('None'), 'none'], [_('Password'), 'password']], 'none'), { class: "form-control js-auth-method" } = select_tag :auth_method, options_for_select([[[_('None'), 'none'], [_('Password'), 'password']], 'none'), { class: "form-control js-auth-method" }
=======
= select_tag :auth_method, options_for_select([[_('None'), 'none'], [_('Password'), 'password']], 'none'), { class: "form-control js-auth-method" }
>>>>>>> upstream/master
.form-group.js-password-group.collapse .form-group.js-password-group.collapse
= label_tag :password, _('Password'), class: 'label-bold' = label_tag :password, _('Password'), class: 'label-bold'
......
---
title: CE Port of Protected Environments backend
merge_request: 20859
author:
type: other
---
title: Add the ability to reference projects in comments and other markdown text.
merge_request: 20285
author: Reuben Pereira
type: added
---
title: Allows to cancel a Created job
merge_request: 20635
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Enable frozen string in rest of app/models/**/*.rb
merge_request: gfyoung
author:
type: performance
---
title: Rails5 fix specs duplicate key value violates unique constraint 'index_gpg_signatures_on_commit_sha'
merge_request: 21119
author: Jasper Maes
type: fixed
...@@ -260,6 +260,7 @@ GFM will recognize the following: ...@@ -260,6 +260,7 @@ GFM will recognize the following:
| `@user_name` | specific user | | `@user_name` | specific user |
| `@group_name` | specific group | | `@group_name` | specific group |
| `@all` | entire team | | `@all` | entire team |
| `namespace/project>` | project |
| `#12345` | issue | | `#12345` | issue |
| `!123` | merge request | | `!123` | merge request |
| `$123` | snippet | | `$123` | snippet |
......
...@@ -1147,7 +1147,6 @@ X-Gitlab-Event: Build Hook ...@@ -1147,7 +1147,6 @@ X-Gitlab-Event: Build Hook
}, },
"repository": { "repository": {
"name": "gitlab_test", "name": "gitlab_test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"description": "Atque in sunt eos similique dolores voluptatem.", "description": "Atque in sunt eos similique dolores voluptatem.",
"homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test", "homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
......
module Banzai
module Filter
# HTML filter that replaces project references with links.
class ProjectReferenceFilter < ReferenceFilter
self.reference_type = :project
# Public: Find `namespace/project>` project references in text
#
# ProjectReferenceFilter.references_in(text) do |match, project|
# "<a href=...>#{project}></a>"
# end
#
# text - String text to search.
#
# Yields the String match, and the String project name.
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
text.gsub(Project.markdown_reference_pattern) do |match|
yield match, "#{$~[:namespace]}/#{$~[:project]}"
end
end
def call
ref_pattern = Project.markdown_reference_pattern
ref_pattern_start = /\A#{ref_pattern}\z/
nodes.each do |node|
if text_node?(node)
replace_text_when_pattern_matches(node, ref_pattern) do |content|
project_link_filter(content)
end
elsif element_node?(node)
yield_valid_link(node) do |link, inner_html|
if link =~ ref_pattern_start
replace_link_node_with_href(node, link) do
project_link_filter(link, link_content: inner_html)
end
end
end
end
end
doc
end
# Replace `namespace/project>` project references in text with links to the referenced
# project page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `namespace/project>` references replaced with links. All links
# have `gfm` and `gfm-project` class names attached for styling.
def project_link_filter(text, link_content: nil)
self.class.references_in(text) do |match, project_path|
cached_call(:banzai_url_for_object, match, path: [Project, project_path.downcase]) do
if project = projects_hash[project_path.downcase]
link_to_project(project, link_content: link_content) || match
else
match
end
end
end
end
# Returns a Hash containing all Project objects for the project
# references in the current document.
#
# The keys of this Hash are the project paths, the values the
# corresponding Project objects.
def projects_hash
@projects ||= Project.eager_load(:route, namespace: [:route])
.where_full_path_in(projects)
.index_by(&:full_path)
.transform_keys(&:downcase)
end
# Returns all projects referenced in the current document.
def projects
refs = Set.new
nodes.each do |node|
node.to_html.scan(Project.markdown_reference_pattern) do
refs << "#{$~[:namespace]}/#{$~[:project]}"
end
end
refs.to_a
end
private
def urls
Gitlab::Routing.url_helpers
end
def link_class
reference_class(:project)
end
def link_to_project(project, link_content: nil)
url = urls.project_url(project, only_path: context[:only_path])
data = data_attribute(project: project.id)
content = link_content || project.to_reference_with_postfix
link_tag(url, data, content, project.name)
end
def link_tag(url, data, link_content, title)
%(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
end
end
end
end
...@@ -38,6 +38,7 @@ module Banzai ...@@ -38,6 +38,7 @@ module Banzai
def self.reference_filters def self.reference_filters
[ [
Filter::UserReferenceFilter, Filter::UserReferenceFilter,
Filter::ProjectReferenceFilter,
Filter::IssueReferenceFilter, Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter, Filter::ExternalIssueReferenceFilter,
Filter::MergeRequestReferenceFilter, Filter::MergeRequestReferenceFilter,
......
module Banzai
module ReferenceParser
class ProjectParser < BaseParser
include Gitlab::Utils::StrongMemoize
self.reference_type = :project
def references_relation
Project
end
private
# Returns an Array of Project ids that can be read by the given user.
#
# user - The User for which to check the projects
def readable_project_ids_for(user)
@project_ids_by_user ||= {}
@project_ids_by_user[user] ||=
Project.public_or_visible_to_user(user).where("projects.id IN (?)", @projects_for_nodes.values.map(&:id)).pluck(:id)
end
def can_read_reference?(user, ref_project, node)
readable_project_ids_for(user).include?(ref_project.try(:id))
end
end
end
end
...@@ -2867,6 +2867,7 @@ msgstr "" ...@@ -2867,6 +2867,7 @@ msgstr ""
msgid "Environments|You don't have any environments right now." msgid "Environments|You don't have any environments right now."
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Environments|protected" msgid "Environments|protected"
msgstr "" msgstr ""
...@@ -2906,6 +2907,8 @@ msgstr "" ...@@ -2906,6 +2907,8 @@ msgstr ""
msgid "Epics|start" msgid "Epics|start"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Error" msgid "Error"
msgstr "" msgstr ""
...@@ -3966,9 +3969,12 @@ msgstr "" ...@@ -3966,9 +3969,12 @@ msgstr ""
msgid "Inline" msgid "Inline"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Input host keys manually" msgid "Input host keys manually"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Input your repository URL" msgid "Input your repository URL"
msgstr "" msgstr ""
...@@ -4626,6 +4632,21 @@ msgstr "" ...@@ -4626,6 +4632,21 @@ msgstr ""
msgid "Mirroring repositories" msgid "Mirroring repositories"
msgstr "" msgstr ""
msgid "Mirror a repository"
msgstr ""
msgid "Mirror direction"
msgstr ""
msgid "Mirror repository"
msgstr ""
msgid "Mirrored repositories"
msgstr ""
msgid "Mirroring repositories"
msgstr ""
msgid "MissingSSHKeyWarningLink|add an SSH key" msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "" msgstr ""
...@@ -5788,6 +5809,7 @@ msgstr "" ...@@ -5788,6 +5809,7 @@ msgstr ""
msgid "Public pipelines" msgid "Public pipelines"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Pull" msgid "Pull"
msgstr "" msgstr ""
...@@ -5797,6 +5819,11 @@ msgstr "" ...@@ -5797,6 +5819,11 @@ msgstr ""
msgid "Push Rules" msgid "Push Rules"
msgstr "" msgstr ""
=======
msgid "Push"
msgstr ""
>>>>>>> upstream/master
msgid "Push events" msgid "Push events"
msgstr "" msgstr ""
...@@ -6304,9 +6331,12 @@ msgstr "" ...@@ -6304,9 +6331,12 @@ msgstr ""
msgid "Set up Koding" msgid "Set up Koding"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}" msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically." msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "" msgstr ""
...@@ -6791,12 +6821,15 @@ msgstr "" ...@@ -6791,12 +6821,15 @@ msgstr ""
msgid "Test coverage parsing" msgid "Test coverage parsing"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Thanks! Don't show me this again" msgid "Thanks! Don't show me this again"
msgstr "" msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project." msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "The Git LFS objects will <strong>not</strong> be synced." msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr "" msgstr ""
...@@ -6908,6 +6941,9 @@ msgstr "" ...@@ -6908,6 +6941,9 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage." msgid "The time taken by each data entry gathered by that stage."
msgstr "" msgstr ""
msgid "The update action will time out after 15 minutes. For big repositories, use a clone/push combination."
msgstr ""
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side." msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr "" msgstr ""
...@@ -7076,6 +7112,7 @@ msgstr "" ...@@ -7076,6 +7112,7 @@ msgstr ""
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches." msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user." msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
msgstr "" msgstr ""
...@@ -7085,6 +7122,8 @@ msgstr "" ...@@ -7085,6 +7122,8 @@ msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here." msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Time before an issue gets scheduled" msgid "Time before an issue gets scheduled"
msgstr "" msgstr ""
...@@ -7458,6 +7497,7 @@ msgstr "" ...@@ -7458,6 +7497,7 @@ msgstr ""
msgid "Updating" msgid "Updating"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Upgrade your plan to activate Advanced Global Search." msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "" msgstr ""
...@@ -7473,6 +7513,8 @@ msgstr "" ...@@ -7473,6 +7513,8 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards." msgid "Upgrade your plan to improve Issue boards."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:" msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr "" msgstr ""
......
require 'spec_helper' require 'spec_helper'
describe 'Blob shortcuts' do describe 'Blob shortcuts', :js do
include TreeHelper include TreeHelper
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:path) { project.repository.ls_files(project.repository.root_ref)[0] } let(:path) { project.repository.ls_files(project.repository.root_ref)[0] }
......
...@@ -210,7 +210,7 @@ describe "User browses files" do ...@@ -210,7 +210,7 @@ describe "User browses files" do
end end
end end
context "when browsing a file content" do context "when browsing a file content", :js do
before do before do
visit(tree_path_root_ref) visit(tree_path_root_ref)
......
require 'spec_helper' require 'spec_helper'
describe 'Projects > Files > User deletes files' do describe 'Projects > Files > User deletes files', :js do
let(:fork_message) do let(:fork_message) do
"You're not allowed to make changes to this project directly. "\ "You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request." "A fork of this project has been created that you can make changes in, so you can submit a merge request."
......
require 'spec_helper' require 'spec_helper'
describe 'Projects > Files > User edits files' do describe 'Projects > Files > User edits files', :js do
include ProjectForksHelper include ProjectForksHelper
let(:project) { create(:project, :repository, name: 'Shop') } let(:project) { create(:project, :repository, name: 'Shop') }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
......
require 'spec_helper' require 'spec_helper'
describe 'Projects > Files > User replaces files' do describe 'Projects > Files > User replaces files', :js do
include DropzoneHelper include DropzoneHelper
let(:fork_message) do let(:fork_message) do
......
require 'spec_helper'
describe Banzai::Filter::ProjectReferenceFilter do
include FilterSpecHelper
def invalidate_reference(reference)
"#{reference.reverse}"
end
def get_reference(project)
project.to_reference_with_postfix
end
let(:project) { create(:project, :public) }
subject { project }
let(:subject_name) { "project" }
let(:reference) { get_reference(project) }
it_behaves_like 'user reference or project reference'
it 'ignores invalid projects' do
exp = act = "Hey #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq(CGI.escapeHTML(exp))
end
it 'allows references with text after the > character' do
doc = reference_filter("Hey #{reference}foo")
expect(doc.css('a').first.attr('href')).to eq urls.project_url(subject)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Hey #{CGI.escapeHTML(reference)}</#{elem}>"
expect(reference_filter(act).to_html).to eq exp
end
end
it 'includes default classes' do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project has-tooltip'
end
context 'in group context' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:nested_group) { create(:group, :nested) }
let(:nested_project) { create(:project, group: nested_group) }
it 'supports mentioning a project' do
reference = get_reference(project)
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.project_url(project)
end
it 'supports mentioning a project in a nested group' do
reference = get_reference(nested_project)
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.project_url(nested_project)
end
end
describe '#projects_hash' do
it 'returns a Hash containing all Projects' do
document = Nokogiri::HTML.fragment("<p>#{get_reference(project)}</p>")
filter = described_class.new(document, project: project)
expect(filter.projects_hash).to eq({ project.full_path => project })
end
end
describe '#projects' do
it 'returns the projects mentioned in a document' do
document = Nokogiri::HTML.fragment("<p>#{get_reference(project)}</p>")
filter = described_class.new(document, project: project)
expect(filter.projects).to eq([project.full_path])
end
end
end
...@@ -3,9 +3,17 @@ require 'spec_helper' ...@@ -3,9 +3,17 @@ require 'spec_helper'
describe Banzai::Filter::UserReferenceFilter do describe Banzai::Filter::UserReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
def get_reference(user)
user.to_reference
end
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:reference) { user.to_reference } subject { user }
let(:subject_name) { "user" }
let(:reference) { get_reference(user) }
it_behaves_like 'user reference or project reference'
it 'requires project context' do it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
...@@ -66,45 +74,6 @@ describe Banzai::Filter::UserReferenceFilter do ...@@ -66,45 +74,6 @@ describe Banzai::Filter::UserReferenceFilter do
end end
end end
context 'mentioning a user' do
it_behaves_like 'a reference containing an element node'
it 'links to a User' do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end
it 'links to a User with a period' do
user = create(:user, name: 'alphA.Beta')
doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
it 'links to a User with an underscore' do
user = create(:user, name: 'ping_pong_king')
doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
it 'links to a User with different case-sensitivity' do
user = create(:user, username: 'RescueRanger')
doc = reference_filter("Hey #{user.to_reference.upcase}")
expect(doc.css('a').length).to eq 1
expect(doc.css('a').text).to eq(user.to_reference)
end
it 'includes a data-user attribute' do
doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-user')
expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
end
end
context 'mentioning a group' do context 'mentioning a group' do
it_behaves_like 'a reference containing an element node' it_behaves_like 'a reference containing an element node'
...@@ -154,36 +123,6 @@ describe Banzai::Filter::UserReferenceFilter do ...@@ -154,36 +123,6 @@ describe Banzai::Filter::UserReferenceFilter do
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member has-tooltip' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member has-tooltip'
end end
it 'supports an :only_path context' do
doc = reference_filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
expect(link).to eq urls.user_path(user)
end
context 'referencing a user in a link href' do
let(:reference) { %Q{<a href="#{user.to_reference}">User</a>} }
it 'links to a User' do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end
it 'links with adjacent text' do
doc = reference_filter("Mention me (#{reference}.)")
expect(doc.to_html).to match(%r{\(<a.+>User</a>\.\)})
end
it 'includes a data-user attribute' do
doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-user')
expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
end
end
context 'when a project is not specified' do context 'when a project is not specified' do
let(:project) { nil } let(:project) { nil }
...@@ -227,7 +166,7 @@ describe Banzai::Filter::UserReferenceFilter do ...@@ -227,7 +166,7 @@ describe Banzai::Filter::UserReferenceFilter do
end end
it 'supports mentioning a single user' do it 'supports mentioning a single user' do
reference = group_member.to_reference reference = get_reference(group_member)
doc = reference_filter("Hey #{reference}", context) doc = reference_filter("Hey #{reference}", context)
expect(doc.css('a').first.attr('href')).to eq urls.user_url(group_member) expect(doc.css('a').first.attr('href')).to eq urls.user_url(group_member)
...@@ -243,7 +182,7 @@ describe Banzai::Filter::UserReferenceFilter do ...@@ -243,7 +182,7 @@ describe Banzai::Filter::UserReferenceFilter do
describe '#namespaces' do describe '#namespaces' do
it 'returns a Hash containing all Namespaces' do it 'returns a Hash containing all Namespaces' do
document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>") document = Nokogiri::HTML.fragment("<p>#{get_reference(user)}</p>")
filter = described_class.new(document, project: project) filter = described_class.new(document, project: project)
ns = user.namespace ns = user.namespace
...@@ -253,7 +192,7 @@ describe Banzai::Filter::UserReferenceFilter do ...@@ -253,7 +192,7 @@ describe Banzai::Filter::UserReferenceFilter do
describe '#usernames' do describe '#usernames' do
it 'returns the usernames mentioned in a document' do it 'returns the usernames mentioned in a document' do
document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>") document = Nokogiri::HTML.fragment("<p>#{get_reference(user)}</p>")
filter = described_class.new(document, project: project) filter = described_class.new(document, project: project)
expect(filter.usernames).to eq([user.username]) expect(filter.usernames).to eq([user.username])
......
require 'spec_helper'
describe Banzai::ReferenceParser::ProjectParser do
include ReferenceParserHelpers
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
subject { described_class.new(Banzai::RenderContext.new(project, user)) }
let(:link) { empty_html_link }
describe '#referenced_by' do
describe 'when the link has a data-project attribute' do
context 'using an existing project ID' do
it 'returns an Array of projects' do
link['data-project'] = project.id.to_s
expect(subject.gather_references([link])).to eq([project])
end
end
context 'using a non-existing project ID' do
it 'returns an empty Array' do
link['data-project'] = ''
expect(subject.gather_references([link])).to eq([])
end
end
context 'using a private project ID' do
it 'returns an empty Array when unauthorized' do
private_project = create(:project, :private)
link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([])
end
it 'returns an Array when authorized' do
private_project = create(:project, :private, namespace: user.namespace)
link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([private_project])
end
end
end
end
end
...@@ -1104,6 +1104,12 @@ describe Ci::Build do ...@@ -1104,6 +1104,12 @@ describe Ci::Build do
it { is_expected.to be_cancelable } it { is_expected.to be_cancelable }
end end
context 'when build is created' do
let(:build) { create(:ci_build, :created) }
it { is_expected.to be_cancelable }
end
end end
context 'when build is not cancelable' do context 'when build is not cancelable' do
......
...@@ -399,6 +399,15 @@ describe Project do ...@@ -399,6 +399,15 @@ describe Project do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) } it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
end end
describe '#to_reference_with_postfix' do
it 'returns the full path with reference_postfix' do
namespace = create(:namespace, path: 'sample-namespace')
project = create(:project, path: 'sample-project', namespace: namespace)
expect(project.to_reference_with_postfix).to eq 'sample-namespace/sample-project>'
end
end
describe '#to_reference' do describe '#to_reference' do
let(:owner) { create(:user, name: 'Gitlab') } let(:owner) { create(:user, name: 'Gitlab') }
let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) } let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
......
...@@ -11,3 +11,76 @@ shared_examples 'a reference containing an element node' do ...@@ -11,3 +11,76 @@ shared_examples 'a reference containing an element node' do
expect(doc.children.first.inner_html).to eq(inner_html) expect(doc.children.first.inner_html).to eq(inner_html)
end end
end end
# Requires a reference, subject and subject_name:
# subject { create(:user) }
# let(:reference) { subject.to_reference }
# let(:subject_name) { 'user' }
shared_examples 'user reference or project reference' do
shared_examples 'it contains a data- attribute' do
it 'includes a data- attribute' do
doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute("data-#{subject_name}")
expect(link.attr("data-#{subject_name}")).to eq subject.id.to_s
end
end
context 'mentioning a resource' do
it_behaves_like 'a reference containing an element node'
it_behaves_like 'it contains a data- attribute'
it "links to a resource" do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.send("#{subject_name}_url", subject)
end
it 'links to a resource with a period' do
subject = create(subject_name.to_sym, name: 'alphA.Beta')
doc = reference_filter("Hey #{get_reference(subject)}")
expect(doc.css('a').length).to eq 1
end
it 'links to a resource with an underscore' do
subject = create(subject_name.to_sym, name: 'ping_pong_king')
doc = reference_filter("Hey #{get_reference(subject)}")
expect(doc.css('a').length).to eq 1
end
it 'links to a resource with different case-sensitivity' do
subject = create(subject_name.to_sym, name: 'RescueRanger')
reference = get_reference(subject)
doc = reference_filter("Hey #{reference.upcase}")
expect(doc.css('a').length).to eq 1
expect(doc.css('a').text).to eq(reference)
end
end
it 'supports an :only_path context' do
doc = reference_filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
expect(link).to eq urls.send "#{subject_name}_path", subject
end
context 'referencing a resource in a link href' do
let(:reference) { %Q{<a href="#{get_reference(subject)}">Some text</a>} }
it_behaves_like 'it contains a data- attribute'
it 'links to the resource' do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.send "#{subject_name}_url", subject
end
it 'links with adjacent text' do
doc = reference_filter("Mention me (#{reference}.)")
expect(doc.to_html).to match(%r{\(<a.+>Some text</a>\.\)})
end
end
end
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