diff options
74 files changed, 854 insertions, 311 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 024f2929252..cd19b6f47ff 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -226,6 +226,7 @@ update-tests-metadata: flaky-examples-check: <<: *dedicated-runner + <<: *except-docs image: ruby:2.3-alpine services: [] before_script: [] @@ -84,7 +84,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'hashie-forbidden_attributes' # Pagination -gem 'kaminari', '~> 0.17.0' +gem 'kaminari', '~> 1.0' # HAML gem 'hamlit', '~> 2.6.1' diff --git a/Gemfile.lock b/Gemfile.lock index ab01a556561..3e661d4e3ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,7 +113,7 @@ GEM activesupport (>= 4.0.0) mime-types (>= 1.16) cause (0.1) - charlock_holmes (0.7.4) + charlock_holmes (0.7.3) chronic (0.10.2) chronic_duration (0.10.6) numerizer (~> 0.1.1) @@ -419,9 +419,18 @@ GEM json-schema (2.6.2) addressable (~> 2.3.8) jwt (1.5.6) - kaminari (0.17.0) - actionpack (>= 3.0.0) - activesupport (>= 3.0.0) + kaminari (1.0.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.0.1) + kaminari-activerecord (= 1.0.1) + kaminari-core (= 1.0.1) + kaminari-actionview (1.0.1) + actionview + kaminari-core (= 1.0.1) + kaminari-activerecord (1.0.1) + activerecord + kaminari-core (= 1.0.1) + kaminari-core (1.0.1) kgio (2.10.0) knapsack (1.11.0) rake @@ -1011,7 +1020,7 @@ DEPENDENCIES jquery-rails (~> 4.1.0) json-schema (~> 2.6.2) jwt (~> 1.5.6) - kaminari (~> 0.17.0) + kaminari (~> 1.0) knapsack (~> 1.11.0) kubeclient (~> 2.2.0) letter_opener_web (~> 1.3.0) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 2bb867052f6..0a194f3707f 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -453,7 +453,10 @@ ul.notes { } .note-actions { + align-self: flex-start; flex-shrink: 0; + display: inline-flex; + align-items: center; // For PhantomJS that does not support flex float: right; margin-left: 10px; @@ -463,18 +466,12 @@ ul.notes { float: none; margin-left: 0; } - - .note-action-button { - margin-left: 8px; - } - - .more-actions-toggle { - margin-left: 2px; - } } .more-actions { - display: inline-block; + float: right; // phantomjs fallback + display: flex; + align-items: flex-end; .tooltip { white-space: nowrap; @@ -482,16 +479,10 @@ ul.notes { } .more-actions-toggle { - padding: 0; - &:hover .icon, &:focus .icon { color: $blue-600; } - - .icon { - padding: 0 6px; - } } .more-actions-dropdown { @@ -519,28 +510,42 @@ ul.notes { @include notes-media('max', $screen-md-max) { float: none; margin-left: 0; + } +} - .note-action-button { - margin-left: 0; - } +.note-actions-item { + margin-left: 15px; + display: flex; + align-items: center; + + &.more-actions { + // compensate for narrow icon + margin-left: 10px; } } .note-action-button { - display: inline; - line-height: 20px; + line-height: 1; + padding: 0; + min-width: 16px; + color: $gray-darkest; .fa { - color: $gray-darkest; position: relative; - font-size: 17px; + font-size: 16px; } + + svg { height: 16px; width: 16px; - fill: $gray-darkest; + top: 0; vertical-align: text-top; + + path { + fill: currentColor; + } } .award-control-icon-positive, @@ -613,10 +618,7 @@ ul.notes { .note-role { position: relative; - top: -2px; - display: inline-block; - padding-left: 7px; - padding-right: 7px; + padding: 0 7px; color: $notes-role-color; font-size: 12px; line-height: 20px; diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb index 52e06f4945a..1ab107168c0 100644 --- a/app/controllers/concerns/cycle_analytics_params.rb +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -6,6 +6,13 @@ module CycleAnalyticsParams end def start_date(params) - params[:start_date] == '30' ? 30.days.ago : 90.days.ago + case params[:start_date] + when '7' + 7.days.ago + when '30' + 30.days.ago + else + 90.days.ago + end end end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 741879dee35..762c6ebf3a3 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -6,7 +6,7 @@ class Explore::ProjectsController < Explore::ApplicationController def index params[:sort] ||= 'latest_activity_desc' @sort = params[:sort] - @projects = load_projects.page(params[:page]) + @projects = load_projects respond_to do |format| format.html @@ -21,7 +21,7 @@ class Explore::ProjectsController < Explore::ApplicationController def trending params[:trending] = true @sort = params[:sort] - @projects = load_projects.page(params[:page]) + @projects = load_projects respond_to do |format| format.html @@ -34,7 +34,7 @@ class Explore::ProjectsController < Explore::ApplicationController end def starred - @projects = load_projects.reorder('star_count DESC').page(params[:page]) + @projects = load_projects.reorder('star_count DESC') respond_to do |format| format.html @@ -50,6 +50,9 @@ class Explore::ProjectsController < Explore::ApplicationController def load_projects ProjectsFinder.new(current_user: current_user, params: params) - .execute.includes(:route, namespace: :route) + .execute + .includes(:route, namespace: :route) + .page(params[:page]) + .without_count end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index f4d4cca8dd8..8893a514207 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -212,7 +212,7 @@ class Projects::IssuesController < Projects::ApplicationController end def create_merge_request - result = MergeRequests::CreateFromIssueService.new(project, current_user, issue_iid: issue.iid).execute + result = ::MergeRequests::CreateFromIssueService.new(project, current_user, issue_iid: issue.iid).execute if result[:status] == :success render json: MergeRequestCreateSerializer.new.represent(result[:merge_request]) diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb new file mode 100644 index 00000000000..83dd76a01dd --- /dev/null +++ b/app/helpers/pagination_helper.rb @@ -0,0 +1,21 @@ +module PaginationHelper + def paginate_collection(collection, remote: nil) + if collection.is_a?(Kaminari::PaginatableWithoutCount) + paginate_without_count(collection) + elsif collection.respond_to?(:total_pages) + paginate_with_count(collection, remote: remote) + end + end + + def paginate_without_count(collection) + render( + 'kaminari/gitlab/without_count', + previous_path: path_to_prev_page(collection), + next_path: path_to_next_page(collection) + ) + end + + def paginate_with_count(collection, remote: nil) + paginate(collection, remote: remote, theme: 'gitlab') + end +end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f90194041b1..ac08dc0ee1f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -443,7 +443,8 @@ class MergeRequest < ActiveRecord::Base end def reload_diff_if_branch_changed - if source_branch_changed? || target_branch_changed? + if (source_branch_changed? || target_branch_changed?) && + (source_branch_head && target_branch_head) reload_diff end end @@ -792,11 +793,7 @@ class MergeRequest < ActiveRecord::Base end def fetch_ref - target_project.repository.fetch_ref( - source_project.repository.path_to_repo, - "refs/heads/#{source_branch}", - ref_path - ) + write_ref update_column(:ref_fetched, true) end @@ -939,4 +936,17 @@ class MergeRequest < ActiveRecord::Base true end + + private + + def write_ref + target_project.repository.with_repo_branch_commit( + source_project.repository, source_branch) do |commit| + if commit + target_project.repository.write_ref(ref_path, commit.sha) + else + raise Rugged::ReferenceError, 'source repository is empty' + end + end + end end diff --git a/app/models/project.rb b/app/models/project.rb index 7010664e1c8..7cdd00bc17b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1048,9 +1048,7 @@ class Project < ActiveRecord::Base def change_head(branch) if repository.branch_exists?(branch) repository.before_change_head - repository.rugged.references.create('HEAD', - "refs/heads/#{branch}", - force: true) + repository.write_ref('HEAD', "refs/heads/#{branch}") repository.copy_gitattributes(branch) repository.after_change_head reload_default_branch diff --git a/app/models/repository.rb b/app/models/repository.rb index 049bebdbe42..a761302b06b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -224,7 +224,7 @@ class Repository # This will still fail if the file is corrupted (e.g. 0 bytes) begin - rugged.references.create(keep_around_ref_name(sha), sha, force: true) + write_ref(keep_around_ref_name(sha), sha) rescue Rugged::ReferenceError => ex Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}" rescue Rugged::OSError => ex @@ -237,6 +237,10 @@ class Repository ref_exists?(keep_around_ref_name(sha)) end + def write_ref(ref_path, sha) + rugged.references.create(ref_path, sha, force: true) + end + def diverging_commit_counts(branch) root_ref_hash = raw_repository.rev_parse_target(root_ref).oid cache.fetch(:"diverging_commit_counts_#{branch.name}") do @@ -985,12 +989,10 @@ class Repository if start_repository == self start_branch_name else - tmp_ref = "refs/tmp/#{SecureRandom.hex}/head" - - fetch_ref( + tmp_ref = fetch_ref( start_repository.path_to_repo, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}", - tmp_ref + "refs/tmp/#{SecureRandom.hex}/head" ) start_repository.commit(start_branch_name).sha @@ -1021,7 +1023,12 @@ class Repository def fetch_ref(source_path, source_ref, target_ref) args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) - run_git(args) + message, status = run_git(args) + + # Make sure ref was created, and raise Rugged::ReferenceError when not + raise Rugged::ReferenceError, message if status != 0 + + target_ref end def create_ref(ref, ref_path) diff --git a/app/models/user.rb b/app/models/user.rb index a4615436245..2b25736bb26 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -726,9 +726,9 @@ class User < ActiveRecord::Base end def sanitize_attrs - %w[username skype linkedin twitter].each do |attr| - value = public_send(attr) # rubocop:disable GitlabSecurity/PublicSend - public_send("#{attr}=", Sanitize.clean(value)) if value.present? # rubocop:disable GitlabSecurity/PublicSend + %i[skype linkedin twitter].each do |attr| + value = self[attr] + self[attr] = Sanitize.clean(value) if value.present? end end diff --git a/app/views/kaminari/gitlab/_without_count.html.haml b/app/views/kaminari/gitlab/_without_count.html.haml new file mode 100644 index 00000000000..250029c4475 --- /dev/null +++ b/app/views/kaminari/gitlab/_without_count.html.haml @@ -0,0 +1,8 @@ +.gl-pagination + %ul.pagination.clearfix + - if previous_path + %li.prev + = link_to(t('views.pagination.previous'), previous_path, rel: 'prev') + - if next_path + %li.next + = link_to(t('views.pagination.next'), next_path, rel: 'next') diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index c704635ead3..3467e357c49 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -40,6 +40,9 @@ %i.fa.fa-chevron-down %ul.dropdown-menu.dropdown-menu-align-right %li + %a{ "href" => "#", "data-value" => "7" } + {{ n__('Last %d day', 'Last %d days', 7) }} + %li %a{ "href" => "#", "data-value" => "30" } {{ n__('Last %d day', 'Last %d days', 30) }} %li diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index 9c42be4e0ff..cb737d129f0 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -17,24 +17,32 @@ "inline-template" => true, "ref" => "note_#{note.id}" } - %button.note-action-button.line-resolve-btn{ type: "button", - class: ("is-disabled" unless can_resolve), - ":class" => "{ 'is-active': isResolved }", - ":aria-label" => "buttonText", - "@click" => "resolve", - ":title" => "buttonText", - ":ref" => "'button'" } + .note-actions-item + %button.note-action-button.line-resolve-btn{ type: "button", + class: ("is-disabled" unless can_resolve), + ":class" => "{ 'is-active': isResolved }", + ":aria-label" => "buttonText", + "@click" => "resolve", + ":title" => "buttonText", + ":ref" => "'button'" } - = icon('spin spinner', 'v-show' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading') - %div{ 'v-show' => '!loading' }= render 'shared/icons/icon_status_success.svg' + = icon('spin spinner', 'v-show' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading') + %div{ 'v-show' => '!loading' }= render 'shared/icons/icon_status_success.svg' - if current_user - if note.emoji_awardable? - user_authored = note.user_authored?(current_user) - = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip", data: { position: 'right' } do - = icon('spinner spin') - %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') - %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') - %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile') + .note-actions-item + = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do + = icon('spinner spin') + %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') + %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') + %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile') - = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable + - if note_editable + .note-actions-item + = button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do + %span.link-highlight + = custom_icon('icon_pencil') + + = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml index 75a4687e1e3..5930209a682 100644 --- a/app/views/projects/notes/_more_actions_dropdown.html.haml +++ b/app/views/projects/notes/_more_actions_dropdown.html.haml @@ -1,14 +1,11 @@ - is_current_user = current_user == note.author - if note_editable || !is_current_user - .dropdown.more-actions + .dropdown.more-actions.note-actions-item = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do - = icon('ellipsis-v', class: 'icon') + %span.icon + = custom_icon('ellipsis_v') %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left - - if note_editable - %li - = button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent' - %li.divider - unless is_current_user %li = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do diff --git a/app/views/shared/icons/_ellipsis_v.svg b/app/views/shared/icons/_ellipsis_v.svg new file mode 100644 index 00000000000..9117a9bb9ec --- /dev/null +++ b/app/views/shared/icons/_ellipsis_v.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></svg> diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 914506bf0ce..0bedfea3502 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -23,6 +23,6 @@ = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon') %strong= pluralize(@private_forks_count, 'private fork') %span you have no access to. - = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages + = paginate_collection(projects, remote: remote) - else .nothing-here-block No projects found diff --git a/app/views/snippets/notes/_actions.html.haml b/app/views/snippets/notes/_actions.html.haml index 098a88c48c5..3a50324770d 100644 --- a/app/views/snippets/notes/_actions.html.haml +++ b/app/views/snippets/notes/_actions.html.haml @@ -1,10 +1,17 @@ - if current_user - if note.emoji_awardable? - user_authored = note.user_authored?(current_user) - = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip", data: { position: 'right' } do - = icon('spinner spin') - %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') - %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') - %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile') + .note-actions-item + = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip", data: { position: 'right' } do + = icon('spinner spin') + %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') + %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') + %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile') + + - if note_editable + .note-actions-item + = button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do + %span.link-highlight + = custom_icon('icon_pencil') = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable diff --git a/changelogs/unreleased/34527-make-edit-comment-button-always-available-outside-of-dropdown.yml b/changelogs/unreleased/34527-make-edit-comment-button-always-available-outside-of-dropdown.yml new file mode 100644 index 00000000000..08171f6bcec --- /dev/null +++ b/changelogs/unreleased/34527-make-edit-comment-button-always-available-outside-of-dropdown.yml @@ -0,0 +1,4 @@ +--- +title: move edit comment button outside of dropdown +merge_request: +author: diff --git a/changelogs/unreleased/fix-thread-safe-gpgme-tmp-directory.yml b/changelogs/unreleased/fix-thread-safe-gpgme-tmp-directory.yml new file mode 100644 index 00000000000..66b5b6b4f47 --- /dev/null +++ b/changelogs/unreleased/fix-thread-safe-gpgme-tmp-directory.yml @@ -0,0 +1,4 @@ +--- +title: Make GPGME temporary directory handling thread safe +merge_request: 13481 +author: Alexis Reigel diff --git a/changelogs/unreleased/pagination-projects-explore.yml b/changelogs/unreleased/pagination-projects-explore.yml new file mode 100644 index 00000000000..dc9c4218793 --- /dev/null +++ b/changelogs/unreleased/pagination-projects-explore.yml @@ -0,0 +1,4 @@ +--- +title: Use Prev/Next pagination for exploring projects +merge_request: +author: diff --git a/changelogs/unreleased/seven-days-cycle-analytics.yml b/changelogs/unreleased/seven-days-cycle-analytics.yml new file mode 100644 index 00000000000..ff660bdd603 --- /dev/null +++ b/changelogs/unreleased/seven-days-cycle-analytics.yml @@ -0,0 +1,5 @@ +--- +title: Add a `Last 7 days` option for Cycle Analytics view +merge_request: 13443 +author: Mehdi Lahmam (@mehlah) +type: added diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml index 5eb01d62924..0642a0b2fe9 100644 --- a/config/prometheus/additional_metrics.yml +++ b/config/prometheus/additional_metrics.yml @@ -1,3 +1,33 @@ +- group: Response metrics (NGINX Ingress) + priority: 10 + metrics: + - title: "Throughput" + y_label: "Requests / Sec" + required_metrics: + - nginx_upstream_requests_total + weight: 1 + queries: + - query_range: 'sum(rate(nginx_upstream_requests_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))' + label: Total + unit: req / sec + - title: "Latency" + y_label: "Latency (ms)" + required_metrics: + - nginx_upstream_response_msecs_avg + weight: 1 + queries: + - query_range: 'avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"})' + label: Average + unit: ms + - title: "HTTP Error Rate" + y_label: "HTTP 500 Errors / Sec" + required_metrics: + - nginx_upstream_responses_total + weight: 1 + queries: + - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))' + label: HTTP Errors + unit: "errors / sec" - group: Response metrics (HA Proxy) priority: 10 metrics: @@ -68,18 +98,18 @@ - nginx_upstream_response_msecs_avg weight: 1 queries: - - query_range: 'avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) * 1000' + - query_range: 'avg(nginx_upstream_response_msecs_avg{%{environment_filter}})' label: Upstream unit: ms - title: "HTTP Error Rate" - y_label: "Error Rate (%)" + y_label: "HTTP 500 Errors / Sec" required_metrics: - nginx_responses_total weight: 1 queries: - - query_range: 'sum(rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m])) / sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m]))' + - query_range: 'sum(rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m]))' label: HTTP Errors - unit: "%" + unit: "errors / sec" - group: System metrics (Kubernetes) priority: 5 metrics: diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index c304e0706dc..30244ee4431 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -28,6 +28,8 @@ Gitlab::Seeder.quiet do project = Project.find_by_full_path('gitlab-org/gitlab-test') + next if project.empty_repo? # We don't have repository on CI + params = { source_branch: 'feature', target_branch: 'master', diff --git a/db/migrate/20160713205315_add_domain_blacklist_to_application_settings.rb b/db/migrate/20160713205315_add_domain_blacklist_to_application_settings.rb index ecdd1bd7e5e..f64dfa7675f 100644 --- a/db/migrate/20160713205315_add_domain_blacklist_to_application_settings.rb +++ b/db/migrate/20160713205315_add_domain_blacklist_to_application_settings.rb @@ -1,6 +1,7 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. +# rubocop:disable Migration/SaferBooleanColumn class AddDomainBlacklistToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20160817133006_add_koding_to_application_settings.rb b/db/migrate/20160817133006_add_koding_to_application_settings.rb index 915d3d78e40..46120652d8e 100644 --- a/db/migrate/20160817133006_add_koding_to_application_settings.rb +++ b/db/migrate/20160817133006_add_koding_to_application_settings.rb @@ -1,3 +1,4 @@ +# rubocop:disable Migration/SaferBooleanColumn class AddKodingToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20161103191444_add_sidekiq_throttling_to_application_settings.rb b/db/migrate/20161103191444_add_sidekiq_throttling_to_application_settings.rb index e644a174964..522437b92b4 100644 --- a/db/migrate/20161103191444_add_sidekiq_throttling_to_application_settings.rb +++ b/db/migrate/20161103191444_add_sidekiq_throttling_to_application_settings.rb @@ -1,6 +1,7 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. +# rubocop:disable Migration/SaferBooleanColumn class AddSidekiqThrottlingToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb b/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb index 1c59241d0fe..38f5781745b 100644 --- a/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb +++ b/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb @@ -1,6 +1,7 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. +# rubocop:disable Migration/SaferBooleanColumn class AddHtmlEmailsEnabledToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb b/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb index 3677f978cc2..7f56ecf4c9e 100644 --- a/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb +++ b/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb @@ -1,6 +1,7 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. +# rubocop:disable Migration/SaferBooleanColumn class AddPlantUmlEnabledToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb index 5e8b667b86d..d358020d182 100644 --- a/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb +++ b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb @@ -1,3 +1,4 @@ +# rubocop:disable Migration/SaferBooleanColumn class AddHelpPageHideCommercialContentToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 6a7f694d705..28b27921f8b 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -607,10 +607,9 @@ exist, you should see something like: - With GitLab 9.2, all deployments to an environment are shown directly on the monitoring dashboard -If you have enabled Prometheus for collecting metrics, you can monitor the performance behavior of your app -through the environments. +If you have enabled [Prometheus for monitoring system and response metrics](https://docs.gitlab.com/ee/user/project/integrations/prometheus.html), you can monitor the performance behavior of your app running in each environment. -Once configured, GitLab will attempt to retrieve performance metrics for any +Once configured, GitLab will attempt to retrieve [supported performance metrics](https://docs.gitlab.com/ee/user/project/integrations/prometheus_library/metrics.html) for any environment which has had a successful deployment. If monitoring data was successfully retrieved, a Monitoring button will appear on the environment's detail page. diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 6f15765751c..5fefb3b69c4 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -40,7 +40,7 @@ Installing and configuring Prometheus to monitor applications is fairly straight ### Configuring Omnibus GitLab Prometheus to monitor Kubernetes deployments With Omnibus GitLab running inside of Kubernetes, you can leverage the bundled -version of Prometheus to collect the supported metrics. Once enabled, Prometheus will automatically begin monitoring Kubernetes Nodes and any [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>). +version of Prometheus to collect the supported metrics. Once enabled, Prometheus will automatically begin monitoring Kubernetes Nodes and any [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>). 1. Read how to configure the bundled Prometheus server in the [Administration guide][gitlab-prometheus-k8s-monitor]. @@ -133,6 +133,8 @@ to integrate with. Once configured, GitLab will attempt to retrieve performance metrics for any environment which has had a successful deployment. +GitLab will automatically scan the Prometheus server for known metrics and attempt to identify the metrics for a particular environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html). + [Learn more about monitoring environments.](../../../ci/environments.md#monitoring-environments) ## Determining the performance impact of a merge @@ -174,7 +176,7 @@ If the "Attempting to load performance data" screen continues to appear, it coul [prometheus-docker-image]: https://hub.docker.com/r/prom/prometheus/ [prometheus-yml]:samples/prometheus.yml [gitlab.com-ip-range]: https://gitlab.com/gitlab-com/infrastructure/issues/434 -[ci-environment-slug]: https://docs.gitlab.com/ce/ci/variables/#predefined-variables-environment-variables +[ci-environment-slug]: ../../../ci/variables/#predefined-variables-environment-variables [ce-8935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8935 [ce-10408]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10408 [promgldocs]: ../../../administration/monitoring/prometheus/index.md diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md index 6bdffce9c55..f09ecf9ff2d 100644 --- a/doc/user/project/integrations/prometheus_library/metrics.md +++ b/doc/user/project/integrations/prometheus_library/metrics.md @@ -4,6 +4,7 @@ GitLab offers automatic detection of select [Prometheus exporters](https://prometheus.io/docs/instrumenting/exporters/). Currently supported exporters are: * [Kubernetes](kubernetes.md) * [NGINX](nginx.md) +* [NGINX Ingress Controller](nginx_ingress.md) * [HAProxy](haproxy.md) * [Amazon Cloud Watch](cloudwatch.md) @@ -14,10 +15,7 @@ We have tried to surface the most important metrics for each exporter, and will GitLab retrieves performance data from the configured Prometheus server, and attempts to identifying the presence of known metrics. Once identified, GitLab then needs to be able to map the data to a particular environment. In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do that, -GitLab will look for the required metrics which have a label that -matches the [$CI_ENVIRONMENT_SLUG][ci-environment-slug]. - -For example if you are deploying to an environment named `production`, there must be a label for the metric with the value of `production`. +GitLab uses the defined queries and fills in the environment specific variables. Typically this involves looking for the [$CI_ENVIRONMENT_SLUG](https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables), but may also include other information such as the project's Kubernetes namespace. Each search query is defined in the [exporter specific documentation](#prometheus-metrics-library). ## Adding to the library diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md index b3470773996..12e3321f5f3 100644 --- a/doc/user/project/integrations/prometheus_library/nginx.md +++ b/doc/user/project/integrations/prometheus_library/nginx.md @@ -8,8 +8,8 @@ GitLab has support for automatically detecting and monitoring NGINX. This is pro | Name | Query | | ---- | ----- | | Throughput (req/sec) | sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) | -| Latency (ms) | avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) * 1000 | -| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m])) | +| Latency (ms) | avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) | +| HTTP Error Rate (HTTP Errors / sec) | rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m])) | ## Configuring Prometheus to monitor for NGINX metrics diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md new file mode 100644 index 00000000000..84ee8bc45e5 --- /dev/null +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -0,0 +1,25 @@ +# Monitoring NGINX Ingress Controller +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13438) in GitLab 9.5 + +GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) of the ingress. + +## Metrics supported + +| Name | Query | +| ---- | ----- | +| Throughput (req/sec) | sum(rate(nginx_upstream_requests_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) | +| Latency (ms) | avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}) | +| HTTP Error Rate (HTTP Errors / sec) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) | + +## Configuring Prometheus to monitor for NGINX ingress metrics + +The easiest way to get started is to use at least version 0.9.0 of [NGINX ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). If you are using NGINX as your Kubernetes ingress, there is [direct support](https://github.com/kubernetes/ingress/pull/423) for enabling Prometheus monitoring in the 0.9.0 release. + +If you have deployed with the [gitlab-omnibus](https://docs.gitlab.com/ee/install/kubernetes/gitlab_omnibus.md) Helm chart, these metrics will be automatically enabled and annotated for Prometheus monitoring. + +## Specifying the Environment label + +In order to isolate and only display relevant metrics for a given environment +however, GitLab needs a method to detect which labels are associated. To do this, GitLab will search metrics with appropriate labels. In this case, the `upstream` label must be of the form `<Kubernetes Namespace>-<CI_ENVIRONMENT_SLUG>-*`. + +If you have used [Auto Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html) to deploy your app, this format will be used automatically and metrics will be detected with no action on your part. diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 810cd75591b..7254fbc2e4e 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -299,9 +299,6 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps step 'I change the comment "Line is wrong" to "Typo, please fix" on diff' do page.within('.diff-file:nth-of-type(5) .note') do - find('.more-actions').click - find('.more-actions .dropdown-menu li', match: :first) - find('.js-note-edit').click page.within('.current-note-edit-form', visible: true) do diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index 80187b83fee..492da38355c 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -11,8 +11,8 @@ module SharedNote note = find('.note') note.hover - note.find('.more-actions').click - note.find('.more-actions .dropdown-menu li', match: :first) + find('.more-actions').click + find('.more-actions .dropdown-menu li', match: :first) find(".js-note-delete").click end @@ -147,9 +147,6 @@ module SharedNote note = find('.note') note.hover - note.find('.more-actions').click - note.find('.more-actions .dropdown-menu li', match: :first) - note.find('.js-note-edit').click end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 7000b173075..afe4fb58ad0 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -620,29 +620,13 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/327 def ls_files(ref) - actual_ref = ref || root_ref - - begin - sha_from_ref(actual_ref) - rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError - # Return an empty array if the ref wasn't found - return [] - end - - cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-tree) - cmd += %w(-r) - cmd += %w(--full-tree) - cmd += %w(--full-name) - cmd += %W(-- #{actual_ref}) - - raw_output = IO.popen(cmd, &:read).split("\n").map do |f| - stuff, path = f.split("\t") - _mode, type, _sha = stuff.split(" ") - path if type == "blob" - # Contain only blob type + gitaly_migrate(:ls_files) do |is_enabled| + if is_enabled + gitaly_ls_files(ref) + else + git_ls_files(ref) + end end - - raw_output.compact end # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/328 @@ -974,6 +958,36 @@ module Gitlab raw_output.to_i end + + def gitaly_ls_files(ref) + gitaly_commit_client.ls_files(ref) + end + + def git_ls_files(ref) + actual_ref = ref || root_ref + + begin + sha_from_ref(actual_ref) + rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError + # Return an empty array if the ref wasn't found + return [] + end + + cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-tree) + cmd += %w(-r) + cmd += %w(--full-tree) + cmd += %w(--full-name) + cmd += %W(-- #{actual_ref}) + + raw_output = IO.popen(cmd, &:read).split("\n").map do |f| + stuff, path = f.split("\t") + _mode, type, _sha = stuff.split(" ") + path if type == "blob" + # Contain only blob type + end + + raw_output.compact + end end end end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index 692d7e02eef..93268d9f33c 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -10,6 +10,18 @@ module Gitlab @repository = repository end + def ls_files(revision) + request = Gitaly::ListFilesRequest.new( + repository: @gitaly_repo, + revision: GitalyClient.encode(revision) + ) + + response = GitalyClient.call(@repository.storage, :commit_service, :list_files, request) + response.flat_map do |msg| + msg.paths.map { |d| d.dup.force_encoding(Encoding::UTF_8) } + end + end + def is_ancestor(ancestor_id, child_id) request = Gitaly::CommitIsAncestorRequest.new( repository: @gitaly_repo, diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb index e1d1724295a..45e9f9d65ae 100644 --- a/lib/gitlab/gpg.rb +++ b/lib/gitlab/gpg.rb @@ -2,6 +2,8 @@ module Gitlab module Gpg extend self + MUTEX = Mutex.new + module CurrentKeyChain extend self @@ -42,21 +44,37 @@ module Gitlab end end - def using_tmp_keychain - Dir.mktmpdir do |dir| - @original_dirs ||= [GPGME::Engine.dirinfo('homedir')] - @original_dirs.push(dir) - - GPGME::Engine.home_dir = dir - - return_value = yield + # Allows thread safe switching of temporary keychain files + # + # 1. The current thread may use nesting of temporary keychain + # 2. Another thread needs to wait for the lock to be released + def using_tmp_keychain(&block) + if MUTEX.locked? && MUTEX.owned? + optimistic_using_tmp_keychain(&block) + else + MUTEX.synchronize do + optimistic_using_tmp_keychain(&block) + end + end + end - @original_dirs.pop + # 1. Returns the custom home directory if one has been set by calling + # `GPGME::Engine.home_dir=` + # 2. Returns the default home directory otherwise + def current_home_dir + GPGME::Engine.info.first.home_dir || GPGME::Engine.dirinfo('homedir') + end - GPGME::Engine.home_dir = @original_dirs[-1] + private - return_value + def optimistic_using_tmp_keychain + previous_dir = current_home_dir + Dir.mktmpdir do |dir| + GPGME::Engine.home_dir = dir + yield end + ensure + GPGME::Engine.home_dir = previous_dir end end end diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index 1f504485e4c..e337c67a0f5 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -15,13 +15,17 @@ namespace :gitlab do checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir) _, status = Gitlab::Popen.popen(%w[which gmake]) - command = status.zero? ? 'gmake' : 'make' + command = status.zero? ? ['gmake'] : ['make'] + + if Rails.env.test? + command += %W[BUNDLE_PATH=#{Bundler.bundle_path}] + end Dir.chdir(args.dir) do create_gitaly_configuration # In CI we run scripts/gitaly-test-build instead of this command unless ENV['CI'].present? - Bundler.with_original_env { run_command!(%w[/usr/bin/env -u RUBYOPT -u BUNDLE_GEMFILE] + [command]) } + Bundler.with_original_env { run_command!(%w[/usr/bin/env -u RUBYOPT -u BUNDLE_GEMFILE] + command) } end end end diff --git a/rubocop/cop/migration/safer_boolean_column.rb b/rubocop/cop/migration/safer_boolean_column.rb new file mode 100644 index 00000000000..0335c25d85d --- /dev/null +++ b/rubocop/cop/migration/safer_boolean_column.rb @@ -0,0 +1,94 @@ +require_relative '../../migration_helpers' + +module RuboCop + module Cop + module Migration + # This cop requires a default value and disallows nulls for boolean + # columns on small tables. + # + # In general, this prevents 3-state-booleans. + # https://robots.thoughtbot.com/avoid-the-threestate-boolean-problem + # + # In particular, for the `application_settings` table, this ensures that + # upgraded installations get a proper default for the new boolean setting. + # A developer might otherwise mistakenly assume that a value in + # `ApplicationSetting.defaults` is sufficient. + # + # See https://gitlab.com/gitlab-org/gitlab-ee/issues/2750 for more + # information. + class SaferBooleanColumn < RuboCop::Cop::Cop + include MigrationHelpers + + DEFAULT_OFFENSE = 'Boolean columns on the `%s` table should have a default. You may wish to use `add_column_with_default`.'.freeze + NULL_OFFENSE = 'Boolean columns on the `%s` table should disallow nulls.'.freeze + DEFAULT_AND_NULL_OFFENSE = 'Boolean columns on the `%s` table should have a default and should disallow nulls. You may wish to use `add_column_with_default`.'.freeze + + SMALL_TABLES = %i[ + application_settings + ].freeze + + def_node_matcher :add_column?, <<~PATTERN + (send nil :add_column $...) + PATTERN + + def on_send(node) + return unless in_migration?(node) + + matched = add_column?(node) + + return unless matched + + table, _, type = matched.to_a.take(3).map(&:children).map(&:first) + opts = matched[3] + + return unless SMALL_TABLES.include?(table) && type == :boolean + + no_default = no_default?(opts) + nulls_allowed = nulls_allowed?(opts) + + offense = if no_default && nulls_allowed + DEFAULT_AND_NULL_OFFENSE + elsif no_default + DEFAULT_OFFENSE + elsif nulls_allowed + NULL_OFFENSE + end + + add_offense(node, :expression, format(offense, table)) if offense + end + + def no_default?(opts) + return true unless opts + + each_hash_node_pair(opts) do |key, value| + return value == 'nil' if key == :default + end + end + + def nulls_allowed?(opts) + return true unless opts + + each_hash_node_pair(opts) do |key, value| + return value != 'false' if key == :null + end + end + + def each_hash_node_pair(hash_node, &block) + hash_node.each_node(:pair) do |pair| + key = hash_pair_key(pair) + value = hash_pair_value(pair) + yield(key, value) + end + end + + def hash_pair_key(pair) + pair.children[0].children[0] + end + + def hash_pair_value(pair) + pair.children[1].source + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 3fbd5b0163c..1b6e8991a17 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -13,6 +13,7 @@ require_relative 'cop/migration/add_concurrent_index' require_relative 'cop/migration/add_index' require_relative 'cop/migration/add_timestamps' require_relative 'cop/migration/datetime' +require_relative 'cop/migration/safer_boolean_column' require_relative 'cop/migration/hash_index' require_relative 'cop/migration/remove_concurrent_index' require_relative 'cop/migration/remove_index' diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 23601c457b0..b571b11dcac 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1,7 +1,7 @@ require('spec_helper') describe Projects::IssuesController do - let(:project) { create(:project_empty_repo) } + let(:project) { create(:project) } let(:user) { create(:user) } let(:issue) { create(:issue, project: project) } @@ -841,7 +841,7 @@ describe Projects::IssuesController do describe 'POST #toggle_award_emoji' do before do sign_in(user) - project.team << [user, :developer] + project.add_developer(user) end it "toggles the award emoji" do @@ -855,6 +855,8 @@ describe Projects::IssuesController do end describe 'POST create_merge_request' do + let(:project) { create(:project, :repository) } + before do project.add_developer(user) sign_in(user) diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 29ad1af9fd9..e5abfd67d60 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -10,6 +10,10 @@ FactoryGirl.define do after(:build) do |deployment, evaluator| deployment.project ||= deployment.environment.project + + unless deployment.project.repository_exists? + allow(deployment.project.repository).to receive(:fetch_ref) + end end end end diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 1bc530d06db..cbec716d6ea 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -68,6 +68,17 @@ FactoryGirl.define do merge_user author end + after(:build) do |merge_request| + target_project = merge_request.target_project + source_project = merge_request.source_project + + # Fake `write_ref` if we don't have repository + # We have too many existing tests replying on this behaviour + unless [target_project, source_project].all?(&:repository_exists?) + allow(merge_request).to receive(:write_ref) + end + end + factory :merged_merge_request, traits: [:merged] factory :closed_merge_request, traits: [:closed] factory :reopened_merge_request, traits: [:opened] diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index 5c60cca10b9..bfe9dac3bd4 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -24,6 +24,12 @@ feature 'Cycle Analytics', js: true do expect(page).to have_content('Introducing Cycle Analytics') end + it 'shows pipeline summary' do + expect(new_issues_counter).to have_content('-') + expect(commits_counter).to have_content('-') + expect(deploys_counter).to have_content('-') + end + it 'shows active stage with empty message' do expect(page).to have_selector('.stage-nav-item.active', text: 'Issue') expect(page).to have_content("We don't have enough data to show this stage.") @@ -42,6 +48,12 @@ feature 'Cycle Analytics', js: true do visit project_cycle_analytics_path(project) end + it 'shows pipeline summary' do + expect(new_issues_counter).to have_content('1') + expect(commits_counter).to have_content('2') + expect(deploys_counter).to have_content('1') + end + it 'shows data on each stage' do expect_issue_to_be_present @@ -63,6 +75,20 @@ feature 'Cycle Analytics', js: true do click_stage('Production') expect_issue_to_be_present end + + context "when I change the time period observed" do + before do + _two_weeks_old_issue = create(:issue, project: project, created_at: 2.weeks.ago) + + click_button('Last 30 days') + click_link('Last 7 days') + wait_for_requests + end + + it 'shows only relevant data' do + expect(new_issues_counter).to have_content('1') + end + end end context "when my preferred language is Spanish" do @@ -109,6 +135,18 @@ feature 'Cycle Analytics', js: true do end end + def new_issues_counter + find(:xpath, "//p[contains(text(),'New Issue')]/preceding-sibling::h3") + end + + def commits_counter + find(:xpath, "//p[contains(text(),'Commits')]/preceding-sibling::h3") + end + + def deploys_counter + find(:xpath, "//p[contains(text(),'Deploy')]/preceding-sibling::h3") + end + def expect_issue_to_be_present expect(find('.stage-events')).to have_content(issue.title) expect(find('.stage-events')).to have_content(issue.author.name) diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index 9f08ecc214b..62dbc3efb01 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -133,8 +133,6 @@ feature 'Issue notes polling', :js do def click_edit_action(note) note_element = find("#note_#{note.id}") - open_more_actions_dropdown(note) - note_element.find('.js-note-edit').click end end diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb index 2d9419d6124..c4f02311f13 100644 --- a/spec/features/merge_requests/diff_notes_avatars_spec.rb +++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb @@ -157,7 +157,7 @@ feature 'Diff note avatars', js: true do end page.within find("[id='#{position.line_code(project.repository)}']") do - find('.diff-notes-collapse').click + find('.diff-notes-collapse').trigger('click') expect(page).to have_selector('img.js-diff-comment-avatar', count: 3) expect(find('.diff-comments-more-count')).to have_content '+1' diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb index 1d52a4659ad..9912e8165e6 100644 --- a/spec/features/merge_requests/filter_by_labels_spec.rb +++ b/spec/features/merge_requests/filter_by_labels_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Merge Request filtering by Labels', js: true do +feature 'Merge Request filtering by Labels', :js do include FilteredSearchHelpers include MergeRequestHelpers @@ -12,9 +12,9 @@ feature 'Merge Request filtering by Labels', js: true do let!(:feature) { create(:label, project: project, title: 'feature') } let!(:enhancement) { create(:label, project: project, title: 'enhancement') } - let!(:mr1) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "bugfix1") } - let!(:mr2) { create(:merge_request, title: "Bugfix2", source_project: project, target_project: project, source_branch: "bugfix2") } - let!(:mr3) { create(:merge_request, title: "Feature1", source_project: project, target_project: project, source_branch: "feature1") } + let!(:mr1) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "fix") } + let!(:mr2) { create(:merge_request, title: "Bugfix2", source_project: project, target_project: project, source_branch: "wip") } + let!(:mr3) { create(:merge_request, title: "Feature1", source_project: project, target_project: project, source_branch: "improve/awesome") } before do mr1.labels << bug @@ -25,7 +25,7 @@ feature 'Merge Request filtering by Labels', js: true do mr3.title = "Feature1" mr3.labels << feature - project.team << [user, :master] + project.add_master(user) sign_in(user) visit project_merge_requests_path(project) diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb index f0019be86ad..3686131fee4 100644 --- a/spec/features/merge_requests/filter_merge_requests_spec.rb +++ b/spec/features/merge_requests/filter_merge_requests_spec.rb @@ -12,7 +12,7 @@ describe 'Filter merge requests' do let!(:wontfix) { create(:label, project: project, title: "Won't fix") } before do - project.team << [user, :master] + project.add_master(user) group.add_developer(user) sign_in(user) create(:merge_request, source_project: project, target_project: project) @@ -170,7 +170,7 @@ describe 'Filter merge requests' do describe 'filter merge requests by text' do before do - create(:merge_request, title: "Bug", source_project: project, target_project: project, source_branch: "bug") + create(:merge_request, title: "Bug", source_project: project, target_project: project, source_branch: "wip") bug_label = create(:label, project: project, title: 'bug') milestone = create(:milestone, title: "8", project: project) @@ -179,7 +179,7 @@ describe 'Filter merge requests' do title: "Bug 2", source_project: project, target_project: project, - source_branch: "bug2", + source_branch: "fix", milestone: milestone, author: user, assignee: user) @@ -259,12 +259,12 @@ describe 'Filter merge requests' do end end - describe 'filter merge requests and sort', js: true do + describe 'filter merge requests and sort', :js do before do bug_label = create(:label, project: project, title: 'bug') - mr1 = create(:merge_request, title: "Frontend", source_project: project, target_project: project, source_branch: "Frontend") - mr2 = create(:merge_request, title: "Bug 2", source_project: project, target_project: project, source_branch: "bug2") + mr1 = create(:merge_request, title: "Frontend", source_project: project, target_project: project, source_branch: "wip") + mr2 = create(:merge_request, title: "Bug 2", source_project: project, target_project: project, source_branch: "fix") mr1.labels << bug_label mr2.labels << bug_label diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb index c1b90e5f875..eed95816bdf 100644 --- a/spec/features/merge_requests/reset_filters_spec.rb +++ b/spec/features/merge_requests/reset_filters_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Merge requests filter clear button', js: true do +feature 'Merge requests filter clear button', :js do include FilteredSearchHelpers include MergeRequestHelpers include IssueHelpers @@ -9,8 +9,8 @@ feature 'Merge requests filter clear button', js: true do let!(:user) { create(:user) } let!(:milestone) { create(:milestone, project: project) } let!(:bug) { create(:label, project: project, name: 'bug')} - let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "Feature", milestone: milestone, author: user, assignee: user) } - let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "Bugfix1") } + let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "improve/awesome", milestone: milestone, author: user, assignee: user) } + let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "fix") } let(:merge_request_css) { '.merge-request' } let(:clear_search_css) { '.filtered-search-box .clear-search' } diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb index d62b035b40b..20008b4e7f9 100644 --- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb +++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb @@ -24,12 +24,10 @@ describe 'Projects > Merge requests > User lists merge requests' do milestone: create(:milestone, due_date: '2013-12-12'), created_at: 2.minutes.ago, updated_at: 2.minutes.ago) - # lfs in itself is not a great choice for the title if one wants to match the whole body content later on - # just think about the scenario when faker generates 'Chester Runolfsson' as the user's name create(:merge_request, - title: 'merge_lfs', + title: 'merge-test', source_project: project, - source_branch: 'merge_lfs', + source_branch: 'merge-test', created_at: 3.minutes.ago, updated_at: 10.seconds.ago) end @@ -38,7 +36,7 @@ describe 'Projects > Merge requests > User lists merge requests' do visit_merge_requests(project, assignee_id: IssuableFinder::NONE) expect(current_path).to eq(project_merge_requests_path(project)) - expect(page).to have_content 'merge_lfs' + expect(page).to have_content 'merge-test' expect(page).not_to have_content 'fix' expect(page).not_to have_content 'markdown' expect(count_merge_requests).to eq(1) @@ -47,7 +45,7 @@ describe 'Projects > Merge requests > User lists merge requests' do it 'filters on a specific assignee' do visit_merge_requests(project, assignee_id: user.id) - expect(page).not_to have_content 'merge_lfs' + expect(page).not_to have_content 'merge-test' expect(page).to have_content 'fix' expect(page).to have_content 'markdown' expect(count_merge_requests).to eq(2) @@ -57,14 +55,14 @@ describe 'Projects > Merge requests > User lists merge requests' do visit_merge_requests(project, sort: sort_value_recently_created) expect(first_merge_request).to include('fix') - expect(last_merge_request).to include('merge_lfs') + expect(last_merge_request).to include('merge-test') expect(count_merge_requests).to eq(3) end it 'sorts by oldest' do visit_merge_requests(project, sort: sort_value_oldest_created) - expect(first_merge_request).to include('merge_lfs') + expect(first_merge_request).to include('merge-test') expect(last_merge_request).to include('fix') expect(count_merge_requests).to eq(3) end @@ -72,7 +70,7 @@ describe 'Projects > Merge requests > User lists merge requests' do it 'sorts by last updated' do visit_merge_requests(project, sort: sort_value_recently_updated) - expect(first_merge_request).to include('merge_lfs') + expect(first_merge_request).to include('merge-test') expect(count_merge_requests).to eq(3) end diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb index 74d21822a59..d7cda73ab40 100644 --- a/spec/features/merge_requests/user_posts_notes_spec.rb +++ b/spec/features/merge_requests/user_posts_notes_spec.rb @@ -75,7 +75,6 @@ describe 'Merge requests > User posts notes', :js do describe 'editing the note' do before do find('.note').hover - open_more_actions_dropdown(note) find('.js-note-edit').click end @@ -104,7 +103,6 @@ describe 'Merge requests > User posts notes', :js do wait_for_requests find('.note').hover - open_more_actions_dropdown(note) find('.js-note-edit').click @@ -132,7 +130,6 @@ describe 'Merge requests > User posts notes', :js do describe 'deleting an attachment' do before do find('.note').hover - open_more_actions_dropdown(note) find('.js-note-edit').click end diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index f1d0905738b..c0c293dee78 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -91,11 +91,7 @@ describe 'Comments on personal snippets', :js do context 'when editing a note' do it 'changes the text' do - open_more_actions_dropdown(snippet_notes[0]) - - page.within("#notes-list li#note_#{snippet_notes[0].id}") do - click_on 'Edit comment' - end + find('.js-note-edit').click page.within('.current-note-edit-form') do fill_in 'note[note]', with: 'new content' diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index c14826df55a..580258f77eb 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -52,8 +52,8 @@ feature 'Task Lists' do before do Warden.test_mode! - project.team << [user, :master] - project.team << [user2, :guest] + project.add_master(user) + project.add_guest(user2) login_as(user) end diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments_finder_spec.rb index 0c063f6d5ee..3a8a1e7de74 100644 --- a/spec/finders/environments_finder_spec.rb +++ b/spec/finders/environments_finder_spec.rb @@ -12,7 +12,7 @@ describe EnvironmentsFinder do context 'tagged deployment' do before do - create(:deployment, environment: environment, ref: '1.0', tag: true, sha: project.commit.id) + create(:deployment, environment: environment, ref: 'v1.1.0', tag: true, sha: project.commit.id) end it 'returns environment when with_tags is set' do diff --git a/spec/helpers/pagination_helper_spec.rb b/spec/helpers/pagination_helper_spec.rb new file mode 100644 index 00000000000..e235475fb47 --- /dev/null +++ b/spec/helpers/pagination_helper_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe PaginationHelper do + describe '#paginate_collection' do + let(:collection) { User.all.page(1) } + + it 'paginates a collection without using a COUNT' do + without_count = collection.without_count + + expect(helper).to receive(:paginate_without_count) + .with(without_count) + .and_call_original + + helper.paginate_collection(without_count) + end + + it 'paginates a collection using a COUNT' do + expect(helper).to receive(:paginate_with_count).and_call_original + + helper.paginate_collection(collection) + end + end +end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 04c86a2c1a7..8d24d6f55b8 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1002,7 +1002,7 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(master_file_paths).to include("files/html/500.html") end - it "dose not read submodule directory and empty directory of master branch" do + it "does not read submodule directory and empty directory of master branch" do expect(master_file_paths).not_to include("six") end diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb index 8041518117d..30ad033b204 100644 --- a/spec/lib/gitlab/gpg_spec.rb +++ b/spec/lib/gitlab/gpg_spec.rb @@ -43,6 +43,58 @@ describe Gitlab::Gpg do ).to eq [] end end + + describe '.current_home_dir' do + let(:default_home_dir) { GPGME::Engine.dirinfo('homedir') } + + it 'returns the default value when no explicit home dir has been set' do + expect(described_class.current_home_dir).to eq default_home_dir + end + + it 'returns the explicitely set home dir' do + GPGME::Engine.home_dir = '/tmp/gpg' + + expect(described_class.current_home_dir).to eq '/tmp/gpg' + + GPGME::Engine.home_dir = GPGME::Engine.dirinfo('homedir') + end + + it 'returns the default value when explicitely setting the home dir to nil' do + GPGME::Engine.home_dir = nil + + expect(described_class.current_home_dir).to eq default_home_dir + end + end + + describe '.using_tmp_keychain' do + it "the second thread does not change the first thread's directory" do + thread1 = Thread.new do + described_class.using_tmp_keychain do + dir = described_class.current_home_dir + sleep 0.1 + expect(described_class.current_home_dir).to eq dir + end + end + + thread2 = Thread.new do + described_class.using_tmp_keychain do + sleep 0.2 + end + end + + thread1.join + thread2.join + end + + it 'allows recursive execution in the same thread' do + expect do + described_class.using_tmp_keychain do + described_class.using_tmp_keychain do + end + end + end.not_to raise_error(ThreadError) + end + end end describe Gitlab::Gpg::CurrentKeyChain do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 62f40c12c0a..4926d5d6c49 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -961,6 +961,27 @@ describe Repository, models: true do end end + context 'when temporary ref failed to be created from other project' do + let(:target_project) { create(:project, :empty_repo) } + + before do + expect(target_project.repository).to receive(:run_git) + end + + it 'raises Rugged::ReferenceError' do + raise_reference_error = raise_error(Rugged::ReferenceError) do |err| + expect(err.cause).to be_nil + end + + expect do + GitOperationService.new(user, target_project.repository) + .with_branch('feature', + start_project: project, + &:itself) + end.to raise_reference_error + end + end + context 'when the update adds more than one commit' do let(:old_rev) { '33f3729a45c02fc67d00adb1b8bca394b0e761d9' } diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 9a6072e7eb7..0db645863fb 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -31,7 +31,7 @@ describe API::MergeRequests do it 'returns authentication error' do get api('/merge_requests') - expect(response).to have_http_status(401) + expect(response).to have_gitlab_http_status(401) end end @@ -43,7 +43,7 @@ describe API::MergeRequests do it 'returns an array of all merge requests' do get api('/merge_requests', user), scope: :all - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.map { |mr| mr['id'] }) @@ -56,7 +56,7 @@ describe API::MergeRequests do get api('/merge_requests', user), scope: :all - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.map { |mr| mr['id'] }) @@ -68,7 +68,7 @@ describe API::MergeRequests do get api('/merge_requests', user2) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request3.id) @@ -79,7 +79,7 @@ describe API::MergeRequests do get api('/merge_requests', user), author_id: user2.id, scope: :all - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request3.id) @@ -90,7 +90,7 @@ describe API::MergeRequests do get api('/merge_requests', user), assignee_id: user2.id, scope: :all - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request3.id) @@ -101,7 +101,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), scope: 'assigned-to-me' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request3.id) @@ -112,7 +112,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), scope: 'created-by-me' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request3.id) @@ -125,7 +125,7 @@ describe API::MergeRequests do it "returns authentication error" do get api("/projects/#{project.id}/merge_requests") - expect(response).to have_http_status(401) + expect(response).to have_gitlab_http_status(401) end end @@ -145,7 +145,7 @@ describe API::MergeRequests do it "returns an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -166,7 +166,7 @@ describe API::MergeRequests do it "returns an array of all merge_requests using simple mode" do get api("/projects/#{project.id}/merge_requests?view=simple", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at)) expect(json_response).to be_an Array @@ -182,7 +182,7 @@ describe API::MergeRequests do it "returns an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -192,7 +192,7 @@ describe API::MergeRequests do it "returns an array of open merge_requests" do get api("/projects/#{project.id}/merge_requests?state=opened", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -202,7 +202,7 @@ describe API::MergeRequests do it "returns an array of closed merge_requests" do get api("/projects/#{project.id}/merge_requests?state=closed", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -212,7 +212,7 @@ describe API::MergeRequests do it "returns an array of merged merge_requests" do get api("/projects/#{project.id}/merge_requests?state=merged", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -222,7 +222,7 @@ describe API::MergeRequests do it 'returns merge_request by "iids" array' do get api("/projects/#{project.id}/merge_requests", user), iids: [merge_request.iid, merge_request_closed.iid] - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['title']).to eq merge_request_closed.title @@ -232,14 +232,14 @@ describe API::MergeRequests do it 'matches V4 response schema' do get api("/projects/#{project.id}/merge_requests", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('public_api/v4/merge_requests') end it 'returns an empty array if no issue matches milestone' do get api("/projects/#{project.id}/merge_requests", user), milestone: '1.0.0' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -247,7 +247,7 @@ describe API::MergeRequests do it 'returns an empty array if milestone does not exist' do get api("/projects/#{project.id}/merge_requests", user), milestone: 'foo' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -262,7 +262,7 @@ describe API::MergeRequests do it 'returns an array of merge requests matching state in milestone' do get api("/projects/#{project.id}/merge_requests", user), milestone: '0.9', state: 'closed' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(merge_request_closed.id) @@ -271,7 +271,7 @@ describe API::MergeRequests do it 'returns an array of labeled merge requests' do get api("/projects/#{project.id}/merge_requests?labels=#{label.title}", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label2.title, label.title]) @@ -280,7 +280,7 @@ describe API::MergeRequests do it 'returns an array of labeled merge requests where all labels match' do get api("/projects/#{project.id}/merge_requests?labels=#{label.title},foo,bar", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -288,7 +288,7 @@ describe API::MergeRequests do it 'returns an empty array if no merge request matches labels' do get api("/projects/#{project.id}/merge_requests?labels=foo,bar", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -307,7 +307,7 @@ describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(mr2.id) @@ -322,7 +322,7 @@ describe API::MergeRequests do it "returns an array of merge_requests in ascending order" do get api("/projects/#{project.id}/merge_requests?sort=asc", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -333,7 +333,7 @@ describe API::MergeRequests do it "returns an array of merge_requests in descending order" do get api("/projects/#{project.id}/merge_requests?sort=desc", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -344,7 +344,7 @@ describe API::MergeRequests do it "returns an array of merge_requests ordered by updated_at" do get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -355,7 +355,7 @@ describe API::MergeRequests do it "returns 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).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(3) @@ -370,7 +370,7 @@ describe API::MergeRequests do it 'exposes known attributes' do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_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) @@ -398,7 +398,7 @@ describe API::MergeRequests do it "returns merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_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) @@ -409,13 +409,13 @@ describe API::MergeRequests do it "returns a 404 error if merge_request_iid not found" do get api("/projects/#{project.id}/merge_requests/999", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it "returns a 404 error if merge_request `id` is used instead of iid" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end context 'Work in Progress' do @@ -423,7 +423,7 @@ describe API::MergeRequests do it "returns merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.iid}", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['work_in_progress']).to eq(true) end end @@ -434,7 +434,7 @@ describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user) commit = merge_request.commits.first - expect(response.status).to eq 200 + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.size).to eq(merge_request.commits.size) @@ -444,13 +444,13 @@ describe API::MergeRequests do it 'returns a 404 when merge_request_iid not found' do get api("/projects/#{project.id}/merge_requests/999/commits", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns a 404 when merge_request id is used instead of iid' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end @@ -458,19 +458,19 @@ describe API::MergeRequests do it 'returns the change information of the merge_request' do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/changes", user) - expect(response.status).to eq 200 + expect(response).to have_gitlab_http_status(200) expect(json_response['changes'].size).to eq(merge_request.diffs.size) end it 'returns a 404 when merge_request_iid not found' do get api("/projects/#{project.id}/merge_requests/999/changes", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns a 404 when merge_request id is used instead of iid' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/changes", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end @@ -485,7 +485,7 @@ describe API::MergeRequests do labels: 'label, label2', milestone_id: milestone.id - expect(response).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['milestone']['id']).to eq(milestone.id) @@ -495,25 +495,25 @@ describe API::MergeRequests do it "returns 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).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end it "returns 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).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 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).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), target_branch: 'master', source_branch: 'markdown' - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it 'allows special label names' do @@ -523,7 +523,7 @@ describe API::MergeRequests do target_branch: 'master', author: user, labels: 'label, label?, label&foo, ?, &' - expect(response.status).to eq(201) + expect(response).to have_gitlab_http_status(201) expect(json_response['labels']).to include 'label' expect(json_response['labels']).to include 'label?' expect(json_response['labels']).to include 'label&foo' @@ -549,7 +549,7 @@ describe API::MergeRequests do target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) - expect(response).to have_http_status(409) + expect(response).to have_gitlab_http_status(409) end end @@ -580,15 +580,17 @@ describe API::MergeRequests do let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) } let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } - before do |each| - fork_project.team << [user2, :reporter] + before do + fork_project.add_reporter(user2) + + allow_any_instance_of(MergeRequest).to receive(:write_ref) end it "returns merge_request" 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).to have_http_status(201) + expect(response).to have_gitlab_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 @@ -599,7 +601,7 @@ describe API::MergeRequests 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).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) expect(json_response['title']).to eq('Test merge_request') end @@ -613,25 +615,25 @@ describe API::MergeRequests do author: user2, target_project_id: project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end it "returns 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).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 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).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 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).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end context 'when target_branch is specified' do @@ -642,7 +644,7 @@ describe API::MergeRequests do source_branch: 'markdown', author: user, target_project_id: fork_project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end it 'returns 422 if targeting a different fork' do @@ -652,14 +654,14 @@ describe API::MergeRequests do source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end end it "returns 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).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) end end end @@ -674,7 +676,7 @@ describe API::MergeRequests do it "denies the deletion of the merge request" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", developer) - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end end @@ -682,19 +684,19 @@ describe API::MergeRequests do it "destroys the merge request owners can destroy" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) - expect(response).to have_http_status(204) + expect(response).to have_gitlab_http_status(204) end it "returns 404 for an invalid merge request IID" do delete api("/projects/#{project.id}/merge_requests/12345", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it "returns 404 if the merge request id is used instead of iid" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end end @@ -705,7 +707,7 @@ describe API::MergeRequests do it "returns merge_request in case of success" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) end it "returns 406 if branch can't be merged" do @@ -714,21 +716,21 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) - expect(response).to have_http_status(406) + expect(response).to have_gitlab_http_status(406) expect(json_response['message']).to eq('Branch cannot be merged') end it "returns 405 if merge_request is not open" do merge_request.close put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) - expect(response).to have_http_status(405) + expect(response).to have_gitlab_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end it "returns 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.iid}/merge", user) - expect(response).to have_http_status(405) + expect(response).to have_gitlab_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -737,7 +739,7 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) - expect(response).to have_http_status(405) + expect(response).to have_gitlab_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -745,21 +747,21 @@ describe API::MergeRequests do user2 = create(:user) project.team << [user2, :reporter] put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user2) - expect(response).to have_http_status(401) + expect(response).to have_gitlab_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.iid}/merge", user), sha: merge_request.diff_head_sha.reverse - expect(response).to have_http_status(409) + expect(response).to have_gitlab_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.iid}/merge", user), sha: merge_request.diff_head_sha - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) end it "enables merge when pipeline succeeds if the pipeline is active" do @@ -768,7 +770,7 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), merge_when_pipeline_succeeds: true - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['title']).to eq('Test') expect(json_response['merge_when_pipeline_succeeds']).to eq(true) end @@ -780,7 +782,7 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), merge_when_pipeline_succeeds: true - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['title']).to eq('Test') expect(json_response['merge_when_pipeline_succeeds']).to eq(true) end @@ -788,13 +790,13 @@ describe API::MergeRequests do it "returns 404 for an invalid merge request IID" do put api("/projects/#{project.id}/merge_requests/12345/merge", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it "returns 404 if the merge request id is used instead of iid" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end @@ -803,39 +805,39 @@ describe API::MergeRequests do it "returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: "close" - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['state']).to eq('closed') end end it "updates title and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), title: "New title" - expect(response).to have_http_status(200) + expect(response).to have_gitlab_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.iid}", user), description: "New description" - expect(response).to have_http_status(200) + expect(response).to have_gitlab_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.iid}", user), milestone_id: milestone.id - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['milestone']['id']).to eq(milestone.id) end it "returns merge_request with renamed target_branch" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), target_branch: "wiki" - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['target_branch']).to eq('wiki') end it "returns merge_request that removes the source branch" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), remove_source_branch: true - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response['force_remove_source_branch']).to be_truthy end @@ -856,7 +858,7 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: 'close', title: nil merge_request.reload - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) expect(merge_request.state).to eq('opened') end @@ -864,20 +866,20 @@ describe API::MergeRequests do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), state_event: 'close', target_branch: nil merge_request.reload - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) expect(merge_request.state).to eq('opened') end it "returns 404 for an invalid merge request IID" do put api("/projects/#{project.id}/merge_requests/12345", user), state_event: "close" - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it "returns 404 if the merge request id is used instead of iid" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close" - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end @@ -890,7 +892,7 @@ describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests/#{mr.iid}/closes_issues", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -900,7 +902,7 @@ describe API::MergeRequests do it 'returns an empty array when there are no issues to be closed' do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/closes_issues", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(0) @@ -916,7 +918,7 @@ describe API::MergeRequests do get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.iid}/closes_issues", user) - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(2) @@ -936,19 +938,19 @@ describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/closes_issues", guest) - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end it "returns 404 for an invalid merge request IID" do get api("/projects/#{project.id}/merge_requests/12345/closes_issues", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it "returns 404 if the merge request id is used instead of iid" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end end @@ -956,26 +958,26 @@ describe API::MergeRequests do it 'subscribes to a merge request' do post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", admin) - expect(response).to have_http_status(201) + expect(response).to have_gitlab_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.iid}/subscribe", user) - expect(response).to have_http_status(304) + expect(response).to have_gitlab_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/subscribe", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns 404 if the merge request id is used instead of iid' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns 403 if user has no access to read code' do @@ -984,7 +986,7 @@ describe API::MergeRequests do post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", guest) - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end end @@ -992,26 +994,26 @@ describe API::MergeRequests do it 'unsubscribes from a merge request' do post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", user) - expect(response).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", admin) - expect(response).to have_http_status(304) + expect(response).to have_gitlab_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/unsubscribe", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns 404 if the merge request id is used instead of iid' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user) - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) end it 'returns 403 if user has no access to read code' do @@ -1020,7 +1022,7 @@ describe API::MergeRequests do post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", guest) - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end end diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb index ec684e7b9cd..86f38dd4ec1 100644 --- a/spec/requests/api/v3/merge_requests_spec.rb +++ b/spec/requests/api/v3/merge_requests_spec.rb @@ -315,15 +315,17 @@ describe API::MergeRequests do let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) } let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } - before do |each| - fork_project.team << [user2, :reporter] + before do + fork_project.add_reporter(user2) + + allow_any_instance_of(MergeRequest).to receive(:write_ref) end it "returns merge_request" do post v3_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).to have_http_status(201) + expect(response).to have_gitlab_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 @@ -334,7 +336,7 @@ describe API::MergeRequests do expect(fork_project.forked_from_project).to eq(project) post v3_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).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) expect(json_response['title']).to eq('Test merge_request') end @@ -348,25 +350,25 @@ describe API::MergeRequests do author: user2, target_project_id: project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end it "returns 400 when source_branch is missing" do post v3_api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 400 when target_branch is missing" do post v3_api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end it "returns 400 when title is missing" do post v3_api("/projects/#{fork_project.id}/merge_requests", user2), target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end context 'when target_branch is specified' do @@ -377,7 +379,7 @@ describe API::MergeRequests do source_branch: 'markdown', author: user, target_project_id: fork_project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end it 'returns 422 if targeting a different fork' do @@ -387,14 +389,14 @@ describe API::MergeRequests do source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id - expect(response).to have_http_status(422) + expect(response).to have_gitlab_http_status(422) end end it "returns 201 when target_branch is specified and for the same project" do post v3_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).to have_http_status(201) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb new file mode 100644 index 00000000000..1006594499a --- /dev/null +++ b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/migration/safer_boolean_column' + +describe RuboCop::Cop::Migration::SaferBooleanColumn do + include CopHelper + + subject(:cop) { described_class.new } + + context 'in migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + end + + described_class::SMALL_TABLES.each do |table| + context "for the #{table} table" do + sources_and_offense = [ + ["add_column :#{table}, :column, :boolean, default: true", 'should disallow nulls'], + ["add_column :#{table}, :column, :boolean, default: false", 'should disallow nulls'], + ["add_column :#{table}, :column, :boolean, default: nil", 'should have a default and should disallow nulls'], + ["add_column :#{table}, :column, :boolean, null: false", 'should have a default'], + ["add_column :#{table}, :column, :boolean, null: true", 'should have a default and should disallow nulls'], + ["add_column :#{table}, :column, :boolean", 'should have a default and should disallow nulls'], + ["add_column :#{table}, :column, :boolean, default: nil, null: false", 'should have a default'], + ["add_column :#{table}, :column, :boolean, default: nil, null: true", 'should have a default and should disallow nulls'], + ["add_column :#{table}, :column, :boolean, default: false, null: true", 'should disallow nulls'] + ] + + sources_and_offense.each do |source, offense| + context "given the source \"#{source}\"" do + it "registers the offense matching \"#{offense}\"" do + inspect_source(cop, source) + + aggregate_failures do + expect(cop.offenses.first.message).to match(offense) + end + end + end + end + + inoffensive_sources = [ + "add_column :#{table}, :column, :boolean, default: true, null: false", + "add_column :#{table}, :column, :boolean, default: false, null: false" + ] + + inoffensive_sources.each do |source| + context "given the source \"#{source}\"" do + it "registers no offense" do + inspect_source(cop, source) + + aggregate_failures do + expect(cop.offenses).to be_empty + end + end + end + end + end + end + + it 'registers no offense for tables not listed in SMALL_TABLES' do + inspect_source(cop, "add_column :large_table, :column, :boolean") + + expect(cop.offenses).to be_empty + end + + it 'registers no offense for non-boolean columns' do + table = described_class::SMALL_TABLES.sample + inspect_source(cop, "add_column :#{table}, :column, :string") + + expect(cop.offenses).to be_empty + end + end + + context 'outside of migration' do + it 'registers no offense' do + table = described_class::SMALL_TABLES.sample + inspect_source(cop, "add_column :#{table}, :column, :boolean") + + expect(cop.offenses).to be_empty + end + end +end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 730df4e0336..53d4fcfed18 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -66,7 +66,7 @@ describe Ci::CreatePipelineService do context 'when there is no pipeline for source branch' do it "does not update merge request head pipeline" do - merge_request = create(:merge_request, source_branch: 'other_branch', target_branch: "branch_1", source_project: project) + merge_request = create(:merge_request, source_branch: 'feature', target_branch: "branch_1", source_project: project) head_pipeline = pipeline diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index 049b082277a..08267d6e6a0 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -20,6 +20,10 @@ describe CreateDeploymentService do let(:service) { described_class.new(job) } + before do + allow_any_instance_of(Deployment).to receive(:create_ref) + end + describe '#execute' do subject { service.execute } diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index fac66791ffb..67a86c50fc1 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -20,7 +20,7 @@ describe Issues::ResolveDiscussions do describe "for resolving discussions" do let(:discussion) { create(:diff_note_on_merge_request, project: project, note: "Almost done").to_discussion } let(:merge_request) { discussion.noteable } - let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "other") } + let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "fix") } describe "#merge_request_for_resolving_discussion" do let(:service) { DummyService.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid) } diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index 672d86e4028..25599dea19f 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" describe MergeRequests::GetUrlsService do let(:project) { create(:project, :public, :repository) } let(:service) { described_class.new(project) } - let(:source_branch) { "my_branch" } + let(:source_branch) { "merge-test" } let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" } let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" } let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" } @@ -111,9 +111,9 @@ describe MergeRequests::GetUrlsService do end context 'pushing new branch and existing branch (with merge request created) at once' do - let!(:merge_request) { create(:merge_request, source_project: project, source_branch: "existing_branch") } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: "markdown") } let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch" } - let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/existing_branch" } + let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/markdown" } let(:changes) { "#{new_branch_changes}\n#{existing_branch_changes}" } let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" } @@ -124,7 +124,7 @@ describe MergeRequests::GetUrlsService do url: new_merge_request_url, new_merge_request: true }, { - branch_name: "existing_branch", + branch_name: "markdown", url: show_merge_request_url, new_merge_request: false }]) diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 27e079c01dd..cb483ae9a5a 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -7,15 +7,18 @@ shared_examples 'reportable note' do let(:more_actions_selector) { '.more-actions.dropdown' } let(:abuse_report_path) { new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) } + it 'has an edit button' do + expect(comment).to have_selector('.js-note-edit') + end + it 'has a `More actions` dropdown' do expect(comment).to have_selector(more_actions_selector) end - it 'dropdown has Edit, Report and Delete links' do + it 'dropdown has Report and Delete links' do dropdown = comment.find(more_actions_selector) open_dropdown(dropdown) - expect(dropdown).to have_button('Edit comment') expect(dropdown).to have_link('Report as abuse', href: abuse_report_path) expect(dropdown).to have_link('Delete comment', href: note_url(note, project)) end diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index a60d3b0d22d..75982432ab4 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -2,12 +2,12 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| before do @issuable_ids = [] - 2.times do |n| + %w[fix improve/awesome].each do |source_branch| issuable = if issuable_type == :issue create(issuable_type, project: project) else - create(issuable_type, source_project: project, source_branch: "#{n}-feature") + create(issuable_type, source_project: project, source_branch: source_branch) end @issuable_ids << issuable.id diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index 43ac1a72152..b29d63c7d67 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -54,17 +54,17 @@ describe 'gitlab:gitaly namespace rake task' do before do FileUtils.mkdir_p(clone_path) expect(Dir).to receive(:chdir).with(clone_path).and_call_original + allow(Bundler).to receive(:bundle_path).and_return('/fake/bundle_path') end context 'gmake is available' do before do expect(main_object).to receive(:checkout_or_clone_version) - allow(main_object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) end it 'calls gmake in the gitaly directory' do expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0]) - expect(main_object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) + expect(main_object).to receive(:run_command!).with(command_preamble + %w[gmake BUNDLE_PATH=/fake/bundle_path]).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end @@ -73,15 +73,26 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is not available' do before do expect(main_object).to receive(:checkout_or_clone_version) - allow(main_object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) + expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42]) end it 'calls make in the gitaly directory' do - expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42]) - expect(main_object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) + expect(main_object).to receive(:run_command!).with(command_preamble + %w[make BUNDLE_PATH=/fake/bundle_path]).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end + + context 'when Rails.env is not "test"' do + before do + allow(Rails.env).to receive(:test?).and_return(false) + end + + it 'calls make in the gitaly directory without BUNDLE_PATH' do + expect(main_object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) + + run_rake_task('gitlab:gitaly:install', clone_path) + end + end end end end diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb index aea20d826d0..9c0be249a50 100644 --- a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb +++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb @@ -24,18 +24,16 @@ describe 'projects/notes/_more_actions_dropdown' do expect(rendered).not_to have_selector('.dropdown.more-actions') end - it 'shows Report as abuse, Edit and Delete buttons if editable and not current users comment' do + it 'shows Report as abuse and Delete buttons if editable and not current users comment' do render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note expect(rendered).to have_link('Report as abuse') - expect(rendered).to have_button('Edit comment') expect(rendered).to have_link('Delete comment') end - it 'shows Edit and Delete buttons if editable and current users comment' do + it 'shows Delete button if editable and current users comment' do render 'projects/notes/more_actions_dropdown', current_user: author_user, note_editable: true, note: note - expect(rendered).to have_button('Edit comment') expect(rendered).to have_link('Delete comment') end end diff --git a/vendor/project_templates/rails.tar.gz b/vendor/project_templates/rails.tar.gz Binary files differindex b54cae3143a..0509016f130 100644 --- a/vendor/project_templates/rails.tar.gz +++ b/vendor/project_templates/rails.tar.gz |