summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-02-17 17:33:09 +0100
committerRémy Coutable <remy@rymai.me>2016-02-17 17:33:09 +0100
commita4ffdf922da488b2f04e81f5fc29243413e42cd3 (patch)
tree84031d4adc384238e052bc12a6935ac52f05bb31
parent49fa849536d0345887477ae2fe5b29da7b3beacb (diff)
parentbe4cdd93449e220237c7eaa9527203a5c5433da9 (diff)
downloadgitlab-ce-a4ffdf922da488b2f04e81f5fc29243413e42cd3.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
-rw-r--r--CHANGELOG9
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/awards_handler.coffee3
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss16
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss8
-rw-r--r--app/assets/stylesheets/pages/issuable.scss25
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/projects/commit_controller.rb4
-rw-r--r--app/controllers/projects/imports_controller.rb12
-rw-r--r--app/helpers/application_helper.rb73
-rw-r--r--app/helpers/diff_helper.rb2
-rw-r--r--app/helpers/gitlab_markdown_helper.rb15
-rw-r--r--app/helpers/issuables_helper.rb37
-rw-r--r--app/helpers/nav_helper.rb18
-rw-r--r--app/mailers/emails/builds.rb13
-rw-r--r--app/models/concerns/issuable.rb11
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/project_services/pushover_service.rb2
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/models/repository.rb21
-rw-r--r--app/services/projects/destroy_service.rb14
-rw-r--r--app/services/system_note_service.rb19
-rw-r--r--app/views/notify/build_fail_email.html.haml3
-rw-r--r--app/views/notify/build_success_email.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/blob/_image.html.haml9
-rw-r--r--app/views/projects/diffs/_image.html.haml16
-rw-r--r--app/views/projects/issues/show.html.haml23
-rw-r--r--app/views/projects/issues/update.js.haml2
-rw-r--r--app/views/projects/merge_requests/update.js.haml6
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml19
-rw-r--r--app/workers/repository_fork_worker.rb1
-rw-r--r--app/workers/repository_import_worker.rb1
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/development/migration_style_guide.md6
-rw-r--r--doc/install/installation.md25
-rw-r--r--doc/install/redis.md60
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--doc/web_hooks/web_hooks.md240
-rw-r--r--doc/workflow/gitlab_flow.md11
-rw-r--r--features/project/issues/award_emoji.feature11
-rw-r--r--features/project/issues/issues.feature9
-rw-r--r--features/project/merge_requests.feature1
-rw-r--r--features/steps/project/issues/award_emoji.rb28
-rw-r--r--features/steps/project/issues/issues.rb5
-rw-r--r--features/steps/shared/issuable.rb24
-rw-r--r--lib/banzai/pipeline/asciidoc_pipeline.rb11
-rw-r--r--lib/gitlab/asciidoc.rb4
-rw-r--r--lib/gitlab/database.rb8
-rw-r--r--lib/gitlab/note_data_builder.rb11
-rw-r--r--lib/gitlab/other_markup.rb24
-rw-r--r--lib/gitlab/push_data_builder.rb18
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb37
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb12
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb16
-rw-r--r--spec/lib/gitlab/database_spec.rb32
-rw-r--r--spec/lib/gitlab/note_data_builder_spec.rb30
-rw-r--r--spec/lib/gitlab/push_data_builder_spec.rb27
-rw-r--r--spec/mailers/emails/builds_spec.rb65
-rw-r--r--spec/mailers/emails/profile_spec.rb107
-rw-r--r--spec/mailers/notify_spec.rb273
-rw-r--r--spec/mailers/shared/notify.rb117
-rw-r--r--spec/models/concerns/issuable_spec.rb23
-rw-r--r--spec/models/merge_request_spec.rb21
-rw-r--r--spec/models/repository_spec.rb11
-rw-r--r--spec/services/system_note_service_spec.rb15
-rw-r--r--spec/support/project_hook_data_shared_example.rb27
-rw-r--r--spec/workers/repository_fork_worker_spec.rb12
-rw-r--r--spec/workers/repository_import_worker_spec.rb19
71 files changed, 1178 insertions, 591 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 9b2d6d58a5c..74bc366d203 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
+ - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- Cache various Repository methods to improve performance (Yorick Peterse)
- Fix duplicated branch creation/deletion Web hooks/service notifications when using Web UI (Stan Hu)
- Ensure rake tasks that don't need a DB connection can be run without one
@@ -17,6 +18,7 @@ v 8.5.0 (unreleased)
set it up
- API: Added "merge_requests/:merge_request_id/closes_issues" (Gal Schlezinger)
- Fix diff comments loaded by AJAX to load comment with diff in discussion tab
+ - Fix relative links in other markup formats (Ben Boeckel)
- Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
- Fix label links for a merge request pointing to issues list
- Don't vendor minified JS
@@ -32,6 +34,11 @@ v 8.5.0 (unreleased)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
- Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson)
- Optimized performance of finding issues to be closed by a merge request
+ - Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`, `path_with_namespace`
+ and `default_branch` in `project` in push, issue, merge-request and note webhooks data (Kirill Zaitsev)
+ - Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in favor of `git_http_url`
+ in `project` for push, issue, merge-request and note webhooks data (Kirill Zaitsev)
+ - Deprecate the `repository` key in push, issue, merge-request and note webhooks data, use `project` instead (Kirill Zaitsev)
- API: Expose MergeRequest#merge_status (Andrei Dziahel)
- Revert "Add IP check against DNSBLs at account sign-up"
- Actually use the `skip_merges` option in Repository#commits (Tony Chu)
@@ -47,12 +54,14 @@ v 8.5.0 (unreleased)
- Fixed logo animation on Safari (Roman Rott)
- Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
- In seach autocomplete show only groups and projects you are member of
+ - Don't process cross-reference notes from forks
- Fix: init.d script not working on OS X
- Faster snippet search
- Title for milestones should be unique (Zeger-Jan van de Weg)
- Validate correctness of maximum attachment size application setting
- Replaces "Create merge request" link with one to the "Merge Request" when one exists
- Fix CI builds badge, add a new link to builds badge, deprecate the old one
+ - Fix broken link to project in build notification emails
v 8.4.4
- Update omniauth-saml gem to 1.4.2
diff --git a/Gemfile b/Gemfile
index 9114fdd33ac..a1c57af4bac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -21,7 +21,7 @@ gem "pg", '~> 0.18.2', group: :postgres
gem 'devise', '~> 3.5.4'
gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
-gem 'omniauth', '~> 1.2.2'
+gem 'omniauth', '~> 1.3.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 98a36724fe0..d84cc2d0789 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -50,7 +50,7 @@ GEM
after_commit_queue (1.3.0)
activerecord (>= 3.0)
akismet (2.0.0)
- allocations (1.0.3)
+ allocations (1.0.4)
annotate (2.6.10)
activerecord (>= 3.2, <= 4.3)
rake (~> 10.4)
@@ -491,9 +491,9 @@ GEM
rack (~> 1.2)
octokit (3.8.0)
sawyer (~> 0.6.0, >= 0.5.3)
- omniauth (1.2.2)
+ omniauth (1.3.1)
hashie (>= 1.2, < 4)
- rack (~> 1.0)
+ rack (>= 1.0, < 3)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
@@ -963,7 +963,7 @@ DEPENDENCIES
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0)
octokit (~> 3.8.0)
- omniauth (~> 1.2.2)
+ omniauth (~> 1.3.1)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 047df4786a9..360acb864f6 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -49,10 +49,11 @@ class @AwardsHandler
counter.text(parseInt(counter.text()) - 1)
emojiIcon.removeClass("active")
@removeMeFromAuthorList(emoji)
- else if emoji =="thumbsup" || emoji == "thumbsdown"
+ else if emoji == "thumbsup" || emoji == "thumbsdown"
emojiIcon.tooltip("destroy")
counter.text(0)
emojiIcon.removeClass("active")
+ @removeMeFromAuthorList(emoji)
else
emojiIcon.tooltip("destroy")
emojiIcon.remove()
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 8d9a0aae568..0f68582e447 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -118,3 +118,19 @@ body {
@include gitlab-theme(#9988CC, $theme-violet, #443366, #332255);
}
}
+
+::-webkit-scrollbar{
+ width: 3px;
+}
+
+::-webkit-scrollbar-thumb{
+ background-color:$theme-charcoal; border-radius: 0;
+}
+
+::-webkit-scrollbar-thumb:hover{
+ background-color:$theme-charcoal;
+}
+
+::-webkit-scrollbar-track{
+ background-color:#FFF;
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 529a43548c8..d93b6ee6733 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -12,6 +12,14 @@
.identifier {
color: #5c5d5e;
}
+
+ .issue_created_ago, .author_link {
+ white-space: nowrap;
+ }
+
+ .issue-meta {
+ margin-left: 65px
+ }
}
.detail-page-description {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 9d5dc42b6cc..ef62f069dc2 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -80,6 +80,10 @@
display: inline-block;
}
+ .select2-container span {
+ margin-top: 0;
+ }
+
.issuable-count {
}
@@ -88,6 +92,10 @@
margin-left: 20px;
border-left: 1px solid $border-gray-light;
padding-left: 10px;
+
+ &:hover {
+ color: $gray-darkest;
+ }
}
}
@@ -192,6 +200,10 @@
.btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
+ &:hover {
+ background: $gray-dark;
+ border: 1px solid $border-gray-dark;
+ }
}
&.right-sidebar-collapsed {
@@ -223,6 +235,19 @@
display: block;
margin-top: 0;
}
+
+ .btn-clipboard {
+ border: none;
+
+ &:hover {
+ background: transparent;
+ }
+
+ i {
+ color: #999999;
+ }
+ }
+
}
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 48b1f95acb9..2c329b60a19 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -164,7 +164,7 @@ class ApplicationController < ActionController::Base
end
def git_not_found!
- render html: "errors/git_not_found", layout: "errors", status: 404
+ render "errors/git_not_found.html", layout: "errors", status: 404
end
def method_missing(method_sym, *arguments, &block)
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 21f4d9f44ec..36951b91372 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -11,8 +11,6 @@ class Projects::CommitController < Projects::ApplicationController
before_action :define_show_vars, only: [:show, :builds]
def show
- return git_not_found! unless @commit
-
apply_diff_view_cookie!
@line_notes = commit.notes.inline
@@ -68,6 +66,8 @@ class Projects::CommitController < Projects::ApplicationController
end
def define_show_vars
+ return git_not_found! unless commit
+
if params[:w].to_i == 1
@diffs = commit.diffs({ ignore_whitespace_change: true })
else
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 07f355c35b1..196996f1752 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -3,6 +3,7 @@ class Projects::ImportsController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :require_no_repo, only: [:new, :create]
before_action :redirect_if_progress, only: [:new, :create]
+ before_action :redirect_if_no_import, only: :show
def new
end
@@ -63,14 +64,19 @@ class Projects::ImportsController < Projects::ApplicationController
def require_no_repo
if @project.repository_exists?
- redirect_to(namespace_project_path(@project.namespace, @project))
+ redirect_to namespace_project_path(@project.namespace, @project)
end
end
def redirect_if_progress
if @project.import_in_progress?
- redirect_to namespace_project_import_path(@project.namespace, @project) &&
- return
+ redirect_to namespace_project_import_path(@project.namespace, @project)
+ end
+ end
+
+ def redirect_if_no_import
+ if @project.repository_exists? && @project.no_import?
+ redirect_to namespace_project_path(@project.namespace, @project)
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 622cbfe3cc4..ecefa9b006d 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -212,8 +212,7 @@ module ApplicationHelper
file_content
end
else
- GitHub::Markup.render(file_name, file_content).
- force_encoding(file_content.encoding).html_safe
+ other_markup(file_name, file_content)
end
rescue RuntimeError
simple_format(file_content)
@@ -281,76 +280,6 @@ module ApplicationHelper
end
end
- def issuable_link_next(project,issuable)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- namespace_project_issue_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
- elsif current_controller?(:merge_requests)
- namespace_project_merge_request_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
- end
- end
-
- def issuable_link_prev(project,issuable)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- namespace_project_issue_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
- elsif current_controller?(:merge_requests)
- namespace_project_merge_request_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
- end
- end
-
- def issuable_count(entity, project)
- if project.nil?
- 0
- elsif current_controller?(:issues)
- project.issues.send(entity).count
- elsif current_controller?(:merge_requests)
- project.merge_requests.send(entity).count
- end
- end
-
- def next_issuable_for(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id > ?", id).last
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id > ?", id).last
- end
- end
-
- def has_next_issuable?(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id > ?", id).last
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id > ?", id).last
- end
- end
-
- def prev_issuable_for(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id < ?", id).first
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id < ?", id).first
- end
- end
-
- def has_prev_issuable?(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id < ?", id).first
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id < ?", id).first
- end
- end
-
def state_filters_text_for(entity, project)
titles = {
opened: "Open"
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index f9bacc8ba45..6a3ab3ea40a 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -69,7 +69,7 @@ module DiffHelper
end
def line_comments
- @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code)
+ @line_comments ||= @line_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code)
end
def organize_comments(type_left, type_right, line_code_left, line_code_right)
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 1a226252251..89d2a648494 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -78,6 +78,21 @@ module GitlabMarkdownHelper
)
end
+ def other_markup(file_name, text)
+ Gitlab::OtherMarkup.render(
+ file_name,
+ text,
+ project: @project,
+ current_user: (current_user if defined?(current_user)),
+
+ # RelativeLinkFilter
+ project_wiki: @project_wiki,
+ requested_path: @path,
+ ref: @ref,
+ commit: @commit
+ )
+ end
+
# Return the first line of +text+, up to +max_chars+, after parsing the line
# as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
new file mode 100644
index 00000000000..91a3aa371ef
--- /dev/null
+++ b/app/helpers/issuables_helper.rb
@@ -0,0 +1,37 @@
+module IssuablesHelper
+
+ def sidebar_gutter_toggle_icon
+ sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
+ end
+
+ def sidebar_gutter_collapsed_class
+ "right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
+ end
+
+ def issuables_count(issuable)
+ base_issuable_scope(issuable).maximum(:iid)
+ end
+
+ def next_issuable_for(issuable)
+ base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
+ end
+
+ def prev_issuable_for(issuable)
+ base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
+ end
+
+ private
+
+ def sidebar_gutter_collapsed?
+ cookies[:collapsed_gutter] == 'true'
+ end
+
+ def base_issuable_scope(issuable)
+ issuable.project.send(issuable.class.table_name).send(issuable_state_scope(issuable))
+ end
+
+ def issuable_state_scope(issuable)
+ issuable.open? ? :opened : :closed
+ end
+
+end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 75f2ed5e054..29cb753e62c 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -3,18 +3,6 @@ module NavHelper
cookies[:collapsed_nav] == 'true'
end
- def sidebar_gutter_collapsed_class
- if cookies[:collapsed_gutter] == 'true'
- "right-sidebar-collapsed"
- else
- "right-sidebar-expanded"
- end
- end
-
- def sidebar_gutter_collapsed?
- cookies[:collapsed_gutter] == 'true'
- end
-
def nav_sidebar_class
if nav_menu_collapsed?
"sidebar-collapsed"
@@ -32,9 +20,9 @@ module NavHelper
end
def page_gutter_class
- if current_path?('merge_requests#show') ||
- current_path?('merge_requests#diffs') ||
- current_path?('merge_requests#commits') ||
+ if current_path?('merge_requests#show') ||
+ current_path?('merge_requests#diffs') ||
+ current_path?('merge_requests#commits') ||
current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed"
diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb
index 64c1ce8cfab..2f86d1be576 100644
--- a/app/mailers/emails/builds.rb
+++ b/app/mailers/emails/builds.rb
@@ -3,26 +3,27 @@ module Emails
def build_fail_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
+
add_project_headers
- add_build_headers
- headers['X-GitLab-Build-Status'] = "failed"
+ add_build_headers('failed')
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
end
def build_success_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
+
add_project_headers
- add_build_headers
- headers['X-GitLab-Build-Status'] = "success"
+ add_build_headers('success')
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
private
- def add_build_headers
+
+ def add_build_headers(status)
headers['X-GitLab-Build-Id'] = @build.id
headers['X-GitLab-Build-Ref'] = @build.ref
+ headers['X-GitLab-Build-Status'] = status.to_s
end
-
end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index cf6aa592e2a..5136d0196a5 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -129,13 +129,10 @@ module Issuable
hook_data = {
object_kind: self.class.name.underscore,
user: user.hook_attrs,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url
- },
- object_attributes: hook_attrs
+ project: project.hook_attrs,
+ object_attributes: hook_attrs,
+ # DEPRECATED
+ repository: project.hook_attrs.slice(:name, :url, :description, :homepage)
}
hook_data.merge!(assignee: assignee.hook_attrs) if assignee
diff --git a/app/models/project.rb b/app/models/project.rb
index f11c6d7c6be..95ad88c76ae 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -342,7 +342,7 @@ class Project < ActiveRecord::Base
end
def repository
- @repository ||= Repository.new(path_with_namespace, nil, self)
+ @repository ||= Repository.new(path_with_namespace, self)
end
def commit(id = 'HEAD')
@@ -382,6 +382,10 @@ class Project < ActiveRecord::Base
external_import? || forked?
end
+ def no_import?
+ import_status == 'none'
+ end
+
def external_import?
import_url.present?
end
@@ -738,11 +742,20 @@ class Project < ActiveRecord::Base
def hook_attrs
{
name: name,
- ssh_url: ssh_url_to_repo,
- http_url: http_url_to_repo,
+ description: description,
web_url: web_url,
+ avatar_url: avatar_url,
+ git_ssh_url: ssh_url_to_repo,
+ git_http_url: http_url_to_repo,
namespace: namespace.name,
- visibility_level: visibility_level
+ visibility_level: visibility_level,
+ path_with_namespace: path_with_namespace,
+ default_branch: default_branch,
+ # Backward compatibility
+ homepage: web_url,
+ url: url_to_repo,
+ ssh_url: ssh_url_to_repo,
+ http_url: http_url_to_repo
}
end
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 3d7e8bbee61..e76d9eca2ab 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -112,7 +112,7 @@ class PushoverService < Service
priority: priority,
title: "#{project.name_with_namespace}",
message: message,
- url: data[:repository][:homepage],
+ url: data[:project][:web_url],
url_title: "See project #{project.name_with_namespace}"
}
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index c847eba8d1c..c96e6f0b8ea 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -123,7 +123,7 @@ class ProjectWiki
end
def repository
- Repository.new(path_with_namespace, default_branch, @project)
+ Repository.new(path_with_namespace, @project)
end
def default_branch
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7f0047a002e..5a25ccb1dd6 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -15,7 +15,7 @@ class Repository
Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
end
- def initialize(path_with_namespace, default_branch = nil, project = nil)
+ def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
end
@@ -238,6 +238,15 @@ class Repository
expire_branch_cache(branch_name)
end
+ # Expires _all_ caches, including those that would normally only be expired
+ # under specific conditions.
+ def expire_all_caches!
+ expire_cache
+ expire_root_ref_cache
+ expire_emptiness_caches
+ expire_has_visible_content_cache
+ end
+
def expire_branch_cache(branch_name = nil)
# When we push to the root branch we have to flush the cache for all other
# branches as their statistics are based on the commits relative to the
@@ -258,6 +267,14 @@ class Repository
@root_ref = nil
end
+ # Expires the cache(s) used to determine if a repository is empty or not.
+ def expire_emptiness_caches
+ cache.expire(:empty?)
+ @empty = nil
+
+ expire_has_visible_content_cache
+ end
+
def expire_has_visible_content_cache
cache.expire(:has_visible_content?)
@has_visible_content = nil
@@ -611,6 +628,8 @@ class Repository
end
def merge_base(first_commit_id, second_commit_id)
+ first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
+ second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
rugged.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 294157b4f0e..f4dcb142850 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -16,11 +16,15 @@ module Projects
return false unless can?(current_user, :remove_project, project)
project.team.truncate
- project.repository.expire_cache unless project.empty_repo?
repo_path = project.path_with_namespace
wiki_path = repo_path + '.wiki'
+ # Flush the cache for both repositories. This has to be done _before_
+ # removing the physical repositories as some expiration code depends on
+ # Git data (e.g. a list of branch names).
+ flush_caches(project, wiki_path)
+
Project.transaction do
project.destroy!
@@ -70,5 +74,13 @@ module Projects
def removal_path(path)
"#{path}+#{project.id}#{DELETED_FLAG}"
end
+
+ def flush_caches(project, wiki_path)
+ project.repository.expire_all_caches! if project.repository.exists?
+
+ wiki_repo = Repository.new(wiki_path, project)
+
+ wiki_repo.expire_all_caches! if wiki_repo.exists?
+ end
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1083bcec054..edced010811 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -274,12 +274,15 @@ class SystemNoteService
# Check if a cross reference to a noteable from a mentioner already exists
#
# This method is used to prevent multiple notes being created for a mention
- # when a issue is updated, for example.
+ # when a issue is updated, for example. The method also calls notes_for_mentioner
+ # to check if the mentioner is a commit, and return matches only on commit hash
+ # instead of project + commit, to avoid repeated mentions from forks.
#
# noteable - Noteable object being referenced
# mentioner - Mentionable object
#
# Returns Boolean
+
def self.cross_reference_exists?(noteable, mentioner)
# Initial scope should be system notes of this noteable type
notes = Note.system.where(noteable_type: noteable.class)
@@ -291,14 +294,20 @@ class SystemNoteService
notes = notes.where(noteable_id: noteable.id)
end
- gfm_reference = mentioner.gfm_reference(noteable.project)
- notes = notes.where(note: cross_reference_note_content(gfm_reference))
-
- notes.count > 0
+ notes_for_mentioner(mentioner, noteable, notes).count > 0
end
private
+ def self.notes_for_mentioner(mentioner, noteable, notes)
+ if mentioner.is_a?(Commit)
+ notes.where('note LIKE ?', "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}")
+ else
+ gfm_reference = mentioner.gfm_reference(noteable.project)
+ notes.where(note: cross_reference_note_content(gfm_reference))
+ end
+ end
+
def self.create_note(args = {})
Note.create(args.merge(system: true))
end
diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml
index f4e9749e5c7..81d65037312 100644
--- a/app/views/notify/build_fail_email.html.haml
+++ b/app/views/notify/build_fail_email.html.haml
@@ -1,9 +1,10 @@
- content_for :header do
%h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
GitLab (build failed)
+
%h3
Project:
- = link_to ci_project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml
index 8b004d34cca..5d247eb4cf2 100644
--- a/app/views/notify/build_success_email.html.haml
+++ b/app/views/notify/build_success_email.html.haml
@@ -4,7 +4,7 @@
%h3
Project:
- = link_to ci_project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 2c5b8dc4356..f3bfe0a18b0 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -36,7 +36,7 @@
= render "download", blob: blob
- elsif blob.text?
- if blob_svg?(blob)
- = render "image", blob: sanitize_svg(blob)
+ = render "image", blob: blob
- else
= render "text", blob: blob
- elsif blob.image?
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index 51fa91b08e4..113dba5d832 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -1,2 +1,9 @@
.file-content.image_file
- %img{ src: namespace_project_raw_path(@project.namespace, @project, @id)}
+ - if blob_svg?(blob)
+ - # We need to scrub SVG but we cannot do so in the RawController: it would
+ - # be wrong/strange if RawController modified the data.
+ - blob.load_all_data!(@repository)
+ - blob = sanitize_svg(blob)
+ %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}
+ - else
+ %img{src: namespace_project_raw_path(@project.namespace, @project, @id)}
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 058b71b21f5..752e92e2e6b 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -1,9 +1,11 @@
- diff = diff_file.diff
+- file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))
+- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))
- if diff.renamed_file || diff.new_file || diff.deleted_file
.image
%span.wrap
.frame{class: image_diff_class(diff)}
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path}
%p.image-info= "#{number_to_human_size file.size}"
- else
.image
@@ -11,7 +13,7 @@
%span.wrap
.frame.deleted
%a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))}
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size old_file.size}"
|
@@ -23,7 +25,7 @@
%span.wrap
.frame.added
%a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))}
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size file.size}"
|
@@ -36,10 +38,10 @@
%div.swipe.view.hide
.swipe-frame
.frame.deleted
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
.swipe-wrap
.frame.added
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
%span.swipe-bar
%span.top-handle
%span.bottom-handle
@@ -47,9 +49,9 @@
%div.onion-skin.view.hide
.onion-skin-frame
.frame.deleted
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
.frame.added
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
.controls
.transparent
.drag-track
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index fe977fd700c..69a0e2a0c4d 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -6,16 +6,6 @@
.issue
.detail-page-header
- .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} Closed
- .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} Open
- %span.identifier
- Issue ##{@issue.iid}
- %span.creator
- &middot;
- opened by #{link_to_member(@project, @issue.author, size: 24)}
- &middot;
- = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
-
.pull-right
- if can?(current_user, :create_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'btn btn-nr btn-grouped new-issue-link btn-success', title: 'New Issue', id: 'new_issue_link' do
@@ -29,6 +19,19 @@
= icon('pencil-square-o')
Edit
+ .pull-left
+ .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} Closed
+ .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} Open
+
+ .issue-meta
+ %span.identifier
+ Issue ##{@issue.iid}
+ %span.creator
+ &middot;
+ by #{link_to_member(@project, @issue.author, size: 24)}
+ &middot;
+ = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
+
.issue-details.issuable-details
.detail-page-description.content-block
%h2.title
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index a54733883b4..986d8c220db 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -1,3 +1,3 @@
$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}";
$('aside.right-sidebar').effect('highlight');
-new Issue(); \ No newline at end of file
+new IssuableContext();
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index ce5157d69a2..9cce5660e1c 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -1,3 +1,3 @@
-$('aside.right-sidebar')[0].outerHTML= "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
-$('aside.right-sidebar').effect('highlight')
-merge_request = new MergeRequest();
+$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
+$('aside.right-sidebar').effect('highlight');
+new IssuableContext();
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index ae96a45453f..a45775f36b5 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -4,21 +4,18 @@
%span.issuable-count.pull-left
= issuable.iid
of
- = issuable_count(:all, @project)
+ = issuables_count(issuable)
%span.pull-right
%a.gutter-toggle{href: '#'}
- - if sidebar_gutter_collapsed?
- = icon('angle-double-left')
- - else
- = icon('angle-double-right')
+ = sidebar_gutter_toggle_icon
.issuable-nav.pull-right.btn-group{role: 'group', "aria-label" => '...'}
- - if has_prev_issuable?(@project, issuable.id)
- = link_to 'Prev', issuable_link_prev(@project, issuable), class: 'btn btn-default prev-btn'
+ - if prev_issuable = prev_issuable_for(issuable)
+ = link_to 'Prev', [@project.namespace.becomes(Namespace), @project, prev_issuable], class: 'btn btn-default prev-btn'
- else
%a.btn.btn-default.disabled{href: '#'}
Prev
- - if has_next_issuable?(@project, issuable.id)
- = link_to 'Next', issuable_link_next(@project, issuable), class: 'btn btn-default next-btn'
+ - if next_issuable = next_issuable_for(issuable)
+ = link_to 'Next', [@project.namespace.becomes(Namespace), @project, next_issuable], class: 'btn btn-default next-btn'
- else
%a.btn.btn-default.disabled{href: '#'}
Next
@@ -50,7 +47,7 @@
.block.milestone
.sidebar-collapsed-icon
- = icon('balance-scale')
+ = icon('clock-o')
%span
- if issuable.milestone
= issuable.milestone.title
@@ -118,7 +115,7 @@
- project_ref = cross_project_reference(@project, issuable)
.block.project-reference
.sidebar-collapsed-icon
- = icon('clipboard')
+ = clipboard_button(clipboard_text: project_ref)
.title
.cross-project-reference
%span
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 2f991c52339..2572b9d6d98 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -27,6 +27,7 @@ class RepositoryForkWorker
return
end
+ project.repository.expire_emptiness_caches
project.import_finish
end
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index e295a9ddd14..0b6f746e118 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -18,6 +18,7 @@ class RepositoryImportWorker
return
end
+ project.repository.expire_emptiness_caches
project.import_finish
end
end
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 018d1898594..9e89e6e395e 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -16,7 +16,7 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
### Predefined variables (Environment Variables)
| Variable | Runner | Description |
-|-------------------------|-------------|
+|-------------------------|-----|--------|
| **CI** | 0.4 | Mark that build is executed in CI environment |
| **GITLAB_CI** | all | Mark that build is executed in GitLab CI environment |
| **CI_SERVER** | all | Mark that build is executed in CI environment |
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index a6077779c08..28dedf3978c 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -8,6 +8,8 @@ In addition, having to take a server offline for a an upgrade small or big is
a big burden for most organizations. For this reason it is important that your
migrations are written carefully, can be applied online and adhere to the style guide below.
+It's advised to have offline migrations only in major GitLab releases.
+
When writing your migrations, also consider that databases might have stale data
or inconsistencies and guard for that. Try to make as little assumptions as possible
about the state of the database.
@@ -33,6 +35,8 @@ It is always preferable to have a migration run online. If you expect the migrat
to take particularly long (for instance, if it loops through all notes),
this is valuable information to add.
+If you don't provide the information it means that a migration is safe to run online.
+
### Reversibility
Your migration should be reversible. This is very important, as it should
@@ -85,4 +89,4 @@ select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(i
execute("UPDATE taggings SET tag_id = #{origin_tag_id} WHERE tag_id IN(#{duplicate_ids.join(",")})")
execute("DELETE FROM tags WHERE id IN(#{duplicate_ids.join(",")})")
end
-```
+``` \ No newline at end of file
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 7d3f9d0a2ed..7ae73450afb 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -182,25 +182,20 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
## 6. Redis
-As of this writing, most Debian/Ubuntu distributions ship with Redis 2.2 or
-2.4. GitLab requires at least Redis 2.8.
+GitLab requires at least Redis 2.8.
-Ubuntu users [can use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server)
-to install a recent version of Redis.
-
-The following instructions cover building and installing Redis from scratch:
+If you are using Debian 8 or Ubuntu 14.04 and up, then you can simply install
+Redis 2.8 with:
```sh
-# Build Redis
-wget http://download.redis.io/releases/redis-2.8.23.tar.gz
-tar xzf redis-2.8.23.tar.gz
-cd redis-2.8.23
-make
+sudo apt-get install redis-server
+```
-# Install Redis
-cd utils
-sudo ./install_server.sh
+If you are using Debian 7 or Ubuntu 12.04, follow the special documentation
+on [an alternate Redis installation](redis.md). Once done, follow the rest of
+the guide here.
+```
# Configure redis to use sockets
sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig
@@ -224,7 +219,7 @@ if [ -d /etc/tmpfiles.d ]; then
fi
# Activate the changes to redis.conf
-sudo service redis_6379 start
+sudo service redis-server restart
# Add git to the redis group
sudo usermod -aG redis git
diff --git a/doc/install/redis.md b/doc/install/redis.md
new file mode 100644
index 00000000000..4075e6283d0
--- /dev/null
+++ b/doc/install/redis.md
@@ -0,0 +1,60 @@
+# Install Redis on old distributions
+
+GitLab requires at least Redis 2.8. The following guide is for Debian 7 and
+Ubuntu 12.04. If you are using Debian 8 or Ubuntu 14.04 and up, follow the
+[installation guide](installation.md).
+
+## Install Redis 2.8 in Debian 7
+
+Redis 2.8 is included in the Debian Wheezy [backports] repository.
+
+1. Edit `/etc/apt/sources.list` and add the following line:
+
+ ```
+ deb http://http.debian.net/debian wheezy-backports main
+ ```
+
+1. Update the repositories:
+
+ ```
+ sudo apt-get update
+ ```
+
+1. Install `redis-server`:
+
+ ```
+ sudo apt-get -t wheezy-backports install redis-server
+ ```
+
+1. Follow the rest of the [installation guide](installation.md).
+
+## Install Redis 2.8 in Ubuntu 12.04
+
+We will [use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server)
+to install a recent version of Redis.
+
+1. Install the PPA repository:
+
+ ```
+ sudo add-apt-repository ppa:chris-lea/redis-server
+ ```
+
+ Your system will now fetch the PPA's key. This enables your Ubuntu system to
+ verify that the packages in the PPA have not been interfered with since they
+ were built.
+
+1. Update the repositories:
+
+ ```
+ sudo apt-get update
+ ```
+
+1. Install `redis-server`:
+
+ ```
+ sudo apt-get install redis-server
+ ```
+
+1. Follow the rest of the [installation guide](installation.md).
+
+[backports]: http://backports.debian.org/Instructions/ "Debian backports website"
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index cdd6652b7b0..f6d1234ac4a 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -18,8 +18,6 @@ for two-factor authentication. If you restore a GitLab backup without
restoring the database encryption key, users who have two-factor
authentication enabled will lose access to your GitLab server.
-If you are interested in GitLab CI backup please follow to the [CI backup documentation](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/raketasks/backup_restore.md)*
-
```
# use this command if you've installed GitLab with the Omnibus package
sudo gitlab-rake gitlab:backup:create
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c556597225c..b82306bd1da 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -1,5 +1,12 @@
# Web hooks
+_**Note:**
+Starting from GitLab 8.5:_
+
+- _the `repository` key is deprecated in favor of the `project` key_
+- _the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key_
+- _the `project.http_url` key is deprecated in favor of the `project.git_http_url` key_
+
Project web hooks allow you to trigger an URL if new code is pushed or a new issue is created.
You can configure web hooks to listen for specific events like pushes, issues or merge requests. GitLab will send a POST request with data to the web hook URL.
@@ -37,8 +44,25 @@ X-Gitlab-Event: Push Hook
"user_id": 4,
"user_name": "John Smith",
"user_email": "john@example.com",
+ "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 15,
- "repository": {
+ "project":{
+ "name":"Diaspora",
+ "description":"",
+ "web_url":"http://example.com/mike/diaspora",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:mike/diaspora.git",
+ "git_http_url":"http://example.com/mike/diaspora.git",
+ "namespace":"Mike",
+ "visibility_level":0,
+ "path_with_namespace":"mike/diaspora",
+ "default_branch":"master",
+ "homepage":"http://example.com/mike/diaspora",
+ "url":"git@example.com:mike/diasporadiaspora.git",
+ "ssh_url":"git@example.com:mike/diaspora.git",
+ "http_url":"http://example.com/mike/diaspora.git"
+ },
+ "repository":{
"name": "Diaspora",
"url": "git@example.com:mike/diasporadiaspora.git",
"description": "",
@@ -100,8 +124,25 @@ X-Gitlab-Event: Tag Push Hook
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
"user_id": 1,
"user_name": "John Smith",
+ "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 1,
- "repository": {
+ "project":{
+ "name":"Example",
+ "description":"",
+ "web_url":"http://example.com/jsmith/example",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:jsmith/example.git",
+ "git_http_url":"http://example.com/jsmith/example.git",
+ "namespace":"Jsmith",
+ "visibility_level":0,
+ "path_with_namespace":"jsmith/example",
+ "default_branch":"master",
+ "homepage":"http://example.com/jsmith/example",
+ "url":"git@example.com:jsmith/example.git",
+ "ssh_url":"git@example.com:jsmith/example.git",
+ "http_url":"http://example.com/jsmith/example.git"
+ },
+ "repository":{
"name": "jsmith",
"url": "ssh://git@example.com/jsmith/example.git",
"description": "",
@@ -135,7 +176,23 @@ X-Gitlab-Event: Issue Hook
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
- "repository": {
+ "project":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlabhq/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlabhq/gitlab-test.git",
+ "namespace":"GitlabHQ",
+ "visibility_level":20,
+ "path_with_namespace":"gitlabhq/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlabhq/gitlab-test",
+ "url":"http://example.com/gitlabhq/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
+ "http_url":"http://example.com/gitlabhq/gitlab-test.git"
+ },
+ "repository":{
"name": "Gitlab Test",
"url": "http://example.com/gitlabhq/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
@@ -197,9 +254,25 @@ X-Gitlab-Event: Note Hook
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
- "repository": {
+ "project":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlabhq/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlabhq/gitlab-test.git",
+ "namespace":"GitlabHQ",
+ "visibility_level":20,
+ "path_with_namespace":"gitlabhq/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlabhq/gitlab-test",
+ "url":"http://example.com/gitlabhq/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
+ "http_url":"http://example.com/gitlabhq/gitlab-test.git"
+ },
+ "repository":{
"name": "Gitlab Test",
- "url": "http://localhost/gitlab-org/gitlab-test.git",
+ "url": "http://example.com/gitlab-org/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlab-org/gitlab-test"
},
@@ -260,9 +333,25 @@ X-Gitlab-Event: Note Hook
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
- "repository": {
+ "project":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlab-org/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlab-org/gitlab-test.git",
+ "namespace":"Gitlab Org",
+ "visibility_level":10,
+ "path_with_namespace":"gitlab-org/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlab-org/gitlab-test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "http_url":"http://example.com/gitlab-org/gitlab-test.git"
+ },
+ "repository":{
"name": "Gitlab Test",
- "url": "http://example.com/gitlab-org/gitlab-test.git",
+ "url": "http://localhost/gitlab-org/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlab-org/gitlab-test"
},
@@ -300,21 +389,37 @@ X-Gitlab-Event: Note Hook
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
"position": 0,
"locked_at": null,
- "source": {
- "name": "Gitlab Test",
- "ssh_url": "git@example.com:gitlab-org/gitlab-test.git",
- "http_url": "http://example.com/gitlab-org/gitlab-test.git",
- "web_url": "http://example.com/gitlab-org/gitlab-test",
- "namespace": "Gitlab Org",
- "visibility_level": 10
+ "source":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlab-org/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlab-org/gitlab-test.git",
+ "namespace":"Gitlab Org",
+ "visibility_level":10,
+ "path_with_namespace":"gitlab-org/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlab-org/gitlab-test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "http_url":"http://example.com/gitlab-org/gitlab-test.git"
},
"target": {
- "name": "Gitlab Test",
- "ssh_url": "git@example.com:gitlab-org/gitlab-test.git",
- "http_url": "http://example.com/gitlab-org/gitlab-test.git",
- "web_url": "http://example.com/gitlab-org/gitlab-test",
- "namespace": "Gitlab Org",
- "visibility_level": 10
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlab-org/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlab-org/gitlab-test.git",
+ "namespace":"Gitlab Org",
+ "visibility_level":10,
+ "path_with_namespace":"gitlab-org/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlab-org/gitlab-test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "http_url":"http://example.com/gitlab-org/gitlab-test.git"
},
"last_commit": {
"id": "562e173be03b8ff2efb05345d12df18815438a4b",
@@ -355,11 +460,27 @@ X-Gitlab-Event: Note Hook
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
- "repository": {
- "name": "Gitlab Test",
- "url": "http://example.com/gitlab-org/gitlab-test.git",
- "description": "Aut reprehenderit ut est.",
- "homepage": "http://example.com/gitlab-org/gitlab-test"
+ "project":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlab-org/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlab-org/gitlab-test.git",
+ "namespace":"Gitlab Org",
+ "visibility_level":10,
+ "path_with_namespace":"gitlab-org/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlab-org/gitlab-test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "http_url":"http://example.com/gitlab-org/gitlab-test.git"
+ },
+ "repository":{
+ "name":"diaspora",
+ "url":"git@example.com:mike/diasporadiaspora.git",
+ "description":"",
+ "homepage":"http://example.com/mike/diaspora"
},
"object_attributes": {
"id": 1241,
@@ -397,7 +518,6 @@ X-Gitlab-Event: Note Hook
### Comment on code snippet
-
**Request header**:
```
@@ -415,11 +535,27 @@ X-Gitlab-Event: Note Hook
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
- "repository": {
- "name": "Gitlab Test",
- "url": "http://example.com/gitlab-org/gitlab-test.git",
- "description": "Aut reprehenderit ut est.",
- "homepage": "http://example.com/gitlab-org/gitlab-test"
+ "project":{
+ "name":"Gitlab Test",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/gitlab-org/gitlab-test",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "git_http_url":"http://example.com/gitlab-org/gitlab-test.git",
+ "namespace":"Gitlab Org",
+ "visibility_level":10,
+ "path_with_namespace":"gitlab-org/gitlab-test",
+ "default_branch":"master",
+ "homepage":"http://example.com/gitlab-org/gitlab-test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
+ "http_url":"http://example.com/gitlab-org/gitlab-test.git"
+ },
+ "repository":{
+ "name":"Gitlab Test",
+ "url":"http://example.com/gitlab-org/gitlab-test.git",
+ "description":"Aut reprehenderit ut est.",
+ "homepage":"http://example.com/gitlab-org/gitlab-test"
},
"object_attributes": {
"id": 1245,
@@ -491,21 +627,37 @@ X-Gitlab-Event: Merge Request Hook
"target_project_id": 14,
"iid": 1,
"description": "",
- "source": {
- "name": "awesome_project",
- "ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
- "http_url": "http://example.com/awesome_space/awesome_project.git",
- "web_url": "http://example.com/awesome_space/awesome_project",
- "visibility_level": 20,
- "namespace": "awesome_space"
+ "source":{
+ "name":"Awesome Project",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/awesome_space/awesome_project",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
+ "git_http_url":"http://example.com/awesome_space/awesome_project.git",
+ "namespace":"Awesome Space",
+ "visibility_level":20,
+ "path_with_namespace":"awesome_space/awesome_project",
+ "default_branch":"master",
+ "homepage":"http://example.com/awesome_space/awesome_project",
+ "url":"http://example.com/awesome_space/awesome_project.git",
+ "ssh_url":"git@example.com:awesome_space/awesome_project.git",
+ "http_url":"http://example.com/awesome_space/awesome_project.git"
},
"target": {
- "name": "awesome_project",
- "ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
- "http_url": "http://example.com/awesome_space/awesome_project.git",
- "web_url": "http://example.com/awesome_space/awesome_project",
- "visibility_level": 20,
- "namespace": "awesome_space"
+ "name":"Awesome Project",
+ "description":"Aut reprehenderit ut est.",
+ "web_url":"http://example.com/awesome_space/awesome_project",
+ "avatar_url":null,
+ "git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
+ "git_http_url":"http://example.com/awesome_space/awesome_project.git",
+ "namespace":"Awesome Space",
+ "visibility_level":20,
+ "path_with_namespace":"awesome_space/awesome_project",
+ "default_branch":"master",
+ "homepage":"http://example.com/awesome_space/awesome_project",
+ "url":"http://example.com/awesome_space/awesome_project.git",
+ "ssh_url":"git@example.com:awesome_space/awesome_project.git",
+ "http_url":"http://example.com/awesome_space/awesome_project.git"
},
"last_commit": {
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index be32f0c720b..0b205ea6de7 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -187,12 +187,15 @@ If you have an issue that spans across multiple repositories, the best thing is
![Vim screen showing the rebase view](rebase.png)
With git you can use an interactive rebase (`rebase -i`) to squash multiple commits into one and reorder them.
+In GitLab EE and .com you can also [rebase before merge](http://doc.gitlab.com/ee/workflow/rebase_before_merge.html) from the web interface.
This functionality is useful if you made a couple of commits for small changes during development and want to replace them with a single commit or if you want to make the order more logical.
However you should never rebase commits you have pushed to a remote server.
Somebody can have referred to the commits or cherry-picked them.
When you rebase you change the identifier (SHA-1) of the commit and this is confusing.
If you do that the same change will be known under multiple identifiers and this can cause much confusion.
If people already reviewed your code it will be hard for them to review only the improvements you made since then if you have rebased everything into one commit.
+Another reasons not to rebase is that you lose authorship information, maybe someone created a merge request, another person pushed a commit on there to improve it and a third one merged it.
+In this case rebasing all the commits into one prevent the other authors from being properly attributed and sharing part of the [git blame](https://git-scm.com/docs/git-blame).
People are encouraged to commit often and to frequently push to the remote repository so other people are aware what everyone is working on.
This will lead to many commits per change which makes the history harder to understand.
@@ -221,13 +224,11 @@ You can reuse recorded resolutions (rerere) sometimes, but without rebasing you
There has to be a better way to avoid many merge commits.
The way to prevent creating many merge commits is to not frequently merge master into the feature branch.
-We'll discuss the three reasons to merge in master: leveraging code, solving merge conflicts and long running branches.
+We'll discuss the three reasons to merge in master: leveraging code, merge conflicts, and long running branches.
If you need to leverage some code that was introduced in master after you created the feature branch you can sometimes solve this by just cherry-picking a commit.
If your feature branch has a merge conflict, creating a merge commit is a normal way of solving this.
-You should aim to prevent merge conflicts where they are likely to occur.
-One example is the CHANGELOG file where each significant change in the codebase is documented under a version header.
-Instead of everyone adding their change at the bottom of the list for the current version it is better to randomly insert it in the current list for that version.
-This it is likely that multiple feature branches that add to the CHANGELOG can be merged before a conflict occurs.
+You can prevent some merge conflicts by using [gitattributes](http://git-scm.com/docs/gitattributes) for files that can be in a random order.
+For example in GitLab our changelog file is specified in .gitattributes as `CHANGELOG merge=union` so that there are fewer merge conflicts in it.
The last reason for creating merge commits is having long lived branches that you want to keep up to date with the latest state of the project.
Martin Fowler, in [his article about feature branches](http://martinfowler.com/bliki/FeatureBranch.html) talks about this Continuous Integration (CI).
At GitLab we are guilty of confusing CI with branch testing. Quoting Martin Fowler: "I've heard people say they are doing CI because they are running builds, perhaps using a CI server, on every branch with every commit.
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
index bfde89fd896..2945bb3753a 100644
--- a/features/project/issues/award_emoji.feature
+++ b/features/project/issues/award_emoji.feature
@@ -7,7 +7,16 @@ Feature: Award Emoji
And I visit "Bugfix" issue page
@javascript
- Scenario: I add and remove award in the issue
+ Scenario: I repeatedly add and remove thumbsup award in the issue
+ Given I click the thumbsup award Emoji
+ Then I have award added
+ Given I click the thumbsup award Emoji
+ Then I have no awards added
+ Given I click the thumbsup award Emoji
+ Then I have award added
+
+ @javascript
+ Scenario: I add and remove custom award in the issue
Given I click to emoji-picker
Then The search field is focused
And I click to emoji in the picker
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 0b3d03aa2a5..ca2399d85a9 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -25,9 +25,16 @@ Feature: Project Issues
Scenario: I visit issue page
Given I click link "Release 0.4"
Then I should see issue "Release 0.4"
+ And I should see "1 of 2" in the sidebar
+
+ Scenario: I navigate between issues
+ Given I click link "Release 0.4"
+ Then I click link "Next" in the sidebar
+ Then I should see issue "Tweet control"
+ And I should see "2 of 2" in the sidebar
@javascript
- Scenario: I visit issue page
+ Scenario: I filter by author
Given I add a user to project "Shop"
And I click "author" dropdown
Then I see current user as the first user
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index ca1ee6b3c2b..5995e787961 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -39,6 +39,7 @@ Feature: Project Merge Requests
Scenario: I visit merge request page
Given I click link "Bug NS-04"
Then I should see merge request "Bug NS-04"
+ And I should see "1 of 1" in the sidebar
Scenario: I close merge request page
Given I click link "Bug NS-04"
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index 69695d493f3..93cf608cc62 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -8,6 +8,15 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
visit namespace_project_issue_path(@project.namespace, @project, @issue)
end
+ step 'I click the thumbsup award Emoji' do
+ page.within '.awards' do
+ thumbsup = page.find('.award .emoji-1F44D')
+ thumbsup.click
+ thumbsup.hover
+ sleep 0.3
+ end
+ end
+
step 'I click to emoji-picker' do
page.within '.awards-controls' do
page.find('.add-award').click
@@ -37,9 +46,28 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
end
step 'I have award added' do
+ sleep 0.2
+
page.within '.awards' do
expect(page).to have_selector '.award'
expect(page.find('.award.active .counter')).to have_content '1'
+ expect(page.find('.award.active')['data-original-title']).to eq('me')
+ end
+ end
+
+ step 'I have no awards added' do
+ page.within '.awards' do
+ expect(page).to have_selector '.award'
+ expect(page.all('.award').size).to eq(2)
+
+ # Check tooltip data
+ page.all('.award').each do |element|
+ expect(element['title']).to eq("")
+ end
+
+ page.all('.award .counter').each do |element|
+ expect(element).to have_content '0'
+ end
end
end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index d556b73f9fd..09a89e99831 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -54,6 +54,10 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
expect(page).to have_content "Release 0.4"
end
+ step 'I should see issue "Tweet control"' do
+ expect(page).to have_content "Tweet control"
+ end
+
step 'I click link "New Issue"' do
click_link "New Issue"
end
@@ -301,4 +305,5 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
def filter_issue(text)
fill_in 'issue_search', with: text
end
+
end
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index 25c2b476f43..2117feaedb8 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -119,6 +119,24 @@ module SharedIssuable
end
end
+ step 'I should see "1 of 1" in the sidebar' do
+ expect_sidebar_content('1 of 1')
+ end
+
+ step 'I should see "1 of 2" in the sidebar' do
+ expect_sidebar_content('1 of 2')
+ end
+
+ step 'I should see "2 of 2" in the sidebar' do
+ expect_sidebar_content('2 of 2')
+ end
+
+ step 'I click link "Next" in the sidebar' do
+ page.within '.issuable-sidebar' do
+ click_link 'Next'
+ end
+ end
+
def create_issuable_for_project(project_name:, title:, type: :issue)
project = Project.find_by(name: project_name)
@@ -159,4 +177,10 @@ module SharedIssuable
expect(page).to have_content("mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
end
+ def expect_sidebar_content(content)
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content content
+ end
+ end
+
end
diff --git a/lib/banzai/pipeline/asciidoc_pipeline.rb b/lib/banzai/pipeline/asciidoc_pipeline.rb
deleted file mode 100644
index f1331c0ebf9..00000000000
--- a/lib/banzai/pipeline/asciidoc_pipeline.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Banzai
- module Pipeline
- class AsciidocPipeline < BasePipeline
- def self.filters
- [
- Filter::RelativeLinkFilter
- ]
- end
- end
- end
-end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index b203b9d70e4..0b9c2e730f9 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -31,9 +31,7 @@ module Gitlab
html = ::Asciidoctor.convert(input, asciidoc_opts)
- if context[:project]
- html = Banzai.render(html, context.merge(pipeline: :asciidoc))
- end
+ html = Banzai.post_process(html, context)
html.html_safe
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 6ebb8027553..6f9da69983a 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -17,7 +17,7 @@ module Gitlab
end
def true_value
- if self.class.postgresql?
+ if Gitlab::Database.postgresql?
"'t'"
else
1
@@ -25,7 +25,7 @@ module Gitlab
end
def false_value
- if self.class.postgresql?
+ if Gitlab::Database.postgresql?
"'f'"
else
0
@@ -47,9 +47,5 @@ module Gitlab
row.first
end
end
-
- def connection
- self.class.connection
- end
end
end
diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb
index ea6b0ee796d..71cf6a0d886 100644
--- a/lib/gitlab/note_data_builder.rb
+++ b/lib/gitlab/note_data_builder.rb
@@ -53,13 +53,10 @@ module Gitlab
object_kind: "note",
user: user.hook_attrs,
project_id: project.id,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url,
- },
- object_attributes: note.hook_attrs
+ project: project.hook_attrs,
+ object_attributes: note.hook_attrs,
+ # DEPRECATED
+ repository: project.hook_attrs.slice(:name, :url, :description, :homepage)
}
base_data[:object_attributes][:url] =
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
new file mode 100644
index 00000000000..746ec283330
--- /dev/null
+++ b/lib/gitlab/other_markup.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ # Parser/renderer for markups without other special support code.
+ module OtherMarkup
+
+ # Public: Converts the provided markup into HTML.
+ #
+ # input - the source text in a markup format
+ # context - a Hash with the template context:
+ # :commit
+ # :project
+ # :project_wiki
+ # :requested_path
+ # :ref
+ #
+ def self.render(file_name, input, context)
+ html = GitHub::Markup.render(file_name, input).
+ force_encoding(input.encoding)
+
+ html = Banzai.post_process(html, context)
+
+ html.html_safe
+ end
+ end
+end
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index 4f9cdef3869..da1c15fef61 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -22,6 +22,8 @@ module Gitlab
# }
#
def build(project, user, oldrev, newrev, ref, commits = [], message = nil)
+ commits = Array(commits)
+
# Total commits count
commits_count = commits.size
@@ -47,18 +49,14 @@ module Gitlab
user_id: user.id,
user_name: user.name,
user_email: user.email,
+ user_avatar: user.avatar_url,
project_id: project.id,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url,
- git_http_url: project.http_url_to_repo,
- git_ssh_url: project.ssh_url_to_repo,
- visibility_level: project.visibility_level
- },
+ project: project.hook_attrs,
commits: commit_attrs,
- total_commits_count: commits_count
+ total_commits_count: commits_count,
+ # DEPRECATED
+ repository: project.hook_attrs.slice(:name, :url, :description, :homepage,
+ :git_http_url, :git_ssh_url, :visibility_level)
}
data
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
new file mode 100644
index 00000000000..438e776ec4b
--- /dev/null
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -0,0 +1,37 @@
+require 'rails_helper'
+
+describe Projects::CommitController do
+ describe 'GET show' do
+ let(:project) { create(:project) }
+
+ before do
+ user = create(:user)
+ project.team << [user, :master]
+
+ sign_in(user)
+ end
+
+ context 'with valid id' do
+ it 'responds with 200' do
+ go id: project.commit.id
+
+ expect(response).to be_ok
+ end
+ end
+
+ context 'with invalid id' do
+ it 'responds with 404' do
+ go id: project.commit.id.reverse
+
+ expect(response).to be_not_found
+ end
+ end
+
+ def go(id:)
+ get :show,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: id
+ end
+ end
+end
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 85d1d1e0524..0147bd2b953 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -104,6 +104,18 @@ describe Projects::ImportsController do
end
end
end
+
+ context 'when import never happened' do
+ before do
+ project.update_attribute(:import_status, :none)
+ end
+
+ it 'redirects to namespace_project_path' do
+ get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
+
+ expect(response).to redirect_to namespace_project_path(project.namespace, project)
+ end
+ end
end
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 30e353148a8..f6c1005d265 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -293,6 +293,10 @@ describe ApplicationHelper do
describe 'render_markup' do
let(:content) { 'Noël' }
+ let(:user) { create(:user) }
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
it 'should preserve encoding' do
expect(content.encoding.name).to eq('UTF-8')
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 6beb21c6d2b..736bf787208 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -42,22 +42,6 @@ module Gitlab
end
end
- context "with project in context" do
-
- let(:context) { { project: create(:project) } }
-
- it "should filter converted input via HTML pipeline and return result" do
- filtered_html = '<b>ASCII</b>'
-
- allow(Asciidoctor).to receive(:convert).and_return(html)
- expect(Banzai).to receive(:render)
- .with(html, context.merge(pipeline: :asciidoc))
- .and_return(filtered_html)
-
- expect( render('foo', context) ).to eql filtered_html
- end
- end
-
def render(*args)
described_class.render(*args)
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index bd8688fefa1..d0a447753b7 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -1,5 +1,9 @@
require 'spec_helper'
+class MigrationTest
+ include Gitlab::Database
+end
+
describe Gitlab::Database, lib: true do
# These are just simple smoke tests to check if the methods work (regardless
# of what they may return).
@@ -34,4 +38,32 @@ describe Gitlab::Database, lib: true do
end
end
end
+
+ describe '#true_value' do
+ it 'returns correct value for PostgreSQL' do
+ expect(described_class).to receive(:postgresql?).and_return(true)
+
+ expect(MigrationTest.new.true_value).to eq "'t'"
+ end
+
+ it 'returns correct value for MySQL' do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(MigrationTest.new.true_value).to eq 1
+ end
+ end
+
+ describe '#false_value' do
+ it 'returns correct value for PostgreSQL' do
+ expect(described_class).to receive(:postgresql?).and_return(true)
+
+ expect(MigrationTest.new.false_value).to eq "'f'"
+ end
+
+ it 'returns correct value for MySQL' do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(MigrationTest.new.false_value).to eq 0
+ end
+ end
end
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
index 691f36e6cb7..da652677443 100644
--- a/spec/lib/gitlab/note_data_builder_spec.rb
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -16,62 +16,80 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
end
describe 'When asking for a note on commit' do
- let(:note) { create(:note_on_commit) }
+ let(:note) { create(:note_on_commit, project: project) }
it 'returns the note and commit-specific data' do
expect(data).to have_key(:commit)
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe 'When asking for a note on commit diff' do
- let(:note) { create(:note_on_commit_diff) }
+ let(:note) { create(:note_on_commit_diff, project: project) }
it 'returns the note and commit-specific data' do
expect(data).to have_key(:commit)
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe 'When asking for a note on issue' do
let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) }
- let(:note) { create(:note_on_issue, noteable_id: issue.id) }
+ let(:note) { create(:note_on_issue, noteable_id: issue.id, project: project) }
it 'returns the note and issue-specific data' do
expect(data).to have_key(:issue)
expect(data[:issue].except('updated_at')).to eq(issue.hook_attrs.except('updated_at'))
expect(data[:issue]['updated_at']).to be > issue.hook_attrs['updated_at']
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe 'When asking for a note on merge request' do
let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
- let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
+ let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id, project: project) }
it 'returns the note and merge request data' do
expect(data).to have_key(:merge_request)
expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at'))
expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at']
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe 'When asking for a note on merge request diff' do
let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
- let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
+ let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id, project: project) }
it 'returns the note and merge request diff data' do
expect(data).to have_key(:merge_request)
expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at'))
expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at']
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe 'When asking for a note on project snippet' do
let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) }
- let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
+ let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id, project: project) }
it 'returns the note and project snippet data' do
expect(data).to have_key(:snippet)
expect(data[:snippet].except('updated_at')).to eq(snippet.hook_attrs.except('updated_at'))
expect(data[:snippet]['updated_at']).to be > snippet.hook_attrs['updated_at']
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
end
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
index 3ef61685398..961022b9d12 100644
--- a/spec/lib/gitlab/push_data_builder_spec.rb
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -1,34 +1,32 @@
require 'spec_helper'
-describe 'Gitlab::PushDataBuilder', lib: true do
+describe Gitlab::PushDataBuilder, lib: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
- describe :build_sample do
- let(:data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+ describe '.build_sample' do
+ let(:data) { described_class.build_sample(project, user) }
it { expect(data).to be_a(Hash) }
it { expect(data[:before]).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) }
- it { expect(data[:repository][:git_http_url]).to eq(project.http_url_to_repo) }
- it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) }
- it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) }
it { expect(data[:total_commits_count]).to eq(3) }
it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) }
it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) }
it { expect(data[:commits].first[:removed]).to eq([]) }
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
- describe :build do
+ describe '.build' do
let(:data) do
- Gitlab::PushDataBuilder.build(project,
- user,
- Gitlab::Git::BLANK_SHA,
- '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b',
- 'refs/tags/v1.1.0')
+ described_class.build(project, user, Gitlab::Git::BLANK_SHA,
+ '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b',
+ 'refs/tags/v1.1.0')
end
it { expect(data).to be_a(Hash) }
@@ -38,5 +36,10 @@ describe 'Gitlab::PushDataBuilder', lib: true do
it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
it { expect(data[:commits]).to be_empty }
it { expect(data[:total_commits_count]).to be_zero }
+
+ it 'does not raise an error when given nil commits' do
+ expect { described_class.build(spy, spy, spy, spy, spy, nil) }.
+ not_to raise_error
+ end
end
end
diff --git a/spec/mailers/emails/builds_spec.rb b/spec/mailers/emails/builds_spec.rb
new file mode 100644
index 00000000000..0df89938e97
--- /dev/null
+++ b/spec/mailers/emails/builds_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+require 'email_spec'
+require 'mailers/shared/notify'
+
+describe Notify do
+ include EmailSpec::Matchers
+
+ include_context 'gitlab email notification'
+
+ describe 'build notification email' do
+ let(:build) { create(:ci_build) }
+ let(:project) { build.project }
+
+ shared_examples 'build email' do
+ it 'contains name of project' do
+ is_expected.to have_body_text build.project_name
+ end
+
+ it 'contains link to project' do
+ is_expected.to have_body_text namespace_project_path(project.namespace, project)
+ end
+ end
+
+ shared_examples 'an email with X-GitLab headers containing build details' do
+ it 'has X-GitLab-Build* headers' do
+ is_expected.to have_header 'X-GitLab-Build-Id', /#{build.id}/
+ is_expected.to have_header 'X-GitLab-Build-Ref', /#{build.ref}/
+ end
+ end
+
+ describe 'build success' do
+ subject { Notify.build_success_email(build.id, 'wow@example.com') }
+ before { build.success }
+
+ it_behaves_like 'build email'
+ it_behaves_like 'an email with X-GitLab headers containing build details'
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+
+ it 'has header indicating build status' do
+ is_expected.to have_header 'X-GitLab-Build-Status', 'success'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Build success for/
+ end
+ end
+
+ describe 'build fail' do
+ subject { Notify.build_fail_email(build.id, 'wow@example.com') }
+ before { build.drop }
+
+ it_behaves_like 'build email'
+ it_behaves_like 'an email with X-GitLab headers containing build details'
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+
+ it 'has header indicating build status' do
+ is_expected.to have_header 'X-GitLab-Build-Status', 'failed'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Build failed for/
+ end
+ end
+ end
+end
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
new file mode 100644
index 00000000000..5b575da34f3
--- /dev/null
+++ b/spec/mailers/emails/profile_spec.rb
@@ -0,0 +1,107 @@
+require 'spec_helper'
+require 'email_spec'
+require 'mailers/shared/notify'
+
+describe Notify do
+ include EmailSpec::Matchers
+ include_context 'gitlab email notification'
+
+ describe 'profile notifications' do
+ describe 'for new users, the email' do
+ let(:example_site_path) { root_path }
+ let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) }
+ let(:token) { 'kETLwRaayvigPq_x3SNM' }
+
+ subject { Notify.new_user_email(new_user.id, token) }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'a new user email'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'contains the password text' do
+ is_expected.to have_body_text /Click here to set your password/
+ end
+
+ it 'includes a link for user to set password' do
+ params = "reset_password_token=#{token}"
+ is_expected.to have_body_text(
+ %r{http://localhost(:\d+)?/users/password/edit\?#{params}}
+ )
+ end
+
+ it 'explains the reset link expiration' do
+ is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/)
+ is_expected.to have_body_text(new_user_password_url)
+ is_expected.to have_body_text(/\?user_email=.*%40.*/)
+ end
+ end
+
+ describe 'for users that signed up, the email' do
+ let(:example_site_path) { root_path }
+ let(:new_user) { create(:user, email: new_user_address, password: "securePassword") }
+
+ subject { Notify.new_user_email(new_user.id) }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'a new user email'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'should not contain the new user\'s password' do
+ is_expected.not_to have_body_text /password/
+ end
+ end
+
+ describe 'user added ssh key' do
+ let(:key) { create(:personal_key) }
+
+ subject { Notify.new_ssh_key_email(key.id) }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'is sent to the new user' do
+ is_expected.to deliver_to key.user.email
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^SSH key was added to your account$/i
+ end
+
+ it 'contains the new ssh key title' do
+ is_expected.to have_body_text /#{key.title}/
+ end
+
+ it 'includes a link to ssh keys page' do
+ is_expected.to have_body_text /#{profile_keys_path}/
+ end
+ end
+
+ describe 'user added email' do
+ let(:email) { create(:email) }
+
+ subject { Notify.new_email_email(email.id) }
+
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'is sent to the new user' do
+ is_expected.to deliver_to email.user.email
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^Email was added to your account$/i
+ end
+
+ it 'contains the new email address' do
+ is_expected.to have_body_text /#{email.email}/
+ end
+
+ it 'includes a link to emails page' do
+ is_expected.to have_body_text /#{profile_emails_path}/
+ end
+ end
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 82bd057b16c..232a11245a6 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -1,237 +1,13 @@
require 'spec_helper'
require 'email_spec'
+require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Helpers
include EmailSpec::Matchers
include RepoHelpers
- new_user_address = 'newguy@example.com'
-
- let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
- let(:gitlab_sender) { Gitlab.config.gitlab.email_from }
- let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to }
- let(:recipient) { create(:user, email: 'recipient@example.com') }
- let(:project) { create(:project) }
- let(:build) { create(:ci_build) }
-
- before(:each) do
- ActionMailer::Base.deliveries.clear
- email = recipient.emails.create(email: "notifications@example.com")
- recipient.update_attribute(:notification_email, email.email)
- end
-
- shared_examples 'a multiple recipients email' do
- it 'is sent to the given recipient' do
- is_expected.to deliver_to recipient.notification_email
- end
- end
-
- shared_examples 'an email sent from GitLab' do
- it 'is sent from GitLab' do
- sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(gitlab_sender_display_name)
- expect(sender.address).to eq(gitlab_sender)
- end
-
- it 'has a Reply-To address' do
- reply_to = subject.header[:reply_to].addresses
- expect(reply_to).to eq([gitlab_sender_reply_to])
- end
- end
-
- shared_examples 'an email with X-GitLab headers containing project details' do
- it 'has X-GitLab-Project* headers' do
- is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
- is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/
- is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/
- end
- end
-
- shared_examples 'an email with X-GitLab headers containing build details' do
- it 'has X-GitLab-Build* headers' do
- is_expected.to have_header 'X-GitLab-Build-Id', /#{build.id}/
- is_expected.to have_header 'X-GitLab-Build-Ref', /#{build.ref}/
- end
- end
-
- shared_examples 'an email that contains a header with author username' do
- it 'has X-GitLab-Author header containing author\'s username' do
- is_expected.to have_header 'X-GitLab-Author', user.username
- end
- end
-
- shared_examples 'an email starting a new thread' do |message_id_prefix|
- include_examples 'an email with X-GitLab headers containing project details'
-
- it 'has a discussion identifier' do
- is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- end
- end
-
- shared_examples 'an answer to an existing thread' do |thread_id_prefix|
- include_examples 'an email with X-GitLab headers containing project details'
-
- it 'has a subject that begins with Re: ' do
- is_expected.to have_subject /^Re: /
- end
-
- it 'has headers that reference an existing thread' do
- is_expected.to have_header 'Message-ID', /<(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- end
- end
-
- shared_examples 'a new user email' do |user_email, site_path|
- it 'is sent to the new user' do
- is_expected.to deliver_to user_email
- end
-
- it 'has the correct subject' do
- is_expected.to have_subject /^Account was created for you$/i
- end
-
- it 'contains the new user\'s login name' do
- is_expected.to have_body_text /#{user_email}/
- end
-
- it 'includes a link to the site' do
- is_expected.to have_body_text /#{site_path}/
- end
- end
-
- shared_examples 'it should have Gmail Actions links' do
- it { is_expected.to have_body_text /ViewAction/ }
- end
-
- shared_examples 'it should not have Gmail Actions links' do
- it { is_expected.to_not have_body_text /ViewAction/ }
- end
-
- shared_examples 'it should show Gmail Actions View Issue link' do
- it_behaves_like 'it should have Gmail Actions links'
-
- it { is_expected.to have_body_text /View Issue/ }
- end
-
- shared_examples 'it should show Gmail Actions View Merge request link' do
- it_behaves_like 'it should have Gmail Actions links'
-
- it { is_expected.to have_body_text /View Merge request/ }
- end
-
- shared_examples 'it should show Gmail Actions View Commit link' do
- it_behaves_like 'it should have Gmail Actions links'
-
- it { is_expected.to have_body_text /View Commit/ }
- end
-
- shared_examples 'an unsubscribeable thread' do
- it { is_expected.to have_body_text /unsubscribe/ }
- end
-
- shared_examples "a user cannot unsubscribe through footer link" do
- it { is_expected.not_to have_body_text /unsubscribe/ }
- end
-
- describe 'for new users, the email' do
- let(:example_site_path) { root_path }
- let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) }
-
- token = 'kETLwRaayvigPq_x3SNM'
-
- subject { Notify.new_user_email(new_user.id, token) }
-
- it_behaves_like 'an email sent from GitLab'
- it_behaves_like 'a new user email', new_user_address
- it_behaves_like 'it should not have Gmail Actions links'
- it_behaves_like 'a user cannot unsubscribe through footer link'
-
- it 'contains the password text' do
- is_expected.to have_body_text /Click here to set your password/
- end
-
- it 'includes a link for user to set password' do
- params = "reset_password_token=#{token}"
- is_expected.to have_body_text(
- %r{http://localhost(:\d+)?/users/password/edit\?#{params}}
- )
- end
-
- it 'explains the reset link expiration' do
- is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/)
- is_expected.to have_body_text(new_user_password_url)
- is_expected.to have_body_text(/\?user_email=.*%40.*/)
- end
- end
-
- describe 'for users that signed up, the email' do
- let(:example_site_path) { root_path }
- let(:new_user) { create(:user, email: new_user_address, password: "securePassword") }
-
- subject { Notify.new_user_email(new_user.id) }
-
- it_behaves_like 'an email sent from GitLab'
- it_behaves_like 'a new user email', new_user_address
- it_behaves_like 'it should not have Gmail Actions links'
- it_behaves_like 'a user cannot unsubscribe through footer link'
-
- it 'should not contain the new user\'s password' do
- is_expected.not_to have_body_text /password/
- end
- end
-
- describe 'user added ssh key' do
- let(:key) { create(:personal_key) }
-
- subject { Notify.new_ssh_key_email(key.id) }
-
- it_behaves_like 'an email sent from GitLab'
- it_behaves_like 'it should not have Gmail Actions links'
- it_behaves_like 'a user cannot unsubscribe through footer link'
-
- it 'is sent to the new user' do
- is_expected.to deliver_to key.user.email
- end
-
- it 'has the correct subject' do
- is_expected.to have_subject /^SSH key was added to your account$/i
- end
-
- it 'contains the new ssh key title' do
- is_expected.to have_body_text /#{key.title}/
- end
-
- it 'includes a link to ssh keys page' do
- is_expected.to have_body_text /#{profile_keys_path}/
- end
- end
-
- describe 'user added email' do
- let(:email) { create(:email) }
-
- subject { Notify.new_email_email(email.id) }
-
- it_behaves_like 'it should not have Gmail Actions links'
- it_behaves_like 'a user cannot unsubscribe through footer link'
-
- it 'is sent to the new user' do
- is_expected.to deliver_to email.user.email
- end
-
- it 'has the correct subject' do
- is_expected.to have_subject /^Email was added to your account$/i
- end
-
- it 'contains the new email address' do
- is_expected.to have_body_text /#{email.email}/
- end
-
- it 'includes a link to emails page' do
- is_expected.to have_body_text /#{profile_emails_path}/
- end
- end
+ include_context 'gitlab email notification'
context 'for a project' do
describe 'items that are assignable, the email' do
@@ -971,49 +747,4 @@ describe Notify do
end
end
- describe 'build success' do
- before { build.success }
-
- subject { Notify.build_success_email(build.id, 'wow@example.com') }
-
- it_behaves_like 'an email with X-GitLab headers containing build details'
- it_behaves_like 'an email with X-GitLab headers containing project details' do
- let(:project) { build.project }
- end
-
- it 'has header indicating build status' do
- is_expected.to have_header 'X-GitLab-Build-Status', 'success'
- end
-
- it 'has the correct subject' do
- should have_subject /Build success for/
- end
-
- it 'contains name of project' do
- should have_body_text build.project_name
- end
- end
-
- describe 'build fail' do
- before { build.drop }
-
- subject { Notify.build_fail_email(build.id, 'wow@example.com') }
-
- it_behaves_like 'an email with X-GitLab headers containing build details'
- it_behaves_like 'an email with X-GitLab headers containing project details' do
- let(:project) { build.project }
- end
-
- it 'has header indicating build status' do
- is_expected.to have_header 'X-GitLab-Build-Status', 'failed'
- end
-
- it 'has the correct subject' do
- should have_subject /Build failed for/
- end
-
- it 'contains name of project' do
- should have_body_text build.project_name
- end
- end
end
diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb
new file mode 100644
index 00000000000..48c851ebbd6
--- /dev/null
+++ b/spec/mailers/shared/notify.rb
@@ -0,0 +1,117 @@
+shared_context 'gitlab email notification' do
+ let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
+ let(:gitlab_sender) { Gitlab.config.gitlab.email_from }
+ let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to }
+ let(:recipient) { create(:user, email: 'recipient@example.com') }
+ let(:project) { create(:project) }
+ let(:new_user_address) { 'newguy@example.com' }
+
+ before do
+ ActionMailer::Base.deliveries.clear
+ email = recipient.emails.create(email: "notifications@example.com")
+ recipient.update_attribute(:notification_email, email.email)
+ end
+end
+
+shared_examples 'a multiple recipients email' do
+ it 'is sent to the given recipient' do
+ is_expected.to deliver_to recipient.notification_email
+ end
+end
+
+shared_examples 'an email sent from GitLab' do
+ it 'is sent from GitLab' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(gitlab_sender_display_name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'has a Reply-To address' do
+ reply_to = subject.header[:reply_to].addresses
+ expect(reply_to).to eq([gitlab_sender_reply_to])
+ end
+end
+
+shared_examples 'an email that contains a header with author username' do
+ it 'has X-GitLab-Author header containing author\'s username' do
+ is_expected.to have_header 'X-GitLab-Author', user.username
+ end
+end
+
+shared_examples 'an email with X-GitLab headers containing project details' do
+ it 'has X-GitLab-Project* headers' do
+ is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
+ is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/
+ is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/
+ end
+end
+
+shared_examples 'an email starting a new thread' do |message_id_prefix|
+ include_examples 'an email with X-GitLab headers containing project details'
+
+ it 'has a discussion identifier' do
+ is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ end
+end
+
+shared_examples 'an answer to an existing thread' do |thread_id_prefix|
+ include_examples 'an email with X-GitLab headers containing project details'
+
+ it 'has a subject that begins with Re: ' do
+ is_expected.to have_subject /^Re: /
+ end
+
+ it 'has headers that reference an existing thread' do
+ is_expected.to have_header 'Message-ID', /<(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ end
+end
+
+shared_examples 'a new user email' do
+ it 'is sent to the new user' do
+ is_expected.to deliver_to new_user_address
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^Account was created for you$/i
+ end
+
+ it 'contains the new user\'s login name' do
+ is_expected.to have_body_text /#{new_user_address}/
+ end
+end
+
+shared_examples 'it should have Gmail Actions links' do
+ it { is_expected.to have_body_text /ViewAction/ }
+end
+
+shared_examples 'it should not have Gmail Actions links' do
+ it { is_expected.to_not have_body_text /ViewAction/ }
+end
+
+shared_examples 'it should show Gmail Actions View Issue link' do
+ it_behaves_like 'it should have Gmail Actions links'
+
+ it { is_expected.to have_body_text /View Issue/ }
+end
+
+shared_examples 'it should show Gmail Actions View Merge request link' do
+ it_behaves_like 'it should have Gmail Actions links'
+
+ it { is_expected.to have_body_text /View Merge request/ }
+end
+
+shared_examples 'it should show Gmail Actions View Commit link' do
+ it_behaves_like 'it should have Gmail Actions links'
+
+ it { is_expected.to have_body_text /View Commit/ }
+end
+
+shared_examples 'an unsubscribeable thread' do
+ it { is_expected.to have_body_text /unsubscribe/ }
+end
+
+shared_examples "a user cannot unsubscribe through footer link" do
+ it { is_expected.not_to have_body_text /unsubscribe/ }
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 8f09ff03a78..600089802b2 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -69,27 +69,28 @@ describe Issue, "Issuable" do
end
describe "#to_hook_data" do
- let(:hook_data) { issue.to_hook_data(user) }
+ let(:data) { issue.to_hook_data(user) }
+ let(:project) { issue.project }
+
it "returns correct hook data" do
- expect(hook_data[:object_kind]).to eq("issue")
- expect(hook_data[:user]).to eq(user.hook_attrs)
- expect(hook_data[:repository][:name]).to eq(issue.project.name)
- expect(hook_data[:repository][:url]).to eq(issue.project.url_to_repo)
- expect(hook_data[:repository][:description]).to eq(issue.project.description)
- expect(hook_data[:repository][:homepage]).to eq(issue.project.web_url)
- expect(hook_data[:object_attributes]).to eq(issue.hook_attrs)
- expect(hook_data).to_not have_key(:assignee)
+ expect(data[:object_kind]).to eq("issue")
+ expect(data[:user]).to eq(user.hook_attrs)
+ expect(data[:object_attributes]).to eq(issue.hook_attrs)
+ expect(data).to_not have_key(:assignee)
end
context "issue is assigned" do
before { issue.update_attribute(:assignee, user) }
it "returns correct hook data" do
- expect(hook_data[:object_attributes]['assignee_id']).to eq(user.id)
- expect(hook_data[:assignee]).to eq(user.hook_attrs)
+ expect(data[:object_attributes]['assignee_id']).to eq(user.id)
+ expect(data[:assignee]).to eq(user.hook_attrs)
end
end
+
+ include_examples 'project hook data'
+ include_examples 'deprecated repository hook data'
end
describe '#card_attributes' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c61ddf01118..f35b48601ad 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -254,13 +254,22 @@ describe MergeRequest, models: true do
end
describe "#hook_attrs" do
+ let(:attrs_hash) { subject.hook_attrs.to_h }
+
+ [:source, :target].each do |key|
+ describe "#{key} key" do
+ include_examples 'project hook data', project_key: key do
+ let(:data) { attrs_hash }
+ let(:project) { subject.send("#{key}_project") }
+ end
+ end
+ end
+
it "has all the required keys" do
- attrs = subject.hook_attrs
- attrs = attrs.to_h
- expect(attrs).to include(:source)
- expect(attrs).to include(:target)
- expect(attrs).to include(:last_commit)
- expect(attrs).to include(:work_in_progress)
+ expect(attrs_hash).to include(:source)
+ expect(attrs_hash).to include(:target)
+ expect(attrs_hash).to include(:last_commit)
+ expect(attrs_hash).to include(:work_in_progress)
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index e1ee43e64db..2cd0606a61d 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -355,6 +355,17 @@ describe Repository, models: true do
end
end
+ describe '#expire_emptiness_caches' do
+ let(:cache) { repository.send(:cache) }
+
+ it 'expires the caches' do
+ expect(cache).to receive(:expire).with(:empty?)
+ expect(repository).to receive(:expire_has_visible_content_cache)
+
+ repository.expire_emptiness_caches
+ end
+ end
+
describe :skip_merged_commit do
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index d3364a71022..1bdc03af12d 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -424,6 +424,21 @@ describe SystemNoteService, services: true do
to be_falsey
end
end
+
+ context 'commit with cross-reference from fork' do
+ let(:author2) { create(:user) }
+ let(:forked_project) { Projects::ForkService.new(project, author2).execute }
+ let(:commit2) { forked_project.commit }
+
+ before do
+ described_class.cross_reference(noteable, commit0, author2)
+ end
+
+ it 'is true when a fork mentions an external issue' do
+ expect(described_class.cross_reference_exists?(noteable, commit2)).
+ to be true
+ end
+ end
end
include JiraServiceHelper
diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb
new file mode 100644
index 00000000000..422083875d7
--- /dev/null
+++ b/spec/support/project_hook_data_shared_example.rb
@@ -0,0 +1,27 @@
+RSpec.shared_examples 'project hook data' do |project_key: :project|
+ it 'contains project data' do
+ expect(data[project_key][:name]).to eq(project.name)
+ expect(data[project_key][:description]).to eq(project.description)
+ expect(data[project_key][:web_url]).to eq(project.web_url)
+ expect(data[project_key][:avatar_url]).to eq(project.avatar_url)
+ expect(data[project_key][:git_http_url]).to eq(project.http_url_to_repo)
+ expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo)
+ expect(data[project_key][:namespace]).to eq(project.namespace.name)
+ expect(data[project_key][:visibility_level]).to eq(project.visibility_level)
+ expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace)
+ expect(data[project_key][:default_branch]).to eq(project.default_branch)
+ expect(data[project_key][:homepage]).to eq(project.web_url)
+ expect(data[project_key][:url]).to eq(project.url_to_repo)
+ expect(data[project_key][:ssh_url]).to eq(project.ssh_url_to_repo)
+ expect(data[project_key][:http_url]).to eq(project.http_url_to_repo)
+ end
+end
+
+RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project|
+ it 'contains deprecated repository data' do
+ expect(data[:repository][:name]).to eq(project.name)
+ expect(data[:repository][:description]).to eq(project.description)
+ expect(data[:repository][:url]).to eq(project.url_to_repo)
+ expect(data[:repository][:homepage]).to eq(project.web_url)
+ end
+end
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index dae31992620..172537474ee 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -19,6 +19,18 @@ describe RepositoryForkWorker do
fork_project.namespace.path)
end
+ it 'flushes the empty caches' do
+ expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).
+ with(project.path_with_namespace, fork_project.namespace.path).
+ and_return(true)
+
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
+ and_call_original
+
+ subject.perform(project.id, project.path_with_namespace,
+ fork_project.namespace.path)
+ end
+
it "handles bad fork" do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false)
subject.perform(
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
new file mode 100644
index 00000000000..6739063543b
--- /dev/null
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe RepositoryImportWorker do
+ let(:project) { create(:project) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'imports a project' do
+ expect_any_instance_of(Projects::ImportService).to receive(:execute).
+ and_return({ status: :ok })
+
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
+ expect_any_instance_of(Project).to receive(:import_finish)
+
+ subject.perform(project.id)
+ end
+ end
+end