diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-27 21:06:20 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-27 21:06:20 +0300 |
commit | 2abb1b54c0305b359b178d6660810e865f619c22 (patch) | |
tree | e388953a0566ef9844b0b98cdb34236049721a14 /app | |
parent | 8320f7956d72986f5a7c850874fce4f8b5a8e015 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/lib/utils/axios_utils.js | 15 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js | 16 | ||||
-rw-r--r-- | app/assets/javascripts/notes/components/notes_app.vue | 7 | ||||
-rw-r--r-- | app/assets/javascripts/pages/groups/shared/group_details.js | 7 | ||||
-rw-r--r-- | app/controllers/boards/lists_controller.rb | 7 | ||||
-rw-r--r-- | app/models/ci/build.rb | 1 | ||||
-rw-r--r-- | app/models/ci/persistent_ref.rb | 54 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 6 | ||||
-rw-r--r-- | app/models/list.rb | 36 | ||||
-rw-r--r-- | app/models/repository.rb | 3 | ||||
-rw-r--r-- | app/presenters/ci/build_runner_presenter.rb | 17 | ||||
-rw-r--r-- | app/services/boards/lists/list_service.rb | 2 |
13 files changed, 138 insertions, 35 deletions
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js index c17f62c671c..a04fe609015 100644 --- a/app/assets/javascripts/lib/utils/axios_utils.js +++ b/app/assets/javascripts/lib/utils/axios_utils.js @@ -1,5 +1,6 @@ import axios from 'axios'; import csrf from './csrf'; +import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation'; axios.defaults.headers.common[csrf.headerKey] = csrf.token; // Used by Rails to check if it is a valid XHR request @@ -25,6 +26,20 @@ axios.interceptors.response.use( }, ); +let isUserNavigating = false; +window.addEventListener('beforeunload', () => { + isUserNavigating = true; +}); + +// Ignore AJAX errors caused by requests +// being cancelled due to browser navigation +const { gon } = window; +const featureFlagEnabled = gon && gon.features && gon.features.suppressAjaxNavigationErrors; +axios.interceptors.response.use( + response => response, + err => suppressAjaxErrorsDuringNavigation(err, isUserNavigating, featureFlagEnabled), +); + export default axios; /** diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6e8f63a10a4..7c4373ba517 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -15,6 +15,8 @@ export const getPagePath = (index = 0) => { return page.split(':')[index]; }; +export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null; + export const isInGroupsPage = () => getPagePath() === 'groups'; export const isInProjectPage = () => getPagePath() === 'projects'; diff --git a/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js new file mode 100644 index 00000000000..4c61da9b862 --- /dev/null +++ b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js @@ -0,0 +1,16 @@ +/** + * An Axios error interceptor that suppresses AJAX errors caused + * by the request being cancelled when the user navigates to a new page + */ +export default (err, isUserNavigating, featureFlagEnabled) => { + if (featureFlagEnabled && isUserNavigating && err.code === 'ECONNABORTED') { + // If the user is navigating away from the current page, + // prevent .then() and .catch() handlers from being + // called by returning a Promise that never resolves + return new Promise(() => {}); + } + + // The error is not related to browser navigation, + // so propagate the error + return Promise.reject(err); +}; diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 16a0fb3f33a..fd592e965bf 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -69,6 +69,7 @@ export default { 'commentsDisabled', 'getNoteableData', 'userCanReply', + 'discussionTabCounter', ]), noteableType() { return this.noteableData.noteableType; @@ -95,13 +96,13 @@ export default { } }, allDiscussions() { - if (this.discussonsCount) { - this.discussonsCount.textContent = this.allDiscussions.length; + if (this.discussionsCount && !this.isLoading) { + this.discussionsCount.textContent = this.discussionTabCounter; } }, }, created() { - this.discussonsCount = document.querySelector('.js-discussions-count'); + this.discussionsCount = document.querySelector('.js-discussions-count'); this.setNotesData(this.notesData); this.setNoteableData(this.noteableData); diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js index 01ef3f1db2b..37b253d7c48 100644 --- a/app/assets/javascripts/pages/groups/shared/group_details.js +++ b/app/assets/javascripts/pages/groups/shared/group_details.js @@ -1,6 +1,6 @@ /* eslint-disable no-new */ -import { getPagePath } from '~/lib/utils/common_utils'; +import { getPagePath, getDashPath } from '~/lib/utils/common_utils'; import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants'; import NewGroupChild from '~/groups/new_group_child'; import notificationsDropdown from '~/notifications_dropdown'; @@ -12,9 +12,8 @@ import GroupTabs from './group_tabs'; export default function initGroupDetails(actionName = 'show') { const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED]; - const paths = window.location.pathname.split('/'); - const subpath = paths[paths.length - 1]; - let action = loadableActions.includes(subpath) ? subpath : getPagePath(1); + const dashPath = getDashPath(); + let action = loadableActions.includes(dashPath) ? dashPath : getPagePath(1); if (actionName && action === actionName) { action = 'show'; // 'show' resets GroupTabs to default action through base class } diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb index b64b18505b6..90e04414d8d 100644 --- a/app/controllers/boards/lists_controller.rb +++ b/app/controllers/boards/lists_controller.rb @@ -11,6 +11,8 @@ module Boards def index lists = Boards::Lists::ListService.new(board.parent, current_user).execute(board) + List.preload_preferences_for_user(lists, current_user) + render json: serialize_as_json(lists) end @@ -51,7 +53,10 @@ module Boards service = Boards::Lists::GenerateService.new(board_parent, current_user) if service.execute(board) - lists = board.lists.movable.preload_associations(current_user) + lists = board.lists.movable.preload_associations + + List.preload_preferences_for_user(lists, current_user) + render json: serialize_as_json(lists) else head :unprocessable_entity diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 3ef0ed0ef49..5cca18024c1 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -234,6 +234,7 @@ module Ci end after_transition pending: :running do |build| + build.pipeline.persistent_ref.create build.deployment&.run build.run_after_commit do diff --git a/app/models/ci/persistent_ref.rb b/app/models/ci/persistent_ref.rb new file mode 100644 index 00000000000..9bb67c88577 --- /dev/null +++ b/app/models/ci/persistent_ref.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Ci + ## + # The persistent pipeline ref to ensure runners can safely fetch source code + # even if force-push/source-branch-deletion happens. + class PersistentRef + include ActiveModel::Model + + attr_accessor :pipeline + + delegate :project, :sha, to: :pipeline + delegate :repository, to: :project + delegate :ref_exists?, :create_ref, :delete_refs, to: :repository + + def exist? + return unless enabled? + + ref_exists?(path) + rescue + false + end + + def create + return unless enabled? && !exist? + + create_ref(sha, path) + rescue => e + Gitlab::Sentry + .track_acceptable_exception(e, extra: { pipeline_id: pipeline.id }) + end + + def delete + return unless enabled? + + delete_refs(path) + rescue Gitlab::Git::Repository::NoRepository + # no-op + rescue => e + Gitlab::Sentry + .track_acceptable_exception(e, extra: { pipeline_id: pipeline.id }) + end + + def path + "refs/#{Repository::REF_PIPELINES}/#{pipeline.id}" + end + + private + + def enabled? + Feature.enabled?(:depend_on_persistent_pipeline_ref, project) + end + end +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 12295c0457d..b22a87e7cef 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -174,6 +174,8 @@ module Ci after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline| pipeline.run_after_commit do + pipeline.persistent_ref.delete + pipeline.all_merge_requests.each do |merge_request| next unless merge_request.auto_merge_enabled? @@ -853,6 +855,10 @@ module Ci end end + def persistent_ref + @persistent_ref ||= PersistentRef.new(pipeline: self) + end + private def ci_yaml_from_repo diff --git a/app/models/list.rb b/app/models/list.rb index b4a4631b397..b53e2fb7c33 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -21,20 +21,10 @@ class List < ApplicationRecord scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) } scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) } - scope :preload_associations, -> (user) do - preload(:board, label: :priorities) - end + scope :preload_associations, -> { preload(:board, label: :priorities) } scope :ordered, -> { order(:list_type, :position) } - # Loads list with preferences for given user - # if preferences exists for user or not - scope :with_preferences_for, -> (user) do - return unless user - - includes(:list_user_preferences).where(list_user_preferences: { user_id: [user.id, nil] }) - end - alias_method :preferences, :list_user_preferences class << self @@ -45,25 +35,25 @@ class List < ApplicationRecord def movable_types [:label] end + + def preload_preferences_for_user(lists, user) + return unless user + + lists.each { |list| list.preferences_for(user) } + end end def preferences_for(user) return preferences.build unless user - if preferences.loaded? - preloaded_preferences_for(user) - else - preferences.find_or_initialize_by(user: user) - end - end + BatchLoader.for(list_id: id, user_id: user.id).batch(default_value: preferences.build(user: user)) do |items, loader| + list_ids = items.map { |i| i[:list_id] } + user_ids = items.map { |i| i[:user_id] } - def preloaded_preferences_for(user) - user_preferences = - preferences.find do |preference| - preference.user_id == user.id + ListUserPreference.where(list_id: list_ids, user_id: user_ids).find_each do |preference| + loader.call({ list_id: preference.list_id, user_id: preference.user_id }, preference) end - - user_preferences || preferences.build(user: user) + end end def update_preferences_for(user, preferences = {}) diff --git a/app/models/repository.rb b/app/models/repository.rb index fb84bbaca56..6c6023a8709 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -6,6 +6,7 @@ class Repository REF_MERGE_REQUEST = 'merge-requests' REF_KEEP_AROUND = 'keep-around' REF_ENVIRONMENTS = 'environments' + REF_PIPELINES = 'pipelines' ARCHIVE_CACHE_TIME = 60 # Cache archives referred to by a (mutable) ref for 1 minute ARCHIVE_CACHE_TIME_IMMUTABLE = 3600 # Cache archives referred to by an immutable reference for 1 hour @@ -16,7 +17,7 @@ class Repository replace #{REF_ENVIRONMENTS} #{REF_KEEP_AROUND} - #{REF_ENVIRONMENTS} + #{REF_PIPELINES} ].freeze include Gitlab::RepositoryCacheAdapter diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb index 5231a8efa55..8e469795581 100644 --- a/app/presenters/ci/build_runner_presenter.rb +++ b/app/presenters/ci/build_runner_presenter.rb @@ -34,7 +34,8 @@ module Ci def refspecs specs = [] - specs << refspec_for_merge_request_ref if merge_request_ref? + specs << refspec_for_pipeline_ref if merge_request_ref? + specs << refspec_for_persistent_ref if persistent_ref_exist? if git_depth > 0 specs << refspec_for_branch(ref) if branch? || legacy_detached_merge_request_pipeline? @@ -86,10 +87,22 @@ module Ci "+#{Gitlab::Git::TAG_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_TAG_PREFIX}#{ref}" end - def refspec_for_merge_request_ref + def refspec_for_pipeline_ref "+#{ref}:#{ref}" end + def refspec_for_persistent_ref + "+#{persistent_ref_path}:#{persistent_ref_path}" + end + + def persistent_ref_exist? + pipeline.persistent_ref.exist? + end + + def persistent_ref_path + pipeline.persistent_ref.path + end + def git_depth_variable strong_memoize(:git_depth_variable) do variables&.find { |variable| variable[:key] == 'GIT_DEPTH' } diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb index 3609d9c6283..82cba1b68c4 100644 --- a/app/services/boards/lists/list_service.rb +++ b/app/services/boards/lists/list_service.rb @@ -6,7 +6,7 @@ module Boards def execute(board) board.lists.create(list_type: :backlog) unless board.lists.backlog.exists? - board.lists.preload_associations(current_user) + board.lists.preload_associations end end end |