diff options
author | Sean McGivern <sean@gitlab.com> | 2017-07-07 15:54:39 +0100 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2017-07-07 15:54:39 +0100 |
commit | b8c4d5e0f552dc9986883bd9c8783c7f43f125ce (patch) | |
tree | 06478887ace997e9b5735f797ddd64b7f028dc6d /app | |
parent | 97611c88fcbae6b025750e6ebf2061a3d87d9753 (diff) | |
parent | c88739ffa6ad99fbd5e20a6e8f2ee251996ea18b (diff) | |
download | gitlab-ce-b8c4d5e0f552dc9986883bd9c8783c7f43f125ce.tar.gz |
Merge branch 'master' into 33929-allow-to-enable-perf-bar-for-a-group
Diffstat (limited to 'app')
122 files changed, 972 insertions, 554 deletions
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 1be9df19c81..6a008112203 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -2,6 +2,7 @@ import './lib/utils/url_utility'; import FilesCommentButton from './files_comment_button'; +import SingleFileDiff from './single_file_diff'; const UNFOLD_COUNT = 20; let isBound = false; @@ -10,7 +11,11 @@ class Diff { constructor() { const $diffFile = $('.files .diff-file'); - $diffFile.singleFileDiff(); + $diffFile.each((index, file) => { + if (!$.data(file, 'singleFileDiff')) { + $.data(file, 'singleFileDiff', new SingleFileDiff(file)); + } + }); FilesCommentButton.init($diffFile); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 4247540de22..8f716696605 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -1,17 +1,13 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ -/* global UsernameValidator */ -/* global ActiveTabMemoizer */ /* global ShortcutsNavigation */ /* global IssuableIndex */ /* global ShortcutsIssuable */ -/* global ZenMode */ /* global Milestone */ /* global IssuableForm */ /* global LabelsSelect */ /* global MilestoneSelect */ /* global Commit */ /* global NotificationsForm */ -/* global TreeView */ /* global NotificationsDropdown */ /* global GroupAvatar */ /* global LineHighlighter */ @@ -25,7 +21,6 @@ /* global ProjectAvatar */ /* global CompareAutocomplete */ /* global ProjectNew */ -/* global Star */ /* global ProjectShow */ /* global Labels */ /* global Shortcuts */ @@ -54,8 +49,19 @@ import UsersSelect from './users_select'; import RefSelectDropdown from './ref_select_dropdown'; import GfmAutoComplete from './gfm_auto_complete'; import ShortcutsBlob from './shortcuts_blob'; +import SigninTabsMemoizer from './signin_tabs_memoizer'; +import Star from './star'; +import Todos from './todos'; +import TreeView from './tree'; +import UsagePing from './usage_ping'; +import UsernameValidator from './username_validator'; +import VersionCheckImage from './version_check_image'; +import Wikis from './wikis'; +import ZenMode from './zen_mode'; import initSettingsPanels from './settings_panels'; import initExperimentalFlags from './experimental_flags'; +import OAuthRememberMe from './oauth_remember_me'; +import PerformanceBar from './performance_bar'; (function() { var Dispatcher; @@ -126,7 +132,8 @@ import initExperimentalFlags from './experimental_flags'; break; case 'sessions:new': new UsernameValidator(); - new ActiveTabMemoizer(); + new SigninTabsMemoizer(); + new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents(); break; case 'projects:boards:show': case 'projects:boards:index': @@ -161,7 +168,7 @@ import initExperimentalFlags from './experimental_flags'; new UsersSelect(); break; case 'dashboard:todos:index': - new gl.Todos(); + new Todos(); break; case 'dashboard:projects:index': case 'dashboard:projects:starred': @@ -377,7 +384,7 @@ import initExperimentalFlags from './experimental_flags'; new BlobViewer(); break; case 'help:index': - gl.VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); + VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); break; case 'search:show': new Search(); @@ -393,6 +400,7 @@ import initExperimentalFlags from './experimental_flags'; initSettingsPanels(); break; case 'projects:settings:ci_cd:show': + case 'groups:settings:ci_cd:show': new gl.ProjectVariables(); break; case 'ci:lints:create': @@ -429,7 +437,7 @@ import initExperimentalFlags from './experimental_flags'; new Admin(); switch (path[1]) { case 'cohorts': - new gl.UsagePing(); + new UsagePing(); break; case 'groups': new UsersSelect(); @@ -481,7 +489,7 @@ import initExperimentalFlags from './experimental_flags'; new NotificationsDropdown(); break; case 'wikis': - new gl.Wikis(); + new Wikis(); shortcut_handler = new ShortcutsWiki(); new ZenMode(); new gl.GLForm($('.wiki-form'), true); @@ -513,6 +521,10 @@ import initExperimentalFlags from './experimental_flags'; if (!shortcut_handler) { new Shortcuts(); } + + if (document.querySelector('#peek')) { + new PerformanceBar({ container: '#peek' }); + } }; Dispatcher.prototype.initSearch = function() { diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 92f6f0d4117..9ac1325fc95 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,12 +1,12 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */ /* global GitLab */ -/* global ZenMode */ /* global Autosave */ /* global dateFormat */ /* global Pikaday */ import UsersSelect from './users_select'; import GfmAutoComplete from './gfm_auto_complete'; +import ZenMode from './zen_mode'; (function() { this.IssuableForm = (function() { diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 0860e237ce1..7490cd31f58 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -4,13 +4,13 @@ import 'vendor/jquery.waitforimages'; import '~/lib/utils/text_utility'; import './flash'; -import './task_list'; +import TaskList from './task_list'; import CreateMergeRequestDropdown from './create_merge_request_dropdown'; class Issue { constructor() { if ($('a.btn-close').length) { - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'issue', fieldName: 'description', selector: '.detail-page-description', diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 43db66c8e08..48bad8f1e68 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,5 +1,6 @@ <script> import animateMixin from '../mixins/animate'; + import TaskList from '../../task_list'; export default { mixins: [animateMixin], @@ -46,7 +47,7 @@ if (this.canUpdate) { // eslint-disable-next-line no-new - new gl.TaskList({ + new TaskList({ dataType: 'issue', fieldName: 'description', selector: '.detail-page-description', diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index fe752d95b90..892b3fab1c6 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -143,26 +143,13 @@ import './render_math'; import './right_sidebar'; import './search'; import './search_autocomplete'; -import './signin_tabs_memoizer'; -import './single_file_diff'; import './smart_interval'; import './snippets_list'; import './star'; import './subscription'; import './subscription_select'; import './syntax_highlight'; -import './task_list'; -import './todos'; -import './tree'; -import './usage_ping'; import './user'; -import './user_tabs'; -import './username_validator'; -import './users_select'; -import './version_check_image'; -import './visibility_select'; -import './wikis'; -import './zen_mode'; // eslint-disable-next-line global-require, import/no-commonjs if (process.env.NODE_ENV !== 'production') require('./test_utils/'); diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index f93feeec1c2..7ba9efc5387 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -2,7 +2,7 @@ /* global MergeRequestTabs */ import 'vendor/jquery.waitforimages'; -import './task_list'; +import TaskList from './task_list'; import './merge_request_tabs'; (function() { @@ -25,7 +25,7 @@ import './merge_request_tabs'; this.initMRBtnListeners(); this.initCommitMessageListeners(); if ($("a.btn-close").length) { - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'merge_request', fieldName: 'description', selector: '.detail-page-description', diff --git a/app/assets/javascripts/monitoring/components/monitoring_column.vue b/app/assets/javascripts/monitoring/components/monitoring_column.vue index 0f33581ec52..c376baea79c 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_column.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_column.vue @@ -105,9 +105,9 @@ this.measurements = measurements.small; } this.data = query.result[0].values; - this.unitOfDisplay = query.unit || 'N/A'; + this.unitOfDisplay = query.unit || ''; this.yAxisLabel = this.columnData.y_label || 'Values'; - this.legendTitle = query.legend || 'Average'; + this.legendTitle = query.label || 'Average'; this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right; this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; @@ -159,12 +159,12 @@ const xAxis = d3.svg.axis() .scale(axisXScale) - .ticks(measurements.ticks) + .ticks(measurements.xTicks) .orient('bottom'); const yAxis = d3.svg.axis() .scale(this.yScale) - .ticks(measurements.ticks) + .ticks(measurements.yTicks) .orient('left'); d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis); @@ -172,8 +172,12 @@ const width = this.graphWidth; d3.select(this.$refs.baseSvg).select('.y-axis').call(yAxis) .selectAll('.tick') - .each(function createTickLines() { - d3.select(this).select('line').attr('x2', width); + .each(function createTickLines(d, i) { + if (i > 0) { + d3.select(this).select('line') + .attr('x2', width) + .attr('class', 'axis-tick'); + } // Avoid adding the class to the first tick, to prevent coloring }); // This will select all of the ticks once they're rendered this.xScale = d3.time.scale() @@ -215,16 +219,16 @@ }; </script> <template> - <div + <div :class="classType"> - <h5 + <h5 class="text-center graph-title"> {{columnData.title}} </h5> <div class="prometheus-svg-container" :style="paddingBottomRootSvg"> - <svg + <svg :viewBox="outterViewBox" ref="baseSvg"> <g @@ -235,7 +239,7 @@ class="y-axis" transform="translate(70, 20)"> </g> - <monitoring-legends + <monitoring-legends :graph-width="graphWidth" :graph-height="graphHeight" :margin="margin" @@ -245,7 +249,7 @@ :y-axis-label="yAxisLabel" :metric-usage="metricUsage" /> - <svg + <svg class="graph-data" :viewBox="innerViewBox" ref="graphData"> @@ -263,7 +267,7 @@ stroke-width="2" transform="translate(-5, 20)"> </path> - <rect + <rect class="prometheus-graph-overlay" :width="(graphWidth - 70)" :height="(graphHeight - 100)" @@ -277,7 +281,7 @@ :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" /> - <monitoring-flag + <monitoring-flag v-if="showFlag" :current-x-coordinate="currentXCoordinate" :current-y-coordinate="currentYCoordinate" diff --git a/app/assets/javascripts/monitoring/components/monitoring_flag.vue b/app/assets/javascripts/monitoring/components/monitoring_flag.vue index 180a771415b..5a0e50fcab3 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_flag.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_flag.vue @@ -87,14 +87,14 @@ </rect> <text class="text-metric text-metric-bold" - x="8" + x="16" y="35" transform="translate(-5, 20)"> {{formatTime}} </text> <text - class="text-metric-date" - x="8" + class="text-metric" + x="16" y="15" transform="translate(-5, 20)"> {{formatDate}} diff --git a/app/assets/javascripts/monitoring/components/monitoring_legends.vue b/app/assets/javascripts/monitoring/components/monitoring_legends.vue index b30ed3cc889..922a5e1bf0e 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_legends.vue +++ b/app/assets/javascripts/monitoring/components/monitoring_legends.vue @@ -109,13 +109,13 @@ </text> <rect class="rect-axis-text" - :x="xPosition + 50" + :x="xPosition + 60" :y="graphHeight - 80" - width="50" + width="35" height="50"> </rect> <text - class="label-axis-text" + class="label-axis-text x-label-text" :x="xPosition + 60" :y="yPosition" dy=".35em"> @@ -131,13 +131,13 @@ <text class="text-metric-title" x="50" - :y="graphHeight - 40"> + :y="graphHeight - 25"> {{legendTitle}} </text> <text class="text-metric-usage" x="50" - :y="graphHeight - 25"> + :y="graphHeight - 10"> {{metricUsage}} </text> </g> diff --git a/app/assets/javascripts/monitoring/utils/measurements.js b/app/assets/javascripts/monitoring/utils/measurements.js index a60d2522f49..62cd19c86e1 100644 --- a/app/assets/javascripts/monitoring/utils/measurements.js +++ b/app/assets/javascripts/monitoring/utils/measurements.js @@ -8,14 +8,14 @@ export default { }, legends: { width: 15, - height: 30, + height: 25, }, backgroundLegend: { width: 30, height: 50, }, axisLabelLineOffset: -20, - legendOffset: 52, + legendOffset: 35, }, large: { // This covers both md and lg screen sizes margin: { @@ -26,14 +26,15 @@ export default { }, legends: { width: 20, - height: 35, + height: 30, }, backgroundLegend: { width: 30, height: 150, }, axisLabelLineOffset: 20, - legendOffset: 55, + legendOffset: 38, }, - ticks: 3, + xTicks: 8, + yTicks: 3, }; diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 555b8c8a65c..1a68c5bca00 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -21,7 +21,7 @@ import CommentTypeToggle from './comment_type_toggle'; import loadAwardsHandler from './awards_handler'; import './autosave'; import './dropzone_input'; -import './task_list'; +import TaskList from './task_list'; window.autosize = autosize; window.Dropzone = Dropzone; @@ -71,7 +71,7 @@ export default class Notes { this.addBinding(); this.setPollingInterval(); this.setupMainTargetNoteForm(); - this.taskList = new gl.TaskList({ + this.taskList = new TaskList({ dataType: 'note', fieldName: 'note', selector: '.notes' diff --git a/app/assets/javascripts/oauth_remember_me.js b/app/assets/javascripts/oauth_remember_me.js new file mode 100644 index 00000000000..ffc2dd6bbca --- /dev/null +++ b/app/assets/javascripts/oauth_remember_me.js @@ -0,0 +1,32 @@ +/** + * OAuth-based login buttons have a separate "remember me" checkbox. + * + * Toggling this checkbox adds/removes a `remember_me` parameter to the + * login buttons' href, which is passed on to the omniauth callback. + **/ + +export default class OAuthRememberMe { + constructor(opts = {}) { + this.container = opts.container || ''; + this.loginLinkSelector = '.oauth-login'; + } + + bindEvents() { + $('#remember_me', this.container).on('click', this.toggleRememberMe); + } + + // eslint-disable-next-line class-methods-use-this + toggleRememberMe(event) { + const rememberMe = $(event.target).is(':checked'); + + $('.oauth-login', this.container).each((i, element) => { + const href = $(element).attr('href'); + + if (rememberMe) { + $(element).attr('href', `${href}?remember_me=1`); + } else { + $(element).attr('href', href.replace('?remember_me=1', '')); + } + }); + } +} diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js deleted file mode 100644 index de1a99fa3bd..00000000000 --- a/app/assets/javascripts/peek.js +++ /dev/null @@ -1,16 +0,0 @@ -import 'vendor/peek'; -import 'vendor/peek.performance_bar'; - -$(document).on('click', '#peek-show-queries', (e) => { - e.preventDefault(); - $('.peek-rblineprof-modal').hide(); - const $modal = $('#modal-peek-pg-queries'); - if ($modal.length) { - $modal.modal('toggle'); - } -}); - -$(document).on('click', '.js-lineprof-file', (e) => { - e.preventDefault(); - $(e.target).parents('.peek-rblineprof-file').find('.data').toggle(); -}); diff --git a/app/assets/javascripts/performance_bar.js b/app/assets/javascripts/performance_bar.js new file mode 100644 index 00000000000..9bbdf7f513c --- /dev/null +++ b/app/assets/javascripts/performance_bar.js @@ -0,0 +1,62 @@ +import 'vendor/peek'; +import 'vendor/peek.performance_bar'; + +export default class PerformanceBar { + constructor(opts) { + if (!PerformanceBar.singleton) { + this.init(opts); + PerformanceBar.singleton = this; + } + return PerformanceBar.singleton; + } + + init(opts) { + const $container = $(opts.container); + this.$sqlProfileLink = $container.find('.js-toggle-modal-peek-sql'); + this.$sqlProfileModal = $container.find('#modal-peek-pg-queries'); + this.$lineProfileLink = $container.find('.js-toggle-modal-peek-line-profile'); + this.$lineProfileModal = $('#modal-peek-line-profile'); + this.initEventListeners(); + this.showModalOnLoad(); + } + + initEventListeners() { + this.$sqlProfileLink.on('click', () => this.handleSQLProfileLink()); + this.$lineProfileLink.on('click', e => this.handleLineProfileLink(e)); + $(document).on('click', '.js-lineprof-file', PerformanceBar.toggleLineProfileFile); + } + + showModalOnLoad() { + // When a lineprofiler query-string param is present, we show the line + // profiler modal upon page load + if (/lineprofiler/.test(window.location.search)) { + PerformanceBar.toggleModal(this.$lineProfileModal); + } + } + + handleSQLProfileLink() { + PerformanceBar.toggleModal(this.$sqlProfileModal); + } + + handleLineProfileLink(e) { + const lineProfilerParameter = gl.utils.getParameterValues('lineprofiler'); + const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`); + const shouldToggleModal = lineProfilerParameter.length > 0 && + lineProfilerParameterRegex.test(e.currentTarget.href); + + if (shouldToggleModal) { + e.preventDefault(); + PerformanceBar.toggleModal(this.$lineProfileModal); + } + } + + static toggleModal($modal) { + if ($modal.length) { + $modal.modal('toggle'); + } + } + + static toggleLineProfileFile(e) { + $(e.currentTarget).parents('.peek-rblineprof-file').find('.data').toggle(); + } +} diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index c0f757269cb..fd89a1a85c3 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */ +import VisibilitySelect from './visibility_select'; + function highlightChanges($elm) { $elm.addClass('highlight-changes'); setTimeout(() => $elm.removeClass('highlight-changes'), 10); @@ -30,7 +32,7 @@ function highlightChanges($elm) { ProjectNew.prototype.initVisibilitySelect = function() { const visibilityContainer = document.querySelector('.js-visibility-select'); if (!visibilityContainer) return; - const visibilitySelect = new gl.VisibilitySelect(visibilityContainer); + const visibilitySelect = new VisibilitySelect(visibilityContainer); visibilitySelect.init(); const $visibilitySelect = $(visibilityContainer).find('select'); diff --git a/app/assets/javascripts/signin_tabs_memoizer.js b/app/assets/javascripts/signin_tabs_memoizer.js index 3997a695d15..20255398047 100644 --- a/app/assets/javascripts/signin_tabs_memoizer.js +++ b/app/assets/javascripts/signin_tabs_memoizer.js @@ -6,7 +6,7 @@ import AccessorUtilities from './lib/utils/accessor'; * Memorize the last selected tab after reloading a page. * Does that setting the current selected tab in the localStorage */ -class ActiveTabMemoizer { +export default class SigninTabsMemoizer { constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) { this.currentTabKey = currentTabKey; this.tabSelector = tabSelector; @@ -51,5 +51,3 @@ class ActiveTabMemoizer { return window.localStorage.getItem(this.currentTabKey); } } - -window.ActiveTabMemoizer = ActiveTabMemoizer; diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 00d04ce0c33..4505a79a2df 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -2,18 +2,13 @@ import FilesCommentButton from './files_comment_button'; -window.SingleFileDiff = (function() { - var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER; +const WRAPPER = '<div class="diff-content"></div>'; +const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; +const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'; +const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; - WRAPPER = '<div class="diff-content"></div>'; - - LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; - - ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'; - - COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; - - function SingleFileDiff(file) { +export default class SingleFileDiff { + constructor(file) { this.file = file; this.toggleDiff = this.toggleDiff.bind(this); this.content = $('.diff-content', this.file); @@ -37,7 +32,7 @@ window.SingleFileDiff = (function() { }).bind(this)); } - SingleFileDiff.prototype.toggleDiff = function($target, cb) { + toggleDiff($target, cb) { if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; this.isOpen = !this.isOpen; if (!this.isOpen && !this.hasError) { @@ -58,9 +53,9 @@ window.SingleFileDiff = (function() { this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); return this.getContentHTML(cb); } - }; + } - SingleFileDiff.prototype.getContentHTML = function(cb) { + getContentHTML(cb) { this.collapsedContent.hide(); this.loadingContent.show(); $.get(this.diffForPath, (function(_this) { @@ -84,15 +79,5 @@ window.SingleFileDiff = (function() { if (cb) cb(); }; })(this)); - }; - - return SingleFileDiff; -})(); - -$.fn.singleFileDiff = function() { - return this.each(function() { - if (!$.data(this, 'singleFileDiff')) { - return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this)); - } - }); -}; + } +} diff --git a/app/assets/javascripts/snippets_list.js b/app/assets/javascripts/snippets_list.js index da7b9e08447..3b6d999b1c3 100644 --- a/app/assets/javascripts/snippets_list.js +++ b/app/assets/javascripts/snippets_list.js @@ -1,9 +1,9 @@ -/* eslint-disable arrow-parens, no-param-reassign, space-before-function-paren, func-names, no-var, max-len */ - -window.gl.SnippetsList = function() { - var $holder = $('.snippets-list-holder'); +function SnippetsList() { + const $holder = $('.snippets-list-holder'); $holder.find('.pagination').on('ajax:success', (e, data) => { $holder.replaceWith(data.html); }); -}; +} + +window.gl.SnippetsList = SnippetsList; diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index 840ae1edd9d..6d38124f1c1 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,8 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */ /* global Flash */ -window.Star = (function() { - function Star() { +export default class Star { + constructor() { $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) { var $starIcon, $starSpan, $this, toggleStar; $this = $(this); @@ -23,6 +23,4 @@ window.Star = (function() { new Flash('Star toggle failed. Try again later.', 'alert'); }); } - - return Star; -})(); +} diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index a48434181b6..37e39ce5477 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,7 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */ -window.SubscriptionSelect = (function() { - function SubscriptionSelect() { +class SubscriptionSelect { + constructor() { $('.js-subscription-event').each(function(i, el) { var fieldName; fieldName = $(el).data("field-name"); @@ -28,6 +28,6 @@ window.SubscriptionSelect = (function() { }); }); } +} - return SubscriptionSelect; -})(); +window.SubscriptionSelect = SubscriptionSelect; diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 419c458ff34..c39f569da5e 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -2,7 +2,7 @@ import 'deckar01-task_list'; -class TaskList { +export default class TaskList { constructor(options = {}) { this.selector = options.selector; this.dataType = options.dataType; @@ -48,6 +48,3 @@ class TaskList { }); } } - -window.gl = window.gl || {}; -window.gl.TaskList = TaskList; diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index 7230946b484..cd305631c10 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -2,7 +2,7 @@ import UsersSelect from './users_select'; -class Todos { +export default class Todos { constructor() { this.initFilters(); this.bindEvents(); @@ -159,6 +159,3 @@ class Todos { } } } - -window.gl = window.gl || {}; -gl.Todos = Todos; diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 77ae6109bc6..7777ed1c3dc 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,7 +1,7 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */ -window.TreeView = (function() { - function TreeView() { +export default class TreeView { + constructor() { this.initKeyNav(); // Code browser tree slider // Make the entire tree-item row clickable, but not if clicking another link (like a commit message) @@ -22,7 +22,7 @@ window.TreeView = (function() { $('span.log_loading:first').removeClass('hide'); } - TreeView.prototype.initKeyNav = function() { + initKeyNav() { var li, liSelected; li = $("tr.tree-item"); liSelected = null; @@ -60,7 +60,5 @@ window.TreeView = (function() { } } }); - }; - - return TreeView; -})(); + } +} diff --git a/app/assets/javascripts/usage_ping.js b/app/assets/javascripts/usage_ping.js index fd3af7d7ab6..2389056bd02 100644 --- a/app/assets/javascripts/usage_ping.js +++ b/app/assets/javascripts/usage_ping.js @@ -1,4 +1,4 @@ -function UsagePing() { +export default function UsagePing() { const usageDataUrl = $('.usage-data').data('endpoint'); $.ajax({ @@ -10,6 +10,3 @@ function UsagePing() { }, }); } - -window.gl = window.gl || {}; -window.gl.UsagePing = UsagePing; diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js index 3ab9ef5408e..9ef94ac7616 100644 --- a/app/assets/javascripts/user.js +++ b/app/assets/javascripts/user.js @@ -1,6 +1,7 @@ /* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */ import Cookies from 'js-cookie'; +import UserTabs from './user_tabs'; class User { constructor({ action }) { @@ -17,7 +18,7 @@ class User { } initTabs() { - return new window.gl.UserTabs({ + return new UserTabs({ parentEl: '.user-profile', action: this.action }); diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js index be70f4cb4e2..f8e23c8624d 100644 --- a/app/assets/javascripts/user_tabs.js +++ b/app/assets/javascripts/user_tabs.js @@ -60,7 +60,7 @@ content on the Users#show page. </div> */ -class UserTabs { +export default class UserTabs { constructor ({ defaultAction, action, parentEl }) { this.loaded = {}; this.defaultAction = defaultAction || 'activity'; @@ -171,6 +171,3 @@ class UserTabs { return this.$parentEl.find('.nav-links .active a').data('action'); } } - -window.gl = window.gl || {}; -window.gl.UserTabs = UserTabs; diff --git a/app/assets/javascripts/username_validator.js b/app/assets/javascripts/username_validator.js index abe6c30f4f3..a348d69153c 100644 --- a/app/assets/javascripts/username_validator.js +++ b/app/assets/javascripts/username_validator.js @@ -8,7 +8,7 @@ const successMessageSelector = '.username .validation-success'; const pendingMessageSelector = '.username .validation-pending'; const invalidMessageSelector = '.username .gl-field-error'; -class UsernameValidator { +export default class UsernameValidator { constructor() { this.inputElement = $('#new_user_username'); this.inputDomElement = this.inputElement.get(0); @@ -129,5 +129,3 @@ class UsernameValidator { $inputErrorMessage.show(); } } - -window.UsernameValidator = UsernameValidator; diff --git a/app/assets/javascripts/version_check_image.js b/app/assets/javascripts/version_check_image.js index 88ba991af47..ec515e892c6 100644 --- a/app/assets/javascripts/version_check_image.js +++ b/app/assets/javascripts/version_check_image.js @@ -3,6 +3,3 @@ export default class VersionCheckImage { imageElement.off('error').on('error', () => imageElement.hide()); } } - -window.gl = window.gl || {}; -gl.VersionCheckImage = VersionCheckImage; diff --git a/app/assets/javascripts/visibility_select.js b/app/assets/javascripts/visibility_select.js index b6bbbaa0936..0c928d0d5f6 100644 --- a/app/assets/javascripts/visibility_select.js +++ b/app/assets/javascripts/visibility_select.js @@ -1,4 +1,4 @@ -class VisibilitySelect { +export default class VisibilitySelect { constructor(container) { if (!container) throw new Error('VisibilitySelect requires a container element as argument 1'); this.container = container; @@ -19,6 +19,3 @@ class VisibilitySelect { this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description; } } - -window.gl = window.gl || {}; -window.gl.VisibilitySelect = VisibilitySelect; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js index e8b3cf2f729..c02e10128e2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js @@ -17,9 +17,6 @@ export default { return hasCI && !ciStatus; }, - hasPipeline() { - return Object.keys(this.mr.pipeline || {}).length > 0; - }, svg() { return statusIconEntityMap.icon_status_failed; }, @@ -33,11 +30,7 @@ export default { template: ` <div class="mr-widget-heading"> <div class="ci-widget"> - <template v-if="!hasPipeline"> - <i class="fa fa-spinner fa-spin append-right-10" aria-hidden="true"></i> - Waiting for pipeline... - </template> - <template v-else-if="hasCIError"> + <template v-if="hasCIError"> <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error"> <span class="js-icon-link icon-link"> <span diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js index 03d183ebd84..00676bcb0b3 100644 --- a/app/assets/javascripts/wikis.js +++ b/app/assets/javascripts/wikis.js @@ -1,10 +1,9 @@ -/* eslint-disable no-param-reassign */ /* global Breakpoints */ import 'vendor/jquery.nicescroll'; import './breakpoints'; -class Wikis { +export default class Wikis { constructor() { this.bp = Breakpoints.get(); this.sidebarEl = document.querySelector('.js-wiki-sidebar'); @@ -63,6 +62,3 @@ class Wikis { } } } - -window.gl = window.gl || {}; -window.gl.Wikis = Wikis; diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js index 08f80735e93..99c7644e4d9 100644 --- a/app/assets/javascripts/zen_mode.js +++ b/app/assets/javascripts/zen_mode.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len, class-methods-use-this */ /* global Mousetrap */ // Zen Mode (full screen) textarea @@ -35,8 +35,8 @@ window.Dropzone = Dropzone; // **Target** a.js-zen-leave // -window.ZenMode = (function() { - function ZenMode() { +export default class ZenMode { + constructor() { this.active_backdrop = null; this.active_textarea = null; $(document).on('click', '.js-zen-enter', function(e) { @@ -66,7 +66,7 @@ window.ZenMode = (function() { }); } - ZenMode.prototype.enter = function(backdrop) { + enter(backdrop) { Mousetrap.pause(); this.active_backdrop = $(backdrop); this.active_backdrop.addClass('fullscreen'); @@ -74,9 +74,9 @@ window.ZenMode = (function() { // Prevent a user-resized textarea from persisting to fullscreen this.active_textarea.removeAttr('style'); return this.active_textarea.focus(); - }; + } - ZenMode.prototype.exit = function() { + exit() { if (this.active_textarea) { Mousetrap.unpause(); this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen'); @@ -85,13 +85,11 @@ window.ZenMode = (function() { this.active_backdrop = null; return Dropzone.forElement('.div-dropzone').enable(); } - }; + } - ZenMode.prototype.scrollTo = function(zen_area) { + scrollTo(zen_area) { return $.scrollTo(zen_area, 0, { offset: -150 }); - }; - - return ZenMode; -})(); + } +} diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 7098203321d..a28f54936be 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -21,3 +21,9 @@ body.modal-open { width: 860px; } } + +@media (min-width: $screen-lg-min) { + .modal-full { + width: 98%; + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index da4d91511e0..2e4b1280a97 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -594,3 +594,15 @@ Convdev Index $color-high-score: $green-400; $color-average-score: $orange-400; $color-low-score: $red-400; + +/* +Performance Bar +*/ +$perf-bar-text: #999; +$perf-bar-production: #222; +$perf-bar-staging: #291430; +$perf-bar-development: #4c1210; +$perf-bar-bucket-bg: #111; +$perf-bar-bucket-color: #ccc; +$perf-bar-bucket-box-shadow-from: rgba($white-light, .2); +$perf-bar-bucket-box-shadow-to: rgba($black, .25); diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index e9a679b20c2..00ebf4e26ac 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -187,8 +187,7 @@ } .text-metric { - font-weight: 600; - font-size: 14px; + font-size: 12px; } .selected-metric-line { @@ -232,10 +231,6 @@ width: 100%; padding: 0; padding-bottom: 100%; - - .text-metric-bold { - font-weight: 600; - } } .prometheus-svg-container > svg { @@ -250,6 +245,10 @@ stroke-width: 0; } + .text-metric-bold { + font-weight: 600; + } + .label-axis-text, .text-metric-usage { fill: $black; @@ -269,6 +268,15 @@ font-size: 12px; } + .y-label-text, + .x-label-text { + fill: $gray-darkest; + } + + .axis-tick { + stroke: $gray-darker; + } + @media (max-width: $screen-sm-max) { .label-axis-text, .text-metric-usage, diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 59e0624d94e..7adf17dddb8 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -731,11 +731,11 @@ .merge-request-tabs-holder { top: $header-height; - z-index: 100; + z-index: 200; background-color: $white-light; border-bottom: 1px solid $border-color; - @media(min-width: $screen-sm-min) { + @media (min-width: $screen-sm-min) { position: sticky; position: -webkit-sticky; } @@ -770,6 +770,12 @@ max-width: $limited-layout-width; margin-left: auto; margin-right: auto; + + .inner-page-scroll-tabs { + background-color: $white-light; + margin-left: -$gl-padding; + padding-left: $gl-padding; + } } } diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss new file mode 100644 index 00000000000..2890b6b1e49 --- /dev/null +++ b/app/assets/stylesheets/performance_bar.scss @@ -0,0 +1,103 @@ +@import "framework/variables"; +@import "peek/views/performance_bar"; +@import "peek/views/rblineprof"; + +#peek { + height: 35px; + background: $black; + line-height: 35px; + color: $perf-bar-text; + + &.disabled { + display: none; + } + + &.production { + background-color: $perf-bar-production; + } + + &.staging { + background-color: $perf-bar-staging; + } + + &.development { + background-color: $perf-bar-development; + } + + .wrapper { + width: 1000px; + margin: 0 auto; + } + + // UI Elements + .bucket { + background: $perf-bar-bucket-bg; + display: inline-block; + padding: 4px 6px; + font-family: Consolas, "Liberation Mono", Courier, monospace; + line-height: 1; + color: $perf-bar-bucket-color; + border-radius: 3px; + box-shadow: 0 1px 0 $perf-bar-bucket-box-shadow-from, inset 0 1px 2px $perf-bar-bucket-box-shadow-to; + + .hidden { + display: none; + } + + &:hover .hidden { + display: inline; + } + } + + strong { + color: $white-light; + } + + table { + color: $black; + + strong { + color: $black; + } + } + + .view { + margin-right: 15px; + float: left; + + &:last-child { + margin-right: 0; + } + } + + .css-truncate { + &.css-truncate-target, + .css-truncate-target { + display: inline-block; + max-width: 125px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: top; + } + + &.expandable:hover .css-truncate-target, + &.expandable:hover.css-truncate-target { + max-width: 10000px !important; + } + } +} + +#modal-peek-pg-queries-content { + color: $black; +} + +.peek-rblineprof-file { + pre.duration { + width: 280px; + } + + .data { + overflow: visible; + } +} diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 650ec1e326a..693e2f6365c 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -47,7 +47,7 @@ module IssuableCollections end def merge_requests_collection - merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, :head_pipeline, target_project: :namespace) + merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :head_pipeline, target_project: :namespace, merge_request_diff: :merge_request_diff_commits) end def issues_finder diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb new file mode 100644 index 00000000000..0142ad8278c --- /dev/null +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -0,0 +1,24 @@ +module Groups + module Settings + class CiCdController < Groups::ApplicationController + before_action :authorize_admin_pipeline! + + def show + define_secret_variables + end + + private + + def define_secret_variables + @variable = Ci::GroupVariable.new(group: group) + .present(current_user: current_user) + @variables = group.variables.order_key_asc + .map { |variable| variable.present(current_user: current_user) } + end + + def authorize_admin_pipeline! + return render_404 unless can?(current_user, :admin_pipeline, group) + end + end + end +end diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb new file mode 100644 index 00000000000..10038ff3ad9 --- /dev/null +++ b/app/controllers/groups/variables_controller.rb @@ -0,0 +1,64 @@ +module Groups + class VariablesController < Groups::ApplicationController + before_action :variable, only: [:show, :update, :destroy] + before_action :authorize_admin_build! + + def index + redirect_to group_settings_ci_cd_path(group) + end + + def show + end + + def update + if variable.update(variable_params) + redirect_to group_variables_path(group), + notice: 'Variable was successfully updated.' + else + render "show" + end + end + + def create + @variable = group.variables.create(variable_params) + .present(current_user: current_user) + + if @variable.persisted? + redirect_to group_settings_ci_cd_path(group), + notice: 'Variable was successfully created.' + else + render "show" + end + end + + def destroy + if variable.destroy + redirect_to group_settings_ci_cd_path(group), + status: 302, + notice: 'Variable was successfully removed.' + else + redirect_to group_settings_ci_cd_path(group), + status: 302, + notice: 'Failed to remove the variable.' + end + end + + private + + def variable_params + params.require(:variable).permit(*variable_params_attributes) + end + + def variable_params_attributes + %i[key value protected] + end + + def variable + @variable ||= group.variables.find(params[:id]).present(current_user: current_user) + end + + def authorize_admin_build! + return render_404 unless can?(current_user, :admin_build, group) + end + end +end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b82681b197e..323d5d26eb6 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,5 +1,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController include AuthenticatesWithTwoFactor + include Devise::Controllers::Rememberable protect_from_forgery except: [:kerberos, :saml, :cas3] @@ -115,8 +116,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController if @user.persisted? && @user.valid? log_audit_event(@user, with: oauth['provider']) if @user.two_factor_enabled? + params[:remember_me] = '1' if remember_me? prompt_for_two_factor(@user) else + remember_me(@user) if remember_me? sign_in_and_redirect(@user) end else @@ -147,4 +150,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController AuditEventService.new(user, user, options) .for_authentication.security_event end + + def remember_me? + request_params = request.env['omniauth.params'] + (request_params['remember_me'] == '1') if request_params.present? + end end diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 919d021b59c..29e223a5273 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -15,6 +15,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do + Gitlab::PollingInterval.set_header(response, interval: 3_000) + render json: { environments: EnvironmentSerializer .new(project: @project, current_user: @current_user) diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 24fe78bc1bd..ea7ceb3eaa5 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -21,7 +21,10 @@ module Projects end def define_secret_variables - @variable = Ci::Variable.new + @variable = Ci::Variable.new(project: project) + .present(current_user: current_user) + @variables = project.variables.order_key_asc + .map { |variable| variable.present(current_user: current_user) } end def define_triggers_variables diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 573d1c05622..6a825137564 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -1,4 +1,5 @@ class Projects::VariablesController < Projects::ApplicationController + before_action :variable, only: [:show, :update, :destroy] before_action :authorize_admin_build! layout 'project_settings' @@ -8,43 +9,52 @@ class Projects::VariablesController < Projects::ApplicationController end def show - @variable = @project.variables.find(params[:id]) end def update - @variable = @project.variables.find(params[:id]) - - if @variable.update_attributes(project_params) - redirect_to project_variables_path(project), notice: 'Variable was successfully updated.' + if variable.update(variable_params) + redirect_to project_variables_path(project), + notice: 'Variable was successfully updated.' else - render action: "show" + render "show" end end def create - @variable = Ci::Variable.new(project_params) + @variable = project.variables.create(variable_params) + .present(current_user: current_user) - if @variable.valid? && @project.variables << @variable - flash[:notice] = 'Variables were successfully updated.' - redirect_to project_settings_ci_cd_path(project) + if @variable.persisted? + redirect_to project_settings_ci_cd_path(project), + notice: 'Variable was successfully created.' else render "show" end end def destroy - @key = @project.variables.find(params[:id]) - @key.destroy - - redirect_to project_settings_ci_cd_path(project), - status: 302, - notice: 'Variable was successfully removed.' + if variable.destroy + redirect_to project_settings_ci_cd_path(project), + status: 302, + notice: 'Variable was successfully removed.' + else + redirect_to project_settings_ci_cd_path(project), + status: 302, + notice: 'Failed to remove the variable.' + end end private - def project_params - params.require(:variable) - .permit([:id, :key, :value, :protected, :_destroy]) + def variable_params + params.require(:variable).permit(*variable_params_attributes) + end + + def variable_params_attributes + %i[id key value protected _destroy] + end + + def variable + @variable ||= project.variables.find(params[:id]).present(current_user: current_user) end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 8bfbe37c543..aa80dfc3f37 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -28,7 +28,14 @@ class ProjectsFinder < UnionFinder end def execute - collection = init_collection + user = params.delete(:user) + collection = + if user + PersonalProjectsFinder.new(user).execute(current_user) + else + init_collection + end + collection = by_ids(collection) collection = by_personal(collection) collection = by_starred(collection) diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 4bb04867e0f..b769462abc2 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -23,7 +23,6 @@ module NavHelper def nav_header_class class_name = '' class_name << " with-horizontal-nav" if defined?(nav) && nav - class_name << " with-peek" if performance_bar_enabled? class_name end diff --git a/app/models/appearance.rb b/app/models/appearance.rb index c79326e8427..f9c48482be7 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -10,5 +10,5 @@ class Appearance < ActiveRecord::Base mount_uploader :logo, AttachmentUploader mount_uploader :header_logo, AttachmentUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 4f9c7235a30..14516091495 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -13,13 +13,13 @@ class ApplicationSetting < ActiveRecord::Base [\r\n] # any number of newline characters }x - serialize :restricted_visibility_levels # rubocop:disable Cop/ActiverecordSerialize - serialize :import_sources # rubocop:disable Cop/ActiverecordSerialize - serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_whitelist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_blacklist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :repository_storages # rubocop:disable Cop/ActiverecordSerialize - serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize + serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize + serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_blacklist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize + serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiveRecordSerialize cache_markdown_field :sign_in_text cache_markdown_field :help_page_text diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 46d412fbd72..112a8778b4e 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,5 +1,5 @@ class AuditEvent < ActiveRecord::Base - serialize :details, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user, foreign_key: :author_id diff --git a/app/models/blob_viewer/readme.rb b/app/models/blob_viewer/readme.rb index 75c373a03bb..4604a9934a0 100644 --- a/app/models/blob_viewer/readme.rb +++ b/app/models/blob_viewer/readme.rb @@ -10,5 +10,11 @@ module BlobViewer def visible_to?(current_user) can?(current_user, :read_wiki, project) end + + def render_error + return if project.has_external_wiki? || (project.wiki_enabled? && project.wiki.has_home_page?) + + :no_wiki + end end end diff --git a/app/models/board.rb b/app/models/board.rb index 18081a32157..97d0f550925 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,7 +1,7 @@ class Board < ActiveRecord::Base belongs_to :project - has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all + has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent validates :project, presence: true diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 2e7a80d308b..25e75a19f37 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -19,8 +19,8 @@ module Ci ) end - serialize :options # rubocop:disable Cop/ActiverecordSerialize - serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiverecordSerialize + serialize :options # rubocop:disable Cop/ActiveRecordSerialize + serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize delegate :name, to: :project, prefix: true @@ -186,6 +186,12 @@ module Ci # Variables whose value does not depend on environment def simple_variables + variables(environment: nil) + end + + # All variables, including those dependent on environment, which could + # contain unexpanded variables. + def variables(environment: persisted_environment) variables = predefined_variables variables += project.predefined_variables variables += pipeline.predefined_variables @@ -194,15 +200,12 @@ module Ci variables += project.deployment_variables if has_environment? variables += yaml_variables variables += user_variables - variables += project.secret_variables_for(ref).map(&:to_runner_variable) + variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group + variables += secret_variables(environment: environment) variables += trigger_request.user_variables if trigger_request - variables - end + variables += persisted_environment_variables if environment - # All variables, including those dependent on environment, which could - # contain unexpanded variables. - def variables - simple_variables.concat(persisted_environment_variables) + variables end def merge_request @@ -216,7 +219,7 @@ module Ci .reorder(iid: :desc) merge_requests.find do |merge_request| - merge_request.commits_sha.include?(pipeline.sha) + merge_request.commit_shas.include?(pipeline.sha) end end end @@ -370,6 +373,11 @@ module Ci ] end + def secret_variables(environment: persisted_environment) + project.secret_variables_for(ref: ref, environment: environment) + .map(&:to_runner_variable) + end + def steps [Gitlab::Ci::Build::Step.from_commands(self), Gitlab::Ci::Build::Step.from_after_script(self)].compact diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb new file mode 100644 index 00000000000..f64bc245a67 --- /dev/null +++ b/app/models/ci/group_variable.rb @@ -0,0 +1,13 @@ +module Ci + class GroupVariable < ActiveRecord::Base + extend Ci::Model + include HasVariable + include Presentable + + belongs_to :group + + validates :key, uniqueness: { scope: :group_id } + + scope :unprotected, -> { where(protected: false) } + end +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index c5847dee7f7..b646b32fc64 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -14,7 +14,7 @@ module Ci has_many :stages has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :builds, foreign_key: :commit_id - has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id + has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent # Merge requests for which the current pipeline is running against # the merge request's latest commit. diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index d12f96f3d0b..f5790f9744f 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -8,7 +8,7 @@ module Ci FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze has_many :builds - has_many :runner_projects, dependent: :destroy + has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :runner_projects has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 564334ad1ad..c58ce5c3717 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -6,7 +6,7 @@ module Ci belongs_to :pipeline, foreign_key: :commit_id has_many :builds - serialize :variables # rubocop:disable Cop/ActiverecordSerialize + serialize :variables # rubocop:disable Cop/ActiveRecordSerialize def user_variables return [] unless variables diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 0b8d0ff881a..cf0fe04ddaf 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -2,6 +2,7 @@ module Ci class Variable < ActiveRecord::Base extend Ci::Model include HasVariable + include Presentable belongs_to :project diff --git a/app/models/commit.rb b/app/models/commit.rb index 20206d57c4c..c7f62617c4c 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -138,7 +138,7 @@ class Commit safe_message.split("\n", 2)[1].try(:chomp) end - + def description? description.present? end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index a7fd0a15f0f..f4f9b037957 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,7 +2,7 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent if self < Participable # By default we always load award_emoji user association diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index eb32bf3d32a..95152dcd68c 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -78,7 +78,7 @@ module CacheMarkdownField def cached_html_up_to_date?(markdown_field) html_field = cached_markdown_fields.html_field(markdown_field) - cached = !cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? + cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? return false unless cached markdown_changed = attribute_changed?(markdown_field) || false diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 41c8b525273..23cb85600da 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -30,7 +30,7 @@ module Issuable belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' belongs_to :milestone - has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do + has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do # rubocop:disable Cop/ActiveRecordDependent def authors_loaded? # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:author).loaded? } @@ -42,9 +42,9 @@ module Issuable end end - has_many :label_links, as: :target, dependent: :destroy + has_many :label_links, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :labels, through: :label_links - has_many :todos, as: :target, dependent: :destroy + has_many :todos, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :metrics diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index 47e71c58557..fc6b840f7a8 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -17,7 +17,7 @@ module ProtectedRef class_methods do def protected_ref_access_levels(*types) types.each do |type| - has_many :"#{type}_access_levels", dependent: :destroy + has_many :"#{type}_access_levels", dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." } diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index ee108f010a6..f5048d17d80 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -4,8 +4,8 @@ module Routable extend ActiveSupport::Concern included do - has_one :route, as: :source, autosave: true, dependent: :destroy - has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy + has_one :route, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates_associated :route validates :route, presence: true diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index c28974a3cdf..67ecf470f7e 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -3,6 +3,8 @@ module ShaAttribute module ClassMethods def sha_attribute(name) + return unless table_exists? + column = columns.find { |c| c.name == name.to_s } # In case the table doesn't exist we won't be able to find the column, diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index 647a6cad3d7..bd75f25a210 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -8,7 +8,7 @@ module Spammable end included do - has_one :user_agent_detail, as: :subject, dependent: :destroy + has_one :user_agent_detail, as: :subject, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent attr_accessor :spam attr_accessor :spam_log diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb index f60a0f8f438..274b38a7708 100644 --- a/app/models/concerns/subscribable.rb +++ b/app/models/concerns/subscribable.rb @@ -9,7 +9,7 @@ module Subscribable extend ActiveSupport::Concern included do - has_many :subscriptions, dependent: :destroy, as: :subscribable + has_many :subscriptions, dependent: :destroy, as: :subscribable # rubocop:disable Cop/ActiveRecordDependent end def subscribed?(user, project = nil) diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb index 9cf83440784..b517ddaebd7 100644 --- a/app/models/concerns/time_trackable.rb +++ b/app/models/concerns/time_trackable.rb @@ -18,7 +18,7 @@ module TimeTrackable validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false validate :check_negative_time_spent - has_many :timelogs, dependent: :destroy + has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end def spend_time(options) diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 053f2a11aa0..51768dd96bc 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -1,5 +1,5 @@ class DeployKey < Key - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :deploy_keys_projects scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) } diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 20ef1378500..e9a60e6ce09 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -6,9 +6,9 @@ class DiffNote < Note NOTEABLE_TYPES = %w(MergeRequest Commit).freeze - serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize validates :original_position, presence: true validates :position, presence: true diff --git a/app/models/environment.rb b/app/models/environment.rb index eb24ff00ce3..e9ebf0637f3 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -6,7 +6,7 @@ class Environment < ActiveRecord::Base belongs_to :project, required: true, validate: true - has_many :deployments, dependent: :destroy + has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment' before_validation :nullify_external_url diff --git a/app/models/event.rb b/app/models/event.rb index 29bc141c5cd..8d93a228494 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -50,7 +50,7 @@ class Event < ActiveRecord::Base belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations # For Hash only - serialize :data # rubocop:disable Cop/ActiverecordSerialize + serialize :data # rubocop:disable Cop/ActiveRecordSerialize # Callbacks after_create :reset_project_activity diff --git a/app/models/group.rb b/app/models/group.rb index a6fdb30f84c..f29e642ac91 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -8,7 +8,7 @@ class Group < Namespace include Referable include SelectForProjectAuthorization - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent alias_method :members, :group_members has_many :users, through: :group_members has_many :owners, @@ -16,12 +16,13 @@ class Group < Namespace through: :group_members, source: :user - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' + has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent - has_many :project_group_links, dependent: :destroy + has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :shared_projects, through: :project_group_links, source: :project - has_many :notification_settings, dependent: :destroy, as: :source + has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent has_many :labels, class_name: 'GroupLabel' + has_many :variables, class_name: 'Ci::GroupVariable' validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :visibility_level_allowed_by_projects @@ -31,7 +32,7 @@ class Group < Namespace validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 } mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent after_create :post_create_hook after_destroy :post_destroy_hook @@ -248,6 +249,14 @@ class Group < Namespace } end + def secret_variables_for(ref, project) + list_of_ids = [self] + ancestors + variables = Ci::GroupVariable.where(group: list_of_ids) + variables = variables.unprotected unless project.protected_for?(ref) + variables = variables.group_by(&:group_id) + list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten + end + protected def update_two_factor_requirement diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7503f3739c3..7a9f8997959 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -12,7 +12,7 @@ class WebHook < ActiveRecord::Base default_value_for :repository_update_events, false default_value_for :enable_ssl_verification, true - has_many :web_hook_logs, dependent: :destroy + has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent scope :push_hooks, -> { where(push_events: true) } scope :tag_push_hooks, -> { where(tag_push_events: true) } diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index d73cfcf630d..e72c125fb69 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -1,9 +1,9 @@ class WebHookLog < ActiveRecord::Base belongs_to :web_hook - serialize :request_headers, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :request_data, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :response_headers, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :request_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :request_data, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :response_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize validates :web_hook, presence: true diff --git a/app/models/issue.rb b/app/models/issue.rb index a97e88f76f6..01f985823e1 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -23,9 +23,14 @@ class Issue < ActiveRecord::Base belongs_to :project belongs_to :moved_to, class_name: 'Issue' - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_many :issue_assignees + has_many :assignees, class_name: "User", through: :issue_assignees has_many :issue_assignees has_many :assignees, class_name: "User", through: :issue_assignees diff --git a/app/models/label.rb b/app/models/label.rb index ed6a8411da9..674bb3f2720 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -15,9 +15,9 @@ class Label < ActiveRecord::Base default_value_for :color, DEFAULT_COLOR - has_many :lists, dependent: :destroy + has_many :lists, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :priorities, class_name: 'LabelPriority' - has_many :label_links, dependent: :destroy + has_many :label_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 2d5909ab25e..c36be956ff0 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -7,7 +7,7 @@ class LegacyDiffNote < Note include NoteOnDiff - serialize :st_diff # rubocop:disable Cop/ActiverecordSerialize + serialize :st_diff # rubocop:disable Cop/ActiveRecordSerialize validates :line_code, presence: true, line_code: true diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 7712d5783e0..b7cf96abe83 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,5 +1,5 @@ class LfsObject < ActiveRecord::Base - has_many :lfs_objects_projects, dependent: :destroy + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :lfs_objects_projects validates :oid, presence: true, uniqueness: true diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 808212c780c..2fc6191e785 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -12,24 +12,26 @@ class MergeRequest < ActiveRecord::Base belongs_to :source_project, class_name: "Project" belongs_to :merge_user, class_name: "User" - has_many :merge_request_diffs, dependent: :destroy + has_many :merge_request_diffs has_one :merge_request_diff, -> { order('merge_request_diffs.id DESC') } belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent belongs_to :assignee, class_name: "User" - serialize :merge_params, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed - delegate :commits, :real_size, :commits_sha, :commits_count, + delegate :commits, :real_size, :commit_shas, :commits_count, to: :merge_request_diff, prefix: nil # When this attribute is true some MR validation is ignored @@ -516,7 +518,7 @@ class MergeRequest < ActiveRecord::Base def related_notes # Fetch comments only from last 100 commits commits_for_notes_limit = 100 - commit_ids = commits.last(commits_for_notes_limit).map(&:id) + commit_ids = commit_shas.take(commits_for_notes_limit) Note.where( "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" + @@ -839,15 +841,15 @@ class MergeRequest < ActiveRecord::Base return Ci::Pipeline.none unless source_project @all_pipelines ||= source_project.pipelines - .where(sha: all_commits_sha, ref: source_branch) + .where(sha: all_commit_shas, ref: source_branch) .order(id: :desc) end # Note that this could also return SHA from now dangling commits # - def all_commits_sha + def all_commit_shas if persisted? - merge_request_diffs.flat_map(&:commits_sha).uniq + merge_request_diffs.preload(:merge_request_diff_commits).flat_map(&:commit_shas).uniq elsif compare_commits compare_commits.to_a.reverse.map(&:id) else diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index f1ee4d3f7a9..4b141945ab4 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -11,9 +11,10 @@ class MergeRequestDiff < ActiveRecord::Base belongs_to :merge_request has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) } + has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) } - serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize - serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize + serialize :st_commits # rubocop:disable Cop/ActiveRecordSerialize + serialize :st_diffs # rubocop:disable Cop/ActiveRecordSerialize state_machine :state, initial: :empty do state :collected @@ -47,14 +48,13 @@ class MergeRequestDiff < ActiveRecord::Base # Collect information about commits and diff from repository # and save it to the database as serialized data def save_git_content - ensure_commits_sha + ensure_commit_shas save_commits - reload_commits save_diffs keep_around_commits end - def ensure_commits_sha + def ensure_commit_shas merge_request.fetch_ref self.start_commit_sha ||= merge_request.target_branch_sha self.head_commit_sha ||= merge_request.source_branch_sha @@ -66,7 +66,7 @@ class MergeRequestDiff < ActiveRecord::Base # created before version 8.4 that does not store head_commit_sha in separate db field. def head_commit_sha if persisted? && super.nil? - last_commit.try(:sha) + last_commit_sha else super end @@ -97,16 +97,11 @@ class MergeRequestDiff < ActiveRecord::Base end def commits - @commits ||= load_commits(st_commits) + @commits ||= load_commits end - def reload_commits - @commits = nil - commits - end - - def last_commit - commits.first + def last_commit_sha + commit_shas.first end def first_commit @@ -131,8 +126,12 @@ class MergeRequestDiff < ActiveRecord::Base project.commit(head_commit_sha) end - def commits_sha - st_commits.map { |commit| commit[:id] } + def commit_shas + if st_commits.present? + st_commits.map { |commit| commit[:id] } + else + merge_request_diff_commits.map(&:sha) + end end def diff_refs=(new_diff_refs) @@ -207,7 +206,11 @@ class MergeRequestDiff < ActiveRecord::Base end def commits_count - st_commits.count + if st_commits.present? + st_commits.size + else + merge_request_diff_commits.size + end end def utf8_st_diffs @@ -231,29 +234,6 @@ class MergeRequestDiff < ActiveRecord::Base raw.any? { |element| VALID_CLASSES.include?(element.class) } end - def dump_commits(commits) - commits.map(&:to_hash) - end - - def load_commits(array) - array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) } - end - - # Load all commits related to current merge request diff from repo - # and save it as array of hashes in st_commits db field - def save_commits - new_attributes = {} - - commits = compare.commits - - if commits.present? - commits = Commit.decorate(commits, merge_request.source_project).reverse - new_attributes[:st_commits] = dump_commits(commits) - end - - update_columns_serialized(new_attributes) - end - def create_merge_request_diff_files(diffs) rows = diffs.map.with_index do |diff, index| diff.to_hash.merge( @@ -294,12 +274,18 @@ class MergeRequestDiff < ActiveRecord::Base end end - # Load diffs between branches related to current merge request diff from repo - # and save it as array of hashes in st_diffs db field + def load_commits + commits = st_commits.presence || merge_request_diff_commits + + commits.map do |commit| + Commit.new(Gitlab::Git::Commit.new(commit.to_hash), merge_request.source_project) + end + end + def save_diffs new_attributes = {} - if commits.size.zero? + if compare.commits.size.zero? new_attributes[:state] = :empty else diff_collection = compare.diffs(Commit.max_diff_options) @@ -319,7 +305,13 @@ class MergeRequestDiff < ActiveRecord::Base new_attributes[:state] = :overflow if diff_collection.overflow? end - update_columns_serialized(new_attributes) + update(new_attributes) + end + + def save_commits + MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse) + + merge_request_diff_commits.reload end def repository @@ -332,29 +324,6 @@ class MergeRequestDiff < ActiveRecord::Base project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha) end - # - # #save or #update_attributes providing changes on serialized attributes do a lot of - # serialization and deserialization calls resulting in bad performance. - # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide. - # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized - # attributes. So to keep the same behaviour as the attribute assignment we reload the instance. - # The difference is in the usage of - # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns) - # - # Ex: - # - # new_attributes[:st_commits].first.slice(:committed_date) - # => {:committed_date=>2014-02-27 11:01:38 +0200} - # YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date))) - # => {:committed_date=>2014-02-27 10:01:38 +0100} - # - def update_columns_serialized(new_attributes) - return unless new_attributes.any? - - update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone)) - reload - end - def keep_around_commits [repository, merge_request.source_project.repository].each do |repo| repo.keep_around(start_commit_sha) diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb new file mode 100644 index 00000000000..cafdbe11849 --- /dev/null +++ b/app/models/merge_request_diff_commit.rb @@ -0,0 +1,38 @@ +class MergeRequestDiffCommit < ActiveRecord::Base + include ShaAttribute + + belongs_to :merge_request_diff + + sha_attribute :sha + alias_attribute :id, :sha + + def self.create_bulk(merge_request_diff_id, commits) + sha_attribute = Gitlab::Database::ShaAttribute.new + + rows = commits.map.with_index do |commit, index| + # See #parent_ids. + commit_hash = commit.to_hash.except(:parent_ids) + sha = commit_hash.delete(:id) + + commit_hash.merge( + merge_request_diff_id: merge_request_diff_id, + relative_order: index, + sha: sha_attribute.type_cast_for_database(sha) + ) + end + + Gitlab::Database.bulk_insert(self.table_name, rows) + end + + def to_hash + Gitlab::Git::Commit::SERIALIZE_KEYS.each_with_object({}) do |key, hash| + hash[key] = public_send(key) + end + end + + # We don't save these, because they would need a table or a serialised + # field. They aren't used anywhere, so just pretend the commit has no parents. + def parent_ids + [] + end +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d2e2749f70d..c0ccbf8e27e 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -21,7 +21,7 @@ class Milestone < ActiveRecord::Base has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 672eab94c07..15713fc5f6d 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -15,13 +15,13 @@ class Namespace < ActiveRecord::Base cache_markdown_field :description, pipeline: :description - has_many :projects, dependent: :destroy + has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :project_statistics belongs_to :owner, class_name: "User" belongs_to :parent, class_name: "Namespace" has_many :children, class_name: "Namespace", foreign_key: :parent_id - has_one :chat_team, dependent: :destroy + has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, diff --git a/app/models/note.rb b/app/models/note.rb index dfd435bcdf6..3d39047d32f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -46,8 +46,8 @@ class Note < ActiveRecord::Base belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' - has_many :todos, dependent: :destroy - has_many :events, as: :target, dependent: :destroy + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :system_note_metadata delegate :gfm_reference, :local_reference, to: :noteable diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 6e13f9b2089..654be927ed8 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -3,7 +3,7 @@ class PersonalAccessToken < ActiveRecord::Base include TokenAuthenticatable add_authentication_token_field :token - serialize :scopes, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user diff --git a/app/models/project.rb b/app/models/project.rb index 3a5a01db518..74c15d2508b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -59,6 +59,7 @@ class Project < ActiveRecord::Base update_column(:last_repository_updated_at, self.created_at) end + before_destroy :remove_private_deploy_keys after_destroy :remove_pages # update visibility_level of forks @@ -80,96 +81,108 @@ class Project < ActiveRecord::Base belongs_to :namespace has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' - has_many :boards, before_add: :validate_board_limit, dependent: :destroy + has_many :boards, before_add: :validate_board_limit # Project services - has_one :campfire_service, dependent: :destroy - has_one :drone_ci_service, dependent: :destroy - has_one :emails_on_push_service, dependent: :destroy - has_one :pipelines_email_service, dependent: :destroy - has_one :irker_service, dependent: :destroy - has_one :pivotaltracker_service, dependent: :destroy - has_one :hipchat_service, dependent: :destroy - has_one :flowdock_service, dependent: :destroy - has_one :assembla_service, dependent: :destroy - has_one :asana_service, dependent: :destroy - has_one :gemnasium_service, dependent: :destroy - has_one :mattermost_slash_commands_service, dependent: :destroy - has_one :mattermost_service, dependent: :destroy - has_one :slack_slash_commands_service, dependent: :destroy - has_one :slack_service, dependent: :destroy - has_one :buildkite_service, dependent: :destroy - has_one :bamboo_service, dependent: :destroy - has_one :teamcity_service, dependent: :destroy - has_one :pushover_service, dependent: :destroy - has_one :jira_service, dependent: :destroy - has_one :redmine_service, dependent: :destroy - has_one :custom_issue_tracker_service, dependent: :destroy - has_one :bugzilla_service, dependent: :destroy - has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project - has_one :external_wiki_service, dependent: :destroy - has_one :kubernetes_service, dependent: :destroy, inverse_of: :project - has_one :prometheus_service, dependent: :destroy, inverse_of: :project - has_one :mock_ci_service, dependent: :destroy - has_one :mock_deployment_service, dependent: :destroy - has_one :mock_monitoring_service, dependent: :destroy - has_one :microsoft_teams_service, dependent: :destroy - - has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" + has_one :campfire_service + has_one :drone_ci_service + has_one :emails_on_push_service + has_one :pipelines_email_service + has_one :irker_service + has_one :pivotaltracker_service + has_one :hipchat_service + has_one :flowdock_service + has_one :assembla_service + has_one :asana_service + has_one :gemnasium_service + has_one :mattermost_slash_commands_service + has_one :mattermost_service + has_one :slack_slash_commands_service + has_one :slack_service + has_one :buildkite_service + has_one :bamboo_service + has_one :teamcity_service + has_one :pushover_service + has_one :jira_service + has_one :redmine_service + has_one :custom_issue_tracker_service + has_one :bugzilla_service + has_one :gitlab_issue_tracker_service, inverse_of: :project + has_one :external_wiki_service + has_one :kubernetes_service, inverse_of: :project + has_one :prometheus_service, inverse_of: :project + has_one :mock_ci_service + has_one :mock_deployment_service + has_one :mock_monitoring_service + has_one :microsoft_teams_service + + has_one :forked_project_link, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link has_many :forked_project_links, foreign_key: "forked_from_project_id" has_many :forks, through: :forked_project_links, source: :forked_to_project # Merge Requests for target project should be removed with it - has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' - has_many :issues, dependent: :destroy - has_many :labels, dependent: :destroy, class_name: 'ProjectLabel' - has_many :services, dependent: :destroy - has_many :events, dependent: :destroy - has_many :milestones, dependent: :destroy - has_many :notes, dependent: :destroy - has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet' - has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' - has_many :protected_branches, dependent: :destroy - has_many :protected_tags, dependent: :destroy + has_many :merge_requests, foreign_key: 'target_project_id' + has_many :issues + has_many :labels, class_name: 'ProjectLabel' + has_many :services + has_many :events + has_many :milestones + has_many :notes + has_many :snippets, class_name: 'ProjectSnippet' + has_many :hooks, class_name: 'ProjectHook' + has_many :protected_branches + has_many :protected_tags has_many :project_authorizations has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User' - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :project_members, -> { where(requested_at: nil) }, + as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + alias_method :members, :project_members has_many :users, through: :project_members - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember' + has_many :requesters, -> { where.not(requested_at: nil) }, + as: :source, class_name: 'ProjectMember', dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects has_many :starrers, through: :users_star_projects, source: :user - has_many :releases, dependent: :destroy - has_many :lfs_objects_projects, dependent: :destroy + has_many :releases + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :lfs_objects, through: :lfs_objects_projects - has_many :project_group_links, dependent: :destroy + has_many :project_group_links has_many :invited_groups, through: :project_group_links, source: :group - has_many :pages_domains, dependent: :destroy - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy, as: :source - - has_one :import_data, dependent: :delete, class_name: 'ProjectImportData' - has_one :project_feature, dependent: :destroy - has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete - has_many :container_repositories, dependent: :destroy - - has_many :commit_statuses, dependent: :destroy - has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline' - has_many :builds, class_name: 'Ci::Build' # the builds are created from the commit_statuses - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' + has_many :pages_domains + has_many :todos + has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_one :import_data, class_name: 'ProjectImportData' + has_one :project_feature + has_one :statistics, class_name: 'ProjectStatistics' + + # Container repositories need to remove data from the container registry, + # which is not managed by the DB. Hence we're still using dependent: :destroy + # here. + has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + + has_many :commit_statuses + has_many :pipelines, class_name: 'Ci::Pipeline' + + # Ci::Build objects store data on the file system such as artifact files and + # build traces. Currently there's no efficient way of removing this data in + # bulk that doesn't involve loading the rows into memory. As a result we're + # still using `dependent: :destroy` here. + has_many :builds, class_name: 'Ci::Build', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :runner_projects, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' has_many :variables, class_name: 'Ci::Variable' - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - has_many :environments, dependent: :destroy - has_many :deployments, dependent: :destroy - has_many :pipeline_schedules, dependent: :destroy, class_name: 'Ci::PipelineSchedule' + has_many :triggers, class_name: 'Ci::Trigger' + has_many :environments + has_many :deployments + has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule' has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' @@ -224,7 +237,7 @@ class Project < ActiveRecord::Base before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :pending_delete, -> { where(pending_delete: true) } @@ -1240,7 +1253,13 @@ class Project < ActiveRecord::Base File.join(pages_path, 'public') end + def remove_private_deploy_keys + deploy_keys.where(public: false).delete_all + end + def remove_pages + ::Projects::UpdatePagesConfigurationService.new(self).execute + # 1. We rename pages to temporary directory # 2. We wait 5 minutes, due to NFS caching # 3. We asynchronously remove pages with force @@ -1326,7 +1345,8 @@ class Project < ActiveRecord::Base variables end - def secret_variables_for(ref) + def secret_variables_for(ref:, environment: nil) + # EE would use the environment if protected_for?(ref) variables else diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index e3cafd4d1c6..37730474324 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -10,7 +10,7 @@ class ProjectImportData < ActiveRecord::Base insecure_mode: true, algorithm: 'aes-256-cbc' - serialize :data, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :data, JSON # rubocop:disable Cop/ActiveRecordSerialize validates :project, presence: true diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 48e7802c557..62f7c057c5b 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -96,10 +96,13 @@ class KubernetesService < DeploymentService end def predefined_variables + config = YAML.dump(kubeconfig) + variables = [ { key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_TOKEN', value: token, public: false }, - { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true } + { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true }, + { key: 'KUBECONFIG', value: config, public: false, file: true } ] if ca_pem.present? @@ -135,6 +138,14 @@ class KubernetesService < DeploymentService private + def kubeconfig + to_kubeconfig( + url: api_url, + namespace: actual_namespace, + token: token, + ca_pem: ca_pem) + end + def namespace_placeholder default_namespace || TEMPLATE_PLACEHOLDER end diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb index 4592cb747a0..eb4da68bb7e 100644 --- a/app/models/project_services/slash_commands_service.rb +++ b/app/models/project_services/slash_commands_service.rb @@ -5,7 +5,7 @@ class SlashCommandsService < Service prop_accessor :token - has_many :chat_names, foreign_key: :service_id, dependent: :destroy + has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent def valid_token?(token) self.respond_to?(:token) && diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index beaadbbd1ab..dfca0031af8 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -63,6 +63,10 @@ class ProjectWiki !!repository.exists? end + def has_home_page? + !!find_page('home') + end + # Returns an Array of Gitlab WikiPage instances or an # empty Array if this Wiki has no pages. def pages diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index edde7bedbab..298569cb7a6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,5 +1,5 @@ class SentNotification < ActiveRecord::Base - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize belongs_to :project belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/service.rb b/app/models/service.rb index 6a0b0a5c522..6b64079215f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,7 +2,7 @@ # and implement a set of methods class Service < ActiveRecord::Base include Sortable - serialize :properties, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize default_value_for :active, false default_value_for :push_events, true @@ -51,6 +51,14 @@ class Service < ActiveRecord::Base active end + def show_active_box? + true + end + + def editable? + true + end + def template? template end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index b3aa7bb986e..09d5ff46618 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -30,7 +30,7 @@ class Snippet < ActiveRecord::Base belongs_to :author, class_name: 'User' belongs_to :project - has_many :notes, as: :noteable, dependent: :destroy + has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent delegate :name, :email, to: :author, prefix: true, allow_nil: true diff --git a/app/models/user.rb b/app/models/user.rb index 0febae84873..4411a06d429 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -41,7 +41,7 @@ class User < ActiveRecord::Base otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base devise :two_factor_backupable, otp_number_of_backup_codes: 10 - serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable @@ -67,24 +67,24 @@ class User < ActiveRecord::Base # # Namespace for personal projects - has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true + has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true # rubocop:disable Cop/ActiveRecordDependent # Profile has_many :keys, -> do type = Key.arel_table[:type] where(type.not_eq('DeployKey').or(type.eq(nil))) - end, dependent: :destroy - has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy + end, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :emails, dependent: :destroy - has_many :personal_access_tokens, dependent: :destroy - has_many :identities, dependent: :destroy, autosave: true - has_many :u2f_registrations, dependent: :destroy - has_many :chat_names, dependent: :destroy + has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent + has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Groups - has_many :members, dependent: :destroy - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' + has_many :members, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -92,35 +92,35 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :starred_projects, through: :users_star_projects, source: :project has_many :project_authorizations has_many :authorized_projects, through: :project_authorizations, source: :project - has_many :snippets, dependent: :destroy, foreign_key: :author_id - has_many :notes, dependent: :destroy, foreign_key: :author_id - has_many :issues, dependent: :destroy, foreign_key: :author_id - has_many :merge_requests, dependent: :destroy, foreign_key: :author_id - has_many :events, dependent: :destroy, foreign_key: :author_id - has_many :subscriptions, dependent: :destroy + has_many :snippets, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :notes, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :issues, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :merge_requests, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :events, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" - has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy - has_one :abuse_report, dependent: :destroy, foreign_key: :user_id - has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" - has_many :spam_logs, dependent: :destroy - has_many :builds, dependent: :nullify, class_name: 'Ci::Build' - has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy - has_many :award_emoji, dependent: :destroy - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id + has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_one :abuse_report, dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent + has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent + has_many :spam_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # rubocop:disable Cop/ActiveRecordDependent + has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' # rubocop:disable Cop/ActiveRecordDependent + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :notification_settings, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :award_emoji, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id # rubocop:disable Cop/ActiveRecordDependent has_many :issue_assignees has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue - has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" + has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent # # Validations @@ -211,7 +211,7 @@ class User < ActiveRecord::Base end mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :admins, -> { where(admin: true) } diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index dcb37416ca3..6defab75fce 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -31,6 +31,8 @@ class GroupPolicy < BasePolicy rule { master }.policy do enable :create_projects enable :admin_milestones + enable :admin_pipeline + enable :admin_build end rule { owner }.policy do diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb new file mode 100644 index 00000000000..81fea106a5c --- /dev/null +++ b/app/presenters/ci/group_variable_presenter.rb @@ -0,0 +1,25 @@ +module Ci + class GroupVariablePresenter < Gitlab::View::Presenter::Delegated + presents :variable + + def placeholder + 'GROUP_VARIABLE' + end + + def form_path + if variable.persisted? + group_variable_path(group, variable) + else + group_variables_path(group) + end + end + + def edit_path + group_variable_path(group, variable) + end + + def delete_path + group_variable_path(group, variable) + end + end +end diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb new file mode 100644 index 00000000000..5d7998393a6 --- /dev/null +++ b/app/presenters/ci/variable_presenter.rb @@ -0,0 +1,25 @@ +module Ci + class VariablePresenter < Gitlab::View::Presenter::Delegated + presents :variable + + def placeholder + 'PROJECT_VARIABLE' + end + + def form_path + if variable.persisted? + project_variable_path(project, variable) + else + project_variables_path(project) + end + end + + def edit_path + project_variable_path(project, variable) + end + + def delete_path + project_variable_path(project, variable) + end + end +end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index e0e7c43f802..c5f959a1874 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -68,7 +68,7 @@ module MergeRequests if merge_request.source_branch == @branch_name || force_push? merge_request.reload_diff(current_user) else - mr_commit_ids = merge_request.commits_sha + mr_commit_ids = merge_request.commit_shas push_commit_ids = @commits.map(&:id) matches = mr_commit_ids & push_commit_ids merge_request.reload_diff(current_user) if matches.any? @@ -128,7 +128,7 @@ module MergeRequests return unless @commits.present? merge_requests_for_source_branch.each do |merge_request| - mr_commit_ids = Set.new(merge_request.commits_sha) + mr_commit_ids = Set.new(merge_request.commit_shas) new_commits, existing_commits = @commits.partition do |commit| mr_commit_ids.include?(commit.id) @@ -144,7 +144,7 @@ module MergeRequests return unless @commits.present? merge_requests_for_source_branch.each do |merge_request| - commit_shas = merge_request.commits_sha + commit_shas = merge_request.commit_shas wip_commit = @commits.detect do |commit| commit.work_in_progress? && commit_shas.include?(commit.sha) diff --git a/app/views/projects/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml index 98f618ca3b8..98f618ca3b8 100644 --- a/app/views/projects/variables/_content.html.haml +++ b/app/views/ci/variables/_content.html.haml diff --git a/app/views/projects/variables/_form.html.haml b/app/views/ci/variables/_form.html.haml index 0a70a301cb4..eebd0955c80 100644 --- a/app/views/projects/variables/_form.html.haml +++ b/app/views/ci/variables/_form.html.haml @@ -1,12 +1,12 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @variable] do |f| += form_for @variable, as: :variable, url: @variable.form_path do |f| = form_errors(@variable) .form-group = f.label :key, "Key", class: "label-light" - = f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true + = f.text_field :key, class: "form-control", placeholder: @variable.placeholder, required: true .form-group = f.label :value, "Value", class: "label-light" - = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE" + = f.text_area :value, class: "form-control", placeholder: @variable.placeholder .form-group .checkbox = f.label :protected do diff --git a/app/views/projects/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 5e6786f6698..007c2344b5a 100644 --- a/app/views/projects/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -1,16 +1,16 @@ .row.prepend-top-default.append-bottom-default .col-lg-4 - = render "projects/variables/content" + = render "ci/variables/content" .col-lg-8 %h5.prepend-top-0 Add a variable - = render "projects/variables/form", btn_text: "Add new variable" + = render "ci/variables/form", btn_text: "Add new variable" %hr %h5.prepend-top-0 - Your variables (#{@project.variables.size}) - - if @project.variables.empty? + Your variables (#{@variables.size}) + - if @variables.empty? %p.settings-message.text-center.append-bottom-0 No variables found, add one with the form above. - else - = render "projects/variables/table" + = render "ci/variables/table" %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values diff --git a/app/views/ci/variables/_show.html.haml b/app/views/ci/variables/_show.html.haml new file mode 100644 index 00000000000..2bfb290629d --- /dev/null +++ b/app/views/ci/variables/_show.html.haml @@ -0,0 +1,9 @@ +- page_title "Variables" + +.row.prepend-top-default.append-bottom-default + .col-lg-3 + = render "ci/variables/content" + .col-lg-9 + %h5.prepend-top-0 + Update variable + = render "ci/variables/form", btn_text: "Save variable" diff --git a/app/views/projects/variables/_table.html.haml b/app/views/ci/variables/_table.html.haml index 4ce6a828812..71a0b56c4f4 100644 --- a/app/views/projects/variables/_table.html.haml +++ b/app/views/ci/variables/_table.html.haml @@ -11,18 +11,18 @@ %th Protected %th %tbody - - @project.variables.order_key_asc.each do |variable| + - @variables.each do |variable| - if variable.id? %tr %td.variable-key= variable.key %td.variable-value{ "data-value" => variable.value }****** %td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected) %td.variable-menu - = link_to project_variable_path(@project, variable), class: "btn btn-transparent btn-variable-edit" do + = link_to variable.edit_path, class: "btn btn-transparent btn-variable-edit" do %span.sr-only Update = icon("pencil") - = link_to project_variable_path(@project, variable), class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do + = link_to variable.delete_path, class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do %span.sr-only Remove = icon("trash") diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index f92f89e73ff..e80d10dc8f1 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -6,4 +6,7 @@ - providers.each do |provider| %span.light - has_icon = provider_has_icon?(provider) - = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn') + = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" + %fieldset + = check_box_tag :remember_me + = label_tag :remember_me, 'Remember Me' diff --git a/app/views/groups/_settings_head.html.haml b/app/views/groups/_settings_head.html.haml index 2454e7355a7..623d233a46a 100644 --- a/app/views/groups/_settings_head.html.haml +++ b/app/views/groups/_settings_head.html.haml @@ -12,3 +12,8 @@ = link_to projects_group_path(@group), title: 'Projects' do %span Projects + + = nav_link(controller: :ci_cd) do + = link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do + %span + Pipelines diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml new file mode 100644 index 00000000000..bf36baf48ab --- /dev/null +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -0,0 +1,4 @@ +- page_title "Pipelines" += render "groups/settings_head" + += render 'ci/variables/index' diff --git a/app/views/groups/variables/show.html.haml b/app/views/groups/variables/show.html.haml new file mode 100644 index 00000000000..df533952b76 --- /dev/null +++ b/app/views/groups/variables/show.html.haml @@ -0,0 +1 @@ += render 'ci/variables/show' diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 331d1181220..56e628a2b74 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -27,10 +27,11 @@ %td.shortcut .key f %td Focus Filter - %tr - %td.shortcut - .key p b - %td Show/hide the Performance Bar + - if performance_bar_enabled? + %tr + %td.shortcut + .key p b + %td Show/hide the Performance Bar %tr %td.shortcut .key ? diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 3d3e29733ff..6ad22958df3 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -30,7 +30,7 @@ = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "test", media: "all" if Rails.env.test? - = stylesheet_link_tag 'peek' if performance_bar_enabled? + = stylesheet_link_tag 'performance_bar' if performance_bar_enabled? - if show_new_nav? = stylesheet_link_tag "new_nav", media: "all" @@ -44,7 +44,7 @@ = webpack_bundle_tag "main" = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "test" if Rails.env.test? - = webpack_bundle_tag 'peek' if performance_bar_enabled? + = webpack_bundle_tag 'performance_bar' if performance_bar_enabled? - if content_for?(:page_specific_javascripts) = yield :page_specific_javascripts diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d879df8fc82..81f83b74826 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -3,7 +3,6 @@ = render "layouts/head" %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } = render "layouts/init_auto_complete" if @gfm_form - = render 'peek/bar' - if show_new_nav? = render "layouts/header/new" - else @@ -11,3 +10,5 @@ = render 'layouts/page', sidebar: sidebar, nav: nav = yield :scripts_body + + = render 'peek/bar' diff --git a/app/views/peek/views/_rblineprof.html.haml b/app/views/peek/views/_rblineprof.html.haml new file mode 100644 index 00000000000..6c037930ca9 --- /dev/null +++ b/app/views/peek/views/_rblineprof.html.haml @@ -0,0 +1,7 @@ +Profile: + += link_to 'all', url_for(lineprofiler: 'true'), class: 'js-toggle-modal-peek-line-profile' +\/ += link_to 'app & lib', url_for(lineprofiler: 'app'), class: 'js-toggle-modal-peek-line-profile' +\/ += link_to 'views', url_for(lineprofiler: 'views'), class: 'js-toggle-modal-peek-line-profile' diff --git a/app/views/peek/views/_sql.html.haml b/app/views/peek/views/_sql.html.haml index 16fc010f66f..dd8b524064f 100644 --- a/app/views/peek/views/_sql.html.haml +++ b/app/views/peek/views/_sql.html.haml @@ -1,13 +1,13 @@ %strong - %a#peek-show-queries{ href: '#' } + %a.js-toggle-modal-peek-sql %span{ data: { defer_to: "#{view.defer_key}-duration" } }... \/ %span{ data: { defer_to: "#{view.defer_key}-calls" } }... #modal-peek-pg-queries.modal{ tabindex: -1 } - .modal-dialog - #modal-peek-pg-queries-content.modal-content + .modal-dialog.modal-full + .modal-content .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × + %button.close.btn.btn-link.btn-sm{ type: 'button', data: { dismiss: 'modal' } } X %h4 SQL queries .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... diff --git a/app/views/projects/blob/viewers/_readme.html.haml b/app/views/projects/blob/viewers/_readme.html.haml index 507f44d4745..d8492abc638 100644 --- a/app/views/projects/blob/viewers/_readme.html.haml +++ b/app/views/projects/blob/viewers/_readme.html.haml @@ -1,4 +1,4 @@ = icon('info-circle fw') = succeed '.' do To learn more about this project, read - = link_to "the wiki", project_wikis_path(viewer.project) + = link_to "the wiki", get_project_wiki_path(viewer.project) diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml deleted file mode 100644 index c7996077bc7..00000000000 --- a/app/views/projects/project_members/_group_members.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -.panel.panel-default - .panel-heading - Group members with access to - %strong= @group.name - %span.badge= members.size - - if can?(current_user, :admin_group_member, @group) - .controls - = link_to 'Manage group members', - group_group_members_path(@group), - class: 'btn' - %ul.content-list - = render partial: 'shared/members/member', - collection: members.limit(20), - as: :member, - locals: { show_controls: false } - - if members.size > 20 - %li - and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)} diff --git a/app/views/projects/project_members/_index.html.haml b/app/views/projects/project_members/_index.html.haml index fa99610c0be..08e2d6177ab 100644 --- a/app/views/projects/project_members/_index.html.haml +++ b/app/views/projects/project_members/_index.html.haml @@ -1,6 +1,6 @@ .row.prepend-top-default - .col-lg-4.settings-sidebar - %h4.prepend-top-0 + .col-lg-12 + %h4 Project members - if can?(current_user, :admin_project_member, @project) %p @@ -13,7 +13,6 @@ %i Masters or %i Owners - .col-lg-8 .light - if can?(current_user, :admin_project_member, @project) %ul.nav-links.project-member-tabs{ role: 'tablist' } diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml deleted file mode 100644 index 7902ddb1ae9..00000000000 --- a/app/views/projects/project_members/_shared_group_members.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- @project_group_links.each do |group_links| - - shared_group = group_links.group - - shared_group_members = shared_group.members - - shared_group_users_count = shared_group_members.size - .panel.panel-default - .panel-heading - Shared with - %strong= shared_group.name - group, members with - %strong= group_links.human_access - role (#{shared_group_users_count}) - - if can?(current_user, :admin_group, shared_group) - .panel-head-actions - = link_to group_group_members_path(shared_group), class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o - Edit group members - %ul.content-list - = render partial: 'shared/members/member', - collection: shared_group_members.order(access_level: :desc).limit(20), - as: :member, - locals: { show_controls: false, show_roles: false } - - if shared_group_users_count > 20 - %li - and #{shared_group_users_count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(shared_group)} diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 7eab428bb2e..b842fd57cf3 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -11,16 +11,17 @@ .col-lg-9 = form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors form-horizontal js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form| = render 'shared/service_settings', form: form, subject: @service - .footer-block.row-content-block - %button.btn.btn-save{ type: 'submit' } - = icon('spinner spin', class: 'hidden js-btn-spinner') - %span.js-btn-label - Save changes - - - if @service.valid? && @service.activated? - - unless @service.can_test? - - disabled_class = 'disabled' - - disabled_title = @service.disabled_title + - if @service.editable? + .footer-block.row-content-block + %button.btn.btn-save{ type: 'submit' } + = icon('spinner spin', class: 'hidden js-btn-spinner') + %span.js-btn-label + Save changes + + - if @service.valid? && @service.activated? + - unless @service.can_test? + - disabled_class = 'disabled' + - disabled_title = @service.disabled_title = link_to 'Cancel', project_settings_integrations_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 00ccc3ec41e..6afb38c5709 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -3,6 +3,6 @@ = render "projects/settings/head" = render 'projects/runners/index' -= render 'projects/variables/index' += render 'ci/variables/index' = render 'projects/triggers/index' = render 'projects/pipelines_settings/show' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 3fb247c5ceb..130980556c1 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -5,7 +5,6 @@ = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = render "projects/commits/head" -= render 'projects/last_push' - -%div{ class: container_class } +%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } + = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index 297a53ca98c..df533952b76 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -1,9 +1 @@ -- page_title "Variables" - -.row.prepend-top-default.append-bottom-default - .col-lg-3 - = render "content" - .col-lg-9 - %h5.prepend-top-0 - Update variable - = render "form", btn_text: "Save variable" += render 'ci/variables/show' diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index b200e5fc528..7ca14ac93cc 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -7,10 +7,11 @@ = markdown @service.help .service-settings - .form-group - = form.label :active, "Active", class: "control-label" - .col-sm-10 - = form.check_box :active + - if @service.show_active_box? + .form-group + = form.label :active, "Active", class: "control-label" + .col-sm-10 + = form.check_box :active - if @service.supported_events.present? .form-group |