Commit b8c4d5e0 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'master' into 33929-allow-to-enable-perf-bar-for-a-group

parents 97611c88 c88739ff
......@@ -12,7 +12,7 @@ entry.
## 9.3.4 (2017-07-03)
- No changes.
- Update gitlab-shell to 5.1.1 !12615
## 9.3.3 (2017-06-30)
......
9.3.0-pre
9.4.0-pre
......@@ -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);
......
/* 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() {
......
/* 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() {
......
......@@ -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',
......
<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',
......
......@@ -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/');
......
......@@ -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',
......
......@@ -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()
......
......@@ -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}}
......
......@@ -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>
......
......@@ -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,
};
......@@ -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'
......
/**
* 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', ''));
}
});
}
}
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();
});
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();
}
}
/* 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');
......
......@@ -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;
......@@ -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));
}
});
};
}
/* 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;
/* 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;
})();
}
/* 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;
......@@ -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;
......@@ -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;
/* 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;
})();
}
}
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;
/* 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
});
......
......@@ -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;
......@@ -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;
......@@ -3,6 +3,3 @@ export default class VersionCheckImage {
imageElement.off('error').on('error', () => imageElement.hide());
}
}
window.gl = window.gl || {};
gl.VersionCheckImage = VersionCheckImage;
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;
......@@ -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
......
/* 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;
/* 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;
})();
}
}
......@@ -21,3 +21,9 @@ body.modal-open {
width: 860px;
}
}
@media (min-width: $screen-lg-min) {
.modal-full {
width: 98%;
}
}
......@@ -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);
......@@ -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,
......
......@@ -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;
}
}
}
......
//= require peek/views/performance_bar
//= require peek/views/rblineprof
header.navbar-gitlab.with-peek {
top: 35px;
}
@import "framework/variables";
@import "peek/views/performance_bar";
@import "peek/views/rblineprof";
#peek {
height: 35px;
background: #000;
background: $black;
line-height: 35px;
color: #999;
color: $perf-bar-text;
&.disabled {
display: none;
}
&.production {
background-color: #222;
background-color: $perf-bar-production;
}
&.staging {
background-color: #291430;
background-color: $perf-bar-staging;
}
&.development {
background-color: #4c1210;
background-color: $perf-bar-development;
}
.wrapper {
width: 800px;
width: 1000px;
margin: 0 auto;
}
// UI Elements
.bucket {
background: #111;
background: $perf-bar-bucket-bg;
display: inline-block;
padding: 4px 6px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
line-height: 1;
color: #ccc;
color: $perf-bar-bucket-color;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(255,255,255,.2), inset 0 1px 2px rgba(0,0,0,.25);
box-shadow: 0 1px 0 $perf-bar-bucket-box-shadow-from, inset 0 1px 2px $perf-bar-bucket-box-shadow-to;
.hidden {
display: none;
......@@ -53,12 +50,14 @@ header.navbar-gitlab.with-peek {
}
strong {
color: #fff;
color: $white-light;
}
table {
color: $black;
strong {
color: #000;
color: $black;
}
}
......@@ -90,5 +89,15 @@ header.navbar-gitlab.with-peek {
}
#modal-peek-pg-queries-content {
color: #000;
color: $black;
}
.peek-rblineprof-file {
pre.duration {
width: 280px;
}
.data {
overflow: visible;
}
}
......@@ -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
......
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
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
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
......@@ -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)
......
......@@ -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
......
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
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
......@@ -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)
......
......@@ -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
......
......@@ -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
......@@ -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
......
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
......
......@@ -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
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
......
......@@ -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
......
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
......@@ -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.
......
......@@ -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'
......
......@@ -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
......
......@@ -2,6 +2,7 @@ module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
include HasVariable
include Presentable
belongs_to :project
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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}." }
......
......@@ -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
......
......@@ -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,
......
......@@ -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
......
......@@ -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)
......
......@@ -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)
......
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) }
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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) }
......
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
......
......@@ -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
......
......@@ -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'
......
......@@ -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
......
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
......
......@@ -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
......
......@@ -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
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)
......
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
......@@ -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) }
......
......@@ -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,
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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) &&
......
......@@ -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
......
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
......
......@@ -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
......
......@@ -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
......
......@@ -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) }
......
......@@ -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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment