diff options
575 files changed, 4680 insertions, 1937 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 219077d79b8..ff8aa351226 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,6 +18,7 @@ variables: SIMPLECOV: "true" USE_DB: "true" USE_BUNDLE_INSTALL: "true" + GIT_DEPTH: "20" before_script: - source ./scripts/prepare_build.sh @@ -134,6 +135,11 @@ spinach 9 10: *spinach-knapsack image: "ruby:2.3" only: - master + cache: + key: "ruby-23" + paths: + - vendor/apt + - vendor/ruby .rspec-knapsack-ruby23: &rspec-knapsack-ruby23 <<: *rspec-knapsack diff --git a/.rubocop.yml b/.rubocop.yml index dbdabbb9d4c..cc3055ef66e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,6 @@ -require: rubocop-rspec +require: + - rubocop-rspec + - ./rubocop/rubocop AllCops: TargetRubyVersion: 2.1 @@ -532,11 +534,11 @@ Style/SingleLineMethods: # Use spaces after colons. Style/SpaceAfterColon: - Enabled: false + Enabled: true # Use spaces after commas. Style/SpaceAfterComma: - Enabled: false + Enabled: true # Do not put a space between a method name and the opening parenthesis in a # method definition. diff --git a/CHANGELOG b/CHANGELOG index 7179cd13e11..775ea606813 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,95 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.10.0 (unreleased) + - Fix commit builds API, return all builds for all pipelines for given commit. !4849 + - Replace Haml with Hamlit to make view rendering faster. !3666 - Wrap code blocks on Activies and Todos page. !4783 (winniehell) + - Display last commit of deleted branch in push events !4699 (winniehell) + - Add Sidekiq queue duration to transaction metrics. + - Let Workhorse serve format-patch diffs + - Make images fit to the size of the viewport !4810 + - Fix check for New Branch button on Issue page !4630 (winniehell) - Fix MR-auto-close text added to description. !4836 + - Fix pagination when sorting by columns with lots of ties (like priority) + - Exclude email check from the standard health check + - Fix changing issue state columns in milestone view + - Add notification settings dropdown for groups + - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) + - PipelinesFinder uses git cache data + - Check for conflicts with existing Project's wiki path when creating a new project. + - Remove unused front-end variable -> default_issues_tracker + - Add API endpoint for a group issues !4520 (mahcsig) + - Add Bugzilla integration !4930 (iamtjg) + - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w) + - Add basic system information like memory and disk usage to the admin panel + +v 8.9.3 + - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963 + - Fix rendering of commit notes. !4953 + - Resolve "Pin should show up at 1280px min". !4947 + - Switched mobile button icons to ellipsis and angle. !4944 + - Correctly returns todo ID after creating todo. !4941 + - Better debugging for memory killer middleware. !4936 + - Remove duplicate new page btn from edit wiki. !4904 + - Use clock_gettime for all performance timestamps. !4899 + - Use memorized tags array when searching tags by name. !4859 + - Fixed avatar alignment in new MR view. !4901 + - Removed fade when filtering results. !4932 + - Fix missing avatar on system notes. !4954 + - Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973 + - Use update_columns to by_pass all the dirty code on active_record. !4985 + +v 8.9.2 + - Fix visibility of snippets when searching. + - Fix an information disclosure when requesting access to a group containing private projects. + - Update omniauth-saml to 1.6.0 !4951 + +v 8.9.1 + - Refactor labels documentation. !3347 + - Eager load award emoji on notes. !4628 + - Fix some CI wording in documentation. !4660 + - Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720 + - Add documentation for the export & import features. !4732 + - Add some docs for Docker Registry configuration. !4738 + - Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744 + - Display group/project access requesters separately in the admin area. !4798 + - Add documentation and examples for configuring cloud storage for registry images. !4812 + - Clarifies documentation about artifact expiry. !4831 + - Fix the Network graph links. !4832 + - Fix MR-auto-close text added to description. !4836 + - Add documentation for award emoji now that comments can be awarded with emojis. !4839 + - Fix typo in export failure email. !4847 + - Fix header vertical centering. !4170 + - Fix subsequent SAML sign ins. !4718 + - Set button label when picking an option from status dropdown. !4771 + - Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775 + - Handle external issues in IssueReferenceFilter. !4789 + - Support for rendering/redacting multiple documents. !4828 + - Update Todos documentation and screenshots to include new functionality. !4840 + - Hide nav arrows by default. !4843 + - Added bottom padding to label color suggestion link. !4845 + - Use jQuery objects in ref dropdown. !4850 + - Fix GitLab project import issues related to notes and builds. !4855 + - Restrict header logo to 36px so it doesn't overflow. !4861 + - Fix unwanted label unassignment. !4863 + - Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869 + - Restore old behavior around diff notes to outdated discussions. !4870 + - Fix merge requests project settings help link anchor. !4873 + - Fix 404 when accessing pipelines as guest user on public projects. !4881 + - Remove width restriction for logo on sign-in page. !4888 + - Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884 + - Apply selected value as label. !4886 + - Fix temp file being deleted after the request while importing a GitLab project. !4894 + - Fix pagination when sorting by columns with lots of ties (like priority) + - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise. + - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) + - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912 + - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915 + - Remove duplicate 'New Page' button on edit wiki page v 8.9.0 +v 8.9.0 (unreleased) + - Fix group visibility form layout in application settings - Fix builds API response not including commit data - Fix error when CI job variables key specified but not defined - Fix pipeline status when there are no builds in pipeline @@ -100,6 +185,7 @@ v 8.9.0 - Add Application Setting to configure Container Registry token expire delay (default 5min) - Cache assigned issue and merge request counts in sidebar nav - Use Knapsack only in CI environment + - Updated project creation page to match new UI #2542 - Cache project build count in sidebar nav - Add milestone expire date to the right sidebar - Manually mark a issue or merge request as a todo @@ -155,6 +241,10 @@ v 8.9.0 - Add tooltip to pin/unpin navbar - Add new sub nav style to Wiki and Graphs sub navigation +v 8.8.6 + - Fix visibility of snippets when searching. + - Update omniauth-saml to 1.6.0 !4951 + v 8.8.5 - Import GitHub repositories respecting the API rate limit !4166 - Fix todos page throwing errors when you have a project pending deletion !4300 @@ -285,6 +375,10 @@ v 8.8.0 - When creating a .gitignore file a dropdown with templates will be provided - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562 +v 8.7.8 + - Fix visibility of snippets when searching. + - Update omniauth-saml to 1.6.0 !4951 + v 8.7.7 - Fix import by `Any Git URL` broken if the URL contains a space - Prevent unauthorized access to other projects build traces diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 8bd6ba8c5c3..879be8a98fc 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.7.5 +0.7.7 @@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos -gem 'omniauth-saml', '~> 1.5.0' +gem 'omniauth-saml', '~> 1.6.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd', '~> 2.2.0' @@ -76,7 +76,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem "kaminari", "~> 0.17.0" # HAML -gem "haml-rails", '~> 0.9.0' +gem 'hamlit', '~> 2.5' # Files attachments gem "carrierwave", '~> 0.10.0' @@ -91,6 +91,7 @@ gem 'fog-core', '~> 1.40' gem 'fog-local', '~> 0.3' gem 'fog-google', '~> 0.3' gem 'fog-openstack', '~> 0.1' +gem 'fog-rackspace', '~> 0.1.1' # for aws storage gem "unf", '~> 0.1.4' @@ -234,7 +235,7 @@ gem 'net-ssh', '~> 3.0.1' gem 'base32', '~> 0.3.0' # Sentry integration -gem 'sentry-raven', '~> 0.15' +gem 'sentry-raven', '~> 1.1.0' gem 'premailer-rails', '~> 1.9.0' @@ -346,3 +347,6 @@ gem "paranoia", "~> 2.0" # Health check gem 'health_check', '~> 1.5.1' + +# System information +gem 'vmstat', '~> 2.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index ba16e4bf337..4c5350ba639 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -243,6 +243,11 @@ GEM fog-core (>= 1.39) fog-json (>= 1.0) ipaddress (>= 0.8) + fog-rackspace (0.1.1) + fog-core (>= 1.35) + fog-json (>= 1.0) + fog-xml (>= 0.1) + ipaddress (>= 0.8) fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) @@ -277,7 +282,7 @@ GEM posix-spawn (~> 0.3) gitlab_emoji (0.3.1) gemojione (~> 2.2, >= 2.2.1) - gitlab_git (10.2.0) + gitlab_git (10.2.3) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) @@ -320,14 +325,10 @@ GEM grape-entity (0.4.8) activesupport multi_json (>= 1.3.2) - haml (4.0.7) + hamlit (2.5.0) + temple (~> 0.7.6) + thor tilt - haml-rails (0.9.0) - actionpack (>= 4.0.1) - activesupport (>= 4.0.1) - haml (>= 4.0.6, < 5.0) - html2haml (>= 1.0.1) - railties (>= 4.0.1) hashie (3.4.3) health_check (1.5.1) rails (>= 2.3.0) @@ -337,11 +338,6 @@ GEM html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) - html2haml (2.0.0) - erubis (~> 2.7.0) - haml (~> 4.0.0) - nokogiri (~> 1.6.0) - ruby_parser (~> 3.5) htmlentities (4.3.4) http_parser.rb (0.5.3) httparty (0.13.7) @@ -468,9 +464,9 @@ GEM omniauth-oauth2 (1.3.1) oauth2 (~> 1.0) omniauth (~> 1.2) - omniauth-saml (1.5.0) + omniauth-saml (1.6.0) omniauth (~> 1.3) - ruby-saml (~> 1.1, >= 1.1.1) + ruby-saml (~> 1.3) omniauth-shibboleth (1.2.1) omniauth (>= 1.0.0) omniauth-twitter (1.2.1) @@ -631,9 +627,8 @@ GEM ruby-fogbugz (0.2.1) crack (~> 0.4) ruby-progressbar (1.8.1) - ruby-saml (1.1.2) + ruby-saml (1.3.0) nokogiri (>= 1.5.10) - uuid (~> 2.3) ruby_parser (3.8.2) sexp_processor (~> 4.1) rubyntlm (0.5.2) @@ -665,7 +660,7 @@ GEM activesupport (>= 3.1, < 4.3) select2-rails (3.5.9.3) thor (~> 0.14) - sentry-raven (0.15.6) + sentry-raven (1.1.0) faraday (>= 0.7.6) settingslogic (2.0.9) sexp_processor (4.7.0) @@ -733,6 +728,7 @@ GEM railties (>= 3.2.5, < 6) teaspoon-jasmine (2.2.0) teaspoon (>= 1.0.0) + temple (0.7.7) term-ansicolor (1.3.2) tins (~> 1.0) test_after_commit (0.4.2) @@ -789,6 +785,7 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) + vmstat (2.1.0) warden (1.2.6) rack (>= 1.0) web-console (2.3.0) @@ -866,6 +863,7 @@ DEPENDENCIES fog-google (~> 0.3) fog-local (~> 0.3) fog-openstack (~> 0.1) + fog-rackspace (~> 0.1.1) font-awesome-rails (~> 4.6.1) foreman fuubar (~> 2.0.0) @@ -882,7 +880,7 @@ DEPENDENCIES gon (~> 6.0.1) grape (~> 0.13.0) grape-entity (~> 0.4.2) - haml-rails (~> 0.9.0) + hamlit (~> 2.5) health_check (~> 1.5.1) hipchat (~> 1.5.0) html-pipeline (~> 1.11.0) @@ -920,7 +918,7 @@ DEPENDENCIES omniauth-gitlab (~> 1.0.0) omniauth-google-oauth2 (~> 0.2.0) omniauth-kerberos (~> 0.3.0) - omniauth-saml (~> 1.5.0) + omniauth-saml (~> 1.6.0) omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd (~> 2.2.0) @@ -960,7 +958,7 @@ DEPENDENCIES sdoc (~> 0.3.20) seed-fu (~> 2.3.5) select2-rails (~> 3.5.9) - sentry-raven (~> 0.15) + sentry-raven (~> 1.1.0) settingslogic (~> 2.0.9) sham_rack shoulda-matchers (~> 2.8.0) @@ -993,6 +991,7 @@ DEPENDENCIES unicorn-worker-killer (~> 0.4.2) version_sorter (~> 2.0.0) virtus (~> 1.0.1) + vmstat (~> 2.1.0) web-console (~> 2.0) webmock (~> 1.21.0) wikicloth (= 0.8.1) diff --git a/app/assets/images/auth_buttons/azure_64.png b/app/assets/images/auth_buttons/azure_64.png Binary files differindex a82c751e001..85de7793440 100644 --- a/app/assets/images/auth_buttons/azure_64.png +++ b/app/assets/images/auth_buttons/azure_64.png diff --git a/app/assets/images/auth_buttons/bitbucket_64.png b/app/assets/images/auth_buttons/bitbucket_64.png Binary files differindex 4b90a57bc7d..b3d022a5a70 100644 --- a/app/assets/images/auth_buttons/bitbucket_64.png +++ b/app/assets/images/auth_buttons/bitbucket_64.png diff --git a/app/assets/images/auth_buttons/facebook_64.png b/app/assets/images/auth_buttons/facebook_64.png Binary files differindex 1f1a80d7368..71ffb1c6a1f 100644 --- a/app/assets/images/auth_buttons/facebook_64.png +++ b/app/assets/images/auth_buttons/facebook_64.png diff --git a/app/assets/images/auth_buttons/github_64.png b/app/assets/images/auth_buttons/github_64.png Binary files differindex 182a1a3f734..1fa19c55d2f 100644 --- a/app/assets/images/auth_buttons/github_64.png +++ b/app/assets/images/auth_buttons/github_64.png diff --git a/app/assets/images/auth_buttons/gitlab_64.png b/app/assets/images/auth_buttons/gitlab_64.png Binary files differindex 99a40583b3a..f675678dc9d 100644 --- a/app/assets/images/auth_buttons/gitlab_64.png +++ b/app/assets/images/auth_buttons/gitlab_64.png diff --git a/app/assets/images/auth_buttons/google_64.png b/app/assets/images/auth_buttons/google_64.png Binary files differindex fb64f8bee68..720824230a5 100644 --- a/app/assets/images/auth_buttons/google_64.png +++ b/app/assets/images/auth_buttons/google_64.png diff --git a/app/assets/images/auth_buttons/twitter_64.png b/app/assets/images/auth_buttons/twitter_64.png Binary files differindex e3bd9169a34..a4f14de57ae 100644 --- a/app/assets/images/auth_buttons/twitter_64.png +++ b/app/assets/images/auth_buttons/twitter_64.png diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png Binary files differindex e5fe659ba63..5c55bc79dec 100644 --- a/app/assets/images/bg_fallback.png +++ b/app/assets/images/bg_fallback.png diff --git a/app/assets/images/dark-scheme-preview.png b/app/assets/images/dark-scheme-preview.png Binary files differindex 2ef58e52549..8855babf147 100644 --- a/app/assets/images/dark-scheme-preview.png +++ b/app/assets/images/dark-scheme-preview.png diff --git a/app/assets/images/emoji.png b/app/assets/images/emoji.png Binary files differindex 1e7cf79ea45..99093d4725a 100644 --- a/app/assets/images/emoji.png +++ b/app/assets/images/emoji.png diff --git a/app/assets/images/emoji@2x.png b/app/assets/images/emoji@2x.png Binary files differindex 74d67f7520d..48989942a9f 100644 --- a/app/assets/images/emoji@2x.png +++ b/app/assets/images/emoji@2x.png diff --git a/app/assets/images/gitlab_logo.png b/app/assets/images/gitlab_logo.png Binary files differindex 0c157546b9c..ca30b459019 100644 --- a/app/assets/images/gitlab_logo.png +++ b/app/assets/images/gitlab_logo.png diff --git a/app/assets/images/gitorious-logo-black.png b/app/assets/images/gitorious-logo-black.png Binary files differindex 78f17a9af79..4a55fdc225a 100644 --- a/app/assets/images/gitorious-logo-black.png +++ b/app/assets/images/gitorious-logo-black.png diff --git a/app/assets/images/gitorious-logo-blue.png b/app/assets/images/gitorious-logo-blue.png Binary files differindex 4962cffba31..5eaa327d3df 100644 --- a/app/assets/images/gitorious-logo-blue.png +++ b/app/assets/images/gitorious-logo-blue.png diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png Binary files differindex 7d89da97e11..5b55e12571c 100644 --- a/app/assets/images/icon-link.png +++ b/app/assets/images/icon-link.png diff --git a/app/assets/images/images.png b/app/assets/images/images.png Binary files differindex ad146246caf..bd60de994c4 100644 --- a/app/assets/images/images.png +++ b/app/assets/images/images.png diff --git a/app/assets/images/monokai-scheme-preview.png b/app/assets/images/monokai-scheme-preview.png Binary files differindex fbb339c6a91..d9c7d2d8041 100644 --- a/app/assets/images/monokai-scheme-preview.png +++ b/app/assets/images/monokai-scheme-preview.png diff --git a/app/assets/images/msapplication-tile.png b/app/assets/images/msapplication-tile.png Binary files differindex 58bbf2b20cb..1e0e2ed73ce 100644 --- a/app/assets/images/msapplication-tile.png +++ b/app/assets/images/msapplication-tile.png diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png Binary files differindex 8287acbce13..5383d687b53 100644 --- a/app/assets/images/no_avatar.png +++ b/app/assets/images/no_avatar.png diff --git a/app/assets/images/no_group_avatar.png b/app/assets/images/no_group_avatar.png Binary files differindex bfb31bb2184..71612d55286 100644 --- a/app/assets/images/no_group_avatar.png +++ b/app/assets/images/no_group_avatar.png diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png Binary files differindex 884378ec96a..52ad11ab7a1 100644 --- a/app/assets/images/slider_handles.png +++ b/app/assets/images/slider_handles.png diff --git a/app/assets/images/touch-icon-ipad-retina.png b/app/assets/images/touch-icon-ipad-retina.png Binary files differindex feb32b48ec9..516dc2f4710 100644 --- a/app/assets/images/touch-icon-ipad-retina.png +++ b/app/assets/images/touch-icon-ipad-retina.png diff --git a/app/assets/images/touch-icon-ipad.png b/app/assets/images/touch-icon-ipad.png Binary files differindex a6ddc543509..b2093d015b8 100644 --- a/app/assets/images/touch-icon-ipad.png +++ b/app/assets/images/touch-icon-ipad.png diff --git a/app/assets/images/touch-icon-iphone-retina.png b/app/assets/images/touch-icon-iphone-retina.png Binary files differindex 8bf7ccb7534..438654e0d20 100644 --- a/app/assets/images/touch-icon-iphone-retina.png +++ b/app/assets/images/touch-icon-iphone-retina.png diff --git a/app/assets/images/touch-icon-iphone.png b/app/assets/images/touch-icon-iphone.png Binary files differindex 87da550f8be..e5f87fbbcf6 100644 --- a/app/assets/images/touch-icon-iphone.png +++ b/app/assets/images/touch-icon-iphone.png diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 0206db461da..b6dbf2d0cc1 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -50,7 +50,7 @@ #= require_directory ./ci #= require_directory ./commit #= require_directory ./extensions -#= require_directory ./lib +#= require_directory ./lib/utils #= require_directory ./u2f #= require_directory . #= require fuzzaldrin-plus @@ -199,7 +199,6 @@ $ -> $('.header-content .header-logo').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) -> @@ -261,7 +260,7 @@ $ -> new Aside() # Sidenav pinning - if $window.width() < 1440 and $.cookie('pin_nav') is 'true' + if $window.width() < 1280 and $.cookie('pin_nav') is 'true' $.cookie('pin_nav', 'false', { path: '/' }) $('.page-with-sidebar') .toggleClass('page-sidebar-collapsed page-sidebar-expanded') diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 030f1564862..37d0adaa625 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -341,7 +341,9 @@ class @AwardsHandler for emoji in frequentlyUsedEmojis $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) - $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) + $('.emoji-menu-content') + .prepend(ul) + .prepend($('<h5>').text('Frequently used')) @frequentEmojiBlockRendered = true @@ -356,7 +358,7 @@ class @AwardsHandler if term # Generate a search result block - h5 = $('<h5>').text('Search results').addClass('emoji-search') + h5 = $('<h5>').text('Search results') found_emojis = @searchEmojis(term).show() ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis) $('.emoji-menu-content ul, .emoji-menu-content h5').hide() diff --git a/app/assets/javascripts/blob/template_selector.js.coffee b/app/assets/javascripts/blob/template_selector.js.coffee index e76e303189d..40c9169beac 100644 --- a/app/assets/javascripts/blob/template_selector.js.coffee +++ b/app/assets/javascripts/blob/template_selector.js.coffee @@ -19,6 +19,7 @@ class @TemplateSelector data: @data, filterable: true, selectable: true, + toggleLabel: @toggleLabel, search: fields: ['name'] clicked: @onClick @@ -31,6 +32,9 @@ class @TemplateSelector @onFilenameUpdate() ) + toggleLabel: (item) -> + item.name + onFilenameUpdate: -> return unless @$input.length diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 7fbff9214cf..9493a575801 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -84,6 +84,8 @@ class Dispatcher new Activities() when 'groups:show' shortcut_handler = new ShortcutsNavigation() + new NotificationsForm() + new NotificationsDropdown() when 'groups:group_members:index' new GroupMembers() new UsersSelect() diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 2a7bf0bc306..ed9dfcc917e 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -186,6 +186,8 @@ class GitLabDropdown @fullData = data @parseData @fullData + + @filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input } # Init filterable @@ -218,6 +220,13 @@ class GitLabDropdown @dropdown.on 'keyup', (e) => if e.which is 27 # Escape key $('.dropdown-menu-close', @dropdown).trigger 'click' + @dropdown.on 'blur', 'a', (e) => + if e.relatedTarget? + $relatedTarget = $(e.relatedTarget) + $dropdownMenu = $relatedTarget.closest('.dropdown-menu') + + if $dropdownMenu.length is 0 + @dropdown.removeClass('open') if @dropdown.find(".dropdown-toggle-page").length @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @@ -280,7 +289,7 @@ class GitLabDropdown html = @renderData(data) # Render the full menu - full_html = @renderMenu(html.join("")) + full_html = @renderMenu(html) @appendMenu(full_html) @@ -351,7 +360,8 @@ class GitLabDropdown if @options.renderMenu menu_html = @options.renderMenu(html) else - menu_html = "<ul>#{html}</ul>" + menu_html = $('<ul />') + .append(html) return menu_html @@ -360,7 +370,9 @@ class GitLabDropdown selector = '.dropdown-content' if @dropdown.find(".dropdown-toggle-page").length selector = ".dropdown-page-one .dropdown-content" - $(selector, @dropdown).html html + $(selector, @dropdown) + .empty() + .append(html) # Render the row renderItem: (data, group = false, index = false) -> @@ -459,7 +471,7 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - @updateLabel() + @updateLabel(selectedObject, el, @) else selectedObject else if el.hasClass(INDETERMINATE_CLASS) @@ -486,7 +498,7 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - @updateLabel(selectedObject, el) + @updateLabel(selectedObject, el, @) if value? if !field.length and fieldName @addInput(fieldName, value) @@ -585,8 +597,8 @@ class GitLabDropdown # Scroll the dropdown content up $dropdownContent.scrollTop(listItemTop - dropdownContentTop) - updateLabel: (selected = null, el = null) => - $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el) + updateLabel: (selected = null, el = null, instance = null) => + $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance) $.fn.glDropdown = (opts) -> return @.each -> diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee index 91f81a5d249..e0f681acf0b 100644 --- a/app/assets/javascripts/graphs/application.js.coffee +++ b/app/assets/javascripts/graphs/application.js.coffee @@ -4,5 +4,4 @@ # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # the compiled file. # -#= require Chart #= require_tree . diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee index d0901be1509..0527c66461c 100644 --- a/app/assets/javascripts/issuable.js.coffee +++ b/app/assets/javascripts/issuable.js.coffee @@ -59,21 +59,23 @@ issuable_created = false filterResults: (form) => formData = form.serialize() - $('.issues-holder, .merge-requests-holder').css('opacity', '0.5') formAction = form.attr('action') issuesUrl = formAction issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") issuesUrl += formData - Turbolinks.visit(issuesUrl); + Turbolinks.visit(issuesUrl) initChecks: -> + @issuableBulkActions = $('.bulk-update').data('bulkActions') + $('.check_all_issues').off('click').on('click', -> $('.selected_issue').prop('checked', @checked) Issuable.checkChanged() ) - $('.selected_issue').off('change').on('change', Issuable.checkChanged) + $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@)) + checkChanged: -> checked_issues = $('.selected_issue:checked') @@ -88,3 +90,6 @@ issuable_created = false $('#update_issues_ids').val [] $('.issues_bulk_update').hide() $('.issues-other-filters').show() + @issuableBulkActions.willUpdateLabels = false + + return true diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 157361404e0..f446aa49cde 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -99,7 +99,7 @@ class @Issue # If the user doesn't have the required permissions the container isn't # rendered at all. - return unless $container + return if $container.length is 0 $.getJSON($container.data('path')) .error -> diff --git a/app/assets/javascripts/issue_status_select.js.coffee b/app/assets/javascripts/issue_status_select.js.coffee index c5740f27ddd..ed50e2e698f 100644 --- a/app/assets/javascripts/issue_status_select.js.coffee +++ b/app/assets/javascripts/issue_status_select.js.coffee @@ -6,6 +6,13 @@ class @IssueStatusSelect $(el).glDropdown( selectable: true fieldName: fieldName + toggleLabel: (selected, el, instance) => + label = 'Author' + $item = instance.dropdown.find('.is-active') + label = $item.text() if $item.length + label + clicked: (item, $el, e)-> + e.preventDefault() id: (obj, el) -> $(el).data("id") ) diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee index b454f9389dd..6b0e69dbae7 100644 --- a/app/assets/javascripts/issues-bulk-assignment.js.coffee +++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee @@ -7,6 +7,11 @@ class @IssuableBulkActions @issues = @getElement('.issues-list .issue') } = opts + # Save instance + @form.data 'bulkActions', @ + + @willUpdateLabels = false + @bindEvents() # Fixes bulk-assign not working when navigating through pages @@ -87,11 +92,12 @@ class @IssuableBulkActions add_label_ids : [] remove_label_ids : [] - @getLabelsToApply().map (id) -> - formData.update.add_label_ids.push id + if @willUpdateLabels + @getLabelsToApply().map (id) -> + formData.update.add_label_ids.push id - @getLabelsToRemove().map (id) -> - formData.update.remove_label_ids.push id + @getLabelsToRemove().map (id) -> + formData.update.remove_label_ids.push id formData diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index 6a10db10eb1..e95fd96a83f 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -319,6 +319,8 @@ class @LabelsSelect multiSelect: $dropdown.hasClass 'js-multiselect' clicked: (label) -> + _this.enableBulkLabelDropdown() + if $dropdown.hasClass('js-filter-bulk-update') return @@ -377,3 +379,8 @@ class @LabelsSelect label_ids.push $("#issue_#{issue_id}").data('labels') _.intersection.apply _, label_ids + + enableBulkLabelDropdown: -> + if $('.selected_issue:checked').length + issuableBulkActions = $('.bulk-update').data('bulkActions') + issuableBulkActions.willUpdateLabels = true diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee index cad095daae1..f639f7f5892 100644 --- a/app/assets/javascripts/layout_nav.js.coffee +++ b/app/assets/javascripts/layout_nav.js.coffee @@ -3,7 +3,7 @@ hideEndFade = ($scrollingTabs) -> $this = $(@) $this - .find('.fade-right') + .siblings('.fade-right') .toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')) $ -> @@ -20,5 +20,5 @@ $ -> currentPosition = $this.scrollLeft() maxPosition = $this.prop('scrollWidth') - $this.outerWidth() - $this.find('.fade-left').toggleClass('scrolling', currentPosition > 0) - $this.find('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1) + $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0) + $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1) diff --git a/app/assets/javascripts/lib/chart.js.coffee b/app/assets/javascripts/lib/chart.js.coffee new file mode 100644 index 00000000000..82217fc5107 --- /dev/null +++ b/app/assets/javascripts/lib/chart.js.coffee @@ -0,0 +1 @@ +#= require Chart diff --git a/app/assets/javascripts/lib/d3.js.coffee b/app/assets/javascripts/lib/d3.js.coffee new file mode 100644 index 00000000000..74f0a0bb06a --- /dev/null +++ b/app/assets/javascripts/lib/d3.js.coffee @@ -0,0 +1 @@ +#= require d3 diff --git a/app/assets/javascripts/lib/raphael.js.coffee b/app/assets/javascripts/lib/raphael.js.coffee new file mode 100644 index 00000000000..ab8e5979b87 --- /dev/null +++ b/app/assets/javascripts/lib/raphael.js.coffee @@ -0,0 +1,3 @@ +#= require raphael +#= require g.raphael +#= require g.bar diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/utils/animate.js.coffee index ec3b44d6126..ec3b44d6126 100644 --- a/app/assets/javascripts/lib/animate.js.coffee +++ b/app/assets/javascripts/lib/utils/animate.js.coffee diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/utils/common_utils.js.coffee index e39dcb2daa9..e39dcb2daa9 100644 --- a/app/assets/javascripts/lib/common_utils.js.coffee +++ b/app/assets/javascripts/lib/utils/common_utils.js.coffee diff --git a/app/assets/javascripts/lib/datetime_utility.js.coffee b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee index 948d6dbf07e..948d6dbf07e 100644 --- a/app/assets/javascripts/lib/datetime_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb index 80f9936b9c2..80f9936b9c2 100644 --- a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb +++ b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb diff --git a/app/assets/javascripts/lib/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js index cc17aa7d3d1..cc17aa7d3d1 100644 --- a/app/assets/javascripts/lib/jquery.timeago.js +++ b/app/assets/javascripts/lib/utils/jquery.timeago.js diff --git a/app/assets/javascripts/lib/md5.js b/app/assets/javascripts/lib/utils/md5.js index b63716eaad2..b63716eaad2 100644 --- a/app/assets/javascripts/lib/md5.js +++ b/app/assets/javascripts/lib/utils/md5.js diff --git a/app/assets/javascripts/lib/notify.js.coffee b/app/assets/javascripts/lib/utils/notify.js.coffee index 9e28353ac34..9e28353ac34 100644 --- a/app/assets/javascripts/lib/notify.js.coffee +++ b/app/assets/javascripts/lib/utils/notify.js.coffee diff --git a/app/assets/javascripts/lib/text_utility.js.coffee b/app/assets/javascripts/lib/utils/text_utility.js.coffee index bb2772dfed2..7bcb876d056 100644 --- a/app/assets/javascripts/lib/text_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/text_utility.js.coffee @@ -10,17 +10,41 @@ gl.text.selectedText = (text, textarea) -> text.substring(textarea.selectionStart, textarea.selectionEnd) - gl.text.insertText = (textArea, text, tag, selected, wrap) -> + gl.text.lineBefore = (text, textarea) -> + split = text.substring(0, textarea.selectionStart).trim().split('\n') + split[split.length - 1] + + gl.text.lineAfter = (text, textarea) -> + text.substring(textarea.selectionEnd).trim().split('\n')[0] + + gl.text.blockTagText = (text, textArea, blockTag, selected) -> + lineBefore = @lineBefore(text, textArea) + lineAfter = @lineAfter(text, textArea) + + if lineBefore is blockTag and lineAfter is blockTag + # To remove the block tag we have to select the line before & after + if blockTag? + textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1) + textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1) + + selected + else + "#{blockTag}\n#{selected}\n#{blockTag}" + + gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) -> selectedSplit = selected.split('\n') startChar = if not wrap and textArea.selectionStart > 0 then '\n' else '' - if selectedSplit.length > 1 and not wrap - insertText = selectedSplit.map((val) -> - if val.indexOf(tag) is 0 - "#{val.replace(tag, '')}" - else - "#{tag}#{val}" - ).join('\n') + if selectedSplit.length > 1 and (not wrap or blockTag?) + if blockTag? + insertText = @blockTagText(text, textArea, blockTag, selected) + else + insertText = selectedSplit.map((val) -> + if val.indexOf(tag) is 0 + "#{val.replace(tag, '')}" + else + "#{tag}#{val}" + ).join('\n') else insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}" @@ -51,7 +75,7 @@ textArea.setSelectionRange pos, pos - gl.text.updateText = (textArea, tag, wrap) -> + gl.text.updateText = (textArea, tag, blockTag, wrap) -> $textArea = $(textArea) oldVal = $textArea.val() textArea = $textArea.get(0) @@ -59,7 +83,7 @@ selected = @selectedText(text, textArea) $textArea.focus() - @insertText(textArea, text, tag, selected, wrap) + @insertText(textArea, text, tag, blockTag, selected, wrap) gl.text.init = (form) -> self = @ @@ -70,6 +94,7 @@ self.updateText( $this.closest('.md-area').find('textarea'), $this.data('md-tag'), + $this.data('md-block'), not $this.data('md-prepend') ) diff --git a/app/assets/javascripts/lib/type_utility.js.coffee b/app/assets/javascripts/lib/utils/type_utility.js.coffee index 957f0d86b36..957f0d86b36 100644 --- a/app/assets/javascripts/lib/type_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/type_utility.js.coffee diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/utils/url_utility.js.coffee index e8085e1c2e4..e8085e1c2e4 100644 --- a/app/assets/javascripts/lib/url_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/url_utility.js.coffee diff --git a/app/assets/javascripts/lib/utf8_encode.js b/app/assets/javascripts/lib/utils/utf8_encode.js index 39ffe44dae0..39ffe44dae0 100644 --- a/app/assets/javascripts/lib/utf8_encode.js +++ b/app/assets/javascripts/lib/utils/utf8_encode.js diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index 0037a3a21c2..a19e68b39e2 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -4,18 +4,10 @@ class @Milestone type: "PUT" url: issue_url data: data - success: (data) -> - if data.saved == true - if data.assignee_avatar_url - img_tag = $('<img/>') - img_tag.attr('src', data.assignee_avatar_url) - img_tag.addClass('avatar s16') - $(li).find('.assignee-icon').html(img_tag) - else - $(li).find('.assignee-icon').html('') - $(li).effect 'highlight' - else - new Flash("Issue update failed", 'alert') + success: (_data) => + @successCallback(_data, li) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" @sortIssues: (data) -> @@ -25,9 +17,10 @@ class @Milestone type: "PUT" url: sort_issues_url data: data - success: (data) -> - if data.saved != true - new Flash("Issues update failed", 'alert') + success: (_data) => + @successCallback(_data) + error: -> + new Flash("Issues update failed", 'alert') dataType: "json" @sortMergeRequests: (data) -> @@ -37,9 +30,10 @@ class @Milestone type: "PUT" url: sort_mr_url data: data - success: (data) -> - if data.saved != true - new Flash("MR update failed", 'alert') + success: (_data) => + @successCallback(_data) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" @updateMergeRequest: (li, merge_request_url, data) -> @@ -47,20 +41,23 @@ class @Milestone type: "PUT" url: merge_request_url data: data - success: (data) -> - if data.saved == true - if data.assignee_avatar_url - img_tag = $('<img/>') - img_tag.attr('src', data.assignee_avatar_url) - img_tag.addClass('avatar s16') - $(li).find('.assignee-icon').html(img_tag) - else - $(li).find('.assignee-icon').html('') - $(li).effect 'highlight' - else - new Flash("Issue update failed", 'alert') + success: (_data) => + @successCallback(_data, li) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" + @successCallback: (data, element) => + if data.assignee + img_tag = $('<img/>') + img_tag.attr('src', data.assignee.avatar_url) + img_tag.addClass('avatar s16') + $(element).find('.assignee-icon').html(img_tag) + else + $(element).find('.assignee-icon').html('') + + $(element).effect 'highlight' + constructor: -> oldMouseStart = $.ui.sortable.prototype._mouseStart $.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) -> @@ -81,8 +78,10 @@ class @Milestone stop: (event, ui) -> $(".issues-sortable-list").css "min-height", "0px" update: (event, ui) -> - data = $(this).sortable("serialize") - Milestone.sortIssues(data) + # Prevents sorting from container which element has been removed. + if $(this).find(ui.item).length > 0 + data = $(this).sortable("serialize") + Milestone.sortIssues(data) receive: (event, ui) -> new_state = $(this).data('state') diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/application.js.coffee index cb9eead855b..f75f63869c5 100644 --- a/app/assets/javascripts/network/application.js.coffee +++ b/app/assets/javascripts/network/application.js.coffee @@ -4,9 +4,6 @@ # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # the compiled file. # -#= require raphael -#= require g.raphael -#= require g.bar #= require_tree . $ -> diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 96e10dd7e8a..3288c801388 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -70,17 +70,20 @@ class @Project fieldName: 'ref' renderRow: (ref) -> if ref.header? - "<li class='dropdown-header'>#{ref.header}</li>" + $('<li />') + .addClass('dropdown-header') + .text(ref.header) else - isActiveClass = if ref is selected then 'is-active' else '' - - "<li> - <a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'> - #{ref} - </a> - </li>" + link = $('<a />') + .attr('href', '#') + .addClass(if ref is selected then 'is-active' else '') + .text(ref) + .attr('data-ref', escape(ref)) + + $('<li />') + .append(link) id: (obj, $el) -> - $el.data('ref') + $el.attr('data-ref') toggleLabel: (obj, $el) -> $el.text().trim() clicked: (e) -> diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 421328554b8..72b1d3dfb1e 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -171,22 +171,15 @@ class @SearchAutocomplete } bindEvents: -> - $(document).on 'click', @onDocumentClick @searchInput.on 'keydown', @onSearchInputKeyDown @searchInput.on 'keyup', @onSearchInputKeyUp @searchInput.on 'click', @onSearchInputClick @searchInput.on 'focus', @onSearchInputFocus + @searchInput.on 'blur', @onSearchInputBlur @clearInput.on 'click', @onClearInputClick @locationBadgeEl.on 'click', => @searchInput.focus() - onDocumentClick: (e) => - # If clicking outside the search box - # And search input is not focused - # And we are not clicking inside a suggestion - if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length - @onSearchInputBlur() - enableAutocomplete: -> # No need to enable anything if user is not logged in return if !gon.current_user_id @@ -287,8 +280,6 @@ class @SearchAutocomplete value: @originalState._location ) - @dropdown.removeClass 'open' - badgePresent: -> @locationBadgeEl.length diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/application.js.coffee index 647ffbf5f45..91cacfece46 100644 --- a/app/assets/javascripts/users/application.js.coffee +++ b/app/assets/javascripts/users/application.js.coffee @@ -1,8 +1,2 @@ -# This is a manifest file that'll be compiled into including all the files listed below. -# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically -# be included in the compiled file accessible from http://example.com/assets/application.js -# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -# the compiled file. # -#= require d3 #= require_tree . diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 38023818709..41e77a4ac68 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -137,7 +137,7 @@ margin: 0; font-size: 24px; font-weight: normal; - margin-bottom: 5px; + margin-bottom: 10px; color: #4c4e54; font-size: 23px; line-height: 1.1; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index a7bcb456560..c32ce5195c6 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -26,7 +26,6 @@ header { text-align: center; #tanuki-logo, img { - width: 36px; height: 36px; } } @@ -132,6 +131,10 @@ header { transition-duration: .3s; z-index: 999; + svg, img { + height: 36px; + } + &:hover { cursor: pointer; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index f53e63606de..6e5f216c894 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -21,9 +21,8 @@ .fa { position: relative; - top: 3px; - font-size: 13px; - color: $btn-placeholder-gray; + top: 5px; + font-size: 18px; } } @@ -32,6 +31,7 @@ overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; + &::-webkit-scrollbar { display: none; } @@ -303,41 +303,9 @@ } .nav-links { - @include scrolling-links(); border-bottom: none; height: 51px; - svg { - position: relative; - top: 2px; - margin-right: 2px; - height: 15px; - width: auto; - - path, - polygon { - fill: $layout-link-gray; - } - } - - .fade-right { - @include fade(left, rgba(250, 250, 250, 0.4), $background-color); - right: 0; - - .fa { - right: -7px; - } - } - - .fade-left { - @include fade(right, rgba(250, 250, 250, 0.4), $background-color); - left: 0; - - .fa { - left: -7px; - } - } - li { a { @@ -373,16 +341,6 @@ } } } - - .nav-control { - - .fade-right { - - @media (max-width: $screen-xs-min) { - right: 0; - } - } - } } .scrolling-tabs-container { @@ -390,15 +348,42 @@ .nav-links { @include scrolling-links(); + } + + .fade-right { + @include fade(left, rgba(255, 255, 255, 0.4), $background-color); + right: -5px; + + .fa { + right: -7px; + } + } + + .fade-left { + @include fade(right, rgba(255, 255, 255, 0.4), $background-color); + left: -5px; + + .fa { + left: -7px; + } + } + + &.sub-nav-scroll { .fade-right { - @include fade(left, rgba(255, 255, 255, 0.4), $background-color); right: 0; + + .fa { + right: -23px; + } } .fade-left { - @include fade(right, rgba(255, 255, 255, 0.4), $background-color); left: 0; + + .fa { + left: 10px; + } } } } @@ -411,21 +396,19 @@ .fade-right { @include fade(left, rgba(255, 255, 255, 0.4), $white-light); - right: 0; + right: -5px; + + .fa { + right: -7px; + } } .fade-left { @include fade(right, rgba(255, 255, 255, 0.4), $white-light); - left: 0; - } - - &.event-filter { - .fade-right { - visibility: hidden; + left: -5px; - @media (max-width: $screen-xs-max) { - visibility: visible; - } + .fa { + left: -7px; } } } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 98f917ce69b..e8d6a7f2775 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,5 +1,6 @@ .page-with-sidebar { padding-top: $header-height; + padding-bottom: 25px; transition: padding $sidebar-transition-duration; .sidebar-wrapper { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index c37574ca7a1..87f8a17659f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 258px; $sidebar-transition-duration: .15s; -$sidebar-breakpoint: 1440px; +$sidebar-breakpoint: 1280px; /* * UI elements diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 6211f3a52eb..5faedfedd66 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -8,8 +8,9 @@ .emoji-menu { position: absolute; margin-top: 3px; - z-index: 1000; - min-width: 160px; + padding: $gl-padding; + z-index: 9; + width: 300px; font-size: 14px; background-color: $award-emoji-menu-bg; border: 1px solid $award-emoji-menu-border; @@ -33,20 +34,18 @@ } .emoji-menu-content { - padding: $gl-padding; - width: 300px; height: 300px; overflow-y: scroll; - - input.emoji-search { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC"); - background-repeat: no-repeat; - background-position: right 5px center; - background-size: 16px; - } } } +.emoji-search { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC"); + background-repeat: no-repeat; + background-position: right 5px center; + background-size: 16px; +} + .emoji-menu-list { list-style: none; padding-left: 0; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index de534d28421..85bbf70e188 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -83,11 +83,7 @@ position: relative; @media (min-width: $screen-sm-min) { - padding-left: 20px; - - .commit-info-block { - padding-left: 44px; - } + padding-left: 46px; } &:not(:last-child) { @@ -102,9 +98,7 @@ .avatar { - position: absolute; - top: 10px; - left: 16px; + margin-left: -46px; } .item-title { diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index ac7721cbe15..101faf59174 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -48,11 +48,7 @@ .access-request-button { @include btn-gray; - position: absolute; - right: 16px; - bottom: 32px; - padding: 3px 10px; + margin-right: 10px; text-transform: none; - background-color: $background-color; } } diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 0b710ef168b..00ab42bec5c 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -63,5 +63,6 @@ border: 1px solid $table-border-gray; padding: 5px; margin: 5px; + max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 21ff6ab71f0..542fa244689 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -10,6 +10,7 @@ border: 1px solid $table-border-gray; padding: 5px; margin: 5px; + max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index f5f67e2cd84..47bfd144930 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -6,6 +6,7 @@ height: 30px; display: inline-block; margin-right: 10px; + margin-bottom: 10px; } &.suggest-colors-dropdown { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index aca82f7f7bf..124f4afaa0d 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -264,8 +264,15 @@ margin-bottom: 4px; } + .item-title { + @media (min-width: $screen-sm-min) { + width: 49%; + } + } + .avatar { - margin-left: 0; + left: 0; + top: 2px; } .commit-row-info { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index ffba3dc5bc6..ac8c02b59dc 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -41,6 +41,10 @@ ul.notes { .timeline-icon { .avatar { visibility: hidden; + + .discussion-body & { + visibility: visible; + } } } } @@ -113,6 +117,7 @@ ul.notes { border: 1px solid $table-border-gray; padding: 5px; margin: 5px 0; + max-height: calc(100vh - 100px); } } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d3e59d7fdb9..89ce1b2df20 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,10 +13,53 @@ .new_project, .edit-project { - fieldset.features { - .control-label { + fieldset { + &.features .control-label { font-weight: normal; } + .form-group { + margin-bottom: 5px; + } + &> .form-group { + padding-left: 0; + } + } + .help-block { + margin-bottom: 10px; + } + .project-path { + padding-right: 0; + .form-control { + border-radius: $border-radius-base; + } + } + .input-group > div { + &:last-child { + padding-right: 0; + } + } + @media (max-width: $screen-xs-max) { + .input-group > div { + margin-bottom: 14px; + &:last-child { + margin-bottom: 0; + } + } + fieldset > .form-group:first-child { + padding-right: 0; + } + } + + .input-group-addon { + &.static-namespace { + height: 35px; + border-radius: 3px; + border: 1px solid #e5e5e5; + } + &+ .select2 a { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } } } @@ -365,10 +408,28 @@ a.deploy-project-label { } } -.project-import .btn { - float: left; - margin-bottom: 10px; - margin-right: 10px; +.project-import { + .form-group { + margin-bottom: 0; + } + .import-buttons { + padding-left: 0; + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + .btn { + margin-right: 10px; + padding: 8px 12px; + } + &> div { + margin-bottom: 14px; + padding-left: 0; + &:last-child { + margin-bottom: 0; + } + } + } } .project-stats { diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 99c9e81ddb9..5b61270daa8 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -101,7 +101,8 @@ margin: 0; .commit { - padding: 0 0 0 55px; + padding-top: 0; + padding-bottom: 0; .commit-row-title { .commit-row-message { diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb new file mode 100644 index 00000000000..3c67370b667 --- /dev/null +++ b/app/controllers/admin/system_info_controller.rb @@ -0,0 +1,13 @@ +class Admin::SystemInfoController < Admin::ApplicationController + def show + system_info = Vmstat.snapshot + + @cpus = system_info.cpus.length + + @mem_used = system_info.memory.active_bytes + @mem_total = system_info.memory.total_bytes + + @disk_used = system_info.disks[0].used_bytes + @disk_total = system_info.disks[0].total_bytes + end +end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 8bf71a1adbb..aa894fde36b 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -25,7 +25,7 @@ module Ci return render_404 unless @project image = Ci::ImageForBuildService.new.execute(@project, params) - send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" + send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml" end protected diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index 71ba6153021..de6bc689bb7 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -1,5 +1,5 @@ class Dashboard::GroupsController < Dashboard::ApplicationController def index - @group_members = current_user.group_members.page(params[:page]) + @group_members = current_user.group_members.includes(:source).page(params[:page]) end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index ee4fcc4e360..a04bf7df722 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -37,15 +37,12 @@ class GroupsController < Groups::ApplicationController end def show - @last_push = current_user.recent_push if current_user - - @projects = @projects.includes(:namespace) - @projects = @projects.sorted_by_activity - @projects = filter_projects(@projects) - @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]) if params[:filter_projects].blank? + if current_user + @last_push = current_user.recent_push + @notification_setting = current_user.notification_settings_for(group) + end - @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) + setup_projects respond_to do |format| format.html @@ -97,6 +94,16 @@ class GroupsController < Groups::ApplicationController protected + def setup_projects + @projects = @projects.includes(:namespace) + @projects = @projects.sorted_by_activity + @projects = filter_projects(@projects) + @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]) if params[:filter_projects].blank? + + @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) + end + def authorize_create_group! unless can?(current_user, :create_group, nil) return render_404 diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index f99aa490d3e..513348c39af 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -12,9 +12,13 @@ class Import::GitlabProjectsController < Import::BaseController return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." }) end + imported_file = project_params[:file].path + "-import" + + FileUtils.copy_entry(project_params[:file].path, imported_file) + @project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id], current_user, - File.expand_path(project_params[:file].path), + File.expand_path(imported_file), project_params[:path]).execute if @project.saved? diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb index eddd03cc229..8ec4bb1233f 100644 --- a/app/controllers/notification_settings_controller.rb +++ b/app/controllers/notification_settings_controller.rb @@ -2,11 +2,9 @@ class NotificationSettingsController < ApplicationController before_action :authenticate_user! def create - project = Project.find(params[:project][:id]) + return render_404 unless can_read?(resource) - return render_404 unless can?(current_user, :read_project, project) - - @notification_setting = current_user.notification_settings_for(project) + @notification_setting = current_user.notification_settings_for(resource) @saved = @notification_setting.update_attributes(notification_setting_params) render_response @@ -21,6 +19,22 @@ class NotificationSettingsController < ApplicationController private + def resource + @resource ||= + if params[:project_id].present? + Project.find(params[:project_id]) + elsif params[:namespace_id].present? + Group.find(params[:namespace_id]) + end + end + + def can_read?(resource) + ability_name = resource.class.name.downcase + ability_name = "read_#{ability_name}".to_sym + + can?(current_user, ability_name, resource) + end + def render_response render json: { html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting), diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index cd8b2911674..7599fec3cdf 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -16,6 +16,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :from_merge_request, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update] before_action :editor_variables, except: [:show, :preview, :diff] + before_action :validate_diff_params, only: :diff def new commit unless @repository.empty? @@ -146,4 +147,10 @@ class Projects::BlobController < Projects::ApplicationController file_content_encoding: params[:encoding] } end + + def validate_diff_params + if [:since, :to, :offset].any? { |key| params[key].blank? } + render nothing: true + end + end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 6751737d15e..d162a5a3165 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController apply_diff_view_cookie! @grouped_diff_notes = commit.notes.grouped_diff_notes + @notes = commit.notes.non_diff_notes.fresh + + Banzai::NoteRenderer.render( + @grouped_diff_notes.values.flatten + @notes, + @project, + current_user, + ) @note = @project.build_commit_note(commit) - @notes = commit.notes.non_diff_notes.fresh + @noteable = @commit @comments_target = { noteable_type: 'Commit', diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4e2d3bebb2e..8b8df680739 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -62,8 +62,12 @@ class Projects::IssuesController < Projects::ApplicationController end def show + raw_notes = @issue.notes_with_associations.fresh + + @notes = Banzai::NoteRenderer. + render(raw_notes, @project, current_user, @path, @project_wiki, @ref) + @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.with_associations.fresh @noteable = @issue respond_to do |format| @@ -111,6 +115,7 @@ class Projects::IssuesController < Projects::ApplicationController render :edit end end + format.json do render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 851822d805a..dd86b940a08 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -59,7 +59,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController respond_to do |format| format.html format.json { render json: @merge_request } - format.patch { render text: @merge_request.to_patch } + format.patch do + headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository, + @merge_request.diff_base_commit.id, + @merge_request.last_commit.id)) + headers['Content-Disposition'] = 'inline' + head :ok + end format.diff do return render_404 unless @merge_request.diff_refs @@ -85,6 +91,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController @grouped_diff_notes = @merge_request.notes.grouped_diff_notes + Banzai::NoteRenderer.render( + @grouped_diff_notes.values.flatten, + @project, + current_user, + @path, + @project_wiki, + @ref + ) + respond_to do |format| format.html format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } @@ -190,7 +205,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController def merge return access_denied! unless @merge_request.can_be_merged_by?(current_user) - unless @merge_request.mergeable? + # Disable the CI check if merge_when_build_succeeds is enabled since we have + # to wait until CI completes to know + unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?) @status = :failed return end @@ -204,8 +221,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.update(merge_error: nil) - if params[:merge_when_build_succeeds].present? - if @merge_request.pipeline && @merge_request.pipeline.active? + if params[:merge_when_build_succeeds].present? + unless @merge_request.pipeline + @status = :failed + return + end + + if @merge_request.pipeline.active? MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) .execute(@merge_request) @status = :merge_when_build_succeeds @@ -320,8 +342,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController def define_show_vars # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.mr_and_commit_notes.inc_author.fresh - @discussions = @notes.discussions + + @discussions = @merge_request.mr_and_commit_notes. + inc_author_project_award_emoji. + fresh. + discussions + + @notes = Banzai::NoteRenderer.render( + @discussions.flatten, + @project, + current_user, + @path, + @project_wiki, + @ref + ) + @noteable = @merge_request # Get commits from repository @@ -368,4 +403,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController def ensure_ref_fetched @merge_request.ensure_ref_fetched end + + def merge_when_build_succeeds_active? + params[:merge_when_build_succeeds].present? && + @merge_request.pipeline && @merge_request.pipeline.active? + end end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 836f79ff080..e14fe26dde7 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -24,6 +24,10 @@ class Projects::NotesController < Projects::ApplicationController def create @note = Notes::CreateService.new(project, current_user, note_params).execute + if @note.is_a?(Note) + Banzai::NoteRenderer.render([@note], @project, current_user) + end + respond_to do |format| format.json { render json: note_json(@note) } format.html { redirect_back_or_default } @@ -33,6 +37,10 @@ class Projects::NotesController < Projects::ApplicationController def update @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) + if @note.is_a?(Note) + Banzai::NoteRenderer.render([@note], @project, current_user) + end + respond_to do |format| format.json { render json: note_json(@note) } format.html { redirect_back_or_default } @@ -118,6 +126,8 @@ class Projects::NotesController < Projects::ApplicationController name: note.name } elsif note.valid? + Banzai::NoteRenderer.render([note], @project, current_user) + { valid: true, id: note.id, diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb index c19a795d467..641fbf838f1 100644 --- a/app/finders/pipelines_finder.rb +++ b/app/finders/pipelines_finder.rb @@ -29,10 +29,10 @@ class PipelinesFinder end def branches - project.repository.branches.map(&:name) + project.repository.branch_names end def tags - project.repository.tags.map(&:name) + project.repository.tag_names end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 41859841834..62d13a4b4f3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -197,7 +197,7 @@ module ApplicationHelper def render_markup(file_name, file_content) if gitlab_markdown?(file_name) - Haml::Helpers.preserve(markdown(file_content)) + Hamlit::RailsHelpers.preserve(markdown(file_content)) elsif asciidoc?(file_name) asciidoc(file_content) elsif plain?(file_name) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4b4bc3d4276..428a42266d0 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,10 +1,10 @@ module BlobHelper - def highlighter(blob_name, blob_content, nowrap: false) - Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) + def highlighter(blob_name, blob_content, repository: nil, nowrap: false) + Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) end - def highlight(blob_name, blob_content, nowrap: false, plain: false) - Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) + def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false) + Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository) end def no_highlight_files diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 6b617e1730a..7c140538012 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -69,7 +69,7 @@ module DropdownsHelper def dropdown_filter(placeholder, search_id: nil) content_tag :div, class: "dropdown-input" do - filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder + filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off' filter_output << icon('search', class: "dropdown-input-search") filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb index 91dd91718dc..0e456214d37 100644 --- a/app/helpers/javascript_helper.rb +++ b/app/helpers/javascript_helper.rb @@ -1,7 +1,5 @@ module JavascriptHelper - def page_specific_javascripts(js = nil) - @page_specific_javascripts = js unless js.nil? - - @page_specific_javascripts + def page_specific_javascript_tag(js) + javascript_include_tag asset_path(js), { "data-turbolinks-track" => true } end end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 5074e645769..5e9f5837101 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -34,10 +34,7 @@ module LabelsHelper # Returns a String def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block) project ||= @project || label.project - link = send("namespace_project_#{type.to_s.pluralize}_path", - project.namespace, - project, - label_name: [label.name]) + link = label_filter_path(project, label, type: type) if block_given? link_to link, class: css_class, &block @@ -46,6 +43,13 @@ module LabelsHelper end end + def label_filter_path(project, label, type: issue) + send("namespace_project_#{type.to_s.pluralize}_path", + project.namespace, + project, + label_name: [label.name]) + end + def project_label_names @project.labels.pluck(:title) end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 77783cd7640..7e8369d0a05 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -72,6 +72,6 @@ module NotificationsHelper # Create hidden field to send notification setting source to controller def hidden_setting_source_input(notification_setting) return unless notification_setting.source_type - hidden_field_tag "#{notification_setting.source_type.downcase}[id]", notification_setting.source_id + hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index e4e8b934bc8..22387d66451 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -52,7 +52,7 @@ module PageLayoutHelper raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2 @page_card_attributes ||= {} - @page_card_attributes = map.reject { |_,v| v.blank? } if map.present? + @page_card_attributes = map.reject { |_, v| v.blank? } if map.present? @page_card_attributes end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d91e3332e48..26796b08fac 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -15,7 +15,7 @@ module ProjectsHelper def link_to_member_avatar(author, opts = {}) default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } opts = default_opts.merge(opts) - image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar] end def link_to_member(project, author, opts = {}, &block) @@ -27,7 +27,7 @@ module ProjectsHelper author_html = "" # Build avatar image tag - author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar] # Build name span tag if opts[:by_username] diff --git a/app/models/ability.rb b/app/models/ability.rb index 9c58b956007..f5950879ccb 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -196,7 +196,8 @@ class Ability @public_project_rules ||= project_guest_rules + [ :download_code, :fork_project, - :read_commit_status + :read_commit_status, + :read_pipeline ] end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index ca5a685dd11..10324bf2257 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -163,13 +163,26 @@ module Ci end def skip_ci? - git_commit_message =~ /(\[ci skip\])/ if git_commit_message + git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message end def environments builds.where.not(environment: nil).success.pluck(:environment).uniq end + # Manually set the notes for a Ci::Pipeline + # There is no ActiveRecord relation between Ci::Pipeline and notes + # as they are related to a commit sha. This method helps importing + # them using the +Gitlab::ImportExport::RelationFactory+ class. + def notes=(notes) + notes.each do |note| + note[:id] = nil + note[:commit_id] = sha + note[:noteable_id] = self['id'] + note.save! + end + end + def notes Note.for_commit_id(sha) end diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index f8d5d4486fd..c9c47ec7419 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -13,6 +13,7 @@ module Ci attr_encrypted :value, mode: :per_attribute_iv_and_salt, + insecure_mode: true, key: Gitlab::Application.secrets.db_key_base, algorithm: 'aes-256-cbc' end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index 539c7c31e30..06beff177b1 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,10 +2,11 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy if self < Participable - participant :award_emoji_with_associations + # By default we always load award_emoji user association + participant :award_emoji end end @@ -34,12 +35,9 @@ module Awardable end end - def award_emoji_with_associations - award_emoji.includes(:user) - end - def grouped_awards(with_thumbs: true) - awards = award_emoji_with_associations.group_by(&:name) + # By default we always load award_emoji user association + awards = award_emoji.group_by(&:name) if with_thumbs awards[AwardEmoji::UPVOTE_NAME] ||= [] diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 0ccd3474b81..d6f55885dd6 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -19,9 +19,14 @@ module Issuable belongs_to :milestone has_many :notes, as: :noteable, dependent: :destroy do def authors_loaded? - # We check first if we're loaded to not load unnecesarily. + # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:author).loaded? } end + + def award_emojis_loaded? + # We check first if we're loaded to not load unnecessarily. + loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? } + end end has_many :label_links, as: :target, dependent: :destroy has_many :labels, through: :label_links @@ -49,7 +54,7 @@ module Issuable scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } - scope :inc_notes_with_associations, -> { includes(notes: :author) } + scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } @@ -112,15 +117,18 @@ module Issuable end def sort(method, excluded_labels: []) - case method.to_s - when 'milestone_due_asc' then order_milestone_due_asc - when 'milestone_due_desc' then order_milestone_due_desc - when 'downvotes_desc' then order_downvotes_desc - when 'upvotes_desc' then order_upvotes_desc - when 'priority' then order_labels_priority(excluded_labels: excluded_labels) - else - order_by(method) - end + sorted = case method.to_s + when 'milestone_due_asc' then order_milestone_due_asc + when 'milestone_due_desc' then order_milestone_due_desc + when 'downvotes_desc' then order_downvotes_desc + when 'upvotes_desc' then order_upvotes_desc + when 'priority' then order_labels_priority(excluded_labels: excluded_labels) + else + order_by(method) + end + + # Break ties with the ID column for pagination + sorted.order(id: :desc) end def order_labels_priority(excluded_labels: []) @@ -257,7 +265,14 @@ module Issuable # already have their authors loaded (possibly because the scope # `inc_notes_with_associations` was used) and skip the inclusion if that's # the case. - notes.authors_loaded? ? notes : notes.includes(:author) + includes = [] + includes << :author unless notes.authors_loaded? + includes << :award_emoji unless notes.award_emojis_loaded? + if includes.any? + notes.includes(includes) + else + notes + end end def updated_tasks diff --git a/app/models/event.rb b/app/models/event.rb index 716039fb54b..d7d23c7ae6d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -315,7 +315,7 @@ class Event < ActiveRecord::Base def body? if push? - push_with_commits? + push_with_commits? || rm_ref? elsif note? true else diff --git a/app/models/group.rb b/app/models/group.rb index e66e04371b2..c70c719e338 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,7 +11,7 @@ class Group < Namespace has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members has_many :owners, - -> { where(members: { access_level: Gitlab::Access::OWNER }) }, + -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :user diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 95fd510eb3a..33d2a69ebaf 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -20,7 +20,7 @@ class LegacyDiffNote < Note end def discussion_id - @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?) + @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code) end def diff_file_hash diff --git a/app/models/member.rb b/app/models/member.rb index c74a16367db..57161397e2b 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -32,6 +32,7 @@ class Member < ActiveRecord::Base scope :request, -> { where.not(requested_at: nil) } scope :non_request, -> { where(requested_at: nil) } scope :non_pending, -> { non_request.non_invite } + scope :has_access, -> { where('access_level > 0') } scope :guests, -> { where(access_level: GUEST) } scope :reporters, -> { where(access_level: REPORTER) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 36bc98bdb1e..53d9aa588af 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -264,19 +264,19 @@ class MergeRequest < ActiveRecord::Base self.title.sub(WIP_REGEX, "") end - def mergeable? - return false unless mergeable_state? + def mergeable?(skip_ci_check: false) + return false unless mergeable_state?(skip_ci_check: skip_ci_check) check_if_can_be_merged can_be_merged? end - def mergeable_state? + def mergeable_state?(skip_ci_check: false) return false unless open? return false if work_in_progress? return false if broken? - return false unless mergeable_ci_state? + return false unless skip_ci_check || mergeable_ci_state? true end @@ -319,13 +319,6 @@ class MergeRequest < ActiveRecord::Base ) end - # Returns the commit as a series of email patches. - # - # see "git format-patch" - def to_patch - target_project.repository.format_patch(diff_base_commit.sha, source_sha) - end - def hook_attrs attrs = { source: source_project.try(:hook_attrs), diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index aca377cc600..86331a33c05 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base # Reload all commits related to current merge request from repo # and save it as array of hashes in st_commits db field def reload_commits + new_attributes = {} + commit_objects = unmerged_commits if commit_objects.present? - self.st_commits = dump_commits(commit_objects) + new_attributes[:st_commits] = dump_commits(commit_objects) end - save + update_columns_serialized(new_attributes) end # Reload diffs between branches related to current merge request from repo # and save it as array of hashes in st_diffs db field def reload_diffs + new_attributes = {} new_diffs = [] if commits.size.zero? - self.state = :empty + new_attributes[:state] = :empty else diff_collection = unmerged_diffs if diff_collection.overflow? # Set our state to 'overflow' to make the #empty? and #collected? # methods (generated by StateMachine) return false. - self.state = :overflow + new_attributes[:state] = :overflow end - self.real_size = diff_collection.real_size + new_attributes[:real_size] = diff_collection.real_size if diff_collection.any? new_diffs = dump_diffs(diff_collection) - self.state = :collected + new_attributes[:state] = :collected end end - self.st_diffs = new_diffs - - self.base_commit_sha = self.repository.merge_base(self.head, self.base) + new_attributes[:st_diffs] = new_diffs + new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base) - self.save + update_columns_serialized(new_attributes) end # Collect array of Git::Diff objects @@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base ) end end + + private + + # + # #save or #update_attributes providing changes on serialized attributes do a lot of + # serialization and deserialization calls resulting in bad performance. + # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide. + # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized + # attributes. So to keep the same behaviour as the attribute assignment we reload the instance. + # The difference is in the usage of + # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns) + # + # Ex: + # + # new_attributes[:st_commits].first.slice(:committed_date) + # => {:committed_date=>2014-02-27 11:01:38 +0200} + # YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date))) + # => {:committed_date=>2014-02-27 10:01:38 +0100} + # + def update_columns_serialized(new_attributes) + return unless new_attributes.any? + + update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone)) + reload + end end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index a2aee2f925b..345041a6ad1 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -54,7 +54,7 @@ module Network @map = {} @reserved = {} - @commits.each_with_index do |c,i| + @commits.each_with_index do |c, i| c.time = i days[i] = c.committed_date @map[c.id] = c @@ -116,7 +116,7 @@ module Network end def commits_sort_by_ref - @commits.sort do |a,b| + @commits.sort do |a, b| if include_ref?(a) -1 elsif include_ref?(b) diff --git a/app/models/note.rb b/app/models/note.rb index 8d164647550..8db500a5219 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -6,6 +6,10 @@ class Note < ActiveRecord::Base include Awardable include Importable + # Attribute containing rendered and redacted Markdown as generated by + # Banzai::ObjectRenderer. + attr_accessor :note_html + default_value_for :system, false attr_mentionable :note, pipeline: :note @@ -49,11 +53,13 @@ class Note < ActiveRecord::Base scope :fresh, ->{ order(created_at: :asc, id: :asc) } scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author, ->{ includes(:author) } + scope :inc_author_project_award_emoji, ->{ includes(:project, :author, :award_emoji) } scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') } scope :non_diff_notes, ->{ where(type: ['Note', nil]) } scope :with_associations, -> do + # FYI noteable cannot be loaded for LegacyDiffNote for commits includes(:author, :noteable, :updated_by, project: [:project_members, { group: [:group_members] }]) end diff --git a/app/models/project.rb b/app/models/project.rb index ca3bc04e2dd..73ded09c162 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,6 +81,7 @@ class Project < ActiveRecord::Base has_one :jira_service, dependent: :destroy has_one :redmine_service, dependent: :destroy has_one :custom_issue_tracker_service, dependent: :destroy + has_one :bugzilla_service, dependent: :destroy has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :external_wiki_service, dependent: :destroy @@ -163,6 +164,7 @@ class Project < ActiveRecord::Base validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validate :visibility_level_allowed_by_group validate :visibility_level_allowed_as_fork + validate :check_wiki_path_conflict add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -539,6 +541,16 @@ class Project < ActiveRecord::Base self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") end + def check_wiki_path_conflict + return if path.blank? + + path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki" + + if Project.where(namespace_id: namespace_id, path: path_to_check).exists? + errors.add(:name, 'has already been taken') + end + end + def to_param path end diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index ca8a9b4217b..331123a5a5b 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base marshal: true, encode: true, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' serialize :data, JSON diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb new file mode 100644 index 00000000000..260f6030957 --- /dev/null +++ b/app/models/project_services/bugzilla_service.rb @@ -0,0 +1,25 @@ +class BugzillaService < IssueTrackerService + + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url + + def title + if self.properties && self.properties['title'].present? + self.properties['title'] + else + 'Bugzilla' + end + end + + def description + if self.properties && self.properties['description'].present? + self.properties['description'] + else + 'Bugzilla issue tracker' + end + end + + def to_param + 'bugzilla' + end + +end diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 6b2b1daa724..8f2db46a7ba 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService ] end - def initialize_properties - self.properties = {} if properties.nil? - end end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 0ff4f4c8dd2..23e5b16221b 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -106,7 +106,7 @@ class HipchatService < Service else message << "pushed to #{ref_type} <a href=\""\ "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> " - message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " + message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/, '')}</a> " message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" push[:commits].take(MAX_COMMITS).each do |commit| diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index beda89d3963..8b3296c712f 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -124,7 +124,7 @@ class JiraService < IssueTrackerService def build_api_url_from_project_url server = URI(project_url) - default_ports = [["http",80],["https",443]].include?([server.scheme,server.port]) + default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port]) server_url = "#{server.scheme}://#{server.host}" server_url.concat(":#{server.port}") unless default_ports "#{server_url}/rest/api/#{DEFAULT_API_VERSION}" diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 73e736820af..0865b979ce0 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -137,20 +137,10 @@ class ProjectTeam def max_member_access(user_id) access = [] - project.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) if group - group.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) end if project.invited_groups.any? && project.allowed_to_share_with_group? diff --git a/app/models/repository.rb b/app/models/repository.rb index 221c87164ca..2a6a3b086c2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -130,7 +130,7 @@ class Repository end def find_tag(name) - raw_repository.tags.find { |tag| tag.name == name } + tags.find { |tag| tag.name == name } end def add_branch(user, branch_name, target) @@ -978,6 +978,10 @@ class Repository raw_repository.ls_files(actual_ref) end + def gitattribute(path, name) + raw_repository.attributes(path)[name] + end + def copy_gitattributes(ref) actual_ref = ref || root_ref begin diff --git a/app/models/service.rb b/app/models/service.rb index 40d39933ad8..d7a32c28267 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -170,6 +170,7 @@ class Service < ActiveRecord::Base bamboo buildkite builds_email + bugzilla campfire custom_issue_tracker drone_ci diff --git a/app/models/snippet.rb b/app/models/snippet.rb index f8034cb5e6b..5ec933601ac 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base length: { within: 0..255 }, format: { with: Gitlab::Regex.file_name_regex, message: Gitlab::Regex.file_name_regex_message } + validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } @@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base 0 end + # alias for compatibility with blobs and highlighting + def path + file_name + end + def name file_name end @@ -135,7 +141,16 @@ class Snippet < ActiveRecord::Base end def accessible_to(user) - where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user) + return are_public unless user.present? + return all if user.admin? + + where( + 'visibility_level IN (:visibility_levels) + OR author_id = :author_id + OR project_id IN (:project_ids)', + visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL], + author_id: user.id, + project_ids: user.authorized_projects.select(:id)) end end end diff --git a/app/models/user.rb b/app/models/user.rb index 876ccc69d8d..eac716b120b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,6 +25,7 @@ class User < ActiveRecord::Base attr_encrypted :otp_secret, key: Gitlab::Application.config.secret_key_base, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' devise :two_factor_authenticatable, @@ -57,7 +58,7 @@ class User < ActiveRecord::Base # Groups has_many :members, dependent: :destroy - has_many :group_members, dependent: :destroy, source: 'GroupMember' + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -65,7 +66,7 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, dependent: :destroy, class_name: 'ProjectMember' + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember' has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :users_star_projects, dependent: :destroy @@ -308,7 +309,7 @@ class User < ActiveRecord::Base def generate_password if self.force_random_password - self.password = self.password_confirmation = Devise.friendly_token.first(8) + self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min) end end @@ -763,7 +764,7 @@ class User < ActiveRecord::Base unless email_domains.blank? match_found = email_domains.any? do |domain| - escaped = Regexp.escape(domain).gsub('\*','.*?') + escaped = Regexp.escape(domain).gsub('\*', '.*?') regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE email_domain = Mail::Address.new(self.email).domain email_domain =~ regexp diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 540bf54b920..239bd17a035 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -159,8 +159,9 @@ class TodoService def create_todos(users, attributes) Array(users).map do |user| next if pending_todos(user, attributes).exists? - Todo.create(attributes.merge(user_id: user.id)) + todo = Todo.create(attributes.merge(user_id: user.id)) user.update_todos_count_cache + todo end end diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb index 28085b31083..046a1d641a9 100644 --- a/app/uploaders/lfs_object_uploader.rb +++ b/app/uploaders/lfs_object_uploader.rb @@ -4,7 +4,7 @@ class LfsObjectUploader < CarrierWave::Uploader::Base storage :file def store_dir - "#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}" + "#{Gitlab.config.lfs.storage_path}/#{model.oid[0, 2]}/#{model.oid[2, 2]}" end def cache_dir diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index c883e8f97da..30ab0717164 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -15,7 +15,7 @@ = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) - .form-group.group-visibility-level-holder + .form-group.project-visibility-level-holder = f.label :default_group_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) diff --git a/app/views/admin/background_jobs/_head.html.haml b/app/views/admin/background_jobs/_head.html.haml index d78682532ed..9d722bd7382 100644 --- a/app/views/admin/background_jobs/_head.html.haml +++ b/app/views/admin/background_jobs/_head.html.haml @@ -1,5 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } + = nav_link(controller: :system_info) do + = link_to admin_system_info_path, title: 'System Info' do + %span + System Info = nav_link(controller: :background_jobs) do = link_to admin_background_jobs_path, title: 'Background Jobs' do %span diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml new file mode 100644 index 00000000000..3ef2f20b589 --- /dev/null +++ b/app/views/admin/system_info/show.html.haml @@ -0,0 +1,22 @@ +- @no_container = true +- page_title "System Info" += render 'admin/background_jobs/head' + +%div{ class: (container_class) } + .prepend-top-default + .row + .col-sm-4 + .light-well + %h4 CPU + .data + %h1= "#{@cpus} cores" + .col-sm-4 + .light-well + %h4 Memory + .data + %h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}" + .col-sm-4 + .light-well + %h4 Disk + .data + %h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}" diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml index b0a709a568a..8f6d13b881a 100644 --- a/app/views/admin/users/groups.html.haml +++ b/app/views/admin/users/groups.html.haml @@ -1,11 +1,12 @@ - page_title "Groups", @user.name, "Users" = render 'admin/users/head' -- if @user.group_members.present? +- group_members = @user.group_members.includes(:source) +- if group_members.any? .panel.panel-default .panel-heading Groups: %ul.well-list - - @user.group_members.each do |group_member| + - group_members.each do |group_member| - group = group_member.group %li.group_member %span{class: ("list-item-name" unless group_member.owner?)} diff --git a/app/views/ci/errors/show.haml b/app/views/ci/errors/show.haml deleted file mode 100644 index 2788112c835..00000000000 --- a/app/views/ci/errors/show.haml +++ /dev/null @@ -1,2 +0,0 @@ -%h3.error Error -= @error diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml deleted file mode 100644 index 09e7e653521..00000000000 --- a/app/views/ci/shared/_guide.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -.bs-callout.help-callout - %h4 How to setup CI for this project - - %ol - %li - Add at least one runner to the project. - Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions. - %li - Put the .gitlab-ci.yml in the root of your repository. Examples can be found in - #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. - You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} - %li - Return to this page and refresh it, it should show a new build. diff --git a/app/views/ci/shared/_no_runners.html.haml b/app/views/ci/shared/_no_runners.html.haml deleted file mode 100644 index f56c37d9b37..00000000000 --- a/app/views/ci/shared/_no_runners.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.alert.alert-danger - %p - Now you need Runners to process your builds. - %span - Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it - - diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml index 97401a2e618..8b38b4c2bd4 100644 --- a/app/views/emojis/index.html.haml +++ b/app/views/emojis/index.html.haml @@ -1,6 +1,6 @@ .emoji-menu + = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis" .emoji-menu-content - = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control" - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| %h5.emoji-menu-title = Gitlab::AwardEmoji::CATEGORIES[category] diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index dc4ff17e31a..ea54ef226ec 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -1,3 +1,5 @@ +- project = event.project + .event-title %span.author_name= link_to_author event %span.event_label.pushed #{event.action_name} #{event.ref_type} @@ -5,19 +7,18 @@ %strong= event.ref_name - else %strong - = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title) + = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title) at - = link_to_project event.project + = link_to_project project - if event.push_with_commits? - - project = event.project .event-body %ul.well-list.event_commits - few_commits = event.commits[0...2] - few_commits.each do |commit| = render "events/commit", commit: commit, project: project, event: event - - create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project) + - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) - if event.commits_count > 1 %li.commits-stat - if event.commits_count > 2 @@ -27,18 +28,26 @@ - from = event.commit_from - from_label = truncate_sha(from) - else - - from = event.project.default_branch + - from = project.default_branch - from_label = from - = link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do + = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do Compare #{from_label}...#{truncate_sha(event.commit_to)} - if create_mr %span{"data-user-is" => event.author_id, "data-display" => "inline"} or - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do create a merge request - elsif create_mr %li.commits-stat{"data-user-is" => event.author_id} - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do Create Merge Request +- elsif event.rm_ref? + - repository = project.repository + - last_commit = repository.commit(event.commit_from) + - if last_commit + .event-body + %ul.well-list.event_commits + = render "events/commit", commit: last_commit, project: project, event: event + diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index aecefbc6e8f..a0a6762edcf 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -15,12 +15,17 @@ %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } = visibility_level_icon(@group.visibility_level, fw: false) + %span.hidden-xs + = render 'shared/notifications/button', notification_setting: @notification_setting + + - if current_user + .pull-right + = render 'shared/members/access_request_buttons', source: @group + - if @group.description.present? .cover-desc.description = markdown(@group.description, pipeline: :description) - - if current_user - = render 'shared/members/access_request_buttons', source: @group %div{ class: container_class } .top-area diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index e0ed657919e..757de92d6d4 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -30,8 +30,8 @@ = javascript_include_tag "application" - - if page_specific_javascripts - = javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true} + - if content_for?(:page_specific_javascripts) + = yield :page_specific_javascripts = csrf_meta_tags diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml deleted file mode 100644 index 24c68a6dbf5..00000000000 --- a/app/views/layouts/ci/_info.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -- if current_user && current_user.is_admin? && Ci::Runner.count.zero? - = render 'ci/shared/no_runners' diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml deleted file mode 100644 index 2e56d0ac6a3..00000000000 --- a/app/views/layouts/ci/_page.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -.page-with-sidebar{ class: page_sidebar_class } - = render "layouts/broadcast" - .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } - - - if defined?(sidebar) && sidebar - = render "layouts/ci/#{sidebar}" - - elsif current_user - = render 'layouts/nav/dashboard' - .collapse-nav - = render partial: 'layouts/collapse_button' - - if current_user - = link_to current_user, class: 'sidebar-user', title: "Profile" do - = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' - .username - = current_user.username - .content-wrapper - = render "layouts/flash" - = render 'layouts/ci/info' - %div{ class: container_class } - .content - .clearfix - = yield diff --git a/app/views/layouts/ci/notify.html.haml b/app/views/layouts/ci/notify.html.haml deleted file mode 100644 index 270b206df5e..00000000000 --- a/app/views/layouts/ci/notify.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%html{lang: "en"} - %head - %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} - %title - GitLab CI - - %body - = yield :header - - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - = yield - %br - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - %p{style: "font-size:small;color:#777"} - - if @project - You're receiving this notification because you are the one who triggered a build on the #{@project.name} project. diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 40a2c81eebd..1a39572ac3c 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -6,7 +6,7 @@ = icon('bars') %button.navbar-toggle{type: 'button'} %span.sr-only Toggle navigation - = icon('angle-left') + = icon('ellipsis-v') .navbar-collapse.collapse %ul.nav.navbar-nav diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 66e5ec1ad1a..5ee8772882e 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -1,15 +1,16 @@ -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/admin_settings' - + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do %span Overview - = nav_link(controller: %w(background_jobs logs health_check)) do - = link_to admin_background_jobs_path, title: 'Monitoring' do + = nav_link(controller: %w(system_info background_jobs logs health_check)) do + = link_to admin_system_info_path, title: 'Monitoring' do %span Monitoring = nav_link(controller: :broadcast_messages) do @@ -37,5 +38,3 @@ = link_to admin_spam_logs_path, title: "Spam Logs" do %span Spam Logs - %li.fade-right - = icon('arrow-right') diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index f7aa9fab7cf..d7d36c84b6c 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,9 +1,10 @@ -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/group_settings' - + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(path: 'groups#show', html_options: {class: 'home'}) do = link_to group_path(@group), title: 'Home' do %span @@ -32,5 +33,3 @@ = link_to group_group_members_path(@group), title: 'Members' do %span Members - %li.fade-right - = icon('arrow-right') diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 44ea939b7e4..96fe62c39c3 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,48 +1,49 @@ -%ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') - = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: 'Profile Settings' do - %span - Profile - = nav_link(controller: [:accounts, :two_factor_auths]) do - = link_to profile_account_path, title: 'Account' do - %span - Account - - if current_application_settings.user_oauth_applications? - = nav_link(controller: 'oauth/applications') do - = link_to applications_profile_path, title: 'Applications' do - %span - Applications - = nav_link(controller: :personal_access_tokens) do - = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do - %span - Personal Access Tokens - = nav_link(controller: :emails) do - = link_to profile_emails_path, title: 'Emails' do - %span - Emails - - unless current_user.ldap_user? - = nav_link(controller: :passwords) do - = link_to edit_profile_password_path, title: 'Password' do - %span - Password - = nav_link(controller: :notifications) do - = link_to profile_notifications_path, title: 'Notifications' do - %span - Notifications +.scrolling-tabs-container + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') + %ul.nav-links.scrolling-tabs + = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do + = link_to profile_path, title: 'Profile Settings' do + %span + Profile + = nav_link(controller: [:accounts, :two_factor_auths]) do + = link_to profile_account_path, title: 'Account' do + %span + Account + - if current_application_settings.user_oauth_applications? + = nav_link(controller: 'oauth/applications') do + = link_to applications_profile_path, title: 'Applications' do + %span + Applications + = nav_link(controller: :personal_access_tokens) do + = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do + %span + Personal Access Tokens + = nav_link(controller: :emails) do + = link_to profile_emails_path, title: 'Emails' do + %span + Emails + - unless current_user.ldap_user? + = nav_link(controller: :passwords) do + = link_to edit_profile_password_path, title: 'Password' do + %span + Password + = nav_link(controller: :notifications) do + = link_to profile_notifications_path, title: 'Notifications' do + %span + Notifications - = nav_link(controller: :keys) do - = link_to profile_keys_path, title: 'SSH Keys' do - %span - SSH Keys - = nav_link(controller: :preferences) do - = link_to profile_preferences_path, title: 'Preferences' do - %span - Preferences - = nav_link(path: 'profiles#audit_log') do - = link_to audit_log_profile_path, title: 'Audit Log' do - %span - Audit Log - %li.fade-right - = icon('arrow-right') + = nav_link(controller: :keys) do + = link_to profile_keys_path, title: 'SSH Keys' do + %span + SSH Keys + = nav_link(controller: :preferences) do + = link_to profile_preferences_path, title: 'Preferences' do + %span + Preferences + = nav_link(path: 'profiles#audit_log') do + = link_to audit_log_profile_path, title: 'Audit Log' do + %span + Audit Log diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 27e840df503..dcef427cda3 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -24,10 +24,12 @@ data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do Leave Project -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(path: 'projects#show', html_options: {class: 'home'}) do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do %span @@ -111,5 +113,3 @@ %li.hidden = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do Commits - %li.fade-right - = icon('arrow-right') diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index ca6714ef42b..58d961d93ca 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -12,13 +12,13 @@ %li.confidential-issue-warning = icon('warning') %span This is a confidential issue. Your comment will not be visible to the public. - + %li.pull-right .toolbar-group = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" }) = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" }) = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) - = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" }) + = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" }) = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" }) = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" }) = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index da522b53417..771a2e0df7d 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -8,4 +8,4 @@ %strong Only allow merge requests to be merged if the build succeeds .help-block Builds need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds') + = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml index b1769759dce..58524418a67 100644 --- a/app/views/projects/blob/_text.html.haml +++ b/app/views/projects/blob/_text.html.haml @@ -16,4 +16,4 @@ .file-content.code .nothing-here-block Empty file - else - = render 'shared/file_highlight', blob: blob + = render 'shared/file_highlight', blob: blob, repository: @repository diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 54dab4bff07..61152649907 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -1,8 +1,10 @@ -.scrolling-tabs-container +.scrolling-tabs-container.sub-nav-scroll + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') .nav-links.sub-nav.scrolling-tabs %ul{ class: (container_class) } - %li.fade-left - = icon('arrow-left') = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = link_to project_files_path(@project) do Files @@ -26,5 +28,3 @@ = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags - %li.fade-right - = icon('arrow-right') diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index a388d9a0a61..ca347406dfe 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -1,7 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } - - page_specific_javascripts asset_path("graphs/application.js") + - content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/chart.js') + = page_specific_javascript_tag('graphs/application.js') = nav_link(action: :show) do = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 593af319a47..3ca30b4ba6b 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,5 +1,7 @@ - page_title "Network", @ref -- page_specific_javascripts asset_path("network/application.js") +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/raphael.js') + = page_specific_javascript_tag('network/application.js') = render "projects/commits/head" = render "head" %div{ class: (container_class) } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 3c1c6060504..8a73b077357 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,110 +1,121 @@ - page_title 'New Project' - header_title "Projects", dashboard_projects_path -%h3.page-title - New Project -%hr - .project-edit-container .project-edit-errors = render 'projects/errors' - .project-edit-content - - = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f| - .form-group - = f.label :path, class: 'control-label' do - Project owner - .col-sm-10 - = f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1} - - - if current_user.can_create_group? - .help-block - Want to house several dependent projects under the same namespace? - = link_to "Create a group", new_group_path - - .form-group - = f.label :path, class: 'control-label' do - Project name - .col-sm-10 - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - - - if import_sources_enabled? - .project-import.js-toggle-container - .form-group - %label.control-label Import project from - .col-sm-10 - - if github_import_enabled? - - if github_import_configured? - = link_to status_import_github_path, class: 'btn import_github' do - %i.fa.fa-github - GitHub - - else - = link_to '#', class: 'how_to_import_link btn import_github' do - %i.fa.fa-github - GitHub - = render 'github_import_modal' - - - if bitbucket_import_enabled? - - if bitbucket_import_configured? - = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - - else - = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - = render 'bitbucket_import_modal' - - - if gitlab_import_enabled? - - if gitlab_import_configured? - = link_to status_import_gitlab_path, class: 'btn import_gitlab' do - %i.fa.fa-heart - GitLab.com + .row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + New project + %p + Create or Import your project from popular Git services + .col-lg-9 + = form_for @project, html: { class: 'new_project' } do |f| + %fieldset.append-bottom-0 + .form-group.col-xs-12.col-sm-6 + = f.label :namespace_id, class: 'label-light' do + %span + Project path + .form-group + .input-group + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1} - else - = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do - %i.fa.fa-heart - GitLab.com - = render 'gitlab_import_modal' - - - if gitorious_import_enabled? - = link_to new_import_gitorious_path, class: 'btn import_gitorious' do - %i.icon-gitorious.icon-gitorious-small - Gitorious.org - - - if google_code_import_enabled? - = link_to new_import_google_code_path, class: 'btn import_google_code' do - %i.fa.fa-google - Google Code - - - if fogbugz_import_enabled? - = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do - %i.fa.fa-bug - Fogbugz - - - if git_import_enabled? - = link_to "#", class: 'btn js-toggle-button import_git' do - %i.fa.fa-git - %span Repo by URL - - - if gitlab_project_import_enabled? - = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do - %i.fa.fa-gitlab - %span GitLab export - - .js-toggle-content.hide - = render "shared/import_form", f: f - - .prepend-botton-10 - - .form-group - = f.label :description, class: 'control-label' do - Description - %span.light (optional) - .col-sm-10 - = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3 - = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project + .input-group-addon.static-namespace + #{root_url}#{current_user.username}/ + .form-group.col-xs-12.col-sm-6.project-path + = f.label :namespace_id, class: 'label-light' do + %span + Project name + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + + - if import_sources_enabled? + .project-import.js-toggle-container + .form-group.clearfix + = f.label :visibility_level, class: 'label-light' do + Import project from + .col-sm-12.import-buttons + %div + - if github_import_enabled? + - if github_import_configured? + = link_to status_import_github_path, class: 'btn import_github' do + %i.fa.fa-github + GitHub + - else + = link_to '#', class: 'how_to_import_link btn import_github' do + %i.fa.fa-github + GitHub + = render 'github_import_modal' + %div + - if bitbucket_import_enabled? + - if bitbucket_import_configured? + = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + - else + = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + = render 'bitbucket_import_modal' + %div + - if gitlab_import_enabled? + - if gitlab_import_configured? + = link_to status_import_gitlab_path, class: 'btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + - else + = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + = render 'gitlab_import_modal' + %div + - if gitorious_import_enabled? + = link_to new_import_gitorious_path, class: 'btn import_gitorious' do + %i.icon-gitorious.icon-gitorious-small + Gitorious.org + %div + - if google_code_import_enabled? + = link_to new_import_google_code_path, class: 'btn import_google_code' do + %i.fa.fa-google + Google Code + %div + - if fogbugz_import_enabled? + = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do + %i.fa.fa-bug + Fogbugz + %div + - if git_import_enabled? + = link_to "#", class: 'btn js-toggle-button import_git' do + %i.fa.fa-git + %span Repo by URL + %div + - if gitlab_project_import_enabled? + = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do + %i.fa.fa-gitlab + %span GitLab export + + .js-toggle-content.hide + = render "shared/import_form", f: f + + .form-group + = f.label :description, class: 'label-light' do + Project description + %span.light (optional) + = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250 + + .form-group.project-visibility-level-holder + = f.label :visibility_level, class: 'label-light' do + Visibility Level + = link_to "(?)", help_page_path("public_access", "public_access") + = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project) - .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index c04d291412c..a5e163b91e9 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -32,7 +32,7 @@ .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text = preserve do - = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author) + = note.note_html = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note_editable = render 'projects/notes/edit_form', note: note diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index bf5d09d50c2..817bf9b3f69 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -15,9 +15,10 @@ Edit Page .nav-controls - - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do - New Page + - if !(@page && @page.persisted?) + - if can?(current_user, :create_wiki, @project) + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + New Page = render 'main_links' diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 0fe8a3b490a..290743feb4a 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -2,9 +2,10 @@ .blob-result .file-holder .file-title - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do + - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename)) + = link_to blob_link do %i.fa.fa-file %strong = blob.filename .file-content.code.term - = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline + = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 37dcf39c062..e26693bf5b9 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,13 +1,16 @@ +- repository = nil unless local_assigns.key?(:repository) + .file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? - link_icon = icon('link') + - link = blob_link if defined?(blob_link) - blob.data.each_line.each_with_index do |_, index| - offset = defined?(first_line_number) ? first_line_number : 1 - i = index + offset -# We're not using `link_to` because it is too slow once we get to thousands of lines. - %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} + %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i} = link_icon = i .blob-content{data: {blob_id: blob.id}} - = highlight(blob.name, blob.data, plain: blob.no_highlighting?) + = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) diff --git a/app/views/shared/_labels_row.html.haml b/app/views/shared/_labels_row.html.haml index 87028ececd4..5507a05f6c1 100644 --- a/app/views/shared/_labels_row.html.haml +++ b/app/views/shared/_labels_row.html.haml @@ -1,6 +1,6 @@ - labels.each do |label| %span.label-row.btn-group{ role: "group", aria: { label: escape_once(label.name) }, style: "color: #{text_color_for_bg(label.color)}" } - = link_to namespace_project_label_path(@project.namespace, @project, label), + = link_to label_filter_path(@project, label, type: controller.controller_name), class: "btn btn-transparent has-tooltip", style: "background-color: #{label.color};", title: escape_once(label.description), diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 47b66d44e43..3c03c220ddd 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -21,7 +21,8 @@ = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do - render_colored_label(label) - - if assignee - = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), - class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do - - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') + %span{ class: "assignee-icon" } + - if assignee + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), + class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do + - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index 630d97e339d..f51599212db 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -14,7 +14,7 @@ - else = event_action_name(event) - if event.target - %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + %strong= link_to "#{event.target.to_reference}", [event.project.namespace.becomes(Namespace), event.project, event.target] at %strong diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 92305594a81..68665858c3e 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,6 +1,8 @@ - page_title @user.name - page_description @user.bio -- page_specific_javascripts asset_path("users/application.js") +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/d3.js') + = page_specific_javascript_tag('users/application.js') - header_title @user.name, user_path(@user) - @no_container = true diff --git a/config/application.rb b/config/application.rb index 05fec995ed3..2b0595ede2b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -84,6 +84,8 @@ module Gitlab config.assets.precompile << "graphs/application.js" config.assets.precompile << "users/application.js" config.assets.precompile << "network/application.js" + config.assets.precompile << "lib/utils/*.js" + config.assets.precompile << "lib/*.js" # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 021bdb11251..73977341b73 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -212,7 +212,7 @@ Devise.setup do |config| if Gitlab::LDAP::Config.enabled? Gitlab.config.ldap.servers.values.each do |server| if server['allow_username_or_email_login'] - email_stripping_proc = ->(name) {name.gsub(/@.*\z/,'')} + email_stripping_proc = ->(name) {name.gsub(/@.*\z/, '')} else email_stripping_proc = ->(name) {name} end diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb deleted file mode 100644 index 1516476815a..00000000000 --- a/config/initializers/haml.rb +++ /dev/null @@ -1,7 +0,0 @@ -Haml::Template.options[:ugly] = true - -# Remove the `:coffee` and `:coffeescript` filters -# -# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897 -Haml::Filters.remove_filter('coffee') -Haml::Filters.remove_filter('coffeescript') diff --git a/config/initializers/hamlit.rb b/config/initializers/hamlit.rb new file mode 100644 index 00000000000..7b545d8c06c --- /dev/null +++ b/config/initializers/hamlit.rb @@ -0,0 +1,18 @@ +module Hamlit + class TemplateHandler + def call(template) + Engine.new( + generator: Temple::Generators::RailsOutputBuffer, + attr_quote: '"', + ).call(template.source) + end + end +end + +ActionView::Template.register_template_handler( + :haml, + Hamlit::TemplateHandler.new, +) + +Hamlit::Filters.remove_filter('coffee') +Hamlit::Filters.remove_filter('coffeescript') diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 79e2d23ab2e..6796407d4e6 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -1,3 +1,17 @@ +# Email forcibly included in the standard checks, but the email health check +# doesn't support the full range of SMTP options, which can result in failures +# for valid SMTP configurations. +# Overwrite the HealthCheck's detection of whether email is configured +# in order to avoid the email check during standard checks +module HealthCheck + class Utils + def self.mailer_configured? + false + end + end +end + HealthCheck.setup do |config| config.standard_checks = ['database', 'migrations', 'cache'] + config.full_checks = ['database', 'migrations', 'cache'] end diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index d159f4eded2..75f89d524e7 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -113,6 +113,10 @@ if Gitlab::Metrics.enabled? config.instrument_methods(Banzai::Renderer) config.instrument_methods(Banzai::Querying) + config.instrument_instance_methods(Banzai::ObjectRenderer) + config.instrument_instance_methods(Banzai::Redactor) + config.instrument_methods(Banzai::NoteRenderer) + [Issuable, Mentionable, Participable].each do |klass| config.instrument_instance_methods(klass) config.instrument_instance_methods(klass::ClassMethods) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 7a2b9a7f6c1..593c14a289f 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -13,7 +13,7 @@ Sidekiq.configure_server do |config| # UGLY Hack to get nested hash from settingslogic cron_jobs = JSON.parse(Gitlab.config.cron_jobs.to_json) # UGLY hack: Settingslogic doesn't allow 'class' key - cron_jobs.each { |k,v| cron_jobs[k]['class'] = cron_jobs[k].delete('job_class') } + cron_jobs.each { |k, v| cron_jobs[k]['class'] = cron_jobs[k].delete('job_class') } Sidekiq::Cron::Job.load_from_hash! cron_jobs # Database pool should be at least `sidekiq_concurrency` + 2 diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample index 2287a76fca7..bd37080b1c8 100644 --- a/config/initializers/smtp_settings.rb.sample +++ b/config/initializers/smtp_settings.rb.sample @@ -10,6 +10,7 @@ if Rails.env.production? Rails.application.config.action_mailer.delivery_method = :smtp + ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.smtp_settings = { address: "email.server.com", port: 465, diff --git a/config/routes.rb b/config/routes.rb index e45293cdf7f..2aab73720f2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -280,6 +280,7 @@ Rails.application.routes.draw do resource :logs, only: [:show] resource :health_check, controller: 'health_check', only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] + resource :system_info, controller: 'system_info', only: [:show] resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do root to: 'projects#index', as: :projects @@ -652,7 +653,7 @@ Rails.application.routes.draw do get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID - post '/wikis/*id/markdown_preview', to:'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview' + post '/wikis/*id/markdown_preview', to: 'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview' end resource :repository, only: [:show, :create] do diff --git a/doc/README.md b/doc/README.md index f1283cea0ad..be0d17084c7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -44,6 +44,7 @@ - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast. - [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics. - [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint. +- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs. - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability. - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab. diff --git a/doc/administration/img/housekeeping_settings.png b/doc/administration/img/housekeeping_settings.png Binary files differindex f7c5bc44367..f72ad9a45d5 100644 --- a/doc/administration/img/housekeeping_settings.png +++ b/doc/administration/img/housekeeping_settings.png diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md new file mode 100644 index 00000000000..e5701b86cf3 --- /dev/null +++ b/doc/administration/troubleshooting/debug.md @@ -0,0 +1,120 @@ +# Debugging Tips + +Sometimes things don't work the way they should. Here are some tips on debugging issues out +in production. + +## The GNU Project Debugger (gdb) + +`gdb` is a must-have tool for debugging issues. To install on Ubuntu/Debian: + +``` +sudo apt-get install gdb +``` + +On CentOS: + +``` +sudo yum install gdb +``` + +## Common Problems + +Many of the tips to diagnose issues below apply to many different situations. We'll use one +concrete example to illustrate what you can do to learn what is going wrong. + +### 502 Gateway Timeout after unicorn spins at 100% CPU + +This error occurs when the Web server times out (default: 60 s) after not +hearing back from the unicorn worker. If the CPU spins to 100% while this in +progress, there may be something taking longer than it should. + +To fix this issue, we first need to figure out what is happening. The +following tips are only recommended if you do NOT mind users being affected by +downtime. Otherwise skip to the next section. + +1. Load the problematic URL +1. Run `sudo gdb -p <PID>` to attach to the unicorn process. +1. In the gdb window, type: + + ``` + call (void) rb_backtrace() + ``` + +1. This forces the process to generate a Ruby backtrace. Check + `/var/log/gitlab/unicorn/unicorn_stderr.log` for the backtace. For example, you may see: + + ```ruby + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' + ``` + +1. To see the current threads, run: + + ``` + apply all thread bt + ``` + +1. Once you're done debugging with `gdb`, be sure to detach from the process and exit: + + ``` + detach + exit + ``` + +Note that if the unicorn process terminates before you are able to run these +commands, gdb will report an error. To buy more time, you can always raise the +Unicorn timeout. For omnibus users, you can edit `/etc/gitlab/gitlab.rb` and +increase it from 60 seconds to 300: + +```ruby +unicorn['worker_timeout'] = 300 +``` + +For source installations, edit `config/unicorn.rb`. + +[Reconfigure] GitLab for the changes to take effect. + +[Reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure + +#### Troubleshooting without affecting other users + +The previous section attached to a running unicorn process, and this may have +undesirable effects for users trying to access GitLab during this time. If you +are concerned about affecting others during a production system, you can run a +separate Rails process to debug the issue: + +1. Log in to your GitLab account. +1. Copy the URL that is causing problems (e.g. https://gitlab.com/ABC). +1. Obtain the private token for your user (Profile Settings -> Account). +1. Bring up the GitLab Rails console. For omnibus users, run: + + ```` + sudo gitlab-rails console + ``` + +1. At the Rails console, run: + + ```ruby + [1] pry(main)> app.get '<URL FROM STEP 1>/private_token?<TOKEN FROM STEP 2>' + ``` + + For example: + + ```ruby + [1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456' + ``` + +1. In a new window, run `top`. It should show this ruby process using 100% CPU. Write down the PID. +1. Follow step 2 from the previous section on using gdb. + +# More information + +* [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/) +* [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt) diff --git a/doc/administration/troubleshooting/gdb-stuck-ruby.txt b/doc/administration/troubleshooting/gdb-stuck-ruby.txt new file mode 100644 index 00000000000..13d5dfcffa4 --- /dev/null +++ b/doc/administration/troubleshooting/gdb-stuck-ruby.txt @@ -0,0 +1,142 @@ +# Here's the script I'll use to demonstrate - it just loops forever: + +$ cat test.rb +#!/usr/bin/env ruby + +loop do + sleep 1 +end + +# Now, I'll start the script in the background, and redirect stdout and stderr +# to /dev/null: + +$ ruby ./test.rb >/dev/null 2>/dev/null & +[1] 1343 + +# Next, I'll grab the PID of the script (1343): + +$ ps aux | grep test.rb +vagrant 1343 0.0 0.4 3884 1652 pts/0 S 14:42 0:00 ruby ./test.rb +vagrant 1345 0.0 0.2 4624 852 pts/0 S+ 14:42 0:00 grep --color=auto test.rb + +# Now I start gdb. Note that I'm using sudo here. This may or may not be +# necessary in your setup. I'd try without sudo first, and fall back to adding +# it if the next step fails: + +$ sudo gdb +GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 +Copyright (C) 2012 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. Type "show copying" +and "show warranty" for details. +This GDB was configured as "i686-linux-gnu". +For bug reporting instructions, please see: +<http://bugs.launchpad.net/gdb-linaro/>. + +# OK, now I'm in gdb, and I want to instruct it to attach to our Ruby process. +# I can do that using the 'attach' command, which takes a PID (the one we +# gathered above): + +(gdb) attach 1343 +Attaching to process 1343 +Reading symbols from /opt/vagrant_ruby/bin/ruby...done. +Reading symbols from /lib/i386-linux-gnu/librt.so.1...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/librt.so.1 +Reading symbols from /lib/i386-linux-gnu/libdl.so.2...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libdl.so.2 +Reading symbols from /lib/i386-linux-gnu/libcrypt.so.1...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libcrypt.so.1 +Reading symbols from /lib/i386-linux-gnu/libm.so.6...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libm.so.6 +Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libc.so.6 +Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done. +[Thread debugging using libthread_db enabled] +Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". +Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0 +Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. +Loaded symbols for /lib/ld-linux.so.2 +0xb770c424 in __kernel_vsyscall () + +# Great, now gdb is attached to the target process. If the step above fails, try +# going back and running gdb under sudo. The next thing I want to do is gather +# C-level backtraces from all threads in the process. The following command +# stands for 'thread apply all backtrace': + +(gdb) t a a bt + +Thread 1 (Thread 0xb74d76c0 (LWP 1343)): +#0 0xb770c424 in __kernel_vsyscall () +#1 0xb75d7abd in select () from /lib/i386-linux-gnu/libc.so.6 +#2 0x08069c56 in rb_thread_wait_for (time=...) at eval.c:11376 +#3 0x080a20fd in rb_f_sleep (argc=1, argv=0xbf85f490) at process.c:1633 +#4 0x0805e0e2 in call_cfunc (argv=0xbf85f490, argc=1, len=-1, recv=3075299660, func=0x80a20b0 <rb_f_sleep>) + at eval.c:5778 +#5 rb_call0 (klass=3075304600, recv=3075299660, id=9393, oid=9393, argc=1, argv=0xbf85f490, body=0xb74c85a8, flags=2) + at eval.c:5928 +#6 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=9393, argc=1, argv=0xbf85f490, scope=1, + self=<optimized out>) at eval.c:6176 +#7 0x080651ec in rb_eval (self=3075299660, n=0xb74c4e1c) at eval.c:3521 +#8 0x0805c31c in rb_yield_0 (val=6, self=3075299660, klass=<optimized out>, flags=0, avalue=0) at eval.c:5095 +#9 0x0806a1e5 in loop_i () at eval.c:5227 +#10 0x08058dbd in rb_rescue2 (b_proc=0x806a1c0 <loop_i>, data1=0, r_proc=0, data2=0) at eval.c:5491 +#11 0x08058f28 in rb_f_loop () at eval.c:5252 +#12 0x0805e0c1 in call_cfunc (argv=0x0, argc=0, len=0, recv=3075299660, func=0x8058ef0 <rb_f_loop>) at eval.c:5781 +#13 rb_call0 (klass=3075304600, recv=3075299660, id=4121, oid=4121, argc=0, argv=0x0, body=0xb74d4dbc, flags=2) + at eval.c:5928 +#14 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=4121, argc=0, argv=0x0, scope=1, self=<optimized out>) + at eval.c:6176 +#15 0x080651ec in rb_eval (self=3075299660, n=0xb74c4dcc) at eval.c:3521 +#16 0x080662c6 in rb_eval (self=3075299660, n=0xb74c4de0) at eval.c:3236 +#17 0x08068ee4 in ruby_exec_internal () at eval.c:1654 +#18 0x08068f24 in ruby_exec () at eval.c:1674 +#19 0x0806b2cd in ruby_run () at eval.c:1684 +#20 0x08053771 in main (argc=2, argv=0xbf860204, envp=0xbf860210) at main.c:48 + +# C backtraces are sometimes sufficient, but often Ruby backtraces are necessary +# for debugging as well. Ruby has a built-in function called rb_backtrace() that +# we can use to dump out a Ruby backtrace, but it prints to stdout or stderr +# (depending on your Ruby version), which might have been redirected to a file +# or to /dev/null (as in our example) when the process started up. +# +# To get aroundt this, we'll do a little trick and redirect the target process's +# stdout and stderr to the current TTY, so that any output from the process +# will appear directly on our screen. + +# First, let's close the existing file descriptors for stdout and stderr +# (FD 1 and 2, respectively): +(gdb) call (void) close(1) +(gdb) call (void) close(2) + +# Next, we need to figure out the device name for the current TTY: +(gdb) shell tty +/dev/pts/0 + +# OK, now we can pass the device name obtained above to open() and attach +# file descriptors 1 and 2 back to the current TTY with these calls: + +(gdb) call (int) open("/dev/pts/0", 2, 0) +$1 = 1 +(gdb) call (int) open("/dev/pts/0", 2, 0) +$2 = 2 + +# Finally, we call rb_backtrace() in order to dump the Ruby backtrace: + +(gdb) call (void) rb_backtrace() + from ./test.rb:4:in `sleep' + from ./test.rb:4 + from ./test.rb:3:in `loop' + from ./test.rb:3 + +# And here's how we get out of gdb. Once you've quit, you'll probably want to +# clean up the stuck process by killing it. + +(gdb) quit +A debugging session is active. + + Inferior 1 [process 1343] will be detached. + +Quit anyway? (y or n) y +Detaching from program: /opt/vagrant_ruby/bin/ruby, process 1343 +$ diff --git a/doc/api/builds.md b/doc/api/builds.md index de998944352..2adea11247e 100644 --- a/doc/api/builds.md +++ b/doc/api/builds.md @@ -107,6 +107,11 @@ Example of response Get a list of builds for specific commit in a project. +This endpoint will return all builds, from all pipelines for a given commit. +If the commit SHA is not found, it will respond with 404, otherwise it will +return an array of builds (an empty array if there are no builds for this +particular commit). + ``` GET /projects/:id/repository/commits/:sha/builds ``` diff --git a/doc/api/issues.md b/doc/api/issues.md index 0bc82ef9edb..708fc691f67 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -28,7 +28,7 @@ GET /issues?labels=foo,bar&state=opened | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `state` | string | no | Return all issues or just those that are `opened` or `closed`| -| `labels` | string | no | Comma-separated list of label names | +| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | @@ -83,6 +83,82 @@ Example response: ] ``` +## List group issues + +Get a list of a group's issues. + +``` +GET /groups/:id/issues +GET /groups/:id/issues?state=opened +GET /groups/:id/issues?state=closed +GET /groups/:id/issues?labels=foo +GET /groups/:id/issues?labels=foo,bar +GET /groups/:id/issues?labels=foo,bar&state=opened +GET /groups/:id/issues?milestone=1.0.0 +GET /groups/:id/issues?milestone=1.0.0&state=opened +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a group | +| `state` | string | no | Return all issues or just those that are `opened` or `closed`| +| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned | +| `milestone` | string| no | The milestone title | +| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | + + +```bash +curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4/issues +``` + +Example response: + +```json +[ + { + "project_id" : 4, + "milestone" : { + "due_date" : null, + "project_id" : 4, + "state" : "closed", + "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", + "iid" : 3, + "id" : 11, + "title" : "v3.0", + "created_at" : "2016-01-04T15:31:39.788Z", + "updated_at" : "2016-01-04T15:31:39.788Z" + }, + "author" : { + "state" : "active", + "web_url" : "https://gitlab.example.com/u/root", + "avatar_url" : null, + "username" : "root", + "id" : 1, + "name" : "Administrator" + }, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "state" : "closed", + "iid" : 1, + "assignee" : { + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/u/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }, + "labels" : [], + "id" : 41, + "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, + "user_notes_count": 1 + } +] +``` + ## List project issues Get a list of a project's issues. @@ -104,7 +180,7 @@ GET /projects/:id/issues?iid=42 | `id` | integer | yes | The ID of a project | | `iid` | integer | no | Return the issue having the given `iid` | | `state` | string | no | Return all issues or just those that are `opened` or `closed`| -| `labels` | string | no | Comma-separated list of label names | +| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `milestone` | string| no | The milestone title | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index d416a826f79..31902e145f6 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -65,6 +65,13 @@ curl -H "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user ## Resource Owner Password Credentials +## Deprecation Notice + +1. Starting in GitLab 9.0, the Resource Owner Password Credentials will be *disabled* for users with two-factor authentication turned on. +2. These users can access the API using [personal access tokens] instead. + +--- + In this flow, a token is requested in exchange for the resource owner credentials (username and password). The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g. the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not @@ -100,3 +107,5 @@ client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "http access_token = client.password.get_token('user@example.com', 'sekret') puts access_token.token ``` + +[personal access tokens]: ./README.md#personal-access-tokens diff --git a/doc/api/services.md b/doc/api/services.md index ccfc0fccb7f..32d6e2dea78 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -374,40 +374,6 @@ Get Gemnasium service settings for a project. GET /projects/:id/services/gemnasium ``` -## GitLab CI - -Continuous integration server from GitLab - -### Create/Edit GitLab CI service - -Set GitLab CI service for a project. - -``` -PUT /projects/:id/services/gitlab-ci -``` - -Parameters: - -- `token` (**required**) - GitLab CI project specific token -- `project_url` (**required**) - http://ci.gitlabhq.com/projects/3 -- `enable_ssl_verification` (optional) - Enable SSL verification - -### Delete GitLab CI service - -Delete GitLab CI service for a project. - -``` -DELETE /projects/:id/services/gitlab-ci -``` - -### Get GitLab CI service settings - -Get GitLab CI service settings for a project. - -``` -GET /projects/:id/services/gitlab-ci -``` - ## HipChat Private group chat and IM diff --git a/doc/api/session.md b/doc/api/session.md index 71e93d0bb0a..066a055702d 100644 --- a/doc/api/session.md +++ b/doc/api/session.md @@ -1,5 +1,12 @@ # Session +## Deprecation Notice + +1. Starting in GitLab 9.0, this feature will be *disabled* for users with two-factor authentication turned on. +2. These users can access the API using [personal access tokens] instead. + +--- + You can login with both GitLab and LDAP credentials in order to obtain the private token. @@ -45,3 +52,5 @@ Example response: "private_token": "9koXpg98eAheJpvBs5tK" } ``` + +[personal access tokens]: ./README.md#personal-access-tokens diff --git a/doc/api/settings.md b/doc/api/settings.md index 43a0fe35e42..b5152311f28 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -56,7 +56,7 @@ PUT /application/settings | `gravatar_enabled` | boolean | no | Enable Gravatar | | `sign_in_text` | string | no | Text on login page | | `home_page_url` | string | no | Redirect to this URL when not logged in | -| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `1`. | +| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `2`. | | `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. | | `max_attachment_size` | integer | no | Limit attachment size in MB | | `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes | diff --git a/doc/ci/build_artifacts/img/build_artifacts_browser.png b/doc/ci/build_artifacts/img/build_artifacts_browser.png Binary files differindex 73ed4eeb927..59cf2b8746b 100644 --- a/doc/ci/build_artifacts/img/build_artifacts_browser.png +++ b/doc/ci/build_artifacts/img/build_artifacts_browser.png diff --git a/doc/ci/build_artifacts/img/build_artifacts_browser_button.png b/doc/ci/build_artifacts/img/build_artifacts_browser_button.png Binary files differindex f5d15bc3e7d..7801c2e6fa6 100644 --- a/doc/ci/build_artifacts/img/build_artifacts_browser_button.png +++ b/doc/ci/build_artifacts/img/build_artifacts_browser_button.png diff --git a/doc/ci/img/builds_tab.png b/doc/ci/img/builds_tab.png Binary files differindex d088b8b329d..35780e277ae 100644 --- a/doc/ci/img/builds_tab.png +++ b/doc/ci/img/builds_tab.png diff --git a/doc/ci/img/features_settings.png b/doc/ci/img/features_settings.png Binary files differindex 17aba5d14d8..38d7036f606 100644 --- a/doc/ci/img/features_settings.png +++ b/doc/ci/img/features_settings.png diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png Binary files differindex 89e6cd40cb6..b53a6cd86b0 100644 --- a/doc/ci/quick_start/img/build_log.png +++ b/doc/ci/quick_start/img/build_log.png diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png Binary files differindex b8e6c2a361a..47862761ffe 100644 --- a/doc/ci/quick_start/img/builds_status.png +++ b/doc/ci/quick_start/img/builds_status.png diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png Binary files differindex 3d3c9d5c0bd..a53562ce328 100644 --- a/doc/ci/quick_start/img/new_commit.png +++ b/doc/ci/quick_start/img/new_commit.png diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png Binary files differindex eafcfd6ecd5..23261123b18 100644 --- a/doc/ci/quick_start/img/runners_activated.png +++ b/doc/ci/quick_start/img/runners_activated.png diff --git a/doc/ci/quick_start/img/single_commit_status_pending.png b/doc/ci/quick_start/img/single_commit_status_pending.png Binary files differindex 23b3bb5acfc..ccf3ac957bb 100644 --- a/doc/ci/quick_start/img/single_commit_status_pending.png +++ b/doc/ci/quick_start/img/single_commit_status_pending.png diff --git a/doc/ci/quick_start/img/status_pending.png b/doc/ci/quick_start/img/status_pending.png Binary files differindex a049ec2a5ba..9feacf0c961 100644 --- a/doc/ci/quick_start/img/status_pending.png +++ b/doc/ci/quick_start/img/status_pending.png diff --git a/doc/ci/runners/project_specific.png b/doc/ci/runners/project_specific.png Binary files differindex f51ea694e78..c812defa67b 100644 --- a/doc/ci/runners/project_specific.png +++ b/doc/ci/runners/project_specific.png diff --git a/doc/ci/runners/shared_runner.png b/doc/ci/runners/shared_runner.png Binary files differindex 9755144eb08..31574a17764 100644 --- a/doc/ci/runners/shared_runner.png +++ b/doc/ci/runners/shared_runner.png diff --git a/doc/ci/runners/shared_to_specific_admin.png b/doc/ci/runners/shared_to_specific_admin.png Binary files differindex 44a4bef22f7..8f4010a5849 100644 --- a/doc/ci/runners/shared_to_specific_admin.png +++ b/doc/ci/runners/shared_to_specific_admin.png diff --git a/doc/ci/triggers/img/builds_page.png b/doc/ci/triggers/img/builds_page.png Binary files differindex e78794fbee7..2dee8ee6107 100644 --- a/doc/ci/triggers/img/builds_page.png +++ b/doc/ci/triggers/img/builds_page.png diff --git a/doc/ci/triggers/img/trigger_single_build.png b/doc/ci/triggers/img/trigger_single_build.png Binary files differindex c25f27409d6..baf3fc183d8 100644 --- a/doc/ci/triggers/img/trigger_single_build.png +++ b/doc/ci/triggers/img/trigger_single_build.png diff --git a/doc/ci/triggers/img/trigger_variables.png b/doc/ci/triggers/img/trigger_variables.png Binary files differindex 2207e8b34cb..908355c33a5 100644 --- a/doc/ci/triggers/img/trigger_variables.png +++ b/doc/ci/triggers/img/trigger_variables.png diff --git a/doc/ci/triggers/img/triggers_page.png b/doc/ci/triggers/img/triggers_page.png Binary files differindex 268368dc3c5..69cec5cdebf 100644 --- a/doc/ci/triggers/img/triggers_page.png +++ b/doc/ci/triggers/img/triggers_page.png diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 1892acda29b..d2d1b04f893 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1034,8 +1034,8 @@ You can find the link under `/ci/lint` of your gitlab instance. ## Skipping builds -If your commit message contains `[ci skip]`, the commit will be created but the -builds will be skipped. +If your commit message contains `[ci skip]` or `[skip ci]`, using any +capitalization, the commit will be created but the builds will be skipped. ## Examples diff --git a/doc/container_registry/img/container_registry.png b/doc/container_registry/img/container_registry.png Binary files differindex e9505a73b40..57d6f9f22c5 100644 --- a/doc/container_registry/img/container_registry.png +++ b/doc/container_registry/img/container_registry.png diff --git a/doc/container_registry/img/project_feature.png b/doc/container_registry/img/project_feature.png Binary files differindex 57a73d253c0..a59b4f82b56 100644 --- a/doc/container_registry/img/project_feature.png +++ b/doc/container_registry/img/project_feature.png diff --git a/doc/customization/branded_login_page/appearance.png b/doc/customization/branded_login_page/appearance.png Binary files differindex 6bce1f0a287..023dc5599b4 100644 --- a/doc/customization/branded_login_page/appearance.png +++ b/doc/customization/branded_login_page/appearance.png diff --git a/doc/customization/branded_login_page/custom_sign_in.png b/doc/customization/branded_login_page/custom_sign_in.png Binary files differindex d6020b029a2..7d99e0a2b3b 100644 --- a/doc/customization/branded_login_page/custom_sign_in.png +++ b/doc/customization/branded_login_page/custom_sign_in.png diff --git a/doc/customization/branded_login_page/default_login_page.png b/doc/customization/branded_login_page/default_login_page.png Binary files differindex 795c7954d8e..0cfa9da202e 100644 --- a/doc/customization/branded_login_page/default_login_page.png +++ b/doc/customization/branded_login_page/default_login_page.png diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md index 194b8e00299..4620bb2dcde 100644 --- a/doc/customization/issue_closing.md +++ b/doc/customization/issue_closing.md @@ -8,7 +8,7 @@ the matched text will be closed. This happens when the commit is pushed to a pro When not specified, the default `issue_closing_pattern` as shown below will be used: ```bash -((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+) +((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+) ``` Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`). diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 12e33406cb6..33fd50f4c11 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -52,7 +52,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell ### Components -![GitLab Diagram Overview](gitlab_diagram_overview.png) +![GitLab Diagram Overview](gitlab_architecture_diagram.png) + +_[edit diagram (for GitLab team members only)](https://docs.google.com/drawings/d/1fBzAyklyveF-i-2q-OHUIqDkYfjjxC4mq5shwKSZHLs/edit)_ A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs. diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png Binary files differnew file mode 100644 index 00000000000..80e975718e0 --- /dev/null +++ b/doc/development/gitlab_architecture_diagram.png diff --git a/doc/development/gitlab_diagram_overview.png b/doc/development/gitlab_diagram_overview.png Binary files differdeleted file mode 100644 index d9b9eed3d8f..00000000000 --- a/doc/development/gitlab_diagram_overview.png +++ /dev/null diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md index 21078c8d6f9..9d7fe7440d2 100644 --- a/doc/development/gotchas.md +++ b/doc/development/gotchas.md @@ -46,7 +46,7 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9 Using the inline `:coffee` or `:coffeescript` Haml filters comes with a performance overhead. -_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/initializers/haml.rb) +_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb) in an initializer._ ### Further reading diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 8a7547e5322..e2ca46504e7 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -37,7 +37,6 @@ First, you need to provide information on whether the migration can be applied: For example: ``` -# rubocop:disable all # Migration type: online without errors (works on previous version and new one) class MyMigration < ActiveRecord::Migration ... diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 6d04b9590e6..41685c7ee41 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -33,3 +33,23 @@ bundle exec rake gitlab:generate_docs ``` bundle exec rake services:doc ``` + +## Updating Emoji Digests + +To update the Emoji digests file (used for Emoji autocomplete) you must run the +following: + +``` +bundle exec rake gemojione:digests +``` + +This will update the file `fixtures/emojis/digests.json` based on the currently +available Emoji. + +## Emoji Sprites + +Generating a sprite file containing all the Emoji can be done by running: + +``` +bundle exec rake gemojione:sprite +``` diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md index 3625c4191b8..a6d22e5a04a 100644 --- a/doc/downgrade_ee_to_ce/README.md +++ b/doc/downgrade_ee_to_ce/README.md @@ -44,13 +44,13 @@ to avoid getting this error, you need to remove all instances of the **Omnibus Installation** ``` -$ sudo gitlab-rails runner "Service.where(type: 'JenkinsService').delete_all" +$ sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" ``` **Source Installation** ``` -$ bundle exec rails runner "Service.where(type: 'JenkinsService').delete_all" production +$ bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" production ``` ## Downgrade to CE diff --git a/doc/gitlab-basics/basicsimages/add_new_merge_request.png b/doc/gitlab-basics/basicsimages/add_new_merge_request.png Binary files differindex 9d93b217a59..e60992c4c6a 100644 --- a/doc/gitlab-basics/basicsimages/add_new_merge_request.png +++ b/doc/gitlab-basics/basicsimages/add_new_merge_request.png diff --git a/doc/gitlab-basics/basicsimages/add_sshkey.png b/doc/gitlab-basics/basicsimages/add_sshkey.png Binary files differindex 2dede97aa40..89c86018629 100644 --- a/doc/gitlab-basics/basicsimages/add_sshkey.png +++ b/doc/gitlab-basics/basicsimages/add_sshkey.png diff --git a/doc/gitlab-basics/basicsimages/branch_info.png b/doc/gitlab-basics/basicsimages/branch_info.png Binary files differindex c5e38b552a5..2264f3c5bf2 100644 --- a/doc/gitlab-basics/basicsimages/branch_info.png +++ b/doc/gitlab-basics/basicsimages/branch_info.png diff --git a/doc/gitlab-basics/basicsimages/branch_name.png b/doc/gitlab-basics/basicsimages/branch_name.png Binary files differindex 06e77f5eea9..75fe8313611 100644 --- a/doc/gitlab-basics/basicsimages/branch_name.png +++ b/doc/gitlab-basics/basicsimages/branch_name.png diff --git a/doc/gitlab-basics/basicsimages/branches.png b/doc/gitlab-basics/basicsimages/branches.png Binary files differindex c18fa83b968..8621bc05776 100644 --- a/doc/gitlab-basics/basicsimages/branches.png +++ b/doc/gitlab-basics/basicsimages/branches.png diff --git a/doc/gitlab-basics/basicsimages/button-create-mr.png b/doc/gitlab-basics/basicsimages/button-create-mr.png Binary files differindex 457af459bb9..b52ab148839 100644 --- a/doc/gitlab-basics/basicsimages/button-create-mr.png +++ b/doc/gitlab-basics/basicsimages/button-create-mr.png diff --git a/doc/gitlab-basics/basicsimages/click-on-new-group.png b/doc/gitlab-basics/basicsimages/click-on-new-group.png Binary files differindex 94b6d5756d3..6450deec6fc 100644 --- a/doc/gitlab-basics/basicsimages/click-on-new-group.png +++ b/doc/gitlab-basics/basicsimages/click-on-new-group.png diff --git a/doc/gitlab-basics/basicsimages/commit_changes.png b/doc/gitlab-basics/basicsimages/commit_changes.png Binary files differindex 81588336f37..a88809c5a3f 100644 --- a/doc/gitlab-basics/basicsimages/commit_changes.png +++ b/doc/gitlab-basics/basicsimages/commit_changes.png diff --git a/doc/gitlab-basics/basicsimages/commit_message.png b/doc/gitlab-basics/basicsimages/commit_message.png Binary files differindex 0df2c32653c..4abe4517f98 100644 --- a/doc/gitlab-basics/basicsimages/commit_message.png +++ b/doc/gitlab-basics/basicsimages/commit_message.png diff --git a/doc/gitlab-basics/basicsimages/commits.png b/doc/gitlab-basics/basicsimages/commits.png Binary files differindex 7e606539077..2bfcaf75f01 100644 --- a/doc/gitlab-basics/basicsimages/commits.png +++ b/doc/gitlab-basics/basicsimages/commits.png diff --git a/doc/gitlab-basics/basicsimages/compare_branches.png b/doc/gitlab-basics/basicsimages/compare_branches.png Binary files differindex 7eebaed9075..8a18453dd05 100644 --- a/doc/gitlab-basics/basicsimages/compare_branches.png +++ b/doc/gitlab-basics/basicsimages/compare_branches.png diff --git a/doc/gitlab-basics/basicsimages/create_file.png b/doc/gitlab-basics/basicsimages/create_file.png Binary files differindex 688e355cca2..5ebe1b227dd 100644 --- a/doc/gitlab-basics/basicsimages/create_file.png +++ b/doc/gitlab-basics/basicsimages/create_file.png diff --git a/doc/gitlab-basics/basicsimages/create_group.png b/doc/gitlab-basics/basicsimages/create_group.png Binary files differindex 57da898abdc..7ecc3baa990 100644 --- a/doc/gitlab-basics/basicsimages/create_group.png +++ b/doc/gitlab-basics/basicsimages/create_group.png diff --git a/doc/gitlab-basics/basicsimages/edit_file.png b/doc/gitlab-basics/basicsimages/edit_file.png Binary files differindex afa68760108..9d3e817d036 100644 --- a/doc/gitlab-basics/basicsimages/edit_file.png +++ b/doc/gitlab-basics/basicsimages/edit_file.png diff --git a/doc/gitlab-basics/basicsimages/file_located.png b/doc/gitlab-basics/basicsimages/file_located.png Binary files differindex 1def489d16b..e357cb5c6ab 100644 --- a/doc/gitlab-basics/basicsimages/file_located.png +++ b/doc/gitlab-basics/basicsimages/file_located.png diff --git a/doc/gitlab-basics/basicsimages/file_name.png b/doc/gitlab-basics/basicsimages/file_name.png Binary files differindex 9ac2f1c355f..01639c77d0d 100644 --- a/doc/gitlab-basics/basicsimages/file_name.png +++ b/doc/gitlab-basics/basicsimages/file_name.png diff --git a/doc/gitlab-basics/basicsimages/find_file.png b/doc/gitlab-basics/basicsimages/find_file.png Binary files differindex 98639149a39..6f26d26ae18 100644 --- a/doc/gitlab-basics/basicsimages/find_file.png +++ b/doc/gitlab-basics/basicsimages/find_file.png diff --git a/doc/gitlab-basics/basicsimages/find_group.png b/doc/gitlab-basics/basicsimages/find_group.png Binary files differindex 5ac33c7e953..1211510aae9 100644 --- a/doc/gitlab-basics/basicsimages/find_group.png +++ b/doc/gitlab-basics/basicsimages/find_group.png diff --git a/doc/gitlab-basics/basicsimages/fork.png b/doc/gitlab-basics/basicsimages/fork.png Binary files differindex b1f94938613..13ff8345616 100644 --- a/doc/gitlab-basics/basicsimages/fork.png +++ b/doc/gitlab-basics/basicsimages/fork.png diff --git a/doc/gitlab-basics/basicsimages/group_info.png b/doc/gitlab-basics/basicsimages/group_info.png Binary files differindex e78d84e4d80..2507d6c295b 100644 --- a/doc/gitlab-basics/basicsimages/group_info.png +++ b/doc/gitlab-basics/basicsimages/group_info.png diff --git a/doc/gitlab-basics/basicsimages/groups.png b/doc/gitlab-basics/basicsimages/groups.png Binary files differindex b8104343afa..ef3dca60cc8 100644 --- a/doc/gitlab-basics/basicsimages/groups.png +++ b/doc/gitlab-basics/basicsimages/groups.png diff --git a/doc/gitlab-basics/basicsimages/https.png b/doc/gitlab-basics/basicsimages/https.png Binary files differindex 2a31b4cf751..e74dbc13f9a 100644 --- a/doc/gitlab-basics/basicsimages/https.png +++ b/doc/gitlab-basics/basicsimages/https.png diff --git a/doc/gitlab-basics/basicsimages/image_file.png b/doc/gitlab-basics/basicsimages/image_file.png Binary files differindex 1061d9c5082..7f304b8e1f2 100644 --- a/doc/gitlab-basics/basicsimages/image_file.png +++ b/doc/gitlab-basics/basicsimages/image_file.png diff --git a/doc/gitlab-basics/basicsimages/issue_title.png b/doc/gitlab-basics/basicsimages/issue_title.png Binary files differindex 7b69c705392..60a6f7973be 100644 --- a/doc/gitlab-basics/basicsimages/issue_title.png +++ b/doc/gitlab-basics/basicsimages/issue_title.png diff --git a/doc/gitlab-basics/basicsimages/issues.png b/doc/gitlab-basics/basicsimages/issues.png Binary files differindex 9354d05319e..14e9cdb64e1 100644 --- a/doc/gitlab-basics/basicsimages/issues.png +++ b/doc/gitlab-basics/basicsimages/issues.png diff --git a/doc/gitlab-basics/basicsimages/key.png b/doc/gitlab-basics/basicsimages/key.png Binary files differindex 321805cda98..04400173ce8 100644 --- a/doc/gitlab-basics/basicsimages/key.png +++ b/doc/gitlab-basics/basicsimages/key.png diff --git a/doc/gitlab-basics/basicsimages/merge_requests.png b/doc/gitlab-basics/basicsimages/merge_requests.png Binary files differindex 7601d40de47..570164df18b 100644 --- a/doc/gitlab-basics/basicsimages/merge_requests.png +++ b/doc/gitlab-basics/basicsimages/merge_requests.png diff --git a/doc/gitlab-basics/basicsimages/new_merge_request.png b/doc/gitlab-basics/basicsimages/new_merge_request.png Binary files differindex 9120d2b1ab1..842f5ebed74 100644 --- a/doc/gitlab-basics/basicsimages/new_merge_request.png +++ b/doc/gitlab-basics/basicsimages/new_merge_request.png diff --git a/doc/gitlab-basics/basicsimages/new_project.png b/doc/gitlab-basics/basicsimages/new_project.png Binary files differindex ac255270a66..421e8bc247b 100644 --- a/doc/gitlab-basics/basicsimages/new_project.png +++ b/doc/gitlab-basics/basicsimages/new_project.png diff --git a/doc/gitlab-basics/basicsimages/newbranch.png b/doc/gitlab-basics/basicsimages/newbranch.png Binary files differindex da1a6b604ea..d5fcf33c4ea 100644 --- a/doc/gitlab-basics/basicsimages/newbranch.png +++ b/doc/gitlab-basics/basicsimages/newbranch.png diff --git a/doc/gitlab-basics/basicsimages/paste_sshkey.png b/doc/gitlab-basics/basicsimages/paste_sshkey.png Binary files differindex 9880ddfead1..578ebee4440 100644 --- a/doc/gitlab-basics/basicsimages/paste_sshkey.png +++ b/doc/gitlab-basics/basicsimages/paste_sshkey.png diff --git a/doc/gitlab-basics/basicsimages/profile_settings.png b/doc/gitlab-basics/basicsimages/profile_settings.png Binary files differindex 5f2e7a7e10c..cb3f79f1879 100644 --- a/doc/gitlab-basics/basicsimages/profile_settings.png +++ b/doc/gitlab-basics/basicsimages/profile_settings.png diff --git a/doc/gitlab-basics/basicsimages/project_info.png b/doc/gitlab-basics/basicsimages/project_info.png Binary files differindex 6c06ff351fa..e1adb8d48c2 100644 --- a/doc/gitlab-basics/basicsimages/project_info.png +++ b/doc/gitlab-basics/basicsimages/project_info.png diff --git a/doc/gitlab-basics/basicsimages/public_file_link.png b/doc/gitlab-basics/basicsimages/public_file_link.png Binary files differindex 1a60a3d880a..f60df6807f4 100644 --- a/doc/gitlab-basics/basicsimages/public_file_link.png +++ b/doc/gitlab-basics/basicsimages/public_file_link.png diff --git a/doc/gitlab-basics/basicsimages/select-group.png b/doc/gitlab-basics/basicsimages/select-group.png Binary files differindex d02c2255ff2..33b978dd899 100644 --- a/doc/gitlab-basics/basicsimages/select-group.png +++ b/doc/gitlab-basics/basicsimages/select-group.png diff --git a/doc/gitlab-basics/basicsimages/select-group2.png b/doc/gitlab-basics/basicsimages/select-group2.png Binary files differindex fd40bce499b..aee22c638db 100644 --- a/doc/gitlab-basics/basicsimages/select-group2.png +++ b/doc/gitlab-basics/basicsimages/select-group2.png diff --git a/doc/gitlab-basics/basicsimages/select_branch.png b/doc/gitlab-basics/basicsimages/select_branch.png Binary files differindex 3475b2df576..f72a3ffb57f 100644 --- a/doc/gitlab-basics/basicsimages/select_branch.png +++ b/doc/gitlab-basics/basicsimages/select_branch.png diff --git a/doc/gitlab-basics/basicsimages/select_project.png b/doc/gitlab-basics/basicsimages/select_project.png Binary files differindex 6d5aa439124..3bb832ea8d0 100644 --- a/doc/gitlab-basics/basicsimages/select_project.png +++ b/doc/gitlab-basics/basicsimages/select_project.png diff --git a/doc/gitlab-basics/basicsimages/settings.png b/doc/gitlab-basics/basicsimages/settings.png Binary files differindex 9bf9c5a0d39..78637013d9b 100644 --- a/doc/gitlab-basics/basicsimages/settings.png +++ b/doc/gitlab-basics/basicsimages/settings.png diff --git a/doc/gitlab-basics/basicsimages/shh_keys.png b/doc/gitlab-basics/basicsimages/shh_keys.png Binary files differindex d7ef4dafe77..c87f11a9d3d 100644 --- a/doc/gitlab-basics/basicsimages/shh_keys.png +++ b/doc/gitlab-basics/basicsimages/shh_keys.png diff --git a/doc/gitlab-basics/basicsimages/submit_new_issue.png b/doc/gitlab-basics/basicsimages/submit_new_issue.png Binary files differindex 18944417085..78b854c8903 100644 --- a/doc/gitlab-basics/basicsimages/submit_new_issue.png +++ b/doc/gitlab-basics/basicsimages/submit_new_issue.png diff --git a/doc/gitlab-basics/basicsimages/title_description_mr.png b/doc/gitlab-basics/basicsimages/title_description_mr.png Binary files differindex e08eb628414..c31d61ec336 100644 --- a/doc/gitlab-basics/basicsimages/title_description_mr.png +++ b/doc/gitlab-basics/basicsimages/title_description_mr.png diff --git a/doc/gitlab-basics/basicsimages/white_space.png b/doc/gitlab-basics/basicsimages/white_space.png Binary files differindex 6363a09360e..eaa969bdcf4 100644 --- a/doc/gitlab-basics/basicsimages/white_space.png +++ b/doc/gitlab-basics/basicsimages/white_space.png diff --git a/doc/install/installation.md b/doc/install/installation.md index d9290b1fa76..dc8d9c65535 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -391,6 +391,10 @@ GitLab Shell is an SSH access and repository management software developed speci ### Install gitlab-workhorse +GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). +If you are not using Linux you may have to run `gmake` instead of +`make` below. + cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 09c6211b3ab..a65ac8a5f79 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -52,7 +52,7 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ### CPU -- 1 core works supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core +- 1 core supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core - **2 cores** is the **recommended** number of cores and supports up to 500 users - 4 cores supports up to 2,000 users - 8 cores supports up to 5,000 users diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index a2d7e922aad..8d2c6351fb8 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -1,7 +1,7 @@ # External issue tracker GitLab has a great issue tracker but you can also use an external one such as -Jira or Redmine. Issue trackers are configurable per GitLab project and allow +Jira, Redmine, or Bugzilla. Issue trackers are configurable per GitLab project and allow you to do the following: - the **Issues** link on the GitLab project pages takes you to the appropriate @@ -20,6 +20,7 @@ Visit the links below for details: - [Redmine](../project_services/redmine.md) - [Jira](../project_services/jira.md) +- [Bugzilla](../project_services/bugzilla.md) ### Service Template diff --git a/doc/integration/img/akismet_settings.png b/doc/integration/img/akismet_settings.png Binary files differindex ccdd3adb1c5..c2aa97b132e 100644 --- a/doc/integration/img/akismet_settings.png +++ b/doc/integration/img/akismet_settings.png diff --git a/doc/integration/img/enabled-oauth-sign-in-sources.png b/doc/integration/img/enabled-oauth-sign-in-sources.png Binary files differindex 95f8bbdcd24..b23d6dcc595 100644 --- a/doc/integration/img/enabled-oauth-sign-in-sources.png +++ b/doc/integration/img/enabled-oauth-sign-in-sources.png diff --git a/doc/integration/img/facebook_api_keys.png b/doc/integration/img/facebook_api_keys.png Binary files differindex d6c44ac0f11..995845d5a69 100644 --- a/doc/integration/img/facebook_api_keys.png +++ b/doc/integration/img/facebook_api_keys.png diff --git a/doc/integration/img/facebook_app_settings.png b/doc/integration/img/facebook_app_settings.png Binary files differindex 30dd21e198a..1cd586ecd7c 100644 --- a/doc/integration/img/facebook_app_settings.png +++ b/doc/integration/img/facebook_app_settings.png diff --git a/doc/integration/img/facebook_website_url.png b/doc/integration/img/facebook_website_url.png Binary files differindex dc3088bb2fa..10e1bd5d5a6 100644 --- a/doc/integration/img/facebook_website_url.png +++ b/doc/integration/img/facebook_website_url.png diff --git a/doc/integration/img/github_app.png b/doc/integration/img/github_app.png Binary files differindex d890345ced9..de31242679a 100644 --- a/doc/integration/img/github_app.png +++ b/doc/integration/img/github_app.png diff --git a/doc/integration/img/gitlab_app.png b/doc/integration/img/gitlab_app.png Binary files differindex 3f9391a821b..065316fd3c7 100644 --- a/doc/integration/img/gitlab_app.png +++ b/doc/integration/img/gitlab_app.png diff --git a/doc/integration/img/gmail_action_buttons_for_gitlab.png b/doc/integration/img/gmail_action_buttons_for_gitlab.png Binary files differindex b08f54d137b..a6704139091 100644 --- a/doc/integration/img/gmail_action_buttons_for_gitlab.png +++ b/doc/integration/img/gmail_action_buttons_for_gitlab.png diff --git a/doc/integration/img/google_app.png b/doc/integration/img/google_app.png Binary files differindex 5a62ad35009..08f7f714553 100644 --- a/doc/integration/img/google_app.png +++ b/doc/integration/img/google_app.png diff --git a/doc/integration/img/oauth_provider_admin_application.png b/doc/integration/img/oauth_provider_admin_application.png Binary files differindex a2d8e14c120..fc5f7596fcc 100644 --- a/doc/integration/img/oauth_provider_admin_application.png +++ b/doc/integration/img/oauth_provider_admin_application.png diff --git a/doc/integration/img/oauth_provider_application_form.png b/doc/integration/img/oauth_provider_application_form.png Binary files differindex 3a676b22393..606ab3e3467 100644 --- a/doc/integration/img/oauth_provider_application_form.png +++ b/doc/integration/img/oauth_provider_application_form.png diff --git a/doc/integration/img/oauth_provider_application_id_secret.png b/doc/integration/img/oauth_provider_application_id_secret.png Binary files differindex 6d68df001af..cbedcef8376 100644 --- a/doc/integration/img/oauth_provider_application_id_secret.png +++ b/doc/integration/img/oauth_provider_application_id_secret.png diff --git a/doc/integration/img/oauth_provider_authorized_application.png b/doc/integration/img/oauth_provider_authorized_application.png Binary files differindex efc3b807d71..6a2ea09073c 100644 --- a/doc/integration/img/oauth_provider_authorized_application.png +++ b/doc/integration/img/oauth_provider_authorized_application.png diff --git a/doc/integration/img/oauth_provider_user_wide_applications.png b/doc/integration/img/oauth_provider_user_wide_applications.png Binary files differindex 45ad8a6d468..0c7b095a2dd 100644 --- a/doc/integration/img/oauth_provider_user_wide_applications.png +++ b/doc/integration/img/oauth_provider_user_wide_applications.png diff --git a/doc/integration/img/twitter_app_api_keys.png b/doc/integration/img/twitter_app_api_keys.png Binary files differindex 1076337172a..15b29ac7d16 100644 --- a/doc/integration/img/twitter_app_api_keys.png +++ b/doc/integration/img/twitter_app_api_keys.png diff --git a/doc/integration/img/twitter_app_details.png b/doc/integration/img/twitter_app_details.png Binary files differindex b95e8af8a74..323112a88bb 100644 --- a/doc/integration/img/twitter_app_details.png +++ b/doc/integration/img/twitter_app_details.png diff --git a/doc/markdown/img/logo.png b/doc/markdown/img/logo.png Binary files differindex 7da5f23ed9b..05c8b0d0ccf 100644 --- a/doc/markdown/img/logo.png +++ b/doc/markdown/img/logo.png diff --git a/doc/monitoring/img/health_check_token.png b/doc/monitoring/img/health_check_token.png Binary files differindex 2daf8606b00..2d7c82a65a8 100644 --- a/doc/monitoring/img/health_check_token.png +++ b/doc/monitoring/img/health_check_token.png diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md index 168bd85c26a..7947b0fedc4 100644 --- a/doc/monitoring/performance/grafana_configuration.md +++ b/doc/monitoring/performance/grafana_configuration.md @@ -44,70 +44,32 @@ on a separate server) ## Apply retention policies and create continuous queries -If you intend to import the GitLab provided Grafana dashboards, you will need -to copy and run a set of queries against InfluxDB to create the needed data -sets. +If you intend to import the GitLab provided Grafana dashboards, you will need to +set up the right retention policies and continuous queries. The easiest way of +doing this is by using the [influxdb-management](https://gitlab.com/gitlab-org/influxdb-management) +repository. -On the InfluxDB server, run the following command, substituting your InfluxDB -user and password: +To use this repository you must first clone it: -```bash -influxdb --username admin -password super_secret +``` +git clone https://gitlab.com/gitlab-org/influxdb-management.git +cd influxdb-management ``` -This will drop you in to an InfluxDB interactive session. Copy the entire -contents below and paste it in to the interactive session: +Next you must install the required dependencies: ``` -CREATE RETENTION POLICY default ON gitlab DURATION 1h REPLICATION 1 DEFAULT -CREATE RETENTION POLICY downsampled ON gitlab DURATION 7d REPLICATION 1 -CREATE CONTINUOUS QUERY grape_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY grape_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY grape_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.rails_file_descriptor_counts FROM gitlab."default".rails_file_descriptors GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.rails_gc_counts FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.rails_gc_timings FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.rails_memory_usage_overall FROM gitlab."default".rails_memory_usage GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY rails_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY rails_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_overall FROM gitlab."default".rails_object_counts GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_per_type FROM gitlab."default".rails_object_counts GROUP BY time(1m), type END; -CREATE CONTINUOUS QUERY rails_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_view_timings_per_action_and_view FROM gitlab."default".rails_views WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action, view END; -CREATE CONTINUOUS QUERY sidekiq_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.sidekiq_file_descriptor_counts FROM gitlab."default".sidekiq_file_descriptors GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.sidekiq_gc_counts FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.sidekiq_gc_timings FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_git_timings_per_action FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_overall FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_per_action FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_markdown_timings_overall FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.sidekiq_memory_usage_overall FROM gitlab."default".sidekiq_memory_usage GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_action_and_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY sidekiq_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_overall FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_per_type FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m), type END; -CREATE CONTINUOUS QUERY sidekiq_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_view_timings_per_action_and_view FROM gitlab."default".sidekiq_views GROUP BY time(1m), action, view END; -CREATE CONTINUOUS QUERY web_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.web_transaction_counts_overall FROM gitlab."default".rails_transactions GROUP BY time(1m) END; +gem install bundler +bundle install ``` +Now you must configure the repository by first copying `.env.example` to `.env` +and then editing the `.env` file to contain the correct InfluxDB settings. Once +configured you can simply run `bundle exec rake` and the InfluxDB database will +be configured for you. + +For more information see the [influxdb-management README](https://gitlab.com/gitlab-org/influxdb-management/blob/master/README.md). + ## Import Dashboards You can now import a set of default dashboards that will give you a good diff --git a/doc/monitoring/performance/img/grafana_dashboard_dropdown.png b/doc/monitoring/performance/img/grafana_dashboard_dropdown.png Binary files differindex b4448c7a09f..7e34fad71ce 100644 --- a/doc/monitoring/performance/img/grafana_dashboard_dropdown.png +++ b/doc/monitoring/performance/img/grafana_dashboard_dropdown.png diff --git a/doc/monitoring/performance/img/grafana_dashboard_import.png b/doc/monitoring/performance/img/grafana_dashboard_import.png Binary files differindex 5a2d3c0937a..f97624365c7 100644 --- a/doc/monitoring/performance/img/grafana_dashboard_import.png +++ b/doc/monitoring/performance/img/grafana_dashboard_import.png diff --git a/doc/monitoring/performance/img/grafana_data_source_configuration.png b/doc/monitoring/performance/img/grafana_data_source_configuration.png Binary files differindex 7e2e111f570..7d50e4c88c2 100644 --- a/doc/monitoring/performance/img/grafana_data_source_configuration.png +++ b/doc/monitoring/performance/img/grafana_data_source_configuration.png diff --git a/doc/monitoring/performance/img/grafana_data_source_empty.png b/doc/monitoring/performance/img/grafana_data_source_empty.png Binary files differindex 11e27571e64..aa39a53acae 100644 --- a/doc/monitoring/performance/img/grafana_data_source_empty.png +++ b/doc/monitoring/performance/img/grafana_data_source_empty.png diff --git a/doc/monitoring/performance/img/grafana_save_icon.png b/doc/monitoring/performance/img/grafana_save_icon.png Binary files differindex 3d4265bee8e..c740e33cd1c 100644 --- a/doc/monitoring/performance/img/grafana_save_icon.png +++ b/doc/monitoring/performance/img/grafana_save_icon.png diff --git a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png Binary files differindex 14d82b6ac98..e6ed45a0386 100644 --- a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png +++ b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png diff --git a/doc/profile/2fa.png b/doc/profile/2fa.png Binary files differindex bbf415210d5..bb464efa685 100644 --- a/doc/profile/2fa.png +++ b/doc/profile/2fa.png diff --git a/doc/profile/2fa_auth.png b/doc/profile/2fa_auth.png Binary files differindex 4a4fbe68984..0caaed10805 100644 --- a/doc/profile/2fa_auth.png +++ b/doc/profile/2fa_auth.png diff --git a/doc/project_services/bugzilla.md b/doc/project_services/bugzilla.md new file mode 100644 index 00000000000..215ed6fe9cc --- /dev/null +++ b/doc/project_services/bugzilla.md @@ -0,0 +1,17 @@ +# Bugzilla Service + +Go to your project's **Settings > Services > Bugzilla** and fill in the required +details as described in the table below. + +| Field | Description | +| ----- | ----------- | +| `description` | A name for the issue tracker (to differentiate between instances, for example) | +| `project_url` | The URL to the project in Bugzilla which is being linked to this GitLab project. Note that the `project_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | +| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | +| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | + +Once you have configured and enabled Bugzilla: + +- the **Issues** link on the GitLab project pages takes you to the appropriate + Bugzilla product page +- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue diff --git a/doc/project_services/img/builds_emails_service.png b/doc/project_services/img/builds_emails_service.png Binary files differindex e604dd73ffa..88943dc410e 100644 --- a/doc/project_services/img/builds_emails_service.png +++ b/doc/project_services/img/builds_emails_service.png diff --git a/doc/project_services/img/jira_add_gitlab_commit_message.png b/doc/project_services/img/jira_add_gitlab_commit_message.png Binary files differindex 85e54861b3e..aec472b9118 100644 --- a/doc/project_services/img/jira_add_gitlab_commit_message.png +++ b/doc/project_services/img/jira_add_gitlab_commit_message.png diff --git a/doc/project_services/img/jira_add_user_to_group.png b/doc/project_services/img/jira_add_user_to_group.png Binary files differindex e4576433889..0ba737bda9a 100644 --- a/doc/project_services/img/jira_add_user_to_group.png +++ b/doc/project_services/img/jira_add_user_to_group.png diff --git a/doc/project_services/img/jira_create_new_group.png b/doc/project_services/img/jira_create_new_group.png Binary files differindex edaa1326058..0609060cb05 100644 --- a/doc/project_services/img/jira_create_new_group.png +++ b/doc/project_services/img/jira_create_new_group.png diff --git a/doc/project_services/img/jira_create_new_group_name.png b/doc/project_services/img/jira_create_new_group_name.png Binary files differindex 9e518ad7843..53d77b17df0 100644 --- a/doc/project_services/img/jira_create_new_group_name.png +++ b/doc/project_services/img/jira_create_new_group_name.png diff --git a/doc/project_services/img/jira_create_new_user.png b/doc/project_services/img/jira_create_new_user.png Binary files differindex 57e433dd818..9eaa444ed25 100644 --- a/doc/project_services/img/jira_create_new_user.png +++ b/doc/project_services/img/jira_create_new_user.png diff --git a/doc/project_services/img/jira_group_access.png b/doc/project_services/img/jira_group_access.png Binary files differindex 47716ca6d0e..8d4657427ae 100644 --- a/doc/project_services/img/jira_group_access.png +++ b/doc/project_services/img/jira_group_access.png diff --git a/doc/project_services/img/jira_issue_closed.png b/doc/project_services/img/jira_issue_closed.png Binary files differindex cabec1ae137..acdd83702d3 100644 --- a/doc/project_services/img/jira_issue_closed.png +++ b/doc/project_services/img/jira_issue_closed.png diff --git a/doc/project_services/img/jira_issue_reference.png b/doc/project_services/img/jira_issue_reference.png Binary files differindex 15739a22dc7..1a2d9f04a6c 100644 --- a/doc/project_services/img/jira_issue_reference.png +++ b/doc/project_services/img/jira_issue_reference.png diff --git a/doc/project_services/img/jira_issues_workflow.png b/doc/project_services/img/jira_issues_workflow.png Binary files differindex 28e17be3a84..0703081d77b 100644 --- a/doc/project_services/img/jira_issues_workflow.png +++ b/doc/project_services/img/jira_issues_workflow.png diff --git a/doc/project_services/img/jira_merge_request_close.png b/doc/project_services/img/jira_merge_request_close.png Binary files differindex 1e78daf105f..47785e3ba27 100644 --- a/doc/project_services/img/jira_merge_request_close.png +++ b/doc/project_services/img/jira_merge_request_close.png diff --git a/doc/project_services/img/jira_project_name.png b/doc/project_services/img/jira_project_name.png Binary files differindex 5986fdb63fb..e785ec6140d 100644 --- a/doc/project_services/img/jira_project_name.png +++ b/doc/project_services/img/jira_project_name.png diff --git a/doc/project_services/img/jira_reference_commit_message_in_jira_issue.png b/doc/project_services/img/jira_reference_commit_message_in_jira_issue.png Binary files differindex 0149181dc86..fb270d85e3c 100644 --- a/doc/project_services/img/jira_reference_commit_message_in_jira_issue.png +++ b/doc/project_services/img/jira_reference_commit_message_in_jira_issue.png diff --git a/doc/project_services/img/jira_service.png b/doc/project_services/img/jira_service.png Binary files differindex 1f6628c4371..13aefce6f84 100644 --- a/doc/project_services/img/jira_service.png +++ b/doc/project_services/img/jira_service.png diff --git a/doc/project_services/img/jira_service_close_issue.png b/doc/project_services/img/jira_service_close_issue.png Binary files differindex 67dfc6144c4..eed69e80d2c 100644 --- a/doc/project_services/img/jira_service_close_issue.png +++ b/doc/project_services/img/jira_service_close_issue.png diff --git a/doc/project_services/img/jira_service_page.png b/doc/project_services/img/jira_service_page.png Binary files differindex c225daa81e1..a5b49c501ba 100644 --- a/doc/project_services/img/jira_service_page.png +++ b/doc/project_services/img/jira_service_page.png diff --git a/doc/project_services/img/jira_submit_gitlab_merge_request.png b/doc/project_services/img/jira_submit_gitlab_merge_request.png Binary files differindex e935d9362aa..77630d39d39 100644 --- a/doc/project_services/img/jira_submit_gitlab_merge_request.png +++ b/doc/project_services/img/jira_submit_gitlab_merge_request.png diff --git a/doc/project_services/img/jira_user_management_link.png b/doc/project_services/img/jira_user_management_link.png Binary files differindex 2745916972c..5f002b59bac 100644 --- a/doc/project_services/img/jira_user_management_link.png +++ b/doc/project_services/img/jira_user_management_link.png diff --git a/doc/project_services/img/jira_workflow_screenshot.png b/doc/project_services/img/jira_workflow_screenshot.png Binary files differindex 8635a32eb68..937a50a77d9 100644 --- a/doc/project_services/img/jira_workflow_screenshot.png +++ b/doc/project_services/img/jira_workflow_screenshot.png diff --git a/doc/project_services/img/redmine_configuration.png b/doc/project_services/img/redmine_configuration.png Binary files differindex d14e526ad33..e9d8c0d2da8 100644 --- a/doc/project_services/img/redmine_configuration.png +++ b/doc/project_services/img/redmine_configuration.png diff --git a/doc/project_services/img/services_templates_redmine_example.png b/doc/project_services/img/services_templates_redmine_example.png Binary files differindex 384d057fc8e..77c2b98e5d0 100644 --- a/doc/project_services/img/services_templates_redmine_example.png +++ b/doc/project_services/img/services_templates_redmine_example.png diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index f81a035f70b..e15d5db3253 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -30,6 +30,7 @@ further configuration instructions and details. Contributions are welcome. | [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | | Buildkite | Continuous integration and deployments | | [Builds emails](builds_emails.md) | Email the builds status to a list of recipients | +| [Bugzilla](bugzilla.md) | Bugzilla issue tracker | | Campfire | Simple web-based real-time group chat | | Custom Issue Tracker | Custom issue tracker | | Drone CI | Continuous Integration platform built on Docker, written in Go | diff --git a/doc/raketasks/backup_hrz.png b/doc/raketasks/backup_hrz.png Binary files differindex 03e50df1d76..42084717ebe 100644 --- a/doc/raketasks/backup_hrz.png +++ b/doc/raketasks/backup_hrz.png diff --git a/doc/raketasks/check_repos_output.png b/doc/raketasks/check_repos_output.png Binary files differindex 916b1685101..1f632566b00 100644 --- a/doc/raketasks/check_repos_output.png +++ b/doc/raketasks/check_repos_output.png diff --git a/doc/security/img/two_factor_authentication_settings.png b/doc/security/img/two_factor_authentication_settings.png Binary files differindex aa51ce030bb..6af5feabb13 100644 --- a/doc/security/img/two_factor_authentication_settings.png +++ b/doc/security/img/two_factor_authentication_settings.png diff --git a/doc/update/8.8-to-8.9.md b/doc/update/8.8-to-8.9.md index f14046bb4be..423140a92c7 100644 --- a/doc/update/8.8-to-8.9.md +++ b/doc/update/8.8-to-8.9.md @@ -122,6 +122,19 @@ 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-9-stable/lib/support/init.d/gitlab.default.example#L37 +#### SMTP configuration + +If you're installing from source and use SMTP to deliver mail, you will need to add the following line +to config/initializers/smtp_settings.rb: + +```ruby +ActionMailer::Base.delivery_method = :smtp +``` + +See [smtp_settings.rb.sample] as an example. + +[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/v8.9.0/config/initializers/smtp_settings.rb.sample#L13 + #### Init script Ensure you're still up-to-date with the latest init script changes: diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md new file mode 100644 index 00000000000..73a2d176b54 --- /dev/null +++ b/doc/user/project/highlighting.md @@ -0,0 +1,31 @@ +[Rouge]: https://rubygems.org/gems/rouge + +# Syntax Highlighting + +GitLab provides syntax highlighting on all files and snippets through the [Rouge][] rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient. + +If GitLab is guessing wrong, you can override its choice of language using the `gitlab-language` attribute in `.gitattributes`. For example, if you are working in a Prolog project and using the `.pl` file extension (which would normally be highlighted as Perl), you can add the following to your `.gitattributes` file: + +``` conf +*.pl gitlab-language=prolog +``` + +When you check in and push that change, all `*.pl` files in your project will be highlighted as Prolog. + +The paths here are simply git's builtin [`.gitattributes` interface](https://git-scm.com/docs/gitattributes). So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is: + +``` conf +/Nicefile gitlab-language=ruby +``` + +To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through CGI options, such as: + +``` conf +# json with erb in it +/my-cool-file gitlab-language=erb?parent=json + +# an entire file of highlighting errors! +/other-file gitlab-language=text?token=Error +``` + +Please note that these configurations will only take effect when the `.gitattributes` file is in your default branch (usually `master`). diff --git a/doc/web_hooks/ssl.png b/doc/web_hooks/ssl.png Binary files differindex 698f1a0f64a..8c4f08d1825 100644 --- a/doc/web_hooks/ssl.png +++ b/doc/web_hooks/ssl.png diff --git a/doc/workflow/add-user/img/add_new_user_to_project_settings.png b/doc/workflow/add-user/img/add_new_user_to_project_settings.png Binary files differindex 3da18cdae53..5da0552f9d6 100644 --- a/doc/workflow/add-user/img/add_new_user_to_project_settings.png +++ b/doc/workflow/add-user/img/add_new_user_to_project_settings.png diff --git a/doc/workflow/add-user/img/add_user_email_accept.png b/doc/workflow/add-user/img/add_user_email_accept.png Binary files differindex 18aabf93d50..a2954ad7c37 100644 --- a/doc/workflow/add-user/img/add_user_email_accept.png +++ b/doc/workflow/add-user/img/add_user_email_accept.png diff --git a/doc/workflow/add-user/img/add_user_email_ready.png b/doc/workflow/add-user/img/add_user_email_ready.png Binary files differindex 385d64330c0..19d91bc0999 100644 --- a/doc/workflow/add-user/img/add_user_email_ready.png +++ b/doc/workflow/add-user/img/add_user_email_ready.png diff --git a/doc/workflow/add-user/img/add_user_email_search.png b/doc/workflow/add-user/img/add_user_email_search.png Binary files differindex 84741edbca4..cb31b77d941 100644 --- a/doc/workflow/add-user/img/add_user_email_search.png +++ b/doc/workflow/add-user/img/add_user_email_search.png diff --git a/doc/workflow/add-user/img/add_user_give_permissions.png b/doc/workflow/add-user/img/add_user_give_permissions.png Binary files differindex 7e580384e54..e6b77022f06 100644 --- a/doc/workflow/add-user/img/add_user_give_permissions.png +++ b/doc/workflow/add-user/img/add_user_give_permissions.png diff --git a/doc/workflow/add-user/img/add_user_import_members_from_another_project.png b/doc/workflow/add-user/img/add_user_import_members_from_another_project.png Binary files differindex 8dbd73a5bc8..1068589c5ff 100644 --- a/doc/workflow/add-user/img/add_user_import_members_from_another_project.png +++ b/doc/workflow/add-user/img/add_user_import_members_from_another_project.png diff --git a/doc/workflow/add-user/img/add_user_imported_members.png b/doc/workflow/add-user/img/add_user_imported_members.png Binary files differindex abac1f59c02..5cd120a4245 100644 --- a/doc/workflow/add-user/img/add_user_imported_members.png +++ b/doc/workflow/add-user/img/add_user_imported_members.png diff --git a/doc/workflow/add-user/img/add_user_list_members.png b/doc/workflow/add-user/img/add_user_list_members.png Binary files differindex e17d88c6f5f..5fe3482192e 100644 --- a/doc/workflow/add-user/img/add_user_list_members.png +++ b/doc/workflow/add-user/img/add_user_list_members.png diff --git a/doc/workflow/add-user/img/add_user_members_menu.png b/doc/workflow/add-user/img/add_user_members_menu.png Binary files differindex ec5d39f402d..340d15c9830 100644 --- a/doc/workflow/add-user/img/add_user_members_menu.png +++ b/doc/workflow/add-user/img/add_user_members_menu.png diff --git a/doc/workflow/add-user/img/add_user_search_people.png b/doc/workflow/add-user/img/add_user_search_people.png Binary files differindex eaa062376f4..1c05d70ca31 100644 --- a/doc/workflow/add-user/img/add_user_search_people.png +++ b/doc/workflow/add-user/img/add_user_search_people.png diff --git a/doc/workflow/award_emoji.png b/doc/workflow/award_emoji.png Binary files differindex 3408ed95841..481680af80c 100644 --- a/doc/workflow/award_emoji.png +++ b/doc/workflow/award_emoji.png diff --git a/doc/workflow/ci_mr.png b/doc/workflow/ci_mr.png Binary files differindex a577356f8e8..f8a7708643e 100644 --- a/doc/workflow/ci_mr.png +++ b/doc/workflow/ci_mr.png diff --git a/doc/workflow/close_issue_mr.png b/doc/workflow/close_issue_mr.png Binary files differindex a136d642e12..5e520240233 100644 --- a/doc/workflow/close_issue_mr.png +++ b/doc/workflow/close_issue_mr.png diff --git a/doc/workflow/environment_branches.png b/doc/workflow/environment_branches.png Binary files differindex ee893ced13b..13fb0478eaa 100644 --- a/doc/workflow/environment_branches.png +++ b/doc/workflow/environment_branches.png diff --git a/doc/workflow/forking/branch_select.png b/doc/workflow/forking/branch_select.png Binary files differindex 275f64d113b..7f19414f3a9 100644 --- a/doc/workflow/forking/branch_select.png +++ b/doc/workflow/forking/branch_select.png diff --git a/doc/workflow/forking/merge_request.png b/doc/workflow/forking/merge_request.png Binary files differindex 2dc00ed08a1..e2da42a2be7 100644 --- a/doc/workflow/forking/merge_request.png +++ b/doc/workflow/forking/merge_request.png diff --git a/doc/workflow/four_stages.png b/doc/workflow/four_stages.png Binary files differindex 2f444fc6f79..49413087dca 100644 --- a/doc/workflow/four_stages.png +++ b/doc/workflow/four_stages.png diff --git a/doc/workflow/git_pull.png b/doc/workflow/git_pull.png Binary files differindex 7d47064eb14..9a1fdf899bf 100644 --- a/doc/workflow/git_pull.png +++ b/doc/workflow/git_pull.png diff --git a/doc/workflow/gitdashflow.png b/doc/workflow/gitdashflow.png Binary files differindex f2f091dd10b..e456cf9309d 100644 --- a/doc/workflow/gitdashflow.png +++ b/doc/workflow/gitdashflow.png diff --git a/doc/workflow/github_flow.png b/doc/workflow/github_flow.png Binary files differindex 88addb623ee..b3fca97cc2d 100644 --- a/doc/workflow/github_flow.png +++ b/doc/workflow/github_flow.png diff --git a/doc/workflow/gitlab_flow.png b/doc/workflow/gitlab_flow.png Binary files differindex 1ea191a672b..d85d4ff374e 100644 --- a/doc/workflow/gitlab_flow.png +++ b/doc/workflow/gitlab_flow.png diff --git a/doc/workflow/good_commit.png b/doc/workflow/good_commit.png Binary files differindex 3737a026644..7958feea4d9 100644 --- a/doc/workflow/good_commit.png +++ b/doc/workflow/good_commit.png diff --git a/doc/workflow/groups/add_member_to_group.png b/doc/workflow/groups/add_member_to_group.png Binary files differindex fa340ce572f..6e3f660d2e4 100644 --- a/doc/workflow/groups/add_member_to_group.png +++ b/doc/workflow/groups/add_member_to_group.png diff --git a/doc/workflow/groups/group_dashboard.png b/doc/workflow/groups/group_dashboard.png Binary files differindex 7fc9048d74d..662c932e536 100644 --- a/doc/workflow/groups/group_dashboard.png +++ b/doc/workflow/groups/group_dashboard.png diff --git a/doc/workflow/groups/group_with_two_projects.png b/doc/workflow/groups/group_with_two_projects.png Binary files differindex 87242781e4f..dc3475949f5 100644 --- a/doc/workflow/groups/group_with_two_projects.png +++ b/doc/workflow/groups/group_with_two_projects.png diff --git a/doc/workflow/groups/max_access_level.png b/doc/workflow/groups/max_access_level.png Binary files differindex 71106a8a5a0..2855a514013 100644 --- a/doc/workflow/groups/max_access_level.png +++ b/doc/workflow/groups/max_access_level.png diff --git a/doc/workflow/groups/new_group_button.png b/doc/workflow/groups/new_group_button.png Binary files differindex 51e82798658..26136312c8f 100644 --- a/doc/workflow/groups/new_group_button.png +++ b/doc/workflow/groups/new_group_button.png diff --git a/doc/workflow/groups/new_group_form.png b/doc/workflow/groups/new_group_form.png Binary files differindex bf992c40bc2..dc50a069ef2 100644 --- a/doc/workflow/groups/new_group_form.png +++ b/doc/workflow/groups/new_group_form.png diff --git a/doc/workflow/groups/other_group_sees_shared_project.png b/doc/workflow/groups/other_group_sees_shared_project.png Binary files differindex cbf2c3c1fdc..2230720cecd 100644 --- a/doc/workflow/groups/other_group_sees_shared_project.png +++ b/doc/workflow/groups/other_group_sees_shared_project.png diff --git a/doc/workflow/groups/override_access_level.png b/doc/workflow/groups/override_access_level.png Binary files differindex f4225a63679..9d6aaf4c363 100644 --- a/doc/workflow/groups/override_access_level.png +++ b/doc/workflow/groups/override_access_level.png diff --git a/doc/workflow/groups/project_members_via_group.png b/doc/workflow/groups/project_members_via_group.png Binary files differindex b13cb1cfd95..58270936a0b 100644 --- a/doc/workflow/groups/project_members_via_group.png +++ b/doc/workflow/groups/project_members_via_group.png diff --git a/doc/workflow/groups/share_project_with_groups.png b/doc/workflow/groups/share_project_with_groups.png Binary files differindex a5dbc89fe90..5772d4deced 100644 --- a/doc/workflow/groups/share_project_with_groups.png +++ b/doc/workflow/groups/share_project_with_groups.png diff --git a/doc/workflow/groups/transfer_project.png b/doc/workflow/groups/transfer_project.png Binary files differindex 044fe10d073..0aef3ab3f0f 100644 --- a/doc/workflow/groups/transfer_project.png +++ b/doc/workflow/groups/transfer_project.png diff --git a/doc/workflow/img/award_emoji_select.png b/doc/workflow/img/award_emoji_select.png Binary files differindex fffdfedda5d..ad664c0aeff 100644 --- a/doc/workflow/img/award_emoji_select.png +++ b/doc/workflow/img/award_emoji_select.png diff --git a/doc/workflow/img/award_emoji_votes_least_popular.png b/doc/workflow/img/award_emoji_votes_least_popular.png Binary files differindex 2ef5be7154f..57d595d9602 100644 --- a/doc/workflow/img/award_emoji_votes_least_popular.png +++ b/doc/workflow/img/award_emoji_votes_least_popular.png diff --git a/doc/workflow/img/award_emoji_votes_most_popular.png b/doc/workflow/img/award_emoji_votes_most_popular.png Binary files differindex 5b089730d93..432bd09b8a7 100644 --- a/doc/workflow/img/award_emoji_votes_most_popular.png +++ b/doc/workflow/img/award_emoji_votes_most_popular.png diff --git a/doc/workflow/img/award_emoji_votes_sort_options.png b/doc/workflow/img/award_emoji_votes_sort_options.png Binary files differindex 9bbf3f82a0b..ae6e224b317 100644 --- a/doc/workflow/img/award_emoji_votes_sort_options.png +++ b/doc/workflow/img/award_emoji_votes_sort_options.png diff --git a/doc/workflow/img/cherry_pick_changes_commit.png b/doc/workflow/img/cherry_pick_changes_commit.png Binary files differindex ae91d2cae53..7fb68cc9e9b 100644 --- a/doc/workflow/img/cherry_pick_changes_commit.png +++ b/doc/workflow/img/cherry_pick_changes_commit.png diff --git a/doc/workflow/img/cherry_pick_changes_commit_modal.png b/doc/workflow/img/cherry_pick_changes_commit_modal.png Binary files differindex f502f87677a..5267e04562f 100644 --- a/doc/workflow/img/cherry_pick_changes_commit_modal.png +++ b/doc/workflow/img/cherry_pick_changes_commit_modal.png diff --git a/doc/workflow/img/cherry_pick_changes_mr.png b/doc/workflow/img/cherry_pick_changes_mr.png Binary files differindex 59c610e620b..975fb13e463 100644 --- a/doc/workflow/img/cherry_pick_changes_mr.png +++ b/doc/workflow/img/cherry_pick_changes_mr.png diff --git a/doc/workflow/img/cherry_pick_changes_mr_modal.png b/doc/workflow/img/cherry_pick_changes_mr_modal.png Binary files differindex 96a80f4726d..6c003bacbe3 100644 --- a/doc/workflow/img/cherry_pick_changes_mr_modal.png +++ b/doc/workflow/img/cherry_pick_changes_mr_modal.png diff --git a/doc/workflow/img/file_finder_find_button.png b/doc/workflow/img/file_finder_find_button.png Binary files differindex c5005d0d7ca..96e383f0213 100644 --- a/doc/workflow/img/file_finder_find_button.png +++ b/doc/workflow/img/file_finder_find_button.png diff --git a/doc/workflow/img/file_finder_find_file.png b/doc/workflow/img/file_finder_find_file.png Binary files differindex 58500f4c163..c6508514c76 100644 --- a/doc/workflow/img/file_finder_find_file.png +++ b/doc/workflow/img/file_finder_find_file.png diff --git a/doc/workflow/img/forking_workflow_choose_namespace.png b/doc/workflow/img/forking_workflow_choose_namespace.png Binary files differindex eefe5769554..1839d5e8be2 100644 --- a/doc/workflow/img/forking_workflow_choose_namespace.png +++ b/doc/workflow/img/forking_workflow_choose_namespace.png diff --git a/doc/workflow/img/forking_workflow_fork_button.png b/doc/workflow/img/forking_workflow_fork_button.png Binary files differindex 49e68d33e89..cc79d6fd40c 100644 --- a/doc/workflow/img/forking_workflow_fork_button.png +++ b/doc/workflow/img/forking_workflow_fork_button.png diff --git a/doc/workflow/img/forking_workflow_path_taken_error.png b/doc/workflow/img/forking_workflow_path_taken_error.png Binary files differindex 7a3139506fe..a859155aef0 100644 --- a/doc/workflow/img/forking_workflow_path_taken_error.png +++ b/doc/workflow/img/forking_workflow_path_taken_error.png diff --git a/doc/workflow/img/new_branch_from_issue.png b/doc/workflow/img/new_branch_from_issue.png Binary files differindex 394c139e17e..61acdd30ae9 100644 --- a/doc/workflow/img/new_branch_from_issue.png +++ b/doc/workflow/img/new_branch_from_issue.png diff --git a/doc/workflow/img/revert_changes_commit.png b/doc/workflow/img/revert_changes_commit.png Binary files differindex d84211e20db..e7194fc3504 100644 --- a/doc/workflow/img/revert_changes_commit.png +++ b/doc/workflow/img/revert_changes_commit.png diff --git a/doc/workflow/img/revert_changes_commit_modal.png b/doc/workflow/img/revert_changes_commit_modal.png Binary files differindex e94d151a2af..c660ec7eaec 100644 --- a/doc/workflow/img/revert_changes_commit_modal.png +++ b/doc/workflow/img/revert_changes_commit_modal.png diff --git a/doc/workflow/img/revert_changes_mr.png b/doc/workflow/img/revert_changes_mr.png Binary files differindex 7adad88463b..3002f0ac1c5 100644 --- a/doc/workflow/img/revert_changes_mr.png +++ b/doc/workflow/img/revert_changes_mr.png diff --git a/doc/workflow/img/revert_changes_mr_modal.png b/doc/workflow/img/revert_changes_mr_modal.png Binary files differindex 9da78f84828..c6aaeecc8a6 100644 --- a/doc/workflow/img/revert_changes_mr_modal.png +++ b/doc/workflow/img/revert_changes_mr_modal.png diff --git a/doc/workflow/img/todos_icon.png b/doc/workflow/img/todos_icon.png Binary files differindex a63bad0c258..bba77f88913 100644 --- a/doc/workflow/img/todos_icon.png +++ b/doc/workflow/img/todos_icon.png diff --git a/doc/workflow/img/todos_index.png b/doc/workflow/img/todos_index.png Binary files differindex 4ee18dd1285..f1438ef7355 100644 --- a/doc/workflow/img/todos_index.png +++ b/doc/workflow/img/todos_index.png diff --git a/doc/workflow/img/web_editor_new_branch_dropdown.png b/doc/workflow/img/web_editor_new_branch_dropdown.png Binary files differindex 009e4b05adf..a8e635d2faf 100644 --- a/doc/workflow/img/web_editor_new_branch_dropdown.png +++ b/doc/workflow/img/web_editor_new_branch_dropdown.png diff --git a/doc/workflow/img/web_editor_new_branch_page.png b/doc/workflow/img/web_editor_new_branch_page.png Binary files differindex dd6cfc6e7bb..7f36b7faf63 100644 --- a/doc/workflow/img/web_editor_new_branch_page.png +++ b/doc/workflow/img/web_editor_new_branch_page.png diff --git a/doc/workflow/img/web_editor_new_directory_dialog.png b/doc/workflow/img/web_editor_new_directory_dialog.png Binary files differindex 2c76f84f395..d16e3c67116 100644 --- a/doc/workflow/img/web_editor_new_directory_dialog.png +++ b/doc/workflow/img/web_editor_new_directory_dialog.png diff --git a/doc/workflow/img/web_editor_new_directory_dropdown.png b/doc/workflow/img/web_editor_new_directory_dropdown.png Binary files differindex cedf46aedfd..c8d77b16ee8 100644 --- a/doc/workflow/img/web_editor_new_directory_dropdown.png +++ b/doc/workflow/img/web_editor_new_directory_dropdown.png diff --git a/doc/workflow/img/web_editor_new_file_dropdown.png b/doc/workflow/img/web_editor_new_file_dropdown.png Binary files differindex 6e884f6504d..3fcb91c9b93 100644 --- a/doc/workflow/img/web_editor_new_file_dropdown.png +++ b/doc/workflow/img/web_editor_new_file_dropdown.png diff --git a/doc/workflow/img/web_editor_new_file_editor.png b/doc/workflow/img/web_editor_new_file_editor.png Binary files differindex c76473bcfa7..21c340b9288 100644 --- a/doc/workflow/img/web_editor_new_file_editor.png +++ b/doc/workflow/img/web_editor_new_file_editor.png diff --git a/doc/workflow/img/web_editor_new_push_widget.png b/doc/workflow/img/web_editor_new_push_widget.png Binary files differindex a2108735741..c7738a4c930 100644 --- a/doc/workflow/img/web_editor_new_push_widget.png +++ b/doc/workflow/img/web_editor_new_push_widget.png diff --git a/doc/workflow/img/web_editor_new_tag_dropdown.png b/doc/workflow/img/web_editor_new_tag_dropdown.png Binary files differindex 263dd635b95..ac7415009b3 100644 --- a/doc/workflow/img/web_editor_new_tag_dropdown.png +++ b/doc/workflow/img/web_editor_new_tag_dropdown.png diff --git a/doc/workflow/img/web_editor_new_tag_page.png b/doc/workflow/img/web_editor_new_tag_page.png Binary files differindex 64d7cd11ed1..231e1a13fc0 100644 --- a/doc/workflow/img/web_editor_new_tag_page.png +++ b/doc/workflow/img/web_editor_new_tag_page.png diff --git a/doc/workflow/img/web_editor_start_new_merge_request.png b/doc/workflow/img/web_editor_start_new_merge_request.png Binary files differindex be12a151cac..2755501dfd1 100644 --- a/doc/workflow/img/web_editor_start_new_merge_request.png +++ b/doc/workflow/img/web_editor_start_new_merge_request.png diff --git a/doc/workflow/img/web_editor_upload_file_dialog.png b/doc/workflow/img/web_editor_upload_file_dialog.png Binary files differindex 6dd2207bca0..9d6d8250bbe 100644 --- a/doc/workflow/img/web_editor_upload_file_dialog.png +++ b/doc/workflow/img/web_editor_upload_file_dialog.png diff --git a/doc/workflow/img/web_editor_upload_file_dropdown.png b/doc/workflow/img/web_editor_upload_file_dropdown.png Binary files differindex bf6528701b0..6b5205b05ec 100644 --- a/doc/workflow/img/web_editor_upload_file_dropdown.png +++ b/doc/workflow/img/web_editor_upload_file_dropdown.png diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png Binary files differindex 0e08703f421..1a5661de75d 100644 --- a/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png +++ b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png Binary files differindex 205c515bd3f..fd7a4d3fabf 100644 --- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png +++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png Binary files differindex a1e348d46ad..fd1ba6f5884 100644 --- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png +++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png Binary files differindex ed362846909..186c1563951 100644 --- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png +++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png Binary files differindex d2fbd0267bd..2f84d3232f2 100644 --- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png +++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png Binary files differindex b1cc4b58525..652ca20b9ab 100644 --- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png +++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png diff --git a/doc/workflow/importing/gitlab_importer/importer.png b/doc/workflow/importing/gitlab_importer/importer.png Binary files differindex d2a286d8cac..35a7ddc8318 100644 --- a/doc/workflow/importing/gitlab_importer/importer.png +++ b/doc/workflow/importing/gitlab_importer/importer.png diff --git a/doc/workflow/importing/gitlab_importer/new_project_page.png b/doc/workflow/importing/gitlab_importer/new_project_page.png Binary files differindex 5e239208e1e..81074d2d016 100644 --- a/doc/workflow/importing/gitlab_importer/new_project_page.png +++ b/doc/workflow/importing/gitlab_importer/new_project_page.png diff --git a/doc/workflow/importing/img/import_projects_from_github_importer.png b/doc/workflow/importing/img/import_projects_from_github_importer.png Binary files differindex f744dc06f81..b6ed8dd692a 100644 --- a/doc/workflow/importing/img/import_projects_from_github_importer.png +++ b/doc/workflow/importing/img/import_projects_from_github_importer.png diff --git a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png Binary files differindex 86be35acb37..c8f35a50f48 100644 --- a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png +++ b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png diff --git a/doc/workflow/merge_commits.png b/doc/workflow/merge_commits.png Binary files differindex 757b589d0db..8aa1587cde6 100644 --- a/doc/workflow/merge_commits.png +++ b/doc/workflow/merge_commits.png diff --git a/doc/workflow/merge_request.png b/doc/workflow/merge_request.png Binary files differindex fde3ff5c854..6aad1d82f6e 100644 --- a/doc/workflow/merge_request.png +++ b/doc/workflow/merge_request.png diff --git a/doc/workflow/merge_requests/commit_compare.png b/doc/workflow/merge_requests/commit_compare.png Binary files differindex dfd7ee220f0..0e4a2b23c04 100644 --- a/doc/workflow/merge_requests/commit_compare.png +++ b/doc/workflow/merge_requests/commit_compare.png diff --git a/doc/workflow/merge_requests/merge_request_diff.png b/doc/workflow/merge_requests/merge_request_diff.png Binary files differindex f368423c746..3ebbfb75ea3 100644 --- a/doc/workflow/merge_requests/merge_request_diff.png +++ b/doc/workflow/merge_requests/merge_request_diff.png diff --git a/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png Binary files differindex b2d03bb66f9..a0db535019c 100644 --- a/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png +++ b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png diff --git a/doc/workflow/merge_when_build_succeeds/enable.png b/doc/workflow/merge_when_build_succeeds/enable.png Binary files differindex 633efa1246f..b86e6d7b3fd 100644 --- a/doc/workflow/merge_when_build_succeeds/enable.png +++ b/doc/workflow/merge_when_build_succeeds/enable.png diff --git a/doc/workflow/merge_when_build_succeeds/status.png b/doc/workflow/merge_when_build_succeeds/status.png Binary files differindex c856c7d14dc..f3ea61d8147 100644 --- a/doc/workflow/merge_when_build_succeeds/status.png +++ b/doc/workflow/merge_when_build_succeeds/status.png diff --git a/doc/workflow/messy_flow.png b/doc/workflow/messy_flow.png Binary files differindex 1addb95ca54..8d2c0dae8c2 100644 --- a/doc/workflow/messy_flow.png +++ b/doc/workflow/messy_flow.png diff --git a/doc/workflow/milestones/form.png b/doc/workflow/milestones/form.png Binary files differindex de44c1ffc1a..3965ca4d083 100644 --- a/doc/workflow/milestones/form.png +++ b/doc/workflow/milestones/form.png diff --git a/doc/workflow/milestones/group_form.png b/doc/workflow/milestones/group_form.png Binary files differindex 38862dcca68..ff20df8081f 100644 --- a/doc/workflow/milestones/group_form.png +++ b/doc/workflow/milestones/group_form.png diff --git a/doc/workflow/mr_inline_comments.png b/doc/workflow/mr_inline_comments.png Binary files differindex e851b95bcef..af7df3100d0 100644 --- a/doc/workflow/mr_inline_comments.png +++ b/doc/workflow/mr_inline_comments.png diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md index fe4485e148a..b4a9c2f3d3e 100644 --- a/doc/workflow/notifications.md +++ b/doc/workflow/notifications.md @@ -37,12 +37,14 @@ This means that you can set a different level of notifications per group while s to have a finer level setting per project. Organization like this is suitable for users that belong to different groups but don't have the same need for being notified for every group they are member of. +These settings can be configured on group page or user profile notifications dropdown. #### Project Settings Project Settings are at the top level and any setting placed at this level will take precedence of any other setting. This is suitable for users that have different needs for notifications per project basis. +These settings can be configured on project page or user profile notifications dropdown. ## Notification events diff --git a/doc/workflow/notifications/settings.png b/doc/workflow/notifications/settings.png Binary files differindex 7c6857aad1a..d50757beffc 100644 --- a/doc/workflow/notifications/settings.png +++ b/doc/workflow/notifications/settings.png diff --git a/doc/workflow/production_branch.png b/doc/workflow/production_branch.png Binary files differindex 33fb26dd621..d88a3687151 100644 --- a/doc/workflow/production_branch.png +++ b/doc/workflow/production_branch.png diff --git a/doc/workflow/protected_branches/protected_branches1.png b/doc/workflow/protected_branches/protected_branches1.png Binary files differindex 5c2a3de5f70..bb3ab7d7913 100644 --- a/doc/workflow/protected_branches/protected_branches1.png +++ b/doc/workflow/protected_branches/protected_branches1.png diff --git a/doc/workflow/protected_branches/protected_branches2.png b/doc/workflow/protected_branches/protected_branches2.png Binary files differindex 2dca3541365..58ace31ac57 100644 --- a/doc/workflow/protected_branches/protected_branches2.png +++ b/doc/workflow/protected_branches/protected_branches2.png diff --git a/doc/workflow/rebase.png b/doc/workflow/rebase.png Binary files differindex ef82c834755..df353311fa0 100644 --- a/doc/workflow/rebase.png +++ b/doc/workflow/rebase.png diff --git a/doc/workflow/release_branches.png b/doc/workflow/release_branches.png Binary files differindex da7ae53413a..c2162248d25 100644 --- a/doc/workflow/release_branches.png +++ b/doc/workflow/release_branches.png diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png Binary files differindex e2b64bfe17f..2456a8500f4 100644 --- a/doc/workflow/releases/new_tag.png +++ b/doc/workflow/releases/new_tag.png diff --git a/doc/workflow/releases/tags.png b/doc/workflow/releases/tags.png Binary files differindex aca91906c68..eeda967afd6 100644 --- a/doc/workflow/releases/tags.png +++ b/doc/workflow/releases/tags.png diff --git a/doc/workflow/remove_checkbox.png b/doc/workflow/remove_checkbox.png Binary files differindex 3e247d38155..3b0393deb0f 100644 --- a/doc/workflow/remove_checkbox.png +++ b/doc/workflow/remove_checkbox.png diff --git a/doc/workflow/share_with_group.png b/doc/workflow/share_with_group.png Binary files differindex a0ca6f14552..2c47625e29a 100644 --- a/doc/workflow/share_with_group.png +++ b/doc/workflow/share_with_group.png diff --git a/doc/workflow/shortcuts.png b/doc/workflow/shortcuts.png Binary files differindex 16be0413b64..a9b1c4b4dcc 100644 --- a/doc/workflow/shortcuts.png +++ b/doc/workflow/shortcuts.png diff --git a/doc/workflow/wip_merge_requests/blocked_accept_button.png b/doc/workflow/wip_merge_requests/blocked_accept_button.png Binary files differindex 4791e5de972..89c458aa8d9 100644 --- a/doc/workflow/wip_merge_requests/blocked_accept_button.png +++ b/doc/workflow/wip_merge_requests/blocked_accept_button.png diff --git a/doc/workflow/wip_merge_requests/mark_as_wip.png b/doc/workflow/wip_merge_requests/mark_as_wip.png Binary files differindex 8fa83a201ac..9c37354a653 100644 --- a/doc/workflow/wip_merge_requests/mark_as_wip.png +++ b/doc/workflow/wip_merge_requests/mark_as_wip.png diff --git a/doc/workflow/wip_merge_requests/unmark_as_wip.png b/doc/workflow/wip_merge_requests/unmark_as_wip.png Binary files differindex d45e68f31c5..31f7326beb0 100644 --- a/doc/workflow/wip_merge_requests/unmark_as_wip.png +++ b/doc/workflow/wip_merge_requests/unmark_as_wip.png diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 29e6b9f1a01..31f8924c38c 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps end step 'I see "New Project" page' do - expect(page).to have_content('Project owner') + expect(page).to have_content('Project path') expect(page).to have_content('Project name') end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 483370f41c6..4fa7d7c6567 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -93,7 +93,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I should see new group "Owned" avatar' do expect(owned_group.avatar).to be_instance_of AvatarUploader - expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{Group.find_by(name:"Owned").id}/banana_sample.gif" + expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{Group.find_by(name: "Owned").id}/banana_sample.gif" end step 'I should see the "Remove avatar" button' do diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 0ead83d6937..6b56a77b832 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -153,6 +153,6 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps # Verify a link is generated against the correct project def verify_commit_link(container_div, container_project) # This should force a wait for the javascript to execute - expect(find(:div,container_div).find(".commit_short_id")['href']).to have_content "#{container_project.path_with_namespace}/commit" + expect(find(:div, container_div).find(".commit_short_id")['href']).to have_content "#{container_project.path_with_namespace}/commit" end end diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index 1b14659b4df..1498f899cf5 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -81,9 +81,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps end step 'I search "hand"' do - page.within('.emoji-menu-content') do - fill_in 'emoji_search', with: 'hand' - end + fill_in 'emoji_search', with: 'hand' end step 'I see search result for "hand"' do diff --git a/lib/api/api.rb b/lib/api/api.rb index f8f680a6311..c3fff8b2f8f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -39,7 +39,7 @@ module API mount ::API::Issues mount ::API::Keys mount ::API::Labels - mount ::API::Licenses + mount ::API::LicenseTemplates mount ::API::MergeRequests mount ::API::Milestones mount ::API::Namespaces diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 979328efe0e..086d8511e8f 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -33,10 +33,10 @@ module API get ':id/repository/commits/:sha/builds' do authorize_read_builds! - commit = user_project.pipelines.find_by_sha(params[:sha]) - return not_found! unless commit + return not_found! unless user_project.commit(params[:sha]) - builds = commit.builds.order('id DESC') + pipelines = user_project.pipelines.where(sha: params[:sha]) + builds = user_project.builds.where(pipeline: pipelines).order('id DESC') builds = filter_builds(builds, params[:scope]) present paginate(builds), with: Entities::Build, diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb index ab9b7c602b5..dbe5bb08d3f 100644 --- a/lib/api/group_members.rb +++ b/lib/api/group_members.rb @@ -77,7 +77,7 @@ module API member = group.group_members.find_by(user_id: params[:user_id]) if member.nil? - render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404) + render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}", 404) else member.destroy end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 4c43257c48a..8a03a41e9c5 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -59,6 +59,41 @@ module API end end + resource :groups do + # Get a list of group issues + # + # Parameters: + # id (required) - The ID of a group + # state (optional) - Return "opened" or "closed" issues + # labels (optional) - Comma-separated list of label names + # milestone (optional) - Milestone title + # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` + # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` + # + # Example Requests: + # GET /groups/:id/issues + # GET /groups/:id/issues?state=opened + # GET /groups/:id/issues?state=closed + # GET /groups/:id/issues?labels=foo + # GET /groups/:id/issues?labels=foo,bar + # GET /groups/:id/issues?labels=foo,bar&state=opened + # GET /groups/:id/issues?milestone=1.0.0 + # GET /groups/:id/issues?milestone=1.0.0&state=closed + get ":id/issues" do + group = find_group(params[:id]) + + params[:state] ||= 'opened' + params[:group_id] = group.id + params[:milestone_title] = params.delete(:milestone) + params[:label_name] = params.delete(:labels) + params[:sort] = "#{params.delete(:order_by)}_#{params.delete(:sort)}" if params[:order_by] && params[:sort] + + issues = IssuesFinder.new(current_user, params).execute + + present paginate(issues), with: Entities::Issue, current_user: current_user + end + end + resource :projects do # Get a list of project issues # diff --git a/lib/api/licenses.rb b/lib/api/license_templates.rb index be0e113fbcb..d0552299ed0 100644 --- a/lib/api/licenses.rb +++ b/lib/api/license_templates.rb @@ -1,6 +1,6 @@ module API - # Licenses API - class Licenses < Grape::API + # License Templates API + class LicenseTemplates < Grape::API PROJECT_TEMPLATE_REGEX = /[\<\{\[] (project|description| diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index ccd106860bd..8aa6f8f124a 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -9,6 +9,11 @@ module Banzai def call doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |img| + div = doc.document.create_element( + 'div', + class: 'image-container' + ) + link = doc.document.create_element( 'a', class: 'no-attachment-icon', @@ -17,7 +22,10 @@ module Banzai ) link.children = img.clone - img.replace(link) + + div.children = link + + img.replace(div) end doc diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 2614261f9eb..5351272f42d 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -31,10 +31,14 @@ module Banzai projects_per_reference.each do |path, project| issue_ids = references_per_project[path] - next unless project.default_issues_tracker? + if project.default_issues_tracker? + issues = project.issues.where(iid: issue_ids.to_a) + else + issues = issue_ids.map { |id| ExternalIssue.new(id, project) } + end - project.issues.where(iid: issue_ids.to_a).each do |issue| - hash[project][issue.iid] = issue + issues.each do |issue| + hash[project][issue.iid.to_i] = issue end end diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb index c753a84a20d..c59a80dd1c7 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/redactor_filter.rb @@ -7,40 +7,13 @@ module Banzai # class RedactorFilter < HTML::Pipeline::Filter def call - nodes = Querying.css(doc, 'a.gfm[data-reference-type]') - visible = nodes_visible_to_user(nodes) - - nodes.each do |node| - unless visible.include?(node) - # The reference should be replaced by the original text, - # which is not always the same as the rendered text. - text = node.attr('data-original') || node.text - node.replace(text) - end - end + Redactor.new(project, current_user).redact([doc]) doc end private - def nodes_visible_to_user(nodes) - per_type = Hash.new { |h, k| h[k] = [] } - visible = Set.new - - nodes.each do |node| - per_type[node.attr('data-reference-type')] << node - end - - per_type.each do |type, nodes| - parser = Banzai::ReferenceParser[type].new(project, current_user) - - visible.merge(parser.nodes_visible_to_user(current_user, nodes)) - end - - visible - end - def current_user context[:current_user] end diff --git a/lib/banzai/note_renderer.rb b/lib/banzai/note_renderer.rb new file mode 100644 index 00000000000..bab6a9934d1 --- /dev/null +++ b/lib/banzai/note_renderer.rb @@ -0,0 +1,22 @@ +module Banzai + module NoteRenderer + # Renders a collection of Note instances. + # + # notes - The notes to render. + # project - The project to use for rendering/redacting. + # user - The user viewing the notes. + # path - The request path. + # wiki - The project's wiki. + # git_ref - The current Git reference. + def self.render(notes, project, user = nil, path = nil, wiki = nil, git_ref = nil) + renderer = ObjectRenderer.new(project, + user, + requested_path: path, + project_wiki: wiki, + ref: git_ref, + pipeline: :note) + + renderer.render(notes, :note) + end + end +end diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb new file mode 100644 index 00000000000..f0e4f28bf12 --- /dev/null +++ b/lib/banzai/object_renderer.rb @@ -0,0 +1,85 @@ +module Banzai + # Class for rendering multiple objects (e.g. Note instances) in a single pass. + # + # Rendered Markdown is stored in an attribute in every object based on the + # name of the attribute containing the Markdown. For example, when the + # attribute `note` is rendered the HTML is stored in `note_html`. + class ObjectRenderer + attr_reader :project, :user + + # Make sure to set the appropriate pipeline in the `raw_context` attribute + # (e.g. `:note` for Note instances). + # + # project - A Project to use for rendering and redacting Markdown. + # user - The user viewing the Markdown/HTML documents, if any. + # context - A Hash containing extra attributes to use in the rendering + # pipeline. + def initialize(project, user = nil, raw_context = {}) + @project = project + @user = user + @raw_context = raw_context + end + + # Renders and redacts an Array of objects. + # + # objects - The objects to render + # attribute - The attribute containing the raw Markdown to render. + # + # Returns the same input objects. + def render(objects, attribute) + documents = render_objects(objects, attribute) + redacted = redact_documents(documents) + + objects.each_with_index do |object, index| + object.__send__("#{attribute}_html=", redacted.fetch(index)) + end + + objects + end + + # Renders the attribute of every given object. + def render_objects(objects, attribute) + objects.map do |object| + render_attribute(object, attribute) + end + end + + # Redacts the list of documents. + # + # Returns an Array containing the redacted documents. + def redact_documents(documents) + redactor = Redactor.new(project, user) + + redactor.redact(documents).map do |document| + document.to_html.html_safe + end + end + + # Returns a Banzai context for the given object and attribute. + def context_for(object, attribute) + context = base_context.merge(cache_key: [object, attribute]) + + if object.respond_to?(:author) + context[:author] = object.author + end + + context + end + + # Renders the attribute of an object. + # + # Returns a `Nokogiri::HTML::Document`. + def render_attribute(object, attribute) + context = context_for(object, attribute) + + string = object.__send__(attribute) + html = Banzai.render(string, context) + + Banzai::Pipeline[:relative_link].to_document(html, context) + end + + def base_context + @base_context ||= @raw_context.merge(current_user: user, project: project) + end + end +end diff --git a/lib/banzai/pipeline/relative_link_pipeline.rb b/lib/banzai/pipeline/relative_link_pipeline.rb new file mode 100644 index 00000000000..270990e7ab4 --- /dev/null +++ b/lib/banzai/pipeline/relative_link_pipeline.rb @@ -0,0 +1,11 @@ +module Banzai + module Pipeline + class RelativeLinkPipeline < BasePipeline + def self.filters + FilterArray[ + Filter::RelativeLinkFilter + ] + end + end + end +end diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb new file mode 100644 index 00000000000..ffd267d5e9a --- /dev/null +++ b/lib/banzai/redactor.rb @@ -0,0 +1,69 @@ +module Banzai + # Class for removing Markdown references a certain user is not allowed to + # view. + class Redactor + attr_reader :user, :project + + # project - A Project to use for redacting links. + # user - The currently logged in user (if any). + def initialize(project, user = nil) + @project = project + @user = user + end + + # Redacts the references in the given Array of documents. + # + # This method modifies the given documents in-place. + # + # documents - A list of HTML documents containing references to redact. + # + # Returns the documents passed as the first argument. + def redact(documents) + nodes = documents.flat_map do |document| + Querying.css(document, 'a.gfm[data-reference-type]') + end + + redact_nodes(nodes) + + documents + end + + # Redacts the given nodes + # + # nodes - An Array of HTML nodes to redact. + def redact_nodes(nodes) + visible = nodes_visible_to_user(nodes) + + nodes.each do |node| + unless visible.include?(node) + # The reference should be replaced by the original text, + # which is not always the same as the rendered text. + text = node.attr('data-original') || node.text + node.replace(text) + end + end + end + + # Returns the nodes visible to the current user. + # + # nodes - The input nodes to check. + # + # Returns a new Array containing the visible nodes. + def nodes_visible_to_user(nodes) + per_type = Hash.new { |h, k| h[k] = [] } + visible = Set.new + + nodes.each do |node| + per_type[node.attr('data-reference-type')] << node + end + + per_type.each do |type, nodes| + parser = Banzai::ReferenceParser[type].new(project, user) + + visible.merge(parser.nodes_visible_to_user(user, nodes)) + end + + visible + end + end +end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index ed86de819eb..c52d4d63382 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -2,7 +2,7 @@ module Ci class GitlabCiYamlProcessor class ValidationError < StandardError; end - include Gitlab::Ci::Config::Node::ValidationHelpers + include Gitlab::Ci::Config::Node::LegacyValidationHelpers DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index ab7b811c5d8..ad412f56cca 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -22,7 +22,7 @@ module Grack # Need this if under RELATIVE_URL_ROOT unless Gitlab.config.gitlab.relative_url_root.empty? # If website is mounted using relative_url_root need to remove it first - @env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'') + @env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root, '') else @env['PATH_INFO'] = @request.path end diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb index 997a22779a0..d62bc50ce78 100644 --- a/lib/gitlab/blame.rb +++ b/lib/gitlab/blame.rb @@ -41,7 +41,8 @@ module Gitlab def highlighted_lines @blob.load_all_data!(repository) - @highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines + @highlighted_lines ||= + Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines end def project diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index b48d3592f16..adfd097736e 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -4,8 +4,6 @@ module Gitlab # Base GitLab CI Configuration facade # class Config - delegate :valid?, :errors, to: :@global - ## # Temporary delegations that should be removed after refactoring # @@ -18,6 +16,14 @@ module Gitlab @global.process! end + def valid? + @global.valid? + end + + def errors + @global.errors + end + def to_hash @config end diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb index d60f87f3f94..374ff71d0f5 100644 --- a/lib/gitlab/ci/config/node/configurable.rb +++ b/lib/gitlab/ci/config/node/configurable.rb @@ -15,27 +15,24 @@ module Gitlab # module Configurable extend ActiveSupport::Concern + include Validatable - def allowed_nodes - self.class.allowed_nodes || {} + included do + validations do + validates :config, hash: true + end end private - def prevalidate! - unless @value.is_a?(Hash) - @errors << 'should be a configuration entry with hash value' - end - end - def create_node(key, factory) - factory.with(value: @value[key]) - factory.nullify! unless @value.has_key?(key) + factory.with(value: @config[key], key: key) + factory.nullify! unless @config.has_key?(key) factory.create! end class_methods do - def allowed_nodes + def nodes Hash[@allowed_nodes.map { |key, factory| [key, factory.dup] }] end @@ -47,7 +44,6 @@ module Gitlab define_method(symbol) do raise Entry::InvalidError unless valid? - @nodes[symbol].try(:value) end diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb index 52758a962f3..f044ef965e9 100644 --- a/lib/gitlab/ci/config/node/entry.rb +++ b/lib/gitlab/ci/config/node/entry.rb @@ -8,14 +8,14 @@ module Gitlab class Entry class InvalidError < StandardError; end - attr_accessor :description + attr_reader :config + attr_accessor :key, :description - def initialize(value) - @value = value + def initialize(config) + @config = config @nodes = {} - @errors = [] - - prevalidate! + @validator = self.class.validator.new(self) + @validator.validate end def process! @@ -23,50 +23,54 @@ module Gitlab return unless valid? compose! - - nodes.each(&:process!) - nodes.each(&:validate!) + process_nodes! end def nodes @nodes.values end - def valid? - errors.none? - end - def leaf? - allowed_nodes.none? + self.class.nodes.none? end - def errors - @errors + nodes.map(&:errors).flatten + def key + @key || self.class.name.demodulize.underscore end - def allowed_nodes - {} + def valid? + errors.none? end - def validate! - raise NotImplementedError + def errors + @validator.full_errors + + nodes.map(&:errors).flatten end def value raise NotImplementedError end - private + def self.nodes + {} + end - def prevalidate! + def self.validator + Validator end + private + def compose! - allowed_nodes.each do |key, essence| + self.class.nodes.each do |key, essence| @nodes[key] = create_node(key, essence) end end + def process_nodes! + nodes.each(&:process!) + end + def create_node(key, essence) raise NotImplementedError end diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/node/factory.rb index 787ca006f5a..025ae40ef94 100644 --- a/lib/gitlab/ci/config/node/factory.rb +++ b/lib/gitlab/ci/config/node/factory.rb @@ -30,6 +30,7 @@ module Gitlab @entry_class.new(@attributes[:value]).tap do |entry| entry.description = @attributes[:description] + entry.key = @attributes[:key] end end end diff --git a/lib/gitlab/ci/config/node/validation_helpers.rb b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb index 72f648975dc..4d9a508796a 100644 --- a/lib/gitlab/ci/config/node/validation_helpers.rb +++ b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb @@ -2,7 +2,7 @@ module Gitlab module Ci class Config module Node - module ValidationHelpers + module LegacyValidationHelpers private def validate_duration(value) diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/node/script.rb index 5072bf0db7d..c044f5c5e71 100644 --- a/lib/gitlab/ci/config/node/script.rb +++ b/lib/gitlab/ci/config/node/script.rb @@ -11,16 +11,14 @@ module Gitlab # implementation in Runner. # class Script < Entry - include ValidationHelpers + include Validatable - def value - @value.join("\n") + validations do + validates :config, array_of_strings: true end - def validate! - unless validate_array_of_strings(@value) - @errors << 'before_script should be an array of strings' - end + def value + @config.join("\n") end end end diff --git a/lib/gitlab/ci/config/node/validatable.rb b/lib/gitlab/ci/config/node/validatable.rb new file mode 100644 index 00000000000..f6e2896dfb2 --- /dev/null +++ b/lib/gitlab/ci/config/node/validatable.rb @@ -0,0 +1,29 @@ +module Gitlab + module Ci + class Config + module Node + module Validatable + extend ActiveSupport::Concern + + class_methods do + def validator + validator = Class.new(Node::Validator) + + if defined?(@validations) + @validations.each { |rules| validator.class_eval(&rules) } + end + + validator + end + + private + + def validations(&block) + (@validations ||= []).append(block) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/node/validator.rb b/lib/gitlab/ci/config/node/validator.rb new file mode 100644 index 00000000000..02edc9219c3 --- /dev/null +++ b/lib/gitlab/ci/config/node/validator.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + class Config + module Node + class Validator < SimpleDelegator + include ActiveModel::Validations + include Node::Validators + + def initialize(node) + super(node) + @node = node + end + + def full_errors + errors.full_messages.map do |error| + "#{@node.key} #{error}".humanize + end + end + + def self.name + 'Validator' + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/node/validators.rb b/lib/gitlab/ci/config/node/validators.rb new file mode 100644 index 00000000000..dc9cdb9a220 --- /dev/null +++ b/lib/gitlab/ci/config/node/validators.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + class Config + module Node + module Validators + class ArrayOfStringsValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_array_of_strings(value) + record.errors.add(attribute, 'should be an array of strings') + end + end + end + + class HashValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value.is_a?(Hash) + record.errors.add(attribute, 'should be a configuration entry hash') + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index f751a3a12fd..d4f12cb1df9 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -3,7 +3,6 @@ module Gitlab def add_gon_variables gon.api_version = API::API.version gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s - gon.default_issues_tracker = Project.new.default_issue_tracker.to_param gon.max_file_size = current_application_settings.max_attachment_size gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.shortcuts_path = help_shortcuts_path diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb index 280120b0f9e..41296415e35 100644 --- a/lib/gitlab/highlight.rb +++ b/lib/gitlab/highlight.rb @@ -1,7 +1,7 @@ module Gitlab class Highlight - def self.highlight(blob_name, blob_content, nowrap: true, plain: false) - new(blob_name, blob_content, nowrap: nowrap). + def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) + new(blob_name, blob_content, nowrap: nowrap, repository: repository). highlight(blob_content, continue: false, plain: plain) end @@ -10,12 +10,21 @@ module Gitlab return [] unless blob blob.load_all_data!(repository) - highlight(file_name, blob.data).lines.map!(&:html_safe) + highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) end - def initialize(blob_name, blob_content, nowrap: true) + attr_reader :lexer + def initialize(blob_name, blob_content, repository: nil, nowrap: true) + @blob_name = blob_name + @blob_content = blob_content + @repository = repository @formatter = rouge_formatter(nowrap: nowrap) - @lexer = Rouge::Lexer.guess(filename: blob_name, source: blob_content).new rescue Rouge::Lexers::PlainText + + @lexer = custom_language || begin + Rouge::Lexer.guess(filename: blob_name, source: blob_content).new + rescue Rouge::Lexer::AmbiguousGuess => e + e.alternatives.sort_by(&:tag).first + end end def highlight(text, continue: true, plain: false) @@ -30,6 +39,14 @@ module Gitlab private + def custom_language + language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language') + + return nil unless language_name + + Rouge::Lexer.find_fancy(language_name) + end + def rouge_formatter(options = {}) options = options.reverse_merge( nowrap: true, diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb index 0e70d9282d5..82d1e1805c5 100644 --- a/lib/gitlab/import_export/file_importer.rb +++ b/lib/gitlab/import_export/file_importer.rb @@ -23,7 +23,11 @@ module Gitlab private def decompress_archive - untar_zxf(archive: @archive_file, dir: @shared.export_path) + result = untar_zxf(archive: @archive_file, dir: @shared.export_path) + + raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result + + true end end end diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb index d209e04f7be..595b20a09bd 100644 --- a/lib/gitlab/import_export/importer.rb +++ b/lib/gitlab/import_export/importer.rb @@ -10,17 +10,22 @@ module Gitlab end def execute - Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file, - shared: @shared) - if check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) + if import_file && check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) project_tree.restored_project else raise Projects::ImportService::Error.new(@shared.errors.join(', ')) end + + remove_import_file end private + def import_file + Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file, + shared: @shared) + end + def check_version! Gitlab::ImportExport::VersionChecker.check!(shared: @shared) end @@ -59,6 +64,10 @@ module Gitlab def wiki_repo_path File.join(@shared.export_path, 'project.wiki.bundle') end + + def remove_import_file + FileUtils.rm_rf(@archive_file) + end end end end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index b872780f20a..92bf7e0a2fc 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -12,6 +12,8 @@ module Gitlab USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze + BUILD_MODELS = %w[Ci::Build commit_status].freeze + def self.create(*args) new(*args).create end @@ -70,7 +72,7 @@ module Gitlab end def generate_imported_object - if @relation_sym == 'commit_status' # call #trace= method after assigning the other attributes + if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes trace = @relation_hash.delete('trace') imported_object do |object| object.trace = trace diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index faf0d9b6318..c048fe20ba7 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -18,12 +18,12 @@ module Gitlab # Measures the real and CPU execution time of the supplied block. def measure - start_real = Time.now + start_real = System.monotonic_time start_cpu = System.cpu_time retval = yield - @real_time += (Time.now - start_real) * 1000.0 - @cpu_time += System.cpu_time.to_f - start_cpu + @real_time += System.monotonic_time - start_real + @cpu_time += System.cpu_time - start_cpu @call_count += 1 retval diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index 1cd1ca30f70..f23d67e1e38 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -4,16 +4,15 @@ module Gitlab class Metric JITTER_RANGE = 0.000001..0.001 - attr_reader :series, :values, :tags, :created_at + attr_reader :series, :values, :tags # series - The name of the series (as a String) to store the metric in. # values - A Hash containing the values to store. # tags - A Hash containing extra tags to add to the metrics. def initialize(series, values, tags = {}) - @values = values - @series = series - @tags = tags - @created_at = Time.now.utc + @values = values + @series = series + @tags = tags end # Returns a Hash in a format that can be directly written to InfluxDB. @@ -27,20 +26,20 @@ module Gitlab # # Due to the way InfluxDB is set up there's no solution to this problem, # all we can do is lower the amount of collisions. We do this by using - # Time#to_f which returns the seconds as a Float providing greater - # accuracy. We then add a small random value that is large enough to - # distinguish most timestamps but small enough to not alter the amount - # of seconds. + # System.real_time which returns the nanoseconds as a Float providing + # greater accuracy. We then add a small random value that is large + # enough to distinguish most timestamps but small enough to not alter + # the timestamp significantly. # # See https://gitlab.com/gitlab-com/operations/issues/175 for more # information. - time = @created_at.to_f + rand(JITTER_RANGE) + time = System.real_time(:nanosecond) + rand(JITTER_RANGE) { series: @series, tags: @tags, values: @values, - timestamp: (time * 1_000_000_000).to_i + timestamp: time.to_i } end end diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index fd98aa3412e..a1240fd33ee 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -8,6 +8,8 @@ module Gitlab trans = Transaction.new("#{worker.class.name}#perform") begin + # Old gitlad-shell messages don't provide enqueued_at/created_at attributes + trans.set(:sidekiq_queue_duration, Time.now.to_f - (message['enqueued_at'] || message['created_at'] || 0)) trans.run { yield } ensure trans.finish diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index a7d183b2f94..82c18bb108b 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -34,13 +34,29 @@ module Gitlab # THREAD_CPUTIME is not supported on OS X if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID) def self.cpu_time - Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond) + Process. + clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond).to_f end else def self.cpu_time - Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond) + Process. + clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond).to_f end end + + # Returns the current real time in a given precision. + # + # Returns the time as a Float. + def self.real_time(precision = :millisecond) + Process.clock_gettime(Process::CLOCK_REALTIME, precision).to_f + end + + # Returns the current monotonic clock time in a given precision. + # + # Returns the time as a Float. + def self.monotonic_time(precision = :millisecond) + Process.clock_gettime(Process::CLOCK_MONOTONIC, precision).to_f + end end end end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 4bc5081aa03..bded245da43 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -30,7 +30,7 @@ module Gitlab end def duration - @finished_at ? (@finished_at - @started_at) * 1000.0 : 0.0 + @finished_at ? (@finished_at - @started_at) : 0.0 end def allocated_memory @@ -41,12 +41,12 @@ module Gitlab Thread.current[THREAD_KEY] = self @memory_before = System.memory_usage - @started_at = Time.now + @started_at = System.monotonic_time yield ensure @memory_after = System.memory_usage - @finished_at = Time.now + @finished_at = System.monotonic_time Thread.current[THREAD_KEY] = nil end diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb index 36e5c2670bb..7d6911a1ab3 100644 --- a/lib/gitlab/o_auth/auth_hash.rb +++ b/lib/gitlab/o_auth/auth_hash.rb @@ -66,7 +66,7 @@ module Gitlab # Get the first part of the email address (before @) # In addtion in removes illegal characters def generate_username(email) - email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/,'').to_s + email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/, '').to_s end def generate_temporarily_email(username) diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 78f3ecb4cb4..7af75a9cc4c 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -74,7 +74,7 @@ module Gitlab if user # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account. log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity." - user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider) + user.identities.find_or_initialize_by(extern_uid: auth_hash.uid, provider: auth_hash.provider) else log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account." user = find_by_uid_and_provider diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb index ae85b294d31..4831c46c4be 100644 --- a/lib/gitlab/sidekiq_middleware/memory_killer.rb +++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb @@ -25,7 +25,7 @@ module Gitlab Sidekiq.logger.warn "current RSS #{current_rss} exceeds maximum RSS "\ "#{MAX_RSS}" - Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} "\ + Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}"\ "in #{GRACE_TIME} seconds" sleep(GRACE_TIME) @@ -36,7 +36,7 @@ module Gitlab "#{SHUTDOWN_SIGNAL} to PID #{Process.pid}" sleep(SHUTDOWN_WAIT) - Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid}" + Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}" Process.kill(SHUTDOWN_SIGNAL, Process.pid) end end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 40e8299c36b..ef1241f8600 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -52,6 +52,19 @@ module Gitlab ] end + def send_git_patch(repository, from, to) + params = { + 'RepoPath' => repository.path_to_repo, + 'ShaFrom' => from, + 'ShaTo' => to + } + + [ + SEND_DATA_HEADER, + "git-format-patch:#{encode(params)}" + ] + end + protected def encode(hash) diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png Binary files differindex 7da5f23ed9b..05c8b0d0ccf 100644 --- a/public/apple-touch-icon-precomposed.png +++ b/public/apple-touch-icon-precomposed.png diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png Binary files differindex 7da5f23ed9b..05c8b0d0ccf 100644 --- a/public/apple-touch-icon.png +++ b/public/apple-touch-icon.png diff --git a/rubocop/cop/migration/add_index.rb b/rubocop/cop/migration/add_index.rb new file mode 100644 index 00000000000..d9247a1f7ea --- /dev/null +++ b/rubocop/cop/migration/add_index.rb @@ -0,0 +1,46 @@ +module RuboCop + module Cop + module Migration + # Cop that checks if indexes are added in a concurrent manner. + class AddIndex < RuboCop::Cop::Cop + include MigrationHelpers + + MSG = 'add_index requires downtime, use add_concurrent_index instead' + + def on_def(node) + return unless in_migration?(node) + + new_tables = [] + + node.each_descendant(:send) do |send_node| + first_arg = first_argument(send_node) + + # The first argument of "create_table" / "add_index" is the table + # name. + new_tables << first_arg if create_table?(send_node) + + next if method_name(send_node) != :add_index + + # Using "add_index" is fine for newly created tables as there's no + # data in these tables yet. + next if new_tables.include?(first_arg) + + add_offense(send_node, :selector) + end + end + + def create_table?(node) + method_name(node) == :create_table + end + + def method_name(node) + node.children[1] + end + + def first_argument(node) + node.children[2] ? node.children[0] : nil + end + end + end + end +end diff --git a/rubocop/cop/migration/column_with_default.rb b/rubocop/cop/migration/column_with_default.rb new file mode 100644 index 00000000000..97ee8b11044 --- /dev/null +++ b/rubocop/cop/migration/column_with_default.rb @@ -0,0 +1,50 @@ +module RuboCop + module Cop + module Migration + # Cop that checks if columns are added in a way that doesn't require + # downtime. + class ColumnWithDefault < RuboCop::Cop::Cop + include MigrationHelpers + + WHITELISTED_TABLES = [:application_settings] + + MSG = 'add_column with a default value requires downtime, ' \ + 'use add_column_with_default instead' + + def on_send(node) + return unless in_migration?(node) + + name = node.children[1] + + return unless name == :add_column + + # Ignore whitelisted tables. + return if table_whitelisted?(node.children[2]) + + opts = node.children.last + + return unless opts && opts.type == :hash + + opts.each_node(:pair) do |pair| + if hash_key_type(pair) == :sym && hash_key_name(pair) == :default + add_offense(node, :selector) + end + end + end + + def table_whitelisted?(symbol) + symbol && symbol.type == :sym && + WHITELISTED_TABLES.include?(symbol.children[0]) + end + + def hash_key_type(pair) + pair.children[0].type + end + + def hash_key_name(pair) + pair.children[0].children[0] + end + end + end + end +end diff --git a/rubocop/migration_helpers.rb b/rubocop/migration_helpers.rb new file mode 100644 index 00000000000..3160a784a04 --- /dev/null +++ b/rubocop/migration_helpers.rb @@ -0,0 +1,10 @@ +module RuboCop + # Module containing helper methods for writing migration cops. + module MigrationHelpers + # Returns true if the given node originated from the db/migrate directory. + def in_migration?(node) + File.dirname(node.location.expression.source_buffer.name). + end_with?('db/migrate') + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb new file mode 100644 index 00000000000..7922e19768b --- /dev/null +++ b/rubocop/rubocop.rb @@ -0,0 +1,3 @@ +require_relative 'migration_helpers' +require_relative 'cop/migration/add_index' +require_relative 'cop/migration/column_with_default' diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb index eb82476b179..d5f0b289b5b 100644 --- a/spec/controllers/admin/impersonations_controller_spec.rb +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -22,7 +22,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in" do @@ -46,7 +46,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in as the impersonator" do @@ -65,7 +65,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in as the impersonator" do diff --git a/spec/controllers/admin/spam_logs_controller_spec.rb b/spec/controllers/admin/spam_logs_controller_spec.rb index b51b303a714..520a4f6f9c5 100644 --- a/spec/controllers/admin/spam_logs_controller_spec.rb +++ b/spec/controllers/admin/spam_logs_controller_spec.rb @@ -14,7 +14,7 @@ describe Admin::SpamLogsController do it 'lists all spam logs' do get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -22,14 +22,14 @@ describe Admin::SpamLogsController do it 'removes only the spam log when removing log' do expect { delete :destroy, id: first_spam.id }.to change { SpamLog.count }.by(-1) expect(User.find(user.id)).to be_truthy - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'removes user and his spam logs when removing the user' do delete :destroy, id: first_spam.id, remove_user: true expect(flash[:notice]).to eq "User #{user.username} was successfully removed." - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(SpamLog.count).to eq(0) expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 6caf37ddc2c..ab9aa65f7b9 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -17,7 +17,7 @@ describe Admin::UsersController do it 'deletes user' do delete :destroy, id: user.username, format: :json - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect { User.find(user.id) }.to raise_exception(ActiveRecord::RecordNotFound) end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index ff5b3916273..10824c20c87 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -44,7 +44,7 @@ describe ApplicationController do context "when the 'private_token' param is populated with the private token" do it "logs the user in" do get :index, private_token: user.private_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq("authenticated") end end @@ -54,7 +54,7 @@ describe ApplicationController do it "logs the user in" do @request.headers['PRIVATE-TOKEN'] = user.private_token get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq("authenticated") end end @@ -80,7 +80,7 @@ describe ApplicationController do context "when the 'personal_access_token' param is populated with the personal access token" do it "logs the user in" do get :index, private_token: personal_access_token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq('authenticated') end end @@ -89,7 +89,7 @@ describe ApplicationController do it "logs the user in" do @request.headers["PRIVATE-TOKEN"] = personal_access_token.token get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq('authenticated') end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 28cf804c1b2..60c654f622d 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -29,7 +29,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end @@ -58,7 +58,7 @@ describe AutocompleteController do get(:users, group_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end @@ -114,7 +114,7 @@ describe AutocompleteController do get(:users, project_id: project.id) end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with unknown project' do @@ -122,7 +122,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with inaccessible group' do @@ -131,7 +131,7 @@ describe AutocompleteController do get(:users, group_id: user.namespace.id) end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with no project' do diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index cf5c606c723..a3a3309e15e 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -155,7 +155,7 @@ describe Projects::CommitController do id: commit.id) expect(response).not_to be_success - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -204,7 +204,7 @@ describe Projects::CommitController do id: master_pickable_commit.id) expect(response).not_to be_success - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index c8601341d54..ddc54108a7b 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -13,7 +13,7 @@ describe Groups::GroupMembersController do it 'renders index with group members' do get :index, group_id: group - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template(:index) end end @@ -26,7 +26,7 @@ describe Groups::GroupMembersController do delete :destroy, group_id: group, id: 42 - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -48,7 +48,7 @@ describe Groups::GroupMembersController do delete :destroy, group_id: group, id: member - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(group.users).to include group_user end end @@ -89,7 +89,7 @@ describe Groups::GroupMembersController do it 'returns 403' do delete :leave, group_id: group - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -118,7 +118,7 @@ describe Groups::GroupMembersController do it 'cannot removes himself from the group' do delete :leave, group_id: group - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -166,7 +166,7 @@ describe Groups::GroupMembersController do post :approve_access_request, group_id: group, id: 42 - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -188,7 +188,7 @@ describe Groups::GroupMembersController do post :approve_access_request, group_id: group, id: member - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(group.users).not_to include group_requester end end diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb index 0d8a68bb51a..56ecf2bb644 100644 --- a/spec/controllers/health_check_controller_spec.rb +++ b/spec/controllers/health_check_controller_spec.rb @@ -65,21 +65,21 @@ describe HealthCheckController do it 'supports passing the token in the header' do request.headers['TOKEN'] = token get :index - expect(response.status).to eq(500) + expect(response).to have_http_status(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).to have_http_status(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).to have_http_status(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') @@ -87,7 +87,7 @@ describe HealthCheckController do it 'supports failure xml response' do get :index, token: token, format: :xml - expect(response.status).to eq(500) + expect(response).to have_http_status(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') @@ -95,7 +95,7 @@ describe HealthCheckController do it 'supports failure responses for specific checks' do get :index, token: token, checks: 'email', format: :json - expect(response.status).to eq(500) + expect(response).to have_http_status(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') diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 3c6e54839b5..e478a253b3f 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -15,7 +15,7 @@ describe InvitesController do get :accept, id: token member.reload - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(member.user).to eq(user) expect(flash[:notice]).to include 'You have been granted' end @@ -26,7 +26,7 @@ describe InvitesController do get :decline, id: token expect{member.reload}.to raise_error ActiveRecord::RecordNotFound - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(flash[:notice]).to include 'You have declined the invitation to join' end end diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 27e9afe582e..2b334ed1172 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -86,7 +86,7 @@ describe NamespacesController do it "responds with status 404" do get :show, id: group.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -102,7 +102,7 @@ describe NamespacesController do it "responds with status 404" do get :show, id: "doesntexist" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb index 15d155833b4..79b819a1377 100644 --- a/spec/controllers/notification_settings_controller_spec.rb +++ b/spec/controllers/notification_settings_controller_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe NotificationSettingsController do let(:project) { create(:empty_project) } + let(:group) { create(:group, :internal) } let(:user) { create(:user) } before do @@ -12,7 +13,7 @@ describe NotificationSettingsController do context 'when not authorized' do it 'redirects to sign in page' do post :create, - project: { id: project.id }, + project_id: project.id, notification_setting: { level: :participating } expect(response).to redirect_to(new_user_session_path) @@ -20,33 +21,73 @@ describe NotificationSettingsController do end context 'when authorized' do + let(:custom_events) do + events = {} + + NotificationSetting::EMAIL_EVENTS.each do |event| + events[event.to_s] = true + end + + events + end + before do sign_in(user) end - it 'returns success' do - post :create, - project: { id: project.id }, - notification_setting: { level: :participating } + context 'for projects' do + let(:notification_setting) { user.notification_settings_for(project) } - expect(response.status).to eq 200 - end + it 'creates notification setting' do + post :create, + project_id: project.id, + notification_setting: { level: :participating } - context 'and setting custom notification setting' do - let(:custom_events) do - events = {} + expect(response.status).to eq 200 + expect(notification_setting.level).to eq("participating") + expect(notification_setting.user_id).to eq(user.id) + expect(notification_setting.source_id).to eq(project.id) + expect(notification_setting.source_type).to eq("Project") + end - NotificationSetting::EMAIL_EVENTS.each do |event| - events[event] = "true" + context 'with custom settings' do + it 'creates notification setting' do + post :create, + project_id: project.id, + notification_setting: { level: :custom }.merge(custom_events) + + expect(response.status).to eq 200 + expect(notification_setting.level).to eq("custom") + expect(notification_setting.events).to eq(custom_events) end end + end - it 'returns success' do + context 'for groups' do + let(:notification_setting) { user.notification_settings_for(group) } + + it 'creates notification setting' do post :create, - project: { id: project.id }, - notification_setting: { level: :participating, events: custom_events } + namespace_id: group.id, + notification_setting: { level: :watch } expect(response.status).to eq 200 + expect(notification_setting.level).to eq("watch") + expect(notification_setting.user_id).to eq(user.id) + expect(notification_setting.source_id).to eq(group.id) + expect(notification_setting.source_type).to eq("Namespace") + end + + context 'with custom settings' do + it 'creates notification setting' do + post :create, + namespace_id: group.id, + notification_setting: { level: :custom }.merge(custom_events) + + expect(response.status).to eq 200 + expect(notification_setting.level).to eq("custom") + expect(notification_setting.events).to eq(custom_events) + end end end end @@ -57,10 +98,10 @@ describe NotificationSettingsController do it 'returns 404' do post :create, - project: { id: private_project.id }, + project_id: private_project.id, notification_setting: { level: :participating } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -118,7 +159,7 @@ describe NotificationSettingsController do id: notification_setting, notification_setting: { level: :participating } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index af378304893..552899eb36c 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -12,7 +12,7 @@ describe Oauth::ApplicationsController do it 'shows list of applications' do get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'redirects back to profile page if OAuth applications are disabled' do @@ -21,7 +21,7 @@ describe Oauth::ApplicationsController do get :index - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(profile_path) end end diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb index 4eafc11abaa..2dc9adfd60c 100644 --- a/spec/controllers/profiles/accounts_controller_spec.rb +++ b/spec/controllers/profiles/accounts_controller_spec.rb @@ -13,7 +13,7 @@ describe Profiles::AccountsController do delete :unlink, provider: 'saml' updated_user = User.find(user.id) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(updated_user.identities.size).to eq(1) expect(updated_user.identities).to include(identity) end diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb new file mode 100644 index 00000000000..9444a50b1ce --- /dev/null +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -0,0 +1,40 @@ +require 'rails_helper' + +describe Projects::BlobController do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + user = create(:user) + project.team << [user, :master] + + sign_in(user) + end + + describe 'GET diff' do + render_views + + def do_get(opts = {}) + params = { namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: 'master/CHANGELOG' } + get :diff, params.merge(opts) + end + + context 'when essential params are missing' do + it 'renders nothing' do + do_get + + expect(response.body).to be_blank + end + end + + context 'when essential params are present' do + it 'renders the diff content' do + do_get(since: 1, to: 5, offset: 10) + + expect(response.body).to be_present + end + end + end +end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index c4b4a888b4e..f59d4937157 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -103,7 +103,7 @@ describe Projects::BranchesController do namespace_id: project.namespace.to_param, project_id: project.to_param - expect(response.status).to eq(303) + expect(response).to have_http_status(303) end end @@ -121,24 +121,24 @@ describe Projects::BranchesController do context "valid branch name, valid source" do let(:branch) { "feature" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "valid branch name with unencoded slashes" do let(:branch) { "improve/awesome" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "valid branch name with encoded slashes" do let(:branch) { "improve%2Fawesome" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "invalid branch name, valid ref" do let(:branch) { "no-branch" } - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index cbaa3e0b7b2..7cf09fa4a4a 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -14,7 +14,7 @@ describe Projects::IssuesController do it "returns index" do get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "return 301 if request path doesn't match project path" do @@ -28,7 +28,7 @@ describe Projects::IssuesController do project.save get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when external issue tracker is enabled" do @@ -36,7 +36,7 @@ describe Projects::IssuesController do allow(project).to receive(:default_issues_tracker?).and_return(false) get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -248,7 +248,7 @@ describe Projects::IssuesController do before { sign_in(user) } it "rejects a developer to destroy an issue" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -262,7 +262,7 @@ describe Projects::IssuesController do it "deletes the issue" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now end end @@ -280,7 +280,7 @@ describe Projects::IssuesController do project_id: project.path, id: issue.iid, name: "thumbsup") end.to change { issue.award_emoji.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 4b408c03703..74c050f48f1 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -96,26 +96,14 @@ describe Projects::MergeRequestsController do end describe "as patch" do - include_examples "export merge as", :patch - let(:format) { :patch } - - it "should really be a git email patch with commit" do - get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, - id: merge_request.iid, format: format) - - expect(response.body[0..100]).to start_with("From #{merge_request.commits.last.id}") - end - - it "should contain git diffs" do + it 'triggers workhorse to serve the request' do get(:show, namespace_id: project.namespace.to_param, project_id: project.to_param, id: merge_request.iid, - format: format) + format: :patch) - expect(response.body).to match(/^diff --git/) + expect(response.headers['Gitlab-Workhorse-Send-Data']).to start_with("git-format-patch:") end end end @@ -264,6 +252,18 @@ describe Projects::MergeRequestsController do merge_when_build_succeeds end + + context 'when project.only_allow_merge_if_build_succeeds? is true' do + before do + project.update_column(:only_allow_merge_if_build_succeeds, true) + end + + it 'returns :merge_when_build_succeeds' do + merge_when_build_succeeds + + expect(assigns(:status)).to eq(:merge_when_build_succeeds) + end + end end end end @@ -272,7 +272,7 @@ describe Projects::MergeRequestsController do it "denies access to users unless they're admin or project owner" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "when the user is owner" do @@ -285,7 +285,7 @@ describe Projects::MergeRequestsController do it "deletes the merge request" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now end end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 00bc38b6071..75590c1ed4f 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::NotesController do project_id: project.path, id: note.id, name: "thumbsup") end.to change { note.award_emoji.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "removes the already awarded emoji" do @@ -30,7 +30,7 @@ describe Projects::NotesController do project_id: project.path, id: note.id, name: "thumbsup") end.to change { AwardEmoji.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index e5e750c855f..29aaceb2302 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -58,7 +58,7 @@ describe Projects::ProjectMembersController do get :index, namespace_id: project.namespace, project_id: project end - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end end @@ -71,7 +71,7 @@ describe Projects::ProjectMembersController do project_id: project, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -94,7 +94,7 @@ describe Projects::ProjectMembersController do project_id: project, id: member - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(project.users).to include team_user end end @@ -139,7 +139,7 @@ describe Projects::ProjectMembersController do delete :leave, namespace_id: project.namespace, project_id: project - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -171,7 +171,7 @@ describe Projects::ProjectMembersController do delete :leave, namespace_id: project.namespace, project_id: project - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -224,7 +224,7 @@ describe Projects::ProjectMembersController do project_id: project, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -247,7 +247,7 @@ describe Projects::ProjectMembersController do project_id: project, id: member - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(project.users).not_to include team_requester end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 33c35161da3..48f799d8ca1 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -13,7 +13,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') expect(response.header['Content-Disposition']). to eq("inline") @@ -30,7 +30,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.header['Content-Type']).to eq('image/jpeg') expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:") end @@ -54,7 +54,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -65,7 +65,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index aad62cf20e3..ee905d11fb2 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -36,7 +36,7 @@ describe Projects::RepositoriesController do it "renders Not Found" do get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 0f32a30f18b..b8a28f43707 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -19,7 +19,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).not_to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -41,7 +41,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -56,7 +56,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -67,7 +67,7 @@ describe Projects::SnippetsController do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -78,7 +78,7 @@ describe Projects::SnippetsController do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -88,7 +88,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -98,7 +98,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb index 40a3403b660..5a8bba28594 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todo_controller_spec.rb @@ -22,7 +22,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -36,7 +36,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not create todo for issue when user not logged in' do @@ -47,7 +47,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) end end end @@ -69,7 +69,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -83,7 +83,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not create todo for merge request user has no access to' do @@ -94,7 +94,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) end end end diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index e74731c9ed8..4e3a2bdb19e 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -64,7 +64,7 @@ describe Projects::TreeController do context "valid SHA commit ID with path" do let(:id) { '6d39438/.gitignore' } - it { expect(response.status).to eq(302) } + it { expect(response).to have_http_status(302) } end end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 93c4494c660..0893ee89f6a 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::UploadsController do namespace_id: project.namespace.to_param, project_id: project.to_param, format: :json - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -79,7 +79,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -87,7 +87,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -106,7 +106,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -114,7 +114,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -140,7 +140,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -192,7 +192,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -224,7 +224,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -232,7 +232,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -253,7 +253,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -261,7 +261,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -270,7 +270,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 146b2c2e131..d60579030c0 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -77,7 +77,7 @@ describe ProjectsController do get :show, namespace_id: public_project.namespace.path, id: public_project.path expect(assigns(:project)).to eq(public_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -101,7 +101,7 @@ describe ProjectsController do get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase expect(assigns(:project)).to eq(other_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -146,7 +146,7 @@ describe ProjectsController do expect(project.repository.path).to include(new_path) expect(assigns(:repository).path).to eq(project.repository.path) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -161,7 +161,7 @@ describe ProjectsController do delete :destroy, namespace_id: project.namespace.path, id: project.path expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(dashboard_projects_path) end end @@ -234,7 +234,7 @@ describe ProjectsController do delete(:remove_fork, namespace_id: project.namespace.to_param, id: project.to_param, format: :js) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index b3dcb52c500..2a89159c070 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -19,7 +19,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: other_personal_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -28,7 +28,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -54,7 +54,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -79,7 +79,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -88,7 +88,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -102,7 +102,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -110,7 +110,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -134,7 +134,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: other_personal_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -143,7 +143,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -169,7 +169,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -194,7 +194,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -203,7 +203,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -217,7 +217,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -225,7 +225,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 73858e6f063..69124ab06bf 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -26,7 +26,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -35,7 +35,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -52,7 +52,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -64,7 +64,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -109,7 +109,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -118,7 +118,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -133,7 +133,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -145,7 +145,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -181,7 +181,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -190,7 +190,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -210,7 +210,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -222,7 +222,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -267,7 +267,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -276,7 +276,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index c61ec174665..8d6f486efdd 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -33,7 +33,7 @@ describe UsersController do it 'renders the show template' do get :show, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -47,7 +47,7 @@ describe UsersController do context 'when logged out' do it 'renders 404' do get :show, username: user.username - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -56,7 +56,7 @@ describe UsersController do it 'renders show' do get :show, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -121,7 +121,7 @@ describe UsersController do context 'format html' do it 'renders snippets page' do get :snippets, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -129,7 +129,7 @@ describe UsersController do 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(response).to have_http_status(200) expect(JSON.parse(response.body)).to have_key('html') end end diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb new file mode 100644 index 00000000000..dbc1d829b67 --- /dev/null +++ b/spec/features/admin/admin_system_info_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe 'Admin System Info' do + before do + login_as :admin + end + + describe 'GET /admin/system_info' do + it 'shows system info page' do + visit admin_system_info_path + + expect(page).to have_content 'CPU' + expect(page).to have_content 'Memory' + expect(page).to have_content 'Disk' + end + end +end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index de6aed74fb4..91704377a07 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -61,7 +61,7 @@ describe "User Feed", feature: true do end it 'should have XHTML summaries in merge request descriptions' do - expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/ + expect(body).to match /Here is the fix: <\/p><div[^>]*><a[^>]*><img[^>]*\/><\/a><\/div>/ end end end diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb index 1ea607cbca0..4944301c938 100644 --- a/spec/features/groups/members/user_requests_access_spec.rb +++ b/spec/features/groups/members/user_requests_access_spec.rb @@ -4,6 +4,7 @@ feature 'Groups > Members > User requests access', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public) } + let!(:project) { create(:project, :private, namespace: group) } background do group.add_owner(owner) @@ -24,6 +25,20 @@ feature 'Groups > Members > User requests access', feature: true do expect(page).not_to have_content 'Leave Group' end + scenario 'user does not see private projects' do + perform_enqueued_jobs { click_link 'Request Access' } + + expect(page).not_to have_content project.name + end + + scenario 'user does not see group in the Dashboard > Groups page' do + perform_enqueued_jobs { click_link 'Request Access' } + + visit dashboard_groups_path + + expect(page).not_to have_content group.name + end + scenario 'user is not listed in the group members page' do click_link 'Request Access' diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 7143d0e40f3..afc093cc1f5 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -10,7 +10,7 @@ feature 'Issues > Labels bulk assignment', feature: true do let!(:bug) { create(:label, project: project, title: 'bug') } let!(:feature) { create(:label, project: project, title: 'feature') } - context 'as a allowed user', js: true do + context 'as an allowed user', js: true do before do project.team << [user, :master] @@ -164,6 +164,133 @@ feature 'Issues > Labels bulk assignment', feature: true do end end end + + context 'toggling a milestone' do + let!(:milestone) { create(:milestone, project: project, title: 'First Release') } + + context 'setting a milestone' do + before do + issue1.labels << bug + issue2.labels << feature + visit namespace_project_issues_path(project.namespace, project) + end + + it 'labels are kept' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + + check 'check_all_issues' + open_milestone_dropdown(['First Release']) + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'setting a milestone and adding another label' do + before do + issue1.labels << bug + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'existing label is kept and new label is present' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + + check 'check_all_issues' + open_milestone_dropdown ['First Release'] + open_labels_dropdown ['feature'] + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'feature' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'setting a milestone and removing existing label' do + before do + issue1.labels << bug + issue1.labels << feature + issue2.labels << feature + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'existing label is kept and new label is present' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + + check 'check_all_issues' + open_milestone_dropdown ['First Release'] + unmark_labels_in_dropdown ['feature'] + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'feature' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).not_to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'unsetting a milestone' do + before do + issue1.milestone = milestone + issue2.milestone = milestone + issue1.save + issue2.save + issue1.labels << bug + issue2.labels << feature + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'labels are kept' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + + check 'check_all_issues' + open_milestone_dropdown(['No Milestone']) + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).not_to have_content 'First Release' + end + end + end + + context 'toggling checked issues' do + before do + issue1.labels << bug + + visit namespace_project_issues_path(project.namespace, project) + end + + it do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + + check_issue issue1 + open_labels_dropdown ['feature'] + uncheck_issue issue1 + check_issue issue1 + update_issues + sleep 1 # needed + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'feature' + end + end end context 'as a guest' do @@ -181,6 +308,16 @@ feature 'Issues > Labels bulk assignment', feature: true do end end + def open_milestone_dropdown(items = []) + page.within('.issues_bulk_update') do + click_button 'Milestone' + wait_for_ajax + items.map do |item| + click_link item + end + end + end + def open_labels_dropdown(items = [], unmark = false) page.within('.issues_bulk_update') do click_button 'Label' @@ -201,12 +338,20 @@ feature 'Issues > Labels bulk assignment', feature: true do open_labels_dropdown(items, true) end - def check_issue(issue) + def check_issue(issue, uncheck = false) page.within('.issues-list') do - check "selected_issue_#{issue.id}" + if uncheck + uncheck "selected_issue_#{issue.id}" + else + check "selected_issue_#{issue.id}" + end end end + def uncheck_issue(issue) + check_issue(issue, true) + end + def update_issues click_button 'Update issues' wait_for_ajax diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 5065dfb849c..17df66e73b4 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -92,7 +92,7 @@ describe 'Issues', feature: true do end context 'on edit form' do - let(:issue) { create(:issue, author: @user,project: project, due_date: Date.today.at_beginning_of_month.to_s) } + let(:issue) { create(:issue, author: @user, project: project, due_date: Date.today.at_beginning_of_month.to_s) } before do visit edit_namespace_project_issue_path(project.namespace, project, issue) diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb index 073a83b6896..9ebef505b92 100644 --- a/spec/features/projects/files/gitignore_dropdown_spec.rb +++ b/spec/features/projects/files/gitignore_dropdown_spec.rb @@ -24,6 +24,7 @@ feature 'User wants to add a .gitignore file', feature: true do end wait_for_ajax + expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Rails') expect(page).to have_content('/.bundle') expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset') end diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index d516e8ce55a..b8c06c383fb 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -24,6 +24,7 @@ feature 'User wants to add a .gitlab-ci.yml file', feature: true do end wait_for_ajax + expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'jekyll') expect(page).to have_content('This file is a template, and might need editing before it works on your project') expect(page).to have_content('jekyll build -d test') end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index c5fb0fc783b..9d66f76ef58 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -24,7 +24,7 @@ feature 'project import', feature: true, js: true do visit new_project_path select2('2', from: '#project_namespace_id') - fill_in :project_path, with:'test-project-path', visible: true + fill_in :project_path, with: 'test-project-path', visible: true click_link 'GitLab export' expect(page).to have_content('GitLab project export') diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 8625ea6bc10..13d980a326f 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -288,4 +288,142 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end + + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds" do + subject { namespace_project_builds_path(project.namespace, project) } + + context "when allowed for public and internal" do + before { project.update(public_builds: true) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "when disallowed for public and internal" do + before { project.update(public_builds: false) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + end + + describe "GET /:project_path/builds/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + subject { namespace_project_build_path(project.namespace, project, build.id) } + + context "when allowed for public and internal" do + before { project.update(public_builds: true) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "when disallowed for public and internal" do + before { project.update(public_builds: false) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + end + + describe "GET /:project_path/environments" do + subject { namespace_project_environments_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/:id" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/new" do + subject { new_namespace_project_environment_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 544270b4037..ac9690cc127 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -260,4 +260,106 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end + + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds" do + subject { namespace_project_builds_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + subject { namespace_project_build_path(project.namespace, project, build.id) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments" do + subject { namespace_project_environments_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/:id" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/new" do + subject { new_namespace_project_environment_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index f6c6687e162..737897de52b 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -109,6 +109,35 @@ describe "Public Project Access", feature: true do it { is_expected.to be_allowed_for :external } end + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + describe "GET /:project_path/builds" do subject { namespace_project_builds_path(project.namespace, project) } @@ -191,7 +220,7 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/environments/:id" do let(:environment) { create(:environment, project: project) } - subject { namespace_project_environments_path(project.namespace, project, environment) } + subject { namespace_project_environment_path(project.namespace, project, environment) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for owner } diff --git a/spec/fixtures/dk.png b/spec/fixtures/dk.png Binary files differindex 87ce25e877a..1247f2fecd7 100644 --- a/spec/fixtures/dk.png +++ b/spec/fixtures/dk.png diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index ff98249570d..5e7594170c5 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -1,11 +1,6 @@ require 'spec_helper' describe VisibilityLevelHelper do - include Haml::Helpers - - before :all do - init_haml_helpers - end let(:project) { build(:project) } let(:group) { build(:group) } diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb index e58f2c80e95..1bcae8a27db 100644 --- a/spec/initializers/settings_spec.rb +++ b/spec/initializers/settings_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require_relative '../../config/initializers/1_settings' describe Settings, lib: true do diff --git a/spec/javascripts/application_spec.js.coffee b/spec/javascripts/application_spec.js.coffee index 8af39c41f2f..4b6a2bb5440 100644 --- a/spec/javascripts/application_spec.js.coffee +++ b/spec/javascripts/application_spec.js.coffee @@ -1,4 +1,4 @@ -#= require lib/common_utils +#= require lib/utils/common_utils describe 'Application', -> describe 'disable buttons', -> diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee index ba191199dc7..d7f9c6fc076 100644 --- a/spec/javascripts/awards_handler_spec.js.coffee +++ b/spec/javascripts/awards_handler_spec.js.coffee @@ -160,7 +160,6 @@ describe 'AwardsHandler', -> expect($('[data-emoji=angel]').is(':visible')).toBe no expect($('[data-emoji=anger]').is(':visible')).toBe no expect($('[data-emoji=alien]').is(':visible')).toBe yes - expect($('h5.emoji-search').is(':visible')).toBe yes describe 'emoji menu', -> diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee index e529dd5f1cd..ce1a41390d2 100644 --- a/spec/javascripts/fixtures/emoji_menu.coffee +++ b/spec/javascripts/fixtures/emoji_menu.coffee @@ -1,7 +1,7 @@ window.emojiMenu = """ <div class='emoji-menu'> + <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" /> <div class='emoji-menu-content'> - <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" /> <h5 class='emoji-menu-title'> Emoticons </h5> diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index 71f0c1076c5..d84d80f266b 100644 --- a/spec/javascripts/issue_spec.js.coffee +++ b/spec/javascripts/issue_spec.js.coffee @@ -1,4 +1,4 @@ -#= require lib/text_utility +#= require lib/utils/text_utility #= require issue describe 'Issue', -> diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee index 9be29097f4c..f0d26fb5446 100644 --- a/spec/javascripts/project_title_spec.js.coffee +++ b/spec/javascripts/project_title_spec.js.coffee @@ -1,6 +1,6 @@ #= require bootstrap #= require select2 -#= require lib/type_utility +#= require lib/utils/type_utility #= require gl_dropdown #= require api #= require project_select diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee index e77177783a7..1c1faca3333 100644 --- a/spec/javascripts/search_autocomplete_spec.js.coffee +++ b/spec/javascripts/search_autocomplete_spec.js.coffee @@ -1,8 +1,8 @@ #= require gl_dropdown #= require search_autocomplete #= require jquery -#= require lib/common_utils -#= require lib/type_utility +#= require lib/utils/common_utils +#= require lib/utils/type_utility #= require fuzzaldrin-plus diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb index dd5594750c8..a2a1ed58d1b 100644 --- a/spec/lib/banzai/filter/image_link_filter_spec.rb +++ b/spec/lib/banzai/filter/image_link_filter_spec.rb @@ -21,4 +21,9 @@ describe Banzai::Filter::ImageLinkFilter, lib: true do doc = filter(image('https://i.imgur.com/DfssX9C.jpg')) expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href'] end + + it 'wraps the image with a link and a div' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.to_html).to include('<div class="image-container">') + end end diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index 5b63c946114..8d6ce114aa9 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -198,4 +198,40 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/) end end + + describe '#issues_per_Project' do + context 'using an internal issue tracker' do + it 'returns a Hash containing the issues per project' do + doc = Nokogiri::HTML.fragment('') + filter = described_class.new(doc, project: project) + + expect(filter).to receive(:projects_per_reference). + and_return({ project.path_with_namespace => project }) + + expect(filter).to receive(:references_per_project). + and_return({ project.path_with_namespace => Set.new([issue.iid]) }) + + expect(filter.issues_per_project). + to eq({ project => { issue.iid => issue } }) + end + end + + context 'using an external issue tracker' do + it 'returns a Hash containing the issues per project' do + doc = Nokogiri::HTML.fragment('') + filter = described_class.new(doc, project: project) + + expect(project).to receive(:default_issues_tracker?).and_return(false) + + expect(filter).to receive(:projects_per_reference). + and_return({ project.path_with_namespace => project }) + + expect(filter).to receive(:references_per_project). + and_return({ project.path_with_namespace => Set.new([1]) }) + + expect(filter.issues_per_project[project][1]). + to be_an_instance_of(ExternalIssue) + end + end + end end diff --git a/spec/lib/banzai/note_renderer_spec.rb b/spec/lib/banzai/note_renderer_spec.rb new file mode 100644 index 00000000000..98f76f36fd5 --- /dev/null +++ b/spec/lib/banzai/note_renderer_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Banzai::NoteRenderer do + describe '.render' do + it 'renders a Note' do + note = double(:note) + project = double(:project) + wiki = double(:wiki) + user = double(:user) + + expect(Banzai::ObjectRenderer).to receive(:new). + with(project, user, + requested_path: 'foo', + project_wiki: wiki, + ref: 'bar', + pipeline: :note). + and_call_original + + expect_any_instance_of(Banzai::ObjectRenderer). + to receive(:render).with([note], :note) + + described_class.render([note], project, user, 'foo', wiki, 'bar') + end + end +end diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb new file mode 100644 index 00000000000..44256b32bdc --- /dev/null +++ b/spec/lib/banzai/object_renderer_spec.rb @@ -0,0 +1,120 @@ +require 'spec_helper' + +describe Banzai::ObjectRenderer do + let(:project) { create(:empty_project) } + let(:user) { project.owner } + + describe '#render' do + it 'renders and redacts an Array of objects' do + renderer = described_class.new(project, user) + object = double(:object, note: 'hello', note_html: nil) + + expect(renderer).to receive(:render_objects).with([object], :note). + and_call_original + + expect(renderer).to receive(:redact_documents). + with(an_instance_of(Array)). + and_call_original + + expect(object).to receive(:note_html=).with('<p>hello</p>') + + renderer.render([object], :note) + end + end + + describe '#render_objects' do + it 'renders an Array of objects' do + object = double(:object, note: 'hello') + renderer = described_class.new(project, user) + + expect(renderer).to receive(:render_attribute).with(object, :note). + and_call_original + + rendered = renderer.render_objects([object], :note) + + expect(rendered).to be_an_instance_of(Array) + expect(rendered[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) + end + end + + describe '#redact_documents' do + it 'redacts a set of documents and returns them as an Array of Strings' do + doc = Nokogiri::HTML.fragment('<p>hello</p>') + renderer = described_class.new(project, user) + + expect_any_instance_of(Banzai::Redactor).to receive(:redact). + with([doc]). + and_call_original + + redacted = renderer.redact_documents([doc]) + + expect(redacted).to eq(['<p>hello</p>']) + end + end + + describe '#context_for' do + let(:object) { double(:object, note: 'hello') } + let(:renderer) { described_class.new(project, user) } + + it 'returns a Hash' do + expect(renderer.context_for(object, :note)).to be_an_instance_of(Hash) + end + + it 'includes the cache key' do + context = renderer.context_for(object, :note) + + expect(context[:cache_key]).to eq([object, :note]) + end + + context 'when the object responds to "author"' do + it 'includes the author in the context' do + expect(object).to receive(:author).and_return('Alice') + + context = renderer.context_for(object, :note) + + expect(context[:author]).to eq('Alice') + end + end + + context 'when the object does not respond to "author"' do + it 'does not include the author in the context' do + context = renderer.context_for(object, :note) + + expect(context.key?(:author)).to eq(false) + end + end + end + + describe '#render_attribute' do + it 'renders the attribute of an object' do + object = double(:doc, note: 'hello') + renderer = described_class.new(project, user, pipeline: :note) + doc = renderer.render_attribute(object, :note) + + expect(doc).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) + expect(doc.to_html).to eq('<p>hello</p>') + end + end + + describe '#base_context' do + let(:context) do + described_class.new(project, user, pipeline: :note).base_context + end + + it 'returns a Hash' do + expect(context).to be_an_instance_of(Hash) + end + + it 'includes the custom attributes' do + expect(context[:pipeline]).to eq(:note) + end + + it 'includes the current user' do + expect(context[:current_user]).to eq(user) + end + + it 'includes the current project' do + expect(context[:project]).to eq(project) + end + end +end diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb new file mode 100644 index 00000000000..488f465bcda --- /dev/null +++ b/spec/lib/banzai/redactor_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Banzai::Redactor do + let(:user) { build(:user) } + let(:project) { build(:empty_project) } + let(:redactor) { described_class.new(project, user) } + + describe '#redact' do + it 'redacts an Array of documents' do + doc1 = Nokogiri::HTML. + fragment('<a class="gfm" data-reference-type="issue">foo</a>') + + doc2 = Nokogiri::HTML. + fragment('<a class="gfm" data-reference-type="issue">bar</a>') + + expect(redactor).to receive(:nodes_visible_to_user).and_return([]) + + expect(redactor.redact([doc1, doc2])).to eq([doc1, doc2]) + + expect(doc1.to_html).to eq('foo') + expect(doc2.to_html).to eq('bar') + end + end + + describe '#redact_nodes' do + it 'redacts an Array of nodes' do + doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>') + node = doc.children[0] + + expect(redactor).to receive(:nodes_visible_to_user). + with([node]). + and_return(Set.new) + + redactor.redact_nodes([node]) + + expect(doc.to_html).to eq('foo') + end + end + + describe '#nodes_visible_to_user' do + it 'returns a Set containing the visible nodes' do + doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>') + node = doc.children[0] + + expect_any_instance_of(Banzai::ReferenceParser::IssueParser). + to receive(:nodes_visible_to_user). + with(user, [node]). + and_return([node]) + + expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node])) + 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 d562d8b25ea..2ca9f554b07 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -951,7 +951,7 @@ EOT config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Before script config should be an array of strings") end it "returns errors if job before_script parameter is not an array of strings" do @@ -1206,5 +1206,17 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: dependencies parameter should be an array of strings") end end + + describe "Validate configuration templates" do + templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml") + + templates.each do |file| + it "does not return errors for #{file}" do + file = File.read(file) + + expect { GitlabCiYamlProcessor.new(file) }.not_to raise_error + end + end + end end end diff --git a/spec/lib/gitlab/ci/config/node/configurable_spec.rb b/spec/lib/gitlab/ci/config/node/configurable_spec.rb index 47c68f96dc8..9bbda6e7396 100644 --- a/spec/lib/gitlab/ci/config/node/configurable_spec.rb +++ b/spec/lib/gitlab/ci/config/node/configurable_spec.rb @@ -7,26 +7,26 @@ describe Gitlab::Ci::Config::Node::Configurable do node.include(described_class) end - describe 'allowed nodes' do + describe 'configured nodes' do before do node.class_eval do allow_node :object, Object, description: 'test object' end end - describe '#allowed_nodes' do - it 'has valid allowed nodes' do - expect(node.allowed_nodes).to include :object + describe '.nodes' do + it 'has valid nodes' do + expect(node.nodes).to include :object end it 'creates a node factory' do - expect(node.allowed_nodes[:object]) + expect(node.nodes[:object]) .to be_an_instance_of Gitlab::Ci::Config::Node::Factory end it 'returns a duplicated factory object' do - first_factory = node.allowed_nodes[:object] - second_factory = node.allowed_nodes[:object] + first_factory = node.nodes[:object] + second_factory = node.nodes[:object] expect(first_factory).not_to be_equal(second_factory) end diff --git a/spec/lib/gitlab/ci/config/node/factory_spec.rb b/spec/lib/gitlab/ci/config/node/factory_spec.rb index d681aa32456..01a707a6bd4 100644 --- a/spec/lib/gitlab/ci/config/node/factory_spec.rb +++ b/spec/lib/gitlab/ci/config/node/factory_spec.rb @@ -25,6 +25,16 @@ describe Gitlab::Ci::Config::Node::Factory do expect(entry.description).to eq 'test description' end end + + context 'when setting key' do + it 'creates entry with custom key' do + entry = factory + .with(value: ['ls', 'pwd'], key: 'test key') + .create! + + expect(entry.key).to eq 'test key' + end + end end context 'when not setting value' do diff --git a/spec/lib/gitlab/ci/config/node/global_spec.rb b/spec/lib/gitlab/ci/config/node/global_spec.rb index b1972172435..fddd53a2b57 100644 --- a/spec/lib/gitlab/ci/config/node/global_spec.rb +++ b/spec/lib/gitlab/ci/config/node/global_spec.rb @@ -3,13 +3,19 @@ require 'spec_helper' describe Gitlab::Ci::Config::Node::Global do let(:global) { described_class.new(hash) } - describe '#allowed_nodes' do + describe '.nodes' do it 'can contain global config keys' do - expect(global.allowed_nodes).to include :before_script + expect(described_class.nodes).to include :before_script end it 'returns a hash' do - expect(global.allowed_nodes).to be_a Hash + expect(described_class.nodes).to be_a Hash + end + end + + describe '#key' do + it 'returns underscored class name' do + expect(global.key).to eq 'global' end end @@ -79,7 +85,7 @@ describe Gitlab::Ci::Config::Node::Global do describe '#errors' do it 'reports errors from child nodes' do expect(global.errors) - .to include 'before_script should be an array of strings' + .to include 'Before script config should be an array of strings' end end diff --git a/spec/lib/gitlab/ci/config/node/script_spec.rb b/spec/lib/gitlab/ci/config/node/script_spec.rb index e4d6481f8a5..6af6aa15eef 100644 --- a/spec/lib/gitlab/ci/config/node/script_spec.rb +++ b/spec/lib/gitlab/ci/config/node/script_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' describe Gitlab::Ci::Config::Node::Script do - let(:entry) { described_class.new(value) } + let(:entry) { described_class.new(config) } - describe '#validate!' do - before { entry.validate! } + describe '#process!' do + before { entry.process! } - context 'when entry value is correct' do - let(:value) { ['ls', 'pwd'] } + context 'when entry config value is correct' do + let(:config) { ['ls', 'pwd'] } describe '#value' do it 'returns concatenated command' do @@ -29,12 +29,12 @@ describe Gitlab::Ci::Config::Node::Script do end context 'when entry value is not correct' do - let(:value) { 'ls' } + let(:config) { 'ls' } describe '#errors' do it 'saves errors' do expect(entry.errors) - .to include /should be an array of strings/ + .to include 'Script config should be an array of strings' end end diff --git a/spec/lib/gitlab/ci/config/node/validatable_spec.rb b/spec/lib/gitlab/ci/config/node/validatable_spec.rb new file mode 100644 index 00000000000..10cd01afcd1 --- /dev/null +++ b/spec/lib/gitlab/ci/config/node/validatable_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Node::Validatable do + let(:node) { Class.new } + + before do + node.include(described_class) + end + + describe '.validator' do + before do + node.class_eval do + attr_accessor :test_attribute + + validations do + validates :test_attribute, presence: true + end + end + end + + it 'returns validator' do + expect(node.validator.superclass) + .to be Gitlab::Ci::Config::Node::Validator + end + + context 'when validating node instance' do + let(:node_instance) { node.new } + + context 'when attribute is valid' do + before do + node_instance.test_attribute = 'valid' + end + + it 'instance of validator is valid' do + expect(node.validator.new(node_instance)).to be_valid + end + end + + context 'when attribute is not valid' do + before do + node_instance.test_attribute = nil + end + + it 'instance of validator is invalid' do + expect(node.validator.new(node_instance)).to be_invalid + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/node/validator_spec.rb b/spec/lib/gitlab/ci/config/node/validator_spec.rb new file mode 100644 index 00000000000..ad875d55384 --- /dev/null +++ b/spec/lib/gitlab/ci/config/node/validator_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Node::Validator do + let(:validator) { Class.new(described_class) } + let(:validator_instance) { validator.new(node) } + let(:node) { spy('node') } + + shared_examples 'delegated validator' do + context 'when node is valid' do + before do + allow(node).to receive(:test_attribute).and_return('valid value') + end + + it 'validates attribute in node' do + expect(node).to receive(:test_attribute) + expect(validator_instance).to be_valid + end + + it 'returns no errors' do + validator_instance.validate + + expect(validator_instance.full_errors).to be_empty + end + end + + context 'when node is invalid' do + before do + allow(node).to receive(:test_attribute).and_return(nil) + end + + it 'validates attribute in node' do + expect(node).to receive(:test_attribute) + expect(validator_instance).to be_invalid + end + + it 'returns errors' do + validator_instance.validate + + expect(validator_instance.full_errors).not_to be_empty + end + end + end + + describe 'attributes validations' do + before do + validator.class_eval do + validates :test_attribute, presence: true + end + end + + it_behaves_like 'delegated validator' + end + + describe 'interface validations' do + before do + validator.class_eval do + validate do + unless @node.test_attribute == 'valid value' + errors.add(:test_attribute, 'invalid value') + end + end + end + end + + it_behaves_like 'delegated validator' + end +end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 3871d939feb..2a5d132db7b 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -67,6 +67,12 @@ describe Gitlab::Ci::Config do expect(config.errors).not_to be_empty end end + + describe '#errors' do + it 'returns an array of strings' do + expect(config.errors).to all(be_an_instance_of(String)) + end + end end end end diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 1620eb6c60a..364532e94e3 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -4,6 +4,7 @@ describe Gitlab::Highlight, lib: true do include RepoHelpers let(:project) { create(:project) } + let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } describe '.highlight_lines' do @@ -18,4 +19,30 @@ describe Gitlab::Highlight, lib: true do end end + describe 'custom highlighting from .gitattributes' do + let(:branch) { 'gitattributes' } + let(:blob) { repository.blob_at_branch(branch, path) } + + let(:highlighter) do + Gitlab::Highlight.new(blob.path, blob.data, repository: repository) + end + + before { project.change_head('gitattributes') } + + describe 'basic language selection' do + let(:path) { 'custom-highlighting/test.gitlab-custom' } + it 'highlights as ruby' do + expect(highlighter.lexer.tag).to eq 'ruby' + end + end + + describe 'cgi options' do + let(:path) { 'custom-highlighting/test.gitlab-cgi' } + + it 'highlights as json with erb' do + expect(highlighter.lexer.tag).to eq 'erb' + expect(highlighter.lexer.parent.tag).to eq 'json' + end + end + end end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 400d44ac162..403bd582ef3 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -4894,6 +4894,29 @@ "started_at": null, "finished_at": null, "duration": null, + "notes": [ + { + "id": 999, + "note": "Natus rerum qui dolorem dolorum voluptas.", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2016-03-22T15:19:59.469Z", + "updated_at": "2016-03-22T15:19:59.469Z", + "project_id": 5, + "attachment": { + "url": null + }, + "line_code": null, + "commit_id": "be93687618e4b132087f430a4d8fc3a609c9b77c", + "noteable_id": 36, + "system": false, + "st_diff": null, + "updated_by_id": null, + "author": { + "name": "Administrator" + } + } + ], "statuses": [ { "id": 71, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 7a40a43f8ae..23036ab8108 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -18,6 +18,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do it 'restores models based on JSON' do expect(restored_project_json).to be true end + + it 'creates a valid pipeline note' do + restored_project_json + + expect(Ci::Pipeline.first.notes).not_to be_empty + end end end end diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb index 109522fa626..211ef68dfab 100644 --- a/spec/lib/gitlab/import_export/reader_spec.rb +++ b/spec/lib/gitlab/import_export/reader_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::ImportExport::Reader, lib: true do - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path:'') } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') } let(:test_config) { 'spec/support/import_export/import_export.yml' } let(:project_tree_hash) do { diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb index e520a968999..4d2aa03e722 100644 --- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::Metrics::SidekiqMiddleware do let(:middleware) { described_class.new } + let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } } describe '#call' do it 'tracks the transaction' do @@ -11,9 +12,23 @@ describe Gitlab::Metrics::SidekiqMiddleware do with('TestWorker#perform'). and_call_original + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float)) expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish) - middleware.call(worker, 'test', :test) { nil } + middleware.call(worker, message, :test) { nil } + end + + it 'tracks the transaction (for messages without `enqueued_at`)' do + worker = double(:worker, class: double(:class, name: 'TestWorker')) + + expect(Gitlab::Metrics::Transaction).to receive(:new). + with('TestWorker#perform'). + and_call_original + + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float)) + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish) + + middleware.call(worker, {}, :test) { nil } end end end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index d6ae54e25e8..cf0e282c2fb 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -28,8 +28,20 @@ describe Gitlab::Metrics::System do end describe '.cpu_time' do - it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an_instance_of(Fixnum) + it 'returns a Float' do + expect(described_class.cpu_time).to be_an_instance_of(Float) + end + end + + describe '.real_time' do + it 'returns a Float' do + expect(described_class.real_time).to be_an_instance_of(Float) + end + end + + describe '.monotonic_time' do + it 'returns a Float' do + expect(described_class.monotonic_time).to be_an_instance_of(Float) end end end diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 6727a83e58a..5ec5ab40b6f 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -122,7 +122,7 @@ describe Gitlab::OAuth::User, lib: true do before do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } - allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] } + allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) end @@ -203,7 +203,7 @@ describe Gitlab::OAuth::User, lib: true do stub_omniauth_config(auto_link_ldap_user: true) allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } - allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] } + allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } allow(oauth_user).to receive(:ldap_person).and_return(ldap_user) end diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 84c21ceefd9..2753aecc1f4 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -164,7 +164,14 @@ describe Gitlab::Saml::User, lib: true do end context 'and LDAP user has an account already' do - let!(:existing_user) { create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + before do + create(:omniauth_user, + email: 'john@mail.com', + extern_uid: 'uid=user1,ou=People,dc=example', + provider: 'ldapmain', + username: 'john') + end + it 'adds the omniauth identity to the LDAP account' do saml_user.save @@ -177,6 +184,15 @@ describe Gitlab::Saml::User, lib: true do { provider: 'saml', extern_uid: uid } ]) end + + it 'saves successfully on subsequent tries, when both identities are present' do + saml_user.save + local_saml_user = described_class.new(auth_hash) + local_saml_user.save + + expect(local_saml_user.gl_user).to be_valid + expect(local_saml_user.gl_user).to be_persisted + end end context 'user has SAML user, and wants to add their LDAP identity' do diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index efbcbf72f76..89730ab8eb8 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -154,6 +154,20 @@ describe Issue, "Issuable" do expect(issues).to match_array([issue1, issue2, issue, issue3]) end end + + context 'when all of the results are level on the sort key' do + let!(:issues) do + 10.times { create(:issue, project: project) } + end + + it 'has no duplicates across pages' do + sorted_issue_ids = 1.upto(10).map do |i| + project.issues.sort('milestone_due_desc').page(i).per(1).first.id + end + + expect(sorted_issue_ids).to eq(sorted_issue_ids.uniq) + end + end end diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb new file mode 100644 index 00000000000..a6d9717ccb5 --- /dev/null +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -0,0 +1,49 @@ +# == 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 +# + +require 'spec_helper' + +describe BugzillaService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 53c8408633c..d305cd9ff1e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -63,6 +63,27 @@ describe Project, models: true do expect(project2).not_to be_valid expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/) end + + describe 'wiki path conflict' do + context "when the new path has been used by the wiki of other Project" do + it 'should have an error on the name attribute' do + new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki") + + expect(new_project).not_to be_valid + expect(new_project.errors[:name].first).to eq('has already been taken') + end + end + + context "when the new wiki path has been used by the path of other Project" do + it 'should have an error on the name attribute' do + project_with_wiki_suffix = create(:project, path: 'foo.wiki') + new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo') + + expect(new_project).not_to be_valid + expect(new_project.errors[:name].first).to eq('has already been taken') + end + end + end end describe 'default_scope' do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 789816bf2c7..0621c6a06ce 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -72,7 +72,7 @@ describe Snippet, models: true do end end - describe '#search_code' do + describe '.search_code' do let(:snippet) { create(:snippet, content: 'class Foo; end') } it 'returns snippets with matching content' do @@ -88,6 +88,46 @@ describe Snippet, models: true do end end + describe '.accessible_to' do + let(:author) { create(:author) } + let(:project) { create(:empty_project) } + + let!(:public_snippet) { create(:snippet, :public) } + let!(:internal_snippet) { create(:snippet, :internal) } + let!(:private_snippet) { create(:snippet, :private, author: author) } + + let!(:project_public_snippet) { create(:snippet, :public, project: project) } + let!(:project_internal_snippet) { create(:snippet, :internal, project: project) } + let!(:project_private_snippet) { create(:snippet, :private, project: project) } + + it 'returns only public snippets when user is blank' do + expect(described_class.accessible_to(nil)).to match_array [public_snippet, project_public_snippet] + end + + it 'returns only public, and internal snippets for regular users' do + user = create(:user) + + expect(described_class.accessible_to(user)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns public, internal snippets and project private snippets for project members' do + member = create(:user) + project.team << [member, :developer] + + expect(described_class.accessible_to(member)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + + it 'returns private snippets where the user is the author' do + expect(described_class.accessible_to(author)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns all snippets when for admins' do + admin = create(:admin) + + expect(described_class.accessible_to(admin)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + end + describe '#participants' do let(:project) { create(:project, :public) } let(:snippet) { create(:snippet, content: 'foo', project: project) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 73bee535fe3..328254ed56b 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -31,6 +31,26 @@ describe User, models: true do it { is_expected.to have_many(:spam_logs).dependent(:destroy) } it { is_expected.to have_many(:todos).dependent(:destroy) } it { is_expected.to have_many(:award_emoji).dependent(:destroy) } + + describe '#group_members' do + it 'does not include group memberships for which user is a requester' do + user = create(:user) + group = create(:group, :public) + group.request_access(user) + + expect(user.group_members).to be_empty + end + end + + describe '#project_members' do + it 'does not include project memberships for which user is a requester' do + user = create(:user) + project = create(:project, :public) + project.request_access(user) + + expect(user.project_members).to be_empty + end + end end describe 'validations' do diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index 2e65e7f1920..ed78d582bd0 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -17,7 +17,7 @@ describe API::API, api: true do it "returns an array of award_emoji" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(award_emoji.name) end @@ -25,7 +25,7 @@ describe API::API, api: true do it "should return a 404 error when issue id not found" do get api("/projects/#{project.id}/issues/12345/award_emoji", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -33,7 +33,7 @@ describe API::API, api: true do it "returns an array of award_emoji" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(downvote.name) end @@ -45,7 +45,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -56,7 +56,7 @@ describe API::API, api: true do it 'returns an array of award emoji' do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(rocket.name) end @@ -68,7 +68,7 @@ describe API::API, api: true do it "returns the award emoji" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(award_emoji.name) expect(json_response['awardable_id']).to eq(issue.id) expect(json_response['awardable_type']).to eq("Issue") @@ -77,7 +77,7 @@ describe API::API, api: true do it "returns a 404 error if the award is not found" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -85,7 +85,7 @@ describe API::API, api: true do it 'returns the award emoji' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(downvote.name) expect(json_response['awardable_id']).to eq(merge_request.id) expect(json_response['awardable_type']).to eq("MergeRequest") @@ -98,7 +98,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -109,7 +109,7 @@ describe API::API, api: true do it 'returns an award emoji' do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).not_to be_an Array expect(json_response['name']).to eq(rocket.name) end @@ -120,7 +120,7 @@ describe API::API, api: true do it "creates a new award emoji" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('blowfish') expect(json_response['user']['username']).to eq(user.username) end @@ -128,13 +128,13 @@ describe API::API, api: true do it "should return a 400 bad request error if the name is not given" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if the user is not authenticated" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji"), name: 'thumbsup' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -145,7 +145,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket' end.to change { note.award_emoji.count }.from(0).to(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['user']['username']).to eq(user.username) end end @@ -157,13 +157,13 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) end.to change { issue.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'returns a 404 error when the award emoji can not be found' do delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -173,13 +173,13 @@ describe API::API, api: true do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) end.to change { merge_request.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -192,7 +192,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user) end.to change { note.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 55582aa53d2..b11ca26ee68 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -17,7 +17,7 @@ describe API::API, api: true do project.repository.expire_cache get api("/projects/#{project.id}/repository/branches", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array branch_names = json_response.map { |x| x['name'] } expect(branch_names).to match_array(project.repository.branch_names) @@ -27,7 +27,7 @@ describe API::API, api: true do describe "GET /projects/:id/repository/branches/:branch" do it "should return the branch information for a single branch" do get api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -36,19 +36,19 @@ describe API::API, api: true do it "should return a 403 error if guest" do get api("/projects/#{project.id}/repository/branches", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return a 404 error if branch is not available" do get api("/projects/#{project.id}/repository/branches/unknown", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe "PUT /projects/:id/repository/branches/:branch/protect" do it "should protect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -57,25 +57,25 @@ describe API::API, api: true do it "should return a 404 error if branch not found" do put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 403 error if guest" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return success when protect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "PUT /projects/:id/repository/branches/:branch/unprotect" do it "should unprotect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -84,13 +84,13 @@ describe API::API, api: true do it "should return success when unprotect branch" do put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return success when unprotect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -100,7 +100,7 @@ describe API::API, api: true do branch_name: 'feature1', ref: branch_sha - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('feature1') expect(json_response['commit']['id']).to eq(branch_sha) @@ -110,14 +110,14 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user2), branch_name: branch_name, ref: branch_sha - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should return 400 if branch name is invalid' do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new design', ref: branch_sha - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') end @@ -125,12 +125,12 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - expect(response.status).to eq(201) + expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch already exists') end @@ -138,7 +138,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design3', ref: 'foo' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Invalid reference name') end end @@ -150,25 +150,25 @@ describe API::API, api: true do it "should remove branch" do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['branch_name']).to eq(branch_name) end it 'should return 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should remove protected branch" do project.protected_branches.create(name: branch_name) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('Protected branch cant be removed') end it "should not remove HEAD branch" do delete api("/projects/#{project.id}/repository/branches/master", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('Cannot remove HEAD branch') end end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 47e9253a10c..f5b39c3d698 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -19,7 +19,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return project builds' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end @@ -32,7 +32,7 @@ describe API::API, api: true do let(:query) { 'scope=pending' } it do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end end @@ -41,7 +41,7 @@ describe API::API, api: true do let(:query) { 'scope[0]=pending&scope[1]=running' } it do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end end @@ -49,7 +49,7 @@ describe API::API, api: true do context 'respond 400 when scope contains invalid state' do let(:query) { 'scope[0]=pending&scope[1]=unknown_status' } - it { expect(response.status).to eq(400) } + it { expect(response).to have_http_status(400) } end end @@ -57,29 +57,66 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return project builds' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end describe 'GET /projects/:id/repository/commits/:sha/builds' do - before do - project.ensure_pipeline(pipeline.sha, 'master') - get api("/projects/#{project.id}/repository/commits/#{pipeline.sha}/builds", api_user) - end + context 'when commit does not exist in repository' do + before do + get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user) + end - context 'authorized user' do - it 'should return project builds for specific commit' do - expect(response.status).to eq(200) - expect(json_response).to be_an Array + it 'responds with 404' do + expect(response).to have_http_status(404) end end - context 'unauthorized user' do - let(:api_user) { nil } + context 'when commit exists in repository' do + context 'when user is authorized' do + context 'when pipeline has builds' do + before do + create(:ci_pipeline, project: project, sha: project.commit.id) + create(:ci_build, pipeline: pipeline) + create(:ci_build) + + get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) + end + + it 'should return project builds for specific commit' do + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq 2 + end + end - it 'should not return project builds' do - expect(response.status).to eq(401) + context 'when pipeline has no builds' do + before do + branch_head = project.commit('feature').id + get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) + end + + it 'returns an empty array' do + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response).to be_empty + end + end + end + + context 'when user is not authorized' do + before do + create(:ci_pipeline, project: project, sha: project.commit.id) + create(:ci_build, pipeline: pipeline) + + get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) + end + + it 'should not return project builds' do + expect(response).to have_http_status(401) + expect(json_response.except('message')).to be_empty + end end end end @@ -89,7 +126,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return specific build data' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('test') end end @@ -98,7 +135,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build data' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -116,7 +153,7 @@ describe API::API, api: true do end it 'should return specific build artifacts' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.headers).to include(download_headers) end end @@ -125,13 +162,13 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build artifacts' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end it 'should not return build artifacts if not uploaded' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -142,7 +179,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return specific build trace' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq(build.trace) end end @@ -151,7 +188,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build trace' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -162,7 +199,7 @@ describe API::API, api: true do context 'authorized user' do context 'user with :update_build persmission' do it 'should cancel running or pending build' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(project.builds.first.status).to eq('canceled') end end @@ -171,7 +208,7 @@ describe API::API, api: true do let(:api_user) { user2 } it 'should not cancel build' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -180,7 +217,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not cancel build' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -193,7 +230,7 @@ describe API::API, api: true do context 'authorized user' do context 'user with :update_build permission' do it 'should retry non-running build' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(project.builds.first.status).to eq('canceled') expect(json_response['status']).to eq('pending') end @@ -203,7 +240,7 @@ describe API::API, api: true do let(:api_user) { user2 } it 'should not retry build' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -212,7 +249,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not retry build' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 298cdbad329..6668f3543f6 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -41,7 +41,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter) } it 'returns latest commit statuses' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id) @@ -54,7 +54,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), all: 1 } it 'returns all commit statuses' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status1.id, status2.id, @@ -67,7 +67,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), ref: 'develop' } it 'returns latest commit statuses for specific ref' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status3.id, status5.id) @@ -78,7 +78,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), name: 'coverage' } it 'return latest commit statuses for specific name' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status4.id, status5.id) @@ -101,7 +101,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, guest) } it "should not return project commits" do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -109,7 +109,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url) } it "should not return project commits" do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -122,7 +122,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'success' } it 'creates commit status' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') expect(json_response['name']).to eq('default') @@ -141,7 +141,7 @@ describe API::CommitStatuses, api: true do end it 'creates commit status' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') expect(json_response['name']).to eq('coverage') @@ -155,7 +155,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'invalid' } it 'does not create commit status' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -163,7 +163,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer) } it 'does not create commit status' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -172,7 +172,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'running' } it 'returns not found error' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -181,7 +181,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, reporter) } it 'should not create commit status' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -189,7 +189,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, guest) } it 'should not create commit status' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -197,7 +197,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url) } it 'should not create commit status' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 6fc38f537d3..5219c808791 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -19,7 +19,7 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/commits", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project.repository.commit.id) @@ -29,7 +29,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/commits") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -63,7 +63,7 @@ describe API::API, api: true do it "should return an invalid parameter error message" do get api("/projects/#{project.id}/repository/commits?since=invalid-date", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format" end end @@ -73,26 +73,26 @@ describe API::API, api: true do context "authorized user" do it "should return a commit by sha" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(project.repository.commit.id) expect(json_response['title']).to eq(project.repository.commit.title) end it "should return a 404 error if not found" do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return nil for commit without CI" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['status']).to be_nil end it "should return status for CI" do pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master') get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['status']).to eq(pipeline.status) end end @@ -100,7 +100,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -111,7 +111,7 @@ describe API::API, api: true do it "should return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to be >= 1 @@ -120,14 +120,14 @@ describe API::API, api: true do it "should return a 404 error if invalid commit" do get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "unauthorized user" do it "should not return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -136,7 +136,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return merge_request comments' do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['note']).to eq('a comment on a commit') @@ -145,14 +145,14 @@ describe API::API, api: true do it 'should return a 404 error if merge_request_id not found' do get api("/projects/#{project.id}/repository/commits/1234ab/comments", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do get api("/projects/#{project.id}/repository/commits/1234ab/comments") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -161,7 +161,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['path']).to be_nil expect(json_response['line']).to be_nil @@ -170,7 +170,7 @@ describe API::API, api: true do it 'should return the inline comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.diffs.first.new_path, line: 7, line_type: 'new' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['path']).to eq(project.repository.commit.diffs.first.new_path) expect(json_response['line']).to eq(7) @@ -179,19 +179,19 @@ describe API::API, api: true do it 'should return 400 if note is missing' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 404 if note is attached to non existent commit' do post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index 0afc3e79339..881b818b5e9 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -11,21 +11,21 @@ describe API::API, api: true do describe "when unauthenticated" do it "returns authentication success" do get api("/user"), access_token: token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "when token invalid" do it "returns authentication error" do get api("/user"), access_token: "123a" - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end describe "authorization by private token" do it "returns authentication success" do get api("/user", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 8efa09f75fd..2e5448143d5 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -16,7 +16,7 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq('popen.rb') expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') @@ -25,7 +25,7 @@ describe API::API, api: true do it "should return a 400 bad request if no params given" do get api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 404 if such file does not exist" do @@ -35,7 +35,7 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -51,13 +51,13 @@ describe API::API, api: true do it "should create a new file in project repo" do post api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['file_path']).to eq('newfile.rb') end it "should return a 400 bad request if no params given" do post api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 if editor fails to create file" do @@ -65,7 +65,7 @@ describe API::API, api: true do and_return(false) post api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -81,13 +81,13 @@ describe API::API, api: true do it "should update existing file in project repo" do put api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do put api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -102,20 +102,20 @@ describe API::API, api: true do it "should delete existing file in project repo" do delete api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do delete api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 if fails to create file" do allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) delete api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -143,7 +143,7 @@ describe API::API, api: true do it "remains unchanged" do get api("/projects/#{project.id}/repository/files", user), get_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq(file_path) expect(json_response['content']).to eq(put_params[:content]) diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb index fa94e03ec32..a9f5aa924b7 100644 --- a/spec/requests/api/fork_spec.rb +++ b/spec/requests/api/fork_spec.rb @@ -22,7 +22,7 @@ describe API::API, api: true do context 'when authenticated' do it 'should fork if user has sufficient access to project' do post api("/projects/fork/#{project.id}", user2) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(project.name) expect(json_response['path']).to eq(project.path) expect(json_response['owner']['id']).to eq(user2.id) @@ -32,7 +32,7 @@ describe API::API, api: true do it 'should fork if user is admin' do post api("/projects/fork/#{project.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(project.name) expect(json_response['path']).to eq(project.path) expect(json_response['owner']['id']).to eq(admin.id) @@ -42,20 +42,20 @@ describe API::API, api: true do it 'should fail on missing project access for the project to fork' do post api("/projects/fork/#{project.id}", user3) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should fail if forked project exists in the user namespace' do post api("/projects/fork/#{project.id}", user) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']['name']).to eq(['has already been taken']) expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should fail if project to fork from does not exist' do post api('/projects/fork/424242', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end end @@ -63,7 +63,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do post api("/projects/fork/#{project.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['message']).to eq('401 Unauthorized') end end diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb index 02553d0f8e2..52f9e7d4681 100644 --- a/spec/requests/api/group_members_spec.rb +++ b/spec/requests/api/group_members_spec.rb @@ -31,7 +31,7 @@ describe API::API, api: true do it "each user: should return an array of members groups of group3" do [owner, master, developer, reporter, guest].each do |user| get api("/groups/#{group_with_members.id}/members", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(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) @@ -45,7 +45,7 @@ describe API::API, api: true do it 'users not part of the group should get access error' do get api("/groups/#{group_with_members.id}/members", stranger) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -54,7 +54,7 @@ describe API::API, api: true do context "when not a member of the group" do it "should not add guest as member of group_no_members when adding being done by person outside the group" do post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -66,7 +66,7 @@ describe API::API, api: true do post api("/groups/#{group_no_members.id}/members", owner), user_id: new_user.id, access_level: GroupMember::MASTER end.to change { group_no_members.members.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(new_user.name) expect(json_response['access_level']).to eq(GroupMember::MASTER) end @@ -78,27 +78,27 @@ describe API::API, api: true do post api("/groups/#{group_with_members.id}/members", guest), user_id: new_user.id, access_level: GroupMember::MASTER end.not_to change { group_with_members.members.count } - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return error if member already exists" do post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end it "should return a 400 error when user id is not given" do post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 error when access level is not given" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end end @@ -110,7 +110,7 @@ describe API::API, api: true do api("/groups/#{group_no_members.id}/members/#{developer.id}", owner), access_level: GroupMember::MASTER ) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -122,7 +122,7 @@ describe API::API, api: true do access_level: GroupMember::MASTER ) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) get api("/groups/#{group_with_members.id}/members", owner) json_reporter = json_response.find do |e| @@ -139,7 +139,7 @@ describe API::API, api: true do access_level: GroupMember::MASTER ) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) get api("/groups/#{group_with_members.id}/members", owner) json_developer = json_response.find do |e| @@ -153,7 +153,7 @@ describe API::API, api: true do put( api("/groups/#{group_with_members.id}/members/#{master.id}", owner) ) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return a 422 error when access level is not known' do @@ -161,7 +161,7 @@ describe API::API, api: true do api("/groups/#{group_with_members.id}/members/#{master.id}", owner), access_level: 1234 ) - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end end @@ -172,7 +172,7 @@ describe API::API, api: true do random_user = create(:user) delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -182,17 +182,17 @@ describe API::API, api: true do delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) end.to change { group_with_members.members.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return a 404 error when user id is not known" do delete api("/groups/#{group_with_members.id}/members/1328", owner) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not allow guest to modify group members" do delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 7ecefce80d6..04141a45031 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -23,14 +23,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/groups") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated as user" do it "normal user: should return an array of groups of user1" do get api("/groups", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(group1.name) @@ -40,7 +40,7 @@ describe API::API, api: true do context "when authenticated as admin" do it "admin: should return an array of all groups" do get api("/groups", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) end @@ -51,51 +51,51 @@ describe API::API, api: true do context "when authenticated as user" do it "should return one of user1's groups" do get api("/groups/#{group1.id}", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) json_response['name'] == group1.name end it "should not return a non existing group" do get api("/groups/1328", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not return a group not attached to user1" do get api("/groups/#{group2.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "when authenticated as admin" do it "should return any existing group" do get api("/groups/#{group2.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(group2.name) end it "should not return a non existing group" do get api("/groups/1328", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'when using group path in URL' do it 'should return any existing group' do get api("/groups/#{group1.path}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(group1.name) end it 'should not return a non existing group' do get api('/groups/unknown', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not return a group not attached to user1' do get api("/groups/#{group2.path}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -107,14 +107,14 @@ describe API::API, api: true do it 'updates the group' do put api("/groups/#{group1.id}", user1), name: new_group_name - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(new_group_name) end it 'returns 404 for a non existing group' do put api('/groups/1328', user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -122,7 +122,7 @@ describe API::API, api: true do it 'updates the group' do put api("/groups/#{group1.id}", admin), name: new_group_name - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(new_group_name) end end @@ -131,7 +131,7 @@ describe API::API, api: true do it 'does not updates the group' do put api("/groups/#{group1.id}", user2), name: new_group_name - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -139,7 +139,7 @@ describe API::API, api: true do it 'returns 404 when trying to update the group' do put api("/groups/#{group2.id}", user1), name: new_group_name - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -149,7 +149,7 @@ describe API::API, api: true do it "should return the group's projects" do get api("/groups/#{group1.id}/projects", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(2) project_names = json_response.map { |proj| proj['name' ] } expect(project_names).to match_array([project1.name, project3.name]) @@ -157,13 +157,13 @@ describe API::API, api: true do it "should not return a non existing group" do get api("/groups/1328/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not return a group not attached to user1" do get api("/groups/#{group2.id}/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should only return projects to which user has access" do @@ -171,7 +171,7 @@ describe API::API, api: true do get api("/groups/#{group1.id}/projects", user3) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(project3.name) end @@ -180,14 +180,14 @@ describe API::API, api: true do context "when authenticated as admin" do it "should return any existing group" do get api("/groups/#{group2.id}/projects", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(project2.name) end it "should not return a non existing group" do get api("/groups/1328/projects", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -195,20 +195,20 @@ describe API::API, api: true do it 'should return any existing group' do get api("/groups/#{group1.path}/projects", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_names = json_response.map { |proj| proj['name' ] } expect(project_names).to match_array([project1.name, project3.name]) end it 'should not return a non existing group' do get api('/groups/unknown/projects', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not return a group not attached to user1' do get api("/groups/#{group2.path}/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -217,30 +217,30 @@ describe API::API, api: true do context "when authenticated as user without group permissions" do it "should not create group" do post api("/groups", user1), attributes_for(:group) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as user with group permissions" do it "should create group" do post api("/groups", user3), attributes_for(:group) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should not create group, duplicate" do post api("/groups", user3), { name: 'Duplicate Test', path: group2.path } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(response.message).to eq("Bad Request") end it "should return 400 bad request error if name not given" do post api("/groups", user3), { path: group2.path } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 bad request error if path not given" do post api("/groups", user3), { name: 'test' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end end @@ -249,37 +249,37 @@ describe API::API, api: true do context "when authenticated as user" do it "should remove group" do delete api("/groups/#{group1.id}", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should not remove a group if not an owner" do user4 = create(:user) group1.add_master(user4) delete api("/groups/#{group1.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should not remove a non existing group" do delete api("/groups/1328", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not remove a group not attached to user1" do delete api("/groups/#{group2.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "when authenticated as admin" do it "should remove any existing group" do delete api("/groups/#{group2.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should not remove a non existing group" do delete api("/groups/1328", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -295,14 +295,14 @@ describe API::API, api: true do context "when authenticated as user" do it "should not transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as admin" do it "should transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 22802dd0e05..437c89c3577 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -11,7 +11,7 @@ describe API::API, api: true do it do get api("/internal/check"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['api_version']).to eq(API::API.version) end end @@ -23,7 +23,7 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["message"]).to eq(broadcast_message.message) end end @@ -32,7 +32,7 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_empty end end @@ -42,7 +42,7 @@ describe API::API, api: true do it do get(api("/internal/discover"), key_id: key.id, secret_token: secret_token) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(user.name) end @@ -61,7 +61,7 @@ describe API::API, api: true do push(key, project_wiki) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -70,7 +70,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -79,7 +79,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -94,7 +94,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -103,7 +103,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -120,7 +120,7 @@ describe API::API, api: true do it do pull(key, personal_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -129,7 +129,7 @@ describe API::API, api: true do it do push(key, personal_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -147,7 +147,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -156,7 +156,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -173,7 +173,7 @@ describe API::API, api: true do it do archive(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -182,7 +182,7 @@ describe API::API, api: true do it do archive(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -192,7 +192,7 @@ describe API::API, api: true do it do pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists')) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -201,7 +201,7 @@ describe API::API, api: true do it do pull(OpenStruct.new(id: 0), project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 59e557c5b2a..2cf130df328 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -51,14 +51,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/issues") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated" do it "should return an array of issues" do get api("/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(issue.title) end @@ -72,7 +72,7 @@ describe API::API, api: true do it 'should return an array of closed issues' do get api('/issues?state=closed', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_issue.id) @@ -80,7 +80,7 @@ describe API::API, api: true do it 'should return an array of opened issues' do get api('/issues?state=opened', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(issue.id) @@ -88,7 +88,7 @@ describe API::API, api: true do it 'should return an array of all issues' do get api('/issues?state=all', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['id']).to eq(issue.id) @@ -97,7 +97,7 @@ describe API::API, api: true do it 'should return an array of labeled issues' do get api("/issues?labels=#{label.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -105,7 +105,7 @@ describe API::API, api: true do it 'should return an array of labeled issues when at least one label matches' do get api("/issues?labels=#{label.title},foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -113,14 +113,14 @@ describe API::API, api: true do it 'should return an empty array if no issue matches labels' do get api('/issues?labels=foo,bar', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an array of labeled issues matching given state' do get api("/issues?labels=#{label.title}&state=opened", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -129,20 +129,162 @@ describe API::API, api: true do it 'should return an empty array if no issue matches labels and state filters' do get api("/issues?labels=#{label.title}&state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end end end + describe "GET /groups/:id/issues" do + let!(:group) { create(:group) } + let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) } + let!(:group_closed_issue) do + create :closed_issue, + author: user, + assignee: user, + project: group_project, + state: :closed, + milestone: group_milestone + end + let!(:group_confidential_issue) do + create :issue, + :confidential, + project: group_project, + author: author, + assignee: assignee + end + let!(:group_issue) do + create :issue, + author: user, + assignee: user, + project: group_project, + milestone: group_milestone + end + let!(:group_label) do + create(:label, title: 'group_lbl', color: '#FFAABB', project: group_project) + end + let!(:group_label_link) { create(:label_link, label: group_label, target: group_issue) } + let!(:group_milestone) { create(:milestone, title: '3.0.0', project: group_project) } + let!(:group_empty_milestone) do + create(:milestone, title: '4.0.0', project: group_project) + end + let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) } + + before do + group_project.team << [user, :reporter] + end + let(:base_url) { "/groups/#{group.id}/issues" } + + it 'returns group issues without confidential issues for non project members' do + get api(base_url, non_member) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['title']).to eq(group_issue.title) + end + + it 'returns group confidential issues for author' do + get api(base_url, author) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group confidential issues for assignee' do + get api(base_url, assignee) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group issues with confidential issues for project members' do + get api(base_url, user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group confidential issues for admin' do + get api(base_url, admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns an array of labeled group issues' do + get api("#{base_url}?labels=#{group_label.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([group_label.title]) + end + + it 'returns an array of labeled group issues where all labels match' do + get api("#{base_url}?labels=#{group_label.title},foo,bar", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if no group issue matches labels' do + get api("#{base_url}?labels=foo,bar", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if no issue matches milestone' do + get api("#{base_url}?milestone=#{group_empty_milestone.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if milestone does not exist' do + get api("#{base_url}?milestone=foo", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an array of issues in given milestone' do + get api("#{base_url}?milestone=#{group_milestone.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(group_issue.id) + end + + it 'returns an array of issues matching state in milestone' do + get api("#{base_url}?milestone=#{group_milestone.title}"\ + '&state=closed', user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(group_closed_issue.id) + end + end + describe "GET /projects/:id/issues" do let(:base_url) { "/projects/#{project.id}" } let(:title) { milestone.title } it 'should return project issues without confidential issues for non project members' do get api("#{base_url}/issues", non_member) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['title']).to eq(issue.title) @@ -150,7 +292,7 @@ describe API::API, api: true do it 'should return project issues without confidential issues for project members with guest role' do get api("#{base_url}/issues", guest) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['title']).to eq(issue.title) @@ -158,7 +300,7 @@ describe API::API, api: true do it 'should return project confidential issues for author' do get api("#{base_url}/issues", author) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -166,7 +308,7 @@ describe API::API, api: true do it 'should return project confidential issues for assignee' do get api("#{base_url}/issues", assignee) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -174,7 +316,7 @@ describe API::API, api: true do it 'should return project issues with confidential issues for project members' do get api("#{base_url}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -182,7 +324,7 @@ describe API::API, api: true do it 'should return project confidential issues for admin' do get api("#{base_url}/issues", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -190,7 +332,7 @@ describe API::API, api: true do it 'should return an array of labeled project issues' do get api("#{base_url}/issues?labels=#{label.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -198,7 +340,7 @@ describe API::API, api: true do it 'should return an array of labeled project issues when at least one label matches' do get api("#{base_url}/issues?labels=#{label.title},foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -206,28 +348,28 @@ describe API::API, api: true do it 'should return an empty array if no project issue matches labels' do get api("#{base_url}/issues?labels=foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an empty array if no issue matches milestone' do get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an empty array if milestone does not exist' do get api("#{base_url}/issues?milestone=foo", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an array of issues in given milestone' do get api("#{base_url}/issues?milestone=#{title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['id']).to eq(issue.id) @@ -237,7 +379,7 @@ describe API::API, api: true do it 'should return an array of issues matching state in milestone' do get api("#{base_url}/issues?milestone=#{milestone.title}"\ '&state=closed', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_issue.id) @@ -248,7 +390,7 @@ describe API::API, api: true do it 'exposes known attributes' do get api("/projects/#{project.id}/issues/#{issue.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(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) @@ -266,7 +408,7 @@ describe API::API, api: true do 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(response).to have_http_status(200) expect(json_response['title']).to eq(issue.title) expect(json_response['iid']).to eq(issue.iid) end @@ -281,44 +423,44 @@ describe API::API, api: true do it "should return 404 if issue id not found" do get api("/projects/#{project.id}/issues/54321", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'confidential issues' do it "should return 404 for non project members" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 for project members with guest role" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return confidential issue for project members" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for author" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", author) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for assignee" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", assignee) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for admin" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end @@ -329,7 +471,7 @@ describe API::API, api: true do it "should create a new project issue" do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, label2' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil expect(json_response['labels']).to eq(['label', 'label2']) @@ -337,21 +479,21 @@ describe API::API, api: true do it "should return a 400 bad request if title not given" do post api("/projects/#{project.id}/issues", user), labels: 'label, label2' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should return 400 if title is too long' do post api("/projects/#{project.id}/issues", user), title: 'g' * 256 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' ]) @@ -363,7 +505,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, label2', created_at: creation_time - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) end end @@ -387,7 +529,7 @@ describe API::API, api: true do it "should not create a new project issue" do expect { post api("/projects/#{project.id}/issues", user), params }.not_to change(Issue, :count) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq({ "error" => "Spam detected" }) spam_logs = SpamLog.all @@ -404,7 +546,7 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end @@ -412,14 +554,14 @@ describe API::API, api: true do it "should return 404 error if issue id not found" do put api("/projects/#{project.id}/issues/44444", user), title: 'updated title' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end @@ -427,33 +569,33 @@ describe API::API, api: true do it "should return 403 for non project members" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member), title: 'updated title' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 403 for project members with guest role" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest), title: 'updated title' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should update a confidential issue for project members" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it "should update a confidential issue for author" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", author), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it "should update a confidential issue for admin" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end end @@ -466,21 +608,21 @@ describe API::API, api: true do it 'should not update labels if not present' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to eq([label.title]) end it 'should remove all labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: '' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to eq([]) end it 'should update labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'foo,bar' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'foo' expect(json_response['labels']).to include 'bar' end @@ -488,14 +630,14 @@ describe API::API, api: true do it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should allow special label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label:foo, label-bar,label_bar,label/bar' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label:foo' expect(json_response['labels']).to include 'label-bar' expect(json_response['labels']).to include 'label_bar' @@ -505,7 +647,7 @@ describe API::API, api: true do it 'should return 400 if title is too long' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'g' * 256 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' ]) @@ -516,7 +658,7 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label2', state_event: "close" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label2' expect(json_response['state']).to eq "closed" @@ -527,7 +669,7 @@ describe API::API, api: true do update_time = 2.weeks.ago put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label3', state_event: 'close', updated_at: update_time - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label3' expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time) @@ -538,12 +680,12 @@ describe API::API, api: true do describe "DELETE /projects/:id/issues/:issue_id" do it "rejects a non member from deleting an issue" do delete api("/projects/#{project.id}/issues/#{issue.id}", non_member) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end it "rejects a developer from deleting an issue" do delete api("/projects/#{project.id}/issues/#{issue.id}", author) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end context "when the user is project owner" do @@ -552,7 +694,7 @@ describe API::API, api: true do it "deletes the issue if an admin requests it" do delete api("/projects/#{project.id}/issues/#{issue.id}", owner) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq 'opened' end end @@ -566,7 +708,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: target_project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['project_id']).to eq(target_project.id) end @@ -575,7 +717,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Cannot move issue to project it originates from!') end end @@ -585,7 +727,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: target_project2.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Cannot move issue due to insufficient permissions!') end end @@ -594,7 +736,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", admin), to_project_id: target_project2.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['project_id']).to eq(target_project2.id) end @@ -603,7 +745,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/123/move", user), to_project_id: target_project.id - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -612,7 +754,7 @@ describe API::API, api: true do post api("/projects/123/issues/#{issue.id}/move", user), to_project_id: target_project.id - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -621,7 +763,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: 123 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -630,26 +772,26 @@ describe API::API, api: true do it 'subscribes to an issue' do post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do post api("/projects/#{project.id}/issues/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(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) + expect(response).to have_http_status(404) end end @@ -657,26 +799,26 @@ describe API::API, api: true do it 'unsubscribes from an issue' do delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do delete api("/projects/#{project.id}/issues/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(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) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb index d2b87f88712..1861882d59e 100644 --- a/spec/requests/api/keys_spec.rb +++ b/spec/requests/api/keys_spec.rb @@ -14,14 +14,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/keys/#{key.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing key' do get api('/keys/999999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -29,7 +29,7 @@ describe API::API, api: true do user.keys << key user.save get api("/keys/#{key.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(key.title) expect(json_response['user']['id']).to eq(user.id) expect(json_response['user']['username']).to eq(user.username) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index b2c7f8d9acb..39736779986 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do describe 'GET /projects/:id/labels' do it 'should return project labels' do get api("/projects/#{project.id}/labels", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.first['name']).to eq(label1.name) @@ -28,7 +28,7 @@ describe API::API, api: true do name: 'Foo', color: '#FFAABB', description: 'test' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('Foo') expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to eq('test') @@ -38,7 +38,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAABB' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('Foo') expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to be_nil @@ -46,19 +46,19 @@ describe API::API, api: true do it 'should return a 400 bad request if name not given' do post api("/projects/#{project.id}/labels", user), color: '#FFAABB' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return a 400 bad request if color not given' do post api("/projects/#{project.id}/labels", user), name: 'Foobar' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 for invalid color' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAA' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -66,7 +66,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -74,7 +74,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: '?', color: '#FFAABB' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq(['is invalid']) end @@ -82,7 +82,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFAABB' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Label already exists') end end @@ -90,18 +90,18 @@ describe API::API, api: true do describe 'DELETE /projects/:id/labels' do it 'should return 200 for existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label1' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 for non existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label2' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Label Not Found') end it 'should return 400 for wrong parameters' do delete api("/projects/#{project.id}/labels", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -112,7 +112,7 @@ describe API::API, api: true do new_name: 'New Label', color: '#FFFFFF', description: 'test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('New Label') expect(json_response['color']).to eq('#FFFFFF') expect(json_response['description']).to eq('test') @@ -122,7 +122,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', new_name: 'New Label' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('New Label') expect(json_response['color']).to eq(label1.color) end @@ -131,7 +131,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFFFFF' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(label1.name) expect(json_response['color']).to eq('#FFFFFF') end @@ -140,7 +140,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', description: 'test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(label1.name) expect(json_response['description']).to eq('test') end @@ -149,18 +149,18 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label2', new_name: 'label3' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return 400 if no label name given' do put api("/projects/#{project.id}/labels", user), new_name: 'label2' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "name" not given') end it 'should return 400 if no new parameters given' do put api("/projects/#{project.id}/labels", user), name: 'label1' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Required parameters '\ '"new_name" or "color" missing') end @@ -170,7 +170,7 @@ describe API::API, api: true do name: 'label1', new_name: '?', color: '#FFFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq(['is invalid']) end @@ -178,7 +178,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -186,7 +186,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end end @@ -196,7 +196,7 @@ describe API::API, api: true 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(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_truthy end @@ -206,7 +206,7 @@ describe API::API, api: true 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(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_truthy end @@ -218,7 +218,7 @@ describe API::API, api: true do it "should return 304" do post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end @@ -226,7 +226,7 @@ describe API::API, api: true do it "should a return 404 error" do post api("/projects/#{project.id}/labels/1234/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -238,7 +238,7 @@ describe API::API, api: true 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(response).to have_http_status(200) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -248,7 +248,7 @@ describe API::API, api: true 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(response).to have_http_status(200) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -260,7 +260,7 @@ describe API::API, api: true do it "should return 304" do delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end @@ -268,7 +268,7 @@ describe API::API, api: true do it "should a return 404 error" do delete api("/projects/#{project.id}/labels/1234/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/license_templates_spec.rb index 3726b2f5688..9a1894d63a2 100644 --- a/spec/requests/api/licenses_spec.rb +++ b/spec/requests/api/license_templates_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Licenses, api: true do +describe API::API, api: true do include ApiHelpers describe 'Entity' do @@ -23,7 +23,7 @@ describe API::Licenses, api: true do it 'returns a list of available license templates' do get api('/licenses') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(15) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') @@ -34,7 +34,7 @@ describe API::Licenses, api: true do it 'returns a list of available popular license templates' do get api('/licenses?popular=1') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(3) expect(json_response.map { |l| l['key'] }).to include('apache-2.0') @@ -116,7 +116,7 @@ describe API::Licenses, api: true do let(:license_type) { 'muth-over9000' } it 'returns a 404' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 5896b93603f..61e897edf87 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -22,14 +22,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/projects/#{project.id}/merge_requests") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated" do it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.last['title']).to eq(merge_request.title) @@ -37,7 +37,7 @@ describe API::API, api: true do it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.last['title']).to eq(merge_request.title) @@ -45,7 +45,7 @@ describe API::API, api: true do it "should return an array of open merge_requests" do get api("/projects/#{project.id}/merge_requests?state=opened", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.last['title']).to eq(merge_request.title) @@ -53,7 +53,7 @@ describe API::API, api: true do it "should return an array of closed merge_requests" do get api("/projects/#{project.id}/merge_requests?state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(merge_request_closed.title) @@ -61,7 +61,7 @@ describe API::API, api: true do it "should return an array of merged merge_requests" do get api("/projects/#{project.id}/merge_requests?state=merged", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(merge_request_merged.title) @@ -75,7 +75,7 @@ describe API::API, api: true do it "should return an array of merge_requests in ascending order" do get api("/projects/#{project.id}/merge_requests?sort=asc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -84,7 +84,7 @@ describe API::API, api: true do it "should return an array of merge_requests in descending order" do get api("/projects/#{project.id}/merge_requests?sort=desc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -93,7 +93,7 @@ describe API::API, api: true do it "should return an array of merge_requests ordered by updated_at" do get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['updated_at'] } @@ -102,7 +102,7 @@ describe API::API, api: true do it "should return an array of merge_requests ordered by created_at" do get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -116,7 +116,7 @@ describe API::API, api: true do it 'exposes known attributes' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(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) @@ -142,7 +142,7 @@ describe API::API, api: true do it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(merge_request.title) expect(json_response['iid']).to eq(merge_request.iid) expect(json_response['work_in_progress']).to eq(false) @@ -159,7 +159,7 @@ describe API::API, api: true do it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_requests/999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'Work in Progress' do @@ -167,7 +167,7 @@ describe API::API, api: true do it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['work_in_progress']).to eq(true) end end @@ -186,7 +186,7 @@ describe API::API, api: true do it 'returns a 404 when merge_request_id not found' do get api("/projects/#{project.id}/merge_requests/999/commits", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -199,7 +199,7 @@ describe API::API, api: true do it 'returns a 404 when merge_request_id not found' do get api("/projects/#{project.id}/merge_requests/999/changes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -213,7 +213,7 @@ describe API::API, api: true do author: user, labels: 'label, label2', milestone_id: milestone.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['labels']).to eq(['label', 'label2']) expect(json_response['milestone']['id']).to eq(milestone.id) @@ -222,25 +222,25 @@ describe API::API, api: true do it "should return 422 when source_branch equals target_branch" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "master", target_branch: "master", author: user - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end it "should return 400 when source_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", target_branch: "master", author: user - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "markdown", author: user - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), target_branch: 'master', source_branch: 'markdown' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 on invalid label names' do @@ -250,7 +250,7 @@ describe API::API, api: true do target_branch: 'master', author: user, labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq( ['is invalid'] ) @@ -274,7 +274,7 @@ describe API::API, api: true do target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end end end @@ -292,7 +292,7 @@ describe API::API, api: true do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['description']).to eq('Test description for Test merge_request') end @@ -303,26 +303,26 @@ describe API::API, api: true do expect(fork_project.forked_from_project).to eq(project) post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') end it "should return 400 when source_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when title is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end context 'when target_branch is specified' do @@ -333,7 +333,7 @@ describe API::API, api: true do source_branch: 'markdown', author: user, target_project_id: fork_project.id - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end it 'should return 422 if targeting a different fork' do @@ -343,14 +343,14 @@ describe API::API, api: true do source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end it "should return 201 when target_branch is specified and for the same project" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end end @@ -365,7 +365,7 @@ describe API::API, api: true do it "denies the deletion of the merge request" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end end @@ -373,7 +373,7 @@ describe API::API, api: true do it "destroys the merge request owners can destroy" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -381,7 +381,7 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do it "should return merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq('closed') end end @@ -392,7 +392,7 @@ describe API::API, api: true do it "should return merge_request in case of success" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 406 if branch can't be merged" do @@ -401,21 +401,21 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(406) + expect(response).to have_http_status(406) expect(json_response['message']).to eq('Branch cannot be merged') end it "should return 405 if merge_request is not open" do merge_request.close put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end it "should return 405 if merge_request is a work in progress" do merge_request.update_attribute(:title, "WIP: #{merge_request.title}") put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -424,7 +424,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -432,21 +432,21 @@ describe API::API, api: true do user2 = create(:user) project.team << [user2, :reporter] put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['message']).to eq('401 Unauthorized') end it "returns 409 if the SHA parameter doesn't match" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha.succ - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to start_with('SHA does not match HEAD of source branch') end it "succeeds if the SHA parameter matches" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "enables merge when build succeeds if the ci is active" do @@ -455,7 +455,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('Test') expect(json_response['merge_when_build_succeeds']).to eq(true) end @@ -464,31 +464,31 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_requests/:merge_request_id" do it "updates title and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('New title') end it "updates description and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq('New description') end it "updates milestone_id and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), milestone_id: milestone.id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['milestone']['id']).to eq(milestone.id) end it "should return 400 when source_branch is specified" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), source_branch: "master", target_branch: "master" - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return merge_request with renamed target_branch" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['target_branch']).to eq('wiki') end @@ -497,7 +497,7 @@ describe API::API, api: true do user), title: 'new issue', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end end @@ -507,7 +507,7 @@ describe API::API, api: true do original_count = merge_request.notes.size post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment" - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['author']['name']).to eq(user.name) expect(json_response['author']['username']).to eq(user.username) @@ -516,20 +516,20 @@ describe API::API, api: true do it "should return 400 if note is missing" do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 404 if note is attached to non existent merge request" do post api("/projects/#{project.id}/merge_requests/404/comments", user), note: 'My comment' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe "GET :id/merge_requests/:merge_request_id/comments" do it "should return merge_request comments ordered by created_at" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['note']).to eq("a comment on a MR") @@ -539,7 +539,7 @@ describe API::API, api: true do it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_requests/999/comments", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -551,7 +551,7 @@ describe API::API, api: true do end get api("/projects/#{project.id}/merge_requests/#{mr.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(issue.id) @@ -559,7 +559,7 @@ describe API::API, api: true do it 'returns an empty array when there are no issues to be closed' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -572,7 +572,7 @@ describe API::API, api: true do get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(issue.title) @@ -584,20 +584,20 @@ describe API::API, api: true do it 'subscribes to a merge request' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -605,20 +605,20 @@ describe API::API, api: true do it 'unsubscribes from a merge request' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 0154d1c62cc..0f4e38b2475 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -12,20 +12,20 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones' do it 'should return project milestones' do get api("/projects/#{project.id}/milestones", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it 'returns an array of active milestones' do get api("/projects/#{project.id}/milestones?state=active", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(milestone.id) @@ -34,7 +34,7 @@ describe API::API, api: true do it 'returns an array of closed milestones' do get api("/projects/#{project.id}/milestones?state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_milestone.id) @@ -44,7 +44,7 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones/:milestone_id' do it 'should return a project milestone by id' do get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(milestone.title) expect(json_response['iid']).to eq(milestone.iid) end @@ -60,19 +60,19 @@ describe API::API, api: true do it 'should return 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it 'should return a 404 error if milestone id not found' do get api("/projects/#{project.id}/milestones/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'POST /projects/:id/milestones' do it 'should create a new project milestone' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('new milestone') expect(json_response['description']).to be_nil end @@ -80,14 +80,14 @@ describe API::API, api: true do it 'should create a new project milestone with description and due date' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone', description: 'release', due_date: '2013-03-02' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['description']).to eq('release') expect(json_response['due_date']).to eq('2013-03-02') end it 'should return a 400 error if title is missing' do post api("/projects/#{project.id}/milestones", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -95,14 +95,14 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it 'should return a 404 error if milestone id not found' do put api("/projects/#{project.id}/milestones/1234", user), title: 'updated title' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -110,7 +110,7 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), state_event: 'close' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq('closed') end @@ -131,14 +131,14 @@ describe API::API, api: true do end it 'should return project issues for a particular milestone' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['milestone']['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end describe 'confidential issues' do @@ -155,7 +155,7 @@ describe API::API, api: true do it 'returns confidential issues to team members' do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) @@ -167,7 +167,7 @@ describe API::API, api: true do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.map { |issue| issue['id'] }).to include(issue.id) @@ -176,7 +176,7 @@ describe API::API, api: true do it 'does not return confidential issues to regular users' do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.map { |issue| issue['id'] }).to include(issue.id) diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index 21787fdd895..237b4b17eb5 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -11,14 +11,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/namespaces") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated as admin" do it "admin: should return an array of all namespaces" do get api("/namespaces", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(Namespace.count) @@ -26,7 +26,7 @@ describe API::API, api: true do it "admin: should return an array of matched namespaces" do get api("/namespaces?search=#{group1.name}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -36,7 +36,7 @@ describe API::API, api: true do context "when authenticated as a regular user" do it "user: should return an array of namespaces" do get api("/namespaces", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -44,7 +44,7 @@ describe API::API, api: true do it "admin: should return an array of matched namespaces" do get api("/namespaces?search=#{user.username}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index beb29a68692..bacd01f8e74 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -40,7 +40,7 @@ describe API::API, api: true do it "should return an array of issue notes" do get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(issue_note.note) end @@ -48,14 +48,14 @@ describe API::API, api: true do it "should return a 404 error when issue id not found" do get api("/projects/#{project.id}/issues/12345/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "and current user cannot view the notes" do it "should return an empty array" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response).to be_empty end @@ -66,7 +66,7 @@ describe API::API, api: true do it "returns 404" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -74,7 +74,7 @@ describe API::API, api: true do it "should return an empty array" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(cross_reference_note.note) end @@ -86,7 +86,7 @@ describe API::API, api: true do it "should return an array of snippet notes" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(snippet_note.note) end @@ -94,13 +94,13 @@ describe API::API, api: true do it "should return a 404 error when snippet id not found" do get api("/projects/#{project.id}/snippets/42/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when not authorized" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -108,7 +108,7 @@ describe API::API, api: true do it "should return an array of merge_requests notes" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(merge_request_note.note) end @@ -116,13 +116,13 @@ describe API::API, api: true do it "should return a 404 error if merge request id not found" do get api("/projects/#{project.id}/merge_requests/4444/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when not authorized" do get api("/projects/#{project.id}/merge_requests/4444/notes", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -132,21 +132,21 @@ describe API::API, api: true do it "should return an issue note by id" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(issue_note.note) end it "should return a 404 error if issue note not found" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "and current user cannot view the note" do it "should return a 404 error" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "when issue is confidential" do @@ -155,7 +155,7 @@ describe API::API, api: true do it "returns 404" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -164,7 +164,7 @@ describe API::API, api: true do it "should return an issue note by id" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(cross_reference_note.note) end end @@ -175,14 +175,14 @@ describe API::API, api: true do it "should return a snippet note by id" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(snippet_note.note) end it "should return a 404 error if snippet note not found" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -192,7 +192,7 @@ describe API::API, api: true do it "should create a new issue note" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) end @@ -200,13 +200,13 @@ describe API::API, api: true do it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end context 'when an admin or owner makes the request' do @@ -215,7 +215,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!', created_at: creation_time - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) @@ -228,7 +228,7 @@ describe API::API, api: true do it "should create a new snippet note" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) end @@ -236,13 +236,13 @@ describe API::API, api: true do it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -282,7 +282,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -290,14 +290,14 @@ describe API::API, api: true do put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), body: 'Hello!' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return a 400 bad request error if body not given' do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -306,7 +306,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -314,7 +314,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/12345", user), body: "Hello!" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -323,7 +323,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/#{merge_request_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -331,7 +331,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/12345", user), body: "Hello!" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -342,17 +342,17 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -361,18 +361,18 @@ describe API::API, api: true do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -381,18 +381,18 @@ describe API::API, api: true do delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index ffb93bbb120..fd1fffa6223 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -22,7 +22,7 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return project hooks" do get api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(1) @@ -40,7 +40,7 @@ describe API::API, 'ProjectHooks', api: true do context "unauthorized user" do it "should not access project hooks" do get api("/projects/#{project.id}/hooks", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -49,7 +49,7 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return a project hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['url']).to eq(hook.url) expect(json_response['issues_events']).to eq(hook.issues_events) expect(json_response['push_events']).to eq(hook.push_events) @@ -61,20 +61,20 @@ describe API::API, 'ProjectHooks', api: true do it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "unauthorized user" do it "should not access an existing hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -83,7 +83,7 @@ describe API::API, 'ProjectHooks', api: true do expect do post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true end.to change {project.hooks.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['url']).to eq('http://example.com') expect(json_response['issues_events']).to eq(true) expect(json_response['push_events']).to eq(true) @@ -96,12 +96,12 @@ describe API::API, 'ProjectHooks', api: true do it "should return a 400 error if url not given" do post api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error if url not valid" do post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -109,7 +109,7 @@ describe API::API, 'ProjectHooks', api: true do it "should update an existing project hook" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'http://example.org', push_events: false - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['url']).to eq('http://example.org') expect(json_response['issues_events']).to eq(hook.issues_events) expect(json_response['push_events']).to eq(false) @@ -121,17 +121,17 @@ describe API::API, 'ProjectHooks', api: true do it "should return 404 error if hook id not found" do put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 400 error if url is not given" do put api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error if url is not valid" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -140,22 +140,22 @@ describe API::API, 'ProjectHooks', api: true do expect do delete api("/projects/#{project.id}/hooks/#{hook.id}", user) end.to change {project.hooks.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success when deleting hook" do delete api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return a 404 error when deleting non existent hook" do delete api("/projects/#{project.id}/hooks/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 405 error if hook id not given" do delete api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end it "shold return a 404 if a user attempts to delete project hooks he/she does not own" do @@ -164,7 +164,7 @@ describe API::API, 'ProjectHooks', api: true do other_project.team << [test_user, :master] delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(WebHook.exists?(hook.id)).to be_truthy end end diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index 44b532b10e1..9a7c1da4401 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do it "should return project team members" do get api("/projects/#{project.id}/members", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(2) expect(json_response.map { |u| u['username'] }).to include user.username @@ -23,7 +23,7 @@ describe API::API, api: true do it "finds team members with query string" do get api("/projects/#{project.id}/members", user), query: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(1) expect(json_response.first['username']).to eq(user.username) @@ -31,7 +31,7 @@ describe API::API, api: true do it "should return a 404 error if id not found" do get api("/projects/9999/members", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -40,14 +40,14 @@ describe API::API, api: true do it "should return project team member" do get api("/projects/#{project.id}/members/#{user.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user id not found" do get api("/projects/#{project.id}/members/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -57,7 +57,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER end.to change { ProjectMember.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['username']).to eq(user2.username) expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end @@ -70,24 +70,24 @@ describe API::API, api: true do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER end.not_to change { ProjectMember.count } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['username']).to eq(user2.username) expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end it "should return a 400 error when user id is not given" do post api("/projects/#{project.id}/members", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 error when access level is not given" do post api("/projects/#{project.id}/members", user), user_id: user2.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -96,24 +96,24 @@ describe API::API, api: true do it "should update project team member" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user3.username) expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user_id is not found" do put api("/projects/#{project.id}/members/1234", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 400 error when access level is not given" do put api("/projects/#{project.id}/members/#{user3.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -134,20 +134,20 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/members/#{user3.id}", user) end.not_to change { ProjectMember.count } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 200 if team member already removed" do delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 200 OK when the user was not member" do expect do delete api("/projects/#{project.id}/members/1000000", user) end.to change { ProjectMember.count }.by(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(1000000) expect(json_response['message']).to eq('Access revoked') end @@ -158,7 +158,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/members/#{user3.id}", user3) end.to change { ProjectMember.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(project_member2.id) end end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 9706d060cfa..4ebde201941 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -27,7 +27,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/snippets/", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.size).to eq(3) expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id) end @@ -38,7 +38,7 @@ describe API::API, api: true do create(:project_snippet, :private, project: project) get api("/projects/#{project.id}/snippets/", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.size).to eq(0) end end @@ -56,7 +56,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/snippets/", admin), params - expect(response.status).to eq(201) + expect(response).to have_http_status(201) snippet = ProjectSnippet.find(json_response['id']) expect(snippet.content).to eq(params[:code]) expect(snippet.title).to eq(params[:title]) @@ -73,7 +73,7 @@ describe API::API, api: true do put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content - expect(response.status).to eq(200) + expect(response).to have_http_status(200) snippet.reload expect(snippet.content).to eq(new_content) end @@ -86,7 +86,7 @@ describe API::API, api: true do delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -97,7 +97,7 @@ describe API::API, api: true do get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'text/plain' expect(response.body).to eq(snippet.content) end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 01eb4b44b83..5c909d8b3b3 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -45,14 +45,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return an array of projects' do get api('/projects', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(project.name) expect(json_response.first['owner']['username']).to eq(user.username) @@ -84,7 +84,7 @@ describe API::API, api: true do context 'and using search' do it 'should return searched project' do get api('/projects', user), { search: project.name } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) end @@ -93,21 +93,21 @@ describe API::API, api: true do context 'and using the visibility filter' do it 'should filter based on private visibility param' do get api('/projects', user), { visibility: 'private' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count) end it 'should filter based on internal visibility param' do get api('/projects', user), { visibility: 'internal' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count) end it 'should filter based on public visibility param' do get api('/projects', user), { visibility: 'public' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count) end @@ -121,7 +121,7 @@ describe API::API, api: true do it 'should return the correct order when sorted by id' do get api('/projects', user), { order_by: 'id', sort: 'desc' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project3.id) end @@ -135,21 +135,21 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects/all') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated as regular user' do it 'should return authentication error' do get api('/projects/all', user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context 'when authenticated as admin' do it 'should return an array of all projects' do get api('/projects/all', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response).to satisfy do |response| @@ -173,7 +173,7 @@ describe API::API, api: true do it 'should return the starred projects viewable by the user' do get api('/projects/starred', user3) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id) end @@ -185,25 +185,25 @@ describe API::API, api: true do allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0) expect { post api('/projects', user2), name: 'foo' }. to change {Project.count}.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should create new project without path and return 201' do expect { post api('/projects', user), name: 'foo' }. to change { Project.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should create last project before reaching project limit' do allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1) post api('/projects', user2), name: 'foo' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should not create new project without name and return 400' do expect { post api('/projects', user) }.not_to change { Project.count } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should assign attributes to project" do @@ -217,7 +217,7 @@ describe API::API, api: true do post api('/projects', user), project - project.each_pair do |k,v| + project.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end end @@ -273,7 +273,7 @@ describe API::API, api: true do it 'should not allow a non-admin to use a restricted visibility level' do post api('/projects', user), @project - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['visibility_level'].first).to( match('restricted by your GitLab administrator') ) @@ -295,14 +295,14 @@ describe API::API, api: true do it 'should create new project without path and return 201' do expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should respond with 400 on failure and not project' do expect { post api("/projects/user/#{user.id}", admin) }. not_to change { Project.count } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['name']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', @@ -325,7 +325,7 @@ describe API::API, api: true do post api("/projects/user/#{user.id}", admin), project - project.each_pair do |k,v| + project.each_pair do |k, v| next if k == :path expect(json_response[k.to_s]).to eq(v) end @@ -380,7 +380,7 @@ describe API::API, api: true do it "uploads the file and returns its info" do post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") - expect(response.status).to be(201) + expect(response).to have_http_status(201) expect(json_response['alt']).to eq("dk") expect(json_response['url']).to start_with("/uploads/") expect(json_response['url']).to end_with("/dk.png") @@ -394,27 +394,27 @@ describe API::API, api: true do it 'should return a project by id' do get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) expect(json_response['owner']['username']).to eq(user.username) end it 'should return a project by path name' do get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end it 'should return a 404 error if not found' do get api('/projects/42', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}", other_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should handle users with dots' do @@ -422,7 +422,7 @@ describe API::API, api: true do project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace) get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end @@ -433,7 +433,7 @@ describe API::API, api: true do it 'contains permission information' do get api("/projects", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.first['permissions']['project_access']['access_level']). to eq(Gitlab::Access::MASTER) expect(json_response.first['permissions']['group_access']).to be_nil @@ -445,7 +445,7 @@ describe API::API, api: true do project.team << [user, :master] get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['permissions']['project_access']['access_level']). to eq(Gitlab::Access::MASTER) expect(json_response['permissions']['group_access']).to be_nil @@ -460,7 +460,7 @@ describe API::API, api: true do it 'should set the owner and return 200' do get api("/projects/#{project2.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['permissions']['project_access']).to be_nil expect(json_response['permissions']['group_access']['access_level']). to eq(Gitlab::Access::OWNER) @@ -479,7 +479,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/events", user) end - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } context 'joined event' do let(:json_event) { json_response[1] } @@ -500,14 +500,14 @@ describe API::API, api: true do it 'should return a 404 error if not found' do get api('/projects/42/events', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}/events", other_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -516,7 +516,7 @@ describe API::API, api: true do it 'should return an array of project snippets' do get api("/projects/#{project.id}/snippets", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(snippet.title) end @@ -525,13 +525,13 @@ describe API::API, api: true do describe 'GET /projects/:id/snippets/:snippet_id' do it 'should return a project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(snippet.title) end it 'should return a 404 error if snippet id not found' do get api("/projects/#{project.id}/snippets/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -540,7 +540,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/snippets", user), title: 'api test', file_name: 'sample.rb', code: 'test', visibility_level: '0' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('api test') end @@ -554,7 +554,7 @@ describe API::API, api: true do it 'should update an existing project snippet' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), code: 'updated code' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('example') expect(snippet.reload.content).to eq('updated code') end @@ -562,7 +562,7 @@ describe API::API, api: true do it 'should update an existing project snippet with new title' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), title: 'other api test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('other api test') end end @@ -574,24 +574,24 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) end.to change { Snippet.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 when deleting unknown snippet id' do delete api("/projects/#{project.id}/snippets/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'GET /projects/:id/snippets/:snippet_id/raw' do it 'should get a raw project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return a 404 error if raw project snippet not found' do get api("/projects/#{project.id}/snippets/5555/raw", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -604,7 +604,7 @@ describe API::API, api: true do it 'should return array of ssh keys' do get api("/projects/#{project.id}/keys", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(deploy_key.title) end @@ -613,20 +613,20 @@ describe API::API, api: true do describe 'GET /projects/:id/keys/:key_id' do it 'should return a single key' do get api("/projects/#{project.id}/keys/#{deploy_key.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(deploy_key.title) end it 'should return 404 Not Found with invalid ID' do get api("/projects/#{project.id}/keys/404", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'POST /projects/:id/keys' do it 'should not create an invalid ssh key' do post api("/projects/#{project.id}/keys", user), { title: 'invalid key' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['key']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', @@ -636,7 +636,7 @@ describe API::API, api: true do it 'should not create a key without title' do post api("/projects/#{project.id}/keys", user), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)' @@ -662,7 +662,7 @@ describe API::API, api: true do it 'should return 404 Not Found with invalid ID' do delete api("/projects/#{project.id}/keys/404", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -676,13 +676,13 @@ describe API::API, api: true do it "shouldn't available for non admin users" do post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should allow project to be forked from an existing project' do expect(project_fork_target.forked?).not_to be_truthy post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) expect(project_fork_target.forked_project_link).not_to be_nil @@ -691,7 +691,7 @@ describe API::API, api: true do it 'should fail if forked_from project which does not exist' do post api("/projects/#{project_fork_target.id}/fork/9999", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should fail with 409 if already forked' do @@ -699,7 +699,7 @@ describe API::API, api: true do project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) expect(project_fork_target.forked?).to be_truthy @@ -710,7 +710,7 @@ describe API::API, api: true do it "shouldn't be visible to users outside group" do delete api("/projects/#{project_fork_target.id}/fork", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'when users belong to project group' do @@ -723,7 +723,7 @@ describe API::API, api: true do it 'should be forbidden to non-owner users' do delete api("/projects/#{project_fork_target.id}/fork", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should make forked project unforked' do @@ -732,7 +732,7 @@ describe API::API, api: true do expect(project_fork_target.forked_from_project).not_to be_nil expect(project_fork_target.forked?).to be_truthy delete api("/projects/#{project_fork_target.id}/fork", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_fork_target.reload expect(project_fork_target.forked_from_project).to be_nil expect(project_fork_target.forked?).not_to be_truthy @@ -741,7 +741,7 @@ describe API::API, api: true do it 'should be idempotent if not forked' do expect(project_fork_target.forked_from_project).to be_nil delete api("/projects/#{project_fork_target.id}/fork", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(project_fork_target.reload.forked_from_project).to be_nil end end @@ -799,14 +799,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/projects/search/#{query}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return an array of projects' do - get api("/projects/search/#{query}",user) - expect(response.status).to eq(200) + get api("/projects/search/#{query}", user) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(6) json_response.each {|project| expect(project['name']).to match(/.*query.*/)} @@ -816,7 +816,7 @@ describe API::API, api: true do context 'when authenticated as a different user' do it 'should return matching public projects' do get api("/projects/search/#{query}", user2) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)} @@ -838,7 +838,7 @@ describe API::API, api: true do it 'should return authentication error' do project_param = { name: 'bar' } put api("/projects/#{project.id}"), project_param - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -846,7 +846,7 @@ describe API::API, api: true do it 'should update name' do project_param = { name: 'bar' } put api("/projects/#{project.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -855,7 +855,7 @@ describe API::API, api: true do it 'should update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -866,7 +866,7 @@ describe API::API, api: true do project_param = { public: false } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -876,14 +876,14 @@ describe API::API, api: true do it 'should not update name to existing name' do project_param = { name: project3.name } put api("/projects/#{project.id}", user), project_param - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['name']).to eq(['has already been taken']) end it 'should update path & name to existing path & name in different namespace' do project_param = { path: project4.path, name: project4.name } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -894,7 +894,7 @@ describe API::API, api: true do it 'should update path' do project_param = { path: 'bar' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -908,7 +908,7 @@ describe API::API, api: true do description: 'new description' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -917,20 +917,20 @@ describe API::API, api: true do it 'should not update path to existing path' do project_param = { path: project.path } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should not update name' do project_param = { name: 'bar' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -943,7 +943,7 @@ describe API::API, api: true do merge_requests_enabled: true, description: 'new description' } put api("/projects/#{project.id}", user3), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -953,7 +953,7 @@ describe API::API, api: true do it 'archives the project' do post api("/projects/#{project.id}/archive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_truthy end end @@ -966,7 +966,7 @@ describe API::API, api: true do it 'remains archived' do post api("/projects/#{project.id}/archive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_truthy end end @@ -979,7 +979,7 @@ describe API::API, api: true do it 'rejects the action' do post api("/projects/#{project.id}/archive", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -989,7 +989,7 @@ describe API::API, api: true do it 'remains unarchived' do post api("/projects/#{project.id}/unarchive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_falsey end end @@ -1002,7 +1002,7 @@ describe API::API, api: true do it 'unarchives the project' do post api("/projects/#{project.id}/unarchive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_falsey end end @@ -1015,7 +1015,7 @@ describe API::API, api: true do it 'rejects the action' do post api("/projects/#{project.id}/unarchive", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -1025,7 +1025,7 @@ describe API::API, api: true do it 'stars the project' do expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['star_count']).to eq(1) end end @@ -1039,7 +1039,7 @@ describe API::API, api: true do it 'does not modify the star count' do expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end end @@ -1054,7 +1054,7 @@ describe API::API, api: true do it 'unstars the project' do expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['star_count']).to eq(0) end end @@ -1063,7 +1063,7 @@ describe API::API, api: true do it 'does not modify the star count' do expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end end @@ -1072,36 +1072,36 @@ describe API::API, api: true do context 'when authenticated as user' do it 'should remove project' do delete api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not remove a project if not an owner' do user3 = create(:user) project.team << [user3, :developer] delete api("/projects/#{project.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not remove a non existing project' do delete api('/projects/1328', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not remove a project not attached to user' do delete api("/projects/#{project.id}", user2) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'when authenticated as admin' do it 'should remove any existing project' do delete api("/projects/#{project.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not remove a non existing project' do delete api('/projects/1328', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 7cf4a01d76b..5890e9c9d3d 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -18,7 +18,7 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/tree", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq('encoding') @@ -28,7 +28,7 @@ describe API::API, api: true do it 'should return a 404 for unknown ref' do get api("/projects/#{project.id}/repository/tree?ref_name=foo", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response).to be_an Object json_response['message'] == '404 Tree Not Found' @@ -38,7 +38,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/tree") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -46,41 +46,41 @@ describe API::API, api: true do describe "GET /projects/:id/repository/blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 404 for invalid branch_name" do get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 for invalid file" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 400 error if filepath is missing" do get api("/projects/#{project.id}/repository/blobs/master", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end describe "GET /projects/:id/repository/commits/:sha/blob" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "GET /projects/:id/repository/raw_blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return a 404 for unknown blob' do get api("/projects/#{project.id}/repository/raw_blobs/123456", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response).to be_an Object json_response['message'] == '404 Blob Not Found' @@ -91,7 +91,7 @@ describe API::API, api: true do it "should get the archive" do get api("/projects/#{project.id}/repository/archive", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) @@ -100,7 +100,7 @@ describe API::API, api: true do it "should get the archive.zip" do get api("/projects/#{project.id}/repository/archive.zip", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) @@ -109,7 +109,7 @@ describe API::API, api: true do it "should get the archive.tar.bz2" do get api("/projects/#{project.id}/repository/archive.tar.bz2", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) @@ -117,28 +117,28 @@ describe API::API, api: true do it "should return 404 for invalid sha" do get api("/projects/#{project.id}/repository/archive/?sha=xxx", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'GET /projects/:id/repository/compare' do it "should compare branches" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare tags" do get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare commits" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty expect(json_response['diffs']).to be_empty expect(json_response['compare_same_ref']).to be_falsey @@ -146,14 +146,14 @@ describe API::API, api: true do it "should compare commits in reverse order" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare same refs" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty expect(json_response['diffs']).to be_empty expect(json_response['compare_same_ref']).to be_truthy @@ -163,7 +163,7 @@ describe API::API, api: true do describe 'GET /projects/:id/repository/contributors' do it 'should return valid data' do get api("/projects/#{project.id}/repository/contributors", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array contributor = json_response.first expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com') diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index b4c826522a5..00a3c917b6a 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -39,7 +39,7 @@ describe API::Runners, api: true do get api('/runners', user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end @@ -48,14 +48,14 @@ describe API::Runners, api: true do get api('/runners?scope=active', user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end it 'should avoid filtering if scope is invalid' do get api('/runners?scope=unknown', user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -63,7 +63,7 @@ describe API::Runners, api: true do it 'should not return runners' do get api('/runners') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -75,7 +75,7 @@ describe API::Runners, api: true do get api('/runners/all', admin) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_truthy end @@ -85,7 +85,7 @@ describe API::Runners, api: true do it 'should not return runners list' do get api('/runners/all', user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -93,14 +93,14 @@ describe API::Runners, api: true do get api('/runners/all?scope=specific', admin) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end it 'should avoid filtering if scope is invalid' do get api('/runners?scope=unknown', admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -108,7 +108,7 @@ describe API::Runners, api: true do it 'should not return runners' do get api('/runners') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -119,7 +119,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{shared_runner.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(shared_runner.description) end end @@ -128,7 +128,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{specific_runner.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(specific_runner.description) end end @@ -136,7 +136,7 @@ describe API::Runners, api: true do it 'should return 404 if runner does not exists' do get api('/runners/9999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -145,7 +145,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{specific_runner.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(specific_runner.description) end end @@ -154,7 +154,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(shared_runner.description) end end @@ -164,7 +164,7 @@ describe API::Runners, api: true do it "should not return runner's details" do get api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -172,7 +172,7 @@ describe API::Runners, api: true do it "should not return runner's details" do get api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -191,7 +191,7 @@ describe API::Runners, api: true do locked: 'true') shared_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(shared_runner.description).to eq("#{description}_updated") expect(shared_runner.active).to eq(!active) expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql') @@ -206,7 +206,7 @@ describe API::Runners, api: true do update_runner(specific_runner.id, admin, description: 'test') specific_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(specific_runner.description).to eq('test') expect(specific_runner.description).not_to eq(description) end @@ -215,7 +215,7 @@ describe API::Runners, api: true do it 'should return 404 if runner does not exists' do update_runner(9999, admin, description: 'test') - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end def update_runner(id, user, args) @@ -228,7 +228,7 @@ describe API::Runners, api: true do it 'should not update runner' do put api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -236,7 +236,7 @@ describe API::Runners, api: true do it 'should not update runner without access to it' do put api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should update runner with access to it' do @@ -244,7 +244,7 @@ describe API::Runners, api: true do put api("/runners/#{specific_runner.id}", admin), description: 'test' specific_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(specific_runner.description).to eq('test') expect(specific_runner.description).not_to eq(description) end @@ -255,7 +255,7 @@ describe API::Runners, api: true do it 'should not delete runner' do put api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -267,7 +267,7 @@ describe API::Runners, api: true do expect do delete api("/runners/#{shared_runner.id}", admin) end.to change{ Ci::Runner.shared.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -276,21 +276,21 @@ describe API::Runners, api: true do expect do delete api("/runners/#{unused_specific_runner.id}", admin) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should delete used runner' do expect do delete api("/runners/#{specific_runner.id}", admin) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end it 'should return 404 if runner does not exists' do delete api('/runners/9999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -298,26 +298,26 @@ describe API::Runners, api: true do context 'when runner is shared' do it 'should not delete runner' do delete api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context 'when runner is not shared' do it 'should not delete runner without access to it' do delete api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not delete runner with more than one associated project' do delete api("/runners/#{two_projects_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should delete runner for one owned project' do expect do delete api("/runners/#{specific_runner.id}", user) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -326,7 +326,7 @@ describe API::Runners, api: true do it 'should not delete runner' do delete api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -337,7 +337,7 @@ describe API::Runners, api: true do get api("/projects/#{project.id}/runners", user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_truthy end @@ -347,7 +347,7 @@ describe API::Runners, api: true do it "should not return project's runners" do get api("/projects/#{project.id}/runners", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -355,7 +355,7 @@ describe API::Runners, api: true do it "should not return project's runners" do get api("/projects/#{project.id}/runners") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -372,14 +372,14 @@ describe API::Runners, api: true do expect do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id end.to change{ project.runners.count }.by(+1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should avoid changes when enabling already enabled runner' do expect do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end it 'should not enable locked runner' do @@ -389,13 +389,13 @@ describe API::Runners, api: true do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not enable shared runner' do post api("/projects/#{project.id}/runners", user), runner_id: shared_runner.id - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end context 'user is admin' do @@ -403,7 +403,7 @@ describe API::Runners, api: true do expect do post api("/projects/#{project.id}/runners", admin), runner_id: unused_specific_runner.id end.to change{ project.runners.count }.by(+1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end @@ -411,14 +411,14 @@ describe API::Runners, api: true do it 'should not enable runner without access to' do post api("/projects/#{project.id}/runners", user), runner_id: unused_specific_runner.id - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should raise an error when no runner_id param is provided' do post api("/projects/#{project.id}/runners", admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -426,7 +426,7 @@ describe API::Runners, api: true do it 'should not enable runner' do post api("/projects/#{project.id}/runners", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -434,7 +434,7 @@ describe API::Runners, api: true do it 'should not enable runner' do post api("/projects/#{project.id}/runners") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -446,7 +446,7 @@ describe API::Runners, api: true do expect do delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user) end.to change{ project.runners.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -455,14 +455,14 @@ describe API::Runners, api: true do expect do delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user) end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should return 404 is runner is not found' do delete api("/projects/#{project.id}/runners/9999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -470,7 +470,7 @@ describe API::Runners, api: true do it "should not disable project's runner" do delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -478,7 +478,7 @@ describe API::Runners, api: true do it "should not disable project's runner" do delete api("/projects/#{project.id}/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index fed9ae1949b..bf7eaaaaaed 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -14,7 +14,7 @@ describe API::API, api: true do it "should update #{service} settings" do put api("/projects/#{project.id}/services/#{dashed_service}", user), service_attrs - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return if required fields missing" do @@ -45,7 +45,7 @@ describe API::API, api: true do it "should delete #{service}" do delete api("/projects/#{project.id}/services/#{dashed_service}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project.send(service_method).reload expect(project.send(service_method).activated?).to be_falsey end @@ -64,20 +64,20 @@ describe API::API, api: true do it 'should return authentication error when unauthenticated' do get api("/projects/#{project.id}/services/#{dashed_service}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should return all properties of service #{service} when authenticated as admin" do get api("/projects/#{project.id}/services/#{dashed_service}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map) end it "should return properties of service #{service} other than passwords when authenticated as project owner" do get api("/projects/#{project.id}/services/#{dashed_service}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords) end @@ -85,7 +85,7 @@ describe API::API, api: true do project.team << [user2, :developer] get api("/projects/#{project.id}/services/#{dashed_service}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index fbd57b34a58..c15b7ff9792 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -9,7 +9,7 @@ describe API::API, api: true do context "when valid password" do it "should return private token" do post api("/session"), email: user.email, password: '12345678' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['email']).to eq(user.email) expect(json_response['private_token']).to eq(user.private_token) @@ -48,7 +48,7 @@ describe API::API, api: true do context "when invalid password" do it "should return authentication error" do post api("/session"), email: user.email, password: '123' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil @@ -58,7 +58,7 @@ describe API::API, api: true do context "when empty password" do it "should return authentication error" do post api("/session"), email: user.email - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil @@ -68,7 +68,7 @@ describe API::API, api: true do context "when empty name" do it "should return authentication error" do post api("/session"), password: user.password - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index c815a8e1d73..f756101c514 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -10,7 +10,7 @@ describe API::API, 'Settings', api: true do describe "GET /application/settings" do it "should return application settings" do get api("/application/settings", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Hash expect(json_response['default_projects_limit']).to eq(42) expect(json_response['signin_enabled']).to be_truthy @@ -21,7 +21,7 @@ describe API::API, 'Settings', api: true do it "should update application settings" do put api("/application/settings", admin), default_projects_limit: 3, signin_enabled: false - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['signin_enabled']).to be_falsey end diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb index 41cbf0c6669..28067f8ca88 100644 --- a/spec/requests/api/sidekiq_metrics_spec.rb +++ b/spec/requests/api/sidekiq_metrics_spec.rb @@ -9,28 +9,28 @@ describe API::SidekiqMetrics, api: true do it 'defines the `queue_metrics` endpoint' do get api('/sidekiq/queue_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash end it 'defines the `process_metrics` endpoint' do get api('/sidekiq/process_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['processes']).to be_an Array end it 'defines the `job_stats` endpoint' do get api('/sidekiq/job_stats', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash end it 'defines the `compound_metrics` endpoint' do get api('/sidekiq/compound_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash expect(json_response['queues']).to be_a Hash expect(json_response['processes']).to be_an Array diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 94eebc48ec8..cf66f261ade 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -13,21 +13,21 @@ describe API::API, api: true do context "when no user" do it "should return authentication error" do get api("/hooks") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when not an admin" do it "should return forbidden error" do get api("/hooks", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as admin" do it "should return an array of hooks" do get api("/hooks", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['url']).to eq(hook.url) end @@ -43,7 +43,7 @@ describe API::API, api: true do it "should respond with 400 if url not given" do post api("/hooks", admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should not create new hook without url" do @@ -56,13 +56,13 @@ describe API::API, api: true do describe "GET /hooks/:id" do it "should return hook by id" do get api("/hooks/#{hook.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['event_name']).to eq('project_create') end it "should return 404 on failure" do get api("/hooks/404", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -75,7 +75,7 @@ describe API::API, api: true do it "should return success if hook id not found" do delete api("/hooks/12345", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 12e170b232f..fa700ab7343 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -18,7 +18,7 @@ describe API::API, api: true do context 'without releases' do it "should return an array of project tags" do get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) end @@ -33,7 +33,7 @@ describe API::API, api: true do it "should return an array of project tags with release info" do get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) expect(json_response.first['message']).to eq('Version 1.1.0') @@ -48,14 +48,14 @@ describe API::API, api: true do it 'returns a specific tag' do get api("/projects/#{project.id}/repository/tags/#{tag_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(tag_name) end it 'returns 404 for an invalid tag name' do get api("/projects/#{project.id}/repository/tags/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -66,7 +66,7 @@ describe API::API, api: true do tag_name: 'v7.0.1', ref: 'master' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.0.1') end end @@ -78,7 +78,7 @@ describe API::API, api: true do ref: 'master', release_description: 'Wow' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.0.1') expect(json_response['release']['description']).to eq('Wow') end @@ -94,13 +94,13 @@ describe API::API, api: true do context 'delete tag' do it 'should delete an existing tag' do delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['tag_name']).to eq(tag_name) end it 'should raise 404 if the tag does not exist' do delete api("/projects/#{project.id}/repository/tags/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -117,7 +117,7 @@ describe API::API, api: true do ref: 'master', message: 'Release 7.1.0' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.1.0') expect(json_response['message']).to eq('Release 7.1.0') end @@ -127,14 +127,14 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user2), tag_name: 'v1.9.0', ref: '621491c677087aa243f165eab467bfdfbee00be1' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should return 400 if tag name is invalid' do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v 1.0.0', ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Tag name invalid') end @@ -142,11 +142,11 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Tag v8.0.0 already exists') end @@ -154,7 +154,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'mytag', ref: 'foo' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Target foo is invalid') end end @@ -167,7 +167,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end @@ -176,7 +176,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/foobar/release", user), description: description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Tag does not exist') end @@ -190,7 +190,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Release already exists') end end @@ -211,7 +211,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: new_description - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(new_description) end @@ -221,7 +221,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/foobar/release", user), description: new_description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Tag does not exist') end @@ -229,7 +229,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: new_description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Release does not exist') end end diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index a6d5ade3013..68d0f41b489 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -22,7 +22,7 @@ describe API::Templates, api: true do it 'returns a list of available gitignore templates' do get api('/gitignores') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to be > 15 end @@ -34,7 +34,7 @@ describe API::Templates, api: true do it 'returns a list of available gitlab_ci_ymls' do get api('/gitlab_ci_ymls') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).not_to be_nil end @@ -45,7 +45,7 @@ describe API::Templates, api: true do it 'adds a disclaimer on the top' do get api('/gitlab_ci_ymls/Ruby') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['content']).to start_with("# This file is a template,") end end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index fdd4ec6d761..8992996c30a 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -29,17 +29,17 @@ describe API::API do context 'Handles errors' do it 'should return bad request if token is missing' do post api("/projects/#{project.id}/trigger/builds"), ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return not found if project is not found' do post api('/projects/0/trigger/builds'), options.merge(ref: 'master') - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return unauthorized if token is for different project' do post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -48,14 +48,14 @@ describe API::API do it 'should create builds' do post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master') - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('No builds created') end @@ -66,19 +66,19 @@ describe API::API do it 'should validate variables to be a hash' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master') - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.first.trigger_request.variables).to eq(variables) end @@ -91,7 +91,7 @@ describe API::API do it 'should return list of triggers' do get api("/projects/#{project.id}/triggers", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Array) expect(json_response[0]).to have_key('token') end @@ -101,7 +101,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -109,7 +109,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -119,14 +119,14 @@ describe API::API do it 'should return trigger details' do get api("/projects/#{project.id}/triggers/#{trigger.token}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Hash) end it 'should respond with 404 Not Found if requesting non-existing trigger' do get api("/projects/#{project.id}/triggers/abcdef012345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -134,7 +134,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers/#{trigger.token}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -142,7 +142,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers/#{trigger.token}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -154,7 +154,7 @@ describe API::API do post api("/projects/#{project.id}/triggers", user) end.to change{project.triggers.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response).to be_a(Hash) end end @@ -163,7 +163,7 @@ describe API::API do it 'should not create trigger' do post api("/projects/#{project.id}/triggers", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -171,7 +171,7 @@ describe API::API do it 'should not create trigger' do post api("/projects/#{project.id}/triggers") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -182,13 +182,13 @@ describe API::API do expect do delete api("/projects/#{project.id}/triggers/#{trigger.token}", user) end.to change{project.triggers.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should respond with 404 Not Found if requesting non-existing trigger' do delete api("/projects/#{project.id}/triggers/abcdef012345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -196,7 +196,7 @@ describe API::API do it 'should not delete trigger' do delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -204,7 +204,7 @@ describe API::API do it 'should not delete trigger' do delete api("/projects/#{project.id}/triggers/#{trigger.token}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a7690f430c4..056256a29f5 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/users") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -29,18 +29,18 @@ describe API::API, api: true do it "renders 403" do get api("/users") - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "renders 404" do get api("/users/#{user.id}") - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end it "should return an array of users" do get api("/users", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array username = user.username expect(json_response.detect do |user| @@ -50,7 +50,7 @@ describe API::API, api: true do it "should return one user" do get api("/users?username=#{omniauth_user.username}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['username']).to eq(omniauth_user.username) end @@ -59,7 +59,7 @@ describe API::API, api: true do context "when admin" do it "should return an array of users" do get api("/users", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first.keys).to include 'email' expect(json_response.first.keys).to include 'identities' @@ -74,24 +74,24 @@ describe API::API, api: true do describe "GET /users/:id" do it "should return a user by id" do get api("/users/#{user.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) end it "should return a 401 if unauthenticated" do get api("/users/9998") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should return a 404 error if user id not found" do get api("/users/9999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return a 404 if invalid ID" do get api("/users/1ASDF", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -106,7 +106,7 @@ describe API::API, api: true do it "should create user with correct attributes" do post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -116,7 +116,7 @@ describe API::API, api: true do it "should create non-admin user" do post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -126,7 +126,7 @@ describe API::API, api: true do it "should create non-admin users by default" do post api('/users', admin), attributes_for(:user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -135,12 +135,12 @@ describe API::API, api: true do it "should return 201 Created on success" do post api("/users", admin), attributes_for(:user, projects_limit: 3) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'creates non-external users by default' do post api("/users", admin), attributes_for(:user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) @@ -150,7 +150,7 @@ describe API::API, api: true do it 'should allow an external user to be created' do post api("/users", admin), attributes_for(:user, external: true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) @@ -163,27 +163,27 @@ describe API::API, api: true do email: 'invalid email', password: 'password', name: 'test' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if name not given' do post api('/users', admin), attributes_for(:user).except(:name) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if password not given' do post api('/users', admin), attributes_for(:user).except(:password) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if email not given' do post api('/users', admin), attributes_for(:user).except(:email) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if username not given' do post api('/users', admin), attributes_for(:user).except(:username) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if user does not validate' do @@ -194,7 +194,7 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['password']). to eq(['is too short (minimum is 8 characters)']) expect(json_response['message']['bio']). @@ -207,7 +207,7 @@ describe API::API, api: true do it "shouldn't available for non admin users" do post api("/users", user), attributes_for(:user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end context 'with existing user' do @@ -227,7 +227,7 @@ describe API::API, api: true do password: 'password', username: 'foo' end.to change { User.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Email has already been taken') end @@ -239,7 +239,7 @@ describe API::API, api: true do password: 'password', username: 'test' end.to change { User.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Username has already been taken') end end @@ -249,7 +249,7 @@ describe API::API, api: true do it "should redirect to sign in page" do get "/users/sign_up" - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(new_user_session_path) end end @@ -261,41 +261,41 @@ describe API::API, api: true do it "should update user with new bio" do put api("/users/#{user.id}", admin), { bio: 'new test bio' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['bio']).to eq('new test bio') expect(user.reload.bio).to eq('new test bio') end it 'should update user with his own email' do put api("/users/#{user.id}", admin), email: user.email - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['email']).to eq(user.email) expect(user.reload.email).to eq(user.email) end it 'should update user with his own username' do put api("/users/#{user.id}", admin), username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) expect(user.reload.username).to eq(user.username) end it "should update user's existing identity" do put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321') end it 'should update user with new identity' do put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.identities.first.extern_uid).to eq('67890') expect(user.reload.identities.first.provider).to eq('github') end it "should update admin status" do put api("/users/#{user.id}", admin), { admin: true } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['is_admin']).to eq(true) expect(user.reload.admin).to eq(true) end @@ -309,7 +309,7 @@ describe API::API, api: true do it "should not update admin status" do put api("/users/#{admin_user.id}", admin), { can_create_group: false } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['is_admin']).to eq(true) expect(admin_user.reload.admin).to eq(true) expect(admin_user.can_create_group).to eq(false) @@ -317,18 +317,18 @@ describe API::API, api: true do it "should not allow invalid update" do put api("/users/#{user.id}", admin), { email: 'invalid email' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(user.reload.email).not_to eq('invalid email') end it "shouldn't available for non admin users" do put api("/users/#{user.id}", user), attributes_for(:user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 404 for non-existing user" do put api("/users/999999", admin), { bio: 'update should fail' } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -344,7 +344,7 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['password']). to eq(['is too short (minimum is 8 characters)']) expect(json_response['message']['bio']). @@ -364,14 +364,14 @@ describe API::API, api: true do it 'should return 409 conflict error if email address exists' do put api("/users/#{@user.id}", admin), email: 'test@example.com' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(@user.reload.email).to eq(@user.email) end it 'should return 409 conflict error if username taken' do @user_id = User.all.last.id put api("/users/#{@user.id}", admin), username: 'test' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(@user.reload.username).to eq(@user.username) end end @@ -382,13 +382,13 @@ describe API::API, api: true do it "should not create invalid ssh key" do post api("/users/#{user.id}/keys", admin), { title: "invalid key" } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create key without title' do post api("/users/#{user.id}/keys", admin), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "title" not given') end @@ -401,7 +401,7 @@ describe API::API, api: true do it "should return 405 for invalid ID" do post api("/users/ASDF/keys", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end @@ -411,14 +411,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/users/#{user.id}/keys") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing user' do get api('/users/999999/keys', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -426,14 +426,14 @@ describe API::API, api: true do user.keys << key user.save get api("/users/#{user.id}/keys", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(key.title) end it "should return 405 for invalid ID" do get api("/users/ASDF/keys", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end end @@ -444,7 +444,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do delete api("/users/#{user.id}/keys/42") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -455,20 +455,20 @@ describe API::API, api: true do expect do delete api("/users/#{user.id}/keys/#{key.id}", admin) end.to change { user.keys.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 error if user not found' do user.keys << key user.save delete api("/users/999999/keys/#{key.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it 'should return 404 error if key not foud' do delete api("/users/#{user.id}/keys/42", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Key Not Found') end end @@ -479,7 +479,7 @@ describe API::API, api: true do it "should not create invalid email" do post api("/users/#{user.id}/emails", admin), {} - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "email" not given') end @@ -492,7 +492,7 @@ describe API::API, api: true do it "should raise error for invalid ID" do post api("/users/ASDF/emails", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end @@ -502,14 +502,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/users/#{user.id}/emails") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing user' do get api('/users/999999/emails', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -517,14 +517,14 @@ describe API::API, api: true do user.emails << email user.save get api("/users/#{user.id}/emails", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['email']).to eq(email.email) end it "should raise error for invalid ID" do put api("/users/ASDF/emails", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end end @@ -535,7 +535,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do delete api("/users/#{user.id}/emails/42") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -546,20 +546,20 @@ describe API::API, api: true do expect do delete api("/users/#{user.id}/emails/#{email.id}", admin) end.to change { user.emails.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 error if user not found' do user.emails << email user.save delete api("/users/999999/emails/#{email.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it 'should return 404 error if email not foud' do delete api("/users/#{user.id}/emails/42", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Email Not Found') end @@ -574,24 +574,24 @@ describe API::API, api: true do it "should delete user" do delete api("/users/#{user.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound expect(json_response['email']).to eq(user.email) end it "should not delete for unauthenticated user" do delete api("/users/#{user.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "shouldn't available for non admin users" do delete api("/users/#{user.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 404 for non-existing user" do delete api("/users/999999", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -603,7 +603,7 @@ describe API::API, api: true do describe "GET /user" do it "should return current user" do get api("/user", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['email']).to eq(user.email) expect(json_response['is_admin']).to eq(user.is_admin?) expect(json_response['can_create_project']).to eq(user.can_create_project?) @@ -613,7 +613,7 @@ describe API::API, api: true do it "should return 401 error if user is unauthenticated" do get api("/user") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -621,7 +621,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/user/keys") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -630,7 +630,7 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first["title"]).to eq(key.title) end @@ -642,13 +642,13 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys/#{key.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["title"]).to eq(key.title) end it "should return 404 Not Found within invalid ID" do get api("/user/keys/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -657,13 +657,13 @@ describe API::API, api: true do user.save admin get api("/user/keys/#{key.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return 404 for invalid ID" do get api("/users/keys/ASDF", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -673,29 +673,29 @@ describe API::API, api: true do expect do post api("/user/keys", user), key_attrs end.to change{ user.keys.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should return a 401 error if unauthorized" do post api("/user/keys"), title: 'some title', key: 'some key' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should not create ssh key without key" do post api("/user/keys", user), title: 'title' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create ssh key without title' do post api('/user/keys', user), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "title" not given') end it "should not create ssh key without title" do post api("/user/keys", user), key: "somekey" - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -706,19 +706,19 @@ describe API::API, api: true do expect do delete api("/user/keys/#{key.id}", user) end.to change{user.keys.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success if key ID not found" do delete api("/user/keys/42", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 401 error if unauthorized" do user.keys << key user.save delete api("/user/keys/#{key.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should raise error for invalid ID" do @@ -730,7 +730,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/user/emails") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -739,7 +739,7 @@ describe API::API, api: true do user.emails << email user.save get api("/user/emails", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first["email"]).to eq(email.email) end @@ -751,13 +751,13 @@ describe API::API, api: true do user.emails << email user.save get api("/user/emails/#{email.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["email"]).to eq(email.email) end it "should return 404 Not Found within invalid ID" do get api("/user/emails/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -766,13 +766,13 @@ describe API::API, api: true do user.save admin get api("/user/emails/#{email.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return 404 for invalid ID" do get api("/users/emails/ASDF", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -782,17 +782,17 @@ describe API::API, api: true do expect do post api("/user/emails", user), email_attrs end.to change{ user.emails.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should return a 401 error if unauthorized" do post api("/user/emails"), email: 'some email' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should not create email with invalid email" do post api("/user/emails", user), {} - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "email" not given') end end @@ -804,19 +804,19 @@ describe API::API, api: true do expect do delete api("/user/emails/#{email.id}", user) end.to change{user.emails.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success if email ID not found" do delete api("/user/emails/42", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 401 error if unauthorized" do user.emails << email user.save delete api("/user/emails/#{email.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should raise error for invalid ID" do @@ -828,25 +828,25 @@ describe API::API, api: true do before { admin } it 'should block existing user' do put api("/users/#{user.id}/block", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.state).to eq('blocked') end it 'should not re-block ldap blocked users' do put api("/users/#{ldap_blocked_user.id}/block", admin) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(ldap_blocked_user.reload.state).to eq('ldap_blocked') end it 'should not be available for non admin users' do put api("/users/#{user.id}/block", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(user.reload.state).to eq('active') end it 'should return a 404 error if user id not found' do put api('/users/9999/block', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end end @@ -857,31 +857,31 @@ describe API::API, api: true do it 'should unblock existing user' do put api("/users/#{user.id}/unblock", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.state).to eq('active') end it 'should unblock a blocked user' do put api("/users/#{blocked_user.id}/unblock", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(blocked_user.reload.state).to eq('active') end it 'should not unblock ldap blocked users' do put api("/users/#{ldap_blocked_user.id}/unblock", admin) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(ldap_blocked_user.reload.state).to eq('ldap_blocked') end it 'should not be available for non admin users' do put api("/users/#{user.id}/unblock", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(user.reload.state).to eq('active') end it 'should return a 404 error if user id not found' do put api('/users/9999/block', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index b1e1053d037..ddba18245f8 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do it 'should return project variables' do get api("/projects/#{project.id}/variables", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Array) end end @@ -24,7 +24,7 @@ describe API::API, api: true do it 'should not return project variables' do get api("/projects/#{project.id}/variables", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -32,7 +32,7 @@ describe API::API, api: true do it 'should not return project variables' do get api("/projects/#{project.id}/variables") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -42,14 +42,14 @@ describe API::API, api: true do it 'should return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['value']).to eq(variable.value) end it 'should respond with 404 Not Found if requesting non-existing variable' do get api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -57,7 +57,7 @@ describe API::API, api: true do it 'should not return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -65,7 +65,7 @@ describe API::API, api: true do it 'should not return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -77,7 +77,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2' end.to change{project.variables.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['key']).to eq('TEST_VARIABLE_2') expect(json_response['value']).to eq('VALUE_2') end @@ -87,7 +87,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/variables", user), key: variable.key, value: 'VALUE_2' end.to change{project.variables.count}.by(0) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -95,7 +95,7 @@ describe API::API, api: true do it 'should not create variable' do post api("/projects/#{project.id}/variables", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -103,7 +103,7 @@ describe API::API, api: true do it 'should not create variable' do post api("/projects/#{project.id}/variables") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -118,7 +118,7 @@ describe API::API, api: true do updated_variable = project.variables.first - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(value_before).to eq(variable.value) expect(updated_variable.value).to eq('VALUE_1_UP') end @@ -126,7 +126,7 @@ describe API::API, api: true do it 'should responde with 404 Not Found if requesting non-existing variable' do put api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -134,7 +134,7 @@ describe API::API, api: true do it 'should not update variable' do put api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -142,7 +142,7 @@ describe API::API, api: true do it 'should not update variable' do put api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -153,13 +153,13 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/variables/#{variable.key}", user) end.to change{project.variables.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should responde with 404 Not Found if requesting non-existing variable' do delete api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -167,7 +167,7 @@ describe API::API, api: true do it 'should not delete variable' do delete api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -175,7 +175,7 @@ describe API::API, api: true do it 'should not delete variable' do delete api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7e50bea90d1..1bc51783c3a 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -26,7 +26,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(build.sha) expect(runner.reload.platform).to eq("darwin") end @@ -34,7 +34,7 @@ describe Ci::API::API do it "should return 404 error if no pending build found" do post ci_api("/builds/register"), token: runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 error if no builds for specific runner" do @@ -43,7 +43,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 error if no builds for shared runner" do @@ -52,7 +52,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: shared_runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns options" do @@ -61,7 +61,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] }) end @@ -72,7 +72,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["variables"]).to eq([ { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, @@ -91,7 +91,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["variables"]).to eq([ { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, @@ -109,7 +109,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["depends_on_builds"].count).to eq(2) expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec") end @@ -122,7 +122,7 @@ describe Ci::API::API do it do post ci_api("/builds/register"), token: runner.token, info: { param => value } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) runner.reload is_expected.to eq(value) end @@ -172,7 +172,7 @@ describe Ci::API::API do end it "should update a running build" do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not override trace information when no trace is given' do @@ -252,13 +252,13 @@ describe Ci::API::API do context "should authorize posting artifact to running build" do it "using token as parameter" do post authorize_url, { token: build.token }, headers - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["TempPath"]).not_to be_nil end it "using token as header" do post authorize_url, {}, headers_with_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["TempPath"]).not_to be_nil end end @@ -267,13 +267,13 @@ describe Ci::API::API do it "using token as parameter" do stub_application_setting(max_artifacts_size: 0) post authorize_url, { token: build.token, filesize: 100 }, headers - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end it "using token as header" do stub_application_setting(max_artifacts_size: 0) post authorize_url, { filesize: 100 }, headers_with_token - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end end @@ -281,7 +281,7 @@ describe Ci::API::API do before { post authorize_url, { token: 'invalid', filesize: 100 } } it 'should respond with forbidden' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -305,20 +305,20 @@ describe Ci::API::API do context "should post artifact to running build" do it "uses regual file post" do upload_artifacts(file_upload, headers_with_token, false) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) end it "uses accelerated file post" do upload_artifacts(file_upload, headers_with_token, true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) end it "updates artifact" do upload_artifacts(file_upload, headers_with_token) upload_artifacts(file_upload2, headers_with_token) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename) end end @@ -343,7 +343,7 @@ describe Ci::API::API do end it 'stores artifacts and artifacts metadata' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename) expect(stored_metadata_file.original_filename).to eq(metadata.original_filename) end @@ -355,7 +355,7 @@ describe Ci::API::API do end it 'is expected to respond with bad request' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'does not store metadata' do @@ -382,7 +382,7 @@ describe Ci::API::API do it 'updates when specified' do build.reload - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['artifacts_expire_at']).not_to be_empty expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days) end @@ -393,7 +393,7 @@ describe Ci::API::API do it 'ignores if not specified' do build.reload - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['artifacts_expire_at']).to be_nil expect(build.artifacts_expire_at).to be_nil end @@ -404,21 +404,21 @@ describe Ci::API::API do it "should fail to post too large artifact" do stub_application_setting(max_artifacts_size: 0) upload_artifacts(file_upload, headers_with_token) - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end end context "artifacts post request does not contain file" do it "should fail to post artifacts without file" do post post_url, {}, headers_with_token - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end context 'GitLab Workhorse is not configured' do it "should fail to post artifacts without GitLab-Workhorse" do post post_url, { token: build.token }, {} - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -437,7 +437,7 @@ describe Ci::API::API do it "should fail to post artifacts for outside of tmp path" do upload_artifacts(file_upload, headers_with_token) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -458,7 +458,7 @@ describe Ci::API::API do before { delete delete_url, token: build.token } it 'should remove build artifacts' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(build.artifacts_file.exists?).to be_falsy expect(build.artifacts_metadata.exists?).to be_falsy end @@ -475,14 +475,14 @@ describe Ci::API::API do end it 'should download artifact' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.headers).to include download_headers end end context 'build does not has artifacts' do it 'should respond with not found' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 72f6a3c981d..f12678e5a8e 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -21,17 +21,17 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger") - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return not found if project is not found' do post ci_api('/projects/0/refs/master/trigger'), options - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return unauthorized if token is for different project' do post ci_api("/projects/#{project2.ci_id}/refs/master/trigger"), options - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -40,14 +40,14 @@ describe Ci::API::API do it 'should create builds' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('No builds created') end @@ -58,19 +58,19 @@ describe Ci::API::API do it 'should validate variables to be a hash' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.first.trigger_request.variables).to eq(variables) end diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index fd26ca97818..bae56334be4 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -14,7 +14,7 @@ describe 'Git HTTP requests', lib: true do context "when no authentication is provided" do it "responds with status 401 (no project existence information leak)" do download('doesnt/exist.git') do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -23,7 +23,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication fails" do it "responds with status 401" do download('doesnt/exist.git', user: user.username, password: "nope") do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -31,7 +31,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication succeeds" do it "responds with status 404" do download('/doesnt/exist.git', user: user.username, password: user.password) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -46,7 +46,7 @@ describe 'Git HTTP requests', lib: true do download("/#{wiki.repository.path_with_namespace}.git") do |response| json_body = ActiveSupport::JSON.decode(response.body) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace) end end @@ -62,13 +62,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do download(path, {}) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end it "uploads get status 401" do upload(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -77,7 +77,7 @@ describe 'Git HTTP requests', lib: true do it "uploads get status 200 (because Git hooks do the real check)" do upload(path, env) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -86,7 +86,7 @@ describe 'Git HTTP requests', lib: true do allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false) upload(path, env) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -97,7 +97,7 @@ describe 'Git HTTP requests', lib: true do allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false) download(path, {}) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -111,13 +111,13 @@ describe 'Git HTTP requests', lib: true do context "when no authentication is provided" do it "responds with status 401 to downloads" do download(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end it "responds with status 401 to uploads" do upload(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -128,7 +128,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication fails" do it "responds with status 401" do download(path, env) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -139,7 +139,7 @@ describe 'Git HTTP requests', lib: true do clone_get(path, env) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -158,7 +158,7 @@ describe 'Git HTTP requests', lib: true do project.team << [user, :master] download(path, env) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -169,12 +169,12 @@ describe 'Git HTTP requests', lib: true do clone_get(path, env) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 200" do upload(path, env) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -188,13 +188,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do clone_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 401 (no project existence information leak)" do push_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -232,13 +232,13 @@ describe 'Git HTTP requests', lib: true do context "when the user doesn't have access to the project" do it "downloads get status 404" do download(path, user: user.username, password: user.password) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end it "uploads get status 200 (because Git hooks do the real check)" do upload(path, user: user.username, password: user.password) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -256,13 +256,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 401 (no project existence information leak)" do push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -336,7 +336,7 @@ describe 'Git HTTP requests', lib: true do end it "returns the file" do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -344,7 +344,7 @@ describe 'Git HTTP requests', lib: true do before { get "/#{project.path_with_namespace}/blob/master/info/refs" } it "returns not found" do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index d2d4a9eca18..c6172b9cc7d 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -11,12 +11,12 @@ describe JwtController do context 'existing service' do subject! { get '/jwt/auth', parameters } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } context 'returning custom http code' do let(:service) { double(execute: { http_status: 505 }) } - it { expect(response.status).to eq(505) } + it { expect(response).to have_http_status(505) } end end @@ -36,7 +36,7 @@ describe JwtController do context 'project with disabled CI' do let(:builds_enabled) { false } - it { expect(response.status).to eq(403) } + it { expect(response).to have_http_status(403) } end end @@ -56,14 +56,14 @@ describe JwtController do subject! { get '/jwt/auth', parameters, headers } - it { expect(response.status).to eq(403) } + it { expect(response).to have_http_status(403) } end end context 'unknown service' do subject! { get '/jwt/auth', service: 'unknown' } - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end def credentials(login, password) diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index deab242f45a..309213bd44c 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -83,6 +83,9 @@ describe CreateCommitBuildsService, services: true do context 'when commit contains a [ci skip] directive' do let(:message) { "some message[ci skip]" } + let(:messageFlip) { "some message[skip ci]" } + let(:capMessage) { "some message[CI SKIP]" } + let(:capMessageFlip) { "some message[SKIP CI]" } before do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } @@ -96,12 +99,55 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [skip ci] tag in commit message" do + commits = [{ message: messageFlip }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [CI SKIP] tag in commit message" do + commits = [{ message: capMessage }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [SKIP CI] tag in commit message" do + commits = [{ message: capMessageFlip }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(pipeline).to be_persisted expect(pipeline.builds.any?).to be false expect(pipeline.status).to eq("skipped") end - it "does not skips builds creation if there is no [ci skip] tag in commit message" do + it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" } commits = [{ message: "some message" }] diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb new file mode 100644 index 00000000000..14f3301d9f4 --- /dev/null +++ b/spec/services/search/snippet_service_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Search::SnippetService, services: true do + let(:author) { create(:author) } + let(:project) { create(:empty_project) } + + let!(:public_snippet) { create(:snippet, :public, content: 'password: XXX') } + let!(:internal_snippet) { create(:snippet, :internal, content: 'password: XXX') } + let!(:private_snippet) { create(:snippet, :private, content: 'password: XXX', author: author) } + + let!(:project_public_snippet) { create(:snippet, :public, project: project, content: 'password: XXX') } + let!(:project_internal_snippet) { create(:snippet, :internal, project: project, content: 'password: XXX') } + let!(:project_private_snippet) { create(:snippet, :private, project: project, content: 'password: XXX') } + + describe '#execute' do + context 'unauthenticated' do + it 'returns public snippets only' do + search = described_class.new(nil, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, project_public_snippet] + end + end + + context 'authenticated' do + it 'returns only public & internal snippets for regular users' do + user = create(:user) + search = described_class.new(user, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns public, internal snippets and project private snippets for project members' do + member = create(:user) + project.team << [member, :developer] + search = described_class.new(member, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + + it 'returns public, internal and private snippets where user is the author' do + search = described_class.new(author, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns all snippets when user is admin' do + admin = create(:admin) + search = described_class.new(admin, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 498bd4bf800..426bf53f198 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -17,6 +17,7 @@ module TestEnv "'test'" => 'e56497b', 'orphaned-branch' => '45127a9', 'binary-encoding' => '7b1cf43', + 'gitattributes' => '5a62481', } # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily |