From 0af391d2f45f23fb806584a5c80747145a07b7c0 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 15 Feb 2018 12:26:38 +0100 Subject: Refactored use of boards/boards_bundle.js, created a dipatcher import --- app/assets/javascripts/boards/boards_bundle.js | 239 --------------------- app/assets/javascripts/boards/index.js | 239 +++++++++++++++++++++ app/assets/javascripts/dispatcher.js | 5 + .../javascripts/pages/projects/boards/index.js | 6 +- app/views/shared/boards/_show.html.haml | 1 - config/webpack.config.js | 1 - spec/javascripts/test_bundle.js | 2 +- 7 files changed, 249 insertions(+), 244 deletions(-) delete mode 100644 app/assets/javascripts/boards/boards_bundle.js create mode 100644 app/assets/javascripts/boards/index.js diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js deleted file mode 100644 index 90166b3d3d1..00000000000 --- a/app/assets/javascripts/boards/boards_bundle.js +++ /dev/null @@ -1,239 +0,0 @@ -/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ - -import _ from 'underscore'; -import Vue from 'vue'; -import Flash from '../flash'; -import { __ } from '../locale'; -import FilteredSearchBoards from './filtered_search_boards'; -import eventHub from './eventhub'; -import sidebarEventHub from '../sidebar/event_hub'; -import './models/issue'; -import './models/label'; -import './models/list'; -import './models/milestone'; -import './models/assignee'; -import './stores/boards_store'; -import './stores/modal_store'; -import BoardService from './services/board_service'; -import './mixins/modal_mixins'; -import './mixins/sortable_default_options'; -import './filters/due_date_filters'; -import './components/board'; -import './components/board_sidebar'; -import './components/new_list_dropdown'; -import './components/modal/index'; -import '../vue_shared/vue_resource_interceptor'; - -$(() => { - const $boardApp = document.getElementById('board-app'); - const Store = gl.issueBoards.BoardsStore; - const ModalStore = gl.issueBoards.ModalStore; - - window.gl = window.gl || {}; - - if (gl.IssueBoardsApp) { - gl.IssueBoardsApp.$destroy(true); - } - - Store.create(); - - // hack to allow sidebar scripts like milestone_select manipulate the BoardsStore - gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args); - gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args); - - gl.IssueBoardsApp = new Vue({ - el: $boardApp, - components: { - 'board': gl.issueBoards.Board, - 'board-sidebar': gl.issueBoards.BoardSidebar, - 'board-add-issues-modal': gl.issueBoards.IssuesModal, - }, - data: { - state: Store.state, - loading: true, - boardsEndpoint: $boardApp.dataset.boardsEndpoint, - listsEndpoint: $boardApp.dataset.listsEndpoint, - boardId: $boardApp.dataset.boardId, - disabled: $boardApp.dataset.disabled === 'true', - issueLinkBase: $boardApp.dataset.issueLinkBase, - rootPath: $boardApp.dataset.rootPath, - bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, - detailIssue: Store.detail, - defaultAvatar: $boardApp.dataset.defaultAvatar, - }, - computed: { - detailIssueVisible () { - return Object.keys(this.detailIssue.issue).length; - }, - }, - created () { - gl.boardService = new BoardService({ - boardsEndpoint: this.boardsEndpoint, - listsEndpoint: this.listsEndpoint, - bulkUpdatePath: this.bulkUpdatePath, - boardId: this.boardId, - }); - Store.rootPath = this.boardsEndpoint; - - eventHub.$on('updateTokens', this.updateTokens); - eventHub.$on('newDetailIssue', this.updateDetailIssue); - eventHub.$on('clearDetailIssue', this.clearDetailIssue); - sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); - }, - beforeDestroy() { - eventHub.$off('updateTokens', this.updateTokens); - eventHub.$off('newDetailIssue', this.updateDetailIssue); - eventHub.$off('clearDetailIssue', this.clearDetailIssue); - sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); - }, - mounted () { - this.filterManager = new FilteredSearchBoards(Store.filter, true); - this.filterManager.setup(); - - Store.disabled = this.disabled; - gl.boardService.all() - .then(res => res.data) - .then((data) => { - data.forEach((board) => { - const list = Store.addList(board, this.defaultAvatar); - - if (list.type === 'closed') { - list.position = Infinity; - } else if (list.type === 'backlog') { - list.position = -1; - } - }); - - this.state.lists = _.sortBy(this.state.lists, 'position'); - - Store.addBlankState(); - this.loading = false; - }) - .catch(() => { - Flash('An error occurred while fetching the board lists. Please try again.'); - }); - }, - methods: { - updateTokens() { - this.filterManager.updateTokens(); - }, - updateDetailIssue(newIssue) { - const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint; - if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { - newIssue.setFetchingState('subscriptions', true); - BoardService.getIssueInfo(sidebarInfoEndpoint) - .then(res => res.data) - .then((data) => { - newIssue.setFetchingState('subscriptions', false); - newIssue.updateData({ - subscribed: data.subscribed, - }); - }) - .catch(() => { - newIssue.setFetchingState('subscriptions', false); - Flash(__('An error occurred while fetching sidebar data')); - }); - } - - Store.detail.issue = newIssue; - }, - clearDetailIssue() { - Store.detail.issue = {}; - }, - toggleSubscription(id) { - const issue = Store.detail.issue; - if (issue.id === id && issue.toggleSubscriptionEndpoint) { - issue.setFetchingState('subscriptions', true); - BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) - .then(() => { - issue.setFetchingState('subscriptions', false); - issue.updateData({ - subscribed: !issue.subscribed, - }); - }) - .catch(() => { - issue.setFetchingState('subscriptions', false); - Flash(__('An error occurred when toggling the notification subscription')); - }); - } - } - }, - }); - - gl.IssueBoardsSearch = new Vue({ - el: document.getElementById('js-add-list'), - data: { - filters: Store.state.filters, - }, - mounted () { - gl.issueBoards.newListDropdownInit(); - }, - }); - - gl.IssueBoardsModalAddBtn = new Vue({ - el: document.getElementById('js-add-issues-btn'), - mixins: [gl.issueBoards.ModalMixins], - data() { - return { - modal: ModalStore.store, - store: Store.state, - }; - }, - computed: { - disabled() { - if (!this.store) { - return true; - } - return !this.store.lists.filter(list => !list.preset).length; - }, - tooltipTitle() { - if (this.disabled) { - return 'Please add a list to your board first'; - } - - return ''; - }, - }, - watch: { - disabled() { - this.updateTooltip(); - }, - }, - mounted() { - this.updateTooltip(); - }, - methods: { - updateTooltip() { - const $tooltip = $(this.$refs.addIssuesButton); - - this.$nextTick(() => { - if (this.disabled) { - $tooltip.tooltip(); - } else { - $tooltip.tooltip('destroy'); - } - }); - }, - openModal() { - if (!this.disabled) { - this.toggleModal(true); - } - }, - }, - template: ` -
- -
- `, - }); -}); diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js new file mode 100644 index 00000000000..7adbf442f00 --- /dev/null +++ b/app/assets/javascripts/boards/index.js @@ -0,0 +1,239 @@ +/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ + +import _ from 'underscore'; +import Vue from 'vue'; +import Flash from '../flash'; +import { __ } from '../locale'; +import FilteredSearchBoards from './filtered_search_boards'; +import eventHub from './eventhub'; +import sidebarEventHub from '../sidebar/event_hub'; +import './models/issue'; +import './models/label'; +import './models/list'; +import './models/milestone'; +import './models/assignee'; +import './stores/boards_store'; +import './stores/modal_store'; +import BoardService from './services/board_service'; +import './mixins/modal_mixins'; +import './mixins/sortable_default_options'; +import './filters/due_date_filters'; +import './components/board'; +import './components/board_sidebar'; +import './components/new_list_dropdown'; +import './components/modal/index'; +import '../vue_shared/vue_resource_interceptor'; + +export default function initBoards() { + const $boardApp = document.getElementById('board-app'); + const Store = gl.issueBoards.BoardsStore; + const ModalStore = gl.issueBoards.ModalStore; + + window.gl = window.gl || {}; + + if (gl.IssueBoardsApp) { + gl.IssueBoardsApp.$destroy(true); + } + + Store.create(); + + // hack to allow sidebar scripts like milestone_select manipulate the BoardsStore + gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args); + gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args); + + gl.IssueBoardsApp = new Vue({ + el: $boardApp, + components: { + 'board': gl.issueBoards.Board, + 'board-sidebar': gl.issueBoards.BoardSidebar, + 'board-add-issues-modal': gl.issueBoards.IssuesModal, + }, + data: { + state: Store.state, + loading: true, + boardsEndpoint: $boardApp.dataset.boardsEndpoint, + listsEndpoint: $boardApp.dataset.listsEndpoint, + boardId: $boardApp.dataset.boardId, + disabled: $boardApp.dataset.disabled === 'true', + issueLinkBase: $boardApp.dataset.issueLinkBase, + rootPath: $boardApp.dataset.rootPath, + bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, + detailIssue: Store.detail, + defaultAvatar: $boardApp.dataset.defaultAvatar, + }, + computed: { + detailIssueVisible () { + return Object.keys(this.detailIssue.issue).length; + }, + }, + created () { + gl.boardService = new BoardService({ + boardsEndpoint: this.boardsEndpoint, + listsEndpoint: this.listsEndpoint, + bulkUpdatePath: this.bulkUpdatePath, + boardId: this.boardId, + }); + Store.rootPath = this.boardsEndpoint; + + eventHub.$on('updateTokens', this.updateTokens); + eventHub.$on('newDetailIssue', this.updateDetailIssue); + eventHub.$on('clearDetailIssue', this.clearDetailIssue); + sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); + }, + beforeDestroy() { + eventHub.$off('updateTokens', this.updateTokens); + eventHub.$off('newDetailIssue', this.updateDetailIssue); + eventHub.$off('clearDetailIssue', this.clearDetailIssue); + sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); + }, + mounted () { + this.filterManager = new FilteredSearchBoards(Store.filter, true); + this.filterManager.setup(); + + Store.disabled = this.disabled; + gl.boardService.all() + .then(res => res.data) + .then((data) => { + data.forEach((board) => { + const list = Store.addList(board, this.defaultAvatar); + + if (list.type === 'closed') { + list.position = Infinity; + } else if (list.type === 'backlog') { + list.position = -1; + } + }); + + this.state.lists = _.sortBy(this.state.lists, 'position'); + + Store.addBlankState(); + this.loading = false; + }) + .catch(() => { + Flash('An error occurred while fetching the board lists. Please try again.'); + }); + }, + methods: { + updateTokens() { + this.filterManager.updateTokens(); + }, + updateDetailIssue(newIssue) { + const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint; + if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { + newIssue.setFetchingState('subscriptions', true); + BoardService.getIssueInfo(sidebarInfoEndpoint) + .then(res => res.data) + .then((data) => { + newIssue.setFetchingState('subscriptions', false); + newIssue.updateData({ + subscribed: data.subscribed, + }); + }) + .catch(() => { + newIssue.setFetchingState('subscriptions', false); + Flash(__('An error occurred while fetching sidebar data')); + }); + } + + Store.detail.issue = newIssue; + }, + clearDetailIssue() { + Store.detail.issue = {}; + }, + toggleSubscription(id) { + const issue = Store.detail.issue; + if (issue.id === id && issue.toggleSubscriptionEndpoint) { + issue.setFetchingState('subscriptions', true); + BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) + .then(() => { + issue.setFetchingState('subscriptions', false); + issue.updateData({ + subscribed: !issue.subscribed, + }); + }) + .catch(() => { + issue.setFetchingState('subscriptions', false); + Flash(__('An error occurred when toggling the notification subscription')); + }); + } + } + }, + }); + + gl.IssueBoardsSearch = new Vue({ + el: document.getElementById('js-add-list'), + data: { + filters: Store.state.filters, + }, + mounted () { + gl.issueBoards.newListDropdownInit(); + }, + }); + + gl.IssueBoardsModalAddBtn = new Vue({ + el: document.getElementById('js-add-issues-btn'), + mixins: [gl.issueBoards.ModalMixins], + data() { + return { + modal: ModalStore.store, + store: Store.state, + }; + }, + computed: { + disabled() { + if (!this.store) { + return true; + } + return !this.store.lists.filter(list => !list.preset).length; + }, + tooltipTitle() { + if (this.disabled) { + return 'Please add a list to your board first'; + } + + return ''; + }, + }, + watch: { + disabled() { + this.updateTooltip(); + }, + }, + mounted() { + this.updateTooltip(); + }, + methods: { + updateTooltip() { + const $tooltip = $(this.$refs.addIssuesButton); + + this.$nextTick(() => { + if (this.disabled) { + $tooltip.tooltip(); + } else { + $tooltip.tooltip('destroy'); + } + }); + }, + openModal() { + if (!this.disabled) { + this.toggleModal(true); + } + }, + }, + template: ` +
+ +
+ `, + }); +} diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f8082c74943..872ddcbbbe6 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -180,6 +180,11 @@ var Dispatcher; .then(callDefault) .catch(fail); break; + case 'projects:boards:index': + import('./pages/projects/boards') + .then(callDefault) + .catch(fail); + break; case 'projects:issues:new': import('./pages/projects/issues/new') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js index 3aeeedbb45d..0bda9fcc421 100644 --- a/app/assets/javascripts/pages/projects/boards/index.js +++ b/app/assets/javascripts/pages/projects/boards/index.js @@ -1,7 +1,9 @@ import UsersSelect from '~/users_select'; import ShortcutsNavigation from '~/shortcuts_navigation'; +import initBoards from '~/boards'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { new UsersSelect(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new -}); + initBoards(); +}; diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index ee8ad8e3999..de470730f5e 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -7,7 +7,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'filtered_search' - = webpack_bundle_tag 'boards' %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal diff --git a/config/webpack.config.js b/config/webpack.config.js index a4e6c64fce5..ba7003f3673 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -51,7 +51,6 @@ var config = { account: './profile/account/index.js', balsamiq_viewer: './blob/balsamiq_viewer.js', blob: './blob_edit/blob_bundle.js', - boards: './boards/boards_bundle.js', common: './commons/index.js', common_vue: './vue_shared/vue_resource_interceptor.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 9b2a5379855..96671041cd2 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -113,7 +113,7 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', - './boards/boards_bundle.js', + './boards/index.js', './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', -- cgit v1.2.1 From 7282dbea094e5dffa3af29f00e183d063a3717b1 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 16 Feb 2018 01:17:23 +0100 Subject: Fixed failing test --- app/assets/javascripts/dispatcher.js | 1 + spec/javascripts/test_bundle.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 872ddcbbbe6..99ac424c433 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -180,6 +180,7 @@ var Dispatcher; .then(callDefault) .catch(fail); break; + case 'projects:boards:show': case 'projects:boards:index': import('./pages/projects/boards') .then(callDefault) diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 96671041cd2..9d2dee990fd 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -113,7 +113,6 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', - './boards/index.js', './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', -- cgit v1.2.1 From 73e50395733285c0e6c8369c2354b166ce19ac40 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 26 Jan 2018 17:25:01 -0200 Subject: test merge request rebase --- .../components/states/mr_widget_ready_to_merge.js | 3 +- .../components/states/mr_widget_rebase.vue | 2 +- .../_merge_request_fast_forward_settings.html.haml | 2 +- app/views/projects/edit.html.haml | 2 +- qa/qa.rb | 2 + qa/qa/factory/base.rb | 2 +- qa/qa/factory/product.rb | 6 +-- qa/qa/factory/repository/push.rb | 4 +- qa/qa/factory/resource/merge_request.rb | 9 +++++ qa/qa/git/repository.rb | 4 ++ qa/qa/page/merge_request/show.rb | 46 ++++++++++++++++++++++ qa/qa/page/project/settings/merge_request.rb | 27 +++++++++++++ qa/qa/specs/features/merge_request/rebase_spec.rb | 39 ++++++++++++++++++ qa/spec/factory/base_spec.rb | 17 ++++++-- qa/spec/factory/product_spec.rb | 16 +++++++- 15 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 qa/qa/page/merge_request/show.rb create mode 100644 qa/qa/page/project/settings/merge_request.rb create mode 100644 qa/qa/specs/features/merge_request/rebase_spec.rb diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js index 7ba6c29006a..162f048aac7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js @@ -227,7 +227,8 @@ export default { @click="handleMergeButtonClick()" :disabled="isMergeButtonDisabled" :class="mergeButtonClass" - type="button"> + type="button" + class="qa-merge-button"> - -
  • -
  • - -
  • - - - - `, -}; diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue new file mode 100644 index 00000000000..26618af9515 --- /dev/null +++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue @@ -0,0 +1,104 @@ + + diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js index c99ed63c4af..f9338b82acf 100644 --- a/app/assets/javascripts/filtered_search/recent_searches_root.js +++ b/app/assets/javascripts/filtered_search/recent_searches_root.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import RecentSearchesDropdownContent from './components/recent_searches_dropdown_content'; +import RecentSearchesDropdownContent from './components/recent_searches_dropdown_content.vue'; import eventHub from './event_hub'; class RecentSearchesRoot { @@ -33,7 +33,7 @@ class RecentSearchesRoot { this.vm = new Vue({ el: this.wrapperElement, components: { - 'recent-searches-dropdown-content': RecentSearchesDropdownContent, + RecentSearchesDropdownContent, }, data() { return state; }, template: ` diff --git a/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml b/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml new file mode 100644 index 00000000000..d65318d7ba1 --- /dev/null +++ b/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move RecentSearchesDropdownContent vue component +merge_request: 16951 +author: George Tsiolis +type: performance diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb index f355cec3ba9..41b9ada988a 100644 --- a/spec/features/issues/filtered_search/recent_searches_spec.rb +++ b/spec/features/issues/filtered_search/recent_searches_spec.rb @@ -39,8 +39,8 @@ describe 'Recent searches', :js do items = all('.filtered-search-history-dropdown-item', visible: false, count: 2) - expect(items[0].text).to eq('label:~qux garply') - expect(items[1].text).to eq('label:~foo bar') + expect(items[0].text).to eq('label: ~qux garply') + expect(items[1].text).to eq('label: ~foo bar') end it 'saved recent searches are restored last on the list' do diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 4a516c517ef..59bd2650081 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -1,7 +1,6 @@ import Vue from 'vue'; import eventHub from '~/filtered_search/event_hub'; -import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content'; - +import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue'; import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; const createComponent = (propsData) => { -- cgit v1.2.1 From 53209a00862d54528cf141b61c5e877c20bd61a2 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Feb 2018 12:27:50 +0530 Subject: Fix assignee avatar and notes icon alignment --- app/assets/stylesheets/pages/issues.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 6763af4e98b..b9390450477 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -13,10 +13,20 @@ display: inline-block; } + .issuable-meta { + .author_link { + display: inline-block; + } + + .issuable-comments { + height: 18px; + } + } + .icon-merge-request-unmerged { height: 13px; margin-bottom: 3px; - } + } } } -- cgit v1.2.1 From 699607f2e50c7c71742205833d088e8cd5de1919 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 5 Feb 2018 16:25:31 +0100 Subject: initial refactor --- app/helpers/blob_helper.rb | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index a6e1de6ffdc..0bd1ee7c5f2 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,10 +17,7 @@ module BlobHelper end def edit_blob_link(project = @project, ref = @ref, path = @path, options = {}) - blob = options.delete(:blob) - blob ||= project.repository.blob_at(ref, path) rescue nil - - return unless blob && blob.readable_text? + return unless readable_blob?(options, path, project, ref) common_classes = "btn js-edit-blob #{options[:extra_class]}" @@ -30,16 +27,7 @@ module BlobHelper elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) link_to 'Edit', edit_blob_path(project, ref, path, options), class: "#{common_classes} btn-sm" elsif current_user && can?(current_user, :fork_project, project) - continue_params = { - to: edit_blob_path(project, ref, path, options), - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now - } - fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - - button_tag 'Edit', - class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: 'edit', fork_path: fork_path } + edit_blob_fork(common_classes, options, path, project, ref) end end @@ -332,4 +320,24 @@ module BlobHelper options end + + def readable_blob?(options, path, project, ref) + blob = options.delete(:blob) + blob ||= project.repository.blob_at(ref, path) rescue nil + + blob && blob.readable_text? + end + + def edit_blob_fork(common_classes, options, path, project, ref) + continue_params = { + to: edit_blob_path(project, ref, path, options), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now + } + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) + + button_tag 'Edit', + class: "#{common_classes} js-edit-blob-link-fork-toggler", + data: { action: 'edit', fork_path: fork_path } + end end -- cgit v1.2.1 From cb31b369091e00ad3639bfde0fdd184d20619e7e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 7 Feb 2018 11:43:30 +0100 Subject: a bit more refactoring --- app/helpers/blob_helper.rb | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 0bd1ee7c5f2..cba87176631 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -41,11 +41,7 @@ module BlobHelper def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? - - blob = options.delete(:blob) - blob ||= project.repository.blob_at(ref, path) rescue nil - - return unless blob && blob.readable_text? + return unless readable_blob?(options, path, project, ref) common_classes = "btn js-edit-ide #{options[:extra_class]}" @@ -55,16 +51,7 @@ module BlobHelper elsif current_user && can_modify_blob?(blob, project, ref) link_to ide_edit_text, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" elsif current_user && can?(current_user, :fork_project, project) - continue_params = { - to: ide_edit_path(project, ref, path, options), - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now - } - fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - - button_tag ide_edit_text, - class: common_classes, - data: { fork_path: fork_path } + edit_blob_fork(common_classes, options, path, project, ref) end end @@ -84,16 +71,7 @@ module BlobHelper elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) - continue_params = { - to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to #{action} this file again.", - notice_now: edit_in_new_fork_notice_now - } - fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - - button_tag label, - class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: action, fork_path: fork_path } + edit_blob_fork(common_classes, options, path, project, ref) end end -- cgit v1.2.1 From b8d7367ad0049dd3cb230d3439831037c4c25ed0 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 8 Feb 2018 11:58:47 +0100 Subject: refactor methods further (in helper) --- app/helpers/blob_helper.rb | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index cba87176631..16b9be9a916 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -22,10 +22,10 @@ module BlobHelper common_classes = "btn js-edit-blob #{options[:extra_class]}" if !on_top_of_branch?(project, ref) - button_tag 'Edit', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } + edit_button_tag(edit_text, common_classes) # This condition applies to anonymous or users who can edit directly elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) - link_to 'Edit', edit_blob_path(project, ref, path, options), class: "#{common_classes} btn-sm" + edit_link_tag(edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) edit_blob_fork(common_classes, options, path, project, ref) end @@ -35,8 +35,12 @@ module BlobHelper "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" end + def edit_text + _('Edit') + end + def ide_edit_text - "#{_('Web IDE')}" + _('Web IDE') end def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) @@ -46,10 +50,11 @@ module BlobHelper common_classes = "btn js-edit-ide #{options[:extra_class]}" if !on_top_of_branch?(project, ref) - button_tag ide_edit_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' } - # This condition applies to anonymous or users who can edit directly + edit_button_tag(ide_edit_text, common_classes) + # This condition only applies to users who are logged in + # Web IDE (Beta) requires the user to have this feature enabled elsif current_user && can_modify_blob?(blob, project, ref) - link_to ide_edit_text, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) edit_blob_fork(common_classes, options, path, project, ref) end @@ -314,8 +319,16 @@ module BlobHelper } fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - button_tag 'Edit', + button_tag edit_text, class: "#{common_classes} js-edit-blob-link-fork-toggler", data: { action: 'edit', fork_path: fork_path } end + + def edit_button_tag(button_text, common_classes) + button_tag(button_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' }) + end + + def edit_link_tag(link_text, edit_path, common_classes) + link_to link_text, edit_path, class: "#{common_classes} btn-sm" + end end -- cgit v1.2.1 From 9053f8eb4b94669d18674552b8f588071d8c76c6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 8 Feb 2018 14:12:44 +0100 Subject: fix specs --- app/helpers/blob_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 16b9be9a916..8546e72a8a9 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,7 +17,7 @@ module BlobHelper end def edit_blob_link(project = @project, ref = @ref, path = @path, options = {}) - return unless readable_blob?(options, path, project, ref) + return unless blob = readable_blob(options, path, project, ref) common_classes = "btn js-edit-blob #{options[:extra_class]}" @@ -45,7 +45,7 @@ module BlobHelper def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? - return unless readable_blob?(options, path, project, ref) + return unless blob = readable_blob(options, path, project, ref) common_classes = "btn js-edit-ide #{options[:extra_class]}" @@ -304,11 +304,11 @@ module BlobHelper options end - def readable_blob?(options, path, project, ref) + def readable_blob(options, path, project, ref) blob = options.delete(:blob) blob ||= project.repository.blob_at(ref, path) rescue nil - blob && blob.readable_text? + blob if blob&.readable_text? end def edit_blob_fork(common_classes, options, path, project, ref) -- cgit v1.2.1 From 6d885f9c92cdb6e2d8ad332af2490d25269b5f9f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 8 Feb 2018 15:45:12 +0100 Subject: some more refactoring --- app/helpers/blob_helper.rb | 12 ++++++------ app/helpers/tree_helper.rb | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 8546e72a8a9..3f187a41735 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -27,7 +27,7 @@ module BlobHelper elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) edit_link_tag(edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, options, path, project, ref) + edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project, edit_in_new_fork_notice) end end @@ -56,7 +56,7 @@ module BlobHelper elsif current_user && can_modify_blob?(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, options, path, project, ref) + edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project, edit_in_new_fork_notice) end end @@ -76,7 +76,7 @@ module BlobHelper elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, options, path, project, ref) + edit_blob_fork(common_classes, request.fullpath, project, edit_in_new_fork_notice_action(action), action) end end @@ -311,10 +311,10 @@ module BlobHelper blob if blob&.readable_text? end - def edit_blob_fork(common_classes, options, path, project, ref) + def edit_blob_fork(common_classes, path, project, notice, action = 'edit') continue_params = { - to: edit_blob_path(project, ref, path, options), - notice: edit_in_new_fork_notice, + to: path, + notice: notice, notice_now: edit_in_new_fork_notice_now } fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index f5733b4b57c..f6a6d9bebde 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -83,6 +83,10 @@ module TreeHelper " A fork of this project has been created that you can make changes in, so you can submit a merge request." end + def edit_in_new_fork_notice_action(action) + edit_in_new_fork_notice + " Try to #{action} this file again." + end + def commit_in_fork_help "A new branch will be created in your fork and a new merge request will be started." end -- cgit v1.2.1 From 5fc3ff98930f74a694d40cc3533138fb06964bf1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 8 Feb 2018 16:12:28 +0100 Subject: fix specs --- app/helpers/blob_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 3f187a41735..2822fa8144a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -321,7 +321,7 @@ module BlobHelper button_tag edit_text, class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: 'edit', fork_path: fork_path } + data: { action: action, fork_path: fork_path } end def edit_button_tag(button_text, common_classes) -- cgit v1.2.1 From 4c8f8f4fe9248567e4b1a5ef5a6bc049513751a4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Feb 2018 08:59:03 +0100 Subject: fix spec --- app/helpers/blob_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 2822fa8144a..b54dd93cc08 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -12,8 +12,8 @@ module BlobHelper def edit_blob_path(project = @project, ref = @ref, path = @path, options = {}) project_edit_blob_path(project, - tree_join(ref, path), - options[:link_opts]) + tree_join(ref, path), + options[:link_opts]) end def edit_blob_link(project = @project, ref = @ref, path = @path, options = {}) @@ -25,7 +25,7 @@ module BlobHelper edit_button_tag(edit_text, common_classes) # This condition applies to anonymous or users who can edit directly elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) - edit_link_tag(edit_text, ide_edit_path(project, ref, path, options), common_classes) + edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project, edit_in_new_fork_notice) end -- cgit v1.2.1 From f13609663912e940e860b8f7b5e8aa98c99800e9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Feb 2018 10:32:28 +0100 Subject: refactor modify_file_link --- app/helpers/blob_helper.rb | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index b54dd93cc08..848a59d3756 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -27,7 +27,7 @@ module BlobHelper elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project, edit_in_new_fork_notice) + edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) end end @@ -56,7 +56,7 @@ module BlobHelper elsif current_user && can_modify_blob?(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif current_user && can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project, edit_in_new_fork_notice) + edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) end end @@ -76,7 +76,7 @@ module BlobHelper elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) - edit_blob_fork(common_classes, request.fullpath, project, edit_in_new_fork_notice_action(action), action) + edit_modify_file_fork(action, common_classes, label, project) end end @@ -311,15 +311,28 @@ module BlobHelper blob if blob&.readable_text? end - def edit_blob_fork(common_classes, path, project, notice, action = 'edit') + def edit_blob_fork(common_classes, path, project) continue_params = { to: path, - notice: notice, + notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) button_tag edit_text, + class: "#{common_classes} js-edit-blob-link-fork-toggler", + data: { action: 'edit', fork_path: fork_path } + end + + def edit_modify_file_fork(action, common_classes, label, project) + continue_params = { + to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to #{action} this file again.", + notice_now: edit_in_new_fork_notice_now + } + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) + + button_tag label, class: "#{common_classes} js-edit-blob-link-fork-toggler", data: { action: action, fork_path: fork_path } end -- cgit v1.2.1 From bee837d7b5025a2e170dc0c857b188b4d89c014f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Feb 2018 08:53:09 +0100 Subject: some initial refactoring --- app/helpers/blob_helper.rb | 26 +++++++++++++++++--------- app/views/projects/blob/_header.html.haml | 4 ++-- app/views/projects/diffs/_file.html.haml | 2 +- spec/helpers/blob_helper_spec.rb | 10 +++++----- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 848a59d3756..a5fba39630a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -16,7 +16,7 @@ module BlobHelper options[:link_opts]) end - def edit_blob_link(project = @project, ref = @ref, path = @path, options = {}) + def edit_blob_element(project = @project, ref = @ref, path = @path, options = {}) return unless blob = readable_blob(options, path, project, ref) common_classes = "btn js-edit-blob #{options[:extra_class]}" @@ -24,13 +24,17 @@ module BlobHelper if !on_top_of_branch?(project, ref) edit_button_tag(edit_text, common_classes) # This condition applies to anonymous or users who can edit directly - elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) + elsif !current_user || user_can_modify_blob(blob, project, ref) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) - elsif current_user && can?(current_user, :fork_project, project) + elsif user_can_fork_project(project) edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) end end + def user_can_fork_project(project) + current_user && can?(current_user, :fork_project, project) + end + def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" end @@ -43,7 +47,7 @@ module BlobHelper _('Web IDE') end - def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) + def ide_edit_element(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? return unless blob = readable_blob(options, path, project, ref) @@ -53,14 +57,18 @@ module BlobHelper edit_button_tag(ide_edit_text, common_classes) # This condition only applies to users who are logged in # Web IDE (Beta) requires the user to have this feature enabled - elsif current_user && can_modify_blob?(blob, project, ref) + elsif user_can_modify_blob(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) - elsif current_user && can?(current_user, :fork_project, project) + elsif user_can_fork_project(project) edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) end end - def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) + def user_can_modify_blob(blob, project, ref) + current_user && can_modify_blob?(blob, project, ref) + end + + def modify_file_element(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) return unless current_user blob = project.repository.blob_at(ref, path) rescue nil @@ -81,7 +89,7 @@ module BlobHelper end def replace_blob_link(project = @project, ref = @ref, path = @path) - modify_file_link( + modify_file_element( project, ref, path, @@ -93,7 +101,7 @@ module BlobHelper end def delete_blob_link(project = @project, ref = @ref, path = @path) - modify_file_link( + modify_file_element( project, ref, path, diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 2a77dedd9a2..da0c0ed6cb4 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -11,8 +11,8 @@ = view_on_environment_button(@commit.sha, @path, @environment) if @environment .btn-group{ role: "group" }< - = edit_blob_link - = ide_blob_link + = edit_blob_element + = ide_edit_element - if current_user = replace_blob_link = delete_blob_link diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 0b01e38d23d..3e6001e6f30 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -17,7 +17,7 @@ \ - if editable_diff?(diff_file) - link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {} - = edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, + = edit_blob_element(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, blob: blob, link_opts: link_opts) - if image_diff && image_replaced diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index a030796c54e..d1968eac3e3 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -74,7 +74,7 @@ describe BlobHelper do end end - describe "#edit_blob_link" do + describe "#edit_blob_element" do let(:namespace) { create(:namespace, name: 'gitlab' )} let(:project) { create(:project, :repository, namespace: namespace) } @@ -86,7 +86,7 @@ describe BlobHelper do it 'verifies blob is text' do expect(helper).not_to receive(:blob_text_viewable?) - button = edit_blob_link(project, 'refs/heads/master', 'README.md') + button = edit_blob_element(project, 'refs/heads/master', 'README.md') expect(button).to start_with(' Date: Thu, 15 Feb 2018 09:44:08 +0100 Subject: more refactoring --- app/helpers/blob_helper.rb | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index a5fba39630a..7e40e554c60 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -27,7 +27,7 @@ module BlobHelper elsif !current_user || user_can_modify_blob(blob, project, ref) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) elsif user_can_fork_project(project) - edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) + edit_fork_button_tag(common_classes, project, edit_text, edit_blob_fork_params(path)) end end @@ -60,7 +60,7 @@ module BlobHelper elsif user_can_modify_blob(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif user_can_fork_project(project) - edit_blob_fork(common_classes, edit_blob_path(project, ref, path, options), project) + edit_fork_button_tag(common_classes, project, ide_edit_text, edit_blob_fork_params(path)) end end @@ -84,7 +84,7 @@ module BlobHelper elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) - edit_modify_file_fork(action, common_classes, label, project) + edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action)) end end @@ -319,30 +319,28 @@ module BlobHelper blob if blob&.readable_text? end - def edit_blob_fork(common_classes, path, project) - continue_params = { + def edit_blob_fork_params(path) + { to: path, notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } - fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - - button_tag edit_text, - class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: 'edit', fork_path: fork_path } end - def edit_modify_file_fork(action, common_classes, label, project) - continue_params = { - to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to #{action} this file again.", + def edit_modify_file_fork_params(action) + { + to: request.full_path, + notice: edit_in_new_fork_notice_action(action), notice_now: edit_in_new_fork_notice_now } - fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) + end + + def edit_fork_button_tag(common_classes, project, label, params) + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: params) - button_tag label, + button_tag edit_text, class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: action, fork_path: fork_path } + data: { action: 'edit', fork_path: fork_path } end def edit_button_tag(button_text, common_classes) -- cgit v1.2.1 From 50ccc6dcc79d5864f5fb9e18e2c586fd3e5f9218 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Feb 2018 10:24:42 +0100 Subject: fix specs --- app/helpers/blob_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 7e40e554c60..d520f2ed068 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -329,7 +329,7 @@ module BlobHelper def edit_modify_file_fork_params(action) { - to: request.full_path, + to: request.fullpath, notice: edit_in_new_fork_notice_action(action), notice_now: edit_in_new_fork_notice_now } -- cgit v1.2.1 From 6ade6e2bb6a937c08da9a1bc62f62e92ad2ab06e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Feb 2018 12:03:37 +0100 Subject: fix specs --- app/helpers/blob_helper.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index d520f2ed068..1c076321cb7 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -84,7 +84,7 @@ module BlobHelper elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) - edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action)) + edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action), action) end end @@ -331,16 +331,17 @@ module BlobHelper { to: request.fullpath, notice: edit_in_new_fork_notice_action(action), - notice_now: edit_in_new_fork_notice_now + notice_now: edit_in_new_fork_notice_now, + action: action } end - def edit_fork_button_tag(common_classes, project, label, params) + def edit_fork_button_tag(common_classes, project, label, params, action = 'edit') fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: params) - button_tag edit_text, + button_tag label, class: "#{common_classes} js-edit-blob-link-fork-toggler", - data: { action: 'edit', fork_path: fork_path } + data: { action: action, fork_path: fork_path } end def edit_button_tag(button_text, common_classes) -- cgit v1.2.1 From 31abc74ff427f0b350b7121d5e6a7394daf7bf2f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Feb 2018 13:42:33 +0100 Subject: fix specs --- app/helpers/blob_helper.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 1c076321cb7..9c682a856dd 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -27,7 +27,9 @@ module BlobHelper elsif !current_user || user_can_modify_blob(blob, project, ref) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) elsif user_can_fork_project(project) - edit_fork_button_tag(common_classes, project, edit_text, edit_blob_fork_params(path)) + edit_fork_button_tag(common_classes, + project, edit_text, + edit_blob_fork_params(edit_blob_path(project, ref, path, options))) end end @@ -60,7 +62,10 @@ module BlobHelper elsif user_can_modify_blob(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) elsif user_can_fork_project(project) - edit_fork_button_tag(common_classes, project, ide_edit_text, edit_blob_fork_params(path)) + edit_fork_button_tag(common_classes, + project, + ide_edit_text, + edit_blob_fork_params(ide_edit_path(project, ref, path, options))) end end @@ -332,7 +337,6 @@ module BlobHelper to: request.fullpath, notice: edit_in_new_fork_notice_action(action), notice_now: edit_in_new_fork_notice_now, - action: action } end -- cgit v1.2.1 From 9753396fe2da21fdb1cc7656e4bf33e28f374363 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Feb 2018 14:50:00 +0100 Subject: fix static analysis --- app/helpers/blob_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 9c682a856dd..3c45f670015 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -336,7 +336,7 @@ module BlobHelper { to: request.fullpath, notice: edit_in_new_fork_notice_action(action), - notice_now: edit_in_new_fork_notice_now, + notice_now: edit_in_new_fork_notice_now } end -- cgit v1.2.1 From 228b757d29a141c4fda0e7c4be75784a6cefe427 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 19 Feb 2018 11:38:31 +0100 Subject: refactor blob link methods --- app/helpers/blob_helper.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 3c45f670015..6322eb1b671 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -24,19 +24,24 @@ module BlobHelper if !on_top_of_branch?(project, ref) edit_button_tag(edit_text, common_classes) # This condition applies to anonymous or users who can edit directly - elsif !current_user || user_can_modify_blob(blob, project, ref) + elsif !current_user || user_can_modify_blob?(blob, project, ref) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) - elsif user_can_fork_project(project) + elsif user_can_fork_project?(project) edit_fork_button_tag(common_classes, - project, edit_text, + project, + edit_text, edit_blob_fork_params(edit_blob_path(project, ref, path, options))) end end - def user_can_fork_project(project) + def user_can_fork_project?(project) current_user && can?(current_user, :fork_project, project) end + def user_can_modify_blob?(blob, project, ref) + current_user && can_modify_blob?(blob, project, ref) + end + def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" end @@ -59,9 +64,9 @@ module BlobHelper edit_button_tag(ide_edit_text, common_classes) # This condition only applies to users who are logged in # Web IDE (Beta) requires the user to have this feature enabled - elsif user_can_modify_blob(blob, project, ref) + elsif user_can_modify_blob?(blob, project, ref) edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) - elsif user_can_fork_project(project) + elsif user_can_fork_project?(project) edit_fork_button_tag(common_classes, project, ide_edit_text, @@ -69,10 +74,6 @@ module BlobHelper end end - def user_can_modify_blob(blob, project, ref) - current_user && can_modify_blob?(blob, project, ref) - end - def modify_file_element(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) return unless current_user -- cgit v1.2.1 From 0767861bcdc62cedf374c6a4b53d8526efb4ff58 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 22 Feb 2018 13:54:19 +0100 Subject: refactor code based on feedback --- app/helpers/blob_helper.rb | 22 +++++++++++----------- app/views/projects/blob/_header.html.haml | 4 ++-- app/views/projects/diffs/_file.html.haml | 2 +- spec/helpers/blob_helper_spec.rb | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 6322eb1b671..93e2a752547 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -16,13 +16,13 @@ module BlobHelper options[:link_opts]) end - def edit_blob_element(project = @project, ref = @ref, path = @path, options = {}) + def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) return unless blob = readable_blob(options, path, project, ref) common_classes = "btn js-edit-blob #{options[:extra_class]}" if !on_top_of_branch?(project, ref) - edit_button_tag(edit_text, common_classes) + edit_disabled_button_tag(edit_text, common_classes) # This condition applies to anonymous or users who can edit directly elsif !current_user || user_can_modify_blob?(blob, project, ref) edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) @@ -54,14 +54,14 @@ module BlobHelper _('Web IDE') end - def ide_edit_element(project = @project, ref = @ref, path = @path, options = {}) + def ide_edit_button(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? return unless blob = readable_blob(options, path, project, ref) common_classes = "btn js-edit-ide #{options[:extra_class]}" if !on_top_of_branch?(project, ref) - edit_button_tag(ide_edit_text, common_classes) + edit_disabled_button_tag(ide_edit_text, common_classes) # This condition only applies to users who are logged in # Web IDE (Beta) requires the user to have this feature enabled elsif user_can_modify_blob?(blob, project, ref) @@ -327,17 +327,17 @@ module BlobHelper def edit_blob_fork_params(path) { - to: path, - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now + to: path, + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now } end def edit_modify_file_fork_params(action) { - to: request.fullpath, - notice: edit_in_new_fork_notice_action(action), - notice_now: edit_in_new_fork_notice_now + to: request.fullpath, + notice: edit_in_new_fork_notice_action(action), + notice_now: edit_in_new_fork_notice_now } end @@ -349,7 +349,7 @@ module BlobHelper data: { action: action, fork_path: fork_path } end - def edit_button_tag(button_text, common_classes) + def edit_disabled_button_tag(button_text, common_classes) button_tag(button_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' }) end diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index da0c0ed6cb4..1b150ec3e5c 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -11,8 +11,8 @@ = view_on_environment_button(@commit.sha, @path, @environment) if @environment .btn-group{ role: "group" }< - = edit_blob_element - = ide_edit_element + = edit_blob_button + = ide_edit_button - if current_user = replace_blob_link = delete_blob_link diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 3e6001e6f30..47bfcb21cf4 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -17,7 +17,7 @@ \ - if editable_diff?(diff_file) - link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {} - = edit_blob_element(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, + = edit_blob_button(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, blob: blob, link_opts: link_opts) - if image_diff && image_replaced diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index d1968eac3e3..0548e71c89d 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -86,7 +86,7 @@ describe BlobHelper do it 'verifies blob is text' do expect(helper).not_to receive(:blob_text_viewable?) - button = edit_blob_element(project, 'refs/heads/master', 'README.md') + button = edit_blob_button(project, 'refs/heads/master', 'README.md') expect(button).to start_with(' Date: Fri, 23 Feb 2018 09:09:32 +0100 Subject: refactor code based on feedback --- app/helpers/blob_helper.rb | 51 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 93e2a752547..67196d09b48 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -21,25 +21,20 @@ module BlobHelper common_classes = "btn js-edit-blob #{options[:extra_class]}" - if !on_top_of_branch?(project, ref) - edit_disabled_button_tag(edit_text, common_classes) - # This condition applies to anonymous or users who can edit directly - elsif !current_user || user_can_modify_blob?(blob, project, ref) - edit_link_tag(edit_text, edit_blob_path(project, ref, path, options), common_classes) - elsif user_can_fork_project?(project) - edit_fork_button_tag(common_classes, - project, - edit_text, - edit_blob_fork_params(edit_blob_path(project, ref, path, options))) - end + edit_button_tag(blob, + common_classes, + edit_text, + edit_blob_path(project, ref, path, options), + project, + ref) end def user_can_fork_project?(project) current_user && can?(current_user, :fork_project, project) end - def user_can_modify_blob?(blob, project, ref) - current_user && can_modify_blob?(blob, project, ref) + def display_modify_blob?(blob, project, ref) + !current_user || (current_user && can_modify_blob?(blob, project, ref)) end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) @@ -60,18 +55,12 @@ module BlobHelper common_classes = "btn js-edit-ide #{options[:extra_class]}" - if !on_top_of_branch?(project, ref) - edit_disabled_button_tag(ide_edit_text, common_classes) - # This condition only applies to users who are logged in - # Web IDE (Beta) requires the user to have this feature enabled - elsif user_can_modify_blob?(blob, project, ref) - edit_link_tag(ide_edit_text, ide_edit_path(project, ref, path, options), common_classes) - elsif user_can_fork_project?(project) - edit_fork_button_tag(common_classes, - project, - ide_edit_text, - edit_blob_fork_params(ide_edit_path(project, ref, path, options))) - end + edit_button_tag(blob, + common_classes, + ide_edit_text, + ide_edit_path(project, ref, path, options), + project, + ref) end def modify_file_element(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) @@ -356,4 +345,16 @@ module BlobHelper def edit_link_tag(link_text, edit_path, common_classes) link_to link_text, edit_path, class: "#{common_classes} btn-sm" end + + def edit_button_tag(blob, common_classes, text, edit_path, project, ref) + if !on_top_of_branch?(project, ref) + edit_disabled_button_tag(text, common_classes) + # This condition only applies to users who are logged in + # Web IDE (Beta) requires the user to have this feature enabled + elsif display_modify_blob?(blob, project, ref) + edit_link_tag(text, edit_path, common_classes) + elsif user_can_fork_project?(project) + edit_fork_button_tag(common_classes, project, text, edit_path) + end + end end -- cgit v1.2.1 From b8028c6ccc0de02d7a6d97471a555d0e58e59b29 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 23 Feb 2018 13:58:27 +0100 Subject: fix fork button issue --- app/helpers/blob_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 67196d09b48..40a6a7ca574 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -354,7 +354,7 @@ module BlobHelper elsif display_modify_blob?(blob, project, ref) edit_link_tag(text, edit_path, common_classes) elsif user_can_fork_project?(project) - edit_fork_button_tag(common_classes, project, text, edit_path) + edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path)) end end end -- cgit v1.2.1 From 801e42715c801c8460849a8ae18a3272515f36ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Fri, 23 Feb 2018 13:07:25 +0100 Subject: Minimal fix for artifacts service --- app/services/ci/create_trace_artifact_service.rb | 31 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index 280a2c3afa4..bfc5c1c0732 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -4,13 +4,34 @@ module Ci return if job.job_artifacts_trace job.trace.read do |stream| - if stream.file? - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: stream) + return unless stream.file? + + temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| + FileUtils.cp(stream.path, temp_path) + create_job_trace!(temp_path) + FileUtils.rm(stream.path) end end end + + private + + def create_job_trace!(path) + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: UploadedFile.new(path, 'build.log', 'application/octet-stream') + ) + end + + def temp_file!(temp_dir) + FileUtils.mkdir_p(temp_dir) + temp_file = Tempfile.new('file', temp_dir) + temp_file&.close + yield(temp_file.path) + ensure + temp_file&.close + temp_file&.unlink + end end end -- cgit v1.2.1 From 0fca3bce812f2c37891b62f80b754099d369f92f Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 23 Feb 2018 23:31:01 +0900 Subject: Copy original file to temp file always to prevent data loss --- app/services/ci/create_trace_artifact_service.rb | 8 ++--- .../ci/create_trace_artifact_service_spec.rb | 42 +++++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index bfc5c1c0732..d31d8b63da6 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -8,7 +8,7 @@ module Ci temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| FileUtils.cp(stream.path, temp_path) - create_job_trace!(temp_path) + create_job_trace!(job, temp_path) FileUtils.rm(stream.path) end end @@ -16,17 +16,17 @@ module Ci private - def create_job_trace!(path) + def create_job_trace!(job, path) job.create_job_artifacts_trace!( project: job.project, file_type: :trace, - file: UploadedFile.new(path, 'build.log', 'application/octet-stream') + file: UploadedFile.new(path, 'job.log', 'application/octet-stream') ) end def temp_file!(temp_dir) FileUtils.mkdir_p(temp_dir) - temp_file = Tempfile.new('file', temp_dir) + temp_file = Tempfile.new('legacy-trace-tmp-', temp_dir) temp_file&.close yield(temp_file.path) ensure diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb index 847a88920fe..0628e6d112e 100644 --- a/spec/services/ci/create_trace_artifact_service_spec.rb +++ b/spec/services/ci/create_trace_artifact_service_spec.rb @@ -4,40 +4,56 @@ describe Ci::CreateTraceArtifactService do describe '#execute' do subject { described_class.new(nil, nil).execute(job) } - let(:job) { create(:ci_build) } - context 'when the job does not have trace artifact' do context 'when the job has a trace file' do - before do - allow_any_instance_of(Gitlab::Ci::Trace) - .to receive(:default_path) { expand_fixture_path('trace/sample_trace') } + let!(:job) { create(:ci_build, :trace_live) } + let!(:legacy_path) { job.trace.read { |stream| return stream.path } } - allow_any_instance_of(JobArtifactUploader).to receive(:move_to_cache) { false } - allow_any_instance_of(JobArtifactUploader).to receive(:move_to_store) { false } - end + it { expect(File.exists?(legacy_path)).to be_truthy } it 'creates trace artifact' do expect { subject }.to change { Ci::JobArtifact.count }.by(1) - expect(job.job_artifacts_trace.read_attribute(:file)).to eq('sample_trace') + expect(File.exists?(legacy_path)).to be_falsy + expect(File.exists?(job.job_artifacts_trace.file.path)).to be_truthy + expect(job.job_artifacts_trace.exists?).to be_truthy + expect(job.job_artifacts_trace.file.filename).to eq('job.log') end - context 'when the job has already had trace artifact' do + context 'when failed to create trace artifact record' do before do - create(:ci_job_artifact, :trace, job: job) + # When ActiveRecord error happens + allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false) + allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages) + .and_return("Error") + + subject rescue nil + + job.reload end - it 'does not create trace artifact' do - expect { subject }.not_to change { Ci::JobArtifact.count } + it 'keeps legacy trace and removes trace artifact' do + expect(File.exists?(legacy_path)).to be_truthy + expect(job.job_artifacts_trace).to be_nil end end end context 'when the job does not have a trace file' do + let!(:job) { create(:ci_build) } + it 'does not create trace artifact' do expect { subject }.not_to change { Ci::JobArtifact.count } end end end + + context 'when the job has already had trace artifact' do + let!(:job) { create(:ci_build, :trace_artifact) } + + it 'does not create trace artifact' do + expect { subject }.not_to change { Ci::JobArtifact.count } + end + end end end -- cgit v1.2.1 From 414a638bc2460274e5b977094c327b7be6747576 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 23 Feb 2018 23:58:58 +0900 Subject: Check if the latest permanent file exists before remove file. Add tests. --- app/services/ci/create_trace_artifact_service.rb | 27 +++++++++++----------- .../ci/create_trace_artifact_service_spec.rb | 20 ++++++++++++++-- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index d31d8b63da6..00151f4bd1c 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -6,28 +6,27 @@ module Ci job.trace.read do |stream| return unless stream.file? - temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| - FileUtils.cp(stream.path, temp_path) - create_job_trace!(job, temp_path) - FileUtils.rm(stream.path) + temp_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |temp_path| + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: UploadedFile.new(temp_path, 'job.log', 'application/octet-stream') + ) end + + raise 'Trace artifact not found' unless job.job_artifacts_trace.file.exists? + + FileUtils.rm(stream.path) end end private - def create_job_trace!(job, path) - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: UploadedFile.new(path, 'job.log', 'application/octet-stream') - ) - end - - def temp_file!(temp_dir) + def temp_file!(src_file, temp_dir) FileUtils.mkdir_p(temp_dir) - temp_file = Tempfile.new('legacy-trace-tmp-', temp_dir) + temp_file = Tempfile.new('trace-tmp-', temp_dir) temp_file&.close + FileUtils.cp(src_file, temp_file.path) yield(temp_file.path) ensure temp_file&.close diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb index 0628e6d112e..f928deb8632 100644 --- a/spec/services/ci/create_trace_artifact_service_spec.rb +++ b/spec/services/ci/create_trace_artifact_service_spec.rb @@ -8,6 +8,9 @@ describe Ci::CreateTraceArtifactService do context 'when the job has a trace file' do let!(:job) { create(:ci_build, :trace_live) } let!(:legacy_path) { job.trace.read { |stream| return stream.path } } + let!(:legacy_checksum) { Digest::SHA256.file(legacy_path).hexdigest } + let(:new_path) { job.job_artifacts_trace.file.path } + let(:new_checksum) { Digest::SHA256.file(new_path).hexdigest } it { expect(File.exists?(legacy_path)).to be_truthy } @@ -15,8 +18,9 @@ describe Ci::CreateTraceArtifactService do expect { subject }.to change { Ci::JobArtifact.count }.by(1) expect(File.exists?(legacy_path)).to be_falsy - expect(File.exists?(job.job_artifacts_trace.file.path)).to be_truthy - expect(job.job_artifacts_trace.exists?).to be_truthy + expect(File.exists?(new_path)).to be_truthy + expect(new_checksum).to eq(legacy_checksum) + expect(job.job_artifacts_trace.file.exists?).to be_truthy expect(job.job_artifacts_trace.file.filename).to eq('job.log') end @@ -37,6 +41,18 @@ describe Ci::CreateTraceArtifactService do expect(job.job_artifacts_trace).to be_nil end end + + context 'when migrated trace artifact file is not found' do + before do + allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:exists?) { false } + end + + it 'raises an error' do + expect { subject }.to raise_error('Trace artifact not found') + + expect(File.exists?(legacy_path)).to be_truthy + end + end end context 'when the job does not have a trace file' do -- cgit v1.2.1 From 1670a9fe98dc636cdf455f4177d359934b83b073 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 00:02:13 +0900 Subject: Fixed static analysys --- app/services/ci/create_trace_artifact_service.rb | 2 +- spec/services/ci/create_trace_artifact_service_spec.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index 00151f4bd1c..baa33de2857 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -4,7 +4,7 @@ module Ci return if job.job_artifacts_trace job.trace.read do |stream| - return unless stream.file? + return unless stream.file? # rubocop:disable Lint/NonLocalExitFromIterator temp_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |temp_path| job.create_job_artifacts_trace!( diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb index f928deb8632..dc22165c9e2 100644 --- a/spec/services/ci/create_trace_artifact_service_spec.rb +++ b/spec/services/ci/create_trace_artifact_service_spec.rb @@ -12,13 +12,13 @@ describe Ci::CreateTraceArtifactService do let(:new_path) { job.job_artifacts_trace.file.path } let(:new_checksum) { Digest::SHA256.file(new_path).hexdigest } - it { expect(File.exists?(legacy_path)).to be_truthy } + it { expect(File.exist?(legacy_path)).to be_truthy } it 'creates trace artifact' do expect { subject }.to change { Ci::JobArtifact.count }.by(1) - expect(File.exists?(legacy_path)).to be_falsy - expect(File.exists?(new_path)).to be_truthy + expect(File.exist?(legacy_path)).to be_falsy + expect(File.exist?(new_path)).to be_truthy expect(new_checksum).to eq(legacy_checksum) expect(job.job_artifacts_trace.file.exists?).to be_truthy expect(job.job_artifacts_trace.file.filename).to eq('job.log') @@ -37,7 +37,7 @@ describe Ci::CreateTraceArtifactService do end it 'keeps legacy trace and removes trace artifact' do - expect(File.exists?(legacy_path)).to be_truthy + expect(File.exist?(legacy_path)).to be_truthy expect(job.job_artifacts_trace).to be_nil end end @@ -50,7 +50,7 @@ describe Ci::CreateTraceArtifactService do it 'raises an error' do expect { subject }.to raise_error('Trace artifact not found') - expect(File.exists?(legacy_path)).to be_truthy + expect(File.exist?(legacy_path)).to be_truthy end end end -- cgit v1.2.1 From 76d70841926d21832040d6e7801ed67a7e7f88ee Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 00:04:23 +0900 Subject: Add chnagelog --- changelogs/unreleased/minimal-fix-for-artifacts-service.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/minimal-fix-for-artifacts-service.yml diff --git a/changelogs/unreleased/minimal-fix-for-artifacts-service.yml b/changelogs/unreleased/minimal-fix-for-artifacts-service.yml new file mode 100644 index 00000000000..11f5bc17759 --- /dev/null +++ b/changelogs/unreleased/minimal-fix-for-artifacts-service.yml @@ -0,0 +1,5 @@ +--- +title: Prevent trace artifact migration to incur data loss +merge_request: 17313 +author: +type: fixed -- cgit v1.2.1 From 73c4c995f90ab84ff1a3cff24d2cb189003f54a0 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 23 Feb 2018 16:04:31 +0100 Subject: inline methods --- app/helpers/blob_helper.rb | 30 ++++++-------------------- app/views/projects/tree/_tree_header.html.haml | 4 ++-- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 40a6a7ca574..551af3301db 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -23,32 +23,16 @@ module BlobHelper edit_button_tag(blob, common_classes, - edit_text, + _('Edit'), edit_blob_path(project, ref, path, options), project, ref) end - def user_can_fork_project?(project) - current_user && can?(current_user, :fork_project, project) - end - def display_modify_blob?(blob, project, ref) !current_user || (current_user && can_modify_blob?(blob, project, ref)) end - def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" - end - - def edit_text - _('Edit') - end - - def ide_edit_text - _('Web IDE') - end - def ide_edit_button(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? return unless blob = readable_blob(options, path, project, ref) @@ -57,13 +41,13 @@ module BlobHelper edit_button_tag(blob, common_classes, - ide_edit_text, - ide_edit_path(project, ref, path, options), + _('Web IDE'), + "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}", project, ref) end - def modify_file_element(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) + def modify_file_button(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) return unless current_user blob = project.repository.blob_at(ref, path) rescue nil @@ -84,7 +68,7 @@ module BlobHelper end def replace_blob_link(project = @project, ref = @ref, path = @path) - modify_file_element( + modify_file_button( project, ref, path, @@ -96,7 +80,7 @@ module BlobHelper end def delete_blob_link(project = @project, ref = @ref, path = @path) - modify_file_element( + modify_file_button( project, ref, path, @@ -353,7 +337,7 @@ module BlobHelper # Web IDE (Beta) requires the user to have this feature enabled elsif display_modify_blob?(blob, project, ref) edit_link_tag(text, edit_path, common_classes) - elsif user_can_fork_project?(project) + elsif current_user && can?(current_user, :fork_project, project) edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path)) end end diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 05539dfed7c..ff3928552af 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -74,8 +74,8 @@ .tree-controls - if show_new_ide? = succeed " " do - = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do - = ide_edit_text + = link_to "#{ide_path}/project#{edit_blob_path(@project, @id, @path, {})}", class: 'btn btn-default' do + = _('Web IDE') = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' -- cgit v1.2.1 From c888e73bc6bf7c89c551a45e579a3b319d435d21 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 23 Feb 2018 16:52:18 +0100 Subject: refactor methods inline --- app/helpers/blob_helper.rb | 12 ++++++------ app/views/projects/tree/_tree_header.html.haml | 2 +- spec/helpers/blob_helper_spec.rb | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 551af3301db..0e806d16bc5 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -16,6 +16,10 @@ module BlobHelper options[:link_opts]) end + def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) + "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + end + def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) return unless blob = readable_blob(options, path, project, ref) @@ -29,10 +33,6 @@ module BlobHelper ref) end - def display_modify_blob?(blob, project, ref) - !current_user || (current_user && can_modify_blob?(blob, project, ref)) - end - def ide_edit_button(project = @project, ref = @ref, path = @path, options = {}) return unless show_new_ide? return unless blob = readable_blob(options, path, project, ref) @@ -42,7 +42,7 @@ module BlobHelper edit_button_tag(blob, common_classes, _('Web IDE'), - "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}", + ide_edit_path(project, ref, path, options), project, ref) end @@ -335,7 +335,7 @@ module BlobHelper edit_disabled_button_tag(text, common_classes) # This condition only applies to users who are logged in # Web IDE (Beta) requires the user to have this feature enabled - elsif display_modify_blob?(blob, project, ref) + elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) edit_link_tag(text, edit_path, common_classes) elsif current_user && can?(current_user, :fork_project, project) edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path)) diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index ff3928552af..39511435508 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -74,7 +74,7 @@ .tree-controls - if show_new_ide? = succeed " " do - = link_to "#{ide_path}/project#{edit_blob_path(@project, @id, @path, {})}", class: 'btn btn-default' do + = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do = _('Web IDE') = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 0548e71c89d..1fa194fe1b8 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -74,7 +74,7 @@ describe BlobHelper do end end - describe "#edit_blob_element" do + describe "#edit_blob_link" do let(:namespace) { create(:namespace, name: 'gitlab' )} let(:project) { create(:project, :repository, namespace: namespace) } -- cgit v1.2.1 From aa603812e3d4917107af69d114ea1ab4050aebb7 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 01:44:45 +0900 Subject: Bring back the initial commit --- app/services/ci/create_trace_artifact_service.rb | 27 +++++++++++----------- .../ci/create_trace_artifact_service_spec.rb | 12 ---------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index baa33de2857..3a6d0f6f8e5 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -4,29 +4,30 @@ module Ci return if job.job_artifacts_trace job.trace.read do |stream| - return unless stream.file? # rubocop:disable Lint/NonLocalExitFromIterator + break unless stream.file? - temp_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |temp_path| - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: UploadedFile.new(temp_path, 'job.log', 'application/octet-stream') - ) + temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| + FileUtils.cp(stream.path, temp_path) + create_job_trace!(job, temp_path) + FileUtils.rm(stream.path) end - - raise 'Trace artifact not found' unless job.job_artifacts_trace.file.exists? - - FileUtils.rm(stream.path) end end private - def temp_file!(src_file, temp_dir) + def create_job_trace!(job, path) + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: UploadedFile.new(path, 'job.log', 'application/octet-stream') + ) + end + + def temp_file!(temp_dir) FileUtils.mkdir_p(temp_dir) temp_file = Tempfile.new('trace-tmp-', temp_dir) temp_file&.close - FileUtils.cp(src_file, temp_file.path) yield(temp_file.path) ensure temp_file&.close diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb index dc22165c9e2..8c5e8e438c7 100644 --- a/spec/services/ci/create_trace_artifact_service_spec.rb +++ b/spec/services/ci/create_trace_artifact_service_spec.rb @@ -41,18 +41,6 @@ describe Ci::CreateTraceArtifactService do expect(job.job_artifacts_trace).to be_nil end end - - context 'when migrated trace artifact file is not found' do - before do - allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:exists?) { false } - end - - it 'raises an error' do - expect { subject }.to raise_error('Trace artifact not found') - - expect(File.exist?(legacy_path)).to be_truthy - end - end end context 'when the job does not have a trace file' do -- cgit v1.2.1 From 4a54cc4cd3558413296df5ec14db6788e91e8586 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 02:04:32 +0900 Subject: Change FileUtils.cp to FileUtils.copy --- app/services/ci/create_trace_artifact_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index 3a6d0f6f8e5..dae741a371c 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -7,7 +7,7 @@ module Ci break unless stream.file? temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| - FileUtils.cp(stream.path, temp_path) + FileUtils.copy(stream.path, temp_path) create_job_trace!(job, temp_path) FileUtils.rm(stream.path) end -- cgit v1.2.1 From 544558b23a1970b808363b108eb2c3ec23ff13f8 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 23 Feb 2018 18:26:40 +0100 Subject: Refactored webpack bundle tag for deploy keys --- app/assets/javascripts/deploy_keys/index.js | 4 ++-- .../javascripts/pages/projects/settings/repository/show/index.js | 6 +++++- config/webpack.config.js | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js index ca8798facc9..b727261648c 100644 --- a/app/assets/javascripts/deploy_keys/index.js +++ b/app/assets/javascripts/deploy_keys/index.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import deployKeysApp from './components/app.vue'; -document.addEventListener('DOMContentLoaded', () => new Vue({ +export default () => new Vue({ el: document.getElementById('js-deploy-keys'), components: { deployKeysApp, @@ -18,4 +18,4 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ }, }); }, -})); +}); diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js index d88527351c1..5a6f4138b10 100644 --- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js @@ -1,3 +1,7 @@ import initSettingsPanels from '~/settings_panels'; +import initDeployKeys from '~/deploy_keys'; -document.addEventListener('DOMContentLoaded', initSettingsPanels); +document.addEventListener('DOMContentLoaded', () => { + initDeployKeys(); + initSettingsPanels(); +}); diff --git a/config/webpack.config.js b/config/webpack.config.js index 94ff39485fb..e32e0c32e60 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -57,7 +57,6 @@ var config = { common_vue: './vue_shared/vue_resource_interceptor.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', commit_pipelines: './commit/pipelines/pipelines_bundle.js', - deploy_keys: './deploy_keys/index.js', diff_notes: './diff_notes/diff_notes_bundle.js', environments: './environments/environments_bundle.js', environments_folder: './environments/folder/environments_folder_bundle.js', -- cgit v1.2.1 From 28c6a6b04436230e6e9b29855e4d4d4f88d9a554 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 02:32:20 +0900 Subject: Use Dir.mktmpdir --- app/services/ci/create_trace_artifact_service.rb | 29 ++++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index dae741a371c..ffde824972c 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -6,9 +6,8 @@ module Ci job.trace.read do |stream| break unless stream.file? - temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| - FileUtils.copy(stream.path, temp_path) - create_job_trace!(job, temp_path) + clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path| + create_job_trace!(job, clone_path) FileUtils.rm(stream.path) end end @@ -17,21 +16,21 @@ module Ci private def create_job_trace!(job, path) - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: UploadedFile.new(path, 'job.log', 'application/octet-stream') - ) + File.open(path) do |stream| + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: stream) + end end - def temp_file!(temp_dir) + def clone_file!(src_path, temp_dir) FileUtils.mkdir_p(temp_dir) - temp_file = Tempfile.new('trace-tmp-', temp_dir) - temp_file&.close - yield(temp_file.path) - ensure - temp_file&.close - temp_file&.unlink + Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path| + temp_path = File.join(dir_path, "job.log") + FileUtils.copy(src_path, temp_path) + yield(temp_path) + end end end end -- cgit v1.2.1 From 34e16110e0fbe29c40fe984d4715ca5dcdaef508 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 02:35:03 +0900 Subject: Revert "Use Dir.mktmpdir" This reverts commit 28c6a6b04436230e6e9b29855e4d4d4f88d9a554. --- app/services/ci/create_trace_artifact_service.rb | 29 ++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index ffde824972c..dae741a371c 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -6,8 +6,9 @@ module Ci job.trace.read do |stream| break unless stream.file? - clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path| - create_job_trace!(job, clone_path) + temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| + FileUtils.copy(stream.path, temp_path) + create_job_trace!(job, temp_path) FileUtils.rm(stream.path) end end @@ -16,21 +17,21 @@ module Ci private def create_job_trace!(job, path) - File.open(path) do |stream| - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: stream) - end + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: UploadedFile.new(path, 'job.log', 'application/octet-stream') + ) end - def clone_file!(src_path, temp_dir) + def temp_file!(temp_dir) FileUtils.mkdir_p(temp_dir) - Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path| - temp_path = File.join(dir_path, "job.log") - FileUtils.copy(src_path, temp_path) - yield(temp_path) - end + temp_file = Tempfile.new('trace-tmp-', temp_dir) + temp_file&.close + yield(temp_file.path) + ensure + temp_file&.close + temp_file&.unlink end end end -- cgit v1.2.1 From 8a56c5a1ac744a29a589c5a2ee1f26929f74105d Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 24 Feb 2018 02:40:41 +0900 Subject: Revert "Revert "Use Dir.mktmpdir"" This reverts commit 34e16110e0fbe29c40fe984d4715ca5dcdaef508. --- app/services/ci/create_trace_artifact_service.rb | 29 ++++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb index dae741a371c..ffde824972c 100644 --- a/app/services/ci/create_trace_artifact_service.rb +++ b/app/services/ci/create_trace_artifact_service.rb @@ -6,9 +6,8 @@ module Ci job.trace.read do |stream| break unless stream.file? - temp_file!(JobArtifactUploader.workhorse_upload_path) do |temp_path| - FileUtils.copy(stream.path, temp_path) - create_job_trace!(job, temp_path) + clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path| + create_job_trace!(job, clone_path) FileUtils.rm(stream.path) end end @@ -17,21 +16,21 @@ module Ci private def create_job_trace!(job, path) - job.create_job_artifacts_trace!( - project: job.project, - file_type: :trace, - file: UploadedFile.new(path, 'job.log', 'application/octet-stream') - ) + File.open(path) do |stream| + job.create_job_artifacts_trace!( + project: job.project, + file_type: :trace, + file: stream) + end end - def temp_file!(temp_dir) + def clone_file!(src_path, temp_dir) FileUtils.mkdir_p(temp_dir) - temp_file = Tempfile.new('trace-tmp-', temp_dir) - temp_file&.close - yield(temp_file.path) - ensure - temp_file&.close - temp_file&.unlink + Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path| + temp_path = File.join(dir_path, "job.log") + FileUtils.copy(src_path, temp_path) + yield(temp_path) + end end end end -- cgit v1.2.1 From 11aa990da7794038aef09dd023b85e81b5ac6c4f Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 23 Feb 2018 12:28:45 -0600 Subject: Explicitly add uncovered files to troubleMakers --- spec/javascripts/test_bundle.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 9f0d8f0d01c..94fcc6c7f2b 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -113,6 +113,9 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', + './boards/components/modal/empty_state.js', + './boards/components/modal/footer.js', + './boards/components/modal/header.js', './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', -- cgit v1.2.1 From 2357180a1117d0036a5ed85617f99acae6d43d4c Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 23 Feb 2018 20:01:56 +0100 Subject: Removed webpack tag --- app/views/projects/settings/repository/show.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 3077203c2a6..235d532bf98 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -4,7 +4,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('deploy_keys') -# Protected branches & tags use a lot of nested partials. -# The shared parts of the views can be found in the `shared` directory. -- cgit v1.2.1 From ca7d338bff3635a9809d03785a76647f3dc0fb93 Mon Sep 17 00:00:00 2001 From: Thomas Dudouet Date: Fri, 23 Feb 2018 19:36:44 +0000 Subject: Correcting documentation about project hooks settings --- doc/api/projects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 9e649efea9c..4f4ab906c1a 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1224,7 +1224,7 @@ POST /projects/:id/hooks | `note_events` | boolean | no | Trigger hook on note events | | `job_events` | boolean | no | Trigger hook on job events | | `pipeline_events` | boolean | no | Trigger hook on pipeline events | -| `wiki_events` | boolean | no | Trigger hook on wiki events | +| `wiki_page_events` | boolean | no | Trigger hook on wiki events | | `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook | | `token` | string | no | Secret token to validate received payloads; this will not be returned in the response | -- cgit v1.2.1 From c14cb5bff64b72456a1d6072e6b481b0d4969810 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 23 Feb 2018 15:50:53 -0600 Subject: prevent localStorage.clear from running when it would cause an error --- spec/support/capybara.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 5189c57b7db..8603b7f3e2c 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -78,8 +78,10 @@ RSpec.configure do |config| end config.after(:example, :js) do |example| - # prevent localstorage from introducing side effects based on test order - execute_script("localStorage.clear();") + # prevent localStorage from introducing side effects based on test order + unless ['', 'about:blank', 'data:,'].include? Capybara.current_session.driver.browser.current_url + execute_script("localStorage.clear();") + end # capybara/rspec already calls Capybara.reset_sessions! in an `after` hook, # but `block_and_wait_for_requests_complete` is called before it so by -- cgit v1.2.1 From ace3bf9adad6d5752bfebafb6606bfa57b3818e4 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 23 Feb 2018 18:55:49 -0300 Subject: Fix specs --- qa/qa/factory/product.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb index d892359894f..996b7f14f61 100644 --- a/qa/qa/factory/product.rb +++ b/qa/qa/factory/product.rb @@ -19,7 +19,8 @@ module QA new.tap do |product| factory.class.attributes.each_value do |attribute| product.instance_exec(factory, attribute.block) do |factory, block| - product.define_singleton_method(attribute.name) { block.call(factory) } + value = block.call(factory) + product.define_singleton_method(attribute.name) { value } end end end -- cgit v1.2.1 From 389019aaea34229bd3219a13352d1e321a9db215 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Fri, 23 Feb 2018 14:12:54 -0800 Subject: Fix deletion attempts on dropped tables --- spec/support/db_cleaner.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 1809ae1d141..5edc5de2a09 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -28,11 +28,11 @@ RSpec.configure do |config| end config.before(:each, :js) do - DatabaseCleaner.strategy = :deletion + DatabaseCleaner.strategy = :deletion, { cache_tables: false } end config.before(:each, :delete) do - DatabaseCleaner.strategy = :deletion + DatabaseCleaner.strategy = :deletion, { cache_tables: false } end config.before(:each, :migration) do -- cgit v1.2.1 From 4be20ba923c010c105e30ef8bba3b9bf82b2d0ca Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Thu, 22 Feb 2018 10:51:00 -0800 Subject: Respond 404 when repo does not exist --- .../mk-fix-error-code-for-repo-does-not-exist.yml | 5 +++++ lib/gitlab/git_access.rb | 2 +- spec/lib/gitlab/git_access_spec.rb | 13 +++++++++++++ spec/lib/gitlab/git_access_wiki_spec.rb | 2 +- spec/requests/git_http_spec.rb | 6 +++--- 5 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml diff --git a/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml b/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml new file mode 100644 index 00000000000..a761d610da1 --- /dev/null +++ b/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml @@ -0,0 +1,5 @@ +--- +title: Return a 404 instead of 403 if the repository does not exist on disk +merge_request: 17341 +author: +type: fixed diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index bbdb593d4e2..6400089a22f 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -199,7 +199,7 @@ module Gitlab def check_repository_existence! unless repository.exists? - raise UnauthorizedError, ERROR_MESSAGES[:no_repo] + raise NotFoundError, ERROR_MESSAGES[:no_repo] end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 19d3f55501e..6f07e423c1b 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -534,6 +534,19 @@ describe Gitlab::GitAccess do expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.') end + context 'when the project repository does not exist' do + it 'returns not found' do + project.add_guest(user) + repo = project.repository + FileUtils.rm_rf(repo.path) + + # Sanity check for rm_rf + expect(repo.exists?).to eq(false) + + expect { pull_access_check }.to raise_error(Gitlab::GitAccess::NotFoundError, 'A repository for this project does not exist yet.') + end + end + describe 'without access to project' do context 'pull code' do it { expect { pull_access_check }.to raise_not_found } diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 215f1ecc9c5..730ede99fc9 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -57,7 +57,7 @@ describe Gitlab::GitAccessWiki do # Sanity check for rm_rf expect(wiki_repo.exists?).to eq(false) - expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'A repository for this project does not exist yet.') + expect { subject }.to raise_error(Gitlab::GitAccess::NotFoundError, 'A repository for this project does not exist yet.') end end end diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index c6fdda203ad..1b0a5eac9b0 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -597,7 +597,7 @@ describe 'Git HTTP requests' do context "when a gitlab ci token is provided" do let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, :running) } - let(:other_project) { create(:project) } + let(:other_project) { create(:project, :repository) } before do build.update!(project: project) # can't associate it on factory create @@ -648,10 +648,10 @@ describe 'Git HTTP requests' do context 'when the repo does not exist' do let(:project) { create(:project) } - it 'rejects pulls with 403 Forbidden' do + it 'rejects pulls with 404 Not Found' do clone_get path, env - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:not_found) expect(response.body).to eq(git_access_error(:no_repo)) end end -- cgit v1.2.1 From e1de8a77d5d9749eaa596bfa15e6d11f1828db95 Mon Sep 17 00:00:00 2001 From: Ian Baum Date: Sun, 25 Feb 2018 11:42:19 -0600 Subject: Update CHANGELOG.md for 10.5.2 [ci skip] --- CHANGELOG.md | 21 +++++++++++++++++++++ changelogs/unreleased/34130-null-pipes.yml | 5 ----- .../41461-project-members-slow-due-to-sql.yml | 5 ----- ...legacy-authorization-for-new-clusters-on-gke.yml | 5 ----- .../unreleased/42877-snippets-dashboard-slow.yml | 5 ----- .../unreleased/43373-fix-cache-index-appending.yml | 5 ----- .../unreleased/fix-500-for-invalid-upload-path.yml | 5 ----- changelogs/unreleased/flipper-caching.yml | 5 ----- .../unreleased/jej-fix-slow-lfs-object-check.yml | 5 ----- .../kp-fix-stacked-bar-progress-value-clipping.yml | 5 ----- .../unreleased/sh-guard-read-only-user-updates.yml | 5 ----- changelogs/unreleased/users-autocomplete.yml | 5 ----- .../unreleased/zj-branch-contains-git-message.yml | 5 ----- 13 files changed, 21 insertions(+), 60 deletions(-) delete mode 100644 changelogs/unreleased/34130-null-pipes.yml delete mode 100644 changelogs/unreleased/41461-project-members-slow-due-to-sql.yml delete mode 100644 changelogs/unreleased/41619-turn-on-legacy-authorization-for-new-clusters-on-gke.yml delete mode 100644 changelogs/unreleased/42877-snippets-dashboard-slow.yml delete mode 100644 changelogs/unreleased/43373-fix-cache-index-appending.yml delete mode 100644 changelogs/unreleased/fix-500-for-invalid-upload-path.yml delete mode 100644 changelogs/unreleased/flipper-caching.yml delete mode 100644 changelogs/unreleased/jej-fix-slow-lfs-object-check.yml delete mode 100644 changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml delete mode 100644 changelogs/unreleased/sh-guard-read-only-user-updates.yml delete mode 100644 changelogs/unreleased/users-autocomplete.yml delete mode 100644 changelogs/unreleased/zj-branch-contains-git-message.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 869884f8ca6..c8d399b2b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.5.2 (2018-02-25) + +### Fixed (7 changes) + +- Fix single digit value clipping for stacked progress bar. !17217 +- Fix issue with cache key being empty when variable used as the key. !17260 +- Enable Legacy Authorization by default on Cluster creations. !17302 +- Allow branch names to be named the same as the sha it points to. +- Fix 500 error when loading an invalid upload URL. +- Don't attempt to update user tracked fields if database is in read-only. +- Prevent MR Widget error when no CI configured. + +### Performance (5 changes) + +- Improve query performance for snippets dashboard. !17088 +- Only check LFS integrity for first ref in a push to avoid timeout. !17098 +- Improve query performance of MembersFinder. !17190 +- Increase feature flag cache TTL to one hour. +- Improve performance of searching for and autocompleting of users. + + ## 10.5.1 (2018-02-22) - No changes. diff --git a/changelogs/unreleased/34130-null-pipes.yml b/changelogs/unreleased/34130-null-pipes.yml deleted file mode 100644 index a56e5cf8db2..00000000000 --- a/changelogs/unreleased/34130-null-pipes.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Prevent MR Widget error when no CI configured -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml b/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml deleted file mode 100644 index 27eee7d943b..00000000000 --- a/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve query performance of MembersFinder. -merge_request: 17190 -author: -type: performance diff --git a/changelogs/unreleased/41619-turn-on-legacy-authorization-for-new-clusters-on-gke.yml b/changelogs/unreleased/41619-turn-on-legacy-authorization-for-new-clusters-on-gke.yml deleted file mode 100644 index 507367c98c4..00000000000 --- a/changelogs/unreleased/41619-turn-on-legacy-authorization-for-new-clusters-on-gke.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable Legacy Authorization by default on Cluster creations -merge_request: 17302 -author: -type: fixed diff --git a/changelogs/unreleased/42877-snippets-dashboard-slow.yml b/changelogs/unreleased/42877-snippets-dashboard-slow.yml deleted file mode 100644 index 839b44ad272..00000000000 --- a/changelogs/unreleased/42877-snippets-dashboard-slow.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve query performance for snippets dashboard. -merge_request: 17088 -author: -type: performance diff --git a/changelogs/unreleased/43373-fix-cache-index-appending.yml b/changelogs/unreleased/43373-fix-cache-index-appending.yml deleted file mode 100644 index fdb293ea04d..00000000000 --- a/changelogs/unreleased/43373-fix-cache-index-appending.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix issue with cache key being empty when variable used as the key -merge_request: 17260 -author: -type: fixed diff --git a/changelogs/unreleased/fix-500-for-invalid-upload-path.yml b/changelogs/unreleased/fix-500-for-invalid-upload-path.yml deleted file mode 100644 index a4ce00c64c4..00000000000 --- a/changelogs/unreleased/fix-500-for-invalid-upload-path.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix 500 error when loading an invalid upload URL -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/flipper-caching.yml b/changelogs/unreleased/flipper-caching.yml deleted file mode 100644 index 6db27fd579e..00000000000 --- a/changelogs/unreleased/flipper-caching.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Increase feature flag cache TTL to one hour -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/jej-fix-slow-lfs-object-check.yml b/changelogs/unreleased/jej-fix-slow-lfs-object-check.yml deleted file mode 100644 index 09112fba85e..00000000000 --- a/changelogs/unreleased/jej-fix-slow-lfs-object-check.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Only check LFS integrity for first ref in a push to avoid timeout -merge_request: 17098 -author: -type: performance diff --git a/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml b/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml deleted file mode 100644 index 690536a533b..00000000000 --- a/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix single digit value clipping for stacked progress bar -merge_request: 17217 -author: -type: fixed diff --git a/changelogs/unreleased/sh-guard-read-only-user-updates.yml b/changelogs/unreleased/sh-guard-read-only-user-updates.yml deleted file mode 100644 index b8dbd840ed9..00000000000 --- a/changelogs/unreleased/sh-guard-read-only-user-updates.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Don't attempt to update user tracked fields if database is in read-only -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/users-autocomplete.yml b/changelogs/unreleased/users-autocomplete.yml deleted file mode 100644 index 2cb078a3a7c..00000000000 --- a/changelogs/unreleased/users-autocomplete.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve performance of searching for and autocompleting of users -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-branch-contains-git-message.yml b/changelogs/unreleased/zj-branch-contains-git-message.yml deleted file mode 100644 index ce034e7ec87..00000000000 --- a/changelogs/unreleased/zj-branch-contains-git-message.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow branch names to be named the same as the sha it points to -merge_request: -author: -type: fixed -- cgit v1.2.1 From 936280934ba820846c646f3b10ef2dc533c7a76e Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Mon, 26 Feb 2018 12:42:55 +0530 Subject: Use imported `DropdownUtils` --- app/assets/javascripts/labels_select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 5de48aa49a9..7151ac05a09 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -213,7 +213,7 @@ export default class LabelsSelect { } } if (label.duplicate) { - color = gl.DropdownUtils.duplicateLabelColor(label.color); + color = DropdownUtils.duplicateLabelColor(label.color); } else { if (label.color != null) { -- cgit v1.2.1 From c0145298b3f65151da3bb169c00b631fb8fd7040 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Mon, 26 Feb 2018 12:49:24 +0530 Subject: Add changelog entry --- changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml diff --git a/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml b/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml new file mode 100644 index 00000000000..bda4ec84e5c --- /dev/null +++ b/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml @@ -0,0 +1,5 @@ +--- +title: Fix Group labels load failure when there are duplicate labels present +merge_request: 17353 +author: +type: fixed -- cgit v1.2.1 From e351f67874fb98d1d40bd32dca8d9a44a4fc8582 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 23 Feb 2018 14:25:14 +0000 Subject: Suppress whitespace warnings in squash error messages These are obscuring the real error, which is confusing for everyone. --- lib/gitlab/git/repository.rb | 2 +- spec/lib/gitlab/git/repository_spec.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index e3cbf017e55..ddb9cf433eb 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -2206,7 +2206,7 @@ module Gitlab with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do # Apply diff of the `diff_range` to the worktree diff = run_git!(%W(diff --binary #{diff_range})) - run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin| + run_git!(%w(apply --index --whitespace=nowarn), chdir: squash_path, env: env) do |stdin| stdin.binmode stdin.write(diff) end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index d601a383a98..13358995383 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -2283,6 +2283,20 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(subject).to match(/\h{40}/) end end + + context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do + let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" } + + it 'does not include whitespace warnings in the error' do + allow(repository).to receive(:run_git!).and_call_original + allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) + + expect { subject }.to raise_error do |error| + expect(error).to be_a(described_class::GitError) + expect(error.message).not_to include('trailing whitespace') + end + end + end end end -- cgit v1.2.1 From 22addf26732e15bd63112c21f03a7f20c5feabc9 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 23 Feb 2018 15:45:32 +0100 Subject: Don't convert issuable_initial_data into JSON Instead of converting hash into JSON inside issuable_initial_data method, return hash and convert to JSON later. This allows us to easily extend basic issuable data with resource specific values. For example for Epic these data should include also labels, so we can then do something like: issuable_initial_data(@epic).merge(labels: @epic.labels).to_json --- app/helpers/issuables_helper.rb | 2 +- app/views/projects/issues/show.html.haml | 2 +- spec/helpers/issuables_helper_spec.rb | 32 ++++++++++++++++---------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 7cd84fe69c9..44ecc2212f2 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -234,7 +234,7 @@ module IssuablesHelper data.merge!(updated_at_by(issuable)) - data.to_json + data end def updated_at_by(issuable) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 7bc5c46d64a..d63443c9da5 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,7 +55,7 @@ .issue-details.issuable-details .detail-page-description.content-block - %script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue) + %script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue).to_json #js-issuable-app %h2.title= markdown_field(@issue, :title) - if @issue.description.present? diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 7fa665aecdc..2fecd1a3d27 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -173,23 +173,23 @@ describe IssuablesHelper do @project = issue.project expected_data = { - 'endpoint' => "/#{@project.full_path}/issues/#{issue.iid}", - 'updateEndpoint' => "/#{@project.full_path}/issues/#{issue.iid}.json", - 'canUpdate' => true, - 'canDestroy' => true, - 'issuableRef' => "##{issue.iid}", - 'markdownPreviewPath' => "/#{@project.full_path}/preview_markdown", - 'markdownDocsPath' => '/help/user/markdown', - 'issuableTemplates' => [], - 'projectPath' => @project.path, - 'projectNamespace' => @project.namespace.path, - 'initialTitleHtml' => issue.title, - 'initialTitleText' => issue.title, - 'initialDescriptionHtml' => '

    issue text

    ', - 'initialDescriptionText' => 'issue text', - 'initialTaskStatus' => '0 of 0 tasks completed' + endpoint: "/#{@project.full_path}/issues/#{issue.iid}", + updateEndpoint: "/#{@project.full_path}/issues/#{issue.iid}.json", + canUpdate: true, + canDestroy: true, + issuableRef: "##{issue.iid}", + markdownPreviewPath: "/#{@project.full_path}/preview_markdown", + markdownDocsPath: '/help/user/markdown', + issuableTemplates: [], + projectPath: @project.path, + projectNamespace: @project.namespace.path, + initialTitleHtml: issue.title, + initialTitleText: issue.title, + initialDescriptionHtml: '

    issue text

    ', + initialDescriptionText: 'issue text', + initialTaskStatus: '0 of 0 tasks completed' } - expect(JSON.parse(helper.issuable_initial_data(issue))).to eq(expected_data) + expect(helper.issuable_initial_data(issue)).to eq(expected_data) end end -- cgit v1.2.1 From 006753110a462e62f549cdf3c410e73eed068dbf Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Mon, 26 Feb 2018 09:54:20 +0000 Subject: Restart Unicorn and Sidekiq when GRPC throws 14:Endpoint read failed --- changelogs/unreleased/grpc-unavailable-restart.yml | 5 + config/initializers/sidekiq.rb | 2 +- lib/gitlab/gitaly_client.rb | 23 ++++ lib/gitlab/sidekiq_middleware/memory_killer.rb | 67 ----------- lib/gitlab/sidekiq_middleware/shutdown.rb | 133 +++++++++++++++++++++ .../sidekiq_middleware/memory_killer_spec.rb | 63 ---------- .../lib/gitlab/sidekiq_middleware/shutdown_spec.rb | 88 ++++++++++++++ 7 files changed, 250 insertions(+), 131 deletions(-) create mode 100644 changelogs/unreleased/grpc-unavailable-restart.yml delete mode 100644 lib/gitlab/sidekiq_middleware/memory_killer.rb create mode 100644 lib/gitlab/sidekiq_middleware/shutdown.rb delete mode 100644 spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb create mode 100644 spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb diff --git a/changelogs/unreleased/grpc-unavailable-restart.yml b/changelogs/unreleased/grpc-unavailable-restart.yml new file mode 100644 index 00000000000..5ce08d66004 --- /dev/null +++ b/changelogs/unreleased/grpc-unavailable-restart.yml @@ -0,0 +1,5 @@ +--- +title: Restart Unicorn and Sidekiq when GRPC throws 14:Endpoint read failed +merge_request: 17293 +author: +type: fixed diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 0f164e628f9..161fb185c9b 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -10,7 +10,7 @@ Sidekiq.configure_server do |config| config.server_middleware do |chain| chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] - chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] + chain.add Gitlab::SidekiqMiddleware::Shutdown chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0' chain.add Gitlab::SidekiqStatus::ServerMiddleware end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index c5d3e944f7d..9cd76630484 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -125,6 +125,8 @@ module Gitlab kwargs = yield(kwargs) if block_given? stub(service, storage).__send__(rpc, request, kwargs) # rubocop:disable GitlabSecurity/PublicSend + rescue GRPC::Unavailable => ex + handle_grpc_unavailable!(ex) ensure duration = Gitlab::Metrics::System.monotonic_time - start @@ -135,6 +137,27 @@ module Gitlab duration) end + def self.handle_grpc_unavailable!(ex) + status = ex.to_status + raise ex unless status.details == 'Endpoint read failed' + + # There is a bug in grpc 1.8.x that causes a client process to get stuck + # always raising '14:Endpoint read failed'. The only thing that we can + # do to recover is to restart the process. + # + # See https://gitlab.com/gitlab-org/gitaly/issues/1029 + + if Sidekiq.server? + raise Gitlab::SidekiqMiddleware::Shutdown::WantShutdown.new(ex.to_s) + else + # SIGQUIT requests a Unicorn worker to shut down gracefully after the current request. + Process.kill('QUIT', Process.pid) + end + + raise ex + end + private_class_method :handle_grpc_unavailable! + def self.current_transaction_labels Gitlab::Metrics::Transaction.current&.labels || {} end diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb deleted file mode 100644 index b89ae2505c9..00000000000 --- a/lib/gitlab/sidekiq_middleware/memory_killer.rb +++ /dev/null @@ -1,67 +0,0 @@ -module Gitlab - module SidekiqMiddleware - class MemoryKiller - # Default the RSS limit to 0, meaning the MemoryKiller is disabled - MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i - # Give Sidekiq 15 minutes of grace time after exceeding the RSS limit - GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i - # Wait 30 seconds for running jobs to finish during graceful shutdown - SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i - - # Create a mutex used to ensure there will be only one thread waiting to - # shut Sidekiq down - MUTEX = Mutex.new - - def call(worker, job, queue) - yield - - current_rss = get_rss - - return unless MAX_RSS > 0 && current_rss > MAX_RSS - - Thread.new do - # Return if another thread is already waiting to shut Sidekiq down - return unless MUTEX.try_lock - - Sidekiq.logger.warn "Sidekiq worker PID-#{pid} current RSS #{current_rss}"\ - " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}" - Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later" - - # Wait `GRACE_TIME` to give the memory intensive job time to finish. - # Then, tell Sidekiq to stop fetching new jobs. - wait_and_signal(GRACE_TIME, 'SIGSTP', 'stop fetching new jobs') - - # Wait `SHUTDOWN_WAIT` to give already fetched jobs time to finish. - # Then, tell Sidekiq to gracefully shut down by giving jobs a few more - # moments to finish, killing and requeuing them if they didn't, and - # then terminating itself. - wait_and_signal(SHUTDOWN_WAIT, 'SIGTERM', 'gracefully shut down') - - # Wait for Sidekiq to shutdown gracefully, and kill it if it didn't. - wait_and_signal(Sidekiq.options[:timeout] + 2, 'SIGKILL', 'die') - end - end - - private - - def get_rss - output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s) - return 0 unless status.zero? - - output.to_i - end - - def wait_and_signal(time, signal, explanation) - Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" - sleep(time) - - Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" - Process.kill(signal, pid) - end - - def pid - Process.pid - end - end - end -end diff --git a/lib/gitlab/sidekiq_middleware/shutdown.rb b/lib/gitlab/sidekiq_middleware/shutdown.rb new file mode 100644 index 00000000000..c2b8d6de66e --- /dev/null +++ b/lib/gitlab/sidekiq_middleware/shutdown.rb @@ -0,0 +1,133 @@ +require 'mutex_m' + +module Gitlab + module SidekiqMiddleware + class Shutdown + extend Mutex_m + + # Default the RSS limit to 0, meaning the MemoryKiller is disabled + MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i + # Give Sidekiq 15 minutes of grace time after exceeding the RSS limit + GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i + # Wait 30 seconds for running jobs to finish during graceful shutdown + SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i + + # This exception can be used to request that the middleware start shutting down Sidekiq + WantShutdown = Class.new(StandardError) + + ShutdownWithoutRaise = Class.new(WantShutdown) + private_constant :ShutdownWithoutRaise + + # For testing only, to avoid race conditions (?) in Rspec mocks. + attr_reader :trace + + # We store the shutdown thread in a class variable to ensure that there + # can be only one shutdown thread in the process. + def self.create_shutdown_thread + mu_synchronize do + return unless @shutdown_thread.nil? + + @shutdown_thread = Thread.new { yield } + end + end + + # For testing only: so we can wait for the shutdown thread to finish. + def self.shutdown_thread + mu_synchronize { @shutdown_thread } + end + + # For testing only: so that we can reset the global state before each test. + def self.clear_shutdown_thread + mu_synchronize { @shutdown_thread = nil } + end + + def initialize + @trace = Queue.new if Rails.env.test? + end + + def call(worker, job, queue) + shutdown_exception = nil + + begin + yield + check_rss! + rescue WantShutdown => ex + shutdown_exception = ex + end + + return unless shutdown_exception + + self.class.create_shutdown_thread do + do_shutdown(worker, job, shutdown_exception) + end + + raise shutdown_exception unless shutdown_exception.is_a?(ShutdownWithoutRaise) + end + + private + + def do_shutdown(worker, job, shutdown_exception) + Sidekiq.logger.warn "Sidekiq worker PID-#{pid} shutting down because of #{shutdown_exception} after job "\ + "#{worker.class} JID-#{job['jid']}" + Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later" + + # Wait `GRACE_TIME` to give the memory intensive job time to finish. + # Then, tell Sidekiq to stop fetching new jobs. + wait_and_signal(GRACE_TIME, 'SIGTSTP', 'stop fetching new jobs') + + # Wait `SHUTDOWN_WAIT` to give already fetched jobs time to finish. + # Then, tell Sidekiq to gracefully shut down by giving jobs a few more + # moments to finish, killing and requeuing them if they didn't, and + # then terminating itself. + wait_and_signal(SHUTDOWN_WAIT, 'SIGTERM', 'gracefully shut down') + + # Wait for Sidekiq to shutdown gracefully, and kill it if it didn't. + wait_and_signal(Sidekiq.options[:timeout] + 2, 'SIGKILL', 'die') + end + + def check_rss! + return unless MAX_RSS > 0 + + current_rss = get_rss + return unless current_rss > MAX_RSS + + raise ShutdownWithoutRaise.new("current RSS #{current_rss} exceeds maximum RSS #{MAX_RSS}") + end + + def get_rss + output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s) + return 0 unless status.zero? + + output.to_i + end + + def wait_and_signal(time, signal, explanation) + Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" + sleep(time) + + Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" + kill(signal, pid) + end + + def pid + Process.pid + end + + def sleep(time) + if Rails.env.test? + @trace << [:sleep, time] + else + Kernel.sleep(time) + end + end + + def kill(signal, pid) + if Rails.env.test? + @trace << [:kill, signal, pid] + else + Process.kill(signal, pid) + end + end + end + end +end diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb deleted file mode 100644 index 8fdbbacd04d..00000000000 --- a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'spec_helper' - -describe Gitlab::SidekiqMiddleware::MemoryKiller do - subject { described_class.new } - let(:pid) { 999 } - - let(:worker) { double(:worker, class: 'TestWorker') } - let(:job) { { 'jid' => 123 } } - let(:queue) { 'test_queue' } - - def run - thread = subject.call(worker, job, queue) { nil } - thread&.join - end - - before do - allow(subject).to receive(:get_rss).and_return(10.kilobytes) - allow(subject).to receive(:pid).and_return(pid) - end - - context 'when MAX_RSS is set to 0' do - before do - stub_const("#{described_class}::MAX_RSS", 0) - end - - it 'does nothing' do - expect(subject).not_to receive(:sleep) - - run - end - end - - context 'when MAX_RSS is exceeded' do - before do - stub_const("#{described_class}::MAX_RSS", 5.kilobytes) - end - - it 'sends the STP, TERM and KILL signals at expected times' do - expect(subject).to receive(:sleep).with(15 * 60).ordered - expect(Process).to receive(:kill).with('SIGSTP', pid).ordered - - expect(subject).to receive(:sleep).with(30).ordered - expect(Process).to receive(:kill).with('SIGTERM', pid).ordered - - expect(subject).to receive(:sleep).with(10).ordered - expect(Process).to receive(:kill).with('SIGKILL', pid).ordered - - run - end - end - - context 'when MAX_RSS is not exceeded' do - before do - stub_const("#{described_class}::MAX_RSS", 15.kilobytes) - end - - it 'does nothing' do - expect(subject).not_to receive(:sleep) - - run - end - end -end diff --git a/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb b/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb new file mode 100644 index 00000000000..0001795c3f0 --- /dev/null +++ b/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +describe Gitlab::SidekiqMiddleware::Shutdown do + subject { described_class.new } + + let(:pid) { Process.pid } + let(:worker) { double(:worker, class: 'TestWorker') } + let(:job) { { 'jid' => 123 } } + let(:queue) { 'test_queue' } + let(:block) { proc { nil } } + + def run + subject.call(worker, job, queue) { block.call } + described_class.shutdown_thread&.join + end + + def pop_trace + subject.trace.pop(true) + end + + before do + allow(subject).to receive(:get_rss).and_return(10.kilobytes) + described_class.clear_shutdown_thread + end + + context 'when MAX_RSS is set to 0' do + before do + stub_const("#{described_class}::MAX_RSS", 0) + end + + it 'does nothing' do + expect(subject).not_to receive(:sleep) + + run + end + end + + def expect_shutdown_sequence + expect(pop_trace).to eq([:sleep, 15 * 60]) + expect(pop_trace).to eq([:kill, 'SIGTSTP', pid]) + + expect(pop_trace).to eq([:sleep, 30]) + expect(pop_trace).to eq([:kill, 'SIGTERM', pid]) + + expect(pop_trace).to eq([:sleep, 10]) + expect(pop_trace).to eq([:kill, 'SIGKILL', pid]) + end + + context 'when MAX_RSS is exceeded' do + before do + stub_const("#{described_class}::MAX_RSS", 5.kilobytes) + end + + it 'sends the TSTP, TERM and KILL signals at expected times' do + run + + expect_shutdown_sequence + end + end + + context 'when MAX_RSS is not exceeded' do + before do + stub_const("#{described_class}::MAX_RSS", 15.kilobytes) + end + + it 'does nothing' do + expect(subject).not_to receive(:sleep) + + run + end + end + + context 'when WantShutdown is raised' do + let(:block) { proc { raise described_class::WantShutdown } } + + it 'starts the shutdown sequence and re-raises the exception' do + expect { run }.to raise_exception(described_class::WantShutdown) + + # We can't expect 'run' to have joined on the shutdown thread, because + # it hit an exception. + shutdown_thread = described_class.shutdown_thread + expect(shutdown_thread).not_to be_nil + shutdown_thread.join + + expect_shutdown_sequence + end + end +end -- cgit v1.2.1 From 566be168cff435f088d45baf1c6934bff4bb493f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 22 Feb 2018 12:57:56 +0100 Subject: Get rid of hard-coded user/project/group names that could clash with DB sequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/features/projects_spec.rb | 8 ++++---- spec/requests/openid_connect_spec.rb | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 645d12da09f..cfe979a8647 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -146,8 +146,8 @@ feature 'Project' do end describe 'removal', :js do - let(:user) { create(:user, username: 'test', name: 'test') } - let(:project) { create(:project, namespace: user.namespace, name: 'project1') } + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } before do sign_in(user) @@ -156,8 +156,8 @@ feature 'Project' do end it 'removes a project' do - expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1) - expect(page).to have_content "Project 'test / project1' is in the process of being deleted." + expect { remove_with_confirm('Remove project', project.path) }.to change { Project.count }.by(-1) + expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted." expect(Project.all.count).to be_zero expect(project.issues).to be_empty expect(project.merge_requests).to be_empty diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index de829011e58..6bed8e812c0 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -68,10 +68,10 @@ describe 'OpenID Connect requests' do let!(:public_email) { build :email, email: 'public@example.com' } let!(:private_email) { build :email, email: 'private@example.com' } - let!(:group1) { create :group, path: 'group1' } - let!(:group2) { create :group, path: 'group2' } - let!(:group3) { create :group, path: 'group3', parent: group2 } - let!(:group4) { create :group, path: 'group4', parent: group3 } + let!(:group1) { create :group } + let!(:group2) { create :group } + let!(:group3) { create :group, parent: group2 } + let!(:group4) { create :group, parent: group3 } before do group1.add_user(user, GroupMember::OWNER) @@ -93,8 +93,8 @@ describe 'OpenID Connect requests' do 'groups' => anything })) - expected_groups = %w[group1 group2/group3] - expected_groups << 'group2/group3/group4' if Group.supports_nested_groups? + expected_groups = [group1.full_path, group3.full_path] + expected_groups << group4.full_path if Group.supports_nested_groups? expect(json_response['groups']).to match_array(expected_groups) end end -- cgit v1.2.1 From 1751cab41fb48fb4c231bfea9f3ab334ba98fea5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 26 Feb 2018 12:17:50 +0100 Subject: Extract WaitableWorker out of AuthorizedProjectsWorker --- app/workers/authorized_projects_worker.rb | 36 +--------- app/workers/concerns/waitable_worker.rb | 44 ++++++++++++ lib/gitlab/job_waiter.rb | 8 ++- spec/workers/authorized_projects_worker_spec.rb | 79 --------------------- spec/workers/concerns/waitable_worker_spec.rb | 92 +++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 114 deletions(-) create mode 100644 app/workers/concerns/waitable_worker.rb create mode 100644 spec/workers/concerns/waitable_worker_spec.rb diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb index 09559e3b696..d7e24491516 100644 --- a/app/workers/authorized_projects_worker.rb +++ b/app/workers/authorized_projects_worker.rb @@ -1,42 +1,10 @@ class AuthorizedProjectsWorker include ApplicationWorker + prepend WaitableWorker - # Schedules multiple jobs and waits for them to be completed. - def self.bulk_perform_and_wait(args_list) - # Short-circuit: it's more efficient to do small numbers of jobs inline - return bulk_perform_inline(args_list) if args_list.size <= 3 - - waiter = Gitlab::JobWaiter.new(args_list.size) - - # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]] - # into [[1, "key"], [2, "key"], [3, "key"]] - waiting_args_list = args_list.map { |args| [*args, waiter.key] } - bulk_perform_async(waiting_args_list) - - waiter.wait - end - - # Performs multiple jobs directly. Failed jobs will be put into sidekiq so - # they can benefit from retries - def self.bulk_perform_inline(args_list) - failed = [] - - args_list.each do |args| - begin - new.perform(*args) - rescue - failed << args - end - end - - bulk_perform_async(failed) if failed.present? - end - - def perform(user_id, notify_key = nil) + def perform(user_id) user = User.find_by(id: user_id) user&.refresh_authorized_projects - ensure - Gitlab::JobWaiter.notify(notify_key, jid) if notify_key end end diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb new file mode 100644 index 00000000000..7d2266aea3c --- /dev/null +++ b/app/workers/concerns/waitable_worker.rb @@ -0,0 +1,44 @@ +module WaitableWorker + extend ActiveSupport::Concern + + module ClassMethods + # Schedules multiple jobs and waits for them to be completed. + def bulk_perform_and_wait(args_list) + # Short-circuit: it's more efficient to do small numbers of jobs inline + return bulk_perform_inline(args_list) if args_list.size <= 3 + + waiter = Gitlab::JobWaiter.new(args_list.size) + + # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]] + # into [[1, "key"], [2, "key"], [3, "key"]] + waiting_args_list = args_list.map { |args| [*args, waiter.key] } + bulk_perform_async(waiting_args_list) + + waiter.wait + end + + # Performs multiple jobs directly. Failed jobs will be put into sidekiq so + # they can benefit from retries + def bulk_perform_inline(args_list) + failed = [] + + args_list.each do |args| + begin + new.perform(*args) + rescue + failed << args + end + end + + bulk_perform_async(failed) if failed.present? + end + end + + def perform(*args) + notify_key = args.pop if Gitlab::JobWaiter.key?(args.last) + + super(*args) + ensure + Gitlab::JobWaiter.notify(notify_key, jid) if notify_key + end +end diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb index f654508c391..f7a8eae0be4 100644 --- a/lib/gitlab/job_waiter.rb +++ b/lib/gitlab/job_waiter.rb @@ -15,16 +15,22 @@ module Gitlab # push to that array when done. Once the waiter has popped `count` items, it # knows all the jobs are done. class JobWaiter + KEY_PREFIX = "gitlab:job_waiter".freeze + def self.notify(key, jid) Gitlab::Redis::SharedState.with { |redis| redis.lpush(key, jid) } end + def self.key?(key) + key.is_a?(String) && key =~ /\A#{KEY_PREFIX}:\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/ + end + attr_reader :key, :finished attr_accessor :jobs_remaining # jobs_remaining - the number of jobs left to wait for # key - The key of this waiter. - def initialize(jobs_remaining = 0, key = "gitlab:job_waiter:#{SecureRandom.uuid}") + def initialize(jobs_remaining = 0, key = "#{KEY_PREFIX}:#{SecureRandom.uuid}") @key = key @jobs_remaining = jobs_remaining @finished = [] diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb index 0d6eb536c33..d095138f6b7 100644 --- a/spec/workers/authorized_projects_worker_spec.rb +++ b/spec/workers/authorized_projects_worker_spec.rb @@ -1,79 +1,6 @@ require 'spec_helper' describe AuthorizedProjectsWorker do - let(:project) { create(:project) } - - def build_args_list(*ids, multiply: 1) - args_list = ids.map { |id| [id] } - args_list * multiply - end - - describe '.bulk_perform_and_wait' do - it 'schedules the ids and waits for the jobs to complete' do - args_list = build_args_list(project.owner.id) - - project.owner.project_authorizations.delete_all - described_class.bulk_perform_and_wait(args_list) - - expect(project.owner.project_authorizations.count).to eq(1) - end - - it 'inlines workloads <= 3 jobs' do - args_list = build_args_list(project.owner.id, multiply: 3) - expect(described_class).to receive(:bulk_perform_inline).with(args_list) - - described_class.bulk_perform_and_wait(args_list) - end - - it 'runs > 3 jobs using sidekiq' do - project.owner.project_authorizations.delete_all - - expect(described_class).to receive(:bulk_perform_async).and_call_original - - args_list = build_args_list(project.owner.id, multiply: 4) - described_class.bulk_perform_and_wait(args_list) - - expect(project.owner.project_authorizations.count).to eq(1) - end - end - - describe '.bulk_perform_inline' do - it 'refreshes the authorizations inline' do - project.owner.project_authorizations.delete_all - - expect_any_instance_of(described_class).to receive(:perform).and_call_original - - described_class.bulk_perform_inline(build_args_list(project.owner.id)) - - expect(project.owner.project_authorizations.count).to eq(1) - end - - it 'enqueues jobs if an error is raised' do - invalid_id = -1 - args_list = build_args_list(project.owner.id, invalid_id) - - allow_any_instance_of(described_class).to receive(:perform).with(project.owner.id) - allow_any_instance_of(described_class).to receive(:perform).with(invalid_id).and_raise(ArgumentError) - expect(described_class).to receive(:bulk_perform_async).with(build_args_list(invalid_id)) - - described_class.bulk_perform_inline(args_list) - end - end - - describe '.bulk_perform_async' do - it "uses it's respective sidekiq queue" do - args_list = build_args_list(project.owner.id) - push_bulk_args = { - 'class' => described_class, - 'args' => args_list - } - - expect(Sidekiq::Client).to receive(:push_bulk).with(push_bulk_args).once - - described_class.bulk_perform_async(args_list) - end - end - describe '#perform' do let(:user) { create(:user) } @@ -85,12 +12,6 @@ describe AuthorizedProjectsWorker do job.perform(user.id) end - it 'notifies the JobWaiter when done if the key is provided' do - expect(Gitlab::JobWaiter).to receive(:notify).with('notify-key', job.jid) - - job.perform(user.id, 'notify-key') - end - context "when the user is not found" do it "does nothing" do expect_any_instance_of(User).not_to receive(:refresh_authorized_projects) diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb new file mode 100644 index 00000000000..4af0de86ac9 --- /dev/null +++ b/spec/workers/concerns/waitable_worker_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe WaitableWorker do + let(:worker) do + Class.new do + def self.name + 'Gitlab::Foo::Bar::DummyWorker' + end + + class << self + cattr_accessor(:counter) { 0 } + end + + include ApplicationWorker + prepend WaitableWorker + + def perform(i = 0) + self.class.counter += i + end + end + end + + subject(:job) { worker.new } + + describe '.bulk_perform_and_wait' do + it 'schedules the jobs and waits for them to complete' do + worker.bulk_perform_and_wait([[1], [2]]) + + expect(worker.counter).to eq(3) + end + + it 'inlines workloads <= 3 jobs' do + args_list = [[1], [2], [3]] + expect(worker).to receive(:bulk_perform_inline).with(args_list).and_call_original + + worker.bulk_perform_and_wait(args_list) + + expect(worker.counter).to eq(6) + end + + it 'runs > 3 jobs using sidekiq' do + expect(worker).to receive(:bulk_perform_async) + + worker.bulk_perform_and_wait([[1], [2], [3], [4]]) + end + end + + describe '.bulk_perform_inline' do + it 'runs the jobs inline' do + expect(worker).not_to receive(:bulk_perform_async) + + worker.bulk_perform_inline([[1], [2]]) + + expect(worker.counter).to eq(3) + end + + it 'enqueues jobs if an error is raised' do + expect(worker).to receive(:bulk_perform_async).with([['foo']]) + + worker.bulk_perform_inline([[1], ['foo']]) + end + end + + describe '#perform' do + shared_examples 'perform' do + it 'notifies the JobWaiter when done if the key is provided' do + key = Gitlab::JobWaiter.new.key + expect(Gitlab::JobWaiter).to receive(:notify).with(key, job.jid) + + job.perform(*args, key) + end + + it 'does not notify the JobWaiter when done if no key is provided' do + expect(Gitlab::JobWaiter).not_to receive(:notify) + + job.perform(*args) + end + end + + context 'when the worker takes arguments' do + let(:args) { [1] } + + it_behaves_like 'perform' + end + + context 'when the worker takes no arguments' do + let(:args) { [] } + + it_behaves_like 'perform' + end + end +end -- cgit v1.2.1 From 56af2dbe7369575a5b7a1fb5202b728d6015846b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 26 Feb 2018 13:34:19 +0100 Subject: Allow bulk_perform_and_wait wait timeout to be overridden --- app/workers/concerns/waitable_worker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb index 7d2266aea3c..48ebe862248 100644 --- a/app/workers/concerns/waitable_worker.rb +++ b/app/workers/concerns/waitable_worker.rb @@ -3,7 +3,7 @@ module WaitableWorker module ClassMethods # Schedules multiple jobs and waits for them to be completed. - def bulk_perform_and_wait(args_list) + def bulk_perform_and_wait(args_list, timeout: 10) # Short-circuit: it's more efficient to do small numbers of jobs inline return bulk_perform_inline(args_list) if args_list.size <= 3 @@ -14,7 +14,7 @@ module WaitableWorker waiting_args_list = args_list.map { |args| [*args, waiter.key] } bulk_perform_async(waiting_args_list) - waiter.wait + waiter.wait(timeout) end # Performs multiple jobs directly. Failed jobs will be put into sidekiq so -- cgit v1.2.1 From 9a40b3cc455c293f8338b78a8effbc9099a17a26 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Mon, 26 Feb 2018 13:54:09 +0000 Subject: Clarify when disabling Gitaly makes sense --- doc/administration/gitaly/index.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index e3b10119090..d9a61aea6ef 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -2,7 +2,9 @@ [Gitaly](https://gitlab.com/gitlab-org/gitaly) (introduced in GitLab 9.0) is a service that provides high-level RPC access to Git -repositories. Gitaly is a mandatory component in GitLab 9.4 and newer. +repositories. Gitaly was optional when it was first introduced in +GitLab, but since GitLab 9.4 it is a mandatory component of the +application. GitLab components that access Git repositories (gitlab-rails, gitlab-shell, gitlab-workhorse) act as clients to Gitaly. End users do @@ -184,14 +186,20 @@ Gitaly logs on your Gitaly server (`sudo gitlab-ctl tail gitaly` or coming in. One sure way to trigger a Gitaly request is to clone a repository from your GitLab server over HTTP. -## Disabling or enabling the Gitaly service +## Disabling or enabling the Gitaly service in a cluster environment If you are running Gitaly [as a remote service](#running-gitaly-on-its-own-server) you may want to disable the local Gitaly service that runs on your Gitlab server by default. -To disable the Gitaly service in your Omnibus installation, add the -following line to `/etc/gitlab/gitlab.rb`: +> 'Disabling Gitaly' only makes sense when you run GitLab in a custom +cluster configuration, where different services run on different +machines. Disabling Gitaly on all machines in the cluster is not a +valid configuration. + +If you are setting up a GitLab cluster where Gitaly does not need to +run on all machines, you can disable the Gitaly service in your +Omnibus installation, add the following line to `/etc/gitlab/gitlab.rb`: ```ruby gitaly['enable'] = false @@ -200,11 +208,13 @@ gitaly['enable'] = false When you run `gitlab-ctl reconfigure` the Gitaly service will be disabled. -To disable the Gitaly service in an installation from source, add the -following to `/etc/default/gitlab`: +To disable the Gitaly service in a GitLab cluster where you installed +GitLab from source, add the following to `/etc/default/gitlab` on the +machine where you want to disable Gitaly. ```shell gitaly_enabled=false ``` -When you run `service gitlab restart` Gitaly will be disabled. +When you run `service gitlab restart` Gitaly will be disabled on this +particular machine. -- cgit v1.2.1 From 34c7938804729bc8860096a3ae13450cd3102476 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 26 Feb 2018 14:12:38 +0000 Subject: Fixed issue edit shortcut not working This was caused by the element getting cached on the class before the Vue app has finished rendering. Closes #43560 --- app/assets/javascripts/shortcuts_issuable.js | 7 +++---- changelogs/unreleased/issue-edit-shortcut.yml | 5 +++++ spec/features/issues/form_spec.rb | 12 ++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/issue-edit-shortcut.yml diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 689befc742e..14545824e74 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -9,13 +9,12 @@ export default class ShortcutsIssuable extends Shortcuts { super(); this.$replyField = isMergeRequest ? $('.js-main-target-form #note_note') : $('.js-main-target-form .js-vue-comment-form'); - this.editBtn = document.querySelector('.js-issuable-edit'); Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee')); Mousetrap.bind('m', () => ShortcutsIssuable.openSidebarDropdown('milestone')); Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels')); Mousetrap.bind('r', this.replyWithSelectedText.bind(this)); - Mousetrap.bind('e', this.editIssue.bind(this)); + Mousetrap.bind('e', ShortcutsIssuable.editIssue); if (isMergeRequest) { this.enabledHelp.push('.hidden-shortcut.merge_requests'); @@ -58,10 +57,10 @@ export default class ShortcutsIssuable extends Shortcuts { return false; } - editIssue() { + static editIssue() { // Need to click the element as on issues, editing is inline // on merge request, editing is on a different page - this.editBtn.click(); + document.querySelector('.js-issuable-edit').click(); return false; } diff --git a/changelogs/unreleased/issue-edit-shortcut.yml b/changelogs/unreleased/issue-edit-shortcut.yml new file mode 100644 index 00000000000..2b29b2bc03f --- /dev/null +++ b/changelogs/unreleased/issue-edit-shortcut.yml @@ -0,0 +1,5 @@ +--- +title: Fixed issue edit shortcut not opening edit form +merge_request: +author: +type: fixed diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index faf14be4818..c2c4b479a8a 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -271,6 +271,18 @@ describe 'New/edit issue', :js do end end + context 'inline edit' do + before do + visit project_issue_path(project, issue) + end + + it 'opens inline edit form with shortcut' do + find('body').send_keys('e') + + expect(page).to have_selector('.detail-page-description form') + end + end + describe 'sub-group project' do let(:group) { create(:group) } let(:nested_group_1) { create(:group, parent: group) } -- cgit v1.2.1 From c370f53cb68038b469ec219cf2ec248e62a72683 Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Fri, 16 Feb 2018 21:39:43 +0100 Subject: Migrate recursive tree entries fetching to Gitaly --- GITALY_SERVER_VERSION | 2 +- Gemfile | 2 +- Gemfile.lock | 4 ++-- app/models/tree.rb | 20 +------------------ lib/gitlab/git/tree.rb | 23 ++++++++++++++++++---- lib/gitlab/gitaly_client/commit_service.rb | 5 +++-- .../gitlab/gitaly_client/commit_service_spec.rb | 4 ++-- 7 files changed, 29 insertions(+), 31 deletions(-) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 92fc430ae8f..137c1281121 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.82.0 +0.85.0 diff --git a/Gemfile b/Gemfile index 61c129f3036..6838ddbf01a 100644 --- a/Gemfile +++ b/Gemfile @@ -411,7 +411,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.84.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.85.0', require: 'gitaly' # Locked until https://github.com/google/protobuf/issues/4210 is closed gem 'google-protobuf', '= 3.5.1' diff --git a/Gemfile.lock b/Gemfile.lock index 57ff086f0b1..89b86ae0259 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -285,7 +285,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.84.0) + gitaly-proto (0.85.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (5.3.3) @@ -1057,7 +1057,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.84.0) + gitaly-proto (~> 0.85.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) diff --git a/app/models/tree.rb b/app/models/tree.rb index c89b8eca9be..4c1856b67a8 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -9,10 +9,9 @@ class Tree @repository = repository @sha = sha @path = path - @recursive = recursive git_repo = @repository.raw_repository - @entries = get_entries(git_repo, @sha, @path, recursive: @recursive) + @entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive) end def readme @@ -58,21 +57,4 @@ class Tree def sorted_entries trees + blobs + submodules end - - private - - def get_entries(git_repo, sha, path, recursive: false) - current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path) - ordered_entries = [] - - current_path_entries.each do |entry| - ordered_entries << entry - - if recursive && entry.dir? - ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true)) - end - end - - ordered_entries - end end diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb index ba6058fd3c9..b6ceb542dd1 100644 --- a/lib/gitlab/git/tree.rb +++ b/lib/gitlab/git/tree.rb @@ -14,14 +14,14 @@ module Gitlab # Uses rugged for raw objects # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/320 - def where(repository, sha, path = nil) + def where(repository, sha, path = nil, recursive = false) path = nil if path == '' || path == '/' Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled| if is_enabled - repository.gitaly_commit_client.tree_entries(repository, sha, path) + repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive) else - tree_entries_from_rugged(repository, sha, path) + tree_entries_from_rugged(repository, sha, path, recursive) end end end @@ -57,7 +57,22 @@ module Gitlab end end - def tree_entries_from_rugged(repository, sha, path) + def tree_entries_from_rugged(repository, sha, path, recursive) + current_path_entries = get_tree_entries_from_rugged(repository, sha, path) + ordered_entries = [] + + current_path_entries.each do |entry| + ordered_entries << entry + + if recursive && entry.dir? + ordered_entries.concat(tree_entries_from_rugged(repository, sha, entry.path, true)) + end + end + + ordered_entries + end + + def get_tree_entries_from_rugged(repository, sha, path) commit = repository.lookup(sha) root_tree = commit.tree diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index 269a048cf5d..d60f57717b5 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -105,11 +105,12 @@ module Gitlab entry unless entry.oid.blank? end - def tree_entries(repository, revision, path) + def tree_entries(repository, revision, path, recursive) request = Gitaly::GetTreeEntriesRequest.new( repository: @gitaly_repo, revision: encode_binary(revision), - path: path.present? ? encode_binary(path) : '.' + path: path.present? ? encode_binary(path) : '.', + recursive: recursive ) response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request, timeout: GitalyClient.medium_timeout) diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 001c4d3e10a..9be3fa633a7 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -113,7 +113,7 @@ describe Gitlab::GitalyClient::CommitService do .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .and_return([]) - client.tree_entries(repository, revision, path) + client.tree_entries(repository, revision, path, false) end context 'with UTF-8 params strings' do @@ -126,7 +126,7 @@ describe Gitlab::GitalyClient::CommitService do .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .and_return([]) - client.tree_entries(repository, revision, path) + client.tree_entries(repository, revision, path, false) end end end -- cgit v1.2.1 From 07a227a3200ee563132ffd385c0e2a3f57ec043d Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Mon, 26 Feb 2018 16:23:19 +0000 Subject: Allow to find labels in ancestor groups and better group support in label service --- app/finders/labels_finder.rb | 15 +++-- app/services/issuable_base_service.rb | 14 +++- app/services/labels/find_or_create_service.rb | 22 ++++-- spec/finders/labels_finder_spec.rb | 23 +++++++ .../services/labels/find_or_create_service_spec.rb | 78 +++++++++++++++------- 5 files changed, 116 insertions(+), 36 deletions(-) diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index f013e177c5b..5c9fce211ec 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -39,7 +39,7 @@ class LabelsFinder < UnionFinder end end elsif only_group_labels? - label_ids << Label.where(group_id: group.id) + label_ids << Label.where(group_id: group_ids) else label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(project_id: projects.select(:id)) @@ -59,10 +59,11 @@ class LabelsFinder < UnionFinder items.where(title: title) end - def group - strong_memoize(:group) do + def group_ids + strong_memoize(:group_ids) do group = Group.find(params[:group_id]) - authorized_to_read_labels?(group) && group + groups = params[:include_ancestor_groups].present? ? group.self_and_ancestors : [group] + groups_user_can_read_labels(groups).map(&:id) end end @@ -120,4 +121,10 @@ class LabelsFinder < UnionFinder Ability.allowed?(current_user, :read_label, label_parent) end + + def groups_user_can_read_labels(groups) + DeclarativePolicy.user_scope do + groups.select { |group| authorized_to_read_labels?(group) } + end + end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 66a9b1f82e0..e87fd49d193 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -77,8 +77,12 @@ class IssuableBaseService < BaseService return unless labels params[:label_ids] = labels.split(",").map do |label_name| - service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip) - label = service.execute + label = Labels::FindOrCreateService.new( + current_user, + parent, + title: label_name.strip, + available_labels: available_labels + ).execute label.try(:id) end.compact @@ -102,7 +106,7 @@ class IssuableBaseService < BaseService end def available_labels - LabelsFinder.new(current_user, project_id: @project.id).execute + @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute end def merge_quick_actions_into_params!(issuable) @@ -303,4 +307,8 @@ class IssuableBaseService < BaseService def update_project_counter_caches?(issuable) issuable.state_changed? end + + def parent + project + end end diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb index 940c8b333d3..079f611b3f3 100644 --- a/app/services/labels/find_or_create_service.rb +++ b/app/services/labels/find_or_create_service.rb @@ -1,8 +1,9 @@ module Labels class FindOrCreateService - def initialize(current_user, project, params = {}) + def initialize(current_user, parent, params = {}) @current_user = current_user - @project = project + @parent = parent + @available_labels = params.delete(:available_labels) @params = params.dup.with_indifferent_access end @@ -13,12 +14,13 @@ module Labels private - attr_reader :current_user, :project, :params, :skip_authorization + attr_reader :current_user, :parent, :params, :skip_authorization def available_labels @available_labels ||= LabelsFinder.new( current_user, - project_id: project.id + "#{parent_type}_id".to_sym => parent.id, + only_group_labels: parent_is_group? ).execute(skip_authorization: skip_authorization) end @@ -27,8 +29,8 @@ module Labels def find_or_create_label new_label = available_labels.find_by(title: title) - if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project)) - new_label = Labels::CreateService.new(params).execute(project: project) + if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, parent)) + new_label = Labels::CreateService.new(params).execute(parent_type.to_sym => parent) end new_label @@ -37,5 +39,13 @@ module Labels def title params[:title] || params[:name] end + + def parent_type + parent.model_name.param_key + end + + def parent_is_group? + parent_type == "group" + end end end diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 06031aee217..dc76efea35b 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -5,6 +5,8 @@ describe LabelsFinder do let(:group_1) { create(:group) } let(:group_2) { create(:group) } let(:group_3) { create(:group) } + let(:private_group_1) { create(:group, :private) } + let(:private_subgroup_1) { create(:group, :private, parent: private_group_1) } let(:project_1) { create(:project, namespace: group_1) } let(:project_2) { create(:project, namespace: group_2) } @@ -20,6 +22,8 @@ describe LabelsFinder do let!(:group_label_1) { create(:group_label, group: group_1, title: 'Label 1 (group)') } let!(:group_label_2) { create(:group_label, group: group_1, title: 'Group Label 2') } let!(:group_label_3) { create(:group_label, group: group_2, title: 'Group Label 3') } + let!(:private_group_label_1) { create(:group_label, group: private_group_1, title: 'Private Group Label 1') } + let!(:private_subgroup_label_1) { create(:group_label, group: private_subgroup_1, title: 'Private Sub Group Label 1') } let(:user) { create(:user) } @@ -66,6 +70,25 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2, group_label_1] end end + + context 'when including labels from group ancestors', :nested_groups do + it 'returns labels from group and its ancestors' do + private_group_1.add_developer(user) + private_subgroup_1.add_developer(user) + + finder = described_class.new(user, group_id: private_subgroup_1.id, only_group_labels: true, include_ancestor_groups: true) + + expect(finder.execute).to eq [private_group_label_1, private_subgroup_label_1] + end + + it 'ignores labels from groups which user can not read' do + private_subgroup_1.add_developer(user) + + finder = described_class.new(user, group_id: private_subgroup_1.id, only_group_labels: true, include_ancestor_groups: true) + + expect(finder.execute).to eq [private_subgroup_label_1] + end + end end context 'filtering by project_id' do diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb index 78aa5d442e7..68d5660445a 100644 --- a/spec/services/labels/find_or_create_service_spec.rb +++ b/spec/services/labels/find_or_create_service_spec.rb @@ -15,47 +15,79 @@ describe Labels::FindOrCreateService do context 'when acting on behalf of a specific user' do let(:user) { create(:user) } - subject(:service) { described_class.new(user, project, params) } - before do - project.add_developer(user) - end - context 'when label does not exist at group level' do - it 'creates a new label at project level' do - expect { service.execute }.to change(project.labels, :count).by(1) + context 'when finding labels on project level' do + subject(:service) { described_class.new(user, project, params) } + + before do + project.add_developer(user) end - end - context 'when label exists at group level' do - it 'returns the group label' do - group_label = create(:group_label, group: group, title: 'Security') + context 'when label does not exist at group level' do + it 'creates a new label at project level' do + expect { service.execute }.to change(project.labels, :count).by(1) + end + end - expect(service.execute).to eq group_label + context 'when label exists at group level' do + it 'returns the group label' do + group_label = create(:group_label, group: group, title: 'Security') + + expect(service.execute).to eq group_label + end + end + + context 'when label exists at project level' do + it 'returns the project label' do + project_label = create(:label, project: project, title: 'Security') + + expect(service.execute).to eq project_label + end end end - context 'when label does not exist at group level' do - it 'creates a new label at project leve' do - expect { service.execute }.to change(project.labels, :count).by(1) + context 'when finding labels on group level' do + subject(:service) { described_class.new(user, group, params) } + + before do + group.add_developer(user) + end + + context 'when label does not exist at group level' do + it 'creates a new label at group level' do + expect { service.execute }.to change(group.labels, :count).by(1) + end + end + + context 'when label exists at group level' do + it 'returns the group label' do + group_label = create(:group_label, group: group, title: 'Security') + + expect(service.execute).to eq group_label + end end end + end + + context 'when authorization is not required' do + context 'when finding labels on project level' do + subject(:service) { described_class.new(nil, project, params) } - context 'when label exists at project level' do it 'returns the project label' do project_label = create(:label, project: project, title: 'Security') - expect(service.execute).to eq project_label + expect(service.execute(skip_authorization: true)).to eq project_label end end - end - context 'when authorization is not required' do - subject(:service) { described_class.new(nil, project, params) } + context 'when finding labels on group level' do + subject(:service) { described_class.new(nil, group, params) } - it 'returns the project label' do - project_label = create(:label, project: project, title: 'Security') + it 'returns the group label' do + group_label = create(:group_label, group: group, title: 'Security') - expect(service.execute(skip_authorization: true)).to eq project_label + expect(service.execute(skip_authorization: true)).to eq group_label + end end end end -- cgit v1.2.1 From a06152540794d247a34826313e7ab466bf5ee8bb Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 26 Feb 2018 17:27:42 +0000 Subject: Remove webpack bundle tags for blob --- app/views/projects/blob/_upload.html.haml | 3 --- config/webpack.config.js | 1 - 2 files changed, 4 deletions(-) diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index b3afd16f900..f1324c61500 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -27,6 +27,3 @@ - unless can?(current_user, :push_code, @project) .inline.prepend-left-10 = commit_in_fork_help - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('blob') diff --git a/config/webpack.config.js b/config/webpack.config.js index be827903a6a..05389a2a93f 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -51,7 +51,6 @@ var config = { context: path.join(ROOT_PATH, 'app/assets/javascripts'), entry: { balsamiq_viewer: './blob/balsamiq_viewer.js', - blob: './blob_edit/blob_bundle.js', common: './commons/index.js', common_vue: './vue_shared/vue_resource_interceptor.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', -- cgit v1.2.1