summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml12
-rw-r--r--CHANGELOG26
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/ci/build.coffee21
-rw-r--r--app/assets/javascripts/user_tabs.js.coffee9
-rw-r--r--app/assets/stylesheets/framework/common.scss2
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss5
-rw-r--r--app/assets/stylesheets/framework/header.scss4
-rw-r--r--app/assets/stylesheets/framework/nav.scss19
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss2
-rw-r--r--app/controllers/admin/application_settings_controller.rb15
-rw-r--r--app/controllers/admin/health_check_controller.rb5
-rw-r--r--app/controllers/admin/runners_controller.rb2
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/concerns/creates_commit.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/dashboard_controller.rb2
-rw-r--r--app/controllers/health_check_controller.rb22
-rw-r--r--app/controllers/projects/builds_controller.rb8
-rw-r--r--app/controllers/projects/hooks_controller.rb6
-rw-r--r--app/controllers/projects/runners_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb2
-rw-r--r--app/controllers/users_controller.rb22
-rw-r--r--app/helpers/application_settings_helper.rb14
-rw-r--r--app/helpers/auth_helper.rb10
-rw-r--r--app/helpers/events_helper.rb9
-rw-r--r--app/helpers/nav_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/helpers/search_helper.rb2
-rw-r--r--app/helpers/selects_helper.rb2
-rw-r--r--app/mailers/emails/projects.rb5
-rw-r--r--app/models/application_setting.rb18
-rw-r--r--app/models/ci/build.rb10
-rw-r--r--app/models/ci/runner.rb3
-rw-r--r--app/models/concerns/subscribable.rb6
-rw-r--r--app/models/hooks/web_hook.rb2
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/project_services/external_wiki_service.rb2
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/models/repository.rb6
-rw-r--r--app/models/user.rb5
-rw-r--r--app/services/merge_requests/build_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/views/admin/application_settings/_form.html.haml7
-rw-r--r--app/views/admin/builds/index.html.haml2
-rw-r--r--app/views/admin/health_check/show.html.haml49
-rw-r--r--app/views/admin/runners/show.html.haml22
-rw-r--r--app/views/devise/sessions/new.html.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml2
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/header/_default.html.haml5
-rw-r--r--app/views/layouts/nav/_admin.html.haml5
-rw-r--r--app/views/layouts/nav/_group_settings.html.haml2
-rw-r--r--app/views/projects/builds/index.html.haml2
-rw-r--r--app/views/projects/builds/show.html.haml7
-rw-r--r--app/views/projects/commits/_commits.html.haml2
-rw-r--r--app/views/projects/runners/_form.html.haml25
-rw-r--r--app/views/projects/runners/edit.html.haml26
-rw-r--r--app/views/users/show.html.haml6
-rw-r--r--app/workers/emails_on_push_worker.rb25
-rw-r--r--config/boot.rb2
-rw-r--r--config/environments/test.rb2
-rw-r--r--config/initializers/1_settings.rb18
-rw-r--r--config/initializers/carrierwave.rb4
-rw-r--r--config/initializers/devise.rb2
-rw-r--r--config/initializers/health_check.rb3
-rw-r--r--config/initializers/monkey_patch.rb48
-rw-r--r--config/routes.rb40
-rw-r--r--db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb5
-rw-r--r--db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb5
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/labels.md70
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/integration/cas.md19
-rw-r--r--doc/integration/img/enabled-oauth-sign-in-sources.pngbin0 -> 49081 bytes
-rw-r--r--doc/integration/omniauth.md15
-rw-r--r--doc/update/8.7-to-8.8.md154
-rw-r--r--doc/web_hooks/web_hooks.md13
-rw-r--r--features/steps/dashboard/issues.rb2
-rw-r--r--features/steps/dashboard/merge_requests.rb2
-rw-r--r--features/steps/profile/profile.rb2
-rw-r--r--features/steps/project/hooks.rb2
-rw-r--r--features/steps/project/team_management.rb2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/helpers.rb13
-rw-r--r--lib/api/issues.rb39
-rw-r--r--lib/api/labels.rb6
-rw-r--r--lib/api/merge_requests.rb36
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/subscriptions.rb60
-rw-r--r--lib/banzai/filter/wiki_link_filter.rb11
-rw-r--r--lib/ci/ansi2html.rb83
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/database.rb4
-rw-r--r--lib/gitlab/diff/parser.rb2
-rw-r--r--lib/gitlab/email/message/repository_push.rb4
-rw-r--r--lib/gitlab/email/reply_parser.rb2
-rw-r--r--lib/gitlab/markup_helper.rb2
-rw-r--r--lib/gitlab/metrics/instrumentation.rb2
-rw-r--r--lib/gitlab/redis.rb8
-rw-r--r--spec/config/mail_room_spec.rb2
-rw-r--r--spec/controllers/health_check_controller_spec.rb105
-rw-r--r--spec/controllers/users_controller_spec.rb22
-rw-r--r--spec/features/admin/admin_builds_spec.rb1
-rw-r--r--spec/features/admin/admin_health_check_spec.rb55
-rw-r--r--spec/features/builds_spec.rb1
-rw-r--r--spec/helpers/auth_helper_spec.rb47
-rw-r--r--spec/helpers/events_helper_spec.rb95
-rw-r--r--spec/lib/banzai/filter/wiki_link_filter_spec.rb85
-rw-r--r--spec/lib/ci/ansi2html_spec.rb111
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb4
-rw-r--r--spec/lib/gitlab/email/message/repository_push_spec.rb2
-rw-r--r--spec/lib/gitlab/lfs/lfs_router_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/instrumentation_spec.rb6
-rw-r--r--spec/mailers/notify_spec.rb36
-rw-r--r--spec/models/application_setting_spec.rb9
-rw-r--r--spec/models/commit_spec.rb2
-rw-r--r--spec/models/concerns/subscribable_spec.rb10
-rw-r--r--spec/models/hooks/service_hook_spec.rb4
-rw-r--r--spec/models/hooks/system_hook_spec.rb20
-rw-r--r--spec/models/hooks/web_hook_spec.rb4
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb4
-rw-r--r--spec/models/user_spec.rb19
-rw-r--r--spec/requests/api/builds_spec.rb4
-rw-r--r--spec/requests/api/group_members_spec.rb10
-rw-r--r--spec/requests/api/issues_spec.rb12
-rw-r--r--spec/requests/api/labels_spec.rb82
-rw-r--r--spec/requests/api/projects_spec.rb21
-rw-r--r--spec/requests/ci/api/builds_spec.rb4
-rw-r--r--spec/routing/admin_routing_spec.rb7
-rw-r--r--spec/routing/routing_spec.rb51
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/destroy_service_spec.rb8
-rw-r--r--spec/support/jira_service_helper.rb10
-rw-r--r--spec/support/stub_gitlab_calls.rb20
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb65
-rwxr-xr-xvendor/assets/javascripts/jquery.scrollTo.js210
143 files changed, 1825 insertions, 547 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 9f179efa3ce..562197300b4 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -276,7 +276,7 @@ Style/IdenticalConditionalBranches:
Enabled: false
# Checks the indentation of the first line of the right-hand-side of a
-# multi-line assignment.
+# multi-line assignment.
Style/IndentAssignment:
Enabled: false
@@ -531,7 +531,7 @@ Style/SpaceAroundKeyword:
# Use a single space around operators.
Style/SpaceAroundOperators:
- Enabled: false
+ Enabled: true
# Checks that the left block brace has or doesn't have space before it.
Style/SpaceBeforeBlockBraces:
@@ -770,7 +770,7 @@ Lint/DefEndAlignment:
# Check for deprecated class method calls.
Lint/DeprecatedClassMethods:
- Enabled: false
+ Enabled: true
# Check for duplicate method definitions.
Lint/DuplicateMethods:
@@ -937,10 +937,9 @@ Lint/Void:
##################### Performance ############################
-# TODO: Enable Casecmp Cop.
# Use `casecmp` rather than `downcase ==`.
Performance/Casecmp:
- Enabled: false
+ Enabled: true
# TODO: Enable DoubleStartEndWith Cop.
# Use `str.{start,end}_with?(x, ..., y, ...)` instead of
@@ -990,11 +989,12 @@ Performance/RedundantSortBy:
# string.
Performance/StartWith:
Enabled: false
+
# Use `tr` instead of `gsub` when you are replacing the same number of
# characters. Use `delete` instead of `gsub` when you are deleting
# characters.
Performance/StringReplacement:
- Enabled: false
+ Enabled: true
# TODO: Enable TimesMap Cop.
# Checks for `.times.map` calls.
diff --git a/CHANGELOG b/CHANGELOG
index 54c79551b1d..30d4955c9f3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.8.0 (unreleased)
+ - Snippets tab under user profile. !4001 (Long Nguyen)
- Fix error when using link to uploads in global snippets
- Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- Use a case-insensitive comparison in sanitizing URI schemes
@@ -13,6 +14,7 @@ v 8.8.0 (unreleased)
- Reduce delay in destroying a project from 1-minute to immediately
- Make build status canceled if any of the jobs was canceled and none failed
- Upgrade Sidekiq to 4.1.2
+ - Added /health_check endpoint for checking service status
- Sanitize repo paths in new project error message
- Bump mail_room to 0.7.0 to fix stuck IDLE connections
- Remove future dates from contribution calendar graph.
@@ -21,6 +23,7 @@ v 8.8.0 (unreleased)
- Fix error when visiting commit builds page before build was updated
- Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
- Update SVG sanitizer to conform to SVG 1.1
+ - Speed up push emails with multiple recipients by only generating the email once
- Updated search UI
- Display informative message when new milestone is created
- Sanitize milestones and labels titles
@@ -30,10 +33,12 @@ v 8.8.0 (unreleased)
- Backport GitHub Enterprise import support from EE
- Create tags using Rugged for performance reasons. !3745
- API: Expose Issue#user_notes_count. !3126 (Anton Popov)
+ - Don't show forks button when user can't view forks
- Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
- Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
- Add eager load paths to help prevent dependency load issues in Sidekiq workers. !3724
- Added multiple colors for labels in dropdowns when dups happen.
+ - Always group commits by server timezone, not commit timestamp
- Improve description for the Two-factor Authentication sign-in screen. (Connor Shea)
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
@@ -41,11 +46,26 @@ v 8.8.0 (unreleased)
- Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi)
- Fix adding a todo for private group members (Ahmad Sherif)
- Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
+ - Total method execution timings are no longer tracked
+ - Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga)
+ - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
+ - Hide left sidebar on phone screens to give more space for content
+ - Redesign navigation for profile and group pages
+
+v 8.7.5
+ - Fix relative links in wiki pages. !4050
+ - Fix always showing build notification message when switching between merge requests !4086
+ - Fix an issue when filtering merge requests with more than one label. !3886
+ - Fix short note for the default scope on build page (Takuya Noguchi)
v 8.7.4
- - Fix always showing build notification message when switching between merge requests
- - Links for Redmine issue references are generated correctly again (Benedikt Huss)
- - Fix an issue when filtering merge requests with more than one label. !3886
+ - Links for Redmine issue references are generated correctly again !4048 (Benedikt Huss)
+ - Fix setting trusted proxies !3970
+ - Fix BitBucket importer bug when throwing exceptions !3941
+ - Use sign out path only if not empty !3989
+ - Running rake gitlab:db:drop_tables now drops tables with cascade !4020
+ - Running rake gitlab:db:drop_tables uses "IF EXISTS" as a precaution !4100
+ - Use a case-insensitive comparison in sanitizing URI schemes
v 8.7.3
- Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
diff --git a/Gemfile b/Gemfile
index 8de96025100..6949c16e966 100644
--- a/Gemfile
+++ b/Gemfile
@@ -218,7 +218,6 @@ gem 'gitlab_emoji', '~> 0.3.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
-gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 5.0.0'
gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.3.0'
@@ -332,3 +331,6 @@ gem 'oauth2', '~> 1.0.0'
# Soft deletion
gem "paranoia", "~> 2.0"
+
+# Health check
+gem 'health_check', '~> 1.5.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 45d6de18f26..4b51bf58bba 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -401,6 +401,8 @@ GEM
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashie (3.4.3)
+ health_check (1.5.1)
+ rails (>= 2.3.0)
highline (1.7.8)
hipchat (1.5.2)
httparty
@@ -431,8 +433,6 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- jquery-scrollto-rails (1.4.3)
- railties (> 3.1, < 5.0)
jquery-turbolinks (2.1.0)
railties (>= 3.1.0)
turbolinks
@@ -945,13 +945,13 @@ DEPENDENCIES
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
haml-rails (~> 0.9.0)
+ health_check (~> 1.5.1)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
influxdb (~> 0.2)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.1.0)
- jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3)
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee
index 7afe8bf79e2..fca0c3bae5c 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/ci/build.coffee
@@ -1,9 +1,12 @@
class CiBuild
@interval: null
+ @state: null
- constructor: (build_url, build_status) ->
+ constructor: (build_url, build_status, build_state) ->
clearInterval(CiBuild.interval)
+ @state = build_state
+
@initScrollButtonAffix()
if build_status == "running" || build_status == "pending"
@@ -26,14 +29,18 @@ class CiBuild
CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is build_url
$.ajax
- url: build_url
+ url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
dataType: "json"
- success: (build) =>
- if build.status == "running"
- $('#build-trace code').html build.trace_html
- $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
+ success: (log) =>
+ @state = log.state
+ if log.status is "running"
+ if log.append
+ $('.fa-refresh').before log.html
+ else
+ $('#build-trace code').html log.html
+ $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
@checkAutoscroll()
- else if build.status != build_status
+ else if log.status isnt build_status
Turbolinks.visit build_url
, 4000
diff --git a/app/assets/javascripts/user_tabs.js.coffee b/app/assets/javascripts/user_tabs.js.coffee
index c2aeffe2381..70614396a4e 100644
--- a/app/assets/javascripts/user_tabs.js.coffee
+++ b/app/assets/javascripts/user_tabs.js.coffee
@@ -26,6 +26,10 @@
# Personal projects
# </a>
# </li>
+# <li class="snippets-tab">
+# <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
+# </a>
+# </li>
# </ul>
#
# <div class="tab-content">
@@ -41,6 +45,9 @@
# <div class="tab-pane" id="projects">
# Projects content
# </div>
+# <div class="tab-pane" id="snippets">
+# Snippets content
+# </div>
# </div>
#
# <div class="loading-status">
@@ -100,7 +107,7 @@ class @UserTabs
if action is 'activity'
@loadActivities(source)
- if action in ['groups', 'contributed', 'projects']
+ if action in ['groups', 'contributed', 'projects', 'snippets']
@loadTab(source, action)
loadTab: (source, action) ->
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 3386523dbf7..f8aecd0558d 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -289,7 +289,7 @@ table {
text-shadow: none;
@media (min-width: $screen-sm-min) {
- margin-top: 11px;
+ margin-top: 8px;
}
}
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index dc4668877f2..51a17d1469e 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -9,9 +9,6 @@
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar {
.header-logo {
- background-color: $color;
- border-color: $color;
-
a {
color: $color-light;
@@ -21,7 +18,7 @@
}
&:hover {
- background-color: $color-darker;
+ background-color: $color-dark;
a {
color: #fff;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 8190a97ed58..0da96c4017d 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -6,12 +6,12 @@ header {
transition-duration: .3s;
&.navbar-empty {
- height: 58px;
+ height: $header-height;
background: #fff;
border-bottom: 1px solid $btn-gray-hover;
.center-logo {
- margin: 11px 0;
+ margin: 8px 0;
text-align: center;
#tanuki-logo, img {
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index f2d7dc625e0..a81fcb1c6b3 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -209,6 +209,15 @@
float: right;
padding: 7px 0 0;
+ @media (max-width: $screen-xs-min) {
+ float: none;
+ padding: 0 9px;
+
+ .dropdown-new {
+ width: 100%;
+ }
+ }
+
i {
color: $layout-link-gray;
}
@@ -225,6 +234,10 @@
.dropdown {
margin-left: 7px;
+
+ @media (max-width: $screen-xs-min) {
+ margin-left: 0;
+ }
}
}
@@ -260,4 +273,10 @@
.page-with-layout-nav {
margin-top: 50px;
+
+ &.controls-dropdown-visible {
+ @media (max-width: $screen-xs-min) {
+ margin-top: 96px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index bd91f51708c..f90d7a806d3 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -312,7 +312,7 @@
}
.nav-sidebar li a {
- width: 230px;
+ width: $sidebar_width;
&.back-link {
i {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 84e74db06b0..5fa4c266607 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -2,7 +2,7 @@
* Layout
*/
$sidebar_collapsed_width: 62px;
-$sidebar_width: 230px;
+$sidebar_width: 220px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 258px;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 1cf3023ecc9..d06086a581b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -125,7 +125,7 @@
.right-sidebar {
position: fixed;
- top: 58px;
+ top: $header-height;
bottom: 0;
right: 0;
z-index: 10;
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index ec22548ddeb..8c973f0e4a8 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -19,6 +19,12 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
redirect_to admin_runners_path
end
+ def reset_health_check_token
+ @application_setting.reset_health_check_access_token!
+ flash[:notice] = 'New health check access token has been generated!'
+ redirect_to :back
+ end
+
def clear_repository_check_states
RepositoryCheck::ClearWorker.perform_async
@@ -53,6 +59,12 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
end
+ enabled_oauth_sign_in_sources = params[:application_setting].delete(:enabled_oauth_sign_in_sources)
+
+ params[:application_setting][:disabled_oauth_sign_in_sources] =
+ AuthHelper.button_based_providers.map(&:to_s) -
+ Array(enabled_oauth_sign_in_sources)
+
params.require(:application_setting).permit(
:default_projects_limit,
:default_branch_protection,
@@ -95,7 +107,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:repository_checks_enabled,
:metrics_packet_size,
restricted_visibility_levels: [],
- import_sources: []
+ import_sources: [],
+ disabled_oauth_sign_in_sources: []
)
end
end
diff --git a/app/controllers/admin/health_check_controller.rb b/app/controllers/admin/health_check_controller.rb
new file mode 100644
index 00000000000..241c7be0ea1
--- /dev/null
+++ b/app/controllers/admin/health_check_controller.rb
@@ -0,0 +1,5 @@
+class Admin::HealthCheckController < Admin::ApplicationController
+ def show
+ @errors = HealthCheck::Utils.process_checks('standard')
+ end
+end
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index a701d49b844..8b8a7320072 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -58,6 +58,6 @@ class Admin::RunnersController < Admin::ApplicationController
end
def runner_params
- params.require(:runner).permit(:token, :description, :tag_list, :active)
+ params.require(:runner).permit(Ci::Runner::FORM_EDITABLE)
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 17b3f49aed1..1429ee40bb7 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -176,7 +176,7 @@ class ApplicationController < ActionController::Base
end
def check_password_expiration
- if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
+ if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
redirect_to new_profile_password_path and return
end
end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 787416c17ab..dacb5679dd3 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -122,7 +122,7 @@ module CreatesCommit
# Merge request from fork to this project
@mr_source_project = @tree_edit_project
@mr_target_project = @project
- @mr_target_branch ||= @ref
+ @mr_target_branch ||= @ref
end
end
end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 71acc244a91..c08eb811532 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -28,7 +28,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
def starred
- @projects = current_user.starred_projects.sorted_by_activity
+ @projects = current_user.viewable_starred_projects.sorted_by_activity
@projects = filter_projects(@projects)
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort])
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 1dce4a21729..4dda4e51f6a 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -25,7 +25,7 @@ class DashboardController < Dashboard::ApplicationController
def load_events
projects =
if params[:filter] == "starred"
- current_user.starred_projects
+ current_user.viewable_starred_projects
else
current_user.authorized_projects
end
diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb
new file mode 100644
index 00000000000..037da7d2bce
--- /dev/null
+++ b/app/controllers/health_check_controller.rb
@@ -0,0 +1,22 @@
+class HealthCheckController < HealthCheck::HealthCheckController
+ before_action :validate_health_check_access!
+
+ private
+
+ def validate_health_check_access!
+ render_404 unless token_valid?
+ end
+
+ def token_valid?
+ token = params[:token].presence || request.headers['TOKEN']
+ token.present? &&
+ ActiveSupport::SecurityUtils.variable_size_secure_compare(
+ token,
+ current_application_settings.health_check_access_token
+ )
+ end
+
+ def render_404
+ render file: Rails.root.join('public', '404'), layout: false, status: '404'
+ end
+end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index b8b9e78427d..bb1f6c5e980 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -38,6 +38,14 @@ class Projects::BuildsController < Projects::ApplicationController
end
end
+ def trace
+ respond_to do |format|
+ format.json do
+ render json: @build.trace_with_state(params[:state]).merge!(id: @build.id, status: @build.status)
+ end
+ end
+ end
+
def retry
unless @build.retryable?
return render_404
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index dfa9bd259e8..47524b1cf0b 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -27,8 +27,10 @@ class Projects::HooksController < Projects::ApplicationController
if !@project.empty_repo?
status, message = TestHookService.new.execute(hook, current_user)
- if status
- flash[:notice] = 'Hook successfully executed.'
+ if status && status >= 200 && status < 400
+ flash[:notice] = "Hook executed successfully: HTTP #{status}"
+ elsif status
+ flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}"
else
flash[:alert] = "Hook execution failed: #{message}"
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 0dd2d6a99be..3a9d67aff64 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -64,6 +64,6 @@ class Projects::RunnersController < Projects::ApplicationController
end
def runner_params
- params.require(:runner).permit(:description, :tag_list, :active)
+ params.require(:runner).permit(Ci::Runner::FORM_EDITABLE)
end
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 2daceed039b..2a17c1f34db 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -10,7 +10,7 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy]
- skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw]
+ skip_before_action :authenticate_user!, only: [:index, :show, :raw]
layout 'snippets'
respond_to :html
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2ae180c8a12..799421c185b 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -58,6 +58,19 @@ class UsersController < ApplicationController
end
end
+ def snippets
+ load_snippets
+
+ respond_to do |format|
+ format.html { render 'show' }
+ format.json do
+ render json: {
+ html: view_to_html_string("snippets/_snippets", collection: @snippets)
+ }
+ end
+ end
+ end
+
def calendar
calendar = contributions_calendar
@timestamps = calendar.timestamps
@@ -116,6 +129,15 @@ class UsersController < ApplicationController
@groups = JoinedGroupsFinder.new(user).execute(current_user)
end
+ def load_snippets
+ @snippets = SnippetsFinder.new.execute(
+ current_user,
+ filter: :by_user,
+ user: user,
+ scope: params[:scope]
+ ).page(params[:page])
+ end
+
def projects_for_current_user
ProjectsFinder.new.execute(current_user)
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 914b0ef6042..03080d25931 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -60,4 +60,18 @@ module ApplicationSettingsHelper
end
end
end
+
+ def oauth_providers_checkboxes
+ button_based_providers.map do |source|
+ disabled = current_application_settings.disabled_oauth_sign_in_sources.include?(source.to_s)
+ css_class = 'btn'
+ css_class << ' active' unless disabled
+ checkbox_name = 'application_setting[enabled_oauth_sign_in_sources][]'
+
+ label_tag(checkbox_name, class: css_class) do
+ check_box_tag(checkbox_name, source, !disabled,
+ autocomplete: 'off') + Gitlab::OAuth::Provider.label_for(source)
+ end
+ end
+ end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index b4f80fd9b3e..b05fa0a14d6 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -38,6 +38,16 @@ module AuthHelper
auth_providers.reject { |provider| form_based_provider?(provider) }
end
+ def enabled_button_based_providers
+ disabled_providers = current_application_settings.disabled_oauth_sign_in_sources || []
+
+ button_based_providers.map(&:to_s) - disabled_providers
+ end
+
+ def button_based_providers_enabled?
+ enabled_button_based_providers.any?
+ end
+
def provider_image_tag(provider, size = 64)
label = label_for_provider(provider)
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 592bad8ba24..0bf328e7d19 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -39,15 +39,6 @@ module EventsHelper
end
end
- def icon_for_event
- {
- EventFilter.push => 'upload',
- EventFilter.merged => 'check-square-o',
- EventFilter.comments => 'comments',
- EventFilter.team => 'user',
- }
- end
-
def event_preposition(event)
if event.push? || event.commented? || event.target
"at"
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 3aa41030453..fbb799eecd3 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -43,4 +43,12 @@ module NavHelper
class_name += " with-horizontal-nav" if defined?(nav) && nav
class_name
end
+
+ def layout_nav_class
+ "page-with-layout-nav" if defined?(nav) && nav
+ end
+
+ def layout_dropdown_class
+ "controls-dropdown-visible" if current_user
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 85f8854d2eb..e1ab78df69e 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -138,10 +138,10 @@ module ProjectsHelper
private
def get_project_nav_tabs(project, current_user)
- nav_tabs = [:home, :forks]
+ nav_tabs = [:home]
if !project.empty_repo? && can?(current_user, :download_code, project)
- nav_tabs << [:files, :commits, :network, :graphs]
+ nav_tabs << [:files, :commits, :network, :graphs, :forks]
end
if project.repo_exists? && can?(current_user, :read_merge_request, project)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 24c4c098c65..d2f94d4ae6f 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -59,7 +59,7 @@ module SearchHelper
# Autocomplete results for the current project, if it's defined
def project_autocomplete
if @project && @project.repository.exists? && @project.repository.root_ref
- ref = @ref || @project.repository.root_ref
+ ref = @ref || @project.repository.root_ref
[
{ category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index e951a87a212..bb395e37884 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -18,7 +18,7 @@ module SelectsHelper
first_user: first_user,
current_user: opts[:current_user] || false,
"push-code-to-protected-branches" => opts[:push_code_to_protected_branches],
- author_id: opts[:author_id] || ''
+ author_id: opts[:author_id] || ''
}
}
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 377c2999d6c..5489283432b 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -59,9 +59,9 @@ module Emails
subject: subject("Project was moved"))
end
- def repository_push_email(project_id, recipient, opts = {})
+ def repository_push_email(project_id, opts = {})
@message =
- Gitlab::Email::Message::RepositoryPush.new(self, project_id, recipient, opts)
+ Gitlab::Email::Message::RepositoryPush.new(self, project_id, opts)
# used in notify layout
@target_url = @message.target_url
@@ -72,7 +72,6 @@ module Emails
mail(from: sender(@message.author_id, @message.send_from_committer_email?),
reply_to: @message.reply_to,
- to: @message.recipient,
subject: @message.subject)
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 7039db2d41e..1a10768655f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1,11 +1,13 @@
class ApplicationSetting < ActiveRecord::Base
include TokenAuthenticatable
add_authentication_token_field :runners_registration_token
+ add_authentication_token_field :health_check_access_token
CACHE_KEY = 'application_setting.last'
serialize :restricted_visibility_levels
serialize :import_sources
+ serialize :disabled_oauth_sign_in_sources
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
@@ -69,7 +71,18 @@ class ApplicationSetting < ActiveRecord::Base
end
end
+ validates_each :disabled_oauth_sign_in_sources do |record, attr, value|
+ unless value.nil?
+ value.each do |source|
+ unless Devise.omniauth_providers.include?(source.to_sym)
+ record.errors.add(attr, "'#{source}' is not an OAuth sign-in source")
+ end
+ end
+ end
+ end
+
before_save :ensure_runners_registration_token
+ before_save :ensure_health_check_access_token
after_commit do
Rails.cache.write(CACHE_KEY, self)
@@ -107,6 +120,7 @@ class ApplicationSetting < ActiveRecord::Base
recaptcha_enabled: false,
akismet_enabled: false,
repository_checks_enabled: true,
+ disabled_oauth_sign_in_sources: []
)
end
@@ -133,4 +147,8 @@ class ApplicationSetting < ActiveRecord::Base
def runners_registration_token
ensure_runners_registration_token!
end
+
+ def health_check_access_token
+ ensure_health_check_access_token!
+ end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index c9506dd3057..92327bdb08d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -95,8 +95,12 @@ module Ci
end
def trace_html
- html = Ci::Ansi2html::convert(trace) if trace.present?
- html || ''
+ trace_with_state[:html] || ''
+ end
+
+ def trace_with_state(state = nil)
+ trace_with_state = Ci::Ansi2html::convert(trace, state) if trace.present?
+ trace_with_state || {}
end
def timeout
@@ -201,7 +205,7 @@ module Ci
end
def recreate_trace_dir
- unless Dir.exists?(dir_to_trace)
+ unless Dir.exist?(dir_to_trace)
FileUtils.mkdir_p(dir_to_trace)
end
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 0cdb64d6c33..819064f99bb 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -3,7 +3,8 @@ module Ci
extend Ci::Model
LAST_CONTACT_TIME = 5.minutes.ago
- AVAILABLE_SCOPES = ['specific', 'shared', 'active', 'paused', 'online']
+ AVAILABLE_SCOPES = %w[specific shared active paused online]
+ FORM_EDITABLE = %i[description tag_list active]
has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index d5a881b2445..083257f1005 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -36,6 +36,12 @@ module Subscribable
update(subscribed: !subscribed?(user))
end
+ def subscribe(user)
+ subscriptions.
+ find_or_initialize_by(user_id: user.id).
+ update(subscribed: true)
+ end
+
def unsubscribe(user)
subscriptions.
find_or_initialize_by(user_id: user.id).
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index fde05f729dc..8b87b6c3d64 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -38,7 +38,7 @@ class WebHook < ActiveRecord::Base
basic_auth: auth)
end
- [(response.code >= 200 && response.code < 300), ActionView::Base.full_sanitizer.sanitize(response.to_s)]
+ [response.code, response.to_s]
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
[false, e.to_s]
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 5460935366c..5c5e6007aa0 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -514,7 +514,7 @@ class MergeRequest < ActiveRecord::Base
end
def ref_is_fetched?
- File.exists?(File.join(project.repository.path_to_repo, ref_path))
+ File.exist?(File.join(project.repository.path_to_repo, ref_path))
end
def ensure_ref_fetched
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index f042bfc5210..d7b6e505191 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -25,7 +25,7 @@ class ExternalWikiService < Service
def execute(_data)
@response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil
- if @response !=200
+ if @response != 200
nil
end
end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 7c1a61bb0bf..060ed9b44ec 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -113,7 +113,7 @@ class ProjectWiki
end
def page_title_and_dir(title)
- title_array = title.split("/")
+ title_array = title.split("/")
title = title_array.pop
[title, title_array.join("/")]
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a4b42d7226d..0eff74320f3 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -453,7 +453,7 @@ class Repository
def version
cache.fetch(:version) do
tree(:head).blobs.find do |file|
- file.name.downcase == 'version'
+ file.name.casecmp('version').zero?
end
end
end
@@ -795,7 +795,7 @@ class Repository
def check_revert_content(commit, base_branch)
source_sha = find_branch(base_branch).target
args = [commit.id, source_sha]
- args << { mainline: 1 } if commit.merge_commit?
+ args << { mainline: 1 } if commit.merge_commit?
revert_index = rugged.revert_commit(*args)
return false if revert_index.conflicts?
@@ -809,7 +809,7 @@ class Repository
def check_cherry_pick_content(commit, base_branch)
source_sha = find_branch(base_branch).target
args = [commit.id, source_sha]
- args << 1 if commit.merge_commit?
+ args << 1 if commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts?
diff --git a/app/models/user.rb b/app/models/user.rb
index 1e4814641d1..489bff3fa4a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -381,6 +381,11 @@ class User < ActiveRecord::Base
Project.where("projects.id IN (#{projects_union.to_sql})")
end
+ def viewable_starred_projects
+ starred_projects.where("projects.visibility_level IN (?) OR projects.id IN (#{projects_union.to_sql})",
+ [Project::PUBLIC, Project::INTERNAL])
+ end
+
def owned_projects
@owned_projects ||=
Project.where('namespace_id IN (?) OR namespace_id = ?',
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index cd4230aa5e4..1b48899bb0a 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -66,7 +66,7 @@ module MergeRequests
commits = merge_request.compare_commits
if commits && commits.count == 1
commit = commits.first
- merge_request.title = commit.title
+ merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip)
elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?)
case issue
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 19aab999e00..48a6131b444 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -35,7 +35,7 @@ module Projects
end
end
- log_info("Project \"#{project.name}\" was removed")
+ log_info("Project \"#{project.path_with_namespace}\" was removed")
system_hook_service.execute_hooks_for(project, :destroy)
true
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index e43b5b51e5b..1fb72cf89e9 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -85,7 +85,7 @@ class SystemHooksService
path_with_namespace: model.path_with_namespace,
project_id: model.id,
owner_name: owner.name,
- owner_email: owner.respond_to?(:email) ? owner.email : "",
+ owner_email: owner.respond_to?(:email) ? owner.email : "",
project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
}
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index e0d8d16a954..f7c799c968f 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -109,6 +109,13 @@
= f.label :signin_enabled do
= f.check_box :signin_enabled
Sign-in enabled
+ - if omniauth_enabled? && button_based_providers.any?
+ .form-group
+ = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth Sign-In sources', class: 'control-label col-sm-2'
+ .col-sm-10
+ .btn-group{ data: { toggle: 'buttons' } }
+ - oauth_providers_checkboxes.each do |source|
+ = source
.form-group
= f.label :two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index 804d7851bdb..ed24757087b 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -20,7 +20,7 @@
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
.row-content-block.second-block
- #{(@scope || 'running').capitalize} builds
+ #{(@scope || 'all').capitalize} builds
%ul.content-list
- if @builds.blank?
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
new file mode 100644
index 00000000000..c2313986a7f
--- /dev/null
+++ b/app/views/admin/health_check/show.html.haml
@@ -0,0 +1,49 @@
+- page_title "Health Check"
+
+%h3.page-title
+ Health Check
+.bs-callout.clearfix
+ .pull-left
+ %p
+ Access token is
+ %code#health-check-token= current_application_settings.health_check_access_token
+ = button_to reset_health_check_token_admin_application_settings_path,
+ method: :put, class: 'btn btn-default',
+ data: { confirm: 'Are you sure you want to reset the health check token?' } do
+ = icon('refresh')
+ Reset health check access token
+%p.light
+ Health information can be retrieved as plain text, JSON, or XML using:
+ %ul
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, format: :json)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, format: :xml)
+
+%p.light
+ You can also ask for the status of specific services:
+ %ul
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :cache)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :database)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :migrations)
+
+%hr
+.panel.panel-default
+ .panel-heading
+ Current Status:
+ - if @errors.blank?
+ = icon('circle', class: 'cgreen')
+ Healthy
+ - else
+ = icon('warning', class: 'cred')
+ Unhealthy
+ .panel-body
+ - if @errors.blank?
+ No Health Problems Detected
+ - else
+ = @errors
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 8700b4820cd..4dfb3ed05bb 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -22,25 +22,9 @@
%h4 This runner will process builds only from ASSIGNED projects
%p You can't make this a shared runner.
%hr
-= form_for @runner, url: admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f|
- .form-group
- = label_tag :token, class: 'control-label' do
- Token
- .col-sm-10
- = f.text_field :token, class: 'form-control', readonly: true
- .form-group
- = label_tag :description, class: 'control-label' do
- Description
- .col-sm-10
- = f.text_field :description, class: 'form-control'
- .form-group
- = label_tag :tag_list, class: 'control-label' do
- Tags
- .col-sm-10
- = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
- .help-block You can setup builds to only use runners with specific tags
- .form-actions
- = f.submit 'Save', class: 'btn btn-save'
+
+.append-bottom-20
+ = render '/projects/runners/form', runner: @runner, runner_form_url: admin_runner_path(@runner)
.row
.col-md-6
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index d65fa60025c..28194506acc 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -4,7 +4,7 @@
= render 'devise/shared/signin_box'
-# Omniauth fits between signin/ldap signin and signup and does not have a surrounding box
- - if omniauth_enabled? && devise_mapping.omniauthable?
+ - if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix.prepend-top-20
= render 'devise/shared/omniauth_box'
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index ecf680e7b23..de18bc2d844 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,7 +1,7 @@
%p
%span.light
Sign in with &nbsp;
- - providers = button_based_providers
+ - providers = enabled_button_based_providers
- providers.each do |provider|
%span.light
- has_icon = provider_has_icon?(provider)
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 3c3bc41bf0e..5be0b546a62 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,5 +1,4 @@
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
- = render "layouts/broadcast"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
%a#logo
@@ -26,7 +25,8 @@
.layout-nav
.container-fluid
= render "layouts/nav/#{nav}"
- .content-wrapper{ class: ('page-with-layout-nav' if defined?(nav) && nav) }
+ .content-wrapper{ class: "#{layout_nav_class} #{layout_dropdown_class}" }
+ = render "layouts/broadcast"
= render "layouts/flash"
= yield :flash_message
%div{ class: (container_class unless @no_container) }
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 172579dafda..c33740e23fa 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -27,8 +27,9 @@
%li
= link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
- %span.badge.todos-pending-count
- = todos_pending_count
+ - unless todos_pending_count == 0
+ %span.badge.todos-pending-count
+ = todos_pending_count
- if current_user.can_create_project?
%li
= link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 280a1b93729..f292730fe45 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -41,6 +41,11 @@
= icon('file-text fw')
%span
Logs
+ = nav_link(controller: :health_check) do
+ = link_to admin_health_check_path, title: 'Health Check' do
+ = icon('medkit fw')
+ %span
+ Health Check
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do
= icon('bullhorn fw')
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index e391ec7f2b7..0b2673f1a82 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,7 +1,7 @@
- if current_user
- if access = @group.users.find_by(id: current_user.id)
.controls
- %span.dropdown.group-settings-dropdown
+ .dropdown.group-settings-dropdown
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
= icon('cog')
= icon('caret-down')
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 2e8015d119b..98f4a9416e5 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -36,7 +36,7 @@
%span CI Lint
.row-content-block
- #{(@scope || 'running').capitalize} builds from this project
+ #{(@scope || 'all').capitalize} builds from this project
%ul.content-list
- if @builds.blank?
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index c0f7a7686f0..c7b9c36a3ab 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,5 +1,6 @@
- page_title "#{@build.name} (##{@build.id})", "Builds"
= render "header_title"
+- trace_with_state = @build.trace_with_state
.build-page
.row-content-block.top-block
@@ -85,7 +86,9 @@
%pre.trace#build-trace
%code.bash
= preserve do
- = raw @build.trace_html
+ = raw trace_with_state[:html]
+ - if @build.active?
+ %i{:class => "fa fa-refresh fa-spin"}
%div#down-build-trace
@@ -216,4 +219,4 @@
:javascript
- new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}")
+ new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index 64e8da9201d..82f39e59284 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -3,7 +3,7 @@
- commits, hidden = limited_commits(@commits)
-- commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
+- commits.group_by { |c| c.committed_date.in_time_zone.to_date }.sort.reverse.each do |day, commits|
.row.commits-row
.col-md-2.hidden-xs.hidden-sm
%h5.commits-row-date
diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml
new file mode 100644
index 00000000000..2d6c964ae94
--- /dev/null
+++ b/app/views/projects/runners/_form.html.haml
@@ -0,0 +1,25 @@
+= form_for runner, url: runner_form_url, html: { class: 'form-horizontal' } do |f|
+ .form-group
+ = label :active, "Active", class: 'control-label'
+ .col-sm-10
+ .checkbox
+ = f.check_box :active
+ %span.light Paused runners don't accept new builds
+ .form-group
+ = label_tag :token, class: 'control-label' do
+ Token
+ .col-sm-10
+ = f.text_field :token, class: 'form-control', readonly: true
+ .form-group
+ = label_tag :description, class: 'control-label' do
+ Description
+ .col-sm-10
+ = f.text_field :description, class: 'form-control'
+ .form-group
+ = label_tag :tag_list, class: 'control-label' do
+ Tags
+ .col-sm-10
+ = f.text_field :tag_list, value: runner.tag_list.to_s, class: 'form-control'
+ .help-block You can setup jobs to only use runners with specific tags
+ .form-actions
+ = f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml
index eba03028af8..771947d7908 100644
--- a/app/views/projects/runners/edit.html.haml
+++ b/app/views/projects/runners/edit.html.haml
@@ -2,28 +2,4 @@
%h4 Runner ##{@runner.id}
%hr
-= form_for @runner, url: runner_path(@runner), html: { class: 'form-horizontal' } do |f|
- .form-group
- = label :active, "Active", class: 'control-label'
- .col-sm-10
- .checkbox
- = f.check_box :active
- %span.light Paused runners don't accept new builds
- .form-group
- = label_tag :token, class: 'control-label' do
- Token
- .col-sm-10
- = f.text_field :token, class: 'form-control', readonly: true
- .form-group
- = label_tag :description, class: 'control-label' do
- Description
- .col-sm-10
- = f.text_field :description, class: 'form-control'
- .form-group
- = label_tag :tag_list, class: 'control-label' do
- Tags
- .col-sm-10
- = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
- .help-block You can setup jobs to only use runners with specific tags
- .form-actions
- = f.submit 'Save changes', class: 'btn btn-save'
+ = render 'form', runner: @runner, runner_form_url: runner_path(@runner)
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 3c0b89c6741..9017fd54fcc 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -81,6 +81,9 @@
%li.projects-tab
= link_to user_projects_path, data: {target: 'div#projects', action: 'projects', toggle: 'tab'} do
Personal projects
+ %li.snippets-tab
+ = link_to user_snippets_path, data: {target: 'div#snippets', action: 'snippets', toggle: 'tab'} do
+ Snippets
%div{ class: container_class }
.tab-content
@@ -104,6 +107,9 @@
#projects.tab-pane
- # This tab is always loaded via AJAX
+ #snippets.tab-pane
+ - # This tab is always loaded via AJAX
+
.loading-status
= spinner
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index c4d8595d45d..6ebcba5f39b 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,6 +1,8 @@
class EmailsOnPushWorker
include Sidekiq::Worker
+ attr_reader :email, :skip_premailer
+
def perform(project_id, recipients, push_data, options = {})
options.symbolize_keys!
options.reverse_merge!(
@@ -41,11 +43,11 @@ class EmailsOnPushWorker
end
end
- recipients.split(" ").each do |recipient|
+ recipients.split.each do |recipient|
begin
- Notify.repository_push_email(
- project_id,
+ send_email(
recipient,
+ project_id,
author_id: author_id,
ref: ref,
action: action,
@@ -53,14 +55,29 @@ class EmailsOnPushWorker
reverse_compare: reverse_compare,
send_from_committer_email: send_from_committer_email,
disable_diffs: disable_diffs
- ).deliver_now
+ )
+
# These are input errors and won't be corrected even if Sidekiq retries
rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
logger.info("Failed to send e-mail for project '#{project.name_with_namespace}' to #{recipient}: #{e}")
end
end
ensure
+ @email = nil
compare = nil
GC.start
end
+
+ private
+
+ def send_email(recipient, project_id, options)
+ # Generating the body of this email can be expensive, so only do it once
+ @skip_premailer ||= email.present?
+ @email ||= Notify.repository_push_email(project_id, options)
+
+ email.to = recipient
+ email.add_message_id
+ email.header[:skip_premailer] = true if skip_premailer
+ email.deliver_now
+ end
end
diff --git a/config/boot.rb b/config/boot.rb
index 4489e58688c..f2830ae3166 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -3,4 +3,4 @@ require 'rubygems'
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
diff --git a/config/environments/test.rb b/config/environments/test.rb
index a703c0934f7..fb25d3a8b14 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -20,7 +20,7 @@ Rails.application.configure do
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment
- config.action_controller.allow_forgery_protection = false
+ config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 23c8cea038a..a69b933d811 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -126,7 +126,7 @@ end
Settings['omniauth'] ||= Settingslogic.new({})
-Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
+Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_providers'].nil?
@@ -134,7 +134,7 @@ Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['auto_link_saml_user'] = false if Settings.omniauth['auto_link_saml_user'].nil?
-Settings.omniauth['providers'] ||= []
+Settings.omniauth['providers'] ||= []
Settings.omniauth['cas3'] ||= Settingslogic.new({})
Settings.omniauth.cas3['session_duration'] ||= 8.hours
Settings.omniauth['session_tickets'] ||= Settingslogic.new({})
@@ -168,7 +168,7 @@ end
Settings['shared'] ||= Settingslogic.new({})
Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root)
-Settings['issues_tracker'] ||= {}
+Settings['issues_tracker'] ||= {}
#
# GitLab
@@ -183,7 +183,7 @@ Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
-Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
+Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}"
Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab'
@@ -196,7 +196,7 @@ Settings.gitlab['user_home'] ||= begin
rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user']
end
-Settings.gitlab['time_zone'] ||= nil
+Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
@@ -211,7 +211,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil?
-Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
+Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
Settings.gitlab['restricted_signup_domains'] ||= []
Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
@@ -225,8 +225,8 @@ Settings['gitlab_ci'] ||= Settingslogic.new({})
Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['shared_runners_enabled'].nil?
Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil?
Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil?
-Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
+Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
#
# Reply by email
@@ -240,7 +240,7 @@ Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled']
Settings['artifacts'] ||= Settingslogic.new({})
Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil?
Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root)
-Settings.artifacts['max_size'] ||= 100 # in megabytes
+Settings.artifacts['max_size'] ||= 100 # in megabytes
#
# Git LFS
@@ -298,7 +298,7 @@ Settings['backup'] ||= Settingslogic.new({})
Settings.backup['keep_time'] ||= 0
Settings.backup['pg_schema'] = nil
Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root)
-Settings.backup['archive_permissions'] ||= 0600
+Settings.backup['archive_permissions'] ||= 0600
Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil })
# Convert upload connection settings to use symbol keys, to make Fog happy
if Settings.backup['upload']['connection']
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index df28d30d750..1933afcbfb1 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -2,7 +2,7 @@ CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
aws_file = Rails.root.join('config', 'aws.yml')
-if File.exists?(aws_file)
+if File.exist?(aws_file)
AWS_CONFIG = YAML.load(File.read(aws_file))[Rails.env]
CarrierWave.configure do |config|
@@ -20,7 +20,7 @@ if File.exists?(aws_file)
config.fog_public = false
# optional, defaults to {}
- config.fog_attributes = { 'Cache-Control'=>'max-age=315576000' }
+ config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
# optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 31dceaebcad..021bdb11251 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -243,7 +243,7 @@ Devise.setup do |config|
when Hash
# Add procs for handling SLO
if provider['name'] == 'cas3'
- provider['args'][:on_single_sign_out] = lambda do |request|
+ provider['args'][:on_single_sign_out] = lambda do |request|
ticket = request.params[:session_index]
raise "Service Ticket not found." unless Gitlab::OAuth::Session.valid?(:cas3, ticket)
Gitlab::OAuth::Session.destroy(:cas3, ticket)
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
new file mode 100644
index 00000000000..79e2d23ab2e
--- /dev/null
+++ b/config/initializers/health_check.rb
@@ -0,0 +1,3 @@
+HealthCheck.setup do |config|
+ config.standard_checks = ['database', 'migrations', 'cache']
+end
diff --git a/config/initializers/monkey_patch.rb b/config/initializers/monkey_patch.rb
deleted file mode 100644
index 62b05a55285..00000000000
--- a/config/initializers/monkey_patch.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-## This patch is from rails 4.2-stable. Remove it when 4.2.6 is released
-## https://github.com/rails/rails/issues/21108
-
-module ActiveRecord
- module ConnectionAdapters
- class AbstractMysqlAdapter < AbstractAdapter
- # SHOW VARIABLES LIKE 'name'
- def show_variable(name)
- variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
- variables.first['Value'] unless variables.empty?
- rescue ActiveRecord::StatementInvalid
- nil
- end
-
-
- # MySQL is too stupid to create a temporary table for use subquery, so we have
- # to give it some prompting in the form of a subsubquery. Ugh!
- def subquery_for(key, select)
- subsubselect = select.clone
- subsubselect.projections = [key]
-
- subselect = Arel::SelectManager.new(select.engine)
- subselect.project Arel.sql(key.name)
- # Materialized subquery by adding distinct
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
- subselect.from subsubselect.distinct.as('__active_record_temp')
- end
- end
- end
-end
-
-module ActiveRecord
- module ConnectionAdapters
- class MysqlAdapter < AbstractMysqlAdapter
- ADAPTER_NAME = 'MySQL'.freeze
-
- # Get the client encoding for this database
- def client_encoding
- return @client_encoding if @client_encoding
-
- result = exec_query(
- "select @@character_set_client",
- 'SCHEMA')
- @client_encoding = ENCODINGS[result.rows.last.last]
- end
- end
- end
-end
diff --git a/config/routes.rb b/config/routes.rb
index dafecc94648..9e776a1f541 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -73,6 +73,9 @@ Rails.application.routes.draw do
mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
+ # Health check
+ get 'health_check(/:checks)' => 'health_check#index', as: :health_check
+
# Enable Grack support
mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
@@ -80,7 +83,7 @@ Rails.application.routes.draw do
get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page, constraints: { category: /.*/, file: /[^\/\.]+/ }
get 'help/shortcuts'
- get 'help/ui' => 'help#ui'
+ get 'help/ui' => 'help#ui'
#
# Global snippets
@@ -91,7 +94,8 @@ Rails.application.routes.draw do
end
end
- get '/s/:username' => 'snippets#index', as: :user_snippets, constraints: { username: /.*/ }
+ get '/s/:username', to: redirect('/u/%{username}/snippets'),
+ constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }
#
# Invites
@@ -253,6 +257,7 @@ Rails.application.routes.draw do
end
resource :logs, only: [:show]
+ resource :health_check, controller: 'health_check', only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
@@ -284,6 +289,7 @@ Rails.application.routes.draw do
resource :application_settings, only: [:show, :update] do
resources :services
put :reset_runners_token
+ put :reset_health_check_token
put :clear_repository_check_states
end
@@ -342,23 +348,18 @@ Rails.application.routes.draw do
end
end
- get 'u/:username/calendar' => 'users#calendar', as: :user_calendar,
- constraints: { username: /.*/ }
-
- get 'u/:username/calendar_activities' => 'users#calendar_activities', as: :user_calendar_activities,
- constraints: { username: /.*/ }
-
- get 'u/:username/groups' => 'users#groups', as: :user_groups,
- constraints: { username: /.*/ }
-
- get 'u/:username/projects' => 'users#projects', as: :user_projects,
- constraints: { username: /.*/ }
-
- get 'u/:username/contributed' => 'users#contributed', as: :user_contributed_projects,
- constraints: { username: /.*/ }
-
- get '/u/:username' => 'users#show', as: :user,
- constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }
+ scope(path: 'u/:username',
+ as: :user,
+ constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ },
+ controller: :users) do
+ get :calendar
+ get :calendar_activities
+ get :groups
+ get :projects
+ get :contributed, as: :contributed_projects
+ get :snippets
+ get '/', action: :show
+ end
#
# Dashboard Area
@@ -672,6 +673,7 @@ Rails.application.routes.draw do
post :cancel
post :retry
post :erase
+ get :trace
get :raw
end
diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
new file mode 100644
index 00000000000..facd33875ba
--- /dev/null
+++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :disabled_oauth_sign_in_sources, :text
+ end
+end
diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
new file mode 100644
index 00000000000..9d729fec189
--- /dev/null
+++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :health_check_access_token, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71d953afe30..9b5aa640cb0 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: 20160508194200) do
+ActiveRecord::Schema.define(version: 20160509201028) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -80,6 +80,8 @@ ActiveRecord::Schema.define(version: 20160508194200) do
t.boolean "repository_checks_enabled", default: false
t.text "shared_runners_text"
t.integer "metrics_packet_size", default: 1
+ t.text "disabled_oauth_sign_in_sources"
+ t.string "health_check_access_token"
end
create_table "audit_events", force: :cascade do |t|
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 3730c07c5a7..b857d81768e 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -165,3 +165,73 @@ Example response:
"description": "Documentation"
}
```
+
+## Subscribe to a label
+
+Subscribes the authenticated user to a label to receive notifications. If the
+operation is successful, status code `201` together with the updated label is
+returned. If the user is already subscribed to the label, the status code `304`
+is returned. If the project or label is not found, status code `404` is
+returned.
+
+```
+POST /projects/:id/labels/:label_id/subscription
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ----------------- | -------- | ------------------------------------ |
+| `id` | integer | yes | The ID of a project |
+| `label_id` | integer or string | yes | The ID or title of a project's label |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription
+```
+
+Example response:
+
+```json
+{
+ "name": "Docs",
+ "color": "#cc0033",
+ "description": "",
+ "open_issues_count": 0,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 0,
+ "subscribed": true
+}
+```
+
+## Unsubscribe from a label
+
+Unsubscribes the authenticated user from a label to not receive notifications
+from it. If the operation is successful, status code `200` together with the
+updated label is returned. If the user is not subscribed to the label, the
+status code `304` is returned. If the project or label is not found, status code
+`404` is returned.
+
+```
+DELETE /projects/:id/labels/:label_id/subscription
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ----------------- | -------- | ------------------------------------ |
+| `id` | integer | yes | The ID of a project |
+| `label_id` | integer or string | yes | The ID or title of a project's label |
+
+```bash
+curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription
+```
+
+Example response:
+
+```json
+{
+ "name": "Docs",
+ "color": "#cc0033",
+ "description": "",
+ "open_issues_count": 0,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 0,
+ "subscribed": false
+}
+```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index e3af3022262..44ae0be406c 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -269,7 +269,7 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-7-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-8-stable gitlab
**Note:** You can change `8-7-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
diff --git a/doc/integration/cas.md b/doc/integration/cas.md
index e6b2071f193..e34e306f9ac 100644
--- a/doc/integration/cas.md
+++ b/doc/integration/cas.md
@@ -27,17 +27,18 @@ To enable the CAS OmniAuth provider you must register your application with your
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "cas3",
- label: "cas",
- args: {
- url: 'CAS_SERVER',
- login_url: '/CAS_PATH/login',
- service_validate_url: '/CAS_PATH/p3/serviceValidate',
- logout_url: '/CAS_PATH/logout'} }
- }
+ "name"=> "cas3",
+ "label"=> "cas",
+ "args"=> {
+ "url"=> 'CAS_SERVER',
+ "login_url"=> '/CAS_PATH/login',
+ "service_validate_url"=> '/CAS_PATH/p3/serviceValidate',
+ "logout_url"=> '/CAS_PATH/logout'
+ }
}
]
```
+
For installations from source:
@@ -57,6 +58,8 @@ To enable the CAS OmniAuth provider you must register your application with your
1. Save the configuration file.
+1. Run `gitlab-ctl reconfigure` for the omnibus package.
+
1. Restart GitLab for the changes to take effect.
On the sign in page there should now be a CAS tab in the sign in form.
diff --git a/doc/integration/img/enabled-oauth-sign-in-sources.png b/doc/integration/img/enabled-oauth-sign-in-sources.png
new file mode 100644
index 00000000000..95f8bbdcd24
--- /dev/null
+++ b/doc/integration/img/enabled-oauth-sign-in-sources.png
Binary files differ
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index cab329c0dec..820f40f81a9 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -11,6 +11,7 @@ of the configured mechanisms.
- [Supported Providers](#supported-providers)
- [Enable OmniAuth for an Existing User](#enable-omniauth-for-an-existing-user)
- [OmniAuth configuration sample when using Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master#omniauth-google-twitter-github-login)
+- [Enable or disable Sign In with an OmniAuth provider without disabling import sources](#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources)
## Supported Providers
@@ -191,3 +192,17 @@ experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/w
While we can't officially support every possible authentication mechanism out there,
we'd like to at least help those with specific needs.
+
+## Enable or disable Sign In with an OmniAuth provider without disabling import sources
+
+>**Note:**
+This setting was introduced with version 8.8 of GitLab.
+
+Administrators are able to enable or disable Sign In via some OmniAuth providers.
+
+>**Note:**
+By default Sign In is enabled via all the OAuth Providers that have been configured in `config/gitlab.yml`.
+
+In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings -> Sign-in Restrictions section -> Enabled OAuth Sign-In sources and select the providers you want to enable or disable.
+
+![Enabled OAuth Sign-In sources](img/enabled-oauth-sign-in-sources.png)
diff --git a/doc/update/8.7-to-8.8.md b/doc/update/8.7-to-8.8.md
new file mode 100644
index 00000000000..b4d9212289c
--- /dev/null
+++ b/doc/update/8.7-to-8.8.md
@@ -0,0 +1,154 @@
+# From 8.7 to 8.8
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-8-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-8-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v2.7.2
+```
+
+### 5. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v0.7.1
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+```
+
+### 7. Update configuration files
+
+#### Git configuration
+
+Disable `git gc --auto` because GitLab runs `git gc` for us already.
+
+```sh
+sudo -u git -H git config --global gc.auto 0
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-7-stable:lib/support/nginx/gitlab-ssl origin/8-8-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-7-stable:lib/support/nginx/gitlab origin/8-8-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/lib/support/init.d/gitlab.default.example#L37
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.6)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.6 to 8.7](8.6-to-8.7.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c1c51302e79..45506ac1d7c 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -13,6 +13,19 @@ You can configure webhooks to listen for specific events like pushes, issues or
Webhooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
+## Webhook endpoint tips
+
+If you are writing your own endpoint (web server) that will receive
+GitLab webhooks keep in mind the following things:
+
+- Your endpoint should send its HTTP response as fast as possible. If
+ you wait too long, GitLab may decide the hook failed and retry it.
+- Your endpoint should ALWAYS return a valid HTTP response. If you do
+ not do this then GitLab will think the hook failed and retry it.
+ Most HTTP libraries take care of this for you automatically but if
+ you are writing a low-level hook this is important to remember.
+- GitLab ignores the HTTP status code returned by your endpoint.
+
## SSL Verification
By default, the SSL certificate of the webhook endpoint is verified based on
diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb
index e21af72a777..8706f0e8e78 100644
--- a/features/steps/dashboard/issues.rb
+++ b/features/steps/dashboard/issues.rb
@@ -74,7 +74,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
def project
@project ||= begin
- project =create :project
+ project = create :project
project.team << [current_user, :master]
project
end
diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb
index a2adc87f8ef..06db36c7014 100644
--- a/features/steps/dashboard/merge_requests.rb
+++ b/features/steps/dashboard/merge_requests.rb
@@ -100,7 +100,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
def project
@project ||= begin
- project =create :project
+ project = create :project
project.team << [current_user, :master]
project
end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 909de31a479..b1a87b96efd 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -166,7 +166,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I have group with projects' do
- @group = create(:group)
+ @group = create(:group)
@group.add_owner(current_user)
@project = create(:project, namespace: @group)
@event = create(:closed_issue_event, project: @project)
diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb
index b1ffe7f7b4c..13c0713669a 100644
--- a/features/steps/project/hooks.rb
+++ b/features/steps/project/hooks.rb
@@ -59,7 +59,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
step 'hook should be triggered' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
expect(page).to have_selector '.flash-notice',
- text: 'Hook successfully executed.'
+ text: 'Hook executed successfully: HTTP 200'
end
step 'I should see hook error message' do
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index 3fbcf770b62..c6ced747370 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -126,7 +126,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
step 'I share project with group "OpenSource"' do
project = Project.find_by(name: 'Shop')
- os_group = create(:group, name: 'OpenSource')
+ os_group = create(:group, name: 'OpenSource')
create(:project, group: os_group)
@os_user1 = create(:user)
@os_user2 = create(:user)
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 5fd9c30cb42..360fb41a721 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -57,5 +57,6 @@ module API
mount ::API::Variables
mount ::API::Runners
mount ::API::Licenses
+ mount ::API::Subscriptions
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 2870a6a40ef..406f5ea9139 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -307,6 +307,10 @@ module API
class Label < Grape::Entity
expose :name, :color, :description
expose :open_issues_count, :closed_issues_count, :open_merge_requests_count
+
+ expose :subscribed do |label, options|
+ label.subscribed?(options[:current_user])
+ end
end
class Compare < Grape::Entity
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 40c967453fb..cadf9f98fe3 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -2,7 +2,7 @@ module API
module Helpers
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
PRIVATE_TOKEN_PARAM = :private_token
- SUDO_HEADER ="HTTP_SUDO"
+ SUDO_HEADER = "HTTP_SUDO"
SUDO_PARAM = :sudo
def parse_boolean(value)
@@ -95,6 +95,17 @@ module API
end
end
+ def find_project_label(id)
+ label = user_project.labels.find_by_id(id) || user_project.labels.find_by_title(id)
+ label || not_found!('Label')
+ end
+
+ def find_project_issue(id)
+ issue = user_project.issues.find(id)
+ not_found! unless can?(current_user, :read_issue, issue)
+ issue
+ end
+
def paginate(relation)
relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
add_pagination_headers(data)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 40928749481..f59a4d6c012 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -103,8 +103,7 @@ module API
# Example Request:
# GET /projects/:id/issues/:issue_id
get ":id/issues/:issue_id" do
- @issue = user_project.issues.find(params[:issue_id])
- not_found! unless can?(current_user, :read_issue, @issue)
+ @issue = find_project_issue(params[:issue_id])
present @issue, with: Entities::Issue, current_user: current_user
end
@@ -234,42 +233,6 @@ module API
authorize!(:destroy_issue, issue)
issue.destroy
end
-
- # Subscribes to a project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # POST /projects/:id/issues/:issue_id/subscription
- post ':id/issues/:issue_id/subscription' do
- issue = user_project.issues.find(params[:issue_id])
-
- if issue.subscribed?(current_user)
- not_modified!
- else
- issue.toggle_subscription(current_user)
- present issue, with: Entities::Issue, current_user: current_user
- end
- end
-
- # Unsubscribes from a project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # DELETE /projects/:id/issues/:issue_id/subscription
- delete ':id/issues/:issue_id/subscription' do
- issue = user_project.issues.find(params[:issue_id])
-
- if issue.subscribed?(current_user)
- issue.unsubscribe(current_user)
- present issue, with: Entities::Issue, current_user: current_user
- else
- not_modified!
- end
- end
end
end
end
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 4af6bef0fa7..c806829d69e 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -11,7 +11,7 @@ module API
# Example Request:
# GET /projects/:id/labels
get ':id/labels' do
- present user_project.labels, with: Entities::Label
+ present user_project.labels, with: Entities::Label, current_user: current_user
end
# Creates a new label
@@ -36,7 +36,7 @@ module API
label = user_project.labels.create(attrs)
if label.valid?
- present label, with: Entities::Label
+ present label, with: Entities::Label, current_user: current_user
else
render_validation_error!(label)
end
@@ -90,7 +90,7 @@ module API
attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name)
if label.update(attrs)
- present label, with: Entities::Label
+ present label, with: Entities::Label, current_user: current_user
else
render_validation_error!(label)
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 7e78609ecb9..4e7de8867b4 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -327,42 +327,6 @@ module API
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: Entities::Issue, current_user: current_user
end
-
- # Subscribes to a merge request
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of a merge request
- # Example Request:
- # POST /projects/:id/issues/:merge_request_id/subscription
- post "#{path}/subscription" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- if merge_request.subscribed?(current_user)
- not_modified!
- else
- merge_request.toggle_subscription(current_user)
- present merge_request, with: Entities::MergeRequest, current_user: current_user
- end
- end
-
- # Unsubscribes from a merge request
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of a merge request
- # Example Request:
- # DELETE /projects/:id/merge_requests/:merge_request_id/subscription
- delete "#{path}/subscription" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- if merge_request.subscribed?(current_user)
- merge_request.unsubscribe(current_user)
- present merge_request, with: Entities::MergeRequest, current_user: current_user
- else
- not_modified!
- end
- end
end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index cc2c7a0c503..9b595772675 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -44,7 +44,7 @@ module API
# Example Request:
# GET /projects/starred
get '/starred' do
- @projects = current_user.starred_projects
+ @projects = current_user.viewable_starred_projects
@projects = filter_projects(@projects)
@projects = paginate @projects
present @projects, with: Entities::Project
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
new file mode 100644
index 00000000000..c49e2a21b82
--- /dev/null
+++ b/lib/api/subscriptions.rb
@@ -0,0 +1,60 @@
+module API
+ class Subscriptions < Grape::API
+ before { authenticate! }
+
+ subscribable_types = {
+ 'merge_request' => proc { |id| user_project.merge_requests.find(id) },
+ 'merge_requests' => proc { |id| user_project.merge_requests.find(id) },
+ 'issues' => proc { |id| find_project_issue(id) },
+ 'labels' => proc { |id| find_project_label(id) },
+ }
+
+ resource :projects do
+ subscribable_types.each do |type, finder|
+ type_singularized = type.singularize
+ type_id_str = :"#{type_singularized}_id"
+ entity_class = Entities.const_get(type_singularized.camelcase)
+
+ # Subscribe to a resource
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # subscribable_id (required) - The ID of a resource
+ # Example Request:
+ # POST /projects/:id/labels/:subscribable_id/subscription
+ # POST /projects/:id/issues/:subscribable_id/subscription
+ # POST /projects/:id/merge_requests/:subscribable_id/subscription
+ post ":id/#{type}/:#{type_id_str}/subscription" do
+ resource = instance_exec(params[type_id_str], &finder)
+
+ if resource.subscribed?(current_user)
+ not_modified!
+ else
+ resource.subscribe(current_user)
+ present resource, with: entity_class, current_user: current_user
+ end
+ end
+
+ # Unsubscribe from a resource
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # subscribable_id (required) - The ID of a resource
+ # Example Request:
+ # DELETE /projects/:id/labels/:subscribable_id/subscription
+ # DELETE /projects/:id/issues/:subscribable_id/subscription
+ # DELETE /projects/:id/merge_requests/:subscribable_id/subscription
+ delete ":id/#{type}/:#{type_id_str}/subscription" do
+ resource = instance_exec(params[type_id_str], &finder)
+
+ if !resource.subscribed?(current_user)
+ not_modified!
+ else
+ resource.unsubscribe(current_user)
+ present resource, with: entity_class, current_user: current_user
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 06d10c98501..7dc771afd71 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -25,7 +25,7 @@ module Banzai
end
def process_link_attr(html_attr)
- return if html_attr.blank? || file_reference?(html_attr)
+ return if html_attr.blank? || file_reference?(html_attr) || hierarchical_link?(html_attr)
uri = URI(html_attr.value)
if uri.relative? && uri.path.present?
@@ -40,12 +40,17 @@ module Banzai
uri
end
+ def project_wiki
+ context[:project_wiki]
+ end
+
def file_reference?(html_attr)
!File.extname(html_attr.value).blank?
end
- def project_wiki
- context[:project_wiki]
+ # Of the form `./link`, `../link`, or similar
+ def hierarchical_link?(html_attr)
+ html_attr.value[0] == '.'
end
def project_wiki_base_path
diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb
index ac6d667cf8d..5fed43aaebd 100644
--- a/lib/ci/ansi2html.rb
+++ b/lib/ci/ansi2html.rb
@@ -23,8 +23,8 @@ module Ci
cross: 0x10,
}
- def self.convert(ansi)
- Converter.new().convert(ansi)
+ def self.convert(ansi, state = nil)
+ Converter.new.convert(ansi, state)
end
class Converter
@@ -84,22 +84,36 @@ module Ci
def on_107(s) set_bg_color(7, 'l') end
def on_109(s) set_bg_color(9, 'l') end
- def convert(ansi)
- @out = ""
- @n_open_tags = 0
- reset()
+ attr_accessor :offset, :n_open_tags, :fg_color, :bg_color, :style_mask
+
+ STATE_PARAMS = [:offset, :n_open_tags, :fg_color, :bg_color, :style_mask]
+
+ def convert(raw, new_state)
+ reset_state
+ restore_state(raw, new_state) if new_state
+
+ start = @offset
+ ansi = raw[@offset..-1]
+
+ open_new_tag
- s = StringScanner.new(ansi.gsub("<", "&lt;"))
+ s = StringScanner.new(ansi)
while(!s.eos?)
if s.scan(/\e([@-_])(.*?)([@-~])/)
handle_sequence(s)
+ elsif s.scan(/\e(([@-_])(.*?)?)?$/)
+ break
+ elsif s.scan(/</)
+ @out << '&lt;'
else
@out << s.scan(/./m)
end
+ @offset += s.matched_size
end
close_open_tags()
- @out
+
+ { state: state, html: @out, text: ansi[0, @offset - start], append: start > 0 }
end
def handle_sequence(s)
@@ -121,6 +135,20 @@ module Ci
evaluate_command_stack(commands)
+ open_new_tag
+ end
+
+ def evaluate_command_stack(stack)
+ return unless command = stack.shift()
+
+ if self.respond_to?("on_#{command}", true)
+ self.send("on_#{command}", stack)
+ end
+
+ evaluate_command_stack(stack)
+ end
+
+ def open_new_tag
css_classes = []
unless @fg_color.nil?
@@ -138,20 +166,8 @@ module Ci
css_classes << "term-#{css_class}" if @style_mask & flag != 0
end
- open_new_tag(css_classes) if css_classes.length > 0
- end
+ return if css_classes.empty?
- def evaluate_command_stack(stack)
- return unless command = stack.shift()
-
- if self.respond_to?("on_#{command}", true)
- self.send("on_#{command}", stack)
- end
-
- evaluate_command_stack(stack)
- end
-
- def open_new_tag(css_classes)
@out << %{<span class="#{css_classes.join(' ')}">}
@n_open_tags += 1
end
@@ -163,6 +179,31 @@ module Ci
end
end
+ def reset_state
+ @offset = 0
+ @n_open_tags = 0
+ @out = ''
+ reset
+ end
+
+ def state
+ state = STATE_PARAMS.inject({}) do |h, param|
+ h[param] = send(param)
+ h
+ end
+ Base64.urlsafe_encode64(state.to_json)
+ end
+
+ def restore_state(raw, new_state)
+ state = Base64.urlsafe_decode64(new_state)
+ state = JSON.parse(state, symbolize_names: true)
+ return if state[:offset].to_i > raw.length
+
+ STATE_PARAMS.each do |param|
+ send("#{param}=".to_sym, state[param])
+ end
+ end
+
def reset
@fg_color = nil
@bg_color = nil
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 132f9cd1966..3e3986d6382 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -180,7 +180,7 @@ module Gitlab
# exists?('gitlab/cookies.git')
#
def exists?(dir_name)
- File.exists?(full_path(dir_name))
+ File.exist?(full_path(dir_name))
end
protected
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 6f9da69983a..42bec913a45 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -5,11 +5,11 @@ module Gitlab
end
def self.mysql?
- adapter_name.downcase == 'mysql2'
+ adapter_name.casecmp('mysql2').zero?
end
def self.postgresql?
- adapter_name.downcase == 'postgresql'
+ adapter_name.casecmp('postgresql').zero?
end
def self.version
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index d0815fc7eea..6fe7faa547a 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -18,7 +18,7 @@ module Gitlab
@lines.each do |line|
next if filename?(line)
- full_line = line.gsub(/\n/, '')
+ full_line = line.delete("\n")
if line.match(/^@@ -/)
type = "match"
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index 8f9be6cd9a3..2c91a0487c3 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -2,7 +2,6 @@ module Gitlab
module Email
module Message
class RepositoryPush
- attr_accessor :recipient
attr_reader :author_id, :ref, :action
include Gitlab::Routing.url_helpers
@@ -11,13 +10,12 @@ module Gitlab
delegate :name, to: :author, prefix: :author
delegate :username, to: :author, prefix: :author
- def initialize(notify, project_id, recipient, opts = {})
+ def initialize(notify, project_id, opts = {})
raise ArgumentError, 'Missing options: author_id, ref, action' unless
opts[:author_id] && opts[:ref] && opts[:action]
@notify = notify
@project_id = project_id
- @recipient = recipient
@opts = opts.dup
@author_id = @opts.delete(:author_id)
diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb
index 6ed36b51f12..3411eb1d9ce 100644
--- a/lib/gitlab/email/reply_parser.rb
+++ b/lib/gitlab/email/reply_parser.rb
@@ -65,7 +65,7 @@ module Gitlab
(l =~ /On \w+ \d+,? \d+,?.*wrote:/)
# Headers on subsequent lines
- break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX }
+ break if (0..2).all? { |off| lines[idx + off] =~ REPLYING_HEADER_REGEX }
# Headers on the same line
break if REPLYING_HEADER_LABELS.count { |label| l.include?(label) } >= 3
diff --git a/lib/gitlab/markup_helper.rb b/lib/gitlab/markup_helper.rb
index a5f767b134d..dda371e6554 100644
--- a/lib/gitlab/markup_helper.rb
+++ b/lib/gitlab/markup_helper.rb
@@ -40,7 +40,7 @@ module Gitlab
# Returns boolean
def plain?(filename)
filename.downcase.end_with?('.txt') ||
- filename.downcase == 'readme'
+ filename.casecmp('readme').zero?
end
def previewable?(filename)
diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb
index 708ef79f304..0f115893a15 100644
--- a/lib/gitlab/metrics/instrumentation.rb
+++ b/lib/gitlab/metrics/instrumentation.rb
@@ -154,8 +154,6 @@ module Gitlab
duration = (Time.now - start) * 1000.0
if duration >= Gitlab::Metrics.method_call_threshold
- trans.increment(:method_duration, duration)
-
trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES,
{ duration: duration },
method: #{label.inspect})
diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb
index 5c352c96de5..40766f35f77 100644
--- a/lib/gitlab/redis.rb
+++ b/lib/gitlab/redis.rb
@@ -25,7 +25,7 @@ module Gitlab
end
@pool.with { |redis| yield redis }
end
-
+
def self.redis_store_options
url = new.url
redis_config_hash = ::Redis::Store::Factory.extract_host_options_from_uri(url)
@@ -40,10 +40,10 @@ module Gitlab
def initialize(rails_env=nil)
rails_env ||= Rails.env
config_file = File.expand_path('../../../config/resque.yml', __FILE__)
-
+
@url = "redis://localhost:6379"
- if File.exists?(config_file)
- @url =YAML.load_file(config_file)[rails_env]
+ if File.exist?(config_file)
+ @url = YAML.load_file(config_file)[rails_env]
end
end
end
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb
index 462afb24f08..6fad7e2b9e7 100644
--- a/spec/config/mail_room_spec.rb
+++ b/spec/config/mail_room_spec.rb
@@ -43,7 +43,7 @@ describe "mail_room.yml" do
redis_config_file = Rails.root.join('config', 'resque.yml')
redis_url =
- if File.exists?(redis_config_file)
+ if File.exist?(redis_config_file)
YAML.load_file(redis_config_file)[Rails.env]
else
"redis://localhost:6379"
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
new file mode 100644
index 00000000000..0d8a68bb51a
--- /dev/null
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe HealthCheckController do
+ let(:token) { current_application_settings.health_check_access_token }
+ let(:json_response) { JSON.parse(response.body) }
+ let(:xml_response) { Hash.from_xml(response.body)['hash'] }
+
+ describe 'GET #index' do
+ context 'when services are up but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when services are up and an access token is provided' do
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful plaintest response' do
+ get :index, token: token
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful json response' do
+ get :index, token: token, format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+
+ it 'supports successful xml response' do
+ get :index, token: token, format: :xml
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be true
+ end
+
+ it 'supports successful responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+ end
+
+ context 'when a service is down but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when a service is down and an access token is provided' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).with('standard').and_return('The server is on fire')
+ allow(HealthCheck::Utils).to receive(:process_checks).with('email').and_return('Email is on fire')
+ end
+
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure plaintest response' do
+ get :index, token: token
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure json response' do
+ get :index, token: token, format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure xml response' do
+ get :index, token: token, format: :xml
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be false
+ expect(xml_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('Email is on fire')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 8045c8b940d..c61ec174665 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -112,4 +112,26 @@ describe UsersController do
expect(response).to render_template('calendar_activities')
end
end
+
+ describe 'GET #snippets' do
+ before do
+ sign_in(user)
+ end
+
+ context 'format html' do
+ it 'renders snippets page' do
+ get :snippets, username: user.username
+ expect(response.status).to eq(200)
+ expect(response).to render_template('show')
+ end
+ end
+
+ context 'format json' do
+ it 'response with snippets json data' do
+ get :snippets, username: user.username, format: :json
+ expect(response.status).to eq(200)
+ expect(JSON.parse(response.body)).to have_key('html')
+ end
+ end
+ end
end
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 2e9851fb442..7bbe20fec43 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -19,6 +19,7 @@ describe 'Admin Builds' do
visit admin_builds_path
expect(page).to have_selector('.nav-links li.active', text: 'All')
+ expect(page).to have_selector('.row-content-block', text: 'All builds')
expect(page.all('.build-link').size).to eq(4)
expect(page).to have_link 'Cancel all'
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
new file mode 100644
index 00000000000..dec2dedf2b5
--- /dev/null
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+feature "Admin Health Check", feature: true do
+ include WaitForAjax
+
+ before do
+ login_as :admin
+ end
+
+ describe '#show' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it { page.has_text? 'Health Check' }
+ it { page.has_text? 'Health information can be retrieved' }
+
+ it 'has a health check access token' do
+ token = current_application_settings.health_check_access_token
+ expect(page).to have_content("Access token is #{token}")
+ expect(page).to have_selector('#health-check-token', text: token)
+ end
+
+ describe 'reload access token', js: true do
+ it 'changes the access token' do
+ orig_token = current_application_settings.health_check_access_token
+ click_button 'Reset health check access token'
+ wait_for_ajax
+ expect(find('#health-check-token').text).not_to eq orig_token
+ end
+ end
+ end
+
+ context 'when services are up' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it 'shows healthy status' do
+ expect(page).to have_content('Current Status: Healthy')
+ end
+ end
+
+ context 'when a service is down' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).and_return('The server is on fire')
+ visit admin_health_check_path
+ end
+
+ it 'shows unhealthy status' do
+ expect(page).to have_content('Current Status: Unhealthy')
+ expect(page).to have_content('The server is on fire')
+ end
+ end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 090a941958f..f83a78308e3 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -43,6 +43,7 @@ describe "Builds" do
end
it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
+ it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index e47a54fdac5..16fbb5dcecb 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe AuthHelper do
describe "button_based_providers" do
- it 'returns all enabled providers' do
+ it 'returns all enabled providers from devise' do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
expect(helper.button_based_providers).to include(*[:twitter, :github])
end
@@ -17,4 +17,49 @@ describe AuthHelper do
expect(helper.button_based_providers).to eq([])
end
end
+
+ describe 'enabled_button_based_providers' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'all providers are enabled to sign in' do
+ it 'returns all the enabled providers from settings' do
+ expect(helper.enabled_button_based_providers).to include('twitter', 'github')
+ end
+ end
+
+ context 'GitHub OAuth sign in is disabled from application setting' do
+ it "doesn't return github as provider" do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github']
+ )
+
+ expect(helper.enabled_button_based_providers).to include('twitter')
+ expect(helper.enabled_button_based_providers).to_not include('github')
+ end
+ end
+ end
+
+ describe 'button_based_providers_enabled?' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'button based providers enabled' do
+ it 'returns true' do
+ expect(helper.button_based_providers_enabled?).to be true
+ end
+ end
+
+ context 'all the button based providers are disabled via application_setting' do
+ it 'returns false' do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github', 'twitter']
+ )
+
+ expect(helper.button_based_providers_enabled?).to be false
+ end
+ end
+ end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index e68a5ec29ab..c0d2be98e85 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -1,64 +1,65 @@
require 'spec_helper'
describe EventsHelper do
- include ApplicationHelper
- include GitlabMarkdownHelper
+ describe '#event_note' do
+ before do
+ allow(helper).to receive(:current_user).and_return(double)
+ end
- let(:current_user) { create(:user, email: "current@email.com") }
+ it 'should display one line of plain text without alteration' do
+ input = 'A short, plain note'
+ expect(helper.event_note(input)).to match(input)
+ expect(helper.event_note(input)).not_to match(/\.\.\.\z/)
+ end
- it 'should display one line of plain text without alteration' do
- input = 'A short, plain note'
- expect(event_note(input)).to match(input)
- expect(event_note(input)).not_to match(/\.\.\.\z/)
- end
+ it 'should display inline code' do
+ input = 'A note with `inline code`'
+ expected = 'A note with <code>inline code</code>'
- it 'should display inline code' do
- input = 'A note with `inline code`'
- expected = 'A note with <code>inline code</code>'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a note with multiple paragraphs' do
+ input = "Paragraph 1\n\nParagraph 2"
+ expected = 'Paragraph 1...'
- it 'should truncate a note with multiple paragraphs' do
- input = "Paragraph 1\n\nParagraph 2"
- expected = 'Paragraph 1...'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should display the first line of a code block' do
+ input = "```\nCode block\nwith two lines\n```"
+ expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
- it 'should display the first line of a code block' do
- input = "```\nCode block\nwith two lines\n```"
- expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a single long line of text' do
+ text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
+ input = text * 4
+ expected = (text * 2).sub(/.{3}/, '...')
- it 'should truncate a single long line of text' do
- text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
- input = "#{text}#{text}#{text}#{text}" # 200 chars
- expected = "#{text}#{text}".sub(/.{3}/, '...')
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
-
- it 'should preserve a link href when link text is truncated' do
- text = 'The quick brown fox jumped over the lazy dog' # 44 chars
- input = "#{text}#{text}#{text} " # 133 chars
- link_url = 'http://example.com/foo/bar/baz' # 30 chars
- input << link_url
- expected_link_text = 'http://example...</a>'
+ it 'should preserve a link href when link text is truncated' do
+ text = 'The quick brown fox jumped over the lazy dog' # 44 chars
+ input = "#{text}#{text}#{text} " # 133 chars
+ link_url = 'http://example.com/foo/bar/baz' # 30 chars
+ input << link_url
+ expected_link_text = 'http://example...</a>'
- expect(event_note(input)).to match(link_url)
- expect(event_note(input)).to match(expected_link_text)
- end
+ expect(helper.event_note(input)).to match(link_url)
+ expect(helper.event_note(input)).to match(expected_link_text)
+ end
- it 'should preserve code color scheme' do
- input = "```ruby\ndef test\n 'hello world'\nend\n```"
- expected = '<pre class="code highlight js-syntax-highlight ruby">' \
- "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
- " <span class=\"s1\">\'hello world\'</span>\n" \
- "<span class=\"k\">end</span>" \
- '</code></pre>'
- expect(event_note(input)).to eq(expected)
+ it 'should preserve code color scheme' do
+ input = "```ruby\ndef test\n 'hello world'\nend\n```"
+ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
+ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
+ " <span class=\"s1\">\'hello world\'</span>\n" \
+ "<span class=\"k\">end</span>" \
+ '</code></pre>'
+ expect(helper.event_note(input)).to eq(expected)
+ end
end
end
diff --git a/spec/lib/banzai/filter/wiki_link_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
new file mode 100644
index 00000000000..185abbb2108
--- /dev/null
+++ b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+describe Banzai::Filter::WikiLinkFilter, lib: true do
+ include FilterSpecHelper
+
+ let(:namespace) { build_stubbed(:namespace, name: "wiki_link_ns") }
+ let(:project) { build_stubbed(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
+ let(:user) { double }
+ let(:project_wiki) { ProjectWiki.new(project, user) }
+
+ describe "links within the wiki (relative)" do
+ describe "hierarchical links to the current directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='./page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='./page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./page.md')
+ end
+ end
+
+ describe "hierarchical links to the parent directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='../page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('../page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='../page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('../page.md')
+ end
+ end
+
+ describe "hierarchical links to a sub-directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='./subdirectory/page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./subdirectory/page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='./subdirectory/page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./subdirectory/page.md')
+ end
+ end
+
+ describe "non-hierarchical links" do
+ it 'rewrites non-file links to be at the scope of the wiki root' do
+ link = "<a href='page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to match('/wiki_link_ns/wiki_link_project/wikis/page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('page.md')
+ end
+ end
+ end
+
+ describe "links outside the wiki (absolute)" do
+ it "doesn't rewrite links" do
+ link = "<a href='http://example.com/page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('http://example.com/page')
+ end
+ end
+end
diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb
index 3a2b568f4c7..04afbd06929 100644
--- a/spec/lib/ci/ansi2html_spec.rb
+++ b/spec/lib/ci/ansi2html_spec.rb
@@ -4,131 +4,176 @@ describe Ci::Ansi2html, lib: true do
subject { Ci::Ansi2html }
it "prints non-ansi as-is" do
- expect(subject.convert("Hello")).to eq('Hello')
+ expect(subject.convert("Hello")[:html]).to eq('Hello')
end
it "strips non-color-changing controll sequences" do
- expect(subject.convert("Hello \e[2Kworld")).to eq('Hello world')
+ expect(subject.convert("Hello \e[2Kworld")[:html]).to eq('Hello world')
end
it "prints simply red" do
- expect(subject.convert("\e[31mHello\e[0m")).to eq('<span class="term-fg-red">Hello</span>')
+ expect(subject.convert("\e[31mHello\e[0m")[:html]).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply red without trailing reset" do
- expect(subject.convert("\e[31mHello")).to eq('<span class="term-fg-red">Hello</span>')
+ expect(subject.convert("\e[31mHello")[:html]).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply yellow" do
- expect(subject.convert("\e[33mHello\e[0m")).to eq('<span class="term-fg-yellow">Hello</span>')
+ expect(subject.convert("\e[33mHello\e[0m")[:html]).to eq('<span class="term-fg-yellow">Hello</span>')
end
it "prints default on blue" do
- expect(subject.convert("\e[39;44mHello")).to eq('<span class="term-bg-blue">Hello</span>')
+ expect(subject.convert("\e[39;44mHello")[:html]).to eq('<span class="term-bg-blue">Hello</span>')
end
it "prints red on blue" do
- expect(subject.convert("\e[31;44mHello")).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
+ expect(subject.convert("\e[31;44mHello")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
end
it "resets colors after red on blue" do
- expect(subject.convert("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
+ expect(subject.convert("\e[31;44mHello\e[0m world")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
end
it "performs color change from red/blue to yellow/blue" do
- expect(subject.convert("\e[31;44mHello \e[33mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[33mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
end
it "performs color change from red/blue to yellow/green" do
- expect(subject.convert("\e[31;44mHello \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
end
it "performs color change from red/blue to reset to yellow/green" do
- expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
+ expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
end
it "ignores unsupported codes" do
- expect(subject.convert("\e[51mHello\e[0m")).to eq('Hello')
+ expect(subject.convert("\e[51mHello\e[0m")[:html]).to eq('Hello')
end
it "prints light red" do
- expect(subject.convert("\e[91mHello\e[0m")).to eq('<span class="term-fg-l-red">Hello</span>')
+ expect(subject.convert("\e[91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red">Hello</span>')
end
it "prints default on light red" do
- expect(subject.convert("\e[101mHello\e[0m")).to eq('<span class="term-bg-l-red">Hello</span>')
+ expect(subject.convert("\e[101mHello\e[0m")[:html]).to eq('<span class="term-bg-l-red">Hello</span>')
end
it "performs color change from red/blue to default/blue" do
- expect(subject.convert("\e[31;44mHello \e[39mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "performs color change from light red/blue to default/blue" do
- expect(subject.convert("\e[91;44mHello \e[39mworld")).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(subject.convert("\e[91;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "prints bold text" do
- expect(subject.convert("\e[1mHello")).to eq('<span class="term-bold">Hello</span>')
+ expect(subject.convert("\e[1mHello")[:html]).to eq('<span class="term-bold">Hello</span>')
end
it "resets bold text" do
- expect(subject.convert("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span> world')
- expect(subject.convert("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span> world')
+ expect(subject.convert("\e[1mHello\e[21m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
+ expect(subject.convert("\e[1mHello\e[22m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
end
it "prints italic text" do
- expect(subject.convert("\e[3mHello")).to eq('<span class="term-italic">Hello</span>')
+ expect(subject.convert("\e[3mHello")[:html]).to eq('<span class="term-italic">Hello</span>')
end
it "resets italic text" do
- expect(subject.convert("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span> world')
+ expect(subject.convert("\e[3mHello\e[23m world")[:html]).to eq('<span class="term-italic">Hello</span> world')
end
it "prints underlined text" do
- expect(subject.convert("\e[4mHello")).to eq('<span class="term-underline">Hello</span>')
+ expect(subject.convert("\e[4mHello")[:html]).to eq('<span class="term-underline">Hello</span>')
end
it "resets underlined text" do
- expect(subject.convert("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span> world')
+ expect(subject.convert("\e[4mHello\e[24m world")[:html]).to eq('<span class="term-underline">Hello</span> world')
end
it "prints concealed text" do
- expect(subject.convert("\e[8mHello")).to eq('<span class="term-conceal">Hello</span>')
+ expect(subject.convert("\e[8mHello")[:html]).to eq('<span class="term-conceal">Hello</span>')
end
it "resets concealed text" do
- expect(subject.convert("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span> world')
+ expect(subject.convert("\e[8mHello\e[28m world")[:html]).to eq('<span class="term-conceal">Hello</span> world')
end
it "prints crossed-out text" do
- expect(subject.convert("\e[9mHello")).to eq('<span class="term-cross">Hello</span>')
+ expect(subject.convert("\e[9mHello")[:html]).to eq('<span class="term-cross">Hello</span>')
end
it "resets crossed-out text" do
- expect(subject.convert("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span> world')
+ expect(subject.convert("\e[9mHello\e[29m world")[:html]).to eq('<span class="term-cross">Hello</span> world')
end
it "can print 256 xterm fg colors" do
- expect(subject.convert("\e[38;5;16mHello")).to eq('<span class="xterm-fg-16">Hello</span>')
+ expect(subject.convert("\e[38;5;16mHello")[:html]).to eq('<span class="xterm-fg-16">Hello</span>')
end
it "can print 256 xterm fg colors on normal magenta background" do
- expect(subject.convert("\e[38;5;16;45mHello")).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
+ expect(subject.convert("\e[38;5;16;45mHello")[:html]).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
end
it "can print 256 xterm bg colors" do
- expect(subject.convert("\e[48;5;240mHello")).to eq('<span class="xterm-bg-240">Hello</span>')
+ expect(subject.convert("\e[48;5;240mHello")[:html]).to eq('<span class="xterm-bg-240">Hello</span>')
end
it "can print 256 xterm bg colors on normal magenta foreground" do
- expect(subject.convert("\e[48;5;16;35mHello")).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
+ expect(subject.convert("\e[48;5;16;35mHello")[:html]).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
end
it "prints bold colored text vividly" do
- expect(subject.convert("\e[1;31mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(subject.convert("\e[1;31mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
end
it "prints bold light colored text correctly" do
- expect(subject.convert("\e[1;91mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(subject.convert("\e[1;91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ end
+
+ it "prints &lt;" do
+ expect(subject.convert("<")[:html]).to eq('&lt;')
+ end
+
+ describe "incremental update" do
+ shared_examples 'stateable converter' do
+ let(:pass1) { subject.convert(pre_text) }
+ let(:pass2) { subject.convert(pre_text + text, pass1[:state]) }
+
+ it "to returns html to append" do
+ expect(pass2[:append]).to be_truthy
+ expect(pass2[:html]).to eq(html)
+ expect(pass1[:text] + pass2[:text]).to eq(pre_text + text)
+ expect(pass1[:html] + pass2[:html]).to eq(pre_html + html)
+ end
+ end
+
+ context "with split word" do
+ let(:pre_text) { "\e[1mHello" }
+ let(:pre_html) { "<span class=\"term-bold\">Hello</span>" }
+ let(:text) { "\e[1mWorld" }
+ let(:html) { "<span class=\"term-bold\"></span><span class=\"term-bold\">World</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
+
+ context "with split sequence" do
+ let(:pre_text) { "\e[1m" }
+ let(:pre_html) { "<span class=\"term-bold\"></span>" }
+ let(:text) { "Hello" }
+ let(:html) { "<span class=\"term-bold\">Hello</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
+
+ context "with partial sequence" do
+ let(:pre_text) { "Hello\e" }
+ let(:pre_html) { "Hello" }
+ let(:text) { "[1m World" }
+ let(:html) { "<span class=\"term-bold\"> World</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
end
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index c7ab3185378..9eef8ea0976 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -443,12 +443,12 @@ module Ci
context 'when job variables are defined' do
context 'when syntax is correct' do
it 'returns job variables' do
- variables = {
+ variables = {
KEY1: 'value1',
SOME_KEY_2: 'value2'
}
- config = YAML.dump(
+ config = YAML.dump(
{ before_script: ['pwd'],
rspec: {
variables: variables,
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
index acca0b08bab..46a5b7fce65 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -10,8 +10,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
'path/dir_1/subdir/subfile' => { size: 10 },
'path/second_dir' => {},
'path/second_dir/dir_3/file_2' => { size: 10 },
- 'path/second_dir/dir_3/file_3'=> { size: 10 },
- 'another_directory/'=> {},
+ 'path/second_dir/dir_3/file_3' => { size: 10 },
+ 'another_directory/' => {},
'another_file' => {},
'/file/with/absolute_path' => {} }
end
diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb
index b2d7a799810..7d6cce6daec 100644
--- a/spec/lib/gitlab/email/message/repository_push_spec.rb
+++ b/spec/lib/gitlab/email/message/repository_push_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Email::Message::RepositoryPush do
let!(:author) { create(:author, name: 'Author') }
let(:message) do
- described_class.new(Notify, project.id, 'recipient@example.com', opts)
+ described_class.new(Notify, project.id, opts)
end
context 'new commits have been pushed to repository' do
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
index 5852b31ab3a..3325190789b 100644
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -26,8 +26,8 @@ describe Gitlab::Lfs::Router, lib: true do
let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
let(:sample_size) { 499013 }
- let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
- let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+ let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+ let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
describe 'when lfs is disabled' do
before do
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index 5c885a7a982..7b86450a223 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -56,9 +56,6 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction).
and_return(transaction)
- expect(transaction).to receive(:increment).
- with(:method_duration, a_kind_of(Numeric))
-
expect(transaction).to receive(:add_metric).
with(described_class::SERIES, an_instance_of(Hash),
method: 'Dummy.foo')
@@ -139,9 +136,6 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction).
and_return(transaction)
- expect(transaction).to receive(:increment).
- with(:method_duration, a_kind_of(Numeric))
-
expect(transaction).to receive(:add_metric).
with(described_class::SERIES, an_instance_of(Hash),
method: 'Dummy#bar')
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 495c5cbac00..5f7e4a526e6 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -593,7 +593,7 @@ describe Notify do
let(:user) { create(:user) }
let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -606,10 +606,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Pushed new branch master/
end
@@ -624,7 +620,7 @@ describe Notify do
let(:user) { create(:user) }
let(:tree_path) { namespace_project_tree_path(project.namespace, project, "v1.0") }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -637,10 +633,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Pushed new tag v1\.0/
end
@@ -654,7 +646,7 @@ describe Notify do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -667,10 +659,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Deleted branch master/
end
@@ -680,7 +668,7 @@ describe Notify do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -693,10 +681,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Deleted tag v1\.0/
end
@@ -710,7 +694,7 @@ describe Notify do
let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
let(:send_from_committer_email) { false }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -723,10 +707,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/
end
@@ -818,7 +798,7 @@ describe Notify do
let(:commits) { Commit.decorate(compare.commits, nil) }
let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -831,10 +811,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /#{commits.first.title}/
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 1ce22feed5c..d84f3e998f5 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -20,6 +20,15 @@ describe ApplicationSetting, models: true do
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+ describe 'disabled_oauth_sign_in_sources validations' do
+ before do
+ allow(Devise).to receive(:omniauth_providers).and_return([:github])
+ end
+
+ it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) }
+ it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) }
+ end
+
it { is_expected.to validate_presence_of(:max_attachment_size) }
it do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ad47e338a33..ccb100cd96f 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -56,7 +56,7 @@ describe Commit, models: true do
end
it "does not truncates a message with a newline after 80 but less 100 characters" do
- message =<<eos
+ message = <<eos
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit.
Vivamus egestas lacinia lacus, sed rutrum mauris.
eos
diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb
index e31fdb0bffb..b7fc5a92497 100644
--- a/spec/models/concerns/subscribable_spec.rb
+++ b/spec/models/concerns/subscribable_spec.rb
@@ -44,6 +44,16 @@ describe Subscribable, 'Subscribable' do
end
end
+ describe '#subscribe' do
+ it 'subscribes the given user' do
+ expect(resource.subscribed?(user)).to be_falsey
+
+ resource.subscribe(user)
+
+ expect(resource.subscribed?(user)).to be_truthy
+ end
+ end
+
describe '#unsubscribe' do
it 'unsubscribes the given current user' do
resource.subscriptions.create(user: user, subscribed: true)
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index f800f415bd2..534e1b4f128 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -34,14 +34,14 @@ describe ServiceHook, models: true do
it "POSTs to the webhook URL" do
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Service Hook' }
).once
end
it "POSTs the data as JSON" do
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Service Hook' }
).once
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 56a9fbe9720..4078b9e4ff5 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -33,7 +33,7 @@ describe SystemHook, models: true do
Projects::CreateService.new(user, name: 'empty').execute
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -42,7 +42,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -51,7 +51,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -60,7 +60,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -69,7 +69,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -79,7 +79,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_team/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -88,7 +88,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -97,7 +97,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -106,7 +106,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -116,7 +116,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_group/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 37a27d73aab..f9bab487b96 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -95,13 +95,13 @@ describe WebHook, models: true do
it "handles 200 status code" do
WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "Success")
- expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
+ expect(project_hook.execute(@data, 'push_hooks')).to eq([200, 'Success'])
end
it "handles 2xx status codes" do
WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: "Success")
- expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
+ expect(project_hook.execute(@data, 'push_hooks')).to eq([201, 'Success'])
end
end
end
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
index d37590cab75..379c3e1219c 100644
--- a/spec/models/project_services/slack_service/note_message_spec.rb
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -65,7 +65,7 @@ describe SlackService::NoteMessage, models: true do
expect(message.pretext).to eq("Test User commented on " \
"<url|merge request !30> in <somewhere.com|project_name>: " \
"*merge request title*")
- expected_attachments = [
+ expected_attachments = [
{
text: "comment on a merge request",
color: color,
@@ -117,7 +117,7 @@ describe SlackService::NoteMessage, models: true do
expect(message.pretext).to eq("Test User commented on " \
"<url|snippet #5> in <somewhere.com|project_name>: " \
"*snippet title*")
- expected_attachments = [
+ expected_attachments = [
{
text: "comment on a snippet",
color: color,
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 26d4e139396..10e7e693571 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -782,4 +782,23 @@ describe User, models: true do
it { is_expected.to eq([private_project]) }
end
+
+ describe '#viewable_starred_projects' do
+ let(:user) { create(:user) }
+ let(:public_project) { create(:empty_project, :public) }
+ let(:private_project) { create(:empty_project, :private) }
+ let(:private_viewable_project) { create(:empty_project, :private) }
+
+ before do
+ private_viewable_project.team << [user, Gitlab::Access::MASTER]
+
+ [public_project, private_project, private_viewable_project].each do |project|
+ user.toggle_star(project)
+ end
+ end
+
+ it 'returns only starred projects the user can view' do
+ expect(user.viewable_starred_projects).not_to include(private_project)
+ end
+ end
end
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 5ead735be48..0fbc984c061 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -106,8 +106,8 @@ describe API::API, api: true do
context 'authorized user' do
let(:download_headers) do
- { 'Content-Transfer-Encoding'=>'binary',
- 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' }
+ { 'Content-Transfer-Encoding' => 'binary',
+ 'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'should return specific build artifacts' do
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index 96d89e69209..02553d0f8e2 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -34,11 +34,11 @@ describe API::API, api: true do
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(5)
- expect(json_response.find { |e| e['id']==owner.id }['access_level']).to eq(GroupMember::OWNER)
- expect(json_response.find { |e| e['id']==reporter.id }['access_level']).to eq(GroupMember::REPORTER)
- expect(json_response.find { |e| e['id']==developer.id }['access_level']).to eq(GroupMember::DEVELOPER)
- expect(json_response.find { |e| e['id']==master.id }['access_level']).to eq(GroupMember::MASTER)
- expect(json_response.find { |e| e['id']==guest.id }['access_level']).to eq(GroupMember::GUEST)
+ expect(json_response.find { |e| e['id'] == owner.id }['access_level']).to eq(GroupMember::OWNER)
+ expect(json_response.find { |e| e['id'] == reporter.id }['access_level']).to eq(GroupMember::REPORTER)
+ expect(json_response.find { |e| e['id'] == developer.id }['access_level']).to eq(GroupMember::DEVELOPER)
+ expect(json_response.find { |e| e['id'] == master.id }['access_level']).to eq(GroupMember::MASTER)
+ expect(json_response.find { |e| e['id'] == guest.id }['access_level']).to eq(GroupMember::GUEST)
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 9dd43f4fab3..37ab9cc8cfe 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -623,6 +623,12 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
describe 'DELETE :id/issues/:issue_id/subscription' do
@@ -644,5 +650,11 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 6943ff9d26c..b2c7f8d9acb 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -190,4 +190,86 @@ describe API::API, api: true do
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
end
+
+ describe "POST /projects/:id/labels/:label_id/subscription" do
+ context "when label_id is a label title" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when user is already subscribed to label" do
+ before { label1.subscribe(user) }
+
+ it "should return 304" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ post api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id/labels/:label_id/subscription" do
+ before { label1.subscribe(user) }
+
+ context "when label_id is a label title" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when user is already unsubscribed from label" do
+ before { label1.unsubscribe(user) }
+
+ it "should return 304" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ delete api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 66193eac051..f167813e07d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -10,20 +10,20 @@ describe API::API, api: true do
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
- let(:project3) { create(:project, path: 'project3', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :master, user: user, project: project) }
let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
create(:project,
+ :private,
name: 'second_project',
path: 'second_project',
creator_id: user.id,
namespace: user.namespace,
merge_requests_enabled: false,
issues_enabled: false, wiki_enabled: false,
- snippets_enabled: false, visibility_level: 0)
+ snippets_enabled: false)
end
let(:project_member3) do
create(:project_member,
@@ -164,21 +164,18 @@ describe API::API, api: true do
end
describe 'GET /projects/starred' do
+ let(:public_project) { create(:project, :public) }
+
before do
- admin.starred_projects << project
- admin.save!
+ project_member2
+ user3.update_attributes(starred_projects: [project, project2, project3, public_project])
end
- it 'should return the starred projects' do
- get api('/projects/all', admin)
+ it 'should return the starred projects viewable by the user' do
+ get api('/projects/starred', user3)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
-
- expect(json_response).to satisfy do |response|
- response.one? do |entry|
- entry['name'] == project.name
- end
- end
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
end
end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index dfd361a2cdd..cae4656010f 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -402,8 +402,8 @@ describe Ci::API::API do
context 'build has artifacts' do
let(:build) { create(:ci_build, :artifacts) }
let(:download_headers) do
- { 'Content-Transfer-Encoding'=>'binary',
- 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' }
+ { 'Content-Transfer-Encoding' => 'binary',
+ 'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'should download artifact' do
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index cd16a8e6322..b5ed8584c8a 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -118,3 +118,10 @@ describe Admin::DashboardController, "routing" do
expect(get("/admin")).to route_to('admin/dashboard#index')
end
end
+
+# admin_health_check GET /admin/health_check(.:format) admin/health_check#show
+describe Admin::HealthCheckController, "routing" do
+ it "to #show" do
+ expect(get("/admin/health_check")).to route_to('admin/health_check#show')
+ end
+end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 1527eddfa48..de13c0db5d1 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -1,5 +1,42 @@
require 'spec_helper'
+# user GET /u/:username/
+# user_groups GET /u/:username/groups(.:format)
+# user_projects GET /u/:username/projects(.:format)
+# user_contributed_projects GET /u/:username/contributed(.:format)
+# user_snippets GET /u/:username/snippets(.:format)
+# user_calendar GET /u/:username/calendar(.:format)
+# user_calendar_activities GET /u/:username/calendar_activities(.:format)
+describe UsersController, "routing" do
+ it "to #show" do
+ expect(get("/u/User")).to route_to('users#show', username: 'User')
+ end
+
+ it "to #groups" do
+ expect(get("/u/User/groups")).to route_to('users#groups', username: 'User')
+ end
+
+ it "to #projects" do
+ expect(get("/u/User/projects")).to route_to('users#projects', username: 'User')
+ end
+
+ it "to #contributed" do
+ expect(get("/u/User/contributed")).to route_to('users#contributed', username: 'User')
+ end
+
+ it "to #snippets" do
+ expect(get("/u/User/snippets")).to route_to('users#snippets', username: 'User')
+ end
+
+ it "to #calendar" do
+ expect(get("/u/User/calendar")).to route_to('users#calendar', username: 'User')
+ end
+
+ it "to #calendar_activities" do
+ expect(get("/u/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User')
+ end
+end
+
# search GET /search(.:format) search#show
describe SearchController, "routing" do
it "to #show" do
@@ -27,10 +64,6 @@ end
# PUT /snippets/:id(.:format) snippets#update
# DELETE /snippets/:id(.:format) snippets#destroy
describe SnippetsController, "routing" do
- it "to #user_index" do
- expect(get("/s/User")).to route_to('snippets#index', username: 'User')
- end
-
it "to #raw" do
expect(get("/snippets/1/raw")).to route_to('snippets#raw', id: '1')
end
@@ -243,3 +276,13 @@ describe "Groups", "routing" do
expect(get('/1')).to route_to('namespaces#show', id: '1')
end
end
+
+describe HealthCheckController, 'routing' do
+ it 'to #index' do
+ expect(get('/health_check')).to route_to('health_check#index')
+ end
+
+ it 'also supports passing checks in the url' do
+ expect(get('/health_check/email')).to route_to('health_check#index', checks: 'email')
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e43903dbd3c..fd114359467 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -64,7 +64,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_truthy }
+ it { expect(File.exist?(@path)).to be_truthy }
end
context 'wiki_enabled false does not create wiki repository directory' do
@@ -74,7 +74,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_falsey }
+ it { expect(File.exist?(@path)).to be_falsey }
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 1ec27077717..a5cb6f382e4 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -13,8 +13,8 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_falsey }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_falsey }
end
context 'Sidekiq fake' do
@@ -24,8 +24,8 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_truthy }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_truthy }
end
def destroy_project(project, user, params)
diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb
index a3f496359b1..5ebe095743b 100644
--- a/spec/support/jira_service_helper.rb
+++ b/spec/support/jira_service_helper.rb
@@ -2,11 +2,11 @@ module JiraServiceHelper
def jira_service_settings
properties = {
- "title"=>"JIRA tracker",
- "project_url"=>"http://jira.example/issues/?jql=project=A",
- "issues_url"=>"http://jira.example/browse/JIRA-1",
- "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa",
- "api_url"=>"http://jira.example/rest/api/2"
+ "title" => "JIRA tracker",
+ "project_url" => "http://jira.example/issues/?jql=project=A",
+ "issues_url" => "http://jira.example/browse/JIRA-1",
+ "new_issue_url" => "http://jira.example/secure/CreateIssue.jspa",
+ "api_url" => "http://jira.example/rest/api/2"
}
jira_tracker.update_attributes(properties: properties, active: true)
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index eec2e681117..b5ca34bc028 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -36,20 +36,20 @@ module StubGitlabCalls
stub_request(:post, "#{gitlab_url}api/v3/session.json").
with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
- headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 201, body: f, headers: { 'Content-Type'=>'application/json' })
+ headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_user
f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json'))
stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_project_8
@@ -66,19 +66,19 @@ module StubGitlabCalls
f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json'))
stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_projects_owned
stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
+ with(headers: { 'Content-Type' => 'application/json' }).
to_return(status: 200, body: "", headers: {})
end
def stub_ci_enable
stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
+ with(headers: { 'Content-Type' => 'application/json' }).
to_return(status: 200, body: "", headers: {})
end
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index 3600c771075..439da765c2c 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -6,29 +6,66 @@ describe EmailsOnPushWorker do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+ let(:recipients) { user.email }
+ let(:perform) { subject.perform(project.id, recipients, data.stringify_keys) }
subject { EmailsOnPushWorker.new }
- before do
- allow(Project).to receive(:find).and_return(project)
- end
-
describe "#perform" do
- it "sends mail" do
- subject.perform(project.id, user.email, data.stringify_keys)
+ context "when there are no errors in sending" do
+ let(:email) { ActionMailer::Base.deliveries.last }
+
+ before { perform }
- email = ActionMailer::Base.deliveries.last
- expect(email.subject).to include('Change some files')
- expect(email.to).to eq([user.email])
+ it "sends a mail with the correct subject" do
+ expect(email.subject).to include('Change some files')
+ end
+
+ it "sends the mail to the correct recipient" do
+ expect(email.to).to eq([user.email])
+ end
end
- it "gracefully handles an input SMTP error" do
- ActionMailer::Base.deliveries.clear
- allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError)
+ context "when there is an SMTP error" do
+ before do
+ ActionMailer::Base.deliveries.clear
+ allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError)
+ perform
+ end
+
+ it "gracefully handles an input SMTP error" do
+ expect(ActionMailer::Base.deliveries.count).to eq(0)
+ end
+ end
+
+ context "when there are multiple recipients" do
+ let(:recipients) do
+ 1.upto(5).map { |i| user.email.sub('@', "+#{i}@") }.join("\n")
+ end
+
+ before do
+ # This is a hack because we modify the mail object before sending, for efficency,
+ # but the TestMailer adapter just appends the objects to an array. To clone a mail
+ # object, create a new one!
+ # https://github.com/mikel/mail/issues/314#issuecomment-12750108
+ allow_any_instance_of(Mail::TestMailer).to receive(:deliver!).and_wrap_original do |original, mail|
+ original.call(Mail.new(mail.encoded))
+ end
+
+ ActionMailer::Base.deliveries.clear
+ end
- subject.perform(project.id, user.email, data.stringify_keys)
+ it "sends the mail to each of the recipients" do
+ perform
+ expect(ActionMailer::Base.deliveries.count).to eq(5)
+ expect(ActionMailer::Base.deliveries.map(&:to).flatten).to contain_exactly(*recipients.split)
+ end
- expect(ActionMailer::Base.deliveries.count).to eq(0)
+ it "only generates the mail once" do
+ expect(Notify).to receive(:repository_push_email).once.and_call_original
+ expect(Premailer::Rails::CustomizedPremailer).to receive(:new).once.and_call_original
+ perform
+ end
end
end
end
diff --git a/vendor/assets/javascripts/jquery.scrollTo.js b/vendor/assets/javascripts/jquery.scrollTo.js
new file mode 100755
index 00000000000..7ba17766b70
--- /dev/null
+++ b/vendor/assets/javascripts/jquery.scrollTo.js
@@ -0,0 +1,210 @@
+/*!
+ * jQuery.scrollTo
+ * Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
+ * Licensed under MIT
+ * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
+ * @projectDescription Lightweight, cross-browser and highly customizable animated scrolling with jQuery
+ * @author Ariel Flesler
+ * @version 2.1.2
+ */
+;(function(factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['jquery'], factory);
+ } else if (typeof module !== 'undefined' && module.exports) {
+ // CommonJS
+ module.exports = factory(require('jquery'));
+ } else {
+ // Global
+ factory(jQuery);
+ }
+})(function($) {
+ 'use strict';
+
+ var $scrollTo = $.scrollTo = function(target, duration, settings) {
+ return $(window).scrollTo(target, duration, settings);
+ };
+
+ $scrollTo.defaults = {
+ axis:'xy',
+ duration: 0,
+ limit:true
+ };
+
+ function isWin(elem) {
+ return !elem.nodeName ||
+ $.inArray(elem.nodeName.toLowerCase(), ['iframe','#document','html','body']) !== -1;
+ }
+
+ $.fn.scrollTo = function(target, duration, settings) {
+ if (typeof duration === 'object') {
+ settings = duration;
+ duration = 0;
+ }
+ if (typeof settings === 'function') {
+ settings = { onAfter:settings };
+ }
+ if (target === 'max') {
+ target = 9e9;
+ }
+
+ settings = $.extend({}, $scrollTo.defaults, settings);
+ // Speed is still recognized for backwards compatibility
+ duration = duration || settings.duration;
+ // Make sure the settings are given right
+ var queue = settings.queue && settings.axis.length > 1;
+ if (queue) {
+ // Let's keep the overall duration
+ duration /= 2;
+ }
+ settings.offset = both(settings.offset);
+ settings.over = both(settings.over);
+
+ return this.each(function() {
+ // Null target yields nothing, just like jQuery does
+ if (target === null) return;
+
+ var win = isWin(this),
+ elem = win ? this.contentWindow || window : this,
+ $elem = $(elem),
+ targ = target,
+ attr = {},
+ toff;
+
+ switch (typeof targ) {
+ // A number will pass the regex
+ case 'number':
+ case 'string':
+ if (/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)) {
+ targ = both(targ);
+ // We are done
+ break;
+ }
+ // Relative/Absolute selector
+ targ = win ? $(targ) : $(targ, elem);
+ /* falls through */
+ case 'object':
+ if (targ.length === 0) return;
+ // DOMElement / jQuery
+ if (targ.is || targ.style) {
+ // Get the real position of the target
+ toff = (targ = $(targ)).offset();
+ }
+ }
+
+ var offset = $.isFunction(settings.offset) && settings.offset(elem, targ) || settings.offset;
+
+ $.each(settings.axis.split(''), function(i, axis) {
+ var Pos = axis === 'x' ? 'Left' : 'Top',
+ pos = Pos.toLowerCase(),
+ key = 'scroll' + Pos,
+ prev = $elem[key](),
+ max = $scrollTo.max(elem, axis);
+
+ if (toff) {// jQuery / DOMElement
+ attr[key] = toff[pos] + (win ? 0 : prev - $elem.offset()[pos]);
+
+ // If it's a dom element, reduce the margin
+ if (settings.margin) {
+ attr[key] -= parseInt(targ.css('margin'+Pos), 10) || 0;
+ attr[key] -= parseInt(targ.css('border'+Pos+'Width'), 10) || 0;
+ }
+
+ attr[key] += offset[pos] || 0;
+
+ if (settings.over[pos]) {
+ // Scroll to a fraction of its width/height
+ attr[key] += targ[axis === 'x'?'width':'height']() * settings.over[pos];
+ }
+ } else {
+ var val = targ[pos];
+ // Handle percentage values
+ attr[key] = val.slice && val.slice(-1) === '%' ?
+ parseFloat(val) / 100 * max
+ : val;
+ }
+
+ // Number or 'number'
+ if (settings.limit && /^\d+$/.test(attr[key])) {
+ // Check the limits
+ attr[key] = attr[key] <= 0 ? 0 : Math.min(attr[key], max);
+ }
+
+ // Don't waste time animating, if there's no need.
+ if (!i && settings.axis.length > 1) {
+ if (prev === attr[key]) {
+ // No animation needed
+ attr = {};
+ } else if (queue) {
+ // Intermediate animation
+ animate(settings.onAfterFirst);
+ // Don't animate this axis again in the next iteration.
+ attr = {};
+ }
+ }
+ });
+
+ animate(settings.onAfter);
+
+ function animate(callback) {
+ var opts = $.extend({}, settings, {
+ // The queue setting conflicts with animate()
+ // Force it to always be true
+ queue: true,
+ duration: duration,
+ complete: callback && function() {
+ callback.call(elem, targ, settings);
+ }
+ });
+ $elem.animate(attr, opts);
+ }
+ });
+ };
+
+ // Max scrolling position, works on quirks mode
+ // It only fails (not too badly) on IE, quirks mode.
+ $scrollTo.max = function(elem, axis) {
+ var Dim = axis === 'x' ? 'Width' : 'Height',
+ scroll = 'scroll'+Dim;
+
+ if (!isWin(elem))
+ return elem[scroll] - $(elem)[Dim.toLowerCase()]();
+
+ var size = 'client' + Dim,
+ doc = elem.ownerDocument || elem.document,
+ html = doc.documentElement,
+ body = doc.body;
+
+ return Math.max(html[scroll], body[scroll]) - Math.min(html[size], body[size]);
+ };
+
+ function both(val) {
+ return $.isFunction(val) || $.isPlainObject(val) ? val : { top:val, left:val };
+ }
+
+ // Add special hooks so that window scroll properties can be animated
+ $.Tween.propHooks.scrollLeft =
+ $.Tween.propHooks.scrollTop = {
+ get: function(t) {
+ return $(t.elem)[t.prop]();
+ },
+ set: function(t) {
+ var curr = this.get(t);
+ // If interrupt is true and user scrolled, stop animating
+ if (t.options.interrupt && t._last && t._last !== curr) {
+ return $(t.elem).stop();
+ }
+ var next = Math.round(t.now);
+ // Don't waste CPU
+ // Browsers don't render floating point scroll
+ if (curr !== next) {
+ $(t.elem)[t.prop](next);
+ t._last = this.get(t);
+ }
+ }
+ };
+
+ // AMD requirement
+ return $scrollTo;
+});