Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-02-17 19:59:17 +0300
committerRémy Coutable <remy@rymai.me>2016-02-17 19:59:17 +0300
commit64f4784584bf02f04a62251a753a19bf51178e00 (patch)
tree89ab85bb44cefc9b9251fdadfd48d96c6057078d
parentc348da5b68a1975300e9367ab406e0570e07901a (diff)
parenta4ffdf922da488b2f04e81f5fc29243413e42cd3 (diff)
Merge remote-tracking branch 'origin/master' into 8-5-stable
-rw-r--r--.gitlab-ci.yml46
-rw-r--r--CHANGELOG18
-rw-r--r--CONTRIBUTING.md1
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock10
-rw-r--r--app/assets/javascripts/awards_handler.coffee3
-rw-r--r--app/assets/javascripts/dashboard.js.coffee9
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee2
-rw-r--r--app/assets/javascripts/logo.js.coffee3
-rw-r--r--app/assets/javascripts/projects_list.js.coffee34
-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/ci/projects_controller.rb6
-rw-r--r--app/controllers/dashboard/projects_controller.rb4
-rw-r--r--app/controllers/explore/projects_controller.rb36
-rw-r--r--app/controllers/groups_controller.rb19
-rw-r--r--app/controllers/projects/badges_controller.rb11
-rw-r--r--app/controllers/projects/builds_controller.rb15
-rw-r--r--app/controllers/projects/commit_controller.rb4
-rw-r--r--app/controllers/projects/commits_controller.rb3
-rw-r--r--app/controllers/projects/compare_controller.rb23
-rw-r--r--app/controllers/projects/imports_controller.rb12
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-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/issues_helper.rb6
-rw-r--r--app/helpers/nav_helper.rb18
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/mailers/emails/builds.rb13
-rw-r--r--app/models/application_setting.rb4
-rw-r--r--app/models/concerns/issuable.rb16
-rw-r--r--app/models/email.rb2
-rw-r--r--app/models/member.rb1
-rw-r--r--app/models/merge_request.rb3
-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.rb24
-rw-r--r--app/models/user.rb7
-rw-r--r--app/services/ci/image_for_build_service.rb19
-rw-r--r--app/services/create_branch_service.rb6
-rw-r--r--app/services/delete_branch_service.rb6
-rw-r--r--app/services/projects/destroy_service.rb14
-rw-r--r--app/services/system_note_service.rb19
-rw-r--r--app/validators/email_validator.rb15
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml8
-rw-r--r--app/views/explore/projects/_projects.html.haml2
-rw-r--r--app/views/groups/_projects.html.haml17
-rw-r--r--app/views/groups/show.html.haml2
-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/commits/show.html.haml5
-rw-r--r--app/views/projects/compare/_form.html.haml5
-rw-r--r--app/views/projects/diffs/_image.html.haml16
-rw-r--r--app/views/projects/forks/index.html.haml19
-rw-r--r--app/views/projects/issues/show.html.haml29
-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/projects/tree/_readme.html.haml2
-rw-r--r--app/views/projects/variables/show.html.haml4
-rw-r--r--app/views/shared/_logo.svg28
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml19
-rw-r--r--app/views/shared/projects/_list.html.haml3
-rw-r--r--app/workers/repository_fork_worker.rb1
-rw-r--r--app/workers/repository_import_worker.rb1
-rw-r--r--config/application.rb18
-rw-r--r--config/gitlab.yml.example8
-rw-r--r--config/routes.rb9
-rw-r--r--config/unicorn.rb.example9
-rw-r--r--db/fixtures/development/14_builds.rb39
-rw-r--r--doc/api/builds.md68
-rw-r--r--doc/api/merge_requests.md64
-rw-r--r--doc/ci/docker/using_docker_images.md2
-rw-r--r--doc/ci/quick_start/README.md8
-rw-r--r--doc/ci/variables/README.md7
-rw-r--r--doc/development/migration_style_guide.md8
-rw-r--r--doc/install/installation.md36
-rw-r--r--doc/install/redis.md60
-rw-r--r--doc/install/relative_url.md126
-rw-r--r--doc/markdown/img/logo.pngbin0 -> 11097 bytes
-rw-r--r--doc/markdown/markdown.md8
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--doc/update/8.4-to-8.5.md116
-rw-r--r--doc/web_hooks/web_hooks.md266
-rw-r--r--doc/workflow/gitlab_flow.md11
-rw-r--r--features/project/badges/build.feature22
-rw-r--r--features/project/commits/commits.feature36
-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/badges/build.rb28
-rw-r--r--features/steps/project/commits/commits.rb57
-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/builds.rb16
-rw-r--r--features/steps/shared/issuable.rb24
-rw-r--r--lib/api/merge_requests.rb19
-rw-r--r--lib/banzai/pipeline/asciidoc_pipeline.rb11
-rw-r--r--lib/gitlab/asciidoc.rb4
-rw-r--r--lib/gitlab/blame.rb1
-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
-rwxr-xr-xlib/support/init.d/gitlab.default.example13
-rw-r--r--lib/tasks/gitlab/check.rake18
-rwxr-xr-xscripts/prepare_build.sh12
-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/application_setting_spec.rb4
-rw-r--r--spec/models/concerns/issuable_spec.rb27
-rw-r--r--spec/models/email_spec.rb22
-rw-r--r--spec/models/member_spec.rb6
-rw-r--r--spec/models/merge_request_spec.rb21
-rw-r--r--spec/models/repository_spec.rb17
-rw-r--r--spec/models/user_spec.rb36
-rw-r--r--spec/requests/api/merge_requests_spec.rb37
-rw-r--r--spec/services/system_note_service_spec.rb15
-rw-r--r--spec/support/email_format_shared_examples.rb44
-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
139 files changed, 2179 insertions, 912 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5dfeb8a1f90..1c4d98ea3f6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,6 +5,11 @@ services:
- postgres:latest
- redis:latest
+cache:
+ key: "ruby22"
+ paths:
+ - vendor
+
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
@@ -144,6 +149,10 @@ spec:feature:ruby21:
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -154,6 +163,10 @@ spec:api:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -164,6 +177,10 @@ spec:models:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -174,6 +191,10 @@ spec:lib:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -184,6 +205,10 @@ spec:services:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -194,6 +219,10 @@ spec:benchmark:ruby21:
- master
script:
- RAILS_ENV=test bundle exec rake spec:benchmark
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -205,6 +234,10 @@ spec:other:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -215,6 +248,10 @@ spinach:project:half:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -225,6 +262,10 @@ spinach:project:rest:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
@@ -235,6 +276,11 @@ spinach:other:ruby21:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
+
diff --git a/CHANGELOG b/CHANGELOG
index 800f9c7918f..74bc366d203 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,9 @@
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
- Update New Relic gem to 3.14.1.311 (Stan Hu)
- Add "visibility" flag to GET /projects api endpoint
@@ -14,11 +16,14 @@ v 8.5.0 (unreleased)
- New UI for pagination
- Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
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
- Increase project import timeout to 15 minutes
+ - Be more permissive with email address validation: it only has to contain a single '@'
- Display 404 error on group not found
- Track project import failure
- Support Two-factor Authentication for LDAP users
@@ -29,28 +34,40 @@ 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)
- Fix API to keep request parameters in Link header (Michael Potthoff)
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
- Prevent parse error when name of project ends with .atom and prevent path issues
- Mark inline difference between old and new paths when a file is renamed
- Support Akismet spam checking for creation of issues via API (Stan Hu)
+ - API: Allow to set or update a merge-request's milestone (Kirill Skachkov)
- Improve UI consistency between projects and groups lists
- Add sort dropdown to dashboard projects page
- 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
- Prevent long-running backup tasks from timing out the database connection
- Add a Project setting to allow guests to view build logs (defaults to true)
+ - Sort project milestones by due date including issue editor (Oliver Rogers / Orih)
v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger
@@ -386,6 +403,7 @@ v 8.1.0
- Improved performance of the trending projects page
- Remove CI migration task
- Improved performance of finding projects by their namespace
+ - Add assignee data to Issuables' hook_data (Bram Daams)
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
- Fix build trace updating
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 263f98b1e57..c4522998f42 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -364,6 +364,7 @@ corresponding merge request should be updated to have the following:
This makes it easier for release managers to keep track of what still has to be
merged and where changes have to be merged into.
+Like all merge requests the target should be master so all bugfixes are in master.
## Definition of done
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 844f6a91acb..d2b13eb644d 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.6.3
+0.6.4
diff --git a/Gemfile b/Gemfile
index a13f49ad50e..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'
@@ -105,7 +105,7 @@ gem 'rouge', '~> 1.10.1'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
-gem 'nokogiri', '1.6.7.2'
+gem 'nokogiri', '~> 1.6.7', '>= 1.6.7.2'
# Diffs
gem 'diffy', '~> 3.0.3'
diff --git a/Gemfile.lock b/Gemfile.lock
index df5099c7581..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)
@@ -959,11 +959,11 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
- nokogiri (= 1.6.7.2)
+ nokogiri (~> 1.6.7, >= 1.6.7.2)
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/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee
index dd295088312..62143e66cfe 100644
--- a/app/assets/javascripts/dashboard.js.coffee
+++ b/app/assets/javascripts/dashboard.js.coffee
@@ -1,10 +1,11 @@
@Dashboard =
init: ->
+ $(".projects-list-filter").off('keyup')
this.initSearch()
initSearch: ->
@timer = null
- $("#project-filter-form-field").on('keyup', ->
+ $(".projects-list-filter").on('keyup', ->
clearTimeout(@timer)
@timer = setTimeout(Dashboard.filterResults, 500)
)
@@ -13,8 +14,8 @@
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
- form = $("#project-filter-form")
- search = $("#project-filter-form-field").val()
+ form = $("form#project-filter-form")
+ search = $(".projects-list-filter").val()
project_filter_url = form.attr('action') + '?' + form.serialize()
$.ajax
@@ -24,7 +25,7 @@
complete: ->
$('.projects-list-holder').fadeTo(250, 1)
success: (data) ->
- $('div.projects-list-holder').replaceWith(data.html)
+ $('.projects-list-holder').replaceWith(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: project_filter_url}, document.title, project_filter_url
dataType: "json"
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index d4a2b74b143..b17f8e51470 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -16,6 +16,8 @@ class Dispatcher
shortcut_handler = null
switch page
+ when 'explore:projects:index', 'explore:projects:starred', 'explore:projects:trending'
+ Dashboard.init()
when 'projects:issues:index'
Issues.init()
shortcut_handler = new ShortcutsNavigation()
diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee
index e7d884662ea..35b2fbbba07 100644
--- a/app/assets/javascripts/logo.js.coffee
+++ b/app/assets/javascripts/logo.js.coffee
@@ -44,6 +44,7 @@ $(document).on('page:fetch', start)
$(document).on('page:change', stop)
$ ->
- # Make logo clickable
+ # Make logo clickable as part of a workaround for Safari visited
+ # link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index ebf7140b7e3..eab34be652a 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -3,22 +3,24 @@ class @ProjectsList
$(".projects-list .js-expand").on 'click', (e) ->
e.preventDefault()
list = $(this).closest('.projects-list')
- list.find("li").show()
- list.find("li.bottom").hide()
- $(".projects-list-filter").keyup ->
- terms = $(this).val()
- uiBox = $('div.projects-list-holder')
- filterSelector = $(this).data('filter-selector') || 'span.filter-title'
+ $("#filter_projects").on 'keyup', ->
+ ProjectsList.filter_results($("#filter_projects"))
- if terms == "" || terms == undefined
- uiBox.find("ul.projects-list li").show()
- else
- uiBox.find("ul.projects-list li").each (index) ->
- name = $(this).find(filterSelector).text()
+ @filter_results: ($element) ->
+ terms = $element.val()
+ filterSelector = $element.data('filter-selector') || 'span.filter-title'
- if name.toLowerCase().search(terms.toLowerCase()) == -1
- $(this).hide()
- else
- $(this).show()
- uiBox.find("ul.projects-list li.bottom").hide()
+ if not terms
+ $(".projects-list li").show()
+ $('.gl-pagination').show()
+ else
+ $(".projects-list li").each (index) ->
+ $this = $(this)
+ name = $this.find(filterSelector).text()
+
+ if name.toLowerCase().indexOf(terms.toLowerCase()) == -1
+ $this.hide()
+ else
+ $this.show()
+ $('.gl-pagination').hide()
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/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 711c2847d5e..d1824b481d7 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -12,9 +12,13 @@ module Ci
# Project status badge
# Image with build status for sha or ref
+ #
+ # This action in DEPRECATED, this is here only for backwards compatibility
+ # with projects migrated from GitLab CI.
+ #
def badge
+ return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params)
-
send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml"
end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 0bcc78a8bc7..2df6924b13d 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -12,7 +12,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = @projects.search(terms)
end
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
respond_to do |format|
@@ -41,7 +41,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = @projects.search(terms)
end
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
@groups = []
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 2689bf4f1ec..a384f3004db 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -6,19 +6,49 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.non_archived
@projects = @projects.search(params[:search]) if params[:search].present?
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.sort(@sort = params[:sort])
- @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
+ @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
def trending
@projects = TrendingProjectsFinder.new.execute(current_user)
@projects = @projects.non_archived
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
def starred
@projects = ProjectsFinder.new.execute(current_user)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.reorder('star_count DESC')
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 90475c17c17..ca5ce1e2046 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -14,7 +14,7 @@ class GroupsController < Groups::ApplicationController
# Load group projects
before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete]
- before_action :event_filter, only: :show
+ before_action :event_filter, only: [:show, :events]
layout :determine_layout
@@ -41,14 +41,16 @@ class GroupsController < Groups::ApplicationController
def show
@last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
- load_events
- pager_json("events/_events", @events.count)
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
end
format.atom do
@@ -58,6 +60,15 @@ class GroupsController < Groups::ApplicationController
end
end
+ def events
+ respond_to do |format|
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count)
+ end
+ end
+ end
+
def edit
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
new file mode 100644
index 00000000000..a4dd94b941c
--- /dev/null
+++ b/app/controllers/projects/badges_controller.rb
@@ -0,0 +1,11 @@
+class Projects::BadgesController < Projects::ApplicationController
+ def build
+ respond_to do |format|
+ format.html { render_404 }
+ format.svg do
+ image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref])
+ send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml')
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 9e89296e71d..ec379c53b8f 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,10 +1,8 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
-
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
before_action :authorize_update_build!, except: [:index, :show, :status]
-
- layout "project"
+ layout 'project'
def index
@scope = params[:scope]
@@ -24,7 +22,6 @@ class Projects::BuildsController < Projects::ApplicationController
def cancel_all
@project.builds.running_or_pending.each(&:cancel)
-
redirect_to namespace_project_builds_path(project.namespace, project)
end
@@ -47,20 +44,18 @@ class Projects::BuildsController < Projects::ApplicationController
end
build = Ci::Build.retry(@build)
-
redirect_to build_path(build)
end
- def status
- render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
- end
-
def cancel
@build.cancel
-
redirect_to build_path(@build)
end
+ def status
+ render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
+ end
+
private
def build
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/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index bf5b54c8cb7..1420b96840c 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -21,6 +21,9 @@ class Projects::CommitsController < Projects::ApplicationController
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
+ @merge_request = @project.merge_requests.opened.
+ find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
+
respond_to do |format|
format.html
format.json { pager_json("projects/commits/_commits", @commits.size) }
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 7bbe75b3974..dc5d217f3e4 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -4,24 +4,23 @@ class Projects::CompareController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
+ before_action :assign_ref_vars, only: [:index, :show]
+ before_action :merge_request, only: [:index, :show]
def index
- @ref = Addressable::URI.unescape(params[:to])
end
def show
- base_ref = Addressable::URI.unescape(params[:from])
- @ref = head_ref = Addressable::URI.unescape(params[:to])
diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
compare_result = CompareService.new.
- execute(@project, head_ref, @project, base_ref, diff_options)
+ execute(@project, @head_ref, @project, @base_ref, diff_options)
if compare_result
@commits = Commit.decorate(compare_result.commits, @project)
@diffs = compare_result.diffs
- @commit = @project.commit(head_ref)
- @base_commit = @project.merge_base_commit(base_ref, head_ref)
+ @commit = @project.commit(@head_ref)
+ @base_commit = @project.merge_base_commit(@base_ref, @head_ref)
@diff_refs = [@base_commit, @commit]
@line_notes = []
end
@@ -31,4 +30,16 @@ class Projects::CompareController < Projects::ApplicationController
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
+
+ private
+
+ def assign_ref_vars
+ @base_ref = Addressable::URI.unescape(params[:from])
+ @ref = @head_ref = Addressable::URI.unescape(params[:to])
+ end
+
+ def merge_request
+ @merge_request ||= @project.merge_requests.opened.
+ find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref)
+ end
end
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/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 15506bd677a..a5c4ef1c7c7 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -11,11 +11,12 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
- @milestones = case params[:state]
- when 'all'; @project.milestones.order("state, due_date DESC")
- when 'closed'; @project.milestones.closed.order("due_date DESC")
- else @project.milestones.active.order("due_date ASC")
- end
+ @milestones =
+ case params[:state]
+ when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc)
+ when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc)
+ else @project.milestones.active.reorder(due_date: :asc, title: :asc)
+ end
@milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]).per(PER_PAGE)
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/issues_helper.rb b/app/helpers/issues_helper.rb
index 43262d579e9..ae4ebc0854a 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -44,14 +44,14 @@ module IssuesHelper
end
def bulk_update_milestone_options
- milestones = project_active_milestones.to_a
+ milestones = @project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
def milestone_options(object)
- milestones = object.project.milestones.active.to_a
+ milestones = object.project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
@@ -69,7 +69,7 @@ module IssuesHelper
end
end
- def issue_button_visibility(issue, closed)
+ def issue_button_visibility(issue, closed)
return 'hidden' if issue.closed? == closed
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/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index cc1c61e9ec0..d6fb629b0c2 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -98,10 +98,6 @@ module ProjectsHelper
project_nav_tabs.include? name
end
- def project_active_milestones
- @project.milestones.active.order("due_date, title ASC")
- end
-
def project_for_deploy_key(deploy_key)
if deploy_key.projects.include?(@project)
@project
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/application_setting.rb b/app/models/application_setting.rb
index fa48fe5b9e4..269056e0e77 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -71,8 +71,8 @@ class ApplicationSetting < ActiveRecord::Base
url: true
validates :admin_notification_email,
- allow_blank: true,
- email: true
+ email: true,
+ allow_blank: true
validates :two_factor_grace_period,
numericality: { greater_than_or_equal_to: 0 }
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 04650a9e67a..5136d0196a5 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -126,17 +126,17 @@ module Issuable
end
def to_hook_data(user)
- {
+ 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
+
+ hook_data
end
def label_names
diff --git a/app/models/email.rb b/app/models/email.rb
index 935705e2ed4..b323d1edd10 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -15,7 +15,7 @@ class Email < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
- validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
+ validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email
diff --git a/app/models/member.rb b/app/models/member.rb
index 34efcd0088d..ca08007b7eb 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -39,7 +39,6 @@ class Member < ActiveRecord::Base
if: :invite?
},
email: {
- strict_mode: true,
allow_nil: true
},
uniqueness: {
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ddc476447ca..1be8061e53d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -137,6 +137,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
+ scope :opened, -> { with_state(:opened) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
@@ -240,7 +241,7 @@ class MergeRequest < ActiveRecord::Base
return unless unchecked?
can_be_merged =
- project.repository.can_be_merged?(source_sha, target_branch)
+ !broken? && project.repository.can_be_merged?(source_sha, target_branch)
if can_be_merged
mark_as_mergeable
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 0e17d2b669a..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
@@ -84,7 +84,8 @@ class Repository
offset: offset,
# --follow doesn't play well with --skip. See:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
- follow: false
+ follow: false,
+ skip_merges: skip_merges
}
commits = Gitlab::Git::Commit.where(options)
@@ -237,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
@@ -257,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
@@ -610,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/models/user.rb b/app/models/user.rb
index 234c1cd89f9..9fe94b13e52 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -146,11 +146,8 @@ class User < ActiveRecord::Base
# Validations
#
validates :name, presence: true
- # Note that a 'uniqueness' and presence check is provided by devise :validatable for email. We do not need to
- # duplicate that here as the validation framework will have duplicate errors in the event of a failure.
- validates :email, presence: true, email: { strict_mode: true }
- validates :notification_email, presence: true, email: { strict_mode: true }
- validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
+ validates :notification_email, presence: true, email: true
+ validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username,
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index f469b13e902..005a5c4661c 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -1,28 +1,23 @@
module Ci
class ImageForBuildService
- def execute(project, params)
- sha = params[:sha]
- sha ||=
- if params[:ref]
- project.commit(params[:ref]).try(:sha)
- end
+ def execute(project, opts)
+ sha = opts[:sha] || ref_sha(project, opts[:ref])
commit = project.ci_commits.ordered.find_by(sha: sha)
image_name = image_for_commit(commit)
image_path = Rails.root.join('public/ci', image_name)
-
- OpenStruct.new(
- path: image_path,
- name: image_name
- )
+ OpenStruct.new(path: image_path, name: image_name)
end
private
+ def ref_sha(project, ref)
+ project.commit(ref).try(:sha) if ref
+ end
+
def image_for_commit(commit)
return 'build-unknown.svg' unless commit
-
'build-' + commit.status + ".svg"
end
end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index c0e08a151f2..707c2f7ff85 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -29,11 +29,7 @@ class CreateBranchService < BaseService
end
if new_branch
- push_data = build_push_data(project, current_user, new_branch)
-
- project.execute_hooks(push_data.dup, :push_hooks)
- project.execute_services(push_data.dup, :push_hooks)
-
+ # GitPushService handles execution of services and hooks for branch pushes
success(new_branch)
else
error('Invalid reference name')
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 004b3ce7286..fae069ee4a5 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -25,11 +25,7 @@ class DeleteBranchService < BaseService
end
if repository.rm_branch(current_user, branch_name)
- push_data = build_push_data(branch)
-
- project.execute_hooks(push_data.dup, :push_hooks)
- project.execute_services(push_data.dup, :push_hooks)
-
+ # GitPushService handles execution of services and hooks for branch pushes
success('Branch was removed')
else
error('Failed to remove branch')
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/validators/email_validator.rb b/app/validators/email_validator.rb
index b35af100803..aab07a7ece4 100644
--- a/app/validators/email_validator.rb
+++ b/app/validators/email_validator.rb
@@ -1,18 +1,5 @@
-# EmailValidator
-#
-# Based on https://github.com/balexand/email_validator
-#
-# Extended to use only strict mode with following allowed characters:
-# ' - apostrophe
-#
-# See http://www.remote.org/jochen/mail/info/chars.html
-#
class EmailValidator < ActiveModel::EachValidator
- PATTERN = /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i.freeze
-
def validate_each(record, attribute, value)
- unless value =~ PATTERN
- record.errors.add(attribute, options[:message] || :invalid)
- end
+ record.errors.add(attribute, :invalid) unless value =~ Devise.email_regexp
end
end
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index d46998ec1e9..4bc761b3738 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -14,7 +14,7 @@
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
- = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short', spellcheck: false, id: 'project-filter-form-field'
+ = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field'
= render 'explore/projects/dropdown'
- if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 66a4b535ae5..c248dbb695f 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,11 +1,3 @@
-.pull-left
- = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
- .form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
- = hidden_field_tag :sort, @sort
- .form-group
- = button_tag 'Search', class: "btn"
-
.pull-right.hidden-sm.hidden-xs
- if current_user
.dropdown.inline.append-right-10
diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml
index 669079e9521..999a933390b 100644
--- a/app/views/explore/projects/_projects.html.haml
+++ b/app/views/explore/projects/_projects.html.haml
@@ -1,5 +1,5 @@
- if projects.any?
- .public-projects
+ .projects-list-holder
= render 'shared/projects/list', projects: projects
- else
.nothing-here-block
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index a829479bb38..9c16ab7e30f 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -1,10 +1,11 @@
-.projects-list-holder.prepend-top-default
- .input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- - if can? current_user, :create_projects, @group
- %span.input-group-btn
- = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new' do
- = icon('plus')
- New Project
+.top-area
+ .nav-controls
+ = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+ = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'input-short project-filter-form-field form-control projects-list-filter', spellcheck: false, id: 'project-filter-form-field'
+ - if current_user && current_user.can_create_project?
+ = link_to new_project_path, class: 'btn btn-new' do
+ = icon('plus')
+ New Project
+.projects-list-holder
= render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index ebb3df7dca3..a0ba11b11a1 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -47,7 +47,7 @@
= render 'shared/event_filter'
- .content_list
+ .content_list{data: {href: events_group_path}}
= spinner
.tab-pane#projects
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/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index ede64d47ab3..c52cf25d40a 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -11,7 +11,10 @@
= render 'shared/ref_switcher', destination: 'commits'
.block-controls.hidden-xs.hidden-sm
- - if create_mr_button?(@repository.root_ref, @ref)
+ - if @merge_request.present?
+ .control
+ = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
+ - elsif create_mr_button?(@repository.root_ref, @ref)
.control
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index efc25eda26b..4ab81f3635c 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -13,12 +13,13 @@
= text_field_tag :to, params[:to], class: "form-control", required: true
&nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
- - if create_mr_button?
+ - if @merge_request.present?
+ = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'prepend-left-10 btn'
+ - elsif create_mr_button?
= link_to create_mr_path, class: 'prepend-left-10 btn' do
= icon("plus")
Create Merge Request
-
:javascript
var availableTags = #{@project.repository.ref_names.to_json};
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/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index acb2353d3ca..42fa6fdb782 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -6,7 +6,7 @@
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
.nav-controls
- = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control input-short',
+ = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
@@ -29,14 +29,15 @@
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
= sort_title_oldest_updated
- - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
- - else
- = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
+ - if current_user && can?(current_user, :fork_project, @project)
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
+ - else
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
.projects-list-holder
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 121c775560f..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
@@ -46,8 +49,8 @@
Edited
= time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
- .merge-requests
- = render 'merge_requests'
+ .merge-requests
+ = render 'merge_requests'
.content-block
= render 'votes/votes_block', votable: @issue
@@ -57,4 +60,4 @@
.issuable-discussion
= render 'projects/issues/discussion'
-= render 'shared/issuable/sidebar', issuable: @issue \ No newline at end of file
+= render 'shared/issuable/sidebar', issuable: @issue
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/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index 3c5edf4b033..baaa2caa6de 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,7 +1,7 @@
%article.file-holder.readme-holder
.file-title
= blob_icon readme.mode, readme.name
- = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, @path, readme.name)) do
%strong
= readme.name
.file-content.wiki
diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml
index e80dffc1ced..efe1e6f24c2 100644
--- a/app/views/projects/variables/show.html.haml
+++ b/app/views/projects/variables/show.html.haml
@@ -3,9 +3,11 @@
Secret Variables
%p.light
- These variables will be set to environment by the runner and will be hidden in the build log.
+ These variables will be set to environment by the runner.
%br
So you can use them for passwords, secret keys or whatever you want.
+ %br
+ The value of the variable can be visible in build log if explicitly asked to do so.
%hr
diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg
index 3d279ec228c..b07f1c5603e 100644
--- a/app/views/shared/_logo.svg
+++ b/app/views/shared/_logo.svg
@@ -1,21 +1,9 @@
-<svg width="36px" height="36px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="tanuki-logo">
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
- <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
- <g id="Page-1" sketch:type="MSShapeGroup">
- <g id="Fill-1-+-Group-24">
- <g id="Group-24">
- <g id="Group">
- <path id="tanuki-right-ear" d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" fill="#E24329" class="tanuki-shape"></path>
- <path id="tanuki-right-cheek" d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" fill="#FCA326" class="tanuki-shape"></path>
- <path id="tanuki-right-eye" d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" fill="#FC6D26" class="tanuki-shape"></path>
- <path id="tanuki-nose" d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" fill="#E24329" class="tanuki-shape"></path>
- <path id="tanuki-left-eye" d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" fill="#FC6D26" class="tanuki-shape"></path>
- <path id="tanuki-left-cheek" d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" fill="#FCA326" class="tanuki-shape"></path>
- <path id="tanuki-left-ear" d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" fill="#E24329" class="tanuki-shape"></path>
- </g>
- </g>
- </g>
- </g>
- </g>
- </g>
+<svg width="36" height="36" id="tanuki-logo">
+ <path id="tanuki-right-ear" class="tanuki-shape" fill="#e24329" d="M2 14l9.38 9v-9l-4-12.28c-.205-.632-1.176-.632-1.38 0z"/>
+ <path id="tanuki-left-ear" class="tanuki-shape" fill="#e24329" d="M34 14l-9.38 9v-9l4-12.28c.205-.632 1.176-.632 1.38 0z"/>
+ <path id="tanuki-nose" class="tanuki-shape" fill="#e24329" d="M18,34.38 3,14 33,14 Z"/>
+ <path id="tanuki-right-eye" class="tanuki-shape" fill="#fc6d26" d="M18,34.38 11.38,14 2,14 6,25Z"/>
+ <path id="tanuki-left-eye" class="tanuki-shape" fill="#fc6d26" d="M18,34.38 24.62,14 34,14 30,25Z"/>
+ <path id="tanuki-right-cheek" class="tanuki-shape" fill="#fca326" d="M2 14L.1 20.16c-.18.565 0 1.2.5 1.56l17.42 12.66z"/>
+ <path id="tanuki-left-cheek" class="tanuki-shape" fill="#fca326" d="M34 14l1.9 6.16c.18.565 0 1.2-.5 1.56L18 34.38z"/>
</svg>
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/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 67edb264b7e..75684b972f1 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -21,9 +21,10 @@
#{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
= link_to '#', class: 'js-expand' do
Show all
- = paginate projects, theme: "gitlab" if !projects.kind_of?(Array)
+ = paginate projects, theme: "gitlab" if projects.respond_to? :total_pages
- else
%h3 No projects found
:javascript
new ProjectsList();
+ Dashboard.init();
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/config/application.rb b/config/application.rb
index d255ff0719f..1e9ec74cdbf 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -31,7 +31,7 @@ module Gitlab
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt)
+ config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt, :variables)
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
@@ -52,17 +52,11 @@ module Gitlab
config.action_view.sanitized_allowed_protocols = %w(smb)
- # Relative url support
- # Uncomment and customize the last line to run in a non-root path
- # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
- # Note that following settings need to be changed for this to work.
- # 1) In your application.rb file: config.relative_url_root = "/gitlab"
- # 2) In your gitlab.yml file: relative_url_root: /gitlab
- # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
- # 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
- # 5) In lib/support/nginx/gitlab : do not use asset gzipping, remove block starting with "location ~ ^/(assets)/"
- #
- # To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+ # Relative URL support
+ # WARNING: We recommend using an FQDN to host GitLab in a root path instead
+ # of using a relative URL.
+ # Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+ # Uncomment and customize the following line to run in a non-root path
#
# config.relative_url_root = "/gitlab"
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index d6e2c9380a5..faf05ecd466 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -38,8 +38,12 @@ production: &base
# Otherwise, ssh host will be set to the `host:` value above
# ssh_host: ssh.host_example.com
- # WARNING: See config/application.rb under "Relative url support" for the list of
- # other files that need to be changed for relative url support
+ # Relative URL support
+ # WARNING: We recommend using an FQDN to host GitLab in a root path instead
+ # of using a relative URL.
+ # Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+ # Uncomment and customize the following line to run in a non-root path
+ #
# relative_url_root: /gitlab
# Uncomment and customize if you can't use the default user to run GitLab (default: 'git')
diff --git a/config/routes.rb b/config/routes.rb
index f5951bf3e8d..507bcbc53d7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -352,6 +352,7 @@ Rails.application.routes.draw do
get :issues
get :merge_requests
get :projects
+ get :events
end
scope module: :groups do
@@ -607,7 +608,7 @@ Rails.application.routes.draw do
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :destroy]
- resources :builds, only: [:index, :show] do
+ resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do
collection do
post :cancel_all
end
@@ -696,6 +697,12 @@ Rails.application.routes.draw do
end
resources :runner_projects, only: [:create, :destroy]
+ resources :badges, only: [], path: 'badges/*ref',
+ constraints: { ref: Gitlab::Regex.git_reference_regex } do
+ collection do
+ get :build, constraints: { format: /svg/ }
+ end
+ end
end
end
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index b937b092789..e5058cebce8 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -10,9 +10,12 @@
# Note: If you change this file in a Merge Request, please also create a
# Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-# WARNING: See config/application.rb under "Relative url support" for the list of
-# other files that need to be changed for relative url support
+
+# Relative URL support
+# WARNING: We recommend using an FQDN to host GitLab in a root path instead
+# of using a relative URL.
+# Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+# Uncomment and customize the following line to run in a non-root path
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb
index 03a12323845..e3ca2b4eea3 100644
--- a/db/fixtures/development/14_builds.rb
+++ b/db/fixtures/development/14_builds.rb
@@ -1,24 +1,13 @@
class Gitlab::Seeder::Builds
- BUILD_STATUSES = %w(running pending success failed canceled)
-
def initialize(project)
@project = project
end
def seed!
ci_commits.each do |ci_commit|
- build = Ci::Build.new(build_attributes_for(ci_commit))
-
- artifacts_cache_file(artifacts_archive_path) do |file|
- build.artifacts_file = file
- end
-
- artifacts_cache_file(artifacts_metadata_path) do |file|
- build.artifacts_metadata = file
- end
-
begin
- build.save!
+ build_create!(ci_commit, name: 'test build 1')
+ build_create!(ci_commit, status: 'success', name: 'test build 2')
print '.'
rescue ActiveRecord::RecordInvalid
print 'F'
@@ -36,6 +25,28 @@ class Gitlab::Seeder::Builds
[]
end
+ def build_create!(ci_commit, opts = {})
+ attributes = build_attributes_for(ci_commit).merge(opts)
+ build = Ci::Build.new(attributes)
+
+ if %w(success failed).include?(build.status)
+ artifacts_cache_file(artifacts_archive_path) do |file|
+ build.artifacts_file = file
+ end
+
+ artifacts_cache_file(artifacts_metadata_path) do |file|
+ build.artifacts_metadata = file
+ end
+ end
+
+ build.save!
+
+ if %w(running success failed).include?(build.status)
+ # We need to set build trace after saving a build (id required)
+ build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
+ end
+ end
+
def build_attributes_for(ci_commit)
{ name: 'test build', commands: "$ build command",
stage: 'test', stage_idx: 1, ref: 'master',
@@ -49,7 +60,7 @@ class Gitlab::Seeder::Builds
end
def build_status
- BUILD_STATUSES.sample
+ Ci::Build::AVAILABLE_STATUSES.sample
end
def artifacts_archive_path
diff --git a/doc/api/builds.md b/doc/api/builds.md
index 6e64d096644..43edb40e911 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -8,20 +8,16 @@ Get a list of builds in a project.
GET /projects/:id/builds
```
-### Parameters
-
-| Attribute | Type | required | Description |
+| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| scope | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
-
-### Example of request
+| `id` | integer | yes | The ID of a project |
+| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds"
```
-### Example of response
+Example of response
```json
[
@@ -112,21 +108,17 @@ Get a list of builds for specific commit in a project.
GET /projects/:id/repository/commits/:sha/builds
```
-### Parameters
-
-| Attribute | Type | required | Description |
+| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| sha | string | yes | The SHA id of a commit |
-| scope | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
-
-### Example of request
+| `id` | integer | yes | The ID of a project |
+| `sha` | string | yes | The SHA id of a commit |
+| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds"
```
-### Example of response
+Example of response
```json
[
@@ -203,20 +195,16 @@ Get a single build of a project
GET /projects/:id/builds/:build_id
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8"
```
-### Example of response
+Example of response
```json
{
@@ -267,20 +255,16 @@ Cancel a single build of a project
POST /projects/:id/builds/:build_id/cancel
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/cancel"
```
-### Example of response
+Example of response
```json
{
@@ -317,20 +301,16 @@ Retry a single build of a project
POST /projects/:id/builds/:build_id/retry
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/retry"
```
-### Example of response
+Example of response
```json
{
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index b4dca6dd35e..009532b50c1 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -259,6 +259,7 @@ Parameters:
- `description` (optional) - Description of MR
- `target_project_id` (optional) - The target project (numeric id)
- `labels` (optional) - Labels for MR as a comma-separated list
+- `milestone_id` (optional) - Milestone ID
```json
{
@@ -328,6 +329,7 @@ Parameters:
- `description` - Description of MR
- `state_event` - New state (close|reopen|merge)
- `labels` (optional) - Labels for MR as a comma-separated list
+- `milestone_id` (optional) - Milestone ID
```json
{
@@ -516,3 +518,65 @@ Parameters:
## Comments on merge requets
Comments are done via the [notes](notes.md) resource.
+
+## List issues that will close on merge
+
+Get all the issues that would be closed by merging the provided merge request.
+
+```
+GET /projects/:id/merge_requests/:merge_request_id/closes_issues
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of a project |
+| `merge_request_id` | integer | yes | The ID of the merge request |
+
+```bash
+curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/76/merge_requests/1/closes_issues
+```
+
+Example response:
+
+```json
+[
+ {
+ "state" : "opened",
+ "description" : "Ratione dolores corrupti mollitia soluta quia.",
+ "author" : {
+ "state" : "active",
+ "id" : 18,
+ "web_url" : "https://gitlab.example.com/u/eileen.lowe",
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "username" : "eileen.lowe"
+ },
+ "milestone" : {
+ "project_id" : 1,
+ "description" : "Ducimus nam enim ex consequatur cumque ratione.",
+ "state" : "closed",
+ "due_date" : null,
+ "iid" : 2,
+ "created_at" : "2016-01-04T15:31:39.996Z",
+ "title" : "v4.0",
+ "id" : 17,
+ "updated_at" : "2016-01-04T15:31:39.996Z"
+ },
+ "project_id" : 1,
+ "assignee" : {
+ "state" : "active",
+ "id" : 1,
+ "name" : "Administrator",
+ "web_url" : "https://gitlab.example.com/u/root",
+ "avatar_url" : null,
+ "username" : "root"
+ },
+ "updated_at" : "2016-01-04T15:31:51.081Z",
+ "id" : 76,
+ "title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
+ "created_at" : "2016-01-04T15:31:51.081Z",
+ "iid" : 6,
+ "labels" : []
+ },
+]
+```
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 63fe840b369..bd748f1b986 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -270,7 +270,7 @@ This will forcefully (`-f`) remove the `build` container, the two service
containers as well as all volumes (`-v`) that were created with the container
creation.
-[Docker Fundamentals]: https://docs.docker.com/engine/introduction/understanding-docker/
+[Docker Fundamentals]: https://docs.docker.com/engine/understanding-docker/
[hub]: https://hub.docker.com/
[linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/
[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index a9b36139de9..ae7b760fa67 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -184,6 +184,14 @@ you expected.
You are also able to view the status of any commit in the various pages in
GitLab, such as **Commits** and **Merge Requests**.
+## Builds badge
+
+You can access a builds badge image using following link:
+
+```
+http://example.gitlab.com/namespace/project/badges/branch/build.svg
+```
+
## Next steps
Awesome! You started using CI in GitLab!
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 862cacda586..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 |
@@ -77,9 +77,12 @@ More information about Docker integration can be found in [Using Docker Images](
GitLab CI allows you to define per-project **Secure Variables** that are set in build environment.
The secure variables are stored out of the repository (the `.gitlab-ci.yml`).
-These variables are securely stored in GitLab CI database and are hidden in the build log.
+The variables are securely passed to GitLab Runner and are available in build environment.
It's desired method to use them for storing passwords, secret keys or whatever you want.
+**The value of the variable can be shown in build log if explicitly asked to do so.**
+If your project is public or internal you can make the builds private.
+
Secure Variables can added by going to `Project > Variables > Add Variable`.
They will be available for all subsequent builds.
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 4fa1961fde9..28dedf3978c 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -8,12 +8,14 @@ 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.
Please don't depend on GitLab specific code since it can change in future versions.
-If needed copy-paste GitLab code into the migration to make make it forward compatible.
+If needed copy-paste GitLab code into the migration to make it forward compatible.
## Comments in the migration
@@ -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 39c9b12ade9..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
@@ -238,9 +233,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-4-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-5-stable gitlab
-**Note:** You can change `8-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -358,7 +353,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
- sudo -u git -H git checkout 0.6.3
+ sudo -u git -H git checkout 0.6.4
sudo -u git -H make
### Initialize Database and Activate Advanced Features
@@ -484,6 +479,11 @@ You can use `sudo service gitlab start` and `sudo service gitlab stop` to start
## Advanced Setup Tips
+### Relative URL support
+
+See the [Relative URL documentation](relative_url.md) for more information on
+how to configure GitLab with a relative URL.
+
### Using HTTPS
To use GitLab with HTTPS:
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/install/relative_url.md b/doc/install/relative_url.md
new file mode 100644
index 00000000000..b341b6311c6
--- /dev/null
+++ b/doc/install/relative_url.md
@@ -0,0 +1,126 @@
+## Install GitLab under a relative URL
+
+_**Note:**
+This document describes how to run GitLab under a relative URL for installations
+from source. If you are using an Omnibus package,
+[the steps are different][omnibus-rel]. Use this guide along with the
+[installation guide](installation.md) if you are installing GitLab for the
+first time._
+
+---
+
+While it is recommended to install GitLab on its own (sub)domain, sometimes
+this is not possible due to a variety of reasons. In that case, GitLab can also
+be installed under a relative URL, for example `https://example.com/gitlab`.
+
+There is no limit to how deeply nested the relative URL can be. For example you
+could serve GitLab under `/foo/bar/gitlab/git` without any issues.
+
+Note that by changing the URL on an existing GitLab installation, all remote
+URLs will change, so you'll have to manually edit them in any local repository
+that points to your GitLab instance.
+
+---
+
+The TL;DR list of configuration files that you need to change in order to
+serve GitLab under a relative URL is:
+
+- `/home/git/gitlab/config/application.rb`
+- `/home/git/gitlab/config/gitlab.yml`
+- `/home/git/gitlab/config/unicorn.rb`
+- `/home/git/gitlab-shell/config.yml`
+- `/etc/default/gitlab`
+
+After all the changes you need to recompile the assets and [restart GitLab].
+
+### Relative URL requirements
+
+If you configure GitLab with a relative URL, the assets (JavaScript, CSS, fonts,
+images, etc.) will need to be recompiled, which is a task which consumes a lot
+of CPU and memory resources. To avoid out-of-memory errors, you should have at
+least 2GB of RAM available on your system, while we recommend 4GB RAM, and 4 or
+8 CPU cores.
+
+See the [requirements](requirements.md) document for more information.
+
+### Enable relative URL in GitLab
+
+_**Note:**
+Do not make any changes to your web server configuration file regarding
+relative URL. The relative URL support is implemented by GitLab Workhorse._
+
+---
+
+Before following the steps below to enable relative URL in GitLab, some
+assumptions are made:
+
+- GitLab is served under `/gitlab`
+- The directory under which GitLab is installed is `/home/git/`
+
+Make sure to follow all steps below:
+
+1. (Optional) If you run short on resources, you can temporarily free up some
+ memory by shutting down the GitLab service with the following command:
+
+ ```shell
+ sudo service gitlab stop
+ ```
+
+1. Edit `/home/git/gitlab/config/application.rb` and uncomment/change the
+ following line:
+
+ ```ruby
+ config.relative_url_root = "/gitlab"
+ ```
+
+1. Edit `/home/git/gitlab/config/gitlab.yml` and uncomment/change the
+ following line:
+
+ ```yaml
+ relative_url_root: /gitlab
+ ```
+
+1. Edit `/home/git/gitlab/config/unicorn.rb` and uncomment/change the
+ following line:
+
+ ```ruby
+ ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+ ```
+
+1. Edit `/home/git/gitlab-shell/config.yml` and append the relative path to
+ the following line:
+
+ ```yaml
+ gitlab_url: http://127.0.0.1/gitlab
+ ```
+
+1. Make sure you have copied the supplied init script and the defaults file
+ as stated in the [installation guide](installation.md#install-init-script).
+ Then, edit `/etc/default/gitlab` and set in `gitlab_workhorse_options` the
+ `-authBackend` setting to read like:
+
+ ```shell
+ -authBackend http://127.0.0.1:8080/gitlab
+ ```
+
+ **Note:**
+ If you are using a custom init script, make sure to edit the above
+ gitlab-workhorse setting as needed.
+
+1. After all the above changes recompile the assets. This is an important task
+ and will take some time to complete depending on the server resources:
+
+ ```
+ cd /home/git/gitlab
+ sudo -u git -H bundle exec rake assets:clean assets:precompile RAILS_ENV=production
+ ```
+
+1. [Restart GitLab][] for the changes to take effect.
+
+### Disable relative URL in GitLab
+
+To disable the relative URL, follow the same steps as above and set up the
+GitLab URL to one that doesn't contain a relative path.
+
+[omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab"
+[restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
diff --git a/doc/markdown/img/logo.png b/doc/markdown/img/logo.png
new file mode 100644
index 00000000000..7da5f23ed9b
--- /dev/null
+++ b/doc/markdown/img/logo.png
Binary files differ
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index f6bffba9fdb..c400cdac64d 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -424,24 +424,24 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
Here's our logo (hover to see the title text):
Inline-style:
- ![alt text](assets/logo.svg)
+ ![alt text](img/logo.png)
Reference-style:
![alt text1][logo]
- [logo]: assets/logo.svg
+ [logo]: img/logo.png
Here's our logo:
Inline-style:
-![alt text](/assets/logo.svg)
+![alt text](img/logo.png)
Reference-style:
![alt text][logo]
-[logo]: /assets/logo.svg
+[logo]: img/logo.png
## Blockquotes
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/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md
new file mode 100644
index 00000000000..42b26439848
--- /dev/null
+++ b/doc/update/8.4-to-8.5.md
@@ -0,0 +1,116 @@
+# From 8.4 to 8.5
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-5-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-5-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v2.6.10
+```
+
+### 5. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout 0.6.4
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+```
+
+### 7. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+git diff origin/8-4-stable:config/gitlab.yml.example origin/8-5-stable:config/gitlab.yml.example
+```
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.4)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.3 to 8.4](8.3-to-8.4.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c29037e89c2..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.
@@ -8,8 +15,8 @@ Web hooks can be used to update an external issue tracker, trigger CI builds, up
## SSL Verification
-By default, the SSL certificate of the webhook endpoint is verified based on
-an internal list of Certificate Authorities,
+By default, the SSL certificate of the webhook endpoint is verified based on
+an internal list of Certificate Authorities,
which means the certificate cannot be self-signed.
You can turn this off in the web hook settings in your GitLab projects.
@@ -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": "",
@@ -76,7 +100,6 @@ X-Gitlab-Event: Push Hook
}
],
"total_commits_count": 4
-
}
```
@@ -101,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": "",
@@ -136,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.",
@@ -158,6 +214,11 @@ X-Gitlab-Event: Issue Hook
"iid": 23,
"url": "http://example.com/diaspora/issues/23",
"action": "open"
+ },
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
```
@@ -193,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"
},
@@ -256,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"
},
@@ -296,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",
@@ -322,7 +431,12 @@ X-Gitlab-Event: Note Hook
"email": "john@example.com"
}
},
- "work_in_progress": false
+ "work_in_progress": false,
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
+ }
}
}
```
@@ -346,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,
@@ -388,7 +518,6 @@ X-Gitlab-Event: Note Hook
### Comment on code snippet
-
**Request header**:
```
@@ -397,7 +526,7 @@ X-Gitlab-Event: Note Hook
**Request body:**
-```
+```json
{
"object_kind": "note",
"user": {
@@ -406,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,
@@ -482,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",
@@ -510,7 +671,12 @@ X-Gitlab-Event: Merge Request Hook
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
- "action": "open"
+ "action": "open",
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
+ }
}
}
```
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/badges/build.feature b/features/project/badges/build.feature
new file mode 100644
index 00000000000..9417f62d680
--- /dev/null
+++ b/features/project/badges/build.feature
@@ -0,0 +1,22 @@
+Feature: Project Badges Build
+ Background:
+ Given I sign in as a user
+ And I own a project
+ And project has CI enabled
+ And project has a recent build
+
+ Scenario: I want to see a badge for successfully built project
+ Given recent build is successful
+ When I display builds badge for a master branch
+ Then I should see a build success badge
+
+ Scenario: I want to see a badge for project with failed builds
+ Given recent build failed
+ When I display builds badge for a master branch
+ Then I should see a build failed badge
+
+ Scenario: I want to see a badge for project with running builds
+ Given recent build is successful
+ And project has another build that is running
+ When I display builds badge for a master branch
+ Then I should see a build running badge
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index 01c10721312..a95df038357 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -7,6 +7,26 @@ Feature: Project Commits
Scenario: I browse commits list for master branch
Then I see project commits
+ And I should not see button to create a new merge request
+ Then I click the "Compare" tab
+ And I should not see button to create a new merge request
+
+ Scenario: I browse commits list for feature branch without a merge request
+ Given I visit commits list page for feature branch
+ Then I see feature branch commits
+ And I see button to create a new merge request
+ Then I click the "Compare" tab
+ And I see button to create a new merge request
+
+ Scenario: I browse commits list for feature branch with an open merge request
+ Given project have an open merge request
+ And I visit commits list page for feature branch
+ Then I see feature branch commits
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
+ Then I click the "Compare" tab
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
Scenario: I browse atom feed of commits list for master branch
Given I click atom feed link
@@ -31,6 +51,22 @@ Feature: Project Commits
Then I see inline diff button
@javascript
+ Scenario: I compare branches without a merge request
+ Given I visit compare refs page
+ And I fill compare fields with branches
+ Then I see compared branches
+ And I see button to create a new merge request
+
+ @javascript
+ Scenario: I compare branches with an open merge request
+ Given project have an open merge request
+ And I visit compare refs page
+ And I fill compare fields with branches
+ Then I see compared branches
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
+
+ @javascript
Scenario: I compare refs
Given I visit compare refs page
And I fill compare fields with refs
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/badges/build.rb b/features/steps/project/badges/build.rb
new file mode 100644
index 00000000000..cbfc35bed65
--- /dev/null
+++ b/features/steps/project/badges/build.rb
@@ -0,0 +1,28 @@
+class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedBuilds
+ include RepoHelpers
+
+ step 'I display builds badge for a master branch' do
+ visit build_namespace_project_badges_path(@project.namespace, @project, ref: :master, format: :svg)
+ end
+
+ step 'I should see a build success badge' do
+ expect_badge('success')
+ end
+
+ step 'I should see a build failed badge' do
+ expect_badge('failed')
+ end
+
+ step 'I should see a build running badge' do
+ expect_badge('running')
+ end
+
+ def expect_badge(status)
+ svg = Nokogiri::XML.parse(page.body)
+ expect(page.response_headers).to include('Content-Type' => 'image/svg+xml')
+ expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
+ end
+end
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index daf6cdaaac8..f9fd7332464 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -33,6 +33,13 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "Showing #{sample_commit.files_changed_count} changed files"
end
+ step 'I fill compare fields with branches' do
+ fill_in 'from', with: 'feature'
+ fill_in 'to', with: 'master'
+
+ click_button 'Compare'
+ end
+
step 'I fill compare fields with refs' do
fill_in "from", with: sample_commit.parent_id
fill_in "to", with: sample_commit.id
@@ -56,6 +63,56 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "Showing 2 changed files"
end
+ step 'I visit commits list page for feature branch' do
+ visit namespace_project_commits_path(@project.namespace, @project, 'feature', { limit: 5 })
+ end
+
+ step 'I see feature branch commits' do
+ commit = @project.repository.commit('0b4bc9a')
+ expect(page).to have_content(@project.name)
+ expect(page).to have_content(commit.message[0..12])
+ expect(page).to have_content(commit.short_id)
+ end
+
+ step 'project have an open merge request' do
+ create(:merge_request,
+ title: 'Feature',
+ source_project: @project,
+ source_branch: 'feature',
+ target_branch: 'master',
+ author: @project.users.first
+ )
+ end
+
+ step 'I click the "Compare" tab' do
+ click_link('Compare')
+ end
+
+ step 'I fill compare fields with branches' do
+ fill_in 'from', with: 'master'
+ fill_in 'to', with: 'feature'
+
+ click_button 'Compare'
+ end
+
+ step 'I see compared branches' do
+ expect(page).to have_content 'Commits (1)'
+ expect(page).to have_content 'Showing 1 changed file with 5 additions and 0 deletions'
+ end
+
+ step 'I see button to create a new merge request' do
+ expect(page).to have_link 'Create Merge Request'
+ end
+
+ step 'I should not see button to create a new merge request' do
+ expect(page).to_not have_link 'Create Merge Request'
+ end
+
+ step 'I should see button to the merge request' do
+ merge_request = MergeRequest.find_by(title: 'Feature')
+ expect(page).to have_link "View Open Merge Request", href: namespace_project_merge_request_path(@project.namespace, @project, merge_request)
+ end
+
step 'I see breadcrumb links' do
expect(page).to have_selector('ul.breadcrumb')
expect(page).to have_selector('ul.breadcrumb a', count: 4)
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/builds.rb b/features/steps/shared/builds.rb
index 726e2e814ad..fa54c93df0f 100644
--- a/features/steps/shared/builds.rb
+++ b/features/steps/shared/builds.rb
@@ -6,8 +6,20 @@ module SharedBuilds
end
step 'project has a recent build' do
- ci_commit = create :ci_commit, project: @project, sha: sample_commit.id
- @build = create :ci_build, commit: ci_commit
+ @ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha)
+ @build = create(:ci_build, commit: @ci_commit)
+ end
+
+ step 'recent build is successful' do
+ @build.update_column(:status, 'success')
+ end
+
+ step 'recent build failed' do
+ @build.update_column(:status, 'failed')
+ end
+
+ step 'project has another build that is running' do
+ create(:ci_build, commit: @ci_commit, name: 'second build', status: 'running')
end
step 'I visit recent build details page' do
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/api/merge_requests.rb b/lib/api/merge_requests.rb
index dd7f24f3279..c5e5d57ed4d 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -71,6 +71,7 @@ module API
# title (required) - Title of MR
# description - Description of MR
# labels (optional) - Labels for MR as a comma-separated list
+ # milestone_id (optional) - Milestone ID
#
# Example:
# POST /projects/:id/merge_requests
@@ -78,7 +79,7 @@ module API
post ":id/merge_requests" do
authorize! :create_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title]
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
+ attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description, :milestone_id]
# Validate label names in advance
if (errors = validate_label_params(params)).any?
@@ -163,11 +164,12 @@ module API
# state_event - Status of MR. (close|reopen|merge)
# description - Description of MR
# labels (optional) - Labels for a MR as a comma-separated list
+ # milestone_id (optional) - Milestone ID
# Example:
# PUT /projects/:id/merge_requests/:merge_request_id
#
put path do
- attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
+ attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description, :milestone_id]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :update_merge_request, merge_request
@@ -300,6 +302,19 @@ module API
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
+
+ # List issues that will close on merge
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # Examples:
+ # GET /projects/:id/merge_requests/:merge_request_id/closes_issues
+ get "#{path}/closes_issues" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
+ present paginate(issues), with: Entities::Issue
+ end
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/blame.rb b/lib/gitlab/blame.rb
index 313e6b9fc03..997a22779a0 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -40,6 +40,7 @@ module Gitlab
end
def highlighted_lines
+ @blob.load_all_data!(repository)
@highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines
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/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 4e6e56ac2db..cc8617b72ca 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -34,11 +34,16 @@ sidekiq_pid_path="$pid_path/sidekiq.pid"
# /home/git/gitlab-workhorse .
gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd)
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
+
# The -listenXxx settings determine where gitlab-workhorse
-# listens for connections from NGINX. To listen on localhost:8181, write
-# '-listenNetwork tcp -listenAddr localhost:8181'.
-# The -authBackend setting tells gitlab-workhorse where it can reach
-# Unicorn.
+# listens for connections from the web server. By default it listens to a
+# socket. To listen on TCP connections (needed by Apache) change to:
+# '-listenNetwork tcp -listenAddr 127.0.0.1:8181'
+#
+# The -authBackend setting tells gitlab-workhorse where it can reach Unicorn.
+# For relative URL support change to:
+# '-authBackend http://127.0.0.1/8080/gitlab'
+# Read more in http://doc.gitlab.com/ce/install/relative_url.html
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 2dc2953e328..54d95cd62a5 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -90,24 +90,6 @@ namespace :gitlab do
end
end
- def check_database_is_not_sqlite
- print "Database is SQLite ... "
-
- database_config_file = Rails.root.join("config", "database.yml")
-
- unless File.read(database_config_file) =~ /adapter:\s+sqlite/
- puts "no".green
- else
- puts "yes".red
- puts "Please fix this by removing the SQLite entry from the database.yml".blue
- for_more_information(
- "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
- see_database_guide
- )
- fix_and_rerun
- end
- end
-
def check_gitlab_config_exists
print "GitLab config exists? ... "
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index 5987988dc8e..b6f076a90c3 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -1,15 +1,15 @@
#!/bin/bash
if [ -f /.dockerinit ]; then
- # Docker runners use `/cache` folder which is persisted every build
- if [ ! -e /cache/phantomjs_1.9.8-0jessie_amd64.deb ]; then
+ mkdir -p vendor
+ if [ ! -e vendor/phantomjs_1.9.8-0jessie_amd64.deb ]; then
wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
- mv phantomjs_1.9.8-0jessie_amd64.deb /cache
+ mv phantomjs_1.9.8-0jessie_amd64.deb vendor/
fi
- dpkg -i /cache/phantomjs_1.9.8-0jessie_amd64.deb
+ dpkg -i vendor/phantomjs_1.9.8-0jessie_amd64.deb
apt-get update -qq
- apt-get -o dir::cache::archives="/cache/apt" install -y -qq --force-yes \
+ apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \
libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip
cp config/database.yml.mysql config/database.yml
@@ -20,7 +20,7 @@ if [ -f /.dockerinit ]; then
cp config/resque.yml.example config/resque.yml
sed -i 's/localhost/redis/g' config/resque.yml
- export FLAGS=(--path /cache)
+ export FLAGS=(--path vendor)
else
export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
cp config/database.yml.mysql config/database.yml
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/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 161a32c51e6..b1764d7ac09 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -74,6 +74,10 @@ describe ApplicationSetting, models: true do
.only_integer
.is_greater_than(0)
end
+
+ it_behaves_like 'an object with email-formated attributes', :admin_notification_email do
+ subject { setting }
+ end
end
context 'restricted signup domains' do
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 021d62cdf0c..600089802b2 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -69,17 +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(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(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/email_spec.rb b/spec/models/email_spec.rb
new file mode 100644
index 00000000000..a20a6149649
--- /dev/null
+++ b/spec/models/email_spec.rb
@@ -0,0 +1,22 @@
+# == Schema Information
+#
+# Table name: emails
+#
+# id :integer not null, primary key
+# user_id :integer not null
+# email :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+#
+
+require 'spec_helper'
+
+describe Email, models: true do
+
+ describe 'validations' do
+ it_behaves_like 'an object with email-formated attributes', :email do
+ subject { build(:email) }
+ end
+ end
+
+end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 2aedca20df2..2d8f1cc1ad3 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -31,6 +31,10 @@ describe Member, models: true do
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
+ it_behaves_like 'an object with email-formated attributes', :invite_email do
+ subject { build(:project_member) }
+ end
+
context "when an invite email is provided" do
let(:member) { build(:project_member, invite_email: "user@example.com", user: nil) }
@@ -159,7 +163,7 @@ describe Member, models: true do
describe "#generate_invite_token" do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
-
+
it "sets the invite token" do
expect { member.generate_invite_token }.to change { member.invite_token}
end
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 72b4ac6d660..2cd0606a61d 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -354,4 +354,21 @@ describe Repository, models: true do
repository.expire_branch_cache('foo')
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 } }
+
+ it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cee051f5732..47ce409fe4b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -119,37 +119,15 @@ describe User, models: true do
it { is_expected.to validate_length_of(:bio).is_within(0..255) }
- describe 'email' do
- it 'accepts info@example.com' do
- user = build(:user, email: 'info@example.com')
- expect(user).to be_valid
- end
-
- it 'accepts info+test@example.com' do
- user = build(:user, email: 'info+test@example.com')
- expect(user).to be_valid
- end
-
- it "accepts o'reilly@example.com" do
- user = build(:user, email: "o'reilly@example.com")
- expect(user).to be_valid
- end
-
- it 'rejects test@test@example.com' do
- user = build(:user, email: 'test@test@example.com')
- expect(user).to be_invalid
- end
-
- it 'rejects mailto:test@example.com' do
- user = build(:user, email: 'mailto:test@example.com')
- expect(user).to be_invalid
- end
+ it_behaves_like 'an object with email-formated attributes', :email do
+ subject { build(:user) }
+ end
- it "rejects lol!'+=?><#$%^&*()@gmail.com" do
- user = build(:user, email: "lol!'+=?><#$%^&*()@gmail.com")
- expect(user).to be_invalid
- end
+ it_behaves_like 'an object with email-formated attributes', :public_email, :notification_email do
+ subject { build(:user).tap { |user| user.emails << build(:email, email: email_value) } }
+ end
+ describe 'email' do
context 'when no signup domains listed' do
before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return([]) }
it 'accepts any email' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a91a8696831..4fd1df25568 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -10,6 +10,7 @@ describe API::API, api: true do
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
+ let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do
project.team << [user, :reporters]
@@ -170,10 +171,12 @@ describe API::API, api: true do
source_branch: 'feature_conflict',
target_branch: 'master',
author: user,
- labels: 'label, label2'
+ labels: 'label, label2',
+ milestone_id: milestone.id
expect(response.status).to eq(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['labels']).to eq(['label', 'label2'])
+ expect(json_response['milestone']['id']).to eq(milestone.id)
end
it "should return 422 when source_branch equals target_branch" do
@@ -374,18 +377,24 @@ describe API::API, api: true do
end
describe "PUT /projects/:id/merge_requests/:merge_request_id" do
- it "should return merge_request" do
+ it "updates title and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
expect(response.status).to eq(200)
expect(json_response['title']).to eq('New title')
end
- it "should return merge_request" do
+ it "updates description and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description"
expect(response.status).to eq(200)
expect(json_response['description']).to eq('New description')
end
+ it "updates milestone_id and returns merge_request" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), milestone_id: milestone.id
+ expect(response.status).to eq(200)
+ expect(json_response['milestone']['id']).to eq(milestone.id)
+ end
+
it "should return 400 when source_branch is specified" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
@@ -449,6 +458,28 @@ describe API::API, api: true do
end
end
+ describe 'GET :id/merge_requests/:merge_request_id/closes_issues' do
+ it 'returns the issue that will be closed on merge' do
+ issue = create(:issue, project: project)
+ mr = merge_request.tap do |mr|
+ mr.update_attribute(:description, "Closes #{issue.to_reference(mr.project)}")
+ end
+
+ get api("/projects/#{project.id}/merge_requests/#{mr.id}/closes_issues", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(issue.id)
+ end
+
+ it 'returns an empty array when there are no issues to be closed' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+ end
+
def mr_with_later_created_and_updated_at_time
merge_request
merge_request.created_at += 1.hour
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/email_format_shared_examples.rb b/spec/support/email_format_shared_examples.rb
new file mode 100644
index 00000000000..b924a208e71
--- /dev/null
+++ b/spec/support/email_format_shared_examples.rb
@@ -0,0 +1,44 @@
+# Specifications for behavior common to all objects with an email attribute.
+# Takes a list of email-format attributes and requires:
+# - subject { "the object with a attribute= setter" }
+# Note: You have access to `email_value` which is the email address value
+# being currently tested).
+
+shared_examples 'an object with email-formated attributes' do |*attributes|
+ attributes.each do |attribute|
+ describe "specifically its :#{attribute} attribute" do
+ %w[
+ info@example.com
+ info+test@example.com
+ o'reilly@example.com
+ mailto:test@example.com
+ lol!'+=?><#$%^&*()@gmail.com
+ ].each do |valid_email|
+ context "with a value of '#{valid_email}'" do
+ let(:email_value) { valid_email }
+
+ it 'is valid' do
+ subject.send("#{attribute}=", valid_email)
+
+ expect(subject).to be_valid
+ end
+ end
+ end
+
+ %w[
+ foobar
+ test@test@example.com
+ ].each do |invalid_email|
+ context "with a value of '#{invalid_email}'" do
+ let(:email_value) { invalid_email }
+
+ it 'is invalid' do
+ subject.send("#{attribute}=", invalid_email)
+
+ expect(subject).to be_invalid
+ end
+ end
+ end
+ end
+ end
+end
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