Commit b7b3aef4 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge remote-tracking branch 'origin/active-record-each-batch' into...

Merge remote-tracking branch 'origin/active-record-each-batch' into fix/gb/stage-id-reference-background-migration

* origin/active-record-each-batch: (59 commits)
  Added EachBatch for iterating tables in batches
  Extend MR tabs a bit to cover up the avatar holder and collapse icon on scroll
  Update VERSION to 9.4.0-pre.
  Add CHANGELOG
  Fix some N+1 queries in the GET /projects API
  Don't show auxiliary blob viewer for README when there is no wiki
  Improve & fix the performance bar UI and behavior
  Remove orphaned haml files
  Fixed CHANGELOG.md for 9.3.4 release
  Add table for merge request commits
  34727 Remove two columned layout from project member settings
  Just draw :legacy_builds
  Re-enable polling for environments
  Cleanup minor UX issues in the performance dashboard
  Upgrade GitLab Workhorse to v2.3.0
  Added test for the chart legend
  Use correct field for label name, fix default for unit to be blank
  Fix shorter route helpers in production environment
  Encode certificate-authority-data in base64
  Revert "Merge branch 'winh-mr-widget-no-pipeline' into 'master'"
  ...
parents af0eeefc 5f9c8458
......@@ -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
......@@ -56,6 +56,8 @@ import GfmAutoComplete from './gfm_auto_complete';
import ShortcutsBlob from './shortcuts_blob';
import initSettingsPanels from './settings_panels';
import initExperimentalFlags from './experimental_flags';
import OAuthRememberMe from './oauth_remember_me';
import PerformanceBar from './performance_bar';
(function() {
var Dispatcher;
......@@ -127,6 +129,7 @@ import initExperimentalFlags from './experimental_flags';
case 'sessions:new':
new UsernameValidator();
new ActiveTabMemoizer();
new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents();
break;
case 'projects:boards:show':
case 'projects:boards:index':
......@@ -513,6 +516,10 @@ import initExperimentalFlags from './experimental_flags';
if (!shortcut_handler) {
new Shortcuts();
}
if (document.querySelector('#peek')) {
new PerformanceBar({ container: '#peek' });
}
};
Dispatcher.prototype.initSearch = function() {
......
......@@ -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,
};
/**
* 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();
}
}
......@@ -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
......
......@@ -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
......
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)
......
......@@ -14,7 +14,7 @@ class Projects::VariablesController < Projects::ApplicationController
def update
@variable = @project.variables.find(params[:id])
if @variable.update_attributes(project_params)
if @variable.update_attributes(variable_params)
redirect_to project_variables_path(project), notice: 'Variable was successfully updated.'
else
render action: "show"
......@@ -22,9 +22,9 @@ class Projects::VariablesController < Projects::ApplicationController
end
def create
@variable = Ci::Variable.new(project_params)
@variable = @project.variables.new(variable_params)
if @variable.valid? && @project.variables << @variable
if @variable.save
flash[:notice] = 'Variables were successfully updated.'
redirect_to project_settings_ci_cd_path(project)
else
......@@ -43,8 +43,11 @@ class Projects::VariablesController < Projects::ApplicationController
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
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 peek_enabled?
class_name
end
......
module PerformanceBarHelper
# This is a hack since using `alias_method :performance_bar_enabled?, :peek_enabled?`
# in WithPerformanceBar breaks tests (but works in the browser).
def performance_bar_enabled?
peek_enabled?
end
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,11 @@ 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 += 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 +218,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 +372,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
......
......@@ -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,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
......
module EachBatch
extend ActiveSupport::Concern
module ClassMethods
# Iterates over the rows in a relation in batches, similar to Rails'
# `in_batches` but in a more efficient way.
#
# Unlike `in_batches` provided by Rails this method does not support a
# custom start/end range, nor does it provide support for the `load:`
# keyword argument.
#
# This method will yield an ActiveRecord::Relation to the supplied block, or
# return an Enumerator if no block is given.
#
# Example:
#
# User.each_batch do |relation|
# relation.update_all(updated_at: Time.now)
# end
#
# This will produce SQL queries along the lines of:
#
# User Load (0.7ms) SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 41654) ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1000
# (0.7ms) SELECT COUNT(*) FROM "users" WHERE ("users"."id" >= 41654) AND ("users"."id" < 42687)
#
# of - The number of rows to retrieve per batch.
def each_batch(of: 1000)
start = except(:select)
.select(primary_key)
.reorder(primary_key => :asc)
.take
return unless start
start_id = start[primary_key]
arel_table = self.arel_table
loop do
stop = except(:select)
.select(primary_key)
.where(arel_table[primary_key].gteq(start_id))
.reorder(primary_key => :asc)
.offset(of)
.limit(1)
.take
relation = where(arel_table[primary_key].gteq(start_id))
if stop
stop_id = stop[primary_key]
start_id = stop_id
relation = relation.where(arel_table[primary_key].lt(stop_id))
end
# Any ORDER BYs are useless for this relation and can lead to less
# efficient UPDATE queries, hence we get rid of it.
yield relation.except(:order)
break unless stop
end
end
end
end
......@@ -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
......
......@@ -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,11 +16,11 @@ 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'
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
......@@ -31,7 +31,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
......
......@@ -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) }
......
......@@ -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)
......
......@@ -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'
......@@ -27,6 +27,7 @@
%td.shortcut
.key f
%td Focus Filter
- if performance_bar_enabled?
%tr
%td.shortcut
.key p b
......
......@@ -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 peek_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 peek_enabled?
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
......
......@@ -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'
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'
%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" } }...
= 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)
.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)}
.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' }
......
- @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)}
......@@ -11,6 +11,7 @@
.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
- if @service.editable?
.footer-block.row-content-block
%button.btn.btn-save{ type: 'submit' }
= icon('spinner spin', class: 'hidden js-btn-spinner')
......
......@@ -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
......@@ -7,6 +7,7 @@
= markdown @service.help
.service-settings
- if @service.show_active_box?
.form-group
= form.label :active, "Active", class: "control-label"
.col-sm-10
......
---
title: Honor the "Remember me" parameter for OAuth-based login
merge_request: 11963
author:
---
title: Provide KUBECONFIG from KubernetesService for runners
merge_request: 12223
author:
---
title: Add user projects API
merge_request: 12596
author: Ivan Chernov
---
title: Improve the performance of the project list API
merge_request: 12679
author:
---
title: Add Traditional Chinese in Taiwan translations of Commits Page
merge_request: 12407
author: Huang Tao
---
title: Cleanup minor UX issues in the performance dashboard
merge_request:
author:
---
title: Fixed the chart legend not being set correctly
merge_request: 12628
author:
---
title: Remove two columned layout from project member settings
merge_request:
author:
---
title: Don't show auxiliary blob viewer for README when there is no wiki
merge_request:
author:
---
title: Re-enable realtime for environments table
merge_request:
author:
---
title: Speed up project removals by adding foreign keys with cascading deletes to various tables
merge_request:
author:
---
title: Upgrade GitLab Workhorse to v2.3.0
merge_request: 12676
author:
......@@ -105,7 +105,7 @@ module Gitlab
config.assets.precompile << "katex.css"
config.assets.precompile << "katex.js"
config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "peek.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "vendor/assets/fonts/*"
config.assets.precompile << "test.css"
......@@ -166,8 +166,9 @@ module Gitlab
config.after_initialize do
Rails.application.reload_routes!
named_routes_set = Gitlab::Application.routes.named_routes
project_url_helpers = Module.new do
Gitlab::Application.routes.named_routes.helper_names.each do |name|
named_routes_set.helper_names.each do |name|
next unless name.include?('namespace_project')
define_method(name.sub('namespace_project', 'project')) do |project, *args|
......@@ -176,6 +177,9 @@ module Gitlab
end
end
named_routes_set.url_helpers_module.include project_url_helpers
named_routes_set.url_helpers_module.extend project_url_helpers
Gitlab::Routing.url_helpers.include project_url_helpers
Gitlab::Routing.url_helpers.extend project_url_helpers
......
......@@ -619,6 +619,49 @@ test:
title: "JIRA"
url: https://sample_company.atlassian.net
project_key: PROJECT
omniauth:
enabled: true
allow_single_sign_on: true
external_providers: []
providers:
- { name: 'cas3',
label: 'cas3',
args: { url: 'https://sso.example.com',
disable_ssl_verification: false,
login_url: '/cas/login',
service_validate_url: '/cas/p3/serviceValidate',
logout_url: '/cas/logout'} }
- { name: 'github',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET',
url: "https://github.com/",
verify_ssl: false,
args: { scope: 'user:email' } }
- { name: 'bitbucket',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET' }
- { name: 'gitlab',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET',
args: { scope: 'api' } }
- { name: 'google_oauth2',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET',
args: { access_type: 'offline', approval_prompt: '' } }
- { name: 'facebook',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET' }
- { name: 'twitter',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET' }
- { name: 'auth0',
args: {
client_id: 'YOUR_AUTH0_CLIENT_ID',
client_secret: 'YOUR_AUTH0_CLIENT_SECRET',
namespace: 'YOUR_AUTH0_DOMAIN' } }
ldap:
enabled: false
servers:
......
- group: Kubernetes
priority: 1
- group: AWS Elastic Load Balancer
priority: 10
metrics:
- title: "Memory usage"
y_label: "Values"
- title: "Throughput"
y_label: "Requests / Sec"
required_metrics:
- container_memory_usage_bytes
- aws_elb_request_count_sum
weight: 1
queries:
- query_range: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20'
label: Container memory
unit: MiB
- title: "Current memory usage"
- query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) * 60'
label: Total
unit: req / sec
- title: "Latency"
y_label: "Latency (ms)"
required_metrics:
- container_memory_usage_bytes
- aws_elb_latency_average
weight: 1
queries:
- query: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20'
display_empty: false
unit: MiB
- title: "CPU usage"
- query_range: 'avg(aws_elb_latency_average{%{environment_filter}}) * 1000'
label: Average
unit: ms
- title: "HTTP Error Rate"
y_label: "Error Rate (%)"
required_metrics:
- container_cpu_usage_seconds_total
- aws_elb_request_count_sum
- aws_elb_httpcode_backend_5_xx_sum
weight: 1
queries:
- query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})'
label: HTTP Errors
unit: "%"
- group: NGINX
priority: 10
metrics:
- title: "Throughput"
y_label: "Requests / Sec"
required_metrics:
- nginx_requests_total
weight: 1
queries:
- query_range: 'sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m]))'
label: Total
unit: req / sec
- title: "Latency"
y_label: "Latency (ms)"
required_metrics:
- nginx_upstream_response_msecs_avg
weight: 1
queries:
- query_range: 'avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) * 1000'
label: Upstream
unit: ms
- title: "HTTP Error Rate"
y_label: "Error Rate (%)"
required_metrics:
- nginx_responses_total
weight: 1
queries:
- query_range: 'sum(nginx_responses_total{status_code="5xx", %{environment_filter}}) / sum(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}})'
label: HTTP Errors
unit: "%"
- group: Kubernetes
priority: 5
metrics:
- title: "Memory Usage"
y_label: "Memory Usage (MB)"
required_metrics:
- container_memory_usage_bytes
weight: 1
queries:
- query_range: 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100'
- title: "Current CPU usage"
- query_range: '(sum(container_memory_usage_bytes{container_name!="POD",%{environment_filter}}) / count(container_memory_usage_bytes{container_name!="POD",%{environment_filter}})) /1024/1024'
label: Average
unit: MB
- title: "CPU Utilization"
y_label: "CPU Utilization (%)"
required_metrics:
- container_cpu_usage_seconds_total
weight: 1
queries:
- query: 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100'
- query_range: 'sum(rate(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}[2m])) / count(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}) * 100'
label: Average
unit: "%"
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