Commit e8324327 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'master' into 8-0-stable

parents 2c846bbf ac289687
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.0.0 (unreleased) v 8.0.0 (unreleased)
- Fix Markdown links not showing up in dashboard activity feed (Stan Hu)
- Remove milestones from merge requests when milestones are deleted (Stan Hu)
- Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu)
- Fix broken sort in merge request API (Stan Hu) - Fix broken sort in merge request API (Stan Hu)
- Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu)
...@@ -50,6 +52,7 @@ v 8.0.0 (unreleased) ...@@ -50,6 +52,7 @@ v 8.0.0 (unreleased)
- Add support for Crowd - Add support for Crowd
- Global Labels that are available to all projects - Global Labels that are available to all projects
- Fix highlighting of deleted lines in diffs. - Fix highlighting of deleted lines in diffs.
- Project notification level can be set on the project page itself
- Added service API endpoint to retrieve service parameters (Petheő Bence) - Added service API endpoint to retrieve service parameters (Petheő Bence)
- Add FogBugz project import (Jared Szechy) - Add FogBugz project import (Jared Szechy)
- Sort users autocomplete lists by user (Allister Antosik) - Sort users autocomplete lists by user (Allister Antosik)
......
...@@ -24,3 +24,19 @@ class @Project ...@@ -24,3 +24,19 @@ class @Project
$.cookie('hide_no_password_message', 'false', { path: path }) $.cookie('hide_no_password_message', 'false', { path: path })
$(@).parents('.no-password-message').remove() $(@).parents('.no-password-message').remove()
e.preventDefault() e.preventDefault()
$('.update-notification').on 'click', (e) ->
e.preventDefault()
notification_level = $(@).data 'notification-level'
$('#notification_level').val(notification_level)
$('#notification-form').submit()
label = null
switch notification_level
when 0 then label = ' Disabled '
when 1 then label = ' Participating '
when 2 then label = ' Watching '
when 3 then label = ' Global '
when 4 then label = ' On Mention '
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
$(@).parents('ul').find('li.active').removeClass 'active'
$(@).parent().addClass 'active'
\ No newline at end of file
...@@ -89,6 +89,10 @@ a > code { ...@@ -89,6 +89,10 @@ a > code {
} }
} }
.md-area {
@include md-typography;
}
.md { .md {
@include md-typography; @include md-typography;
} }
...@@ -101,6 +105,10 @@ textarea.js-gfm-input { ...@@ -101,6 +105,10 @@ textarea.js-gfm-input {
font-family: $monospace_font; font-family: $monospace_font;
} }
.md-preview {
font-family: $monospace_font;
}
.strikethrough { .strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
.note-edit-form { .note-edit-form {
display: none; display: none;
font-size: 13px; font-size: 15px;
.form-actions { .form-actions {
padding-left: 20px; padding-left: 20px;
......
...@@ -329,3 +329,7 @@ pre.light-well { ...@@ -329,3 +329,7 @@ pre.light-well {
margin-top: -1px; margin-top: -1px;
} }
} }
.inline-form {
display: inline-block;
}
...@@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
before_action :validates_merge_request, only: [:show, :diffs, :commits] before_action :validates_merge_request, only: [:show, :diffs, :commits]
before_action :define_show_vars, only: [:show, :diffs, :commits] before_action :define_show_vars, only: [:show, :diffs, :commits]
before_action :ensure_ref_fetched, only: [:show, :commits, :diffs]
# Allow read any merge_request # Allow read any merge_request
before_action :authorize_read_merge_request! before_action :authorize_read_merge_request!
...@@ -277,4 +278,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -277,4 +278,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:state_event, :description, :task_num, label_ids: [] :state_event, :description, :task_num, label_ids: []
) )
end end
# Make sure merge requests created before 8.0
# have head file in refs/merge-requests/
def ensure_ref_fetched
@merge_request.ensure_ref_fetched
end
end end
...@@ -86,6 +86,10 @@ class ProjectsController < ApplicationController ...@@ -86,6 +86,10 @@ class ProjectsController < ApplicationController
if @project.empty_repo? if @project.empty_repo?
render 'projects/empty' render 'projects/empty'
else else
if current_user
@membership = @project.project_member_by_id(current_user.id)
end
render :show render :show
end end
else else
......
...@@ -45,7 +45,7 @@ module GitlabMarkdownHelper ...@@ -45,7 +45,7 @@ module GitlabMarkdownHelper
end end
def markdown(text, context = {}) def markdown(text, context = {})
context.merge!( context.reverse_merge!(
current_user: current_user, current_user: current_user,
path: @path, path: @path,
project: @project, project: @project,
...@@ -59,7 +59,7 @@ module GitlabMarkdownHelper ...@@ -59,7 +59,7 @@ module GitlabMarkdownHelper
# TODO (rspeicher): Remove all usages of this helper and just call `markdown` # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
# with a custom pipeline depending on the content being rendered # with a custom pipeline depending on the content being rendered
def gfm(text, options = {}) def gfm(text, options = {})
options.merge!( options.reverse_merge!(
current_user: current_user, current_user: current_user,
path: @path, path: @path,
project: @project, project: @project,
......
...@@ -12,4 +12,49 @@ module NotificationsHelper ...@@ -12,4 +12,49 @@ module NotificationsHelper
icon('circle-o', class: 'ns-default') icon('circle-o', class: 'ns-default')
end end
end end
def notification_list_item(notification_level, user_membership)
case notification_level
when Notification::N_DISABLED
content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do
icon('microphone-slash fw', text: 'Disabled')
end
end
when Notification::N_PARTICIPATING
content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do
icon('volume-up fw', text: 'Participate')
end
end
when Notification::N_WATCH
content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do
icon('eye fw', text: 'Watch')
end
end
when Notification::N_MENTION
content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do
icon('at fw', text: 'On mention')
end
end
when Notification::N_GLOBAL
content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do
icon('globe fw', text: 'Global')
end
end
else
# do nothing
end
end
def notification_label(user_membership)
Notification.new(user_membership).to_s
end
def active_level_for(user_membership, level)
'active' if user_membership.notification_level == level
end
end end
...@@ -156,8 +156,8 @@ module ProjectsHelper ...@@ -156,8 +156,8 @@ module ProjectsHelper
end end
end end
def repository_size(project = nil) def repository_size(project = @project)
"#{(project || @project).repository_size} MB" "#{project.repository_size} MB"
rescue rescue
# In order to prevent 500 error # In order to prevent 500 error
# when application cannot allocate memory # when application cannot allocate memory
......
...@@ -110,7 +110,7 @@ class Notify < BaseMailer ...@@ -110,7 +110,7 @@ class Notify < BaseMailer
if reply_key if reply_key
headers['X-GitLab-Reply-Key'] = reply_key headers['X-GitLab-Reply-Key'] = reply_key
address = Mail::Address.new(Gitlab::ReplyByEmail.reply_address(reply_key)) address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace address.display_name = @project.name_with_namespace
headers['Reply-To'] = address headers['Reply-To'] = address
...@@ -150,6 +150,6 @@ class Notify < BaseMailer ...@@ -150,6 +150,6 @@ class Notify < BaseMailer
end end
def reply_key def reply_key
@reply_key ||= Gitlab::ReplyByEmail.reply_key @reply_key ||= SentNotification.reply_key
end end
end end
...@@ -433,10 +433,22 @@ class MergeRequest < ActiveRecord::Base ...@@ -433,10 +433,22 @@ class MergeRequest < ActiveRecord::Base
target_project.repository.fetch_ref( target_project.repository.fetch_ref(
source_project.repository.path_to_repo, source_project.repository.path_to_repo,
"refs/heads/#{source_branch}", "refs/heads/#{source_branch}",
"refs/merge-requests/#{iid}/head" ref_path
) )
end end
def ref_path
"refs/merge-requests/#{iid}/head"
end
def ref_is_fetched?
File.exists?(File.join(project.repository.path_to_repo, ref_path))
end
def ensure_ref_fetched
fetch_ref unless ref_is_fetched?
end
def in_locked_state def in_locked_state
begin begin
lock_mr lock_mr
......
...@@ -12,7 +12,7 @@ class Notification ...@@ -12,7 +12,7 @@ class Notification
class << self class << self
def notification_levels def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION] [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH]
end end
def options_with_labels def options_with_labels
...@@ -26,7 +26,7 @@ class Notification ...@@ -26,7 +26,7 @@ class Notification
end end
def project_notification_levels def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION] [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL]
end end
end end
...@@ -57,4 +57,21 @@ class Notification ...@@ -57,4 +57,21 @@ class Notification
def level def level
target.notification_level target.notification_level
end end
def to_s
case level
when N_DISABLED
'Disabled'
when N_PARTICIPATING
'Participating'
when N_WATCH
'Watching'
when N_MENTION
'On mention'
when N_GLOBAL
'Global'
else
# do nothing
end
end
end end
...@@ -46,8 +46,10 @@ class GitlabCiService < CiService ...@@ -46,8 +46,10 @@ class GitlabCiService < CiService
end end
ci_project = Ci::Project.find_by(gitlab_id: project.id) ci_project = Ci::Project.find_by(gitlab_id: project.id)
if ci_project
Ci::CreateCommitService.new.execute(ci_project, data) Ci::CreateCommitService.new.execute(ci_project, data)
end end
end
def get_ci_commit(sha, ref) def get_ci_commit(sha, ref)
Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref) Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref)
...@@ -85,8 +87,10 @@ class GitlabCiService < CiService ...@@ -85,8 +87,10 @@ class GitlabCiService < CiService
end end
def build_page(sha, ref) def build_page(sha, ref)
if project.gitlab_ci_project.present?
Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha)
end end
end
def title def title
'GitLab CI' 'GitLab CI'
......
...@@ -25,6 +25,12 @@ class SentNotification < ActiveRecord::Base ...@@ -25,6 +25,12 @@ class SentNotification < ActiveRecord::Base
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
class << self class << self
def reply_key
return nil unless Gitlab::IncomingEmail.enabled?
SecureRandom.hex(16)
end
def for(reply_key) def for(reply_key)
find_by(reply_key: reply_key) find_by(reply_key: reply_key)
end end
......
...@@ -4,10 +4,15 @@ module Milestones ...@@ -4,10 +4,15 @@ module Milestones
Milestone.transaction do Milestone.transaction do
update_params = { milestone: nil } update_params = { milestone: nil }
milestone.issues.each do |issue| milestone.issues.each do |issue|
Issues::UpdateService.new(project, current_user, update_params).execute(issue) Issues::UpdateService.new(project, current_user, update_params).execute(issue)
end end
milestone.merge_requests.each do |merge_request|
MergeRequests::UpdateService.new(project, current_user, update_params).execute(merge_request)
end
event_service.destroy_milestone(milestone, current_user) event_service.destroy_milestone(milestone, current_user)
Event.for_milestone_id(milestone.id).each do |event| Event.for_milestone_id(milestone.id).each do |event|
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
%p %p
Reply by email Reply by email
%span.light.pull-right %span.light.pull-right
= boolean_to_icon Gitlab::ReplyByEmail.enabled? = boolean_to_icon Gitlab::IncomingEmail.enabled?
.col-md-4 .col-md-4
%h4 %h4
Components Components
......
...@@ -8,26 +8,26 @@ ...@@ -8,26 +8,26 @@
%li.separate-item %li.separate-item
= nav_link path: 'projects#index' do = nav_link path: 'projects#index' do
= link_to ci_admin_projects_path do = link_to ci_admin_projects_path do
%i.fa.fa-list-alt = icon('list-alt fw')
Projects Projects
= nav_link path: 'events#index' do = nav_link path: 'events#index' do
= link_to ci_admin_events_path do = link_to ci_admin_events_path do
%i.fa.fa-book = icon('book fw')
Events Events
= nav_link path: ['runners#index', 'runners#show'] do = nav_link path: ['runners#index', 'runners#show'] do
= link_to ci_admin_runners_path do = link_to ci_admin_runners_path do
%i.fa.fa-cog = icon('cog fw')
Runners Runners
%small.pull-right %small.pull-right
= Ci::Runner.count(:all) = Ci::Runner.count(:all)
= nav_link path: 'builds#index' do = nav_link path: 'builds#index' do
= link_to ci_admin_builds_path do = link_to ci_admin_builds_path do
%i.fa.fa-link = icon('link fw')
Builds Builds
%small.pull-right %small.pull-right
= Ci::Build.count(:all) = Ci::Build.count(:all)
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to ci_admin_application_settings_path do = link_to ci_admin_application_settings_path do
%i.fa.fa-cogs = icon('cogs fw')
%span %span
Settings Settings
...@@ -6,48 +6,48 @@ ...@@ -6,48 +6,48 @@
%li.separate-item %li.separate-item
= nav_link path: ['projects#show', 'commits#show', 'builds#show'] do = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do
= link_to ci_project_path(@project) do = link_to ci_project_path(@project) do
%i.fa.fa-list-alt = icon('list-alt fw')
%span %span
Commits Commits
%small.pull-right= @project.commits.count %span.count= @project.commits.count
= nav_link path: 'charts#show' do = nav_link path: 'charts#show' do
= link_to ci_project_charts_path(@project) do = link_to ci_project_charts_path(@project) do
%i.fa.fa-bar-chart = icon('bar-chart fw')
%span %span
Charts Charts
= nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do
= link_to ci_project_runners_path(@project) do = link_to ci_project_runners_path(@project) do
%i.fa.fa-cog = icon('cog fw')
%span %span
Runners Runners
= nav_link path: 'variables#show' do = nav_link path: 'variables#show' do
= link_to ci_project_variables_path(@project) do = link_to ci_project_variables_path(@project) do
%i.fa.fa-code = icon('code fw')
%span %span
Variables Variables
= nav_link path: 'web_hooks#index' do = nav_link path: 'web_hooks#index' do
= link_to ci_project_web_hooks_path(@project) do = link_to ci_project_web_hooks_path(@project) do
%i.fa.fa-link = icon('link fw')
%span %span
Web Hooks Web Hooks
= nav_link path: 'triggers#index' do = nav_link path: 'triggers#index' do
= link_to ci_project_triggers_path(@project) do = link_to ci_project_triggers_path(@project) do
%i.fa.fa-retweet = icon('retweet fw')
%span %span
Triggers Triggers
= nav_link path: ['services#index', 'services#edit'] do = nav_link path: ['services#index', 'services#edit'] do
= link_to ci_project_services_path(@project) do = link_to ci_project_services_path(@project) do
%i.fa.fa-share = icon('share fw')
%span %span
Services Services
= nav_link path: 'events#index' do = nav_link path: 'events#index' do
= link_to ci_project_events_path(@project) do = link_to ci_project_events_path(@project) do
%i.fa.fa-book = icon('book fw')
%span %span
Events Events
%li.separate-item %li.separate-item
= nav_link path: 'projects#edit' do = nav_link path: 'projects#edit' do
= link_to edit_ci_project_path(@project) do = link_to edit_ci_project_path(@project) do
%i.fa.fa-cogs = icon('cogs fw')
%span %span
Settings Settings
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= f.label :notification_level, value: Notification::N_MENTION do = f.label :notification_level, value: Notification::N_MENTION do
= f.radio_button :notification_level, Notification::N_MENTION = f.radio_button :notification_level, Notification::N_MENTION
.level-title .level-title
Mention On Mention
%p You will receive notifications only for comments in which you were @mentioned %p You will receive notifications only for comments in which you were @mentioned
.radio .radio
......
...@@ -26,10 +26,8 @@ ...@@ -26,10 +26,8 @@
= icon('download fw') = icon('download fw')
Download Download
= render 'projects/buttons/dropdown' = render 'projects/buttons/notifications'
- if @project.gitlab_ci? = render 'projects/buttons/dropdown'
= link_to ci_project_path(@project.gitlab_ci_project), class: 'btn btn-default' do
CI
= render "shared/clone_panel" = render "shared/clone_panel"
- return unless @membership
= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do
= hidden_field_tag :notification_type, 'project'
= hidden_field_tag :notification_id, @membership.id
= hidden_field_tag :notification_level
%span.dropdown
%a.dropdown-toggle.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"}
= icon('bell')
= notification_label(@membership)
= icon('angle-down')
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
- Notification.project_notification_levels.each do |level|
= notification_list_item(level, @membership)
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
%span CI build #{status} %span CI build #{status}
for #{@merge_request.last_commit_short_sha}. for #{@merge_request.last_commit_short_sha}.
%span.ci-coverage %span.ci-coverage
- if ci_build_details_path(@merge_request)
= link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
.ci_widget .ci_widget
......
.note-edit-form .note-edit-form
= form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f| = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
= note_target_fields(note) = note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints' = render 'projects/notes/hints'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
= render 'projects/notes/hints' = render 'projects/notes/hints'
.error-alert .error-alert
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= pluralize(number_with_delimiter(@repository.tag_names.count), 'tag') = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
%li %li
= link_to namespace_project_path(@project.namespace, @project) do = link_to project_files_path(@project) do
= repository_size = repository_size
- if !prefer_readme? && @repository.readme - if !prefer_readme? && @repository.readme
......
...@@ -4,7 +4,7 @@ class EmailReceiverWorker ...@@ -4,7 +4,7 @@ class EmailReceiverWorker
sidekiq_options queue: :incoming_email sidekiq_options queue: :incoming_email
def perform(raw) def perform(raw)
return unless Gitlab::ReplyByEmail.enabled? return unless Gitlab::IncomingEmail.enabled?
begin begin
Gitlab::Email::Receiver.new(raw).execute Gitlab::Email::Receiver.new(raw).execute
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# #
########################### NOTE ##################################### ########################### NOTE #####################################
# This file should not receive new settings. All configuration options # # This file should not receive new settings. All configuration options #
# that do not require application restart are being moved to # # that do not require an application restart are being moved to #
# ApplicationSetting model! # # ApplicationSetting model! #
# If you change this file in a Merge Request, please also create # # If you change this file in a Merge Request, please also create #
# a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests # # a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests #
...@@ -96,10 +96,10 @@ production: &base ...@@ -96,10 +96,10 @@ production: &base
## Reply by email ## Reply by email
# Allow users to comment on issues and merge requests by replying to notification emails. # Allow users to comment on issues and merge requests by replying to notification emails.
# For documentation on how to set this up, see http://doc.gitlab.com/ce/reply_by_email/README.html # For documentation on how to set this up, see http://doc.gitlab.com/ce/incoming_email/README.html
reply_by_email: incoming_email:
enabled: false enabled: false
address: "replies+%{reply_key}@gitlab.example.com" address: "incoming+%{key}@gitlab.example.com"
## Gravatar ## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
......
...@@ -187,8 +187,8 @@ Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci[ ...@@ -187,8 +187,8 @@ Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci[
# #
# Reply by email # Reply by email
# #
Settings['reply_by_email'] ||= Settingslogic.new({}) Settings['incoming_email'] ||= Settingslogic.new({})
Settings.reply_by_email['enabled'] = false if Settings.reply_by_email['enabled'].nil? Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled'].nil?
# #
# Gravatar # Gravatar
......
class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration
def up
execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)")
end
end
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation. - [Update](update/README.md) Update guides to upgrade your installation.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
- [Reply by email](reply_by_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails.
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
### Administrator documentation ### Administrator documentation
......
...@@ -12,24 +12,24 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these ...@@ -12,24 +12,24 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
## Set it up ## Set it up
In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
### Omnibus package installations ### Omnibus package installations
1. Find the `reply_by_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `reply_key` and fill in the details for your specific IMAP server and email account: 1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` that references the item being replied to and fill in the details for your specific IMAP server and email account:
```ruby ```ruby
gitlab_rails['reply_by_email_enabled'] = true gitlab_rails['incoming_email_enabled'] = true
gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host gitlab_rails['incoming_email_host'] = "imap.gmail.com" # IMAP server host
gitlab_rails['reply_by_email_port'] = 993 # IMAP server port gitlab_rails['incoming_email_port'] = 993 # IMAP server port
gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL gitlab_rails['incoming_email_ssl'] = true # Whether the IMAP server uses SSL
gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com" # Email account username. Usually the full email address.
gitlab_rails['reply_by_email_password'] = "password" # Email account password gitlab_rails['incoming_email_password'] = "password" # Email account password
gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox".
``` ```
As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`.
1. Reconfigure GitLab for the changes to take effect: 1. Reconfigure GitLab for the changes to take effect:
...@@ -40,7 +40,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -40,7 +40,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
1. Verify that everything is configured correctly: 1. Verify that everything is configured correctly:
```sh ```sh
sudo gitlab-rake gitlab:reply_by_email:check sudo gitlab-rake gitlab:incoming_email:check
``` ```
1. Reply by email should now be working. 1. Reply by email should now be working.
...@@ -53,19 +53,19 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -53,19 +53,19 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
cd /home/git/gitlab cd /home/git/gitlab
``` ```
1. Find the `reply_by_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `reply_key`: 1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to:
```sh ```sh
sudo editor config/gitlab.yml sudo editor config/gitlab.yml
``` ```
```yaml ```yaml
reply_by_email: incoming_email:
enabled: true enabled: true
address: "gitlab-replies+%{reply_key}@gmail.com" address: "gitlab-incoming+%{key}@gmail.com"
``` ```
As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`.
2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`:
...@@ -89,7 +89,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -89,7 +89,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
# Whether the IMAP server uses SSL # Whether the IMAP server uses SSL
:ssl: true :ssl: true
# Email account username. Usually the full email address. # Email account username. Usually the full email address.
:email: "gitlab-replies@gmail.com" :email: "gitlab-incoming@gmail.com"
# Email account password # Email account password
:password: "[REDACTED]" :password: "[REDACTED]"
# The name of the mailbox where incoming mail will end up. Usually "inbox". # The name of the mailbox where incoming mail will end up. Usually "inbox".
...@@ -125,7 +125,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -125,7 +125,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
7. Verify that everything is configured correctly: 7. Verify that everything is configured correctly:
```sh ```sh
sudo -u git -H bundle exec rake gitlab:reply_by_email:check RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
``` ```
8. Reply by email should now be working. 8. Reply by email should now be working.
...@@ -134,15 +134,15 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -134,15 +134,15 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
1. Go to the GitLab installation directory. 1. Go to the GitLab installation directory.
1. Find the `reply_by_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `reply_key`: 1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to:
```yaml ```yaml
reply_by_email: incoming_email:
enabled: true enabled: true
address: "gitlab-replies+%{reply_key}@gmail.com" address: "gitlab-incoming+%{key}@gmail.com"
``` ```
As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`:
...@@ -162,7 +162,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -162,7 +162,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
# Whether the IMAP server uses SSL # Whether the IMAP server uses SSL
:ssl: true :ssl: true
# Email account username. Usually the full email address. # Email account username. Usually the full email address.
:email: "gitlab-replies@gmail.com" :email: "gitlab-incoming@gmail.com"
# Email account password # Email account password
:password: "[REDACTED]" :password: "[REDACTED]"
# The name of the mailbox where incoming mail will end up. Usually "inbox". # The name of the mailbox where incoming mail will end up. Usually "inbox".
...@@ -197,7 +197,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ...@@ -197,7 +197,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`.
7. Verify that everything is configured correctly: 7. Verify that everything is configured correctly:
```sh ```sh
bundle exec rake gitlab:reply_by_email:check RAILS_ENV=development bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
``` ```
8. Reply by email should now be working. 8. Reply by email should now be working.
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
This document will take you through the steps of setting up a basic Postfix mail server with IMAP authentication on Ubuntu, to be used with Reply by email. This document will take you through the steps of setting up a basic Postfix mail server with IMAP authentication on Ubuntu, to be used with Reply by email.
The instructions make the assumption that you will be using the email address `replies@gitlab.example.com`, that is, username `replies` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets. The instructions make the assumption that you will be using the email address `incoming@gitlab.example.com`, that is, username `incoming` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets.
## Configure your server firewall ## Configure your server firewall
...@@ -27,16 +27,16 @@ The instructions make the assumption that you will be using the email address `r ...@@ -27,16 +27,16 @@ The instructions make the assumption that you will be using the email address `r
## Create user ## Create user
1. Create a user for replies. 1. Create a user for incoming email.
```sh ```sh
sudo useradd -m -s /bin/bash replies sudo useradd -m -s /bin/bash incoming
``` ```
1. Set a password for this user. 1. Set a password for this user.
```sh ```sh
sudo passwd replies sudo passwd incoming
``` ```
Be sure not to forget this, you'll need it later. Be sure not to forget this, you'll need it later.
...@@ -70,12 +70,12 @@ The instructions make the assumption that you will be using the email address `r ...@@ -70,12 +70,12 @@ The instructions make the assumption that you will be using the email address `r
sudo postfix start sudo postfix start
``` ```
1. Send the new `replies` user a dummy email to test SMTP, by entering the following into the SMTP prompt: 1. Send the new `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
``` ```
ehlo localhost ehlo localhost
mail from: root@localhost mail from: root@localhost
rcpt to: replies@localhost rcpt to: incoming@localhost
data data
Subject: Re: Some issue Subject: Re: Some issue
...@@ -86,17 +86,17 @@ The instructions make the assumption that you will be using the email address `r ...@@ -86,17 +86,17 @@ The instructions make the assumption that you will be using the email address `r
(Note: The `.` is a literal period on its own line) (Note: The `.` is a literal period on its own line)
1. Check if the `replies` user received the email: 1. Check if the `incoming` user received the email:
```sh ```sh
su - replies su - incoming
mail mail
``` ```
You should see output like this: You should see output like this:
``` ```
"/var/mail/replies": 1 message 1 unread "/var/mail/incoming": 1 message 1 unread
>U 1 root@localhost 59/2842 Re: Some issue >U 1 root@localhost 59/2842 Re: Some issue
``` ```
...@@ -106,7 +106,7 @@ The instructions make the assumption that you will be using the email address `r ...@@ -106,7 +106,7 @@ The instructions make the assumption that you will be using the email address `r
q q
``` ```
1. Log out of the `replies` account and go back to being `root`: 1. Log out of the `incoming` account and go back to being `root`:
```sh ```sh
logout logout
...@@ -131,18 +131,18 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -131,18 +131,18 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
1. Test the new setup: 1. Test the new setup:
1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_. 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_.
2. Check if the `replies` user received the email: 2. Check if the `incoming` user received the email:
```sh ```sh
su - replies su - incoming
MAIL=/home/replies/Maildir MAIL=/home/incoming/Maildir
mail mail
``` ```
You should see output like this: You should see output like this:
``` ```
"/home/replies/Maildir": 1 message 1 unread "/home/incoming/Maildir": 1 message 1 unread
>U 1 root@localhost 59/2842 Re: Some issue >U 1 root@localhost 59/2842 Re: Some issue
``` ```
...@@ -152,7 +152,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -152,7 +152,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
q q
``` ```
1. Log out of the `replies` account and go back to being `root`: 1. Log out of the `incoming` account and go back to being `root`:
```sh ```sh
logout logout
...@@ -221,12 +221,12 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -221,12 +221,12 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
If you get a `Connection refused` error instead, make sure your firewall is setup to allow inbound traffic on port 25. If you get a `Connection refused` error instead, make sure your firewall is setup to allow inbound traffic on port 25.
1. Send the `replies` user a dummy email to test SMTP, by entering the following into the SMTP prompt: 1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
``` ```
ehlo gitlab.example.com ehlo gitlab.example.com
mail from: root@gitlab.example.com mail from: root@gitlab.example.com
rcpt to: replies@gitlab.example.com rcpt to: incoming@gitlab.example.com
data data
Subject: Re: Some issue Subject: Re: Some issue
...@@ -237,18 +237,18 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -237,18 +237,18 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
(Note: The `.` is a literal period on its own line) (Note: The `.` is a literal period on its own line)
1. Check if the `replies` user received the email: 1. Check if the `incoming` user received the email:
```sh ```sh
su - replies su - incoming
MAIL=/home/replies/Maildir MAIL=/home/incoming/Maildir
mail mail
``` ```
You should see output like this: You should see output like this:
``` ```
"/home/replies/Maildir": 1 message 1 unread "/home/incoming/Maildir": 1 message 1 unread
>U 1 root@gitlab.example.com 59/2842 Re: Some issue >U 1 root@gitlab.example.com 59/2842 Re: Some issue
``` ```
...@@ -258,7 +258,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -258,7 +258,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
q q
``` ```
1. Log out of the `replies` account and go back to being `root`: 1. Log out of the `incoming` account and go back to being `root`:
```sh ```sh
logout logout
...@@ -281,13 +281,13 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ...@@ -281,13 +281,13 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
- OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information. - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information.
``` ```
1. Sign in as the `replies` user to test IMAP, by entering the following into the IMAP prompt: 1. Sign in as the `incoming` user to test IMAP, by entering the following into the IMAP prompt:
``` ```
a login replies PASSWORD a login incoming PASSWORD
``` ```
Replace PASSWORD with the password you set on the `replies` user earlier. Replace PASSWORD with the password you set on the `incoming` user earlier.
You should see output like this: You should see output like this:
......
...@@ -74,3 +74,9 @@ Feature: Project ...@@ -74,3 +74,9 @@ Feature: Project
Given I disable snippets in project Given I disable snippets in project
When I visit project "Shop" page When I visit project "Shop" page
Then I should not see "Snippets" button Then I should not see "Snippets" button
@javascript
Scenario: I edit Project Notifications
Given I click notifications drop down button
When I choose Mention setting
Then I should see Notification saved message
...@@ -130,4 +130,18 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -130,4 +130,18 @@ class Spinach::Features::Project < Spinach::FeatureSteps
step 'I should see back to group button' do step 'I should see back to group button' do
expect(page).to have_content 'Back to group' expect(page).to have_content 'Back to group'
end end
step 'I click notifications drop down button' do
click_link 'notifications-button'
end
step 'I choose Mention setting' do
click_link 'On mention'
end
step 'I should see Notification saved message' do
page.within '.flash-container' do
expect(page).to have_content 'Notification settings saved'
end
end
end end
...@@ -13,7 +13,7 @@ module Ci ...@@ -13,7 +13,7 @@ module Ci
backup_existing_builds_dir backup_existing_builds_dir
FileUtils.mkdir_p(app_builds_dir, mode: 0700) FileUtils.mkdir_p(app_builds_dir, mode: 0700)
unless system('tar', '-C', app_builds_dir, '-zxvf', backup_builds_tarball) unless system('tar', '-C', app_builds_dir, '-zxf', backup_builds_tarball)
abort 'Restore failed'.red abort 'Restore failed'.red
end end
end end
......
module Ci module Ci
module Migrate module Migrate
class Manager class Manager
CI_IMPORT_PREFIX = '8.0' # Only allow imports from CI 8.0.x
def cleanup def cleanup
$progress.print "Deleting tmp directories ... " $progress.print "Deleting tmp directories ... "
...@@ -48,7 +50,7 @@ module Ci ...@@ -48,7 +50,7 @@ module Ci
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
# restoring mismatching backups can lead to unexpected problems # restoring mismatching backups can lead to unexpected problems
if settings[:gitlab_version] != GitlabCi::VERSION if !settings[:gitlab_version].start_with?(CI_IMPORT_PREFIX)
puts "GitLab CI version mismatch:".red puts "GitLab CI version mismatch:".red
puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red
exit 1 exit 1
......
...@@ -65,7 +65,7 @@ module Gitlab ...@@ -65,7 +65,7 @@ module Gitlab
def reply_key def reply_key
reply_key = nil reply_key = nil
message.to.each do |address| message.to.each do |address|
reply_key = Gitlab::ReplyByEmail.reply_key_from_address(address) reply_key = Gitlab::IncomingEmail.key_from_address(address)
break if reply_key break if reply_key
end end
......
module Gitlab module Gitlab
module ReplyByEmail module IncomingEmail
class << self class << self
def enabled? def enabled?
config.enabled && address_formatted_correctly? config.enabled && address_formatted_correctly?
...@@ -7,20 +7,14 @@ module Gitlab ...@@ -7,20 +7,14 @@ module Gitlab
def address_formatted_correctly? def address_formatted_correctly?
config.address && config.address &&
config.address.include?("%{reply_key}") config.address.include?("%{key}")
end end
def reply_key def reply_address(key)
return nil unless enabled? config.address.gsub('%{key}', key)
SecureRandom.hex(16)
end
def reply_address(reply_key)
config.address.gsub('%{reply_key}', reply_key)
end end
def reply_key_from_address(address) def key_from_address(address)
regex = address_regex regex = address_regex
return unless regex return unless regex
...@@ -33,7 +27,7 @@ module Gitlab ...@@ -33,7 +27,7 @@ module Gitlab
private private
def config def config
Gitlab.config.reply_by_email Gitlab.config.incoming_email
end end
def address_regex def address_regex
...@@ -41,7 +35,7 @@ module Gitlab ...@@ -41,7 +35,7 @@ module Gitlab
return nil unless wildcard_address return nil unless wildcard_address
regex = Regexp.escape(wildcard_address) regex = Regexp.escape(wildcard_address)
regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.+)") regex = regex.gsub(Regexp.escape('%{key}'), "(.+)")
Regexp.new(regex).freeze Regexp.new(regex).freeze
end end
end end
......
...@@ -59,25 +59,43 @@ module Gitlab ...@@ -59,25 +59,43 @@ module Gitlab
end end
def relative_file_path(path) def relative_file_path(path)
nested_path = build_nested_path(path, context[:requested_path]) nested_path = build_relative_path(path, context[:requested_path])
file_exists?(nested_path) ? nested_path : path file_exists?(nested_path) ? nested_path : path
end end
# Covering a special case, when the link is referencing file in the same # Convert a relative path into its correct location based on the currently
# directory. # requested path
# If we are at doc/api/README.md and the README.md contains relative #
# links like [Users](users.md), this takes the request # path - Relative path String
# path(doc/api/README.md) and replaces the README.md with users.md so the # request_path - Currently-requested path String
# path looks like doc/api/users.md. #
# If we are at doc/api and the README.md shown in below the tree view # Examples:
# this takes the request path(doc/api) and adds users.md so the path #
# looks like doc/api/users.md # # File in the same directory as the current path
def build_nested_path(path, request_path) # build_relative_path("users.md", "doc/api/README.md")
# # => "doc/api/users.md"
#
# # File in the same directory, which is also the current path
# build_relative_path("users.md", "doc/api")
# # => "doc/api/users.md"
#
# # Going up one level to a different directory
# build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md")
# # => "doc/update/7.14-to-8.0.md"
#
# Returns a String
def build_relative_path(path, request_path)
return request_path if path.empty? return request_path if path.empty?
return path unless request_path return path unless request_path
parts = request_path.split('/') parts = request_path.split('/')
parts.pop if path_type(request_path) != 'tree' parts.pop if path_type(request_path) != 'tree'
while parts.length > 1 && path.start_with?('../')
parts.pop
path.sub!('../', '')
end
parts.push(path).join('/') parts.push(path).join('/')
end end
......
...@@ -2,7 +2,7 @@ namespace :gitlab do ...@@ -2,7 +2,7 @@ namespace :gitlab do
desc "GitLab | Check the configuration of GitLab and its environment" desc "GitLab | Check the configuration of GitLab and its environment"
task check: %w{gitlab:gitlab_shell:check task check: %w{gitlab:gitlab_shell:check
gitlab:sidekiq:check gitlab:sidekiq:check
gitlab:reply_by_email:check gitlab:incoming_email:check
gitlab:ldap:check gitlab:ldap:check
gitlab:app:check} gitlab:app:check}
...@@ -634,13 +634,13 @@ namespace :gitlab do ...@@ -634,13 +634,13 @@ namespace :gitlab do
end end
namespace :reply_by_email do namespace :incoming_email do
desc "GitLab | Check the configuration of Reply by email" desc "GitLab | Check the configuration of Reply by email"
task check: :environment do task check: :environment do
warn_user_is_not_gitlab warn_user_is_not_gitlab
start_checking "Reply by email" start_checking "Reply by email"
if Gitlab.config.reply_by_email.enabled if Gitlab.config.incoming_email.enabled
check_address_formatted_correctly check_address_formatted_correctly
check_mail_room_config_exists check_mail_room_config_exists
check_imap_authentication check_imap_authentication
...@@ -665,12 +665,12 @@ namespace :gitlab do ...@@ -665,12 +665,12 @@ namespace :gitlab do
def check_address_formatted_correctly def check_address_formatted_correctly
print "Address formatted correctly? ... " print "Address formatted correctly? ... "
if Gitlab::ReplyByEmail.address_formatted_correctly? if Gitlab::IncomingEmail.address_formatted_correctly?
puts "yes".green puts "yes".green
else else
puts "no".red puts "no".red
try_fixing_it( try_fixing_it(
"Make sure that the address in config/gitlab.yml includes the '%{reply_key}' placeholder." "Make sure that the address in config/gitlab.yml includes the '%{key}' placeholder."
) )
fix_and_rerun fix_and_rerun
end end
...@@ -689,7 +689,7 @@ namespace :gitlab do ...@@ -689,7 +689,7 @@ namespace :gitlab do
"Enable mail_room in the init.d configuration." "Enable mail_room in the init.d configuration."
) )
for_more_information( for_more_information(
"doc/reply_by_email/README.md" "doc/incoming_email/README.md"
) )
fix_and_rerun fix_and_rerun
end end
...@@ -708,7 +708,7 @@ namespace :gitlab do ...@@ -708,7 +708,7 @@ namespace :gitlab do
"Enable mail_room in your Procfile." "Enable mail_room in your Procfile."
) )
for_more_information( for_more_information(
"doc/reply_by_email/README.md" "doc/incoming_email/README.md"
) )
fix_and_rerun fix_and_rerun
end end
...@@ -753,7 +753,7 @@ namespace :gitlab do ...@@ -753,7 +753,7 @@ namespace :gitlab do
"Check that the information in config/mail_room.yml is correct" "Check that the information in config/mail_room.yml is correct"
) )
for_more_information( for_more_information(
"doc/reply_by_email/README.md" "doc/incoming_email/README.md"
) )
fix_and_rerun fix_and_rerun
end end
...@@ -789,7 +789,7 @@ namespace :gitlab do ...@@ -789,7 +789,7 @@ namespace :gitlab do
"Check that the information in config/mail_room.yml is correct" "Check that the information in config/mail_room.yml is correct"
) )
for_more_information( for_more_information(
"doc/reply_by_email/README.md" "doc/incoming_email/README.md"
) )
fix_and_rerun fix_and_rerun
end end
......
...@@ -5,6 +5,7 @@ describe Projects::MilestonesController do ...@@ -5,6 +5,7 @@ describe Projects::MilestonesController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project, milestone: milestone) } let(:issue) { create(:issue, project: project, milestone: milestone) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) }
before do before do
sign_in(user) sign_in(user)
...@@ -14,6 +15,7 @@ describe Projects::MilestonesController do ...@@ -14,6 +15,7 @@ describe Projects::MilestonesController do
describe "#destroy" do describe "#destroy" do
it "should remove milestone" do it "should remove milestone" do
merge_request.reload
expect(issue.milestone_id).to eq(milestone.id) expect(issue.milestone_id).to eq(milestone.id)
delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js
...@@ -24,6 +26,10 @@ describe Projects::MilestonesController do ...@@ -24,6 +26,10 @@ describe Projects::MilestonesController do
expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound) expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound)
issue.reload issue.reload
expect(issue.milestone_id).to eq(nil) expect(issue.milestone_id).to eq(nil)
merge_request.reload
expect(merge_request.milestone_id).to eq(nil)
# Check system note left for milestone removal # Check system note left for milestone removal
last_note = project.issues.find(issue.id).notes[-1].note last_note = project.issues.find(issue.id).notes[-1].note
expect(last_note).to eq('Milestone removed') expect(last_note).to eq('Milestone removed')
......
...@@ -38,6 +38,17 @@ describe GitlabMarkdownHelper do ...@@ -38,6 +38,17 @@ describe GitlabMarkdownHelper do
expect(markdown(actual)).to match(expected) expect(markdown(actual)).to match(expected)
end end
end end
describe "override default project" do
let(:actual) { issue.to_reference }
let(:second_project) { create(:project) }
let(:second_issue) { create(:issue, project: second_project) }
it 'should link to the issue' do
expected = namespace_project_issue_path(second_project.namespace, second_project, second_issue)
expect(markdown(actual, project: second_project)).to match(expected)
end
end
end end
describe '#link_to_gfm' do describe '#link_to_gfm' do
......
...@@ -2,7 +2,7 @@ require "spec_helper" ...@@ -2,7 +2,7 @@ require "spec_helper"
describe Gitlab::Email::Receiver do describe Gitlab::Email::Receiver do
before do before do
stub_reply_by_email_setting(enabled: true, address: "reply+%{reply_key}@appmail.adventuretime.ooo") stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
end end
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
......
require "spec_helper" require "spec_helper"
describe Gitlab::ReplyByEmail do describe Gitlab::IncomingEmail do
describe "self.enabled?" do describe "self.enabled?" do
context "when reply by email is enabled" do context "when reply by email is enabled" do
before do before do
stub_reply_by_email_setting(enabled: true) stub_incoming_email_setting(enabled: true)
end end
context "when the address is valid" do context "when the address is valid" do
before do before do
stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") stub_incoming_email_setting(address: "replies+%{key}@example.com")
end end
it "returns true" do it "returns true" do
...@@ -19,7 +19,7 @@ describe Gitlab::ReplyByEmail do ...@@ -19,7 +19,7 @@ describe Gitlab::ReplyByEmail do
context "when the address is invalid" do context "when the address is invalid" do
before do before do
stub_reply_by_email_setting(address: "replies@example.com") stub_incoming_email_setting(address: "replies@example.com")
end end
it "returns false" do it "returns false" do
...@@ -30,7 +30,7 @@ describe Gitlab::ReplyByEmail do ...@@ -30,7 +30,7 @@ describe Gitlab::ReplyByEmail do
context "when reply by email is disabled" do context "when reply by email is disabled" do
before do before do
stub_reply_by_email_setting(enabled: false) stub_incoming_email_setting(enabled: false)
end end
it "returns false" do it "returns false" do
...@@ -39,34 +39,9 @@ describe Gitlab::ReplyByEmail do ...@@ -39,34 +39,9 @@ describe Gitlab::ReplyByEmail do
end end
end end
describe "self.reply_key" do
context "when enabled" do
before do
allow(described_class).to receive(:enabled?).and_return(true)
end
it "returns a random hex" do
key = described_class.reply_key
key2 = described_class.reply_key
expect(key).not_to eq(key2)
end
end
context "when disabled" do
before do
allow(described_class).to receive(:enabled?).and_return(false)
end
it "returns nil" do
expect(described_class.reply_key).to be_nil
end
end
end
context "self.reply_address" do context "self.reply_address" do
before do before do
stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") stub_incoming_email_setting(address: "replies+%{key}@example.com")
end end
it "returns the address with an interpolated reply key" do it "returns the address with an interpolated reply key" do
...@@ -74,13 +49,13 @@ describe Gitlab::ReplyByEmail do ...@@ -74,13 +49,13 @@ describe Gitlab::ReplyByEmail do
end end
end end
context "self.reply_key_from_address" do context "self.key_from_address" do
before do before do
stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") stub_incoming_email_setting(address: "replies+%{key}@example.com")
end end
it "returns reply key" do it "returns reply key" do
expect(described_class.reply_key_from_address("replies+key@example.com")).to eq("key") expect(described_class.key_from_address("replies+key@example.com")).to eq("key")
end end
end end
end end
...@@ -4,14 +4,16 @@ require 'spec_helper' ...@@ -4,14 +4,16 @@ require 'spec_helper'
module Gitlab::Markdown module Gitlab::Markdown
describe RelativeLinkFilter do describe RelativeLinkFilter do
def filter(doc) def filter(doc, contexts = {})
described_class.call(doc, { contexts.reverse_merge!({
commit: project.commit, commit: project.commit,
project: project, project: project,
project_wiki: project_wiki, project_wiki: project_wiki,
ref: ref, ref: ref,
requested_path: requested_path requested_path: requested_path
}) })
described_class.call(doc, contexts)
end end
def image(path) def image(path)
...@@ -75,6 +77,22 @@ module Gitlab::Markdown ...@@ -75,6 +77,22 @@ module Gitlab::Markdown
to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end end
it 'rebuilds relative URL for a file in the repo up one directory' do
relative_link = link('../api/README.md')
doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md')
expect(doc.at_css('a')['href']).
to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repo up multiple directories' do
relative_link = link('../../../api/README.md')
doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md')
expect(doc.at_css('a')['href']).
to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repo with an anchor' do it 'rebuilds relative URL for a file in the repo with an anchor' do
doc = filter(link('README.md#section')) doc = filter(link('README.md#section'))
expect(doc.at_css('a')['href']). expect(doc.at_css('a')['href']).
...@@ -108,8 +126,8 @@ module Gitlab::Markdown ...@@ -108,8 +126,8 @@ module Gitlab::Markdown
escaped = Addressable::URI.escape(path) escaped = Addressable::URI.escape(path)
# Stub these methods so the file doesn't actually need to be in the repo # Stub these methods so the file doesn't actually need to be in the repo
allow_any_instance_of(described_class).to receive(:file_exists?). allow_any_instance_of(described_class).
and_return(true) to receive(:file_exists?).and_return(true)
allow_any_instance_of(described_class). allow_any_instance_of(described_class).
to receive(:image?).with(path).and_return(true) to receive(:image?).with(path).and_return(true)
......
...@@ -17,8 +17,8 @@ module StubConfiguration ...@@ -17,8 +17,8 @@ module StubConfiguration
allow(Gitlab.config.gravatar).to receive_messages(messages) allow(Gitlab.config.gravatar).to receive_messages(messages)
end end
def stub_reply_by_email_setting(messages) def stub_incoming_email_setting(messages)
allow(Gitlab.config.reply_by_email).to receive_messages(messages) allow(Gitlab.config.incoming_email).to receive_messages(messages)
end end
private private
......
...@@ -5,7 +5,7 @@ describe EmailReceiverWorker do ...@@ -5,7 +5,7 @@ describe EmailReceiverWorker do
context "when reply by email is enabled" do context "when reply by email is enabled" do
before do before do
allow(Gitlab::ReplyByEmail).to receive(:enabled?).and_return(true) allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
end end
it "calls the email receiver" do it "calls the email receiver" do
...@@ -33,7 +33,7 @@ describe EmailReceiverWorker do ...@@ -33,7 +33,7 @@ describe EmailReceiverWorker do
context "when reply by email is disabled" do context "when reply by email is disabled" do
before do before do
allow(Gitlab::ReplyByEmail).to receive(:enabled?).and_return(false) allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(false)
end end
it "doesn't call the email receiver" do it "doesn't call the email receiver" do
......
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