diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-05-13 17:14:46 -0500 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-05-13 17:14:46 -0500 |
commit | 0958f331d1af67514c80f0bc582c93b0ae1ed491 (patch) | |
tree | 512f37d66e349b1b9e6420cf7739aea7ee1ac324 | |
parent | 509654b3784da2a084a0c8303e9c6cc1498b8d2b (diff) | |
parent | d7a28f3c12950d56a187148fda9dce39a9f2bf08 (diff) | |
download | gitlab-ce-0958f331d1af67514c80f0bc582c93b0ae1ed491.tar.gz |
Merge remote-tracking branch 'origin/master' into docker-registry
# Conflicts:
# config/initializers/1_settings.rb
321 files changed, 3010 insertions, 3646 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 7cccb9c9462..3866153b3b8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,20 @@ 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 - Project#open_branches has been cleaned up and no longer loads entire records into memory. + - Escape HTML in commit titles in system note messages + - Improve multiple branch push performance by memoizing permission checking - Log to application.log when an admin starts and stops impersonating a user - Updated gitlab_git to 10.1.0 - GitAccess#protected_tag? no longer loads all tags just to check if a single one exists - 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. @@ -17,21 +23,50 @@ 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 - Added authentication service for Container Registry - Display informative message when new milestone is created + - Sanitize milestones and labels titles + - Support multi-line tag messages. !3833 (Calin Seciu) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Added button to toggle whitespaces changes on diff view - 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) - Expire repository exists? and has_visible_content? caches after a push if necessary - 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 + - 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 @@ -198,7 +198,7 @@ gem 'licensee', '~> 8.0.0' gem "rack-attack", '~> 4.3.1' # Ace editor -gem 'ace-rails-ap', '~> 2.0.1' +gem 'ace-rails-ap', '~> 4.0.2' # Keyboard shortcuts gem 'mousetrap-rails', '~> 1.4.6' @@ -219,7 +219,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' @@ -244,7 +243,6 @@ group :development do gem "foreman" gem 'brakeman', '~> 3.2.0', require: false - gem "annotate", "~> 2.7.0" gem 'letter_opener_web', '~> 1.3.0' gem 'quiet_assets', '~> 1.0.2' gem 'rerun', '~> 0.11.0' @@ -327,7 +325,6 @@ gem "mail_room", "~> 0.7" gem 'email_reply_parser', '~> 0.5.8' ## CI -gem 'activerecord-deprecated_finders', '~> 1.0.3' gem 'activerecord-session_store', '~> 0.1.0' gem "nested_form", '~> 0.3.2' @@ -336,3 +333,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 36efb66665f..b55764504c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ GEM specs: CFPropertyList (2.3.2) RedCloth (4.2.9) - ace-rails-ap (2.0.1) + ace-rails-ap (4.0.2) actionmailer (4.2.6) actionpack (= 4.2.6) actionview (= 4.2.6) @@ -33,7 +33,6 @@ GEM activemodel (= 4.2.6) activesupport (= 4.2.6) arel (~> 6.0) - activerecord-deprecated_finders (1.0.4) activerecord-session_store (0.1.2) actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) @@ -51,9 +50,6 @@ GEM activerecord (>= 3.0) akismet (2.0.0) allocations (1.0.4) - annotate (2.7.0) - activerecord (>= 3.2, < 6.0) - rake (~> 10.4) arel (6.0.3) asana (0.4.0) faraday (~> 0.9) @@ -406,6 +402,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 @@ -436,8 +434,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 @@ -886,15 +882,13 @@ PLATFORMS DEPENDENCIES RedCloth (~> 4.2.9) - ace-rails-ap (~> 2.0.1) - activerecord-deprecated_finders (~> 1.0.3) + ace-rails-ap (~> 4.0.2) activerecord-session_store (~> 0.1.0) acts-as-taggable-on (~> 3.4) addressable (~> 2.3.8) after_commit_queue akismet (~> 2.0) allocations (~> 1.0) - annotate (~> 2.7.0) asana (~> 0.4.0) asciidoctor (~> 1.5.2) attr_encrypted (~> 1.3.4) @@ -953,13 +947,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) jwt @@ -1064,4 +1058,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.12.1 + 1.12.3 diff --git a/README.md b/README.md index 5e41665a6ba..418d06a45a5 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ There are two editions of GitLab: On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: -- [Subscriptions](https://about.gitlab.com/subscription/) +- [Subscriptions](https://about.gitlab.com/pricing/) - [Consultancy](https://about.gitlab.com/consultancy/) - [Community](https://about.gitlab.com/community/) - [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service -- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. +- [GitLab Enterprise Edition](https://about.gitlab.com/features/#enterprise) with additional features aimed at larger organizations. - [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. ## Requirements diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 5bac8eef1cb..bffce5a0c0f 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -204,6 +204,7 @@ $ -> $('.header-content .title').toggle() $('.header-content .navbar-collapse').toggle() $('.navbar-toggle').toggleClass('active') + $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") # Show/hide comments on diff $("body").on "click", ".js-toggle-diff-comments", (e) -> diff --git a/app/assets/javascripts/ci/application.js.coffee b/app/assets/javascripts/ci/application.js.coffee index 05aa0f366bb..ca24c1d759f 100644 --- a/app/assets/javascripts/ci/application.js.coffee +++ b/app/assets/javascripts/ci/application.js.coffee @@ -1,34 +1,6 @@ -# This is a manifest file that'll be compiled into application.js, which will include all the files -# listed below. -# -# Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -# or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. -# -# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -# the compiled file. -# -# WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD -# GO AFTER THE REQUIRES BELOW. -# #= require pager #= require jquery_nested_form #= require_tree . -# -$(document).on 'click', '.edit-runner-link', (event) -> - event.preventDefault() - - descr = $(this).closest('.runner-description').first() - descr.addClass('hide') - form = descr.next('.runner-description-form') - descrInput = form.find('input.description') - originalValue = descrInput.val() - form.removeClass('hide') - form.find('.cancel').on 'click', (event) -> - event.preventDefault() - - form.addClass('hide') - descrInput.val(originalValue) - descr.removeClass('hide') $(document).on 'click', '.assign-all-runner', -> $(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..') 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/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 17a5a057a94..f58647988a2 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -9,11 +9,12 @@ class @MergeRequestWidget constructor: (@opts) -> $('#modal_merge_info').modal(show: false) @firstCICheck = true - @readyForCICheck = true + @readyForCICheck = false clearInterval @fetchBuildStatusInterval @clearEventListeners() @addEventListeners() + @getCIStatus(false) @pollCIStatus() notifyPermissions() @@ -71,7 +72,7 @@ class @MergeRequestWidget if data.status is '' return - if @firstCiCheck || data.status isnt @opts.ci_status and data.status? + if @firstCICheck || data.status isnt @opts.ci_status and data.status? @opts.ci_status = data.status @showCIStatus data.status if data.coverage @@ -79,7 +80,7 @@ class @MergeRequestWidget # The first check should only update the UI, a notification # should only be displayed on status changes - if showNotification and not @firstCiCheck + if showNotification and not @firstCICheck status = @ciLabelForStatus(data.status) if status is "preparing" @@ -102,7 +103,7 @@ class @MergeRequestWidget @close() Turbolinks.visit _this.opts.builds_path ) - @firstCiCheck = false + @firstCICheck = false showCIStatus: (state) -> $('.ci_widget').hide() diff --git a/app/assets/javascripts/shortcuts_issuable.coffee b/app/assets/javascripts/shortcuts_issuable.coffee index ad9b3c1c6bf..ccb42ab2168 100644 --- a/app/assets/javascripts/shortcuts_issuable.coffee +++ b/app/assets/javascripts/shortcuts_issuable.coffee @@ -6,6 +6,10 @@ class @ShortcutsIssuable extends ShortcutsNavigation super() Mousetrap.bind('a', @openSidebarDropdown.bind(@, 'assignee')) Mousetrap.bind('m', @openSidebarDropdown.bind(@, 'milestone')) + Mousetrap.bind('r', => + @replyWithSelectedText() + return false + ) Mousetrap.bind('j', => @prevIssue() return false diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee index 860d4f438d0..ea4ac52da31 100644 --- a/app/assets/javascripts/sidebar.js.coffee +++ b/app/assets/javascripts/sidebar.js.coffee @@ -12,7 +12,7 @@ toggleSidebar = -> niceScrollBars.updateScrollBar(); ), 300 -$(document).on("click", '.toggle-nav-collapse', (e) -> +$(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) -> e.preventDefault() toggleSidebar() 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 c83cf881596..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,9 +18,13 @@ } &:hover { - background-color: $color-darker; + background-color: $color-dark; a { color: #fff; + + h3 { + color: #fff; + } } } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 97f9d582007..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 #eee; + border-bottom: 1px solid $btn-gray-hover; .center-logo { - margin: 11px 0; + margin: 8px 0; text-align: center; #tanuki-logo, img { @@ -22,14 +22,18 @@ header { } &.navbar-gitlab { - padding: 0 20px; + padding: 0 16px; z-index: 100; margin-bottom: 0; - min-height: $header-height; + height: $header-height; background-color: $background-color; border: none; border-bottom: 1px solid $border-color; + @media (max-width: $screen-xs-min) { + padding: 0 16px; + } + &.with-horizontal-nav { border-bottom: none; } @@ -60,16 +64,44 @@ header { margin: 6px 0; border-radius: 0; position: absolute; - right: 2px; + right: -10px; + padding: 6px 10px; &:hover { - background-color: #eee; + background-color: $btn-gray-hover; } + &.active { color: $gl-icon-color; } } } + + &.header-collapsed { + padding: 0 16px; + } + + .side-nav-toggle { + display: none; + position: absolute; + left: -10px; + margin: 6px 0; + padding: 6px 10px; + border: none; + background-color: $background-color; + + &:hover { + background-color: $btn-gray-hover; + } + + &:focus { + outline: none; + } + + @media (max-width: $screen-xs-min) { + display: block; + } + } } .header-content { @@ -77,6 +109,10 @@ header { height: $header-height; padding-right: 40px; + @media (max-width: $screen-xs-min) { + padding-left: 40px; + } + @media (min-width: $screen-sm-min) { padding-right: 0; } @@ -145,6 +181,10 @@ header { @media (min-width: $screen-md-min) { @include collapsed-header; } + + @media (max-width: $screen-xs-min) { + margin-left: 0; + } } .header-expanded { @@ -153,6 +193,10 @@ header { @media (min-width: $screen-md-min) { margin-left: $sidebar_width; } + + @media (max-width: $screen-xs-min) { + margin-left: 0; + } } @media (max-width: $screen-xs-max) { diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 7c18e93a261..a81fcb1c6b3 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -201,9 +201,22 @@ border-bottom: 1px solid $border-color; transition-duration: .3s; + .container-fluid { + position: relative; + } + .controls { float: right; - padding: 7px 5px 0 0; + padding: 7px 0 0; + + @media (max-width: $screen-xs-min) { + float: none; + padding: 0 9px; + + .dropdown-new { + width: 100%; + } + } i { color: $layout-link-gray; @@ -221,6 +234,10 @@ .dropdown { margin-left: 7px; + + @media (max-width: $screen-xs-min) { + margin-left: 0; + } } } @@ -256,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 e940fd7286e..f90d7a806d3 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -98,7 +98,7 @@ } .sidebar-user { - padding: 9px 22px; + padding: 7px 22px; position: fixed; bottom: 40px; width: $sidebar_width; @@ -210,15 +210,33 @@ } } +.sidebar-wrapper { + &.hidden-nav { + width: 0; + } +} + .page-sidebar-collapsed { padding-left: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + padding-left: 0; + } + .sidebar-wrapper { width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + } + .header-logo { width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + } + a { padding-left: ($sidebar_collapsed_width - 36) / 2; @@ -244,12 +262,22 @@ .collapse-nav a { width: $sidebar_collapsed_width; + + @media (max-width: $screen-xs-min) { + width: 0; + } } .sidebar-user { padding-left: ($sidebar_collapsed_width - 36) / 2; width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + padding-left: 0; + padding-right: 0; + } + .username { display: none; } @@ -258,6 +286,10 @@ .layout-nav { padding-right: $sidebar_collapsed_width; + + @media (max-width: $screen-xs-min) { + padding-right: 0;; + } } } @@ -268,6 +300,10 @@ padding-left: $sidebar_width; } + @media (max-width: $screen-xs-min) { + padding-left: 0; + } + .sidebar-wrapper { width: $sidebar_width; @@ -276,7 +312,7 @@ } .nav-sidebar li a { - width: 230px; + width: $sidebar_width; &.back-link { i { @@ -287,7 +323,17 @@ } .layout-nav { - padding-right: $sidebar_width; + @media (max-width: $screen-xs-min) { + padding-right: 0;; + } + + @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) { + padding-right: 62px; + } + + @media (min-width: $screen-md-min) { + padding-right: $sidebar_width; + } } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index ccb4e5381b7..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; @@ -215,6 +215,7 @@ $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color; $btn-active-gray: #ececec; $btn-placeholder-gray: #c7c7c7; $btn-white-active: #848484; +$btn-gray-hover: #eee; /* * Award emoji 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/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index abc5a0e9877..843379a3f54 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -218,7 +218,7 @@ .btn { display: inline-block; - width: 48%; + width: 46%; } } } 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/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb index 23a4ef21ea2..2a88350a4ca 100644 --- a/app/controllers/dashboard/labels_controller.rb +++ b/app/controllers/dashboard/labels_controller.rb @@ -1,6 +1,6 @@ class Dashboard::LabelsController < Dashboard::ApplicationController def index - labels = Label.where(project_id: projects).select(:title, :color).uniq(:title) + labels = Label.where(project_id: projects).select(:id, :title, :color).uniq(:title) respond_to do |format| format.json { render json: labels } 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 f17d02a7a3f..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) @@ -205,7 +205,8 @@ module ProjectsHelper end def default_url_to_repo(project = @project) - if default_clone_protocol == "ssh" + case default_clone_protocol + when 'ssh' project.ssh_url_to_repo else project.http_url_to_repo 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/abuse_report.rb b/app/models/abuse_report.rb index b61f5123127..b01a244032d 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: abuse_reports -# -# id :integer not null, primary key -# reporter_id :integer -# user_id :integer -# message :text -# created_at :datetime -# updated_at :datetime -# - class AbuseReport < ActiveRecord::Base belongs_to :reporter, class_name: 'User' belongs_to :user diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 4528760fefa..4cf8dd9a8ce 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: appearances -# -# id :integer not null, primary key -# title :string -# description :text -# header_logo :string -# logo :string -# created_at :datetime not null -# updated_at :datetime not null -# - class Appearance < ActiveRecord::Base validates :title, presence: true validates :description, presence: true diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 72ec91d2909..1a10768655f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -1,63 +1,13 @@ -# == Schema Information -# -# Table name: application_settings -# -# id :integer not null, primary key -# default_projects_limit :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string -# default_branch_protection :integer default(2) -# restricted_visibility_levels :text -# version_check_enabled :boolean default(TRUE) -# max_attachment_size :integer default(10), not null -# default_project_visibility :integer -# default_snippet_visibility :integer -# restricted_signup_domains :text -# user_oauth_applications :boolean default(TRUE) -# after_sign_out_path :string -# session_expire_delay :integer default(10080), not null -# import_sources :text -# help_page_text :text -# admin_notification_email :string -# shared_runners_enabled :boolean default(TRUE), not null -# max_artifacts_size :integer default(100), not null -# runners_registration_token :string -# require_two_factor_authentication :boolean default(FALSE) -# two_factor_grace_period :integer default(48) -# metrics_enabled :boolean default(FALSE) -# metrics_host :string default("localhost") -# metrics_pool_size :integer default(16) -# metrics_timeout :integer default(10) -# metrics_method_call_threshold :integer default(10) -# recaptcha_enabled :boolean default(FALSE) -# recaptcha_site_key :string -# recaptcha_private_key :string -# metrics_port :integer default(8089) -# metrics_sample_interval :integer default(15) -# sentry_enabled :boolean default(FALSE) -# sentry_dsn :string -# akismet_enabled :boolean default(FALSE) -# akismet_api_key :string -# email_author_in_body :boolean default(FALSE) -# default_group_visibility :integer -# repository_checks_enabled :boolean default(FALSE) -# metrics_packet_size :integer default(1) -# shared_runners_text :text -# - 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 @@ -121,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) @@ -159,6 +120,7 @@ class ApplicationSetting < ActiveRecord::Base recaptcha_enabled: false, akismet_enabled: false, repository_checks_enabled: true, + disabled_oauth_sign_in_sources: [] ) end @@ -185,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/audit_event.rb b/app/models/audit_event.rb index 44b090260e7..967ffd46db0 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: audit_events -# -# id :integer not null, primary key -# author_id :integer not null -# type :string not null -# entity_id :integer not null -# entity_type :string not null -# details :text -# created_at :datetime -# updated_at :datetime -# - class AuditEvent < ActiveRecord::Base serialize :details, Hash diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 075ac733bfc..61498140f27 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: broadcast_messages -# -# id :integer not null, primary key -# message :text not null -# starts_at :datetime -# ends_at :datetime -# created_at :datetime -# updated_at :datetime -# color :string -# font :string -# - class BroadcastMessage < ActiveRecord::Base include Sortable diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 4bc3a225e2c..92327bdb08d 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -1,40 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string -# user_id :integer -# type :string -# target_url :string -# description :string -# artifacts_file :text -# gl_project_id :integer -# artifacts_metadata :text -# erased_by_id :integer -# erased_at :datetime -# - module Ci class Build < CommitStatus belongs_to :runner, class_name: 'Ci::Runner' @@ -132,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 @@ -238,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/commit.rb b/app/models/ci/commit.rb index 4ac4e0fb8b2..f4b61c75ab6 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -1,25 +1,3 @@ -# == Schema Information -# -# Table name: ci_commits -# -# id :integer not null, primary key -# project_id :integer -# ref :string -# sha :string -# before_sha :string -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime -# gl_project_id :integer -# status :string -# started_at :datetime -# finished_at :datetime -# duration :integer -# - module Ci class Commit < ActiveRecord::Base extend Ci::Model diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index add59a08892..819064f99bb 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -1,28 +1,10 @@ -# == Schema Information -# -# Table name: ci_runners -# -# id :integer not null, primary key -# token :string -# created_at :datetime -# updated_at :datetime -# description :string -# contacted_at :datetime -# active :boolean default(TRUE), not null -# is_shared :boolean default(FALSE) -# name :string -# version :string -# revision :string -# platform :string -# architecture :string -# - module Ci class Runner < ActiveRecord::Base 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/ci/runner_project.rb b/app/models/ci/runner_project.rb index 7b16f207a26..4b44ffa886e 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: ci_runner_projects -# -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - module Ci class RunnerProject < ActiveRecord::Base extend Ci::Model diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 4f3f4d79fac..a0b19b51a12 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: ci_triggers -# -# id :integer not null, primary key -# token :string -# project_id :integer -# deleted_at :datetime -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - module Ci class Trigger < ActiveRecord::Base extend Ci::Model diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 9973d2e5ade..872d5fb31de 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: ci_trigger_requests -# -# id :integer not null, primary key -# trigger_id :integer not null -# variables :text -# created_at :datetime -# updated_at :datetime -# commit_id :integer -# - module Ci class TriggerRequest < ActiveRecord::Base extend Ci::Model diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 4229fe085a1..10802f64813 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: ci_variables -# -# id :integer not null, primary key -# project_id :integer -# key :string -# value :text -# encrypted_value :text -# encrypted_value_salt :string -# encrypted_value_iv :string -# gl_project_id :integer -# - module Ci class Variable < ActiveRecord::Base extend Ci::Model diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 1260c448de3..cacbc13b391 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,40 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string -# user_id :integer -# type :string -# target_url :string -# description :string -# artifacts_file :text -# gl_project_id :integer -# artifacts_metadata :text -# erased_by_id :integer -# erased_at :datetime -# - class CommitStatus < ActiveRecord::Base include Statuseable diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 2e4efc4e8d8..c1248b53031 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -124,8 +124,8 @@ module Issuable end def with_label(title) - if title.is_a?(Array) && title.count > 1 - joins(:labels).where(labels: { title: title }).group('issues.id').having("count(distinct labels.title) = #{title.count}") + if title.is_a?(Array) && title.size > 1 + joins(:labels).where(labels: { title: title }).group(arel_table[:id]).having("COUNT(DISTINCT labels.title) = #{title.size}") else joins(:labels).where(labels: { title: title }) end @@ -160,6 +160,10 @@ module Issuable notes.awards.where(note: "thumbsup").count end + def user_notes_count + notes.user.count + end + def subscribed_without_subscriptions?(user) participants(user).include?(user) end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 98f71ae8cb0..b381d225485 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -43,8 +43,8 @@ module Mentionable self end - def all_references(current_user = self.author, text = nil) - ext = Gitlab::ReferenceExtractor.new(self.project, current_user, self.author) + def all_references(current_user = nil, text = nil) + ext = Gitlab::ReferenceExtractor.new(self.project, current_user || self.author, self.author) if text ext.analyze(text) 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/deploy_key.rb b/app/models/deploy_key.rb index 43cf625f770..2c525d4cd7a 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string -# type :string -# fingerprint :string -# public :boolean default(FALSE), not null -# - class DeployKey < Key has_many :deploy_keys_projects, dependent: :destroy has_many :projects, through: :deploy_keys_projects diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb index 18db521741f..ae8486bd9ac 100644 --- a/app/models/deploy_keys_project.rb +++ b/app/models/deploy_keys_project.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: deploy_keys_projects -# -# id :integer not null, primary key -# deploy_key_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - class DeployKeysProject < ActiveRecord::Base belongs_to :project belongs_to :deploy_key diff --git a/app/models/email.rb b/app/models/email.rb index eae2472f337..32a412ab878 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: emails -# -# id :integer not null, primary key -# user_id :integer not null -# email :string not null -# created_at :datetime -# updated_at :datetime -# - class Email < ActiveRecord::Base include Sortable diff --git a/app/models/event.rb b/app/models/event.rb index 25c7c3e6dc7..17ee48b91a8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# target_type :string -# target_id :integer -# title :string -# data :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# action :integer -# author_id :integer -# - class Event < ActiveRecord::Base include Sortable default_scope { where.not(author_id: nil) } diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb index 9b0c6263a96..9803bae0bee 100644 --- a/app/models/forked_project_link.rb +++ b/app/models/forked_project_link.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: forked_project_links -# -# id :integer not null, primary key -# forked_to_project_id :integer not null -# forked_from_project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - class ForkedProjectLink < ActiveRecord::Base belongs_to :forked_to_project, class_name: Project belongs_to :forked_from_project, class_name: Project diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index d4afd8cbe84..fa54e3540d0 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -1,40 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string -# user_id :integer -# type :string -# target_url :string -# description :string -# artifacts_file :text -# gl_project_id :integer -# artifacts_metadata :text -# erased_by_id :integer -# erased_at :datetime -# - class GenericCommitStatus < CommitStatus before_validation :set_default_values diff --git a/app/models/group.rb b/app/models/group.rb index cff76877958..aec92e335e6 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,20 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string not null -# path :string not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string -# description :string default(""), not null -# avatar :string -# share_with_group_lock :boolean default(FALSE) -# visibility_level :integer default(20), not null -# - require 'carrierwave/orm/activerecord' class Group < Namespace diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index 2b8f34a0568..ba42a8eeb70 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -1,25 +1,3 @@ -# == Schema Information -# -# Table name: web_hooks -# -# id :integer not null, primary key -# url :string(2000) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null -# enable_ssl_verification :boolean default(TRUE) -# build_events :boolean default(FALSE), not null -# wiki_page_events :boolean default(FALSE), not null -# token :string -# - class ProjectHook < WebHook belongs_to :project diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 0e176de5ef8..eef24052a06 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -1,25 +1,3 @@ -# == Schema Information -# -# Table name: web_hooks -# -# id :integer not null, primary key -# url :string(2000) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null -# enable_ssl_verification :boolean default(TRUE) -# build_events :boolean default(FALSE), not null -# wiki_page_events :boolean default(FALSE), not null -# token :string -# - class ServiceHook < WebHook belongs_to :service diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index ad508cbbcb8..777bad1e724 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -1,25 +1,3 @@ -# == Schema Information -# -# Table name: web_hooks -# -# id :integer not null, primary key -# url :string(2000) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null -# enable_ssl_verification :boolean default(TRUE) -# build_events :boolean default(FALSE), not null -# wiki_page_events :boolean default(FALSE), not null -# token :string -# - class SystemHook < WebHook def async_execute(data, hook_name) Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name) diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 8e58c9583ab..8b87b6c3d64 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -1,25 +1,3 @@ -# == Schema Information -# -# Table name: web_hooks -# -# id :integer not null, primary key -# url :string(2000) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null -# enable_ssl_verification :boolean default(TRUE) -# build_events :boolean default(FALSE), not null -# wiki_page_events :boolean default(FALSE), not null -# token :string -# - class WebHook < ActiveRecord::Base include Sortable include HTTParty @@ -60,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/identity.rb b/app/models/identity.rb index ef4d5f99091..3bacc450e6e 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: identities -# -# id :integer not null, primary key -# extern_uid :string -# provider :string -# user_id :integer -# created_at :datetime -# updated_at :datetime -# - class Identity < ActiveRecord::Base include Sortable include CaseSensitivity diff --git a/app/models/issue.rb b/app/models/issue.rb index abaa509707c..2d4a9b9f19a 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: issues -# -# id :integer not null, primary key -# title :string -# assignee_id :integer -# author_id :integer -# project_id :integer -# created_at :datetime -# updated_at :datetime -# position :integer default(0) -# branch_name :string -# description :text -# milestone_id :integer -# state :string -# iid :integer -# updated_by_id :integer -# moved_to_id :integer -# confidential :boolean default(FALSE) -# deleted_at :datetime -# due_date :date -# - require 'carrierwave/orm/activerecord' class Issue < ActiveRecord::Base diff --git a/app/models/key.rb b/app/models/key.rb index b2b57849f8a..d52afda67d1 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string -# type :string -# fingerprint :string -# public :boolean default(FALSE), not null -# - require 'digest/md5' class Key < ActiveRecord::Base diff --git a/app/models/label.rb b/app/models/label.rb index 9a22398d952..e5ad11983be 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: labels -# -# id :integer not null, primary key -# title :string -# color :string -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# description :string -# - class Label < ActiveRecord::Base include Referable include Subscribable @@ -117,6 +103,10 @@ class Label < ActiveRecord::Base LabelsHelper::text_color_for_bg(self.color) end + def title=(value) + write_attribute(:title, Sanitize.clean(value.to_s)) if value.present? + end + private def label_format_reference(format = :id) diff --git a/app/models/label_link.rb b/app/models/label_link.rb index 7b8e872b6dd..47bd6eaf35f 100644 --- a/app/models/label_link.rb +++ b/app/models/label_link.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: label_links -# -# id :integer not null, primary key -# label_id :integer -# target_id :integer -# target_type :string -# created_at :datetime -# updated_at :datetime -# - class LabelLink < ActiveRecord::Base belongs_to :target, polymorphic: true belongs_to :label diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 927e764af92..18657c3e1c8 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects -# -# id :integer not null, primary key -# oid :string not null -# size :integer not null -# created_at :datetime -# updated_at :datetime -# file :string -# - class LfsObject < ActiveRecord::Base has_many :lfs_objects_projects, dependent: :destroy has_many :projects, through: :lfs_objects_projects diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb index 890736bfc80..0fd5f089db9 100644 --- a/app/models/lfs_objects_project.rb +++ b/app/models/lfs_objects_project.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects_projects -# -# id :integer not null, primary key -# lfs_object_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - class LfsObjectsProject < ActiveRecord::Base belongs_to :project belongs_to :lfs_object diff --git a/app/models/member.rb b/app/models/member.rb index cca82da89f1..d3060f07fc0 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: members -# -# id :integer not null, primary key -# access_level :integer not null -# source_id :integer not null -# source_type :string not null -# user_id :integer -# notification_level :integer not null -# type :string -# created_at :datetime -# updated_at :datetime -# created_by_id :integer -# invite_email :string -# invite_token :string -# invite_accepted_at :datetime -# - class Member < ActiveRecord::Base include Sortable include Gitlab::Access diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index a48c1943e6f..f63a0debf1a 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: members -# -# id :integer not null, primary key -# access_level :integer not null -# source_id :integer not null -# source_type :string not null -# user_id :integer -# notification_level :integer not null -# type :string -# created_at :datetime -# updated_at :datetime -# created_by_id :integer -# invite_email :string -# invite_token :string -# invite_accepted_at :datetime -# - class GroupMember < Member SOURCE_TYPE = 'Namespace' diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 143350a0b55..8dae3bb2ef2 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: members -# -# id :integer not null, primary key -# access_level :integer not null -# source_id :integer not null -# source_type :string not null -# user_id :integer -# notification_level :integer not null -# type :string -# created_at :datetime -# updated_at :datetime -# created_by_id :integer -# invite_email :string -# invite_token :string -# invite_accepted_at :datetime -# - class ProjectMember < Member SOURCE_TYPE = 'Project' diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4175e1e5fba..5c5e6007aa0 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,33 +1,3 @@ -# == Schema Information -# -# Table name: merge_requests -# -# id :integer not null, primary key -# target_branch :string not null -# source_branch :string not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string -# merge_status :string -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string -# merge_params :text -# merge_when_build_succeeds :boolean default(FALSE), not null -# merge_user_id :integer -# merge_commit_sha :string -# deleted_at :datetime -# - class MergeRequest < ActiveRecord::Base include InternalId include Issuable @@ -544,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/merge_request_diff.rb b/app/models/merge_request_diff.rb index 8951e92a0b8..eb42c07b9b9 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: merge_request_diffs -# -# id :integer not null, primary key -# state :string -# st_commits :text -# st_diffs :text -# merge_request_id :integer not null -# created_at :datetime -# updated_at :datetime -# base_commit_sha :string -# real_size :string -# - class MergeRequestDiff < ActiveRecord::Base include Sortable diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 5ee8a965ad8..e4fdd23badb 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: milestones -# -# id :integer not null, primary key -# title :string not null -# project_id :integer not null -# description :text -# due_date :date -# created_at :datetime -# updated_at :datetime -# state :string -# iid :integer -# - class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. @@ -129,6 +114,10 @@ class Milestone < ActiveRecord::Base nil end + def title=(value) + write_attribute(:title, Sanitize.clean(value.to_s)) if value.present? + end + # Sorts the issues for the given IDs. # # This method runs a single SQL query using a CASE statement to update the diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 741e912171d..9c942a8f4e3 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -1,20 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string not null -# path :string not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string -# description :string default(""), not null -# avatar :string -# share_with_group_lock :boolean default(FALSE) -# visibility_level :integer default(20), not null -# - class Namespace < ActiveRecord::Base include Sortable include Gitlab::ShellAdapter diff --git a/app/models/note.rb b/app/models/note.rb index deee2b9e885..f26aa1bf63f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: notes -# -# id :integer not null, primary key -# note :text -# noteable_type :string -# author_id :integer -# created_at :datetime -# updated_at :datetime -# project_id :integer -# attachment :string -# line_code :string -# commit_id :string -# noteable_id :integer -# system :boolean default(FALSE), not null -# st_diff :text -# updated_by_id :integer -# is_award :boolean default(FALSE), not null -# - require 'carrierwave/orm/activerecord' class Note < ActiveRecord::Base diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 846773752a6..5001738f411 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: notification_settings -# -# id :integer not null, primary key -# user_id :integer not null -# source_id :integer not null -# source_type :string not null -# level :integer default(0), not null -# created_at :datetime not null -# updated_at :datetime not null -# - class NotificationSetting < ActiveRecord::Base enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 } diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb index c78c7f4aa0e..116fb71ac08 100644 --- a/app/models/oauth_access_token.rb +++ b/app/models/oauth_access_token.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: oauth_access_tokens -# -# id :integer not null, primary key -# resource_owner_id :integer -# application_id :integer -# token :string not null -# refresh_token :string -# expires_in :integer -# revoked_at :datetime -# created_at :datetime not null -# scopes :string -# - class OauthAccessToken < ActiveRecord::Base belongs_to :resource_owner, class_name: 'User' belongs_to :application, class_name: 'Doorkeeper::Application' diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index 1d5f4c50254..82c1c4de3a0 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -1,18 +1,2 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string -# type :string -# visibility_level :integer default(0), not null -# - class PersonalSnippet < Snippet end diff --git a/app/models/project.rb b/app/models/project.rb index ab9ee9bad0a..6e85841db44 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,49 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string -# path :string -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string default("gitlab"), not null -# issues_tracker_id :string -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string -# import_status :string -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string -# import_source :string -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# pending_delete :boolean default(FALSE) -# public_builds :boolean default(TRUE), not null -# main_language :string -# pushes_since_gc :integer default(0) -# last_repository_check_failed :boolean -# last_repository_check_at :datetime -# - require 'carrierwave/orm/activerecord' class Project < ActiveRecord::Base @@ -67,7 +21,6 @@ class Project < ActiveRecord::Base default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :wiki_enabled, gitlab_config_features.wiki - default_value_for :wall_enabled, false default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } @@ -774,7 +727,7 @@ class Project < ActiveRecord::Base # Check if current branch name is marked as protected in the system def protected_branch?(branch_name) - protected_branches.where(name: branch_name).any? + protected_branch_names.include?(branch_name) end def developers_can_push_to_protected_branch?(branch_name) diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index 66f5a609bf5..e52a6bd7c84 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: project_group_links -# -# id :integer not null, primary key -# project_id :integer not null -# group_id :integer not null -# created_at :datetime -# updated_at :datetime -# group_access :integer default(30), not null -# - class ProjectGroupLink < ActiveRecord::Base GUEST = 10 REPORTER = 20 diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 7830f764ed3..e2f9ffb69ac 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: project_import_data -# -# id :integer not null, primary key -# project_id :integer -# data :text -# encrypted_credentials :text -# encrypted_credentials_iv :string -# encrypted_credentials_salt :string -# - require 'carrierwave/orm/activerecord' class ProjectImportData < ActiveRecord::Base diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 368485a060a..7c23b766763 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - require 'asana' class AsanaService < Service diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index ffb7455b014..d839221d315 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class AssemblaService < Service include HTTParty diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index c36ee95e378..1d1780dcfbf 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class BambooService < CiService include HTTParty diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index f9f4897a065..86a06321e21 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - require "addressable/uri" class BuildkiteService < CiService diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 20cdfcaffb2..54da4d74fc5 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class BuildsEmailService < Service prop_accessor :recipients boolean_accessor :add_pusher diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 28c969fe57f..511b2eac792 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class CampfireService < Service prop_accessor :token, :subdomain, :room validates :token, presence: true, if: :activated? diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index 9bc8f982da6..596c00705ad 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - # Base class for CI services # List methods you need to implement to get your CI service # working with GitLab Merge Requests diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 4d1319eb6f8..6b2b1daa724 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class CustomIssueTrackerService < IssueTrackerService prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index d8e00e018cc..966dbc41d73 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class DroneCiService < CiService prop_accessor :drone_url, :token, :enable_ssl_verification diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index 2dbd29062df..e0083c43adb 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class EmailsOnPushService < Service prop_accessor :send_from_committer_email prop_accessor :disable_diffs diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index 5469049bb5e..d7b6e505191 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class ExternalWikiService < Service include HTTParty @@ -49,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_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 3dc1e0fbe8b..dd00275187f 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - require "flowdock-git-hook" class FlowdockService < Service diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index b4c311cf664..598aca5e06d 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - require "gemnasium/gitlab_service" class GemnasiumService < Service diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index a92f7226083..bbc312f5215 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - # TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed class GitlabCiService < CiService # We override the active accessor to always make GitLabCiService disabled diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 1adaeeb3b2b..5d17c358330 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class GitlabIssueTrackerService < IssueTrackerService include Gitlab::Routing.url_helpers diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index f9ddf588722..0ff4f4c8dd2 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class HipchatService < Service MAX_COMMITS = 3 diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index b9a592d7096..91015e6c9b1 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - require 'uri' class IrkerService < Service diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 98a3a7c6b86..6ae9b16d3ce 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class IssueTrackerService < Service validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index ba68658f0bd..beda89d3963 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class JiraService < IssueTrackerService include HTTParty include Gitlab::Routing.url_helpers diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index acaa0c39365..ad19b7795da 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class PivotaltrackerService < Service include HTTParty diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index a640c8cb440..3dd878e4c7d 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class PushoverService < Service include HTTParty base_uri 'https://api.pushover.net/1' diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index e2137e92c62..11cce3e0561 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class RedmineService < IssueTrackerService prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 83ffa53a407..cf9e4d5a8b6 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class SlackService < Service prop_accessor :webhook, :username, :channel boolean_accessor :notify_only_broken_builds diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 4015da31509..b0dcb52eba1 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - class TeamcityService < CiService include HTTParty diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index b4b2807eba4..5fba6baa204 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string -# type :string -# visibility_level :integer default(0), not null -# - class ProjectSnippet < Snippet belongs_to :project belongs_to :author, class_name: "User" 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/protected_branch.rb b/app/models/protected_branch.rb index 3d2052c892c..33cf046fa75 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: protected_branches -# -# id :integer not null, primary key -# project_id :integer not null -# name :string not null -# created_at :datetime -# updated_at :datetime -# developers_can_push :boolean default(FALSE), not null -# - class ProtectedBranch < ActiveRecord::Base include Gitlab::ShellAdapter diff --git a/app/models/release.rb b/app/models/release.rb index dc700d1ea5a..e196b84eb18 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: releases -# -# id :integer not null, primary key -# tag :string -# description :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# - class Release < ActiveRecord::Base belongs_to :project diff --git a/app/models/repository.rb b/app/models/repository.rb index 7aebfe279fb..0eff74320f3 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -81,7 +81,7 @@ class Repository def commit(id = 'HEAD') return nil unless exists? commit = Gitlab::Git::Commit.find(raw_repository, id) - commit = Commit.new(commit, @project) if commit + commit = ::Commit.new(commit, @project) if commit commit rescue Rugged::OdbError nil @@ -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/security_event.rb b/app/models/security_event.rb index 0bee03974f1..d131c11cb6c 100644 --- a/app/models/security_event.rb +++ b/app/models/security_event.rb @@ -1,16 +1,2 @@ -# == Schema Information -# -# Table name: audit_events -# -# id :integer not null, primary key -# author_id :integer not null -# type :string not null -# entity_id :integer not null -# entity_type :string not null -# details :text -# created_at :datetime -# updated_at :datetime -# - class SecurityEvent < AuditEvent end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 99279a2e083..375f195dba7 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: sent_notifications -# -# id :integer not null, primary key -# project_id :integer -# noteable_id :integer -# noteable_type :string -# recipient_id :integer -# commit_id :string -# reply_key :string not null -# line_code :string -# - class SentNotification < ActiveRecord::Base belongs_to :project belongs_to :noteable, polymorphic: true diff --git a/app/models/service.rb b/app/models/service.rb index bf16a545307..de3fd24584a 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -1,27 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string -# title :string -# project_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# active :boolean not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# category :string default("common"), not null -# default :boolean default(FALSE) -# wiki_page_events :boolean default(TRUE) -# - # To add new service you should build a class inherited from Service # and implement a set of methods class Service < ActiveRecord::Base diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 2f905a90942..0a3c3b57669 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string -# type :string -# visibility_level :integer default(0), not null -# - class Snippet < ActiveRecord::Base include Gitlab::VisibilityLevel include Linguist::BlobHelper diff --git a/app/models/spam_log.rb b/app/models/spam_log.rb index f49eb7d88e2..12df68ef83b 100644 --- a/app/models/spam_log.rb +++ b/app/models/spam_log.rb @@ -1,20 +1,3 @@ -# == Schema Information -# -# Table name: spam_logs -# -# id :integer not null, primary key -# user_id :integer -# source_ip :string -# user_agent :string -# via_api :boolean -# project_id :integer -# noteable_type :string -# title :string -# description :text -# created_at :datetime not null -# updated_at :datetime not null -# - class SpamLog < ActiveRecord::Base belongs_to :user diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 242faa7d32e..3b8aa1eb866 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: subscriptions -# -# id :integer not null, primary key -# user_id :integer -# subscribable_id :integer -# subscribable_type :string -# subscribed :boolean -# created_at :datetime -# updated_at :datetime -# - class Subscription < ActiveRecord::Base belongs_to :user belongs_to :subscribable, polymorphic: true diff --git a/app/models/todo.rb b/app/models/todo.rb index d85f7bfdf57..f8b59fe4126 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: todos -# -# id :integer not null, primary key -# user_id :integer not null -# project_id :integer not null -# target_id :integer -# target_type :string not null -# author_id :integer -# action :integer not null -# state :string not null -# created_at :datetime -# updated_at :datetime -# note_id :integer -# commit_id :string -# - class Todo < ActiveRecord::Base ASSIGNED = 1 MENTIONED = 2 diff --git a/app/models/user.rb b/app/models/user.rb index 959b1f93758..489bff3fa4a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,68 +1,3 @@ -# == Schema Information -# -# Table name: users -# -# id :integer not null, primary key -# email :string default(""), not null -# encrypted_password :string default(""), not null -# reset_password_token :string -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string -# last_sign_in_ip :string -# created_at :datetime -# updated_at :datetime -# name :string -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string default(""), not null -# linkedin :string default(""), not null -# twitter :string default(""), not null -# authentication_token :string -# theme_id :integer default(1), not null -# bio :string -# failed_attempts :integer default(0) -# locked_at :datetime -# username :string -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# last_credential_check_at :datetime -# avatar :string -# confirmation_token :string -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string default(""), not null -# notification_email :string -# hide_no_password :boolean default(FALSE) -# password_automatically_set :boolean default(FALSE) -# location :string -# encrypted_otp_secret :string -# encrypted_otp_secret_iv :string -# encrypted_otp_secret_salt :string -# otp_required_for_login :boolean default(FALSE), not null -# otp_backup_codes :text -# public_email :string default(""), not null -# dashboard :integer default(0) -# project_view :integer default(0) -# consumed_timestep :integer -# layout :integer default(0) -# hide_project_limit :boolean default(FALSE) -# unlock_token :string -# otp_grace_period_started_at :datetime -# ldap_email :boolean default(FALSE), not null -# external :boolean default(FALSE) -# - require 'carrierwave/orm/activerecord' class User < ActiveRecord::Base @@ -86,7 +21,7 @@ class User < ActiveRecord::Base default_value_for :theme_id, gitlab_config.default_theme devise :two_factor_authenticatable, - otp_secret_encryption_key: File.read(Rails.root.join('.secret')).chomp + otp_secret_encryption_key: Gitlab::Application.config.secret_key_base alias_attribute :two_factor_enabled, :otp_required_for_login devise :two_factor_backupable, otp_number_of_backup_codes: 10 @@ -446,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/models/users_star_project.rb b/app/models/users_star_project.rb index 413f3f485a8..0dfe597317e 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: users_star_projects -# -# id :integer not null, primary key -# project_id :integer not null -# user_id :integer not null -# created_at :datetime -# updated_at :datetime -# - class UsersStarProject < ActiveRecord::Base belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user 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/services/system_note_service.rb b/app/services/system_note_service.rb index 82a0e2fd1f5..4bdb1b0c074 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -351,7 +351,7 @@ class SystemNoteService # Returns an Array of Strings def self.new_commit_summary(new_commits) new_commits.collect do |commit| - "* #{commit.short_id} - #{commit.title}" + "* #{commit.short_id} - #{escape_html(commit.title)}" end end @@ -433,4 +433,8 @@ class SystemNoteService body = "Moved #{direction} #{cross_reference}" create_note(noteable: noteable, project: project, author: author, note: body) end + + def self.escape_html(text) + Rack::Utils.escape_html(text) + end 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/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 6745e58deca..36b21eefdee 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -11,18 +11,10 @@ = link_to admin_runner_path(runner) do = runner.short_sha %td - .runner-description - = runner.description - %span (#{link_to 'edit', '#', class: 'edit-runner-link'}) - .runner-description-form.hide - = form_for [:admin, runner], remote: true, html: { class: 'form-inline' } do |f| - .form-group - = f.text_field :description, class: 'form-control' - = f.submit 'Save', class: 'btn' - %span (#{link_to 'cancel', '#', class: 'cancel'}) + = runner.description %td - if runner.shared? - \- + n/a - else = runner.projects.count(:all) %td 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 - - 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 86930d4eaaf..c33740e23fa 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,9 +1,12 @@ %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } %div{ class: fluid_layout ? "container-fluid" : "container-fluid" } .header-content - %button.navbar-toggle{type: 'button'} + %button.side-nav-toggle{type: 'button'} %span.sr-only Toggle navigation = icon('bars') + %button.navbar-toggle{type: 'button'} + %span.sr-only Toggle navigation + = icon('angle-left') .navbar-collapse.collapse %ul.nav.navbar-nav @@ -24,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/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 9ad86ed71c9..5cf70ea3bb7 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -28,16 +28,10 @@ = downvotes - note_count = issue.notes.user.nonawards.count - - if note_count > 0 - %li - = link_to issue_path(issue) + "#notes" do - = icon('comments') - = note_count - - else - %li - = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do - = icon('comments') - = note_count + %li + = link_to issue_path(issue, anchor: 'notes'), class: ('issue-no-comments' if note_count.zero?) do + = icon('comments') + = note_count .issue-info #{issue.to_reference} · diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index e740fe8c84d..73c6a95f5ca 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -36,16 +36,10 @@ = downvotes - note_count = merge_request.mr_and_commit_notes.user.nonawards.count - - if note_count > 0 - %li - = link_to merge_request_path(merge_request) + "#notes" do - = icon('comments') - = note_count - - else - %li - = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do - = icon('comments') - = note_count + %li + = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('merge-request-no-comments' if note_count.zero?) do + = icon('comments') + = note_count .merge-request-info #{merge_request.to_reference} · diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 2ec0d20a879..4d381754610 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -41,9 +41,4 @@ .ci_widget.ci-error{style: "display:none"} = icon("times-circle") - Could not connect to the CI server. Please check your settings and try again. - - :javascript - $(function() { - merge_request_widget.getCIStatus(false); - }); + Could not connect to the CI server. Please check your settings and try again.
\ No newline at end of file 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/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index b40a6e5cb2d..f9306453297 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -23,7 +23,7 @@ .form-group = label_tag :message, nil, class: 'control-label' .col-sm-10 - = text_field_tag :message, nil, required: false, tabindex: 3, class: 'form-control' + = text_area_tag :message, nil, required: false, tabindex: 3, class: 'form-control', rows: 5 .help-block Optionally, enter a message to create an annotated tag. %hr .form-group diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 9c916fd02de..9f1424aecc7 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -19,15 +19,13 @@ %i.fa.fa-trash-o .title %span.item-title= @tag.name - - if @tag.message.present? - %span.light - - = strip_gpg_signature(@tag.message) - if @commit = render 'projects/branches/commit', commit: @commit, project: @project - else Cant find HEAD commit for this tag - + - if @tag.message.present? + %pre.body + = strip_gpg_signature(@tag.message) .append-bottom-default.prepend-top-default - if @release.description.present? 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/application.rb b/config/application.rb index b602e2b6168..cba80f38f1f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,23 +1,30 @@ require File.expand_path('../boot', __FILE__) require 'rails/all' -require 'devise' -I18n.config.enforce_available_locales = false + Bundler.require(:default, Rails.env) -require_relative '../lib/gitlab/redis' module Gitlab class Application < Rails::Application + require_dependency Rails.root.join('lib/gitlab/redis') + # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. - # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths.push(*%W(#{config.root}/lib - #{config.root}/app/models/hooks - #{config.root}/app/models/concerns - #{config.root}/app/models/project_services - #{config.root}/app/models/members)) + # Sidekiq uses eager loading, but directories not in the standard Rails + # directories must be added to the eager load paths: + # https://github.com/mperham/sidekiq/wiki/FAQ#why-doesnt-sidekiq-autoload-my-rails-application-code + # Also, there is no need to add `lib` to autoload_paths since autoloading is + # configured to check for eager loaded paths: + # https://github.com/rails/rails/blob/v4.2.6/railties/lib/rails/engine.rb#L687 + # This is a nice reference article on autoloading/eager loading: + # http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload + config.eager_load_paths.push(*%W(#{config.root}/lib + #{config.root}/app/models/ci + #{config.root}/app/models/hooks + #{config.root}/app/models/members + #{config.root}/app/models/project_services)) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. 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/gitlab.teatro.yml b/config/gitlab.teatro.yml index f0656400beb..01c8dc5ff98 100644 --- a/config/gitlab.teatro.yml +++ b/config/gitlab.teatro.yml @@ -15,7 +15,6 @@ production: &base issues: true merge_requests: true wiki: true - wall: false snippets: false visibility_level: "private" # can be "private" | "internal" | "public" diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 3853845fee8..e46be3f420a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1,4 +1,4 @@ -require 'gitlab' # Load lib/gitlab.rb as soon as possible +require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible class Settings < Settingslogic source ENV.fetch('GITLAB_CONFIG') { "#{Rails.root}/config/gitlab.yml" } @@ -150,7 +150,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? @@ -158,7 +158,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({}) @@ -192,7 +192,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 @@ -207,7 +207,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' @@ -220,7 +220,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'], []) @@ -236,7 +236,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settin 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['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].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'] @@ -250,8 +250,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 @@ -265,7 +265,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 # # Registry @@ -337,7 +337,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/5_backend.rb b/config/initializers/5_backend.rb index 80d641d73a3..e026151a032 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -1,11 +1,11 @@ # GIT over HTTP -require Rails.root.join("lib", "gitlab", "backend", "grack_auth") +require_dependency Rails.root.join('lib/gitlab/backend/grack_auth') # GIT over SSH -require Rails.root.join("lib", "gitlab", "backend", "shell") +require_dependency Rails.root.join('lib/gitlab/backend/shell') # GitLab shell adapter -require Rails.root.join("lib", "gitlab", "backend", "shell_adapter") +require_dependency Rails.root.join('lib/gitlab/backend/shell_adapter') required_version = Gitlab::VersionInfo.parse(Gitlab::Shell.version_required) current_version = Gitlab::VersionInfo.parse(Gitlab::Shell.new.version) 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/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb index b8cc025bae2..d256a16d42b 100644 --- a/config/initializers/trusted_proxies.rb +++ b/config/initializers/trusted_proxies.rb @@ -1,2 +1,3 @@ -Rails.application.config.action_dispatch.trusted_proxies = +Rails.application.config.action_dispatch.trusted_proxies = ( [ '127.0.0.1', '::1' ] + Array(Gitlab.config.gitlab.trusted_proxies) +).map { |proxy| IPAddr.new(proxy) } diff --git a/config/routes.rb b/config/routes.rb index 3c855cfafd0..e1b72556098 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -76,6 +76,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] @@ -83,7 +86,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 @@ -94,7 +97,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 @@ -256,6 +260,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 @@ -287,6 +292,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 @@ -345,23 +351,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 @@ -675,6 +676,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/20160508194200_remove_wall_enabled_from_projects.rb b/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb new file mode 100644 index 00000000000..aa560bc0f0c --- /dev/null +++ b/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb @@ -0,0 +1,5 @@ +class RemoveWallEnabledFromProjects < ActiveRecord::Migration + def change + remove_column :projects, :wall_enabled, :boolean, default: true, null: false + 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 103a18d3621..f09e4a262b4 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: 20160421130527) 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: 20160421130527) 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| @@ -728,7 +730,6 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.datetime "updated_at" t.integer "creator_id" t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false t.boolean "merge_requests_enabled", default: true, null: false t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md index 4a12e962b62..0881a7d7a90 100644 --- a/doc/api/build_triggers.md +++ b/doc/api/build_triggers.md @@ -101,8 +101,18 @@ DELETE /projects/:id/triggers/:token | Attribute | Type | required | Description | |-----------|---------|----------|--------------------------| | `id` | integer | yes | The ID of a project | -| `token` | string | yes | The `token` of a project | +| `token` | string | yes | The `token` of a trigger | ``` curl -X DELETE -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d" ``` + +```json +{ + "created_at": "2015-12-23T16:25:56.760Z", + "deleted_at": "2015-12-24T12:32:20.100Z", + "last_used": null, + "token": "7b9148c158980bbd9bcea92c17522d", + "updated_at": "2015-12-24T12:32:20.100Z" +} +``` diff --git a/doc/api/issues.md b/doc/api/issues.md index 3e78149f442..fc7a7ae0c0c 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -77,7 +77,8 @@ Example response: "created_at" : "2016-01-04T15:31:51.081Z", "iid" : 6, "labels" : [], - "subscribed" : false + "subscribed" : false, + "user_notes_count": 1 } ] ``` @@ -154,7 +155,8 @@ Example response: "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.", "updated_at" : "2016-01-04T15:31:46.176Z", "created_at" : "2016-01-04T15:31:46.176Z", - "subscribed" : false + "subscribed" : false, + "user_notes_count": 1 } ] ``` @@ -216,7 +218,8 @@ Example response: "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.", "updated_at" : "2016-01-04T15:31:46.176Z", "created_at" : "2016-01-04T15:31:46.176Z", - "subscribed": false + "subscribed": false, + "user_notes_count": 1 } ``` @@ -271,7 +274,8 @@ Example response: "description" : null, "updated_at" : "2016-01-07T12:44:33.959Z", "milestone" : null, - "subscribed" : true + "subscribed" : true, + "user_notes_count": 0 } ``` @@ -329,7 +333,8 @@ Example response: "id" : 85, "assignee" : null, "milestone" : null, - "subscribed" : true + "subscribed" : true, + "user_notes_count": 0 } ``` 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/api/merge_requests.md b/doc/api/merge_requests.md index 2057f9d77aa..8217e30fe25 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -67,7 +67,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : false + "subscribed" : false, + "user_notes_count": 1 } ] ``` @@ -130,7 +131,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true + "subscribed" : true, + "user_notes_count": 1 } ``` @@ -230,6 +232,7 @@ Parameters: "merge_when_build_succeeds": true, "merge_status": "can_be_merged", "subscribed" : true, + "user_notes_count": 1, "changes": [ { "old_path": "VERSION", @@ -308,7 +311,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true + "subscribed" : true, + "user_notes_count": 0 } ``` @@ -378,7 +382,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true + "subscribed" : true, + "user_notes_count": 1 } ``` @@ -472,7 +477,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true + "subscribed" : true, + "user_notes_count": 1 } ``` @@ -537,7 +543,8 @@ Parameters: }, "merge_when_build_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true + "subscribed" : true, + "user_notes_count": 1 } ``` @@ -602,7 +609,8 @@ Example response: "title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.", "created_at" : "2016-01-04T15:31:51.081Z", "iid" : 6, - "labels" : [] + "labels" : [], + "user_notes_count": 1 }, ] ``` diff --git a/doc/api/notes.md b/doc/api/notes.md index a6b5b1787fd..7aa1c2155bf 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -15,7 +15,7 @@ GET /projects/:id/issues/:issue_id/notes Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The IID of an issue (not ID) +- `issue_id` (required) - The ID of an issue ```json [ @@ -73,7 +73,7 @@ GET /projects/:id/issues/:issue_id/notes/:note_id Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The IID of a project issue (not ID) +- `issue_id` (required) - The ID of a project issue - `note_id` (required) - The ID of an issue note ### Create new issue note @@ -87,7 +87,7 @@ POST /projects/:id/issues/:issue_id/notes Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The IID of an issue (not ID) +- `issue_id` (required) - The ID of an issue - `body` (required) - The content of a note - `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z @@ -102,7 +102,7 @@ PUT /projects/:id/issues/:issue_id/notes/:note_id Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The IID of an issue (not ID) +- `issue_id` (required) - The ID of an issue - `note_id` (required) - The ID of a note - `body` (required) - The content of a note @@ -120,7 +120,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | -| `issue_id` | integer | yes | The IID of an issue | +| `issue_id` | integer | yes | The ID of an issue | | `note_id` | integer | yes | The ID of a note | ```bash diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md index c1cf2e77c26..9168c70945a 100644 --- a/doc/development/instrumentation.md +++ b/doc/development/instrumentation.md @@ -1,12 +1,125 @@ # Instrumenting Ruby Code -GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby -code. This can be used to measure the time spent in a specific part of a larger -chunk of code. The resulting data is stored as a field in the transaction that -executed the block. +GitLab Performance Monitoring allows instrumenting of both methods and custom +blocks of Ruby code. Method instrumentation is the primary form of +instrumentation with block-based instrumentation only being used when we want to +drill down to specific regions of code within a method. -To start measuring a block of Ruby code you should use `Gitlab::Metrics.measure` -and give it a name: +## Instrumenting Methods + +Instrumenting methods is done by using the `Gitlab::Metrics::Instrumentation` +module. This module offers a few different methods that can be used to +instrument code: + +* `instrument_method`: instruments a single class method. +* `instrument_instance_method`: instruments a single instance method. +* `instrument_class_hierarchy`: given a Class this method will recursively + instrument all sub-classes (both class and instance methods). +* `instrument_methods`: instruments all public class methods of a Module. +* `instrument_instance_methods`: instruments all public instance methods of a + Module. + +To remove the need for typing the full `Gitlab::Metrics::Instrumentation` +namespace you can use the `configure` class method. This method simply yields +the supplied block while passing `Gitlab::Metrics::Instrumentation` as its +argument. An example: + +``` +Gitlab::Metrics::Instrumentation.configure do |conf| + conf.instrument_method(Foo, :bar) + conf.instrument_method(Foo, :baz) +end +``` + +Using this method is in general preferred over directly calling the various +instrumentation methods. + +Method instrumentation should be added in the initializer +`config/initializers/metrics.rb`. + +### Examples + +Instrumenting a single method: + +``` +Gitlab::Metrics::Instrumentation.configure do |conf| + conf.instrument_method(User, :find_by) +end +``` + +Instrumenting an entire class hierarchy: + +``` +Gitlab::Metrics::Instrumentation.configure do |conf| + conf.instrument_class_hierarchy(ActiveRecord::Base) +end +``` + +Instrumenting all public class methods: + +``` +Gitlab::Metrics::Instrumentation.configure do |conf| + conf.instrument_methods(User) +end +``` + +### Checking Instrumented Methods + +The easiest way to check if a method has been instrumented is to check its +source location. For example: + +``` +method = Rugged::TagCollection.instance_method(:[]) + +method.source_location +``` + +If the source location points to `lib/gitlab/metrics/instrumentation.rb` you +know the method has been instrumented. + +If you're using Pry you can use the `$` command to display the source code of a +method (along with its source location), this is easier than running the above +Ruby code. In case of the above snippet you'd run the following: + +``` +$ Rugged::TagCollection#[] +``` + +This will print out something along the lines of: + +``` +From: /path/to/your/gitlab/lib/gitlab/metrics/instrumentation.rb @ line 148: +Owner: #<Module:0x0055f0865c6d50> +Visibility: public +Number of lines: 21 + +def #{name}(#{args_signature}) + trans = Gitlab::Metrics::Instrumentation.transaction + + if trans + start = Time.now + retval = super + 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}) + end + + retval + else + super + end +end +``` + +## Instrumenting Ruby Blocks + +Measuring blocks of Ruby code is done by calling `Gitlab::Metrics.measure` and +passing it a block. For example: ```ruby Gitlab::Metrics.measure(:foo) do @@ -14,6 +127,10 @@ Gitlab::Metrics.measure(:foo) do end ``` +The block is executed and the execution time is stored as a set of fields in the +currently running transaction. If no transaction is present the block is yielded +without measuring anything. + 3 values are measured for a block: 1. The real time elapsed, stored in NAME_real_time. 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 Binary files differnew file mode 100644 index 00000000000..95f8bbdcd24 --- /dev/null +++ b/doc/integration/img/enabled-oauth-sign-in-sources.png 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/update/patch_versions.md b/doc/update/patch_versions.md index b4283a526f3..60729316cde 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -57,10 +57,10 @@ sudo -u git -H make cd /home/git/gitlab # PostgreSQL -sudo -u git -H bundle install --without development test mysql --with postgres --deployment +sudo -u git -H bundle install --without development test mysql --deployment # MySQL -sudo -u git -H bundle install --without development test postgres --with mysql --deployment +sudo -u git -H bundle install --without development test postgres --deployment # Optional: clean up old gems sudo -u git -H bundle clean 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/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 31620044b15..83db44c10b1 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -152,4 +152,4 @@ If you are using OS X you can use `osxkeychain` to store and encrypt your creden For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases). More details about various methods of storing the user credentials can be found -on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
\ No newline at end of file +on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature deleted file mode 100644 index a4be39b2d40..00000000000 --- a/features/project/commits/tags.feature +++ /dev/null @@ -1,46 +0,0 @@ -@project_commits -Feature: Project Commits Tags - Background: - Given I sign in as a user - And I own project "Shop" - Given I visit project tags page - - Scenario: I can see all git tags - Then I should see "Shop" all tags list - - Scenario: I create a tag - And I click new tag link - And I submit new tag form - Then I should see new tag created - - Scenario: I create a tag with release notes - Given I click new tag link - And I submit new tag form with release notes - Then I should see new tag created - And I should see tag release notes - - Scenario: I create a tag with invalid name - And I click new tag link - And I submit new tag form with invalid name - Then I should see new an error that tag is invalid - - Scenario: I create a tag with invalid reference - And I click new tag link - And I submit new tag form with invalid reference - Then I should see new an error that tag ref is invalid - - Scenario: I create a tag that already exists - And I click new tag link - And I submit new tag form with tag that already exists - Then I should see new an error that tag already exists - - Scenario: I delete a tag - Given I visit tag 'v1.1.0' page - Given I delete tag 'v1.1.0' - Then I should not see tag 'v1.1.0' - - Scenario: I add release notes to the tag - Given I visit tag 'v1.1.0' page - When I click edit tag link - And I fill release notes and submit form - Then I should see tag release notes diff --git a/features/project/create.feature b/features/project/create.feature index 27136798e36..67336d73bf7 100644 --- a/features/project/create.feature +++ b/features/project/create.feature @@ -7,20 +7,8 @@ Feature: Project Create @javascript Scenario: User create a project Given I sign in as a user - When I visit new project page - And I have an ssh key - And fill project form with valid data - Then I should see project page - And I should see empty project instuctions - - @javascript - Scenario: Empty project instructions - Given I sign in as a user And I have an ssh key When I visit new project page And fill project form with valid data - Then I see empty project instuctions - And I click on HTTP - Then Remote url should update to http link - And If I click on SSH - Then Remote url should update to ssh link + Then I should see project page + And I should see empty project instructions 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/commits/tags.rb b/features/steps/project/commits/tags.rb deleted file mode 100644 index 912ba580efd..00000000000 --- a/features/steps/project/commits/tags.rb +++ /dev/null @@ -1,90 +0,0 @@ -class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - - step 'I should see "Shop" all tags list' do - expect(page).to have_content "Tags" - expect(page).to have_content "v1.0.0" - end - - step 'I click new tag link' do - click_link 'New tag' - end - - step 'I submit new tag form' do - fill_in 'tag_name', with: 'v7.0' - fill_in 'ref', with: 'master' - click_button 'Create tag' - end - - step 'I submit new tag form with release notes' do - fill_in 'tag_name', with: 'v7.0' - fill_in 'ref', with: 'master' - fill_in 'release_description', with: 'Awesome release notes' - click_button 'Create tag' - end - - step 'I fill release notes and submit form' do - fill_in 'release_description', with: 'Awesome release notes' - click_button 'Save changes' - end - - step 'I submit new tag form with invalid name' do - fill_in 'tag_name', with: 'v 1.0' - fill_in 'ref', with: 'master' - click_button 'Create tag' - end - - step 'I submit new tag form with invalid reference' do - fill_in 'tag_name', with: 'foo' - fill_in 'ref', with: 'foo' - click_button 'Create tag' - end - - step 'I submit new tag form with tag that already exists' do - fill_in 'tag_name', with: 'v1.0.0' - fill_in 'ref', with: 'master' - click_button 'Create tag' - end - - step 'I should see new tag created' do - expect(page).to have_content 'v7.0' - end - - step 'I should see new an error that tag is invalid' do - expect(page).to have_content 'Tag name invalid' - end - - step 'I should see new an error that tag ref is invalid' do - expect(page).to have_content 'Target foo is invalid' - end - - step 'I should see new an error that tag already exists' do - expect(page).to have_content 'Tag v1.0.0 already exists' - end - - step "I visit tag 'v1.1.0' page" do - click_link 'v1.1.0' - end - - step "I delete tag 'v1.1.0'" do - page.within('.content') do - first('.btn-remove').click - end - end - - step "I should not see tag 'v1.1.0'" do - page.within '.tags' do - expect(page).not_to have_link 'v1.1.0' - end - end - - step 'I click edit tag link' do - click_link 'Edit release notes' - end - - step 'I should see tag release notes' do - expect(page).to have_content 'Awesome release notes' - end -end diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb index 422b151eaa2..5f5f806df36 100644 --- a/features/steps/project/create.rb +++ b/features/steps/project/create.rb @@ -13,33 +13,9 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps expect(current_path).to eq namespace_project_path(Project.last.namespace, Project.last) end - step 'I should see empty project instuctions' do + step 'I should see empty project instructions' do expect(page).to have_content "git init" expect(page).to have_content "git remote" expect(page).to have_content Project.last.url_to_repo end - - step 'I see empty project instuctions' do - expect(page).to have_content "git init" - expect(page).to have_content "git remote" - expect(page).to have_content Project.last.url_to_repo - end - - step 'I click on HTTP' do - find('#clone-dropdown').click - find('.http-selector').click - end - - step 'Remote url should update to http link' do - expect(page).to have_content "git remote add origin #{Project.last.http_url_to_repo}" - end - - step 'If I click on SSH' do - find('#clone-dropdown').click - find('.ssh-selector').click - end - - step 'Remote url should update to ssh link' do - expect(page).to have_content "git remote add origin #{Project.last.url_to_repo}" - end end 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 cc1004f8005..360fb41a721 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -1,5 +1,3 @@ -Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file} - module API class API < Grape::API include APIGuard @@ -25,38 +23,40 @@ module API format :json content_type :txt, "text/plain" - helpers Helpers - - mount Groups - mount GroupMembers - mount Users - mount Projects - mount Repositories - mount Issues - mount Milestones - mount Session - mount MergeRequests - mount Notes - mount Internal - mount SystemHooks - mount ProjectSnippets - mount ProjectMembers - mount DeployKeys - mount ProjectHooks - mount Services - mount Files - mount Commits - mount CommitStatus - mount Namespaces - mount Branches - mount Labels - mount Settings - mount Keys - mount Tags - mount Triggers - mount Builds - mount Variables - mount Runners - mount Licenses + # Ensure the namespace is right, otherwise we might load Grape::API::Helpers + helpers ::API::Helpers + + mount ::API::Groups + mount ::API::GroupMembers + mount ::API::Users + mount ::API::Projects + mount ::API::Repositories + mount ::API::Issues + mount ::API::Milestones + mount ::API::Session + mount ::API::MergeRequests + mount ::API::Notes + mount ::API::Internal + mount ::API::SystemHooks + mount ::API::ProjectSnippets + mount ::API::ProjectMembers + mount ::API::DeployKeys + mount ::API::ProjectHooks + mount ::API::Services + mount ::API::Files + mount ::API::Commits + mount ::API::CommitStatuses + mount ::API::Namespaces + mount ::API::Branches + mount ::API::Labels + mount ::API::Settings + mount ::API::Keys + mount ::API::Tags + mount ::API::Triggers + mount ::API::Builds + mount ::API::Variables + mount ::API::Runners + mount ::API::Licenses + mount ::API::Subscriptions end end diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index b9994fcefda..7e67edb203a 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -2,171 +2,175 @@ require 'rack/oauth2' -module APIGuard - extend ActiveSupport::Concern +module API + module APIGuard + extend ActiveSupport::Concern - included do |base| - # OAuth2 Resource Server Authentication - use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request| - # The authenticator only fetches the raw token string + included do |base| + # OAuth2 Resource Server Authentication + use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request| + # The authenticator only fetches the raw token string - # Must yield access token to store it in the env - request.access_token - end + # Must yield access token to store it in the env + request.access_token + end - helpers HelperMethods + helpers HelperMethods - install_error_responders(base) - end + install_error_responders(base) + end - # Helper Methods for Grape Endpoint - module HelperMethods - # Invokes the doorkeeper guard. - # - # If token is presented and valid, then it sets @current_user. - # - # If the token does not have sufficient scopes to cover the requred scopes, - # then it raises InsufficientScopeError. - # - # If the token is expired, then it raises ExpiredError. - # - # If the token is revoked, then it raises RevokedError. - # - # If the token is not found (nil), then it raises TokenNotFoundError. - # - # Arguments: - # - # scopes: (optional) scopes required for this guard. - # Defaults to empty array. - # - def doorkeeper_guard!(scopes: []) - if (access_token = find_access_token).nil? - raise TokenNotFoundError - - else - case validate_access_token(access_token, scopes) - when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE - raise InsufficientScopeError.new(scopes) - when Oauth2::AccessTokenValidationService::EXPIRED - raise ExpiredError - when Oauth2::AccessTokenValidationService::REVOKED - raise RevokedError - when Oauth2::AccessTokenValidationService::VALID - @current_user = User.find(access_token.resource_owner_id) + # Helper Methods for Grape Endpoint + module HelperMethods + # Invokes the doorkeeper guard. + # + # If token is presented and valid, then it sets @current_user. + # + # If the token does not have sufficient scopes to cover the requred scopes, + # then it raises InsufficientScopeError. + # + # If the token is expired, then it raises ExpiredError. + # + # If the token is revoked, then it raises RevokedError. + # + # If the token is not found (nil), then it raises TokenNotFoundError. + # + # Arguments: + # + # scopes: (optional) scopes required for this guard. + # Defaults to empty array. + # + def doorkeeper_guard!(scopes: []) + if (access_token = find_access_token).nil? + raise TokenNotFoundError + + else + case validate_access_token(access_token, scopes) + when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) + when Oauth2::AccessTokenValidationService::EXPIRED + raise ExpiredError + when Oauth2::AccessTokenValidationService::REVOKED + raise RevokedError + when Oauth2::AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) + end end end - end - def doorkeeper_guard(scopes: []) - if access_token = find_access_token - case validate_access_token(access_token, scopes) - when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE - raise InsufficientScopeError.new(scopes) + def doorkeeper_guard(scopes: []) + if access_token = find_access_token + case validate_access_token(access_token, scopes) + when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) - when Oauth2::AccessTokenValidationService::EXPIRED - raise ExpiredError + when Oauth2::AccessTokenValidationService::EXPIRED + raise ExpiredError - when Oauth2::AccessTokenValidationService::REVOKED - raise RevokedError + when Oauth2::AccessTokenValidationService::REVOKED + raise RevokedError - when Oauth2::AccessTokenValidationService::VALID - @current_user = User.find(access_token.resource_owner_id) + when Oauth2::AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) + end end end - end - def current_user - @current_user - end + def current_user + @current_user + end - private - def find_access_token - @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) - end + private - def doorkeeper_request - @doorkeeper_request ||= ActionDispatch::Request.new(env) - end + def find_access_token + @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) + end - def validate_access_token(access_token, scopes) - Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes) - end - end + def doorkeeper_request + @doorkeeper_request ||= ActionDispatch::Request.new(env) + end - module ClassMethods - # Installs the doorkeeper guard on the whole Grape API endpoint. - # - # Arguments: - # - # scopes: (optional) scopes required for this guard. - # Defaults to empty array. - # - def guard_all!(scopes: []) - before do - guard! scopes: scopes + def validate_access_token(access_token, scopes) + Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes) end end - private - def install_error_responders(base) - error_classes = [ MissingTokenError, TokenNotFoundError, - ExpiredError, RevokedError, InsufficientScopeError] + module ClassMethods + # Installs the doorkeeper guard on the whole Grape API endpoint. + # + # Arguments: + # + # scopes: (optional) scopes required for this guard. + # Defaults to empty array. + # + def guard_all!(scopes: []) + before do + guard! scopes: scopes + end + end - base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler - end + private - def oauth2_bearer_token_error_handler - Proc.new do |e| - response = - case e - when MissingTokenError - Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new - - when TokenNotFoundError - Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( - :invalid_token, - "Bad Access Token.") - - when ExpiredError - Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( - :invalid_token, - "Token is expired. You can either do re-authorization or token refresh.") - - when RevokedError - Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( - :invalid_token, - "Token was revoked. You have to re-authorize from the user.") - - when InsufficientScopeError - # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2) - # does not include WWW-Authenticate header, which breaks the standard. - Rack::OAuth2::Server::Resource::Bearer::Forbidden.new( - :insufficient_scope, - Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope], - { scope: e.scopes }) - end + def install_error_responders(base) + error_classes = [ MissingTokenError, TokenNotFoundError, + ExpiredError, RevokedError, InsufficientScopeError] - response.finish + base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler + end + + def oauth2_bearer_token_error_handler + Proc.new do |e| + response = + case e + when MissingTokenError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new + + when TokenNotFoundError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Bad Access Token.") + + when ExpiredError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Token is expired. You can either do re-authorization or token refresh.") + + when RevokedError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Token was revoked. You have to re-authorize from the user.") + + when InsufficientScopeError + # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2) + # does not include WWW-Authenticate header, which breaks the standard. + Rack::OAuth2::Server::Resource::Bearer::Forbidden.new( + :insufficient_scope, + Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope], + { scope: e.scopes }) + end + + response.finish + end end end - end - # - # Exceptions - # + # + # Exceptions + # - class MissingTokenError < StandardError; end + class MissingTokenError < StandardError; end - class TokenNotFoundError < StandardError; end + class TokenNotFoundError < StandardError; end - class ExpiredError < StandardError; end + class ExpiredError < StandardError; end - class RevokedError < StandardError; end + class RevokedError < StandardError; end - class InsufficientScopeError < StandardError - attr_reader :scopes - def initialize(scopes) - @scopes = scopes + class InsufficientScopeError < StandardError + attr_reader :scopes + def initialize(scopes) + @scopes = scopes + end end end end diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 7388ed2f4ea..9bcd33ff19e 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -2,7 +2,7 @@ require 'mime/types' module API # Project commit statuses API - class CommitStatus < Grape::API + class CommitStatuses < Grape::API resource :projects do before { authenticate! } diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d62575e0a30..24ec551d720 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -171,10 +171,10 @@ module API expose :label_names, as: :labels expose :milestone, using: Entities::Milestone expose :assignee, :author, using: Entities::UserBasic - expose :subscribed do |issue, options| issue.subscribed?(options[:current_user]) end + expose :user_notes_count end class MergeRequest < ProjectEntity @@ -188,10 +188,10 @@ module API expose :milestone, using: Entities::Milestone expose :merge_when_build_succeeds expose :merge_status - expose :subscribed do |merge_request, options| merge_request.subscribed?(options[:current_user]) end + expose :user_notes_count end class MergeRequestChanges < MergeRequest @@ -308,6 +308,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 d14b28e17fd..5a22d14988f 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/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 2732e0b5145..59c5e89c546 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -10,6 +10,9 @@ module Banzai end def self.user_can_see_reference?(user, node, context) + # It is not possible to check access rights for external issue trackers + return true if context[:project].try(:external_issue_tracker) + issue = Issue.find(node.attr('data-issue')) rescue nil Ability.abilities.allowed?(user, :read_issue, issue) end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 42dbab9d27e..ca80aac5a08 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -63,7 +63,7 @@ module Banzai begin uri = Addressable::URI.parse(node['href']) - uri.scheme.strip! if uri.scheme + uri.scheme = uri.scheme.strip.downcase if uri.scheme node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(uri.scheme) rescue Addressable::URI::InvalidURIError diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb index 7edfe5ade2d..c0f503c9af3 100644 --- a/lib/banzai/filter/upload_link_filter.rb +++ b/lib/banzai/filter/upload_link_filter.rb @@ -8,6 +8,8 @@ module Banzai # class UploadLinkFilter < HTML::Pipeline::Filter def call + return doc unless project + doc.search('a').each do |el| process_link_attr el.attribute('href') end @@ -31,7 +33,11 @@ module Banzai end def build_url(uri) - File.join(Gitlab.config.gitlab.url, context[:project].path_with_namespace, uri) + File.join(Gitlab.config.gitlab.url, project.path_with_namespace, uri) + end + + def project + context[:project] end # Ensure that a :project key exists in context 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("<", "<")) + s = StringScanner.new(ansi) while(!s.eos?) if s.scan(/\e([@-_])(.*?)([@-~])/) handle_sequence(s) + elsif s.scan(/\e(([@-_])(.*?)?)?$/) + break + elsif s.scan(/</) + @out << '<' 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/ci/api/api.rb b/lib/ci/api/api.rb index 353c4ddebf8..17bb99a2ae5 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -1,9 +1,7 @@ -Dir["#{Rails.root}/lib/ci/api/*.rb"].each {|file| require file} - module Ci module API class API < Grape::API - include APIGuard + include ::API::APIGuard version 'v1', using: :path rescue_from ActiveRecord::RecordNotFound do @@ -31,9 +29,9 @@ module Ci helpers ::API::Helpers helpers Gitlab::CurrentSettings - mount Builds - mount Runners - mount Triggers + mount ::Ci::API::Builds + mount ::Ci::API::Runners + mount ::Ci::API::Triggers end end end diff --git a/lib/gitlab.rb b/lib/gitlab.rb index 7479e729db1..37f4c34054f 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -1,4 +1,4 @@ -require 'gitlab/git' +require_dependency 'gitlab/git' module Gitlab def self.com? 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/git_access.rb b/lib/gitlab/git_access.rb index 6cb41239871..d2a0e316cbe 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -122,6 +122,11 @@ module Gitlab build_status_object(true) end + def can_user_do_action?(action) + @permission_cache ||= {} + @permission_cache[action] ||= user.can?(action, project) + end + def change_access_check(change) oldrev, newrev, ref = change.split(' ') @@ -135,7 +140,7 @@ module Gitlab :push_code end - unless user.can?(action, project) + unless can_user_do_action?(action) status = case action when :force_push_code_to_protected_branches 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/lib/tasks/auto_annotate_models.rake b/lib/tasks/auto_annotate_models.rake deleted file mode 100644 index 16bad4bd2bd..00000000000 --- a/lib/tasks/auto_annotate_models.rake +++ /dev/null @@ -1,44 +0,0 @@ -if Rails.env.development? - task :set_annotation_options do - # You can override any of these by setting an environment variable of the - # same name. - Annotate.set_defaults( - 'routes' => 'false', - 'position_in_routes' => 'before', - 'position_in_class' => 'before', - 'position_in_test' => 'before', - 'position_in_fixture' => 'before', - 'position_in_factory' => 'before', - 'position_in_serializer' => 'before', - 'show_foreign_keys' => 'true', - 'show_indexes' => 'false', - 'simple_indexes' => 'false', - 'model_dir' => 'app/models', - 'root_dir' => '', - 'include_version' => 'false', - 'require' => '', - 'exclude_tests' => 'true', - 'exclude_fixtures' => 'true', - 'exclude_factories' => 'true', - 'exclude_serializers' => 'true', - 'exclude_scaffolds' => 'true', - 'exclude_controllers' => 'true', - 'exclude_helpers' => 'true', - 'ignore_model_sub_dir' => 'false', - 'ignore_columns' => nil, - 'ignore_unknown_models' => 'false', - 'hide_limit_column_types' => 'integer,boolean', - 'skip_on_db_migrate' => 'false', - 'format_bare' => 'true', - 'format_rdoc' => 'false', - 'format_markdown' => 'false', - 'sort' => 'false', - 'force' => 'false', - 'trace' => 'false', - 'wrapper_open' => nil, - 'wrapper_close' => nil, - ) - end - - Annotate.load_tasks -end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index 1c706dc11b3..e473b756023 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -29,10 +29,12 @@ namespace :gitlab do tables.delete 'schema_migrations' # Truncate schema_migrations to ensure migrations re-run connection.execute('TRUNCATE schema_migrations') + # Drop tables with cascade to avoid dependent table errors # PG: http://www.postgresql.org/docs/current/static/ddl-depend.html # MySQL: http://dev.mysql.com/doc/refman/5.7/en/drop-table.html - tables.each { |t| connection.execute("DROP TABLE #{t} CASCADE") } + # Add `IF EXISTS` because cascade could have already deleted a table. + tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{t} CASCADE") } 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/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb index d0e8c778518..8f6422a7825 100644 --- a/spec/factories/abuse_reports.rb +++ b/spec/factories/abuse_reports.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: abuse_reports -# -# id :integer not null, primary key -# reporter_id :integer -# user_id :integer -# message :text -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :abuse_report do reporter factory: :user diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb index c80e7366551..efe9803b1a7 100644 --- a/spec/factories/broadcast_messages.rb +++ b/spec/factories/broadcast_messages.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: broadcast_messages -# -# id :integer not null, primary key -# message :text not null -# starts_at :datetime -# ends_at :datetime -# created_at :datetime -# updated_at :datetime -# color :string(255) -# font :string(255) -# - FactoryGirl.define do factory :broadcast_message do message "MyText" diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb index 19a54946fe0..b16c1272e68 100644 --- a/spec/factories/forked_project_links.rb +++ b/spec/factories/forked_project_links.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: forked_project_links -# -# id :integer not null, primary key -# forked_to_project_id :integer not null -# forked_from_project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :forked_project_link do association :forked_to_project, factory: :project diff --git a/spec/factories/label_links.rb b/spec/factories/label_links.rb index 2939d4307c5..3580174e873 100644 --- a/spec/factories/label_links.rb +++ b/spec/factories/label_links.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: label_links -# -# id :integer not null, primary key -# label_id :integer -# target_id :integer -# target_type :string(255) -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :label_link do label diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb index ea2be8928d5..eb489099854 100644 --- a/spec/factories/labels.rb +++ b/spec/factories/labels.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: labels -# -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# - FactoryGirl.define do factory :label do sequence(:title) { |n| "label#{n}" } diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index 327858ce435..a81645acd2b 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects -# -# id :integer not null, primary key -# oid :string(255) not null -# size :integer not null -# created_at :datetime -# updated_at :datetime -# file :string(255) -# - include ActionDispatch::TestProcess FactoryGirl.define do diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb index 50b45843c99..1ed0355c8e4 100644 --- a/spec/factories/lfs_objects_projects.rb +++ b/spec/factories/lfs_objects_projects.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects_projects -# -# id :integer not null, primary key -# lfs_object_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :lfs_objects_project do lfs_object diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index e281e2f227b..c6a08d78b78 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -1,32 +1,3 @@ -# == Schema Information -# -# Table name: merge_requests -# -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) -# merge_params :text -# merge_when_build_succeeds :boolean default(FALSE), not null -# merge_user_id :integer -# merge_commit_sha :string -# - FactoryGirl.define do factory :merge_request do title diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index e5dcb159014..840b13196a6 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: notes -# -# id :integer not null, primary key -# note :text -# noteable_type :string(255) -# author_id :integer -# created_at :datetime -# updated_at :datetime -# project_id :integer -# attachment :string(255) -# line_code :string(255) -# commit_id :string(255) -# noteable_id :integer -# system :boolean default(FALSE), not null -# st_diff :text -# updated_by_id :integer -# is_award :boolean default(FALSE), not null -# - require_relative '../support/repo_helpers' include ActionDispatch::TestProcess diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb index 7700b15d538..ccf02d0719b 100644 --- a/spec/factories/oauth_access_tokens.rb +++ b/spec/factories/oauth_access_tokens.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: oauth_access_tokens -# -# id :integer not null, primary key -# resource_owner_id :integer -# application_id :integer -# token :string not null -# refresh_token :string -# expires_in :integer -# revoked_at :datetime -# created_at :datetime not null -# scopes :string -# - FactoryGirl.define do factory :oauth_access_token do resource_owner diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index c14b99606ba..da8d97c9f82 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -1,43 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# - FactoryGirl.define do # Project without repository # @@ -61,6 +21,12 @@ FactoryGirl.define do trait :private do visibility_level Gitlab::VisibilityLevel::PRIVATE end + + trait :empty_repo do + after(:create) do |project| + project.create_repository + end + end end # Project with empty repository @@ -68,9 +34,7 @@ FactoryGirl.define do # This is a case when you just created a project # but not pushed any code there yet factory :project_empty_repo, parent: :empty_project do - after :create do |project| - project.create_repository - end + empty_repo end # Project with test repository diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 7f331c37256..74497dc82c0 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: releases -# -# id :integer not null, primary key -# tag :string(255) -# description :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :release do tag "v1.1.0" diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb index 7ae06c27840..e3681ae93a5 100644 --- a/spec/factories/todos.rb +++ b/spec/factories/todos.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: todos -# -# id :integer not null, primary key -# user_id :integer not null -# project_id :integer not null -# target_id :integer -# target_type :string not null -# author_id :integer -# action :integer not null -# state :string not null -# created_at :datetime -# updated_at :datetime -# note_id :integer -# commit_id :string -# - FactoryGirl.define do factory :todo do project 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/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb new file mode 100644 index 00000000000..cc7f78e7325 --- /dev/null +++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb @@ -0,0 +1,137 @@ +require 'spec_helper' + +describe 'Projects > Merge requests > User lists merge requests', feature: true do + include SortingHelper + + let(:project) { create(:project, :public) } + let(:user) { create(:user) } + + before do + @fix = create(:merge_request, + title: 'fix', + source_project: project, + source_branch: 'fix', + assignee: user, + milestone: create(:milestone, due_date: '2013-12-11'), + created_at: 1.minute.ago, + updated_at: 1.minute.ago) + create(:merge_request, + title: 'markdown', + source_project: project, + source_branch: 'markdown', + assignee: user, + milestone: create(:milestone, due_date: '2013-12-12'), + created_at: 2.minutes.ago, + updated_at: 2.minutes.ago) + create(:merge_request, + title: 'lfs', + source_project: project, + source_branch: 'lfs', + created_at: 3.minutes.ago, + updated_at: 10.seconds.ago) + end + + it 'filters on no assignee' do + visit_merge_requests(project, assignee_id: IssuableFinder::NONE) + + expect(current_path).to eq(namespace_project_merge_requests_path(project.namespace, project)) + expect(page).to have_content 'lfs' + expect(page).not_to have_content 'fix' + expect(page).not_to have_content 'markdown' + end + + it 'filters on a specific assignee' do + visit_merge_requests(project, assignee_id: user.id) + + expect(page).not_to have_content 'lfs' + expect(page).to have_content 'fix' + expect(page).to have_content 'markdown' + end + + it 'sorts by newest' do + visit_merge_requests(project, sort: sort_value_recently_created) + + expect(first_merge_request).to include('lfs') + expect(last_merge_request).to include('fix') + end + + it 'sorts by oldest' do + visit_merge_requests(project, sort: sort_value_oldest_created) + + expect(first_merge_request).to include('fix') + expect(last_merge_request).to include('lfs') + end + + it 'sorts by last updated' do + visit_merge_requests(project, sort: sort_value_recently_updated) + + expect(first_merge_request).to include('lfs') + end + + it 'sorts by oldest updated' do + visit_merge_requests(project, sort: sort_value_oldest_updated) + + expect(first_merge_request).to include('markdown') + end + + it 'sorts by milestone due soon' do + visit_merge_requests(project, sort: sort_value_milestone_soon) + + expect(first_merge_request).to include('fix') + end + + it 'sorts by milestone due later' do + visit_merge_requests(project, sort: sort_value_milestone_later) + + expect(first_merge_request).to include('markdown') + end + + it 'filters on one label and sorts by due soon' do + label = create(:label, project: project) + create(:label_link, label: label, target: @fix) + + visit_merge_requests(project, label_name: [label.name], + sort: sort_value_due_date_soon) + + expect(first_merge_request).to include('fix') + end + + context 'while filtering on two labels' do + let(:label) { create(:label, project: project) } + let(:label2) { create(:label, project: project) } + + before do + create(:label_link, label: label, target: @fix) + create(:label_link, label: label2, target: @fix) + end + + it 'sorts by due soon' do + visit_merge_requests(project, label_name: [label.name, label2.name], + sort: sort_value_due_date_soon) + + expect(first_merge_request).to include('fix') + end + + context 'filter on assignee and' do + it 'sorts by due soon' do + visit_merge_requests(project, label_name: [label.name, label2.name], + assignee_id: user.id, + sort: sort_value_due_date_soon) + + expect(first_merge_request).to include('fix') + end + end + end + + def visit_merge_requests(project, opts = {}) + visit namespace_project_merge_requests_path(project.namespace, project, opts) + end + + def first_merge_request + page.all('ul.mr-list > li').first.text + end + + def last_merge_request + page.all('ul.mr-list > li').last.text + end +end diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb new file mode 100644 index 00000000000..0c51fe72ca4 --- /dev/null +++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb @@ -0,0 +1,63 @@ +require 'rails_helper' + +feature 'Developer views empty project instructions', feature: true do + let(:project) { create(:empty_project, :empty_repo) } + let(:developer) { create(:user) } + + background do + project.team << [developer, :developer] + + login_as(developer) + end + + context 'without an SSH key' do + scenario 'defaults to HTTP' do + visit_project + + expect_instructions_for('http') + end + + scenario 'switches to SSH', js: true do + visit_project + + select_protocol('SSH') + + expect_instructions_for('ssh') + end + end + + context 'with an SSH key' do + background do + create(:personal_key, user: developer) + end + + scenario 'defaults to SSH' do + visit_project + + expect_instructions_for('ssh') + end + + scenario 'switches to HTTP', js: true do + visit_project + + select_protocol('HTTP') + + expect_instructions_for('http') + end + end + + def visit_project + visit namespace_project_path(project.namespace, project) + end + + def select_protocol(protocol) + find('#clone-dropdown').click + find(".#{protocol.downcase}-selector").click + end + + def expect_instructions_for(protocol) + msg = :"#{protocol.downcase}_url_to_repo" + + expect(page).to have_content("git clone #{project.send(msg)}") + end +end diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb new file mode 100644 index 00000000000..08a97085a9c --- /dev/null +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +feature 'Master creates tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'with an invalid name displays an error' do + create_tag_in_form(tag: 'v 1.0', ref: 'master') + + expect(page).to have_content 'Tag name invalid' + end + + scenario 'with an invalid reference displays an error' do + create_tag_in_form(tag: 'v2.0', ref: 'foo') + + expect(page).to have_content 'Target foo is invalid' + end + + scenario 'that already exists displays an error' do + create_tag_in_form(tag: 'v1.1.0', ref: 'master') + + expect(page).to have_content 'Tag v1.1.0 already exists' + end + + scenario 'with multiline message displays the message in a <pre> block' do + create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world") + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v3.0')) + expect(page).to have_content 'v3.0' + page.within 'pre.body' do + expect(page).to have_content "Awesome tag message\n\n- hello\n- world" + end + end + + scenario 'with multiline release notes parses the release note as Markdown' do + create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world") + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v4.0')) + expect(page).to have_content 'v4.0' + page.within '.description' do + expect(page).to have_content 'Awesome release notes' + expect(page).to have_selector('ul li', count: 2) + end + end + + def create_tag_in_form(tag:, ref:, message: nil, desc: nil) + click_link 'New tag' + fill_in 'tag_name', with: tag + fill_in 'ref', with: ref + fill_in 'message', with: message unless message.nil? + fill_in 'release_description', with: desc unless desc.nil? + click_button 'Create tag' + end +end diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb new file mode 100644 index 00000000000..f0990118e3c --- /dev/null +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +feature 'Master deletes tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + context 'from the tags list page' do + scenario 'deletes the tag' do + expect(page).to have_content 'v1.1.0' + + page.within('.content') do + first('.btn-remove').click + end + + expect(current_path).to eq( + namespace_project_tags_path(project.namespace, project)) + expect(page).not_to have_content 'v1.1.0' + end + + end + + context 'from a specific tag page' do + scenario 'deletes the tag' do + click_on 'v1.0.0' + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Delete tag' + + expect(current_path).to eq( + namespace_project_tags_path(project.namespace, project)) + expect(page).not_to have_content 'v1.0.0' + end + end +end diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb new file mode 100644 index 00000000000..c926e9841f3 --- /dev/null +++ b/spec/features/tags/master_updates_tag_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +feature 'Master updates tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + context 'from the tags list page' do + scenario 'updates the release notes' do + page.within(first('.controls')) do + click_link 'Edit release notes' + end + + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Save changes' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.1.0')) + expect(page).to have_content 'v1.1.0' + expect(page).to have_content 'Awesome release notes' + end + end + + context 'from a specific tag page' do + scenario 'updates the release notes' do + click_on 'v1.1.0' + click_link 'Edit release notes' + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Save changes' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.1.0')) + expect(page).to have_content 'v1.1.0' + expect(page).to have_content 'Awesome release notes' + end + end +end diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb new file mode 100644 index 00000000000..29d2c244720 --- /dev/null +++ b/spec/features/tags/master_views_tags_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +feature 'Master views tags', feature: true do + let(:user) { create(:user) } + + before do + project.team << [user, :master] + login_with(user) + end + + context 'when project has no tags' do + let(:project) { create(:project_empty_repo) } + before do + visit namespace_project_path(project.namespace, project) + click_on 'README' + fill_in :commit_message, with: 'Add a README file', visible: true + # Remove pre-receive hook so we can push without auth + FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive')) + click_button 'Commit Changes' + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'displays a specific message' do + expect(page).to have_content 'Repository has no tags yet.' + end + end + + context 'when project has tags' do + let(:project) { create(:project, namespace: user.namespace) } + before do + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'views the tags list page' do + expect(page).to have_content 'v1.0.0' + end + + scenario 'views a specific tag page' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + expect(page).to have_content 'v1.0.0' + expect(page).to have_content 'This tag has no release notes.' + end + + describe 'links on the tag page' do + scenario 'has a button to browse files' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Browse files' + + expect(current_path).to eq( + namespace_project_tree_path(project.namespace, project, 'v1.0.0')) + end + + scenario 'has a button to browse commits' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Browse commits' + + expect(current_path).to eq( + namespace_project_commits_path(project.namespace, project, 'v1.0.0')) + end + end + end +end 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/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 29bcb8c5892..ac5af8740dc 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -88,18 +88,18 @@ describe ProjectsHelper do end describe 'default_clone_protocol' do - describe 'using HTTP' do + context 'when user is not logged in and gitlab protocol is HTTP' do it 'returns HTTP' do - expect(helper).to receive(:current_user).and_return(nil) + allow(helper).to receive(:current_user).and_return(nil) expect(helper.send(:default_clone_protocol)).to eq('http') end end - describe 'using HTTPS' do + context 'when user is not logged in and gitlab protocol is HTTPS' do it 'returns HTTPS' do - allow(Gitlab.config.gitlab).to receive(:protocol).and_return('https') - expect(helper).to receive(:current_user).and_return(nil) + stub_config_setting(protocol: 'https') + allow(helper).to receive(:current_user).and_return(nil) expect(helper.send(:default_clone_protocol)).to eq('https') end diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb new file mode 100644 index 00000000000..4bb149f25ff --- /dev/null +++ b/spec/initializers/trusted_proxies_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'trusted_proxies', lib: true do + context 'with default config' do + before do + set_trusted_proxies([]) + end + + it 'preserves private IPs as remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '10.1.5.89') + expect(request.remote_ip).to eq('10.1.5.89') + end + + it 'filters out localhost from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.1.1.1, 10.1.5.89, 127.0.0.1') + expect(request.remote_ip).to eq('10.1.5.89') + end + end + + context 'with private IP ranges added' do + before do + set_trusted_proxies([ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ]) + end + + it 'filters out private and local IPs from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.2.3.6, 1.1.1.1, 10.1.5.89, 127.0.0.1') + expect(request.remote_ip).to eq('1.1.1.1') + end + end + + context 'with proxy IP added' do + before do + set_trusted_proxies([ "60.98.25.47" ]) + end + + it 'filters out proxy IP from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.2.3.6, 1.1.1.1, 60.98.25.47, 127.0.0.1') + expect(request.remote_ip).to eq('1.1.1.1') + end + end + + def stub_request(headers = {}) + ActionDispatch::RemoteIp.new(Proc.new { }, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers) + ActionDispatch::Request.new(headers) + end + + def set_trusted_proxies(proxies = []) + stub_config_setting('trusted_proxies' => proxies) + load File.join(__dir__, '../../config/initializers/trusted_proxies.rb') + end +end diff --git a/spec/javascripts/merge_request_widget_spec.js.coffee b/spec/javascripts/merge_request_widget_spec.js.coffee index c0bd8a29e43..92b7eeb1116 100644 --- a/spec/javascripts/merge_request_widget_spec.js.coffee +++ b/spec/javascripts/merge_request_widget_spec.js.coffee @@ -47,3 +47,9 @@ describe 'MergeRequestWidget', -> spy = spyOn(@class, 'showCICoverage').and.stub() @class.getCIStatus(false) expect(spy).not.toHaveBeenCalled() + + it 'should not display a notification on the first check after the widget has been created', -> + spy = spyOn(window, 'notify') + @class = new MergeRequestWidget(@opts) + @class.getCIStatus(true) + expect(spy).not.toHaveBeenCalled() diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index ebf3d7489b5..5beb61dac5c 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -43,7 +43,7 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do milestone.update_attribute(:title, %{"></a>whatever<a title="}) doc = reference_filter("milestone #{reference}") - expect(doc.text).to eq "milestone #{milestone.title}" + expect(doc.text).to eq "milestone \">whatever" end it 'includes default classes' do diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index 27ce312b11c..b38e3b17e64 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -22,6 +22,12 @@ describe Banzai::Filter::SanitizationFilter, lib: true do expect(filter(act).to_html).to eq exp end + it 'sanitizes mixed-cased javascript in attributes' do + act = %q(<a href="javaScript:alert('foo')">Text</a>) + exp = '<a>Text</a>' + expect(filter(act).to_html).to eq exp + end + it 'allows whitelisted HTML tags from the user' do exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>" expect(filter(act).to_html).to eq exp diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 3b073a90a95..b83be54746c 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -8,6 +8,10 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do project: project }) + raw_filter(doc, contexts) + end + + def raw_filter(doc, contexts = {}) described_class.call(doc, contexts) end @@ -70,4 +74,18 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png" end end + + context 'when project context does not exist' do + let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') } + + it 'does not raise error' do + expect { raw_filter(upload_link, project: nil) }.not_to raise_error + end + + it 'does not rewrite link' do + doc = raw_filter(upload_link, project: nil) + + expect(doc.to_html).to eq upload_link + 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 <" do + expect(subject.convert("<")[:html]).to eq('<') + 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/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index ac12ab6c757..305f8bc88cc 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: abuse_reports -# -# id :integer not null, primary key -# reporter_id :integer -# user_id :integer -# message :text -# created_at :datetime -# updated_at :datetime -# - require 'rails_helper' RSpec.describe AbuseReport, type: :model do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 520cf1b75de..d84f3e998f5 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1,49 +1,3 @@ -# == Schema Information -# -# Table name: application_settings -# -# id :integer not null, primary key -# default_projects_limit :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string(255) -# default_branch_protection :integer default(2) -# restricted_visibility_levels :text -# version_check_enabled :boolean default(TRUE) -# max_attachment_size :integer default(10), not null -# default_project_visibility :integer -# default_snippet_visibility :integer -# restricted_signup_domains :text -# user_oauth_applications :boolean default(TRUE) -# after_sign_out_path :string(255) -# session_expire_delay :integer default(10080), not null -# import_sources :text -# help_page_text :text -# admin_notification_email :string(255) -# shared_runners_enabled :boolean default(TRUE), not null -# max_artifacts_size :integer default(100), not null -# runners_registration_token :string -# require_two_factor_authentication :boolean default(FALSE) -# two_factor_grace_period :integer default(48) -# metrics_enabled :boolean default(FALSE) -# metrics_host :string default("localhost") -# metrics_username :string -# metrics_password :string -# metrics_pool_size :integer default(16) -# metrics_timeout :integer default(10) -# metrics_method_call_threshold :integer default(10) -# recaptcha_enabled :boolean default(FALSE) -# recaptcha_site_key :string -# recaptcha_private_key :string -# metrics_port :integer default(8089) -# sentry_enabled :boolean default(FALSE) -# sentry_dsn :string -# - require 'spec_helper' describe ApplicationSetting, models: true do @@ -66,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/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index f6f84db57e6..6ad8bfef4f2 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: broadcast_messages -# -# id :integer not null, primary key -# message :text not null -# starts_at :datetime -# ends_at :datetime -# created_at :datetime -# updated_at :datetime -# color :string(255) -# font :string(255) -# - require 'spec_helper' describe BroadcastMessage, models: true do diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index a747aa08447..dc071ad1c90 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: ci_commits -# -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Commit, models: true do diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb index 000a732db77..95fc160b238 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: ci_runner_projects -# -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::RunnerProject, models: true do diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 25e9e5eca48..eaa94228922 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: ci_runners -# -# id :integer not null, primary key -# token :string(255) -# created_at :datetime -# updated_at :datetime -# description :string(255) -# contacted_at :datetime -# active :boolean default(TRUE), not null -# is_shared :boolean default(FALSE) -# name :string(255) -# version :string(255) -# revision :string(255) -# platform :string(255) -# architecture :string(255) -# - require 'spec_helper' describe Ci::Runner, models: true do diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 159be939300..474b0b1621d 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: ci_triggers -# -# id :integer not null, primary key -# token :string(255) -# project_id :integer -# deleted_at :datetime -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Trigger, models: true do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 71e84091cb7..c712d211b0f 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: ci_variables -# -# id :integer not null, primary key -# project_id :integer -# key :string(255) -# value :text -# encrypted_value :text -# encrypted_value_salt :string(255) -# encrypted_value_iv :string(255) -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Variable, models: true 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/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 971e6750375..434e58cfd06 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -1,37 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string(255) -# user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) -# artifacts_file :text -# gl_project_id :integer -# - require 'spec_helper' describe CommitStatus, models: true do 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/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 64ba778afea..6a90598a629 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) -# public :boolean default(FALSE), not null -# - require 'spec_helper' describe DeployKey, models: true do diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index 8aedbfb8636..8a1e337c1a3 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: deploy_keys_projects -# -# id :integer not null, primary key -# deploy_key_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe DeployKeysProject, models: true do diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index a20a6149649..5d0bd31db5a 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: emails -# -# id :integer not null, primary key -# user_id :integer not null -# email :string(255) not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe Email, models: true do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 0c3cd13f399..b0e76fec693 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# target_type :string(255) -# target_id :integer -# title :string(255) -# data :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# action :integer -# author_id :integer -# - require 'spec_helper' describe Event, models: true do diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb index d90fbfe1ea5..3b817608ce0 100644 --- a/spec/models/forked_project_link_spec.rb +++ b/spec/models/forked_project_link_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: forked_project_links -# -# id :integer not null, primary key -# forked_to_project_id :integer not null -# forked_from_project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe ForkedProjectLink, "add link on fork" do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 5b0883d8702..0caf5869c24 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -1,37 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string(255) -# user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) -# artifacts_file :text -# gl_project_id :integer -# - require 'spec_helper' describe GenericCommitStatus, models: true do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 7bfca1e72c3..6fa16be7f04 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) -# - require 'spec_helper' describe Group, models: true do 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/identity_spec.rb b/spec/models/identity_spec.rb index 5afe042e154..1b987588f59 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: identities -# -# id :integer not null, primary key -# extern_uid :string(255) -# provider :string(255) -# user_id :integer -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' RSpec.describe Identity, models: true do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 060e6599104..8ab00c70f9d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1,23 +1,3 @@ -# == Schema Information -# -# Table name: issues -# -# id :integer not null, primary key -# title :string(255) -# assignee_id :integer -# author_id :integer -# project_id :integer -# created_at :datetime -# updated_at :datetime -# position :integer default(0) -# branch_name :string(255) -# description :text -# milestone_id :integer -# state :string(255) -# iid :integer -# updated_by_id :integer -# - require 'spec_helper' describe Issue, models: true do diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index c962b83644a..26fbedbef2f 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) -# public :boolean default(FALSE), not null -# - require 'spec_helper' describe Key, models: true do diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index dc7510b1de3..5e6f8ca1528 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: label_links -# -# id :integer not null, primary key -# label_id :integer -# target_id :integer -# target_type :string(255) -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe LabelLink, models: true do diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 0614ca1e7c9..dad2628651b 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: labels -# -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# - require 'spec_helper' describe Label, models: true do @@ -55,6 +42,14 @@ describe Label, models: true do end end + describe "#title" do + let(:label) { create(:label, title: "<b>test</b>") } + + it "sanitizes title" do + expect(label.title).to eq("test") + end + end + describe '#to_reference' do context 'using id' do it 'returns a String reference to the object' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 2d8f1cc1ad3..6e51730eecd 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: members -# -# id :integer not null, primary key -# access_level :integer not null -# source_id :integer not null -# source_type :string(255) not null -# user_id :integer -# notification_level :integer not null -# type :string(255) -# created_at :datetime -# updated_at :datetime -# created_by_id :integer -# invite_email :string(255) -# invite_token :string(255) -# invite_accepted_at :datetime -# - require 'spec_helper' describe Member, models: true do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index d7884cea336..c8578749b21 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1,32 +1,3 @@ -# == Schema Information -# -# Table name: merge_requests -# -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) -# merge_params :text -# merge_when_build_succeeds :boolean default(FALSE), not null -# merge_user_id :integer -# merge_commit_sha :string -# - require 'spec_helper' describe MergeRequest, models: true do diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 72a4ea70228..247a9fa9910 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: milestones -# -# id :integer not null, primary key -# title :string(255) not null -# project_id :integer not null -# description :text -# due_date :date -# created_at :datetime -# updated_at :datetime -# state :string(255) -# iid :integer -# - require 'spec_helper' describe Milestone, models: true do @@ -34,6 +19,14 @@ describe Milestone, models: true do let(:issue) { create(:issue) } let(:user) { create(:user) } + describe "#title" do + let(:milestone) { create(:milestone, title: "<b>test</b>") } + + it "sanitizes title" do + expect(milestone.title).to eq("test") + end + end + describe "unique milestone title per project" do it "shouldn't accept the same title in a project twice" do new_milestone = Milestone.new(project: milestone.project, title: milestone.title) diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 3c3a580942a..4074f966299 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) -# - require 'spec_helper' describe Namespace, models: true do diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 6b18936edb1..4b788b57882 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: notes -# -# id :integer not null, primary key -# note :text -# noteable_type :string(255) -# author_id :integer -# created_at :datetime -# updated_at :datetime -# project_id :integer -# attachment :string(255) -# line_code :string(255) -# commit_id :string(255) -# noteable_id :integer -# system :boolean default(FALSE), not null -# st_diff :text -# updated_by_id :integer -# is_award :boolean default(FALSE), not null -# - require 'spec_helper' describe Note, models: true do 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/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index e0feb606f78..d9d7c0b0aaa 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string(255) -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string(255) -# type :string(255) -# visibility_level :integer default(0), not null -# - require 'spec_helper' describe ProjectSnippet, models: true do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5b1cf71337e..f6e5b132643 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1,43 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# - require 'spec_helper' describe Project, models: true do diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 7e956cf6779..b523834c6e9 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: protected_branches -# -# id :integer not null, primary key -# project_id :integer not null -# name :string(255) not null -# created_at :datetime -# updated_at :datetime -# developers_can_push :boolean default(FALSE), not null -# - require 'spec_helper' describe ProtectedBranch, models: true do diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 72ecb442a36..527005b2b69 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: releases -# -# id :integer not null, primary key -# tag :string(255) -# description :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# - require 'rails_helper' RSpec.describe Release, type: :model do diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 173628c08d0..8592e112c50 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# - require 'spec_helper' describe Service, models: true do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 5077ac7b62b..7a613e360d4 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string(255) -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string(255) -# type :string(255) -# visibility_level :integer default(0), not null -# - require 'spec_helper' describe Snippet, models: true do diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index d9b86b9368f..623b82c01d8 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: todos -# -# id :integer not null, primary key -# user_id :integer not null -# project_id :integer not null -# target_id :integer -# target_type :string not null -# author_id :integer -# action :integer not null -# state :string not null -# created_at :datetime -# updated_at :datetime -# note_id :integer -# commit_id :string -# - require 'spec_helper' describe Todo, models: true do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8b2fb77e28e..10e7e693571 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,66 +1,3 @@ -# == Schema Information -# -# Table name: users -# -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# last_credential_check_at :datetime -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# notification_email :string(255) -# hide_no_password :boolean default(FALSE) -# password_automatically_set :boolean default(FALSE) -# location :string(255) -# encrypted_otp_secret :string(255) -# encrypted_otp_secret_iv :string(255) -# encrypted_otp_secret_salt :string(255) -# otp_required_for_login :boolean default(FALSE), not null -# otp_backup_codes :text -# public_email :string(255) default(""), not null -# dashboard :integer default(0) -# project_view :integer default(0) -# consumed_timestep :integer -# layout :integer default(0) -# hide_project_limit :boolean default(FALSE) -# unlock_token :string -# otp_grace_period_started_at :datetime -# - require 'spec_helper' describe User, models: true do @@ -845,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/commit_status_spec.rb b/spec/requests/api/commit_statuses_spec.rb index f3785b19362..633927c8c3e 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::CommitStatus, api: true do +describe API::CommitStatuses, api: true do include ApiHelpers let!(:project) { create(:project) } 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 f88e39cad9e..37ab9cc8cfe 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -39,6 +39,7 @@ describe API::API, api: true do let!(:empty_milestone) do create(:milestone, title: '2.0.0', project: project) end + let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) } before { project.team << [user, :reporter] } @@ -232,8 +233,28 @@ describe API::API, api: true do end describe "GET /projects/:id/issues/:issue_id" do + it 'exposes known attributes' do + get api("/projects/#{project.id}/issues/#{issue.id}", user) + + expect(response.status).to eq(200) + expect(json_response['id']).to eq(issue.id) + expect(json_response['iid']).to eq(issue.iid) + expect(json_response['project_id']).to eq(issue.project.id) + expect(json_response['title']).to eq(issue.title) + expect(json_response['description']).to eq(issue.description) + expect(json_response['state']).to eq(issue.state) + expect(json_response['created_at']).to be_present + expect(json_response['updated_at']).to be_present + expect(json_response['labels']).to eq(issue.label_names) + expect(json_response['milestone']).to be_a Hash + expect(json_response['assignee']).to be_a Hash + expect(json_response['author']).to be_a Hash + expect(json_response['user_notes_count']).to be(1) + end + it "should return a project issue by id" do get api("/projects/#{project.id}/issues/#{issue.id}", user) + expect(response.status).to eq(200) expect(json_response['title']).to eq(issue.title) expect(json_response['iid']).to eq(issue.iid) @@ -602,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 @@ -623,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/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 1fa7e76894f..4b0111df149 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -113,6 +113,34 @@ describe API::API, api: true do end describe "GET /projects/:id/merge_requests/:merge_request_id" do + it 'exposes known attributes' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) + + expect(response.status).to eq(200) + expect(json_response['id']).to eq(merge_request.id) + expect(json_response['iid']).to eq(merge_request.iid) + expect(json_response['project_id']).to eq(merge_request.project.id) + expect(json_response['title']).to eq(merge_request.title) + expect(json_response['description']).to eq(merge_request.description) + expect(json_response['state']).to eq(merge_request.state) + expect(json_response['created_at']).to be_present + expect(json_response['updated_at']).to be_present + expect(json_response['labels']).to eq(merge_request.label_names) + expect(json_response['milestone']).to be_nil + expect(json_response['assignee']).to be_a Hash + expect(json_response['author']).to be_a Hash + expect(json_response['target_branch']).to eq(merge_request.target_branch) + expect(json_response['source_branch']).to eq(merge_request.source_branch) + expect(json_response['upvotes']).to eq(0) + expect(json_response['downvotes']).to eq(0) + expect(json_response['source_project_id']).to eq(merge_request.source_project.id) + expect(json_response['target_project_id']).to eq(merge_request.target_project.id) + expect(json_response['work_in_progress']).to be_falsy + expect(json_response['merge_when_build_succeeds']).to be_falsy + expect(json_response['merge_status']).to eq('can_be_merged') + expect(json_response['user_notes_count']).to be(2) + end + it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) expect(response.status).to eq(200) 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/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 240eae10052..5fbf2ae5247 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -506,6 +506,15 @@ describe SystemNoteService, services: true do end end + describe '.new_commit_summary' do + it 'escapes HTML titles' do + commit = double(title: '<pre>This is a test</pre>', short_id: '12345678') + escaped = '* 12345678 - <pre>This is a test</pre>' + + expect(described_class.new_commit_summary([commit])).to eq([escaped]) + end + end + include JiraServiceHelper describe 'JIRA integration' do diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 82b7fbfa816..a075496ee63 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -55,6 +55,25 @@ describe TodoService, services: true do should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) end + + context 'when a private group is mentioned' do + let(:group) { create :group, :private } + let(:project) { create :project, :private, group: group } + let(:issue) { create :issue, author: author, project: project, description: group.to_reference } + + before do + group.add_owner(author) + group.add_user(member, Gitlab::Access::DEVELOPER) + group.add_user(john_doe, Gitlab::Access::DEVELOPER) + + service.new_issue(issue, author) + end + + it 'creates a todo for group members' do + should_create_todo(user: member, target: issue) + should_create_todo(user: john_doe, target: issue) + end + end end describe '#update_issue' do 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; +}); |