summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-01-20 14:53:41 +0100
committerDouwe Maan <douwe@gitlab.com>2016-01-20 14:53:41 +0100
commitc23ca0b0b5640d9cbf3fbfd446916a228b581b2f (patch)
tree1aa14035e6484f0f1c16e71746391d6e6a0277b1
parent701513dcc7afb403372bc738642a9a52e9be5983 (diff)
parent481644ca7c9f763d4646ad557cc3bcf8f4f71816 (diff)
downloadgitlab-ce-c23ca0b0b5640d9cbf3fbfd446916a228b581b2f.tar.gz
Merge branch 'master' into issue_3945
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--CHANGELOG26
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock10
-rw-r--r--Procfile4
-rw-r--r--VERSION2
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Black.ttf.woff2bin0 -> 82052 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-BlackIt.ttf.woff2bin0 -> 34812 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Bold.ttf.woff2bin0 -> 85604 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-BoldIt.ttf.woff2bin0 -> 35864 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2bin0 -> 82808 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2bin0 -> 34560 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-It.ttf.woff2bin0 -> 36016 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Light.ttf.woff2bin0 -> 86336 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-LightIt.ttf.woff2bin0 -> 35952 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Regular.ttf.woff2bin0 -> 86844 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Semibold.ttf.woff2bin0 -> 86196 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2bin0 -> 35984 bytes
-rw-r--r--app/assets/javascripts/awards_handler.coffee15
-rw-r--r--app/assets/javascripts/issue.js.coffee16
-rw-r--r--app/assets/javascripts/merge_request.js.coffee16
-rw-r--r--app/assets/javascripts/notes.js.coffee10
-rw-r--r--app/assets/javascripts/star.js.coffee4
-rw-r--r--app/assets/stylesheets/framework/common.scss2
-rw-r--r--app/assets/stylesheets/framework/filters.scss2
-rw-r--r--app/assets/stylesheets/framework/fonts.scss24
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/branches.scss3
-rw-r--r--app/assets/stylesheets/pages/commit.scss4
-rw-r--r--app/assets/stylesheets/pages/commits.scss4
-rw-r--r--app/assets/stylesheets/pages/diff.scss1
-rw-r--r--app/assets/stylesheets/pages/groups.scss5
-rw-r--r--app/assets/stylesheets/pages/issuable.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss12
-rw-r--r--app/assets/stylesheets/pages/search.scss4
-rw-r--r--app/assets/stylesheets/pages/tags.scss3
-rw-r--r--app/controllers/abuse_reports_controller.rb1
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/projects/artifacts_controller.rb2
-rw-r--r--app/controllers/projects/builds_controller.rb4
-rw-r--r--app/controllers/projects/commit_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/snippets_controller.rb2
-rw-r--r--app/finders/issuable_finder.rb3
-rw-r--r--app/mailers/emails/builds.rb13
-rw-r--r--app/mailers/emails/projects.rb6
-rw-r--r--app/mailers/notify.rb15
-rw-r--r--app/models/ability.rb2
-rw-r--r--app/models/abuse_report.rb2
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/models/hooks/project_hook.rb4
-rw-r--r--app/models/hooks/service_hook.rb4
-rw-r--r--app/models/hooks/system_hook.rb4
-rw-r--r--app/models/hooks/web_hook.rb4
-rw-r--r--app/models/project.rb11
-rw-r--r--app/models/project_services/ci_service.rb8
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb4
-rw-r--r--app/models/project_services/issue_tracker_service.rb6
-rw-r--r--app/models/service.rb11
-rw-r--r--app/models/user.rb5
-rw-r--r--app/services/notification_service.rb21
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/uploaders/artifact_uploader.rb4
-rw-r--r--app/views/abuse_reports/new.html.haml2
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--app/views/projects/artifacts/_tree_directory.html.haml6
-rw-r--r--app/views/projects/artifacts/_tree_file.html.haml10
-rw-r--r--app/views/projects/artifacts/browse.html.haml30
-rw-r--r--app/views/projects/branches/_branch.html.haml4
-rw-r--r--app/views/projects/builds/index.html.haml7
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/tags/_tag.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml4
-rw-r--r--app/views/shared/groups/_group.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml3
-rw-r--r--app/views/sherlock/transactions/_queries.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rwxr-xr-xbin/background_jobs9
-rwxr-xr-xbin/web11
-rw-r--r--config/environments/development.rb5
-rw-r--r--config/initializers/1_settings.rb22
-rw-r--r--config/initializers/metrics.rb6
-rw-r--r--db/migrate/20151201203948_raise_hook_url_limit.rb5
-rw-r--r--db/migrate/20160119111158_add_services_category.rb39
-rw-r--r--db/migrate/20160119112418_add_services_default.rb20
-rw-r--r--db/migrate/20160119145451_add_ldap_email_to_users.rb30
-rw-r--r--db/migrate/limits_to_mysql.rb1
-rw-r--r--db/schema.rb49
-rw-r--r--doc/api/README.md48
-rw-r--r--doc/api/groups.md1
-rw-r--r--doc/api/projects.md4
-rw-r--r--doc/ci/api/builds.md62
-rw-r--r--doc/install/database_mysql.md26
-rw-r--r--doc/integration/saml.md16
-rw-r--r--doc/update/8.3-to-8.4.md2
-rw-r--r--doc/update/README.md1
-rw-r--r--doc/workflow/importing/github_importer/importer.pngbin39335 -> 0 bytes
-rw-r--r--doc/workflow/importing/github_importer/new_project_page.pngbin46276 -> 0 bytes
-rw-r--r--doc/workflow/importing/img/import_projects_from_github_importer.pngbin0 -> 28033 bytes
-rw-r--r--doc/workflow/importing/img/import_projects_from_github_new_project_page.pngbin0 -> 17225 bytes
-rw-r--r--doc/workflow/importing/import_projects_from_github.md46
-rw-r--r--doc/workflow/protected_branches.md2
-rw-r--r--features/project/builds/artifacts.feature (renamed from features/project/builds.feature)25
-rw-r--r--features/project/builds/permissions.feature18
-rw-r--r--features/project/builds/summary.feature15
-rw-r--r--features/steps/project/builds/artifacts.rb (renamed from features/steps/project/builds.rb)19
-rw-r--r--features/steps/project/builds/permissions.rb7
-rw-r--r--features/steps/project/builds/summary.rb21
-rw-r--r--features/steps/shared/builds.rb19
-rw-r--r--features/steps/shared/project.rb18
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/ci/api/builds.rb4
-rw-r--r--lib/ci/api/entities.rb17
-rw-r--r--lib/gitlab/current_settings.rb1
-rw-r--r--lib/gitlab/email/message/repository_push.rb1
-rw-r--r--lib/gitlab/ldap/user.rb29
-rw-r--r--lib/gitlab/o_auth/auth_hash.rb8
-rw-r--r--lib/gitlab/o_auth/user.rb19
-rw-r--r--lib/tasks/gitlab/task_helpers.rake3
-rw-r--r--spec/fixtures/ci_build_artifacts_metadata.gzbin415 -> 461 bytes
-rw-r--r--spec/initializers/settings_spec.rb44
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb28
-rw-r--r--spec/mailers/notify_spec.rb57
-rw-r--r--spec/models/abuse_report_spec.rb2
-rw-r--r--spec/models/build_spec.rb24
-rw-r--r--spec/requests/api/projects_spec.rb23
-rw-r--r--spec/requests/ci/api/builds_spec.rb12
-rw-r--r--spec/services/notification_service_spec.rb58
-rw-r--r--spec/services/projects/create_service_spec.rb1
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml8
132 files changed, 1031 insertions, 275 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c23a7a3bf0e..ac8390074f4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,7 +8,7 @@ before_script:
- touch log/application.log
- touch log/test.log
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
- - bundle exec rake db:reset db:create RAILS_ENV=test
+ - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate
spec:feature:
script:
@@ -118,7 +118,7 @@ flay:
- mysql
bundler:audit:
- script:
+ script:
- "bundle exec bundle-audit update"
- "bundle exec bundle-audit check"
tags:
diff --git a/CHANGELOG b/CHANGELOG
index 7bbc0fc79f0..2e0eee52a59 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,15 +1,18 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
- - Remove gray background from layout in UI
+ - Add "visibility" flag to GET /projects api endpoint
v 8.4.0 (unreleased)
+ - Allow LDAP users to change their email if it was not set by the LDAP server
+ - Ensure Gravatar host looks like an actual host
+ - Consider re-assign as a mention from a notification point of view
- Add pagination headers to already paginated API resources
- Properly generate diff of orphan commits, like the first commit in a repository
- Improve the consistency of commit titles, branch names, tag names, issue/MR titles, on their respective project pages
- - Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse)
- - Improved performance of finding issues for an entire group (Yorick Peterse)
- - Added custom application performance measuring system powered by InfluxDB (Yorick Peterse)
+ - Autocomplete data is now always loaded, instead of when focusing a comment text area
+ - Improved performance of finding issues for an entire group
+ - Added custom application performance measuring system powered by InfluxDB
- Bump fog to 1.36.0 (Stan Hu)
- Add user's last used IP addresses to admin page (Stan Hu)
- Add housekeeping function to project settings page
@@ -19,8 +22,11 @@ v 8.4.0 (unreleased)
- Fix missing date of month in network graph when commits span a month (Stan Hu)
- Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
- Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
+ - Remove gray background from layout in UI
+ - Fix signup for OAuth providers that don't provide a name
- Implement new UI for group page
- Implement search inside emoji picker
+ - Let the CI runner know about builds that this build depends on
- Add API support for looking up a user by username (Stan Hu)
- Add project permissions to all project API endpoints (Stan Hu)
- Link to milestone in "Milestone changed" system note
@@ -56,10 +62,18 @@ v 8.4.0 (unreleased)
- Allow broadcast messages to be edited
- Autosize Markdown textareas
- Import GitHub wiki into GitLab
+ - Add reporters ability to download and browse build artifacts (Andrew Johnson)
+ - Autofill referring url in message box when reporting user abuse.
+ - Remove leading comma on award emoji when the user is the first to award the emoji (Zeger-Jan van de Weg)
+ - Add build artifacts browser
+ - Improve UX in builds artifacts browser
+ - Increase default size of `data` column in `events` table when using MySQL
+ - Expose button to CI Lint tool on project builds page
+ - Fix: Creator should be added as a master of the project on creation
+ - Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
v 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug)
- - Add build artifacts browser
v 8.3.3
- Preserve CE behavior with JIRA integration by only calling API if URL is set
@@ -107,6 +121,7 @@ v 8.3.0
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
- Fix 500 error when update group member permission
+ - Fix: As an admin, cannot add oneself as a member to a group/project
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- Recognize issue/MR/snippet/commit links as references
- Backport JIRA features from EE to CE
@@ -168,7 +183,6 @@ v 8.2.2
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
- - Fix: As an admin, cannot add oneself as a member to a group/project
- Fix invalid links within projects dashboard header
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- Fix: duplicate email notifications on issue comments
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 7d8568351b4..a918a2aa18d 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.5.4
+0.6.0
diff --git a/Gemfile b/Gemfile
index a9a8bed1064..072f7a9fcc8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,7 +18,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries
-gem 'devise', '~> 3.5.3'
+gem 'devise', '~> 3.5.4'
gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index f1bba7f437e..a14fdbeed23 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -157,7 +157,7 @@ GEM
activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
- devise (3.5.3)
+ devise (3.5.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -614,7 +614,7 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
raindrops (0.15.0)
- rake (10.4.2)
+ rake (10.5.0)
raphael-rails (2.1.2)
rb-fsevent (0.9.6)
rb-inotify (0.9.5)
@@ -648,8 +648,8 @@ GEM
request_store (1.2.1)
rerun (0.11.0)
listen (~> 3.0)
- responders (2.1.0)
- railties (>= 4.2.0, < 5)
+ responders (2.1.1)
+ railties (>= 4.2.0, < 5.1)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
@@ -911,7 +911,7 @@ DEPENDENCIES
d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0)
- devise (~> 3.5.3)
+ devise (~> 3.5.4)
devise-async (~> 0.9.0)
devise-two-factor (~> 2.0.0)
diffy (~> 3.0.3)
diff --git a/Procfile b/Procfile
index 9cfdee7040f..cad738d4292 100644
--- a/Procfile
+++ b/Procfile
@@ -2,6 +2,6 @@
# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in
# lib/support/init.d, which call scripts in bin/ .
#
-web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
-worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
+web: RAILS_ENV=development bin/web start_foreground
+worker: RAILS_ENV=development bin/background_jobs start_foreground
# mail_room: bundle exec mail_room -q -c config/mail_room.yml
diff --git a/VERSION b/VERSION
index ce669730119..ffec98087cb 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.4.0.pre
+8.5.0-pre
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf.woff2 b/app/assets/fonts/SourceSansPro-Black.ttf.woff2
new file mode 100755
index 00000000000..c90d078406c
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Black.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2
new file mode 100755
index 00000000000..b87e22c41b5
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf.woff2 b/app/assets/fonts/SourceSansPro-Bold.ttf.woff2
new file mode 100755
index 00000000000..0f46f3e833a
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Bold.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2
new file mode 100755
index 00000000000..8007df6df32
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2 b/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2
new file mode 100755
index 00000000000..b715f274082
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2
new file mode 100755
index 00000000000..d8f9d29d4aa
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-It.ttf.woff2 b/app/assets/fonts/SourceSansPro-It.ttf.woff2
new file mode 100755
index 00000000000..a00852641f8
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-It.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Light.ttf.woff2 b/app/assets/fonts/SourceSansPro-Light.ttf.woff2
new file mode 100755
index 00000000000..d8b610ad76e
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Light.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2
new file mode 100755
index 00000000000..e0eebac8273
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf.woff2 b/app/assets/fonts/SourceSansPro-Regular.ttf.woff2
new file mode 100755
index 00000000000..0dd3464c74b
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Regular.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2 b/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2
new file mode 100755
index 00000000000..2526d2e1b60
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2
new file mode 100755
index 00000000000..606935af089
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2
Binary files differ
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 619abb1fb07..9d5ae6c04e9 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -5,7 +5,7 @@ class @AwardsHandler
event.preventDefault()
$(".emoji-menu").show()
- $("html").click ->
+ $("html").on 'click', (event) ->
if !$(event.target).closest(".emoji-menu").length
if $(".emoji-menu").is(":visible")
$(".emoji-menu").hide()
@@ -19,7 +19,7 @@ class @AwardsHandler
@addAwardToEmojiBar(emoji)
$(".emoji-menu").hide()
-
+
addAwardToEmojiBar: (emoji) ->
@addEmojiToFrequentlyUsedList(emoji)
@@ -66,9 +66,14 @@ class @AwardsHandler
addMeToAuthorList: (emoji) ->
award_block = @findEmojiIcon(emoji).parent()
- authors = award_block.attr("data-original-title").split(", ")
+ authors = _.compact(award_block.attr("data-original-title").split(", "))
authors.push("me")
- award_block.attr("title", authors.join(", "))
+
+ if authors.length == 1
+ award_block.attr("title", "me")
+ else
+ award_block.attr("title", authors.join(", "))
+
@resetTooltip(award_block)
resetTooltip: (award) ->
@@ -78,7 +83,7 @@ class @AwardsHandler
setTimeout (->
award.tooltip()
), 200
-
+
createEmoji: (emoji) ->
emojiCssClass = @resolveNameToCssClass(emoji)
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 0d26c58a81d..cbc70cd846c 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -6,11 +6,25 @@ class @Issue
constructor: ->
# Prevent duplicate event bindings
@disableTaskList()
-
+ @fixAffixScroll()
if $('a.btn-close').length
@initTaskList()
@initIssueBtnEventListeners()
+ fixAffixScroll: ->
+ fixAffix = ->
+ $discussion = $('.issuable-discussion')
+ $sidebar = $('.issuable-sidebar')
+ if $sidebar.hasClass('no-affix')
+ $sidebar.removeClass(['affix-top','affix'])
+ discussionHeight = $discussion.height()
+ sidebarHeight = $sidebar.height()
+ if sidebarHeight > discussionHeight
+ $discussion.height(sidebarHeight + 50)
+ $sidebar.addClass('no-affix')
+ $(window).on('resize', fixAffix)
+ fixAffix()
+
initTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index 1f46e331427..6af5a48a0bb 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -15,6 +15,8 @@ class @MergeRequest
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
+ @fixAffixScroll();
+
@initTabs()
# Prevent duplicate event bindings
@@ -28,6 +30,20 @@ class @MergeRequest
$: (selector) ->
this.$el.find(selector)
+ fixAffixScroll: ->
+ fixAffix = ->
+ $discussion = $('.issuable-discussion')
+ $sidebar = $('.issuable-sidebar')
+ if $sidebar.hasClass('no-affix')
+ $sidebar.removeClass(['affix-top','affix'])
+ discussionHeight = $discussion.height()
+ sidebarHeight = $sidebar.height()
+ if sidebarHeight > discussionHeight
+ $discussion.height(sidebarHeight + 50)
+ $sidebar.addClass('no-affix')
+ $(window).on('resize', fixAffix)
+ fixAffix()
+
initTabs: ->
if @opts.action != 'new'
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 356fb6aa08c..2bfc5cb2d9c 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -320,6 +320,7 @@ class @Notes
form.show()
textarea = form.find("textarea")
textarea.focus()
+ autosize(textarea)
# HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
# The textarea has the correct value, Chrome just won't show it unless we
@@ -355,7 +356,7 @@ class @Notes
$('.note[id="' + note_id + '"]').each ->
note = $(this)
notes = note.closest(".notes")
- count = notes.closest(".notes_holder").find(".discussion-notes-count")
+ count = notes.closest(".issuable-details").find(".notes-tab .badge")
# check if this is the last note for this line
if notes.find(".note").length is 1
@@ -365,9 +366,10 @@ class @Notes
# for diff lines
notes.closest("tr").remove()
- else
- # update notes count
- count.get(0).lastChild.nodeValue = " #{notes.children().length - 1}"
+
+ # update notes count
+ oldNum = parseInt(count.text())
+ count.text(oldNum - 1)
note.remove()
diff --git a/app/assets/javascripts/star.js.coffee b/app/assets/javascripts/star.js.coffee
index d849b2e7950..f27780dda93 100644
--- a/app/assets/javascripts/star.js.coffee
+++ b/app/assets/javascripts/star.js.coffee
@@ -6,7 +6,7 @@ class @Star
$starIcon = $this.find('i')
toggleStar = (isStarred) ->
- $this.parent().find('span.count').text data.star_count
+ $this.parent().find('.star-count').text data.star_count
if isStarred
$starSpan.removeClass('starred').text 'Star'
$starIcon.removeClass('fa-star').addClass 'fa-star-o'
@@ -19,4 +19,4 @@ class @Star
return
).on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert')
- return \ No newline at end of file
+ return
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 05645116268..585a9d83913 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -75,6 +75,8 @@ hr {
@include str-truncated;
}
+.item-title { font-weight: 600; }
+
/** FLASH message **/
.author_link {
color: $gl-link-color;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 8e6922c9231..b7638c86bfa 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -1,5 +1,5 @@
.filter-item {
- margin-right: 15px;
+ margin-right: 6px;
}
@media (min-width: 800px) {
diff --git a/app/assets/stylesheets/framework/fonts.scss b/app/assets/stylesheets/framework/fonts.scss
index 20988f7b430..7a946109e3a 100644
--- a/app/assets/stylesheets/framework/fonts.scss
+++ b/app/assets/stylesheets/framework/fonts.scss
@@ -3,23 +3,39 @@
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
- src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf.woff');
+ src:
+ local('Source Sans Pro Light'),
+ local('SourceSansPro-Light'),
+ font-url('SourceSansPro-Light.ttf.woff2') format('woff2'),
+ font-url('SourceSansPro-Light.ttf.woff') format('woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
- src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf.woff');
+ src:
+ local('Source Sans Pro'),
+ local('SourceSansPro-Regular'),
+ font-url('SourceSansPro-Regular.ttf.woff2') format('woff2'),
+ font-url('SourceSansPro-Regular.ttf.woff') format('woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
- src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf.woff');
+ src:
+ local('Source Sans Pro Semibold'),
+ local('SourceSansPro-Semibold'),
+ font-url('SourceSansPro-Semibold.ttf.woff2') format('woff2'),
+ font-url('SourceSansPro-Semibold.ttf.woff') format('woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
- src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf.woff');
+ src:
+ local('Source Sans Pro Bold'),
+ local('SourceSansPro-Bold'),
+ font-url('SourceSansPro-Bold.ttf.woff2') format('woff2'),
+ font-url('SourceSansPro-Bold.ttf.woff') format('woff');
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 85ecdddda79..3ec48da9a41 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -26,6 +26,7 @@ $gl-vert-padding: 6px;
$gl-padding-top:10px;
$gl-avatar-size: 46px;
$secondary-text: #7f8fa4;
+$error-exclamation-point: #E62958;
/*
* Color schema
diff --git a/app/assets/stylesheets/pages/branches.scss b/app/assets/stylesheets/pages/branches.scss
deleted file mode 100644
index abae5c3d0a5..00000000000
--- a/app/assets/stylesheets/pages/branches.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.branch-name{
- font-weight: 600;
-}
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 6ec88bdd804..e53d6fc6bdc 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -2,10 +2,6 @@
display: block;
}
-.commit-row-title .commit-title {
- font-weight: 600;
-}
-
.commit-author, .commit-committer{
display: block;
color: #999;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 800df95cff3..818fd03e2ae 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -36,6 +36,10 @@ li.commit {
line-height: 20px;
margin-bottom: 2px;
+ .btn-clipboard {
+ margin-top: -1px;
+ }
+
.notes_count {
float: right;
margin-right: 10px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 012232a708e..0b79aa172a4 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -66,6 +66,7 @@
width: 100%;
font-family: $monospace_font;
border: none;
+ border-collapse: separate;
margin: 0px;
padding: 0px;
.line_holder td {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 3404c2631e1..263993f59a5 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -11,8 +11,3 @@
height: 42px;
}
}
-
-.content-list .group-name {
- font-weight: 600;
- color: #4c4e54;
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index eae3590a189..977ada0ff38 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -20,6 +20,11 @@
position: fixed;
top: 70px;
margin-right: 35px;
+
+ &.no-affix {
+ position: relative;
+ top: 0;
+ }
}
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 13b0ed769fc..003a4c22f20 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -92,12 +92,12 @@
}
.project-repo-buttons {
- margin-top: 12px;
+ margin-top: 20px;
margin-bottom: 0px;
.count-buttons {
display: block;
- margin-bottom: 12px;
+ margin-bottom: 20px;
}
.clone-row {
@@ -163,7 +163,7 @@
line-height: 13px;
padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px;
- padding: 10px;
+ padding: 10px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
@@ -558,3 +558,9 @@ pre.light-well {
width: 101%;
}
}
+
+.cannot-be-merged,
+.cannot-be-merged:hover {
+ color: #E62958;
+ margin-top: 2px;
+}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 3aaa96da609..bdcf1897522 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -3,6 +3,10 @@
border-bottom: 1px solid #DDD;
padding-bottom: 15px;
margin-bottom: 15px;
+
+ .term {
+ height: 22px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss
deleted file mode 100644
index e9cd6dc6c5e..00000000000
--- a/app/assets/stylesheets/pages/tags.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.tag-name{
- font-weight: 600;
-}
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 38814459f66..2eac0cabf7a 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -2,6 +2,7 @@ class AbuseReportsController < ApplicationController
def new
@abuse_report = AbuseReport.new
@abuse_report.user_id = params[:user_id]
+ @ref_url = params.fetch(:ref_url, '')
end
def create
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 8484a502024..8b62c11f7cc 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -116,7 +116,7 @@ class ApplicationController < ActionController::Base
# localhost/group/project
#
if id =~ /\.git\Z/
- redirect_to request.original_url.gsub(/\.git\Z/, '') and return
+ redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return
end
project_path = "#{namespace}/#{id}"
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index dff0732bdfe..f159a6d6dc6 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -8,7 +8,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
unless artifacts_file.exists?
- return not_found!
+ return render_404
end
send_file artifacts_file.path, disposition: 'attachment'
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 0e965966ffa..92d9699fe84 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -42,7 +42,7 @@ class Projects::BuildsController < Projects::ApplicationController
def retry
unless @build.retryable?
- return page_404
+ return render_404
end
build = Ci::Build.retry(@build)
@@ -72,7 +72,7 @@ class Projects::BuildsController < Projects::ApplicationController
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
- return page_404
+ return render_404
end
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index ba924ccc5a5..f5a169e5aa9 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -80,7 +80,7 @@ class Projects::CommitController < Projects::ApplicationController
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
- return page_404
+ return render_404
end
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index f476afb2d92..68244883803 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -49,7 +49,7 @@ class Projects::IssuesController < Projects::ApplicationController
assignee_id: ""
)
- @issue = @project.issues.new(issue_params)
+ @issue = @noteable = @project.issues.new(issue_params)
respond_with(@issue)
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index de948d271c8..a6284a24223 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -90,6 +90,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
+ @noteable = @merge_request
@target_branches = if @merge_request.target_project
@merge_request.target_project.repository.branch_names
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 2104c7a7a71..92b0caa2efb 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -25,7 +25,7 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def new
- @snippet = @project.snippets.build
+ @snippet = @noteable = @project.snippets.build
end
def create
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 4d56b48e3f8..0a4192e6bac 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -81,7 +81,8 @@ class IssuableFinder
elsif current_user && params[:authorized_only].presence && !current_user_related?
@projects = current_user.authorized_projects.reorder(nil)
else
- @projects = ProjectsFinder.new.execute(current_user).reorder(nil)
+ @projects = ProjectsFinder.new.execute(current_user, group: group).
+ reorder(nil)
end
end
diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb
index d58609a2de5..64c1ce8cfab 100644
--- a/app/mailers/emails/builds.rb
+++ b/app/mailers/emails/builds.rb
@@ -3,13 +3,26 @@ 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"
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"
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
+
+ private
+ def add_build_headers
+ headers['X-GitLab-Build-Id'] = @build.id
+ headers['X-GitLab-Build-Ref'] = @build.ref
+ end
+
end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index b96418679bd..377c2999d6c 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -43,7 +43,7 @@ module Emails
@current_user = @created_by = User.find(created_by_id)
@access_level = access_level
@invite_email = invite_email
-
+
@target_url = namespace_project_url(@project.namespace, @project)
mail(to: @created_by.notification_email,
@@ -65,6 +65,10 @@ module Emails
# used in notify layout
@target_url = @message.target_url
+ @project = Project.find project_id
+
+ add_project_headers
+ headers['X-GitLab-Author'] = @message.author_username
mail(from: sender(@message.author_id, @message.send_from_committer_email?),
reply_to: @message.reply_to,
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index e1cd075a978..8cbc9eefc7b 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -100,12 +100,7 @@ class Notify < BaseMailer
end
def mail_thread(model, headers = {})
- if @project
- headers['X-GitLab-Project'] = @project.name
- headers['X-GitLab-Project-Id'] = @project.id
- headers['X-GitLab-Project-Path'] = @project.path_with_namespace
- end
-
+ add_project_headers
headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers['X-GitLab-Reply-Key'] = reply_key
@@ -152,4 +147,12 @@ class Notify < BaseMailer
def reply_key
@reply_key ||= SentNotification.reply_key
end
+
+ def add_project_headers
+ return unless @project
+
+ headers['X-GitLab-Project'] = @project.name
+ headers['X-GitLab-Project-Id'] = @project.id
+ headers['X-GitLab-Project-Path'] = @project.path_with_namespace
+ end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 5375148a654..ab59a3506a2 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -160,6 +160,7 @@ class Ability
@project_report_rules ||= project_guest_rules + [
:create_commit_status,
:read_commit_statuses,
+ :read_build_artifacts,
:download_code,
:fork_project,
:create_project_snippet,
@@ -175,7 +176,6 @@ class Ability
:create_merge_request,
:create_wiki,
:manage_builds,
- :read_build_artifacts,
:push_code
]
end
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 2bc15c60d57..cc59aa4e911 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -17,7 +17,7 @@ class AbuseReport < ActiveRecord::Base
validates :reporter, presence: true
validates :user, presence: true
validates :message, presence: true
- validates :user_id, uniqueness: true
+ validates :user_id, uniqueness: { message: 'has already been reported' }
def remove_user
user.block
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 6cc26abce66..16a5b03f591 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -128,6 +128,14 @@ module Ci
!self.commit.latest_builds_for_ref(self.ref).include?(self)
end
+ def depends_on_builds
+ # Get builds of the same type
+ latest_builds = self.commit.builds.similar(self).latest
+
+ # Return builds from previous stages
+ latest_builds.where('stage_idx < ?', stage_idx)
+ end
+
def trace_html
html = Ci::Ansi2html::convert(trace) if trace.present?
html || ''
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index fa18ba5dbbe..fe923fafbe0 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -3,11 +3,11 @@
# Table name: web_hooks
#
# id :integer not null, primary key
-# url :string(255)
+# url :string(2000)
# project_id :integer
# created_at :datetime
# updated_at :datetime
-# type :string(255) default("ProjectHook")
+# type :string default("ProjectHook")
# service_id :integer
# push_events :boolean default(TRUE), not null
# issues_events :boolean default(FALSE), not null
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index b333a337347..80962264ba2 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -3,11 +3,11 @@
# Table name: web_hooks
#
# id :integer not null, primary key
-# url :string(255)
+# url :string(2000)
# project_id :integer
# created_at :datetime
# updated_at :datetime
-# type :string(255) default("ProjectHook")
+# type :string default("ProjectHook")
# service_id :integer
# push_events :boolean default(TRUE), not null
# issues_events :boolean default(FALSE), not null
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index d81512fae5d..c147d8762a9 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -3,11 +3,11 @@
# Table name: web_hooks
#
# id :integer not null, primary key
-# url :string(255)
+# url :string(2000)
# project_id :integer
# created_at :datetime
# updated_at :datetime
-# type :string(255) default("ProjectHook")
+# type :string default("ProjectHook")
# service_id :integer
# push_events :boolean default(TRUE), not null
# issues_events :boolean default(FALSE), not null
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 3bb50c63cac..7a13c3f0a39 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -3,11 +3,11 @@
# Table name: web_hooks
#
# id :integer not null, primary key
-# url :string(255)
+# url :string(2000)
# project_id :integer
# created_at :datetime
# updated_at :datetime
-# type :string(255) default("ProjectHook")
+# type :string default("ProjectHook")
# service_id :integer
# push_events :boolean default(TRUE), not null
# issues_events :boolean default(FALSE), not null
diff --git a/app/models/project.rb b/app/models/project.rb
index 7e131151513..5579710a476 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -272,6 +272,10 @@ class Project < ActiveRecord::Base
query: "%#{query.try(:downcase)}%")
end
+ def search_by_visibility(level)
+ where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase))
+ end
+
def search_by_title(query)
where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
end
@@ -468,12 +472,9 @@ class Project < ActiveRecord::Base
!external_issue_tracker
end
- def external_issues_trackers
- services.select(&:issue_tracker?).reject(&:default?)
- end
-
def external_issue_tracker
- @external_issues_tracker ||= external_issues_trackers.find(&:activated?)
+ @external_issue_tracker ||=
+ services.issue_trackers.active.without_defaults.first
end
def can_have_issues_tracker_id?
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index c3f70d1f972..e10b5529b42 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -23,14 +23,12 @@
# List methods you need to implement to get your CI service
# working with GitLab Merge Requests
class CiService < Service
- def category
- :ci
- end
-
+ default_value_for :category, 'ci'
+
def valid_token?(token)
self.respond_to?(:token) && self.token.present? && self.token == token
end
-
+
def supported_events
%w(push)
end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 7aa04309f54..05436cd0f79 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -24,9 +24,7 @@ class GitlabIssueTrackerService < IssueTrackerService
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
- def default?
- true
- end
+ default_value_for :default, true
def to_param
'gitlab'
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index ed201979d39..25045224ce5 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -23,12 +23,10 @@ class IssueTrackerService < Service
validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
- def category
- :issue_tracker
- end
+ default_value_for :category, 'issue_tracker'
def default?
- false
+ default
end
def issue_url(iid)
diff --git a/app/models/service.rb b/app/models/service.rb
index 24f4bf7646e..721273250ea 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -43,6 +43,9 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
+ scope :issue_trackers, -> { where(category: 'issue_tracker') }
+ scope :active, -> { where(active: true) }
+ scope :without_defaults, -> { where(default: false) }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
@@ -51,6 +54,8 @@ class Service < ActiveRecord::Base
scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) }
+ default_value_for :category, 'common'
+
def activated?
active
end
@@ -60,7 +65,7 @@ class Service < ActiveRecord::Base
end
def category
- :common
+ read_attribute(:category).to_sym
end
def initialize_properties
@@ -153,7 +158,7 @@ class Service < ActiveRecord::Base
# Returns a hash of the properties that have been assigned a new value since last save,
# indicating their original values (attr => original value).
- # ActiveRecord does not provide a mechanism to track changes in serialized keys,
+ # ActiveRecord does not provide a mechanism to track changes in serialized keys,
# so we need a specific implementation for service properties.
# This allows to track changes to properties set with the accessor methods,
# but not direct manipulation of properties hash.
@@ -164,7 +169,7 @@ class Service < ActiveRecord::Base
def reset_updated_properties
@updated_properties = nil
end
-
+
def async_execute(data)
return unless supported_events.include?(data[:object_kind])
diff --git a/app/models/user.rb b/app/models/user.rb
index 592468933ed..4214f01f6a4 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -664,7 +664,10 @@ class User < ActiveRecord::Base
end
def all_emails
- [self.email, *self.emails.map(&:email)]
+ all_emails = []
+ all_emails << self.email unless self.temp_oauth_email?
+ all_emails.concat(self.emails.map(&:email))
+ all_emails
end
def hook_attrs
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index e4edc55bf69..ca8a41d93b8 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -376,10 +376,10 @@ class NotificationService
end
def reassign_resource_email(target, project, current_user, method)
- previous_assignee_id = previous_record(target, "assignee_id")
+ previous_assignee_id = previous_record(target, 'assignee_id')
previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
- recipients = build_recipients(target, project, current_user, [previous_assignee])
+ recipients = build_recipients(target, project, current_user, action: :reassign, previous_assignee: previous_assignee)
recipients.each do |recipient|
mailer.send(
@@ -400,22 +400,27 @@ class NotificationService
end
end
- def build_recipients(target, project, current_user, extra_recipients = nil)
+ def build_recipients(target, project, current_user, action: nil, previous_assignee: nil)
recipients = target.participants(current_user)
- recipients = recipients.concat(extra_recipients).compact.uniq if extra_recipients
-
recipients = add_project_watchers(recipients, project)
recipients = reject_mention_users(recipients, project)
- recipients = reject_muted_users(recipients, project)
+ # Re-assign is considered as a mention of the new assignee so we add the
+ # new assignee to the list of recipients after we rejected users with
+ # the "on mention" notification level
+ if action == :reassign
+ recipients << previous_assignee if previous_assignee
+ recipients << target.assignee
+ end
+
+ recipients = reject_muted_users(recipients, project)
recipients = add_subscribed_users(recipients, target)
recipients = reject_unsubscribed_users(recipients, target)
recipients.delete(current_user)
- recipients = recipients.uniq
- recipients
+ recipients.uniq
end
def mailer
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index a6820183bee..c94d7ab710f 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -95,7 +95,7 @@ module Projects
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group
- @project.team << [current_user, :master, current_user]
+ @project.team << [current_user, :master]
end
@project.import_start if @project.import?
diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
index 1b0ae6c0056..1cd93263c9f 100644
--- a/app/uploaders/artifact_uploader.rb
+++ b/app/uploaders/artifact_uploader.rb
@@ -32,6 +32,10 @@ class ArtifactUploader < CarrierWave::Uploader::Base
self.class.storage == CarrierWave::Storage::File
end
+ def filename
+ file.try(:filename)
+ end
+
def exists?
file.try(:exists?)
end
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index 3e5cdd2ce4a..f125ecf7be5 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -16,7 +16,7 @@
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
- = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
+ = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true, value: sanitize(@ref_url)
.help-block
Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment.
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 9459d8a6295..add9a00138b 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -21,10 +21,10 @@
.form-group
= f.label :email, class: "control-label"
.col-sm-10
- - if @user.ldap_user?
+ - if @user.ldap_user? && @user.ldap_email?
= f.text_field :email, class: "form-control", required: true, readonly: true
%span.help-block.light
- Email is read-only for LDAP user
+ Your email address was automatically set based on the LDAP server.
- else
- if @user.temp_oauth_email?
= f.text_field :email, class: "form-control", required: true, value: nil
diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml
index 5b87d55efd5..e4b7979949c 100644
--- a/app/views/projects/artifacts/_tree_directory.html.haml
+++ b/app/views/projects/artifacts/_tree_directory.html.haml
@@ -1,7 +1,9 @@
-%tr{ class: 'tree-item' }
+- path_to_directory = browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: directory.path)
+
+%tr.tree-item{ 'data-link' => path_to_directory}
%td.tree-item-file-name
= tree_icon('folder', '755', directory.name)
%span.str-truncated
- = link_to directory.name, browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: directory.path)
+ = link_to directory.name, path_to_directory
%td
%td
diff --git a/app/views/projects/artifacts/_tree_file.html.haml b/app/views/projects/artifacts/_tree_file.html.haml
index 92c1648f726..3dfc09cc495 100644
--- a/app/views/projects/artifacts/_tree_file.html.haml
+++ b/app/views/projects/artifacts/_tree_file.html.haml
@@ -1,11 +1,11 @@
-%tr{ class: 'tree-item' }
+- path_to_file = file_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: file.path)
+
+%tr.tree-item{ 'data-link' => path_to_file }
%td.tree-item-file-name
= tree_icon('file', '664', file.name)
%span.str-truncated
- = file.name
+ = link_to file.name, path_to_file
%td
= number_to_human_size(file.metadata[:size], precision: 2)
%td
- = link_to file_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: file.path),
- class: 'btn btn-xs btn-default artifact-download' do
- = icon('download')
+ = number_to_human_size(file.metadata[:zipped], precision: 2)
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index 1add7ef6bfb..b70c776a2b2 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -1,24 +1,32 @@
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
= render 'projects/builds/header_title'
-#tree-holder.tree-holder
- .gray-content-block.top-block.clearfix
- .pull-right
- = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build),
- class: 'btn btn-default' do
- = icon('download')
- Download artifacts archive
+.top-block.gray-content-block.clearfix
+ .pull-right
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build),
+ class: 'btn btn-default' do
+ = icon('download')
+ Download artifacts archive
-%div.tree-content-holder
- .table-holder
- %table.table.tree-table.table-striped
+.tree-holder
+ %div.tree-content-holder
+ %table.table.tree-table
%thead
%tr
%th Name
%th Size
- %th Download
+ %th Compressed to
= render partial: 'tree_directory', collection: @entry.directories(parent: true), as: :directory
= render partial: 'tree_file', collection: @entry.files, as: :file
- if @entry.empty?
.center Empty
+
+:javascript
+ $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
+ e.stopImmediatePropagation();
+ });
+
+ $('.tree-holder').on('click', 'tr[data-link]', function(e) {
+ window.location = this.dataset.link;
+ });
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index d276e5932d1..76a823d3828 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -1,12 +1,12 @@
- commit = @repository.commit(branch.target)
- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
-- diverging_commit_counts = @repository.diverging_commit_counts(branch)
+- diverging_commit_counts = @repository.diverging_commit_counts(branch)
- number_commits_behind = diverging_commit_counts[:behind]
- number_commits_ahead = diverging_commit_counts[:ahead]
%li(class="js-branch-#{branch.name}")
%div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do
- .branch-name.str-truncated= branch.name
+ %span.item-title.str-truncated= branch.name
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 5d18c0d803a..bbb6944a65a 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -6,7 +6,12 @@
- if can?(current_user, :manage_builds, @project)
.pull-left.hidden-xs
- if @all_builds.running_or_pending.any?
- = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+ = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
+ data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+
+ = link_to ci_lint_path, class: 'btn btn-default' do
+ = icon('wrench')
+ %span CI Lint
%ul.nav-links
%li{class: ('active' if @scope.nil?)}
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 4d4b410ee29..7f2903589a9 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -11,7 +11,7 @@
= cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
.commit-row-title
- .commit-title.str-truncated
+ %span.item-title.str-truncated
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
- if commit.description?
%a.text-expander.js-toggle-button ...
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 56a7ced1236..399782273d3 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -3,7 +3,7 @@
%li
%div
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
- .tag-name
+ %span.item-title
= icon('tag')
= tag.name
- if tag.message.present?
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index dbb20347860..8c7f93f93b6 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -17,8 +17,8 @@
.pull-right
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o
- .tag-name.title
- = @tag.name
+ .title
+ %span.item-title= @tag.name
- if @tag.message.present?
%span.light
&nbsp;
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index f4cfa29ae56..778b20fb4f2 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -10,7 +10,8 @@
%i.fa.fa-sign-out
= image_tag group_icon(group), class: "avatar s46 hidden-xs"
- = link_to group.name, group, class: 'group-name'
+ = link_to group, class: 'group-name' do
+ %span.item-title= group.name
- if group_member
as
@@ -18,4 +19,3 @@
%div.light
#{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")}
-
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 9f4a7098ea2..3092ff54242 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -10,6 +10,9 @@
.value
- if issuable.assignee
%strong= link_to_member(@project, issuable.assignee, size: 24)
+ - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
+ %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
+ = icon('exclamation-triangle')
- else
.light None
diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml
index b7e0162e80d..b8d93e9ff45 100644
--- a/app/views/sherlock/transactions/_queries.html.haml
+++ b/app/views/sherlock/transactions/_queries.html.haml
@@ -8,7 +8,7 @@
%tr
%th= t('sherlock.time')
%th= t('sherlock.query')
- %td
+ %th
%tbody
- @transaction.sorted_queries.each do |query|
%tr
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 849304ee2b6..3bfd781e51d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -20,7 +20,7 @@
data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
= icon('exclamation-circle')
- else
- = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray',
+ = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
= icon('exclamation-circle')
- if current_user
diff --git a/bin/background_jobs b/bin/background_jobs
index 5c85fb339e6..1f67d732949 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -27,17 +27,17 @@ restart()
stop
fi
killall
- start_sidekiq -d -L $sidekiq_logfile
+ start_sidekiq -d -L $sidekiq_logfile >> $sidekiq_logfile 2>&1
}
start_no_deamonize()
{
- start_sidekiq
+ start_sidekiq >> $sidekiq_logfile 2>&1
}
start_sidekiq()
{
- bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
+ bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile "$@"
}
load_ok()
@@ -66,6 +66,9 @@ case "$1" in
start_no_deamonize)
start_no_deamonize
;;
+ start_foreground)
+ start_sidekiq
+ ;;
restart)
restart
;;
diff --git a/bin/web b/bin/web
index 67f236eb0bb..03fe7a6354b 100755
--- a/bin/web
+++ b/bin/web
@@ -5,6 +5,7 @@ app_root=$(pwd)
unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
unicorn_config="$app_root/config/unicorn.rb"
+unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV"
get_unicorn_pid()
{
@@ -18,7 +19,12 @@ get_unicorn_pid()
start()
{
- bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV
+ $unicorn_cmd -D
+}
+
+start_foreground()
+{
+ $unicorn_cmd
}
stop()
@@ -37,6 +43,9 @@ case "$1" in
start)
start
;;
+ start_foreground)
+ start_foreground
+ ;;
stop)
stop
;;
diff --git a/config/environments/development.rb b/config/environments/development.rb
index c22722c606b..689694a3480 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -16,6 +16,9 @@ Rails.application.configure do
# Print deprecation notices to the Rails logger
config.active_support.deprecation = :log
+ # Raise an error on page load if there are pending migrations
+ config.active_record.migration_error = :page_load
+
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
@@ -34,6 +37,8 @@ Rails.application.configure do
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# Open sent mails in browser
config.action_mailer.delivery_method = :letter_opener
+ # Don't make a mess when bootstrapping a development environment
+ config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
config.eager_load = false
end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index d625a909bf1..04a7c16ebde 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -9,13 +9,8 @@ class Settings < Settingslogic
gitlab.port.to_i == (gitlab.https ? 443 : 80)
end
- # get host without www, thanks to http://stackoverflow.com/a/6674363/1233435
- def get_host_without_www(url)
- url = CGI.escape(url)
- uri = URI.parse(url)
- uri = URI.parse("http://#{url}") if uri.scheme.nil?
- host = uri.host.downcase
- host.start_with?('www.') ? host[4..-1] : host
+ def host_without_www(url)
+ host(url).sub('www.', '')
end
def build_gitlab_ci_url
@@ -87,6 +82,17 @@ class Settings < Settingslogic
custom_port
]
end
+
+ # Extract the host part of the given +url+.
+ def host(url)
+ url = url.downcase
+ url = "http://#{url}" unless url.start_with?('http')
+
+ # Get rid of the path so that we don't even have to encode it
+ url_without_path = url.sub(%r{(https?://[^\/]+)/?.*}, '\1')
+
+ URI.parse(url_without_path).host
+ end
end
end
@@ -228,7 +234,7 @@ Settings['gravatar'] ||= Settingslogic.new({})
Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil?
Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
-Settings.gravatar['host'] = Settings.get_host_without_www(Settings.gravatar['plain_url'])
+Settings.gravatar['host'] = Settings.host_without_www(Settings.gravatar['plain_url'])
#
# Cron Jobs
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 52ace27b7ae..b1fe36dc21c 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -55,6 +55,12 @@ if Gitlab::Metrics.enabled?
config.instrument_methods(const) if const.is_a?(Module)
end
+
+ Dir[Rails.root.join('app', 'finders', '*.rb')].each do |path|
+ const = File.basename(path, '.rb').camelize.constantize
+
+ config.instrument_instance_methods(const)
+ end
end
GC::Profiler.enable
diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb
new file mode 100644
index 00000000000..98a7fca6f6f
--- /dev/null
+++ b/db/migrate/20151201203948_raise_hook_url_limit.rb
@@ -0,0 +1,5 @@
+class RaiseHookUrlLimit < ActiveRecord::Migration
+ def change
+ change_column :web_hooks, :url, :string, limit: 2000
+ end
+end
diff --git a/db/migrate/20160119111158_add_services_category.rb b/db/migrate/20160119111158_add_services_category.rb
new file mode 100644
index 00000000000..a9110a8418b
--- /dev/null
+++ b/db/migrate/20160119111158_add_services_category.rb
@@ -0,0 +1,39 @@
+class AddServicesCategory < ActiveRecord::Migration
+ def up
+ add_column :services, :category, :string, default: 'common', null: false
+
+ category = quote_column_name('category')
+ type = quote_column_name('type')
+
+ execute <<-EOF
+UPDATE services
+SET #{category} = 'issue_tracker'
+WHERE #{type} IN (
+ 'CustomIssueTrackerService',
+ 'GitlabIssueTrackerService',
+ 'IssueTrackerService',
+ 'JiraService',
+ 'RedmineService'
+);
+EOF
+
+ execute <<-EOF
+UPDATE services
+SET #{category} = 'ci'
+WHERE #{type} IN (
+ 'BambooService',
+ 'BuildkiteService',
+ 'CiService',
+ 'DroneCiService',
+ 'GitlabCiService',
+ 'TeamcityService'
+);
+ EOF
+
+ add_index :services, :category
+ end
+
+ def down
+ remove_column :services, :category
+ end
+end
diff --git a/db/migrate/20160119112418_add_services_default.rb b/db/migrate/20160119112418_add_services_default.rb
new file mode 100644
index 00000000000..69a42d7b873
--- /dev/null
+++ b/db/migrate/20160119112418_add_services_default.rb
@@ -0,0 +1,20 @@
+class AddServicesDefault < ActiveRecord::Migration
+ def up
+ add_column :services, :default, :boolean, default: false
+
+ default = quote_column_name('default')
+ type = quote_column_name('type')
+
+ execute <<-EOF
+UPDATE services
+SET #{default} = true
+WHERE #{type} = 'GitlabIssueTrackerService'
+EOF
+
+ add_index :services, :default
+ end
+
+ def down
+ remove_column :services, :default
+ end
+end
diff --git a/db/migrate/20160119145451_add_ldap_email_to_users.rb b/db/migrate/20160119145451_add_ldap_email_to_users.rb
new file mode 100644
index 00000000000..654d31ab15a
--- /dev/null
+++ b/db/migrate/20160119145451_add_ldap_email_to_users.rb
@@ -0,0 +1,30 @@
+class AddLdapEmailToUsers < ActiveRecord::Migration
+ def up
+ add_column :users, :ldap_email, :boolean, default: false, null: false
+
+ if Gitlab::Database.mysql?
+ execute %{
+ UPDATE users, identities
+ SET users.ldap_email = TRUE
+ WHERE identities.user_id = users.id
+ AND users.email LIKE 'temp-email-for-oauth%'
+ AND identities.provider LIKE 'ldap%'
+ AND identities.extern_uid IS NOT NULL
+ }
+ else
+ execute %{
+ UPDATE users
+ SET ldap_email = TRUE
+ FROM identities
+ WHERE identities.user_id = users.id
+ AND users.email LIKE 'temp-email-for-oauth%'
+ AND identities.provider LIKE 'ldap%'
+ AND identities.extern_uid IS NOT NULL
+ }
+ end
+ end
+
+ def down
+ remove_column :users, :ldap_email
+ end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 2b7afae6d7c..14d7e84d856 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -6,5 +6,6 @@ class LimitsToMysql < ActiveRecord::Migration
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
change_column :snippets, :content, :text, limit: 2147483647
change_column :notes, :st_diff, :text, limit: 2147483647
+ change_column :events, :data, :text, limit: 2147483647
end
end
diff --git a/db/schema.rb b/db/schema.rb
index 2fc8c4d5ed4..dc7cb9f667f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160113111034) do
+ActiveRecord::Schema.define(version: 20160119145451) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -725,20 +725,24 @@ ActiveRecord::Schema.define(version: 20160113111034) do
t.string "type"
t.string "title"
t.integer "project_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.boolean "active", default: false, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.boolean "active", null: false
t.text "properties"
- t.boolean "template", default: false
- t.boolean "push_events", default: true
- t.boolean "issues_events", default: true
- t.boolean "merge_requests_events", default: true
- t.boolean "tag_push_events", default: true
- t.boolean "note_events", default: true, null: false
- t.boolean "build_events", default: false, null: false
- end
-
+ t.boolean "template", default: false
+ t.boolean "push_events", default: true
+ t.boolean "issues_events", default: true
+ t.boolean "merge_requests_events", default: true
+ t.boolean "tag_push_events", default: true
+ t.boolean "note_events", default: true, null: false
+ t.boolean "build_events", default: false, null: false
+ t.string "category", default: "common", null: false
+ t.boolean "default", default: false
+ end
+
+ add_index "services", ["category"], name: "index_services_on_category", using: :btree
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
+ add_index "services", ["default"], name: "index_services_on_default", using: :btree
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
add_index "services", ["template"], name: "index_services_on_template", using: :btree
@@ -850,6 +854,7 @@ ActiveRecord::Schema.define(version: 20160113111034) do
t.boolean "hide_project_limit", default: false
t.string "unlock_token"
t.datetime "otp_grace_period_started_at"
+ t.boolean "ldap_email", default: false, null: false
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -874,19 +879,19 @@ ActiveRecord::Schema.define(version: 20160113111034) do
add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
create_table "web_hooks", force: :cascade do |t|
- t.string "url"
+ t.string "url", limit: 2000
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "type", default: "ProjectHook"
+ t.string "type", default: "ProjectHook"
t.integer "service_id"
- t.boolean "push_events", default: true, null: false
- t.boolean "issues_events", default: false, null: false
- t.boolean "merge_requests_events", default: false, null: false
- t.boolean "tag_push_events", default: false
- t.boolean "note_events", default: false, null: false
- t.boolean "enable_ssl_verification", default: true
- t.boolean "build_events", default: false, null: false
+ t.boolean "push_events", default: true, null: false
+ t.boolean "issues_events", default: false, null: false
+ t.boolean "merge_requests_events", default: false, null: false
+ t.boolean "tag_push_events", default: false
+ t.boolean "note_events", default: false, null: false
+ t.boolean "enable_ssl_verification", default: true
+ t.boolean "build_events", default: false, null: false
end
add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
diff --git a/doc/api/README.md b/doc/api/README.md
index 2fa177ff4dd..4d2fb582833 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -151,7 +151,53 @@ When listing resources you can pass the following parameters:
- `page` (default: `1`) - page number
- `per_page` (default: `20`, max: `100`) - number of items to list per page
-[Link headers](http://www.w3.org/wiki/LinkHeader) are send back with each response. These have `rel` prev/next/first/last and contain the relevant URL. Please use these instead of generating your own URLs.
+### Pagination Link header
+
+[Link headers](http://www.w3.org/wiki/LinkHeader) are sent back with each
+response. They have `rel` set to prev/next/first/last and contain the relevant
+URL. Please use these links instead of generating your own URLs.
+
+In the cURL example below, we limit the output to 3 items per page (`per_page=3`)
+and we request the second page (`page=2`) of [comments](notes.md) of the issue
+with ID `8` which belongs to the project with ID `8`:
+
+```bash
+curl -I -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/issues/8/notes?per_page=3&page=2
+```
+
+The response will then be:
+
+```
+HTTP/1.1 200 OK
+Cache-Control: no-cache
+Content-Length: 1103
+Content-Type: application/json
+Date: Mon, 18 Jan 2016 09:43:18 GMT
+Link: <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="prev", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="next", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="first", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="last"
+Status: 200 OK
+Vary: Origin
+X-Next-Page: 3
+X-Page: 2
+X-Per-Page: 3
+X-Prev-Page: 1
+X-Request-Id: 732ad4ee-9870-4866-a199-a9db0cde3c86
+X-Runtime: 0.108688
+X-Total: 8
+X-Total-Pages: 3
+```
+
+### Other pagination headers
+
+Additional pagination headers are also sent back.
+
+| Header | Description |
+| ------ | ----------- |
+| `X-Total` | The total number of items |
+| `X-Total-Pages` | The total number of pages |
+| `X-Per-Page` | The number of items per page |
+| `X-Page` | The index of the current page (starting at 1) |
+| `X-Next-Page` | The index of the next page |
+| `X-Prev-Page` | The index of the previous page |
## id vs iid
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 808675d8605..d47e79ba47f 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -33,6 +33,7 @@ GET /groups/:id/projects
Parameters:
- `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
- `search` (optional) - Return list of authorized projects according to a search criteria
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 241229221db..3f372c955d2 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -29,6 +29,7 @@ GET /projects
Parameters:
- `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
- `search` (optional) - Return list of authorized projects according to a search criteria
@@ -152,6 +153,7 @@ GET /projects/owned
Parameters:
- `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
- `search` (optional) - Return list of authorized projects according to a search criteria
@@ -167,6 +169,7 @@ GET /projects/starred
Parameters:
- `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
- `search` (optional) - Return list of authorized projects according to a search criteria
@@ -182,6 +185,7 @@ GET /projects/all
Parameters:
- `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
- `search` (optional) - Return list of authorized projects according to a search criteria
diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md
index 3b5008ccdb4..018ca22dbbd 100644
--- a/doc/ci/api/builds.md
+++ b/doc/ci/api/builds.md
@@ -18,18 +18,62 @@ Returns:
```json
{
- "id" : 79,
- "commands" : "",
- "path" : "",
- "ref" : "",
- "sha" : "",
- "project_id" : 6,
- "repo_url" : "git@demo.gitlab.com:gitlab/gitlab-shell.git",
- "before_sha" : ""
+ "id": 48584,
+ "ref": "0.1.1",
+ "tag": true,
+ "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
+ "status": "success",
+ "name": "pages",
+ "token": "9dd60b4f1a439d1765357446c1084c",
+ "stage": "test",
+ "project_id": 479,
+ "project_name": "test",
+ "commands": "echo commands",
+ "repo_url": "http://gitlab-ci-token:token@gitlab.example/group/test.git",
+ "before_sha": "0000000000000000000000000000000000000000",
+ "allow_git_fetch": false,
+ "options": {
+ "image": "docker:image",
+ "artifacts": {
+ "paths": [
+ "public"
+ ]
+ },
+ "cache": {
+ "paths": [
+ "vendor"
+ ]
+ }
+ },
+ "timeout": 3600,
+ "variables": [
+ {
+ "key": "CI_BUILD_TAG",
+ "value": "0.1.1",
+ "public": true
+ }
+ ],
+ "depends_on_builds": [
+ {
+ "id": 48584,
+ "ref": "0.1.1",
+ "tag": true,
+ "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
+ "status": "success",
+ "name": "build",
+ "token": "9dd60b4f1a439d1765357446c1084c",
+ "stage": "build",
+ "project_id": 479,
+ "project_name": "test",
+ "artifacts_file": {
+ "filename": "artifacts.zip",
+ "size": 0
+ }
+ }
+ ]
}
```
-
### Update details of an existing build
PUT /ci/builds/:id
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index 513ad69ec26..e51ff5a5de2 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -8,7 +8,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
-
+
# Ensure you have MySQL version 5.5.14 or later
mysql --version
@@ -31,7 +31,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Ensure you can use the InnoDB engine which is necessary to support long indexes
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
-
+
# Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
@@ -52,3 +52,25 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
mysql> \q
# You are done installing the database and can go back to the rest of the installation.
+
+## MySQL strings limits
+
+After installation or upgrade, remember to run the `add_limits_mysql` Rake task:
+
+```
+bundle exec rake add_limits_mysql
+```
+
+The `text` type in MySQL has a different size limit than the `text` type in
+PostgreSQL. In MySQL `text` columns are limited to ~65kB, whereas in PostgreSQL
+`text` columns are limited up to ~1GB!
+
+The `add_limits_mysql` Rake task converts some important `text` columns in the
+GitLab database to `longtext` columns, which can persist values of up to 4GB
+(sometimes less if the value contains multibyte characters).
+
+Details can be found in the [PostgreSQL][postgres-text-type] and
+[MySQL][mysql-text-types] manuals.
+
+[postgres-text-type]: http://www.postgresql.org/docs/9.1/static/datatype-character.html
+[mysql-text-types]: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 1632e42f701..8841dbdb7c6 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -78,6 +78,18 @@ On the sign in page there should now be a SAML button below the regular sign in
## Troubleshooting
-If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user.
+If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page,
+this likely indicates that GitLab could not get the email address for the SAML user.
-Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. \ No newline at end of file
+Make sure the IdP provides a claim containing the user's email address, using claim name
+'email' or 'mail'. The email will be used to automatically generate the GitLab username.
+
+If after signing in into your SAML server you are redirected back to the sign in page and
+no error is displayed, check your `production.log` file. It will most likely contain the
+message `Can't verify CSRF token authenticity`. This means that there is an error during
+the SAML request, but this error never reaches GitLab due to the CSRF check.
+
+To bypass this you can add `skip_before_action :verify_authenticity_token` to the
+`omniauth_callbacks_controller.rb` file. This will allow the error to hit GitLab,
+where it can then be seen in the usual logs, or as a flash message in the login
+screen. \ No newline at end of file
diff --git a/doc/update/8.3-to-8.4.md b/doc/update/8.3-to-8.4.md
index 1cbeab3eca3..604939cd733 100644
--- a/doc/update/8.3-to-8.4.md
+++ b/doc/update/8.3-to-8.4.md
@@ -48,7 +48,7 @@ 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.5.4
+sudo -u git -H git checkout 0.6.0
sudo -u git -H make
```
diff --git a/doc/update/README.md b/doc/update/README.md
index 0472537eeb5..109d5de3fa2 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -14,3 +14,4 @@ Depending on the installation method and your GitLab version, there are multiple
## Miscellaneous
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating your database from MySQL to PostgreSQL.
+- [MySQL installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/database_mysql.md) contains additional information about configuring GitLab to work with a MySQL database.
diff --git a/doc/workflow/importing/github_importer/importer.png b/doc/workflow/importing/github_importer/importer.png
deleted file mode 100644
index 57636717571..00000000000
--- a/doc/workflow/importing/github_importer/importer.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/importing/github_importer/new_project_page.png b/doc/workflow/importing/github_importer/new_project_page.png
deleted file mode 100644
index 002f22d81d7..00000000000
--- a/doc/workflow/importing/github_importer/new_project_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_importer.png b/doc/workflow/importing/img/import_projects_from_github_importer.png
new file mode 100644
index 00000000000..f744dc06f81
--- /dev/null
+++ b/doc/workflow/importing/img/import_projects_from_github_importer.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
new file mode 100644
index 00000000000..86be35acb37
--- /dev/null
+++ b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
Binary files differ
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 2027a055c37..f693f430a42 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -1,20 +1,44 @@
# Import your project from GitHub to GitLab
-It takes just a couple of steps to import your existing GitHub projects to GitLab. Keep in mind that it is possible only if
-GitHub support is enabled on your GitLab instance. You can read more about GitHub support [here](http://doc.gitlab.com/ce/integration/github.html)
+_**Note:** In order to enable the GitHub import setting, you should first
+enable the [GitHub integration][gh-import] in your GitLab instance._
-If you want to import from a GitHub Enterprise instance, you need to use GitLab Enterprise; please see the [EE docs for the GitHub integration](http://doc.gitlab.com/ee/integration/github.html).
+At its current state, GitHub importer can import:
-* Sign in to GitLab.com and go to your dashboard.
-* To get to the importer page, you need to go to the "New project" page.
+- the repository description (introduced in GitLab 7.7)
+- the git repository data (introduced in GitLab 7.7)
+- the issues (introduced in GitLab 7.7)
+- the pull requests (introduced in GitLab 8.4)
+- the wiki pages (introduced in GitLab 8.4)
-![New project page](github_importer/new_project_page.png)
+It is not yet possible to import your labels, milestones and cross-repository
+pull requests (those from forks). We are working on improving this in the near
+future.
-* Click on the "Import project from GitHub" link and you will be redirected to GitHub for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
+The importer page is visible when you [create a new project][new-project].
+Click on the **GitHub** link and you will be redirected to GitHub for
+permission to access your projects. After accepting, you'll be automatically
+redirected to the importer.
-![Importer page](github_importer/importer.png)
+![New project page on GitLab](img/import_projects_from_github_new_project_page.png)
-* To import a project, you can simple click "Add". The importer will import your repository, issues, and pull requests. Once the importer is done, a new GitLab project will be created with your imported data.
+---
-### Note
-When you import your projects from GitHub, it is not possible to keep your labels, milestones, and cross-repository pull requests. We are working on improving this in the near future.
+While at the GitHub importer page, you can see the import statuses of your
+GitHub projects. Those that are being imported will show a _started_ status,
+those already imported will be green, whereas those that are not yet imported
+have an **Import** button on the right side of the table. If you want, you can
+import all your GitHub projects in one go by hitting **Import all projects**
+in the upper left corner.
+
+![GitHub importer page](img/import_projects_from_github_importer.png)
+
+---
+
+The importer will create any new namespaces if they don't exist or in the
+case the namespace is taken, the project will be imported on the user's
+namespace.
+
+[gh-import]: ../../integration/github.md "GitHub integration"
+[ee-gh]: http://doc.gitlab.com/ee/integration/github.html "GitHub integration for GitLab EE"
+[new-project]: ../../gitlab-basics/create-project.md "How to create a new project in GitLab"
diff --git a/doc/workflow/protected_branches.md b/doc/workflow/protected_branches.md
index 0adf9f8e3e8..fdf9a8d391c 100644
--- a/doc/workflow/protected_branches.md
+++ b/doc/workflow/protected_branches.md
@@ -1,6 +1,6 @@
# Protected branches
-Permission in GitLab are fundamentally defined around the idea of having read or write permission to the repository and branches.
+Permissions in GitLab are fundamentally defined around the idea of having read or write permission to the repository and branches.
To prevent people from messing with history or pushing code without review, we've created protected branches.
diff --git a/features/project/builds.feature b/features/project/builds/artifacts.feature
index c00b0a7ae07..1185854453a 100644
--- a/features/project/builds.feature
+++ b/features/project/builds/artifacts.feature
@@ -1,32 +1,27 @@
-Feature: Project Builds
+Feature: Project Builds Artifacts
Background:
Given I sign in as a user
And I own a project
- And CI is enabled
- And I have recent build for my project
-
- Scenario: I browse build summary page
- When I visit recent build summary page
- Then I see summary for build
- And I see build trace
+ And project has CI enabled
+ And project has a recent build
Scenario: I download build artifacts
Given recent build has artifacts available
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts download button
Then download of build artifacts archive starts
Scenario: I browse build artifacts
Given recent build has artifacts available
And recent build has artifacts metadata available
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts browse button
Then I should see content of artifacts archive
Scenario: I browse subdirectory of build artifacts
Given recent build has artifacts available
And recent build has artifacts metadata available
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts browse button
And I click link to subdirectory within build artifacts
Then I should see content of subdirectory within artifacts archive
@@ -35,7 +30,7 @@ Feature: Project Builds
Given recent build has artifacts available
And recent build has artifacts metadata available
And recent build artifacts contain directory with UTF-8 characters
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts browse button
And I navigate to directory with UTF-8 characters in name
Then I should see content of directory with UTF-8 characters in name
@@ -44,7 +39,7 @@ Feature: Project Builds
Given recent build has artifacts available
And recent build has artifacts metadata available
And recent build artifacts contain directory with invalid UTF-8 characters
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts browse button
And I navigate to parent directory of directory with invalid name
Then I should not see directory with invalid name on the list
@@ -52,7 +47,7 @@ Feature: Project Builds
Scenario: I download a single file from build artifacts
Given recent build has artifacts available
And recent build has artifacts metadata available
- When I visit recent build summary page
+ When I visit recent build details page
And I click artifacts browse button
- And I click download button for a file within build artifacts
+ And I click a link to file within build artifacts
Then download of a file extracted from build artifacts should start
diff --git a/features/project/builds/permissions.feature b/features/project/builds/permissions.feature
new file mode 100644
index 00000000000..1193bcd74f6
--- /dev/null
+++ b/features/project/builds/permissions.feature
@@ -0,0 +1,18 @@
+Feature: Project Builds Permissions
+ Background:
+ Given I sign in as a user
+ And project exists in some group namespace
+ And project has CI enabled
+ And project has a recent build
+
+ Scenario: I try to download build artifacts as guest
+ Given I am member of a project with a guest role
+ And recent build has artifacts available
+ When I access artifacts download page
+ Then page status code should be 404
+
+ Scenario: I try to download build artifacts as reporter
+ Given I am member of a project with a reporter role
+ And recent build has artifacts available
+ When I access artifacts download page
+ Then download of build artifacts archive starts
diff --git a/features/project/builds/summary.feature b/features/project/builds/summary.feature
new file mode 100644
index 00000000000..b69d279517b
--- /dev/null
+++ b/features/project/builds/summary.feature
@@ -0,0 +1,15 @@
+Feature: Project Builds Summary
+ 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 browse build details page
+ When I visit recent build details page
+ Then I see details of a build
+ And I see build trace
+
+ Scenario: I browse project builds page
+ When I visit project builds page
+ Then I see button to CI Lint
diff --git a/features/steps/project/builds.rb b/features/steps/project/builds/artifacts.rb
index 28395281077..25f2f4e837c 100644
--- a/features/steps/project/builds.rb
+++ b/features/steps/project/builds/artifacts.rb
@@ -1,26 +1,13 @@
-class Spinach::Features::ProjectBuilds < Spinach::FeatureSteps
+class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedBuilds
include RepoHelpers
- step 'I see summary for build' do
- expect(page).to have_content "Build ##{@build.id}"
- end
-
- step 'I see build trace' do
- expect(page).to have_css '#build-trace'
- end
-
step 'I click artifacts download button' do
page.within('.artifacts') { click_link 'Download' }
end
- step 'download of build artifacts archive starts' do
- expect(page.response_headers['Content-Type']).to eq 'application/zip'
- expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary'
- end
-
step 'I click artifacts browse button' do
page.within('.artifacts') { click_link 'Browse' }
end
@@ -76,8 +63,8 @@ class Spinach::Features::ProjectBuilds < Spinach::FeatureSteps
end
end
- step 'I click download button for a file within build artifacts' do
- page.within('.tree-table') { first('.artifact-download').click }
+ step 'I click a link to file within build artifacts' do
+ page.within('.tree-table') { find_link('ci_artifacts.txt').click }
end
step 'download of a file extracted from build artifacts should start' do
diff --git a/features/steps/project/builds/permissions.rb b/features/steps/project/builds/permissions.rb
new file mode 100644
index 00000000000..6e9d6504fd5
--- /dev/null
+++ b/features/steps/project/builds/permissions.rb
@@ -0,0 +1,7 @@
+class Spinach::Features::ProjectBuildsPermissions < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedBuilds
+ include SharedPaths
+ include RepoHelpers
+end
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
new file mode 100644
index 00000000000..036bc0a499e
--- /dev/null
+++ b/features/steps/project/builds/summary.rb
@@ -0,0 +1,21 @@
+class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedBuilds
+ include RepoHelpers
+
+ step 'I see details of a build' do
+ expect(page).to have_content "Build ##{@build.id}"
+ end
+
+ step 'I see build trace' do
+ expect(page).to have_css '#build-trace'
+ end
+
+ step 'I see button to CI Lint' do
+ page.within('.controls') do
+ ci_lint_tool_link = page.find_link('CI Lint')
+ expect(ci_lint_tool_link[:href]).to eq ci_lint_path
+ end
+ end
+end
diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb
index a83d74e5946..92bf362879b 100644
--- a/features/steps/shared/builds.rb
+++ b/features/steps/shared/builds.rb
@@ -1,19 +1,23 @@
module SharedBuilds
include Spinach::DSL
- step 'CI is enabled' do
+ step 'project has CI enabled' do
@project.enable_ci
end
- step 'I have recent build for my project' do
+ 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
end
- step 'I visit recent build summary page' do
+ step 'I visit recent build details page' do
visit namespace_project_build_path(@project.namespace, @project, @build)
end
+ step 'I visit project builds page' do
+ visit namespace_project_builds_path(@project.namespace, @project)
+ end
+
step 'recent build has artifacts available' do
artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
archive = fixture_file_upload(artifacts, 'application/zip')
@@ -25,4 +29,13 @@ module SharedBuilds
gzip = fixture_file_upload(metadata, 'application/x-gzip')
@build.update_attributes(artifacts_metadata: gzip)
end
+
+ step 'download of build artifacts archive starts' do
+ expect(page.response_headers['Content-Type']).to eq 'application/zip'
+ expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary'
+ end
+
+ step 'I access artifacts download page' do
+ visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build)
+ end
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index d3501b5f5cb..d9c75d12238 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -7,6 +7,11 @@ module SharedProject
@project.team << [@user, :master]
end
+ step "project exists in some group namespace" do
+ @group = create(:group, name: 'some group')
+ @project = create(:project, namespace: @group)
+ end
+
# Create a specific project called "Shop"
step 'I own project "Shop"' do
@project = Project.find_by(name: "Shop")
@@ -98,6 +103,18 @@ module SharedProject
end
# ----------------------------------------
+ # Project permissions
+ # ----------------------------------------
+
+ step 'I am member of a project with a guest role' do
+ @project.team << [@user, Gitlab::Access::GUEST]
+ end
+
+ step 'I am member of a project with a reporter role' do
+ @project.team << [@user, Gitlab::Access::REPORTER]
+ end
+
+ # ----------------------------------------
# Visibility of archived project
# ----------------------------------------
@@ -229,5 +246,4 @@ module SharedProject
project ||= create(:empty_project, visibility, name: project_name, namespace: user.namespace)
project.team << [user, :master]
end
-
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 6d2380cf47d..3f528b9f7c0 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -264,6 +264,10 @@ module API
projects = projects.search(params[:search])
end
+ if params[:visibility].present?
+ projects = projects.search_by_visibility(params[:visibility])
+ end
+
projects.reorder(project_order_by => project_sort)
end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index fb87637b94f..690bbf97a89 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -20,7 +20,7 @@ module Ci
if build
update_runner_info
- present build, with: Entities::Build
+ present build, with: Entities::BuildDetails
else
not_found!
end
@@ -111,7 +111,7 @@ module Ci
build.artifacts_metadata = metadata
if build.save
- present(build, with: Entities::Build)
+ present(build, with: Entities::BuildDetails)
else
render_validation_error!(build)
end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index e4ac0545ea2..b25e0e573a8 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -16,10 +16,19 @@ module Ci
end
class Build < Grape::Entity
- expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
- :before_sha, :allow_git_fetch, :project_name
-
+ expose :id, :ref, :tag, :sha, :status
expose :name, :token, :stage
+ expose :project_id
+ expose :project_name
+ expose :artifacts_file, using: ArtifactFile, if: lambda { |build, opts| build.artifacts? }
+ end
+
+ class BuildDetails < Build
+ expose :commands
+ expose :repo_url
+ expose :before_sha
+ expose :allow_git_fetch
+ expose :token
expose :options do |model|
model.options
@@ -30,7 +39,7 @@ module Ci
end
expose :variables
- expose :artifacts_file, using: ArtifactFile
+ expose :depends_on_builds, using: Build
end
class Runner < Grape::Entity
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 7f938780ab1..ea054255820 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -39,7 +39,6 @@ module Gitlab
end
use_db && ActiveRecord::Base.connection.active? &&
- !ActiveRecord::Migrator.needs_migration? &&
ActiveRecord::Base.connection.table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index a2eb7a70bd2..a05ffeb9cd2 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -9,6 +9,7 @@ module Gitlab
delegate :namespace, :name_with_namespace, to: :project, prefix: :project
delegate :name, to: :author, prefix: :author
+ delegate :username, to: :author, prefix: :author
def initialize(notify, project_id, recipient, opts = {})
raise ArgumentError, 'Missing options: author_id, ref, action' unless
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index aef08c97d1d..e044f0ecc6d 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -30,28 +30,31 @@ module Gitlab
end
def find_by_uid_and_provider
- self.class.find_by_uid_and_provider(
- auth_hash.uid, auth_hash.provider)
+ self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider)
end
def find_by_email
- ::User.find_by(email: auth_hash.email.downcase)
+ ::User.find_by(email: auth_hash.email.downcase) if auth_hash.has_email?
end
def update_user_attributes
- return unless persisted?
+ if persisted?
+ if auth_hash.has_email?
+ gl_user.skip_reconfirmation!
+ gl_user.email = auth_hash.email
+ end
- gl_user.skip_reconfirmation!
- gl_user.email = auth_hash.email
+ # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
+ identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
+ identity ||= gl_user.identities.build(provider: auth_hash.provider)
- # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
- identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
- identity ||= gl_user.identities.build(provider: auth_hash.provider)
+ # For a new identity set extern_uid to the LDAP DN
+ # For an existing identity with matching email but changed DN, update the DN.
+ # For an existing identity with no change in DN, this line changes nothing.
+ identity.extern_uid = auth_hash.uid
+ end
- # For a new user set extern_uid to the LDAP DN
- # For an existing user with matching email but changed DN, update the DN.
- # For an existing user with no change in DN, this line changes nothing.
- identity.extern_uid = auth_hash.uid
+ gl_user.ldap_email = auth_hash.has_email?
gl_user
end
diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb
index ba31599432b..36e5c2670bb 100644
--- a/lib/gitlab/o_auth/auth_hash.rb
+++ b/lib/gitlab/o_auth/auth_hash.rb
@@ -32,6 +32,10 @@ module Gitlab
@password ||= Gitlab::Utils.force_utf8(Devise.friendly_token[0, 8].downcase)
end
+ def has_email?
+ get_info(:email).present?
+ end
+
private
def info
@@ -46,8 +50,8 @@ module Gitlab
def username_and_email
@username_and_email ||= begin
- username = get_info(:username) || get_info(:nickname)
- email = get_info(:email)
+ username = get_info(:username).presence || get_info(:nickname).presence
+ email = get_info(:email).presence
username ||= generate_username(email) if email
email ||= generate_temporarily_email(username) if username
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index f1a362f5303..d87a72f7ba3 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -111,7 +111,7 @@ module Gitlab
def block_after_signup?
if creating_linked_ldap_user?
ldap_config.block_auto_created_users
- else
+ else
Gitlab.config.omniauth.block_auto_created_users
end
end
@@ -135,15 +135,18 @@ module Gitlab
def user_attributes
# Give preference to LDAP for sensitive information when creating a linked account
if creating_linked_ldap_user?
- username = ldap_person.username
- email = ldap_person.email.first
- else
- username = auth_hash.username
- email = auth_hash.email
+ username = ldap_person.username.presence
+ email = ldap_person.email.first.presence
end
-
+
+ username ||= auth_hash.username
+ email ||= auth_hash.email
+
+ name = auth_hash.name
+ name = ::Namespace.clean_path(username) if name.strip.empty?
+
{
- name: auth_hash.name,
+ name: name,
username: ::Namespace.clean_path(username),
email: email,
password: auth_hash.password,
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index 8c63877e51c..d33b5b31e18 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -4,6 +4,9 @@ end
String.disable_colorization = true unless STDOUT.isatty
+# Prevent StateMachine warnings from outputting during a cron task
+StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
+
namespace :gitlab do
# Ask if the user wants to continue
diff --git a/spec/fixtures/ci_build_artifacts_metadata.gz b/spec/fixtures/ci_build_artifacts_metadata.gz
index fe9d4c8c661..e6d17e4595d 100644
--- a/spec/fixtures/ci_build_artifacts_metadata.gz
+++ b/spec/fixtures/ci_build_artifacts_metadata.gz
Binary files differ
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
new file mode 100644
index 00000000000..e58f2c80e95
--- /dev/null
+++ b/spec/initializers/settings_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../config/initializers/1_settings'
+
+describe Settings, lib: true do
+
+ describe '#host_without_www' do
+ context 'URL with protocol' do
+ it 'returns the host' do
+ expect(Settings.host_without_www('http://foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('http://www.foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com'
+ expect(Settings.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+
+ expect(Settings.host_without_www('https://foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('https://www.foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('https://secure.foo.com')).to eq 'secure.foo.com'
+ expect(Settings.host_without_www('https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'secure.gravatar.com'
+ end
+ end
+
+ context 'URL without protocol' do
+ it 'returns the host' do
+ expect(Settings.host_without_www('foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('www.foo.com')).to eq 'foo.com'
+ expect(Settings.host_without_www('secure.foo.com')).to eq 'secure.foo.com'
+ expect(Settings.host_without_www('www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+ end
+
+ context 'URL with user/port' do
+ it 'returns the host' do
+ expect(Settings.host_without_www('bob:pass@foo.com:8080')).to eq 'foo.com'
+ expect(Settings.host_without_www('bob:pass@www.foo.com:8080')).to eq 'foo.com'
+ expect(Settings.host_without_www('bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com'
+ expect(Settings.host_without_www('bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+
+ expect(Settings.host_without_www('http://bob:pass@foo.com:8080')).to eq 'foo.com'
+ expect(Settings.host_without_www('http://bob:pass@www.foo.com:8080')).to eq 'foo.com'
+ expect(Settings.host_without_www('http://bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com'
+ expect(Settings.host_without_www('http://bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+ end
+ end
+ end
+ end
+
+end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 1e755259dae..03199a2523e 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -37,7 +37,7 @@ describe Gitlab::LDAP::User, lib: true do
end
it "dont marks existing ldap user as changed" do
- create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
+ create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain', ldap_email: true)
expect(ldap_user.changed?).to be_falsey
end
end
@@ -110,6 +110,32 @@ describe Gitlab::LDAP::User, lib: true do
end
end
+ describe 'updating email' do
+ context "when LDAP sets an email" do
+ it "has a real email" do
+ expect(ldap_user.gl_user.email).to eq(info[:email])
+ end
+
+ it "has ldap_email set to true" do
+ expect(ldap_user.gl_user.ldap_email?).to be(true)
+ end
+ end
+
+ context "when LDAP doesn't set an email" do
+ before do
+ info.delete(:email)
+ end
+
+ it "has a temp email" do
+ expect(ldap_user.gl_user.temp_oauth_email?).to be(true)
+ end
+
+ it "has ldap_email set to false" do
+ expect(ldap_user.gl_user.ldap_email?).to be(false)
+ end
+ end
+ end
+
describe 'blocking' do
def configure_block(value)
allow_any_instance_of(Gitlab::LDAP::Config).
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 8f86c491d3f..7289e596ef3 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -40,14 +40,38 @@ describe Notify do
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}>/
- is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
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
@@ -56,7 +80,6 @@ describe Notify 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}>/
- is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
end
end
@@ -656,6 +679,8 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -685,6 +710,8 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -713,6 +740,8 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -737,6 +766,8 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -765,6 +796,8 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -871,6 +904,8 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'an email with X-GitLab headers containing project details'
+ it_behaves_like 'an email that contains a header with author username'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -904,6 +939,15 @@ describe Notify do
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
@@ -918,6 +962,15 @@ describe Notify do
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
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index f9be8fcbcfe..4799bbaa57c 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe AbuseReport, type: :model do
it { is_expected.to validate_presence_of(:reporter) }
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:message) }
- it { is_expected.to validate_uniqueness_of(:user_id) }
+ it { is_expected.to validate_uniqueness_of(:user_id).with_message('has already been reported') }
end
describe '#remove_user' do
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 0e13456723d..d12b9e65c82 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -426,6 +426,30 @@ describe Ci::Build, models: true do
it { is_expected.to include(project.web_url[7..-1]) }
end
+ describe :depends_on_builds do
+ let!(:build) { FactoryGirl.create :ci_build, commit: commit, name: 'build', stage_idx: 0, stage: 'build' }
+ let!(:rspec_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rspec', stage_idx: 1, stage: 'test' }
+ let!(:rubocop_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rubocop', stage_idx: 1, stage: 'test' }
+ let!(:staging) { FactoryGirl.create :ci_build, commit: commit, name: 'staging', stage_idx: 2, stage: 'deploy' }
+
+ it 'to have no dependents if this is first build' do
+ expect(build.depends_on_builds).to be_empty
+ end
+
+ it 'to have one dependent if this is test' do
+ expect(rspec_test.depends_on_builds.map(&:id)).to contain_exactly(build.id)
+ end
+
+ it 'to have all builds from build and test stage if this is last' do
+ expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, rspec_test.id, rubocop_test.id)
+ end
+
+ it 'to have retried builds instead the original ones' do
+ retried_rspec = Ci::Build.retry(rspec_test)
+ expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, retried_rspec.id, rubocop_test.id)
+ end
+ end
+
def create_mr(build, commit, factory: :merge_request, created_at: Time.now)
FactoryGirl.create(factory,
source_project_id: commit.gl_project_id,
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 6f4c336b66c..2a310f3834d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -90,6 +90,29 @@ describe API::API, api: true do
end
end
+ context 'and using the visibility filter' do
+ it 'should filter based on private visibility param' do
+ get api('/projects', user), { visibility: 'private' }
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
+ end
+
+ it 'should filter based on internal visibility param' do
+ get api('/projects', user), { visibility: 'internal' }
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
+ end
+
+ it 'should filter based on public visibility param' do
+ get api('/projects', user), { visibility: 'public' }
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count)
+ end
+ end
+
context 'and using sorting' do
before do
project2
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 648ea0d5f50..eec927102aa 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -101,6 +101,18 @@ describe Ci::API::API do
{ "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
])
end
+
+ it "returns dependent builds" do
+ commit = FactoryGirl.create(:ci_commit, project: project)
+ commit.create_builds('master', false, nil, nil)
+ commit.builds.where(stage: 'test').each(&:success)
+
+ post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
+
+ expect(response.status).to eq(201)
+ expect(json_response["depends_on_builds"].count).to eq(2)
+ expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec")
+ end
end
describe "PUT /builds/:id" do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 6d219f35895..2d0b5df4224 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -227,7 +227,7 @@ describe NotificationService, services: true do
end
describe :reassigned_issue do
- it 'should email new assignee' do
+ it 'emails new assignee' do
notification.reassigned_issue(issue, @u_disabled)
should_email(issue.assignee)
@@ -238,6 +238,62 @@ describe NotificationService, services: true do
should_not_email(@u_participating)
should_not_email(@u_disabled)
end
+
+ it 'emails previous assignee even if he has the "on mention" notif level' do
+ issue.update_attribute(:assignee, @u_mentioned)
+ issue.update_attributes(assignee: @u_watcher)
+ notification.reassigned_issue(issue, @u_disabled)
+
+ should_email(@u_mentioned)
+ should_email(@u_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ end
+
+ it 'emails new assignee even if he has the "on mention" notif level' do
+ issue.update_attributes(assignee: @u_mentioned)
+ notification.reassigned_issue(issue, @u_disabled)
+
+ expect(issue.assignee).to be @u_mentioned
+ should_email(issue.assignee)
+ should_email(@u_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ end
+
+ it 'emails new assignee' do
+ issue.update_attribute(:assignee, @u_mentioned)
+ notification.reassigned_issue(issue, @u_disabled)
+
+ expect(issue.assignee).to be @u_mentioned
+ should_email(issue.assignee)
+ should_email(@u_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ end
+
+ it 'does not email new assignee if they are the current user' do
+ issue.update_attribute(:assignee, @u_mentioned)
+ notification.reassigned_issue(issue, @u_mentioned)
+
+ expect(issue.assignee).to be @u_mentioned
+ should_email(@u_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_not_email(issue.assignee)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ end
end
describe :close_issue do
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 5d0b18558b1..e43903dbd3c 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -32,6 +32,7 @@ describe Projects::CreateService, services: true do
it { expect(@project).to be_valid }
it { expect(@project.owner).to eq(@user) }
+ it { expect(@project.team.masters).to include(@user) }
it { expect(@project.namespace).to eq(@user.namespace) }
end
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index 3482145404e..a5b256bd3ec 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -36,8 +36,8 @@ staging:
script: "cap deploy stating"
type: deploy
tags:
- - capistrano
- - debian
+ - ruby
+ - mysql
except:
- stable
@@ -47,8 +47,8 @@ production:
- cap deploy production
- cap notify
tags:
- - capistrano
- - debian
+ - ruby
+ - mysql
only:
- master
- /^deploy-.*$/