diff options
36 files changed, 359 insertions, 109 deletions
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js index ff2b66046b4..283c0ec0410 100644 --- a/app/assets/javascripts/lib/utils/sticky.js +++ b/app/assets/javascripts/lib/utils/sticky.js @@ -1,5 +1,5 @@ export const isSticky = (el, scrollY, stickyTop) => { - const top = el.offsetTop - scrollY; + const top = Math.floor(el.offsetTop - scrollY); if (top <= stickyTop) { el.classList.add('is-stuck'); diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 5a9b3d19f84..3b3620fe61b 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -253,6 +253,7 @@ import bp from './breakpoints'; loadDiff(source) { if (this.diffsLoaded) { + document.dispatchEvent(new CustomEvent('scroll')); return; } diff --git a/app/assets/javascripts/monitoring/components/monitoring_column.vue b/app/assets/javascripts/monitoring/components/monitoring_column.vue index 407af51cb7a..a31c26fb4fc 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_column.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_column.vue @@ -7,6 +7,7 @@ import eventHub from '../event_hub'; import measurements from '../utils/measurements'; import { formatRelevantDigits } from '../../lib/utils/number_utils'; + import { timeScaleFormat } from '../utils/date_time_formatters'; import bp from '../../breakpoints'; const bisectDate = d3.bisector(d => d.time).left; @@ -159,6 +160,7 @@ const xAxis = d3.svg.axis() .scale(axisXScale) .ticks(measurements.xTicks) + .tickFormat(timeScaleFormat) .orient('bottom'); const yAxis = d3.svg.axis() @@ -266,14 +268,6 @@ stroke-width="2" transform="translate(-5, 20)"> </path> - <rect - class="prometheus-graph-overlay" - :width="(graphWidth - 70)" - :height="(graphHeight - 100)" - transform="translate(-5, 20)" - ref="graphOverlay" - @mousemove="handleMouseOverGraph($event)"> - </rect> <monitoring-deployment :show-deploy-info="showDeployInfo" :deployment-data="reducedDeploymentData" @@ -289,6 +283,14 @@ :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" /> + <rect + class="prometheus-graph-overlay" + :width="(graphWidth - 70)" + :height="(graphHeight - 100)" + transform="translate(-5, 20)" + ref="graphOverlay" + @mousemove="handleMouseOverGraph($event)"> + </rect> </svg> </svg> </div> diff --git a/app/assets/javascripts/monitoring/components/monitoring_deployment.vue b/app/assets/javascripts/monitoring/components/monitoring_deployment.vue index e6432ba3191..dadbcd1aaa6 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_deployment.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_deployment.vue @@ -1,8 +1,5 @@ <script> - import { - dateFormat, - timeFormat, - } from '../constants'; + import { dateFormat, timeFormat } from '../utils/date_time_formatters'; export default { props: { @@ -58,7 +55,7 @@ class="deploy-info" v-if="showDeployInfo"> <g - v-for="(deployment, index) in deploymentData" + v-for="(deployment, index) in deploymentData" :key="index" :class="nameDeploymentClass(deployment)" :transform="transformDeploymentGroup(deployment)"> @@ -92,7 +89,7 @@ width="90" height="58"> </rect> - <g + <g transform="translate(5, 2)"> <text class="deploy-info-text text-metric-bold"> diff --git a/app/assets/javascripts/monitoring/components/monitoring_flag.vue b/app/assets/javascripts/monitoring/components/monitoring_flag.vue index 5a0e50fcab3..61cbeeebb17 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_flag.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_flag.vue @@ -1,8 +1,5 @@ <script> - import { - dateFormat, - timeFormat, - } from '../constants'; + import { dateFormat, timeFormat } from '../utils/date_time_formatters'; export default { props: { @@ -72,7 +69,7 @@ r="5" transform="translate(-5, 20)"> </circle> - <svg + <svg class="rect-text-metric" :x="currentFlagPosition" y="0"> diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js deleted file mode 100644 index c3a8da52404..00000000000 --- a/app/assets/javascripts/monitoring/constants.js +++ /dev/null @@ -1,4 +0,0 @@ -import d3 from 'd3'; - -export const dateFormat = d3.time.format('%b %d, %Y'); -export const timeFormat = d3.time.format('%H:%M%p'); diff --git a/app/assets/javascripts/monitoring/utils/date_time_formatters.js b/app/assets/javascripts/monitoring/utils/date_time_formatters.js new file mode 100644 index 00000000000..26bcaa02511 --- /dev/null +++ b/app/assets/javascripts/monitoring/utils/date_time_formatters.js @@ -0,0 +1,15 @@ +import d3 from 'd3'; + +export const dateFormat = d3.time.format('%b %-d, %Y'); +export const timeFormat = d3.time.format('%-I:%M%p'); + +export const timeScaleFormat = d3.time.format.multi([ + ['.%L', d => d.getMilliseconds()], + [':%S', d => d.getSeconds()], + ['%-I:%M', d => d.getMinutes()], + ['%-I %p', d => d.getHours()], + ['%a %-d', d => d.getDay() && d.getDate() !== 1], + ['%b %-d', d => d.getDate() !== 1], + ['%B', d => d.getMonth()], + ['%Y', () => true], +]); diff --git a/app/assets/javascripts/pipelines/components/navigation_tabs.vue b/app/assets/javascripts/pipelines/components/navigation_tabs.vue index d2f6d47f043..73f7e3a0cad 100644 --- a/app/assets/javascripts/pipelines/components/navigation_tabs.vue +++ b/app/assets/javascripts/pipelines/components/navigation_tabs.vue @@ -1,23 +1,29 @@ <script> -export default { - name: 'PipelineNavigationTabs', - props: { - scope: { - type: String, - required: true, + export default { + name: 'PipelineNavigationTabs', + props: { + scope: { + type: String, + required: true, + }, + count: { + type: Object, + required: true, + }, + paths: { + type: Object, + required: true, + }, }, - count: { - type: Object, - required: true, + mounted() { + $(document).trigger('init.scrolling-tabs'); }, - paths: { - type: Object, - required: true, + methods: { + shouldRenderBadge(count) { + // 0 is valid in a badge, but evaluates to false, we need to check for undefined + return count !== undefined; + }, }, - }, - mounted() { - $(document).trigger('init.scrolling-tabs'); - }, }; </script> <template> @@ -27,7 +33,9 @@ export default { :class="{ active: scope === 'all'}"> <a :href="paths.allPath"> All - <span class="badge js-totalbuilds-count"> + <span + v-if="shouldRenderBadge(count.all)" + class="badge js-totalbuilds-count"> {{count.all}} </span> </a> @@ -37,7 +45,9 @@ export default { :class="{ active: scope === 'pending'}"> <a :href="paths.pendingPath"> Pending - <span class="badge"> + <span + v-if="shouldRenderBadge(count.pending)" + class="badge"> {{count.pending}} </span> </a> @@ -47,7 +57,9 @@ export default { :class="{ active: scope === 'running'}"> <a :href="paths.runningPath"> Running - <span class="badge"> + <span + v-if="shouldRenderBadge(count.running)" + class="badge"> {{count.running}} </span> </a> @@ -57,7 +69,9 @@ export default { :class="{ active: scope === 'finished'}"> <a :href="paths.finishedPath"> Finished - <span class="badge"> + <span + v-if="shouldRenderBadge(count.finished)" + class="badge"> {{count.finished}} </span> </a> diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index d93722e2174..9fccc68b5b6 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -266,7 +266,9 @@ } // TODO: change global style -.ajax-project-dropdown { +.ajax-project-dropdown, +body[data-page="projects:blob:new"] #select2-drop, +body[data-page="projects:blob:edit"] #select2-drop { &.select2-drop { color: $gl-text-color; } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index a385eb359e1..3fb02e9964f 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -96,6 +96,8 @@ } .member-search-form { + @include new-style-dropdown; + position: relative; @media (min-width: $screen-sm-min) { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index d1678a17aaf..334bec8dd7e 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -489,6 +489,8 @@ } .mr-source-target { + @include new-style-dropdown; + display: flex; flex-wrap: wrap; justify-content: space-between; diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 5b9fafe31bd..6c8d87185e9 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -265,3 +265,7 @@ font-weight: $gl-font-weight-bold; } } + +.todos-filters { + @include new-style-dropdown; +} diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 2a3b73577a5..e3fa3736808 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -318,14 +318,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo elsif @merge_request.head_pipeline.success? # This can be triggered when a user clicks the auto merge button while # the tests finish at about the same time - MergeWorker.perform_async(@merge_request.id, current_user.id, params) + @merge_request.merge_async(current_user.id, params) :success else :failed end else - MergeWorker.perform_async(@merge_request.id, current_user.id, params) + @merge_request.merge_async(current_user.id, params) :success end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index dbc73ed3cd4..7f73de67625 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -241,6 +241,14 @@ class MergeRequest < ActiveRecord::Base end end + # Calls `MergeWorker` to proceed with the merge process and + # updates `merge_jid` with the MergeWorker#jid. + # This helps tracking enqueued and ongoing merge jobs. + def merge_async(user_id, params) + jid = MergeWorker.perform_async(id, user_id, params) + update_column(:merge_jid, jid) + end + def first_commit merge_request_diff ? merge_request_diff.first_commit : compare_commits.first end @@ -384,9 +392,7 @@ class MergeRequest < ActiveRecord::Base end def merge_ongoing? - return false unless merge_jid - - Gitlab::SidekiqStatus.num_running([merge_jid]) > 0 + !!merge_jid && !merged? end def closed_without_fork? @@ -819,7 +825,7 @@ class MergeRequest < ActiveRecord::Base lock_mr yield ensure - unlock_mr if locked? + unlock_mr end end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index d0ba9f89460..de2cd7e87be 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -15,7 +15,7 @@ module Ci pipeline_schedule: schedule ) - result = validate(current_user || trigger_request.trigger.owner, + result = validate(current_user, ignore_skip_ci: ignore_skip_ci, save_on_errors: save_on_errors) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 5be749cd6a0..b2b6c5627fb 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -26,10 +26,12 @@ module MergeRequests merge_request.in_locked_state do if commit after_merge + clean_merge_jid success end end rescue MergeError => e + clean_merge_jid log_merge_error(e.message, save_message_on_model: true) end @@ -70,6 +72,10 @@ module MergeRequests end end + def clean_merge_jid + merge_request.update_column(:merge_jid, nil) + end + def branch_deletion_user @merge_request.force_remove_source_branch? ? @merge_request.author : current_user end diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb index aed5287940e..850deb0ac7a 100644 --- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb @@ -30,7 +30,7 @@ module MergeRequests next end - MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params) + merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params) end end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 75a65aecd1a..2832d893e95 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -83,7 +83,7 @@ module MergeRequests if merge_request.head_pipeline && merge_request.head_pipeline.active? MergeRequests::MergeWhenPipelineSucceedsService.new(project, current_user).execute(merge_request) else - MergeWorker.perform_async(merge_request.id, current_user.id, {}) + merge_request.merge_async(current_user.id, {}) end end diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml index 988e7d302f0..9294529f496 100644 --- a/app/views/layouts/nav/_new_admin_sidebar.html.haml +++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml @@ -82,6 +82,7 @@ = custom_icon('messages') %span.nav-item-name Messages + = nav_link(controller: [:hooks, :hook_logs]) do = link_to admin_hooks_path, title: 'Hooks' do .nav-icon-container @@ -140,7 +141,6 @@ %span.nav-item-name Appearance - %li.divider = nav_link(controller: :application_settings) do = link_to admin_application_settings_path, title: 'Settings' do .nav-icon-container diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 647e0a772b1..5698bb281b4 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -68,9 +68,10 @@ - if git_import_enabled? %button.btn.js-toggle-button.import_git{ type: "button" } = icon('git', text: 'Repo by URL') - .import_gitlab_project.has-tooltip{ data: { container: 'body' } } - = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do - = icon('gitlab', text: 'GitLab export') + - if gitlab_project_import_enabled? + .import_gitlab_project.has-tooltip{ data: { container: 'body' } } + = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do + = icon('gitlab', text: 'GitLab export') .row .col-lg-12 diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb index c3b58df92c1..48e2da338f6 100644 --- a/app/workers/merge_worker.rb +++ b/app/workers/merge_worker.rb @@ -7,8 +7,6 @@ class MergeWorker current_user = User.find(current_user_id) merge_request = MergeRequest.find(merge_request_id) - merge_request.update_column(:merge_jid, jid) - MergeRequests::MergeService.new(merge_request.target_project, current_user, params) .execute(merge_request) end diff --git a/changelogs/unreleased/35048-empty-badges.yml b/changelogs/unreleased/35048-empty-badges.yml new file mode 100644 index 00000000000..816fe82887c --- /dev/null +++ b/changelogs/unreleased/35048-empty-badges.yml @@ -0,0 +1,5 @@ +--- +title: Prevents rendering empty badges when request fails +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/36114-stuck-mrs-job-follow-up.yml b/changelogs/unreleased/36114-stuck-mrs-job-follow-up.yml new file mode 100644 index 00000000000..1b664efb8c2 --- /dev/null +++ b/changelogs/unreleased/36114-stuck-mrs-job-follow-up.yml @@ -0,0 +1,4 @@ +--- +title: Present enqueued merge jobs as Merging as well +merge_request: +author: diff --git a/changelogs/unreleased/36882-disable-gitlab-project-import-button-if-source-disabled.yml b/changelogs/unreleased/36882-disable-gitlab-project-import-button-if-source-disabled.yml new file mode 100644 index 00000000000..a06c84c30e6 --- /dev/null +++ b/changelogs/unreleased/36882-disable-gitlab-project-import-button-if-source-disabled.yml @@ -0,0 +1,5 @@ +--- +title: Disable GitLab Project Import Button if source disabled +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/37104-fix-graph-date-format.yml b/changelogs/unreleased/37104-fix-graph-date-format.yml new file mode 100644 index 00000000000..f7d39fe8283 --- /dev/null +++ b/changelogs/unreleased/37104-fix-graph-date-format.yml @@ -0,0 +1,5 @@ +--- +title: Fix incorrect date/time formatting on prometheus graphs +merge_request: 13865 +author: +type: fixed diff --git a/changelogs/unreleased/changes-bar-sticky-fix.yml b/changelogs/unreleased/changes-bar-sticky-fix.yml new file mode 100644 index 00000000000..7d62773ef7a --- /dev/null +++ b/changelogs/unreleased/changes-bar-sticky-fix.yml @@ -0,0 +1,5 @@ +--- +title: Fixed diff changes bar buttons from showing/hiding whilst scrolling +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-sm-37040-regression-pipeline-trigger-via-api-fails-with-500-internal-server-error-in-9-5-1.yml b/changelogs/unreleased/fix-sm-37040-regression-pipeline-trigger-via-api-fails-with-500-internal-server-error-in-9-5-1.yml new file mode 100644 index 00000000000..fb97bdb6b30 --- /dev/null +++ b/changelogs/unreleased/fix-sm-37040-regression-pipeline-trigger-via-api-fails-with-500-internal-server-error-in-9-5-1.yml @@ -0,0 +1,5 @@ +--- +title: Fix pipeline trigger via API fails with 500 Internal Server Error in 9.5 +merge_request: +author: +type: fixed diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index f7c2a0ef0ca..f28c9791bee 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -134,7 +134,7 @@ In order to do that, follow the steps: # When using dind, it's wise to use the overlayfs driver for # improved performance. variables: - DOCKER_DRIVER: overlay + DOCKER_DRIVER: overlay2 services: - docker:dind @@ -248,7 +248,7 @@ aware of the following implications: By default, when using `docker:dind`, Docker uses the `vfs` storage driver which copies the filesystem on every run. This is a very disk-intensive operation -which can be avoided if a different driver is used, for example `overlay`. +which can be avoided if a different driver is used, for example `overlay2`. 1. Make sure a recent kernel is used, preferably `>= 4.2`. 1. Check whether the `overlay` module is loaded: @@ -275,8 +275,12 @@ which can be avoided if a different driver is used, for example `overlay`. ``` variables: - DOCKER_DRIVER: overlay + DOCKER_DRIVER: overlay2 ``` + +> **Note:** +- For more information about using OverlayFS with Docker, you can read + [Use the OverlayFS storage driver](https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/). ## Using the GitLab Container Registry diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js new file mode 100644 index 00000000000..d7a2e86771c --- /dev/null +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -0,0 +1,4 @@ +export default (Component, props = {}) => new Component({ + propsData: props, +}).$mount(); + diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index dc40244c20e..8830a2d29e5 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -295,6 +295,17 @@ import 'vendor/jquery.scrollTo'; this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); }); + it('triggers scroll event when diff already loaded', function () { + spyOn(document, 'dispatchEvent'); + + this.class.diffsLoaded = true; + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + + expect( + document.dispatchEvent, + ).toHaveBeenCalledWith(new CustomEvent('scroll')); + }); + describe('with inline diff', () => { let noteId; let noteLineNumId; diff --git a/spec/javascripts/pipelines/navigation_tabs_spec.js b/spec/javascripts/pipelines/navigation_tabs_spec.js new file mode 100644 index 00000000000..53a88e6322f --- /dev/null +++ b/spec/javascripts/pipelines/navigation_tabs_spec.js @@ -0,0 +1,127 @@ +import Vue from 'vue'; +import navigationTabs from '~/pipelines/components/navigation_tabs.vue'; +import mountComponent from '../helpers/vue_mount_component_helper'; + +describe('navigation tabs pipeline component', () => { + let vm; + let Component; + let data; + + beforeEach(() => { + data = { + scope: 'all', + count: { + all: 16, + running: 1, + pending: 10, + finished: 0, + }, + paths: { + allPath: '/gitlab-org/gitlab-ce/pipelines', + pendingPath: '/gitlab-org/gitlab-ce/pipelines?scope=pending', + finishedPath: '/gitlab-org/gitlab-ce/pipelines?scope=finished', + runningPath: '/gitlab-org/gitlab-ce/pipelines?scope=running', + branchesPath: '/gitlab-org/gitlab-ce/pipelines?scope=branches', + tagsPath: '/gitlab-org/gitlab-ce/pipelines?scope=tags', + }, + }; + + Component = Vue.extend(navigationTabs); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should render tabs with correct paths', () => { + vm = mountComponent(Component, data); + + // All + const allTab = vm.$el.querySelector('.js-pipelines-tab-all a'); + expect(allTab.textContent.trim()).toContain('All'); + expect(allTab.getAttribute('href')).toEqual(data.paths.allPath); + + // Pending + const pendingTab = vm.$el.querySelector('.js-pipelines-tab-pending a'); + expect(pendingTab.textContent.trim()).toContain('Pending'); + expect(pendingTab.getAttribute('href')).toEqual(data.paths.pendingPath); + + // Running + const runningTab = vm.$el.querySelector('.js-pipelines-tab-running a'); + expect(runningTab.textContent.trim()).toContain('Running'); + expect(runningTab.getAttribute('href')).toEqual(data.paths.runningPath); + + // Finished + const finishedTab = vm.$el.querySelector('.js-pipelines-tab-finished a'); + expect(finishedTab.textContent.trim()).toContain('Finished'); + expect(finishedTab.getAttribute('href')).toEqual(data.paths.finishedPath); + + // Branches + const branchesTab = vm.$el.querySelector('.js-pipelines-tab-branches a'); + expect(branchesTab.textContent.trim()).toContain('Branches'); + + // Tags + const tagsTab = vm.$el.querySelector('.js-pipelines-tab-tags a'); + expect(tagsTab.textContent.trim()).toContain('Tags'); + }); + + describe('scope', () => { + it('should render scope provided as active tab', () => { + vm = mountComponent(Component, data); + expect(vm.$el.querySelector('.js-pipelines-tab-all').className).toContain('active'); + }); + }); + + describe('badges', () => { + it('should render provided number', () => { + vm = mountComponent(Component, data); + // All + expect( + vm.$el.querySelector('.js-totalbuilds-count').textContent.trim(), + ).toContain(data.count.all); + + // Pending + expect( + vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim(), + ).toContain(data.count.pending); + + // Running + expect( + vm.$el.querySelector('.js-pipelines-tab-running .badge').textContent.trim(), + ).toContain(data.count.running); + + // Finished + expect( + vm.$el.querySelector('.js-pipelines-tab-finished .badge').textContent.trim(), + ).toContain(data.count.finished); + }); + + it('should not render badge when number is undefined', () => { + vm = mountComponent(Component, { + scope: 'all', + paths: {}, + count: {}, + }); + + // All + expect( + vm.$el.querySelector('.js-totalbuilds-count'), + ).toEqual(null); + + // Pending + expect( + vm.$el.querySelector('.js-pipelines-tab-pending .badge'), + ).toEqual(null); + + // Running + expect( + vm.$el.querySelector('.js-pipelines-tab-running .badge'), + ).toEqual(null); + + // Finished + expect( + vm.$el.querySelector('.js-pipelines-tab-finished .badge'), + ).toEqual(null); + }); + }); +}); diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 2d10c6ef1da..92cf15a5a51 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -931,6 +931,23 @@ describe MergeRequest do end end + describe '#merge_async' do + it 'enqueues MergeWorker job and updates merge_jid' do + merge_request = create(:merge_request) + user_id = double(:user_id) + params = double(:params) + merge_jid = 'hash-123' + + expect(MergeWorker).to receive(:perform_async).with(merge_request.id, user_id, params) do + merge_jid + end + + merge_request.merge_async(user_id, params) + + expect(merge_request.reload.merge_jid).to eq(merge_jid) + end + end + describe '#check_if_can_be_merged' do let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) } @@ -1370,29 +1387,11 @@ describe MergeRequest do end describe '#merge_ongoing?' do - it 'returns true when merge process is ongoing for merge_jid' do - merge_request = create(:merge_request, merge_jid: 'foo') - - allow(Gitlab::SidekiqStatus).to receive(:num_running).with(['foo']).and_return(1) + it 'returns true when merge_id is present and MR is not merged' do + merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo') expect(merge_request.merge_ongoing?).to be(true) end - - it 'returns false when no merge process running for merge_jid' do - merge_request = build(:merge_request, merge_jid: 'foo') - - allow(Gitlab::SidekiqStatus).to receive(:num_running).with(['foo']).and_return(0) - - expect(merge_request.merge_ongoing?).to be(false) - end - - it 'returns false when merge_jid is nil' do - merge_request = build(:merge_request, merge_jid: nil) - - expect(Gitlab::SidekiqStatus).not_to receive(:num_running) - - expect(merge_request.merge_ongoing?).to be(false) - end end describe "#closed_without_fork?" do diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 1e206fd2a9e..402d1040436 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -84,6 +84,22 @@ describe API::Triggers do expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables) end end + + context 'when legacy trigger' do + before do + trigger.update(owner: nil) + end + + it 'creates pipeline' do + post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'master') + + expect(response).to have_http_status(201) + expect(json_response).to include('id' => pipeline.id) + pipeline.builds.reload + expect(pipeline.builds.pending.size).to eq(2) + expect(pipeline.builds.size).to eq(5) + end + end end context 'when triggering a pipeline from a trigger token' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index fdd0cea4f3b..4ba3dada37c 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -413,14 +413,12 @@ describe Ci::CreatePipelineService do end context 'when trigger belongs to a developer' do - let(:user) {} + let(:user) { create(:user) } + let(:trigger) { create(:ci_trigger, owner: user) } + let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) } - let(:trigger_request) do - create(:ci_trigger_request).tap do |request| - user = create(:user) - project.add_developer(user) - request.trigger.update(owner: user) - end + before do + project.add_developer(user) end it 'does not create a pipeline' do @@ -431,17 +429,15 @@ describe Ci::CreatePipelineService do end context 'when trigger belongs to a master' do - let(:user) {} + let(:user) { create(:user) } + let(:trigger) { create(:ci_trigger, owner: user) } + let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) } - let(:trigger_request) do - create(:ci_trigger_request).tap do |request| - user = create(:user) - project.add_master(user) - request.trigger.update(owner: user) - end + before do + project.add_master(user) end - it 'does not create a pipeline' do + it 'creates a pipeline' do expect(execute_service(trigger_request: trigger_request)) .to be_persisted expect(Ci::Pipeline.count).to eq(1) diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 5cfdb5372f3..b60136064b7 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -12,6 +12,38 @@ describe MergeRequests::MergeService do end describe '#execute' do + context 'MergeRequest#merge_jid' do + before do + merge_request.update_column(:merge_jid, 'hash-123') + end + + it 'is cleaned when no error is raised' do + service = described_class.new(project, user, commit_message: 'Awesome message') + + service.execute(merge_request) + + expect(merge_request.reload.merge_jid).to be_nil + end + + it 'is cleaned when expected error is raised' do + service = described_class.new(project, user, commit_message: 'Awesome message') + allow(service).to receive(:commit).and_raise(described_class::MergeError) + + service.execute(merge_request) + + expect(merge_request.reload.merge_jid).to be_nil + end + + it 'is not cleaned when unexpected error is raised' do + service = described_class.new(project, user, commit_message: 'Awesome message') + allow(service).to receive(:commit).and_raise(StandardError) + + expect { service.execute(merge_request) }.to raise_error(StandardError) + + expect(merge_request.reload.merge_jid).to be_present + end + end + context 'valid params' do let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb index ee51000161a..303193bab9b 100644 --- a/spec/workers/merge_worker_spec.rb +++ b/spec/workers/merge_worker_spec.rb @@ -27,15 +27,4 @@ describe MergeWorker do expect(source_project.repository.branch_names).not_to include('markdown') end end - - it 'persists merge_jid' do - merge_request = create(:merge_request, merge_jid: nil) - user = create(:user) - worker = described_class.new - - allow(worker).to receive(:jid) { '999' } - - expect { worker.perform(merge_request.id, user.id, {}) } - .to change { merge_request.reload.merge_jid }.from(nil).to('999') - end end |