diff options
Diffstat (limited to 'app')
21 files changed, 304 insertions, 159 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index 48ba967285f..b6af49c7e2e 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -39,12 +39,12 @@ export default { <div class="diff-viewer"> <template v-if="isTextFile"> <inline-diff-view - v-if="isInlineView" + v-show="isInlineView" :diff-file="diffFile" :diff-lines="diffFile.highlightedDiffLines || []" /> <parallel-diff-view - v-else-if="isParallelView" + v-show="isParallelView" :diff-file="diffFile" :diff-lines="diffFile.parallelDiffLines || []" /> diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue index 68fe6787f9b..5b08b161114 100644 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue @@ -10,6 +10,9 @@ import { NEW_NO_NEW_LINE_TYPE, LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME, + INLINE_DIFF_VIEW_TYPE, + LINE_POSITION_LEFT, + LINE_POSITION_RIGHT, } from '../constants'; export default { @@ -25,6 +28,11 @@ export default { type: Object, required: true, }, + diffViewType: { + type: String, + required: false, + default: INLINE_DIFF_VIEW_TYPE, + }, showCommentButton: { type: Boolean, required: false, @@ -57,13 +65,19 @@ export default { }, }, computed: { - ...mapGetters(['isLoggedIn', 'isInlineView']), + ...mapGetters(['isLoggedIn']), normalizedLine() { - if (this.isInlineView) { - return this.line; + let normalizedLine; + + if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) { + normalizedLine = this.line; + } else if (this.linePosition === LINE_POSITION_LEFT) { + normalizedLine = this.line.left; + } else if (this.linePosition === LINE_POSITION_RIGHT) { + normalizedLine = this.line.right; } - return this.lineType === OLD_LINE_TYPE ? this.line.left : this.line.right; + return normalizedLine; }, isMatchLine() { return this.normalizedLine.type === MATCH_LINE_TYPE; @@ -72,10 +86,10 @@ export default { return this.normalizedLine.type === CONTEXT_LINE_TYPE; }, isMetaLine() { + const { type } = this.normalizedLine; + return ( - this.normalizedLine.type === OLD_NO_NEW_LINE_TYPE || - this.normalizedLine.type === NEW_NO_NEW_LINE_TYPE || - this.normalizedLine.type === EMPTY_CELL_TYPE + type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE ); }, classNameMap() { diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue new file mode 100644 index 00000000000..a2470843ca6 --- /dev/null +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -0,0 +1,104 @@ +<script> +import { mapGetters } from 'vuex'; +import DiffTableCell from './diff_table_cell.vue'; +import { + NEW_LINE_TYPE, + OLD_LINE_TYPE, + CONTEXT_LINE_TYPE, + CONTEXT_LINE_CLASS_NAME, + PARALLEL_DIFF_VIEW_TYPE, + LINE_POSITION_LEFT, + LINE_POSITION_RIGHT, +} from '../constants'; + +export default { + components: { + DiffTableCell, + }, + props: { + diffFile: { + type: Object, + required: true, + }, + line: { + type: Object, + required: true, + }, + isBottom: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + return { + isHover: false, + }; + }, + computed: { + ...mapGetters(['isInlineView']), + isContextLine() { + return this.line.type === CONTEXT_LINE_TYPE; + }, + classNameMap() { + return { + [this.line.type]: this.line.type, + [CONTEXT_LINE_CLASS_NAME]: this.isContextLine, + [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView, + }; + }, + inlineRowId() { + const { lineCode, oldLine, newLine } = this.line; + + return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`; + }, + }, + created() { + this.newLineType = NEW_LINE_TYPE; + this.oldLineType = OLD_LINE_TYPE; + this.linePositionLeft = LINE_POSITION_LEFT; + this.linePositionRight = LINE_POSITION_RIGHT; + }, + methods: { + handleMouseMove(e) { + // To show the comment icon on the gutter we need to know if we hover the line. + // Current table structure doesn't allow us to do this with CSS in both of the diff view types + this.isHover = e.type === 'mouseover'; + }, + }, +}; +</script> + +<template> + <tr + :id="inlineRowId" + :class="classNameMap" + class="line_holder" + @mouseover="handleMouseMove" + @mouseout="handleMouseMove" + > + <diff-table-cell + :diff-file="diffFile" + :line="line" + :line-type="oldLineType" + :is-bottom="isBottom" + :is-hover="isHover" + :show-comment-button="true" + class="diff-line-num old_line" + /> + <diff-table-cell + :diff-file="diffFile" + :line="line" + :line-type="newLineType" + :is-bottom="isBottom" + :is-hover="isHover" + class="diff-line-num new_line" + /> + <diff-table-cell + :class="line.type" + :diff-file="diffFile" + :line="line" + :is-content-line="true" + /> + </tr> +</template> diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index e72f85df77a..b884230fb63 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -1,12 +1,39 @@ <script> -import diffContentMixin from '../mixins/diff_content'; +import { mapGetters } from 'vuex'; +import inlineDiffTableRow from './inline_diff_table_row.vue'; import inlineDiffCommentRow from './inline_diff_comment_row.vue'; +import { trimFirstCharOfLineContent } from '../store/utils'; export default { components: { inlineDiffCommentRow, + inlineDiffTableRow, + }, + props: { + diffFile: { + type: Object, + required: true, + }, + diffLines: { + type: Array, + required: true, + }, + }, + computed: { + ...mapGetters(['commit']), + normalizedDiffLines() { + return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line)); + }, + diffLinesLength() { + return this.normalizedDiffLines.length; + }, + commitId() { + return this.commit && this.commit.id; + }, + userColorScheme() { + return window.gon.user_color_scheme; + }, }, - mixins: [diffContentMixin], }; </script> @@ -19,7 +46,7 @@ export default { <template v-for="(line, index) in normalizedDiffLines" > - <diff-table-row + <inline-diff-table-row :diff-file="diffFile" :line="line" :is-bottom="index + 1 === diffLinesLength" diff --git a/app/assets/javascripts/diffs/components/diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index 8716fdaf44d..eb769584d74 100644 --- a/app/assets/javascripts/diffs/components/diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -35,30 +35,21 @@ export default { }, data() { return { - isHover: false, isLeftHover: false, isRightHover: false, }; }, computed: { - ...mapGetters(['isInlineView', 'isParallelView']), + ...mapGetters(['isParallelView']), isContextLine() { - return this.line.left - ? this.line.left.type === CONTEXT_LINE_TYPE - : this.line.type === CONTEXT_LINE_TYPE; + return this.line.left.type === CONTEXT_LINE_TYPE; }, classNameMap() { return { - [this.line.type]: this.line.type, [CONTEXT_LINE_CLASS_NAME]: this.isContextLine, [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView, }; }, - inlineRowId() { - const { lineCode, oldLine, newLine } = this.line; - - return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`; - }, parallelViewLeftLineType() { if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) { return OLD_NO_NEW_LINE_TYPE; @@ -72,23 +63,19 @@ export default { this.oldLineType = OLD_LINE_TYPE; this.linePositionLeft = LINE_POSITION_LEFT; this.linePositionRight = LINE_POSITION_RIGHT; + this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE; }, methods: { handleMouseMove(e) { const isHover = e.type === 'mouseover'; + const hoveringCell = e.target.closest('td'); + const allCellsInHoveringRow = Array.from(e.currentTarget.children); + const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell); - if (this.isInlineView) { - this.isHover = isHover; + if (hoverIndex >= 2) { + this.isRightHover = isHover; } else { - const hoveringCell = e.target.closest('td'); - const allCellsInHoveringRow = Array.from(e.currentTarget.children); - const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell); - - if (hoverIndex >= 2) { - this.isRightHover = isHover; - } else { - this.isLeftHover = isHover; - } + this.isLeftHover = isHover; } }, // Prevent text selecting on both sides of parallel diff view @@ -110,40 +97,6 @@ export default { <template> <tr - v-if="isInlineView" - :id="inlineRowId" - :class="classNameMap" - class="line_holder" - @mouseover="handleMouseMove" - @mouseout="handleMouseMove" - > - <diff-table-cell - :diff-file="diffFile" - :line="line" - :line-type="oldLineType" - :is-bottom="isBottom" - :is-hover="isHover" - :show-comment-button="true" - class="diff-line-num old_line" - /> - <diff-table-cell - :diff-file="diffFile" - :line="line" - :line-type="newLineType" - :is-bottom="isBottom" - :is-hover="isHover" - class="diff-line-num new_line" - /> - <diff-table-cell - :class="line.type" - :diff-file="diffFile" - :line="line" - :is-content-line="true" - /> - </tr> - - <tr - v-else :class="classNameMap" class="line_holder" @mouseover="handleMouseMove" @@ -157,6 +110,7 @@ export default { :is-bottom="isBottom" :is-hover="isLeftHover" :show-comment-button="true" + :diff-view-type="parallelDiffViewType" class="diff-line-num old_line" /> <diff-table-cell @@ -164,7 +118,9 @@ export default { :diff-file="diffFile" :line="line" :is-content-line="true" + :line-position="linePositionLeft" :line-type="parallelViewLeftLineType" + :diff-view-type="parallelDiffViewType" class="line_content parallel left-side" @mousedown.native="handleParallelLineMouseDown" /> @@ -176,6 +132,7 @@ export default { :is-bottom="isBottom" :is-hover="isRightHover" :show-comment-button="true" + :diff-view-type="parallelDiffViewType" class="diff-line-num new_line" /> <diff-table-cell @@ -183,7 +140,9 @@ export default { :diff-file="diffFile" :line="line" :is-content-line="true" + :line-position="linePositionRight" :line-type="line.right.type" + :diff-view-type="parallelDiffViewType" class="line_content parallel right-side" @mousedown.native="handleParallelLineMouseDown" /> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index ed92b4ee249..d7280338b68 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -1,25 +1,53 @@ <script> -import diffContentMixin from '../mixins/diff_content'; +import { mapGetters } from 'vuex'; +import parallelDiffTableRow from './parallel_diff_table_row.vue'; import parallelDiffCommentRow from './parallel_diff_comment_row.vue'; import { EMPTY_CELL_TYPE } from '../constants'; +import { trimFirstCharOfLineContent } from '../store/utils'; export default { components: { + parallelDiffTableRow, parallelDiffCommentRow, }, - mixins: [diffContentMixin], + props: { + diffFile: { + type: Object, + required: true, + }, + diffLines: { + type: Array, + required: true, + }, + }, computed: { + ...mapGetters(['commit']), parallelDiffLines() { - return this.normalizedDiffLines.map(line => { - if (!line.left) { + return this.diffLines.map(line => { + if (line.left) { + Object.assign(line, { left: trimFirstCharOfLineContent(line.left) }); + } else { Object.assign(line, { left: { type: EMPTY_CELL_TYPE } }); - } else if (!line.right) { + } + + if (line.right) { + Object.assign(line, { right: trimFirstCharOfLineContent(line.right) }); + } else { Object.assign(line, { right: { type: EMPTY_CELL_TYPE } }); } return line; }); }, + diffLinesLength() { + return this.parallelDiffLines.length; + }, + commitId() { + return this.commit && this.commit.id; + }, + userColorScheme() { + return window.gon.user_color_scheme; + }, }, }; </script> @@ -35,7 +63,7 @@ export default { <template v-for="(line, index) in parallelDiffLines" > - <diff-table-row + <parallel-diff-table-row :diff-file="diffFile" :line="line" :is-bottom="index + 1 === diffLinesLength" diff --git a/app/assets/javascripts/diffs/mixins/diff_content.js b/app/assets/javascripts/diffs/mixins/diff_content.js deleted file mode 100644 index ebb511d3a7e..00000000000 --- a/app/assets/javascripts/diffs/mixins/diff_content.js +++ /dev/null @@ -1,57 +0,0 @@ -import { mapGetters } from 'vuex'; -import diffDiscussions from '../components/diff_discussions.vue'; -import diffLineGutterContent from '../components/diff_line_gutter_content.vue'; -import diffLineNoteForm from '../components/diff_line_note_form.vue'; -import diffTableRow from '../components/diff_table_row.vue'; -import { trimFirstCharOfLineContent } from '../store/utils'; - -export default { - props: { - diffFile: { - type: Object, - required: true, - }, - diffLines: { - type: Array, - required: true, - }, - }, - components: { - diffDiscussions, - diffTableRow, - diffLineNoteForm, - diffLineGutterContent, - }, - computed: { - ...mapGetters(['commit']), - commitId() { - return this.commit && this.commit.id; - }, - userColorScheme() { - return window.gon.user_color_scheme; - }, - normalizedDiffLines() { - return this.diffLines.map(line => { - if (line.richText) { - return trimFirstCharOfLineContent(line); - } - - if (line.left) { - Object.assign(line, { left: trimFirstCharOfLineContent(line.left) }); - } - - if (line.right) { - Object.assign(line, { right: trimFirstCharOfLineContent(line.right) }); - } - - return line; - }); - }, - diffLinesLength() { - return this.normalizedDiffLines.length; - }, - fileHash() { - return this.diffFile.fileHash; - }, - }, -}; diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js index 63e9239dce4..2c8e1a1466f 100644 --- a/app/assets/javascripts/diffs/store/mutation_types.js +++ b/app/assets/javascripts/diffs/store/mutation_types.js @@ -1,7 +1,6 @@ export const SET_BASE_CONFIG = 'SET_BASE_CONFIG'; export const SET_LOADING = 'SET_LOADING'; export const SET_DIFF_DATA = 'SET_DIFF_DATA'; -export const SET_DIFF_FILES = 'SET_DIFF_FILES'; export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE'; export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS'; export const ADD_COMMENT_FORM_LINE = 'ADD_COMMENT_FORM_LINE'; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 339a33f8b71..8aa8a114c6f 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -20,12 +20,6 @@ export default { }); }, - [types.SET_DIFF_FILES](state, diffFiles) { - Object.assign(state, { - diffFiles: convertObjectPropsToCamelCase(diffFiles, { deep: true }), - }); - }, - [types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) { Object.assign(state, { mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }), diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 5789c3fa1b1..8bcaf5eb6ac 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -268,8 +268,6 @@ .navbar-sub-nav, .navbar-nav { - align-items: center; - > li { > a:hover, > a:focus { diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index fb788c47ef1..6944857bd33 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -52,8 +52,7 @@ class Admin::HooksController < Admin::ApplicationController end def hook_logs - @hook_logs ||= - Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page]) + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]) end def hook_params diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index dd7aa1a67b9..6800d742b0a 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -58,8 +58,7 @@ class Projects::HooksController < Projects::ApplicationController end def hook_logs - @hook_logs ||= - Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page]) + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]) end def hook_params diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7aa277b3614..1de6ae24622 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -62,7 +62,11 @@ class SessionsController < Devise::SessionsController return unless captcha_enabled? return unless Gitlab::Recaptcha.load_configurations! - unless verify_recaptcha + if verify_recaptcha + increment_successful_login_captcha_counter + else + increment_failed_login_captcha_counter + self.resource = resource_class.new flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' flash.delete :recaptcha_error @@ -71,6 +75,20 @@ class SessionsController < Devise::SessionsController end end + def increment_failed_login_captcha_counter + Gitlab::Metrics.counter( + :failed_login_captcha_total, + 'Number of failed CAPTCHA attempts for logins'.freeze + ).increment + end + + def increment_successful_login_captcha_counter + Gitlab::Metrics.counter( + :successful_login_captcha_total, + 'Number of successful CAPTCHA attempts for logins'.freeze + ).increment + end + def log_failed_login Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}") end diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index e72c125fb69..59a1f2aed69 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -7,6 +7,11 @@ class WebHookLog < ActiveRecord::Base validates :web_hook, presence: true + def self.recent + where('created_at >= ?', 2.days.ago.beginning_of_day) + .order(created_at: :desc) + end + def success? response_status =~ /^2/ end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 7f4c47a6d14..edc5c00d9c4 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -67,11 +67,11 @@ class BambooService < CiService def execute(data) return unless supported_events.include?(data[:object_kind]) - get_path("updateAndBuild.action?buildKey=#{build_key}") + get_path("updateAndBuild.action", { buildKey: build_key }) end def calculate_reactive_cache(sha, ref) - response = get_path("rest/api/latest/result?label=#{sha}") + response = get_path("rest/api/latest/result/byChangeset/#{sha}") { build_page: read_build_page(response), commit_status: read_commit_status(response) } end @@ -113,18 +113,20 @@ class BambooService < CiService URI.join("#{bamboo_url}/", path).to_s end - def get_path(path) + def get_path(path, query_params = {}) url = build_url(path) if username.blank? && password.blank? - Gitlab::HTTP.get(url, verify: false) + Gitlab::HTTP.get(url, verify: false, query: query_params) else - url << '&os_authType=basic' - Gitlab::HTTP.get(url, verify: false, - basic_auth: { - username: username, - password: password - }) + query_params[:os_authType] = 'basic' + Gitlab::HTTP.get(url, + verify: false, + query: query_params, + basic_auth: { + username: username, + password: password + }) end end end diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index ad655a7b3f4..d4d622d84ab 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -27,6 +27,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated def statistics_buttons(show_auto_devops_callout:) [ + readme_anchor_data, changelog_anchor_data, license_anchor_data, contribution_guide_anchor_data, @@ -212,11 +213,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def readme_anchor_data - if current_user && can_current_user_push_to_default_branch? && repository.readme.blank? + if current_user && can_current_user_push_to_default_branch? && repository.readme.nil? OpenStruct.new(enabled: false, label: _('Add Readme'), link: add_readme_path) - elsif repository.readme.present? + elsif repository.readme OpenStruct.new(enabled: true, label: _('Readme'), link: default_view != 'readme' ? readme_path : '#readme') diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index ca4480fe2b1..2de9624aed4 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -35,7 +35,7 @@ class BuildDetailsEntity < JobEntity def build_failed_issue_options { title: "Job Failed ##{build.id}", - description: "Job [##{build.id}](#{project_job_path(project, build)}) failed for #{build.sha}:\n" } + description: "Job [##{build.id}](#{project_job_url(project, build)}) failed for #{build.sha}:\n" } end def current_user diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 36bc0a4575a..73606eb9f83 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -81,6 +81,13 @@ class FileUploader < GitlabUploader apply_context!(uploader_context) end + def initialize_copy(from) + super + + @secret = self.class.generate_secret + @upload = nil # calling record_upload would delete the old upload if set + end + # enforce the usage of Hashed storage when storing to # remote store as the FileMover doesn't support OS def base_dir(store = nil) @@ -144,6 +151,27 @@ class FileUploader < GitlabUploader @secret ||= self.class.generate_secret end + # return a new uploader with a file copy on another project + def self.copy_to(uploader, to_project) + moved = uploader.dup.tap do |u| + u.model = to_project + end + + moved.copy_file(uploader.file) + moved + end + + def copy_file(file) + to_path = if file_storage? + File.join(self.class.root, store_path) + else + store_path + end + + self.file = file.copy_to(to_path) + record_upload # after_store is not triggered + end + private def apply_context!(uploader_context) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 5cec443e969..d8e32651b36 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -21,7 +21,7 @@ - if current_user = render 'layouts/header/new_dropdown' - if header_link?(:search) - %li.nav-item.d-none.d-sm-none.d-md-block + %li.nav-item.d-none.d-sm-none.d-md-block.m-auto = render 'layouts/search' unless current_controller?(:search) %li.nav-item.d-inline-block.d-sm-none.d-md-none = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index d06f51b1828..b8b854853b7 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -20,6 +20,7 @@ - cronjob:ci_archive_traces_cron - cronjob:trending_projects - cronjob:issue_due_scheduler +- cronjob:prune_web_hook_logs - gcp_cluster:cluster_install_app - gcp_cluster:cluster_provision diff --git a/app/workers/prune_web_hook_logs_worker.rb b/app/workers/prune_web_hook_logs_worker.rb new file mode 100644 index 00000000000..45c7d32f7eb --- /dev/null +++ b/app/workers/prune_web_hook_logs_worker.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Worker that deletes a fixed number of outdated rows from the "web_hook_logs" +# table. +class PruneWebHookLogsWorker + include ApplicationWorker + include CronjobQueue + + # The maximum number of rows to remove in a single job. + DELETE_LIMIT = 50_000 + + def perform + # MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner + # query refers to the same table. To work around this we wrap the IN body in + # another sub query. + WebHookLog + .where( + 'id IN (SELECT id FROM (?) ids_to_remove)', + WebHookLog + .select(:id) + .where('created_at < ?', 90.days.ago.beginning_of_day) + .limit(DELETE_LIMIT) + ) + .delete_all + end +end |