Skip to content
Snippets Groups Projects
Commit 6b13a226 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 2ac93cb8
No related branches found
No related tags found
No related merge requests found
Showing
with 169 additions and 42 deletions
Loading
Loading
@@ -8,7 +8,6 @@ import {
GlTable,
GlSearchBoxByClick,
} from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
Loading
Loading
@@ -76,8 +75,8 @@ export default {
this.startPolling(`${this.indexPath}?search_term=${this.errorSearchQuery}`);
},
trackViewInSentryOptions,
viewDetails(errorId) {
visitUrl(`error_tracking/${errorId}/details`);
getDetailsLink(errorId) {
return `error_tracking/${errorId}/details`;
},
},
};
Loading
Loading
@@ -129,11 +128,7 @@ export default {
</template>
<template slot="error" slot-scope="errors">
<div class="d-flex flex-column">
<gl-link
class="d-flex text-dark"
target="_blank"
@click="viewDetails(errors.item.id)"
>
<gl-link class="d-flex text-dark" :href="getDetailsLink(errors.item.id)">
<strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
</gl-link>
<span class="text-secondary text-truncate">
Loading
Loading
Loading
Loading
@@ -27,6 +27,8 @@ export default {
:lines="entry.context"
:file-path="entry.filename"
:error-line="entry.lineNo"
:error-fn="entry.function"
:error-column="entry.colNo"
:expanded="isFirstEntry(index)"
/>
</div>
Loading
Loading
<script>
import { __, sprintf } from '~/locale';
import { GlTooltip } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
Loading
Loading
@@ -22,9 +23,20 @@ export default {
type: String,
required: true,
},
errorFn: {
type: String,
required: false,
default: '',
},
errorLine: {
type: Number,
required: true,
required: false,
default: 0,
},
errorColumn: {
type: Number,
required: false,
default: 0,
},
expanded: {
type: Boolean,
Loading
Loading
@@ -38,12 +50,23 @@ export default {
};
},
computed: {
linesLength() {
return this.lines.length;
hasCode() {
return Boolean(this.lines.length);
},
collapseIcon() {
return this.isExpanded ? 'chevron-down' : 'chevron-right';
},
noCodeFn() {
return this.errorFn ? sprintf(__('in %{errorFn} '), { errorFn: this.errorFn }) : '';
},
noCodeLine() {
return this.errorLine
? sprintf(__('at line %{errorLine}%{errorColumn}'), {
errorLine: this.errorLine,
errorColumn: this.errorColumn ? `:${this.errorColumn}` : '',
})
: '';
},
},
methods: {
isHighlighted(lineNum) {
Loading
Loading
@@ -66,27 +89,31 @@ export default {
<template>
<div class="file-holder">
<div ref="header" class="file-title file-title-flex-parent">
<div class="file-header-content ">
<div class="d-inline-block cursor-pointer" @click="toggle()">
<div class="file-header-content d-flex align-content-center">
<div v-if="hasCode" class="d-inline-block cursor-pointer" @click="toggle()">
<icon :name="collapseIcon" :size="16" aria-hidden="true" class="append-right-5" />
</div>
<div class="d-inline-block append-right-4">
<file-icon
:file-name="filePath"
:size="18"
aria-hidden="true"
css-classes="append-right-5"
/>
<strong v-gl-tooltip :title="filePath" class="file-title-name" data-container="body">
{{ filePath }}
</strong>
</div>
<file-icon
:file-name="filePath"
:size="18"
aria-hidden="true"
css-classes="append-right-5"
/>
<strong
v-gl-tooltip
:title="filePath"
class="file-title-name d-inline-block overflow-hidden text-truncate"
:class="{ 'limited-width': !hasCode }"
data-container="body"
>
{{ filePath }}
</strong>
<clipboard-button
:title="__('Copy file path')"
:text="filePath"
css-class="btn-default btn-transparent btn-clipboard"
css-class="btn-default btn-transparent btn-clipboard position-static"
/>
<span v-if="!hasCode" class="text-tertiary">{{ noCodeFn }}{{ noCodeLine }}</span>
</div>
</div>
 
Loading
Loading
export const stacktrace = state => state.stacktraceData.stack_trace_entries.reverse();
export const stacktrace = state =>
state.stacktraceData.stack_trace_entries
? state.stacktraceData.stack_trace_entries.reverse()
: [];
 
export default () => {};
Loading
Loading
@@ -52,6 +52,11 @@ export default {
header: s__('PerformanceBar|Redis calls'),
keys: ['cmd'],
},
{
metric: 'total',
header: s__('PerformanceBar|Frontend resources'),
keys: ['name', 'size'],
},
],
data() {
return { currentRequestId: '' };
Loading
Loading
/* eslint-disable @gitlab/i18n/no-non-i18n-strings */
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
 
Loading
Loading
@@ -53,12 +54,61 @@ export default ({ container }) =>
PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
.then(res => {
this.store.addRequestDetails(requestId, res.data);
if (this.requestId === requestId) this.collectFrontendPerformanceMetrics();
})
.catch(() =>
// eslint-disable-next-line no-console
console.warn(`Error getting performance bar results for ${requestId}`),
);
},
collectFrontendPerformanceMetrics() {
if (performance) {
const navigationEntries = performance.getEntriesByType('navigation');
const paintEntries = performance.getEntriesByType('paint');
const resourceEntries = performance.getEntriesByType('resource');
let durationString = '';
if (navigationEntries.length > 0) {
durationString = `BE ${this.formatMs(navigationEntries[0].responseEnd)} / `;
durationString += `FCP ${this.formatMs(paintEntries[1].startTime)} / `;
durationString += `DOM ${this.formatMs(navigationEntries[0].domContentLoadedEventEnd)}`;
}
let newEntries = resourceEntries.map(this.transformResourceEntry);
this.updateFrontendPerformanceMetrics(durationString, newEntries);
if ('PerformanceObserver' in window) {
// We start observing for more incoming timings
const observer = new PerformanceObserver(list => {
newEntries = newEntries.concat(list.getEntries().map(this.transformResourceEntry));
this.updateFrontendPerformanceMetrics(durationString, newEntries);
});
observer.observe({ entryTypes: ['resource'] });
}
}
},
updateFrontendPerformanceMetrics(durationString, requestEntries) {
this.store.setRequestDetailsData(this.requestId, 'total', {
duration: durationString,
calls: requestEntries.length,
details: requestEntries,
});
},
transformResourceEntry(entry) {
const nf = new Intl.NumberFormat();
return {
name: entry.name.replace(document.location.origin, ''),
duration: Math.round(entry.duration),
size: entry.transferSize ? `${nf.format(entry.transferSize)} bytes` : 'cached',
};
},
formatMs(msValue) {
const nf = new Intl.NumberFormat();
return `${nf.format(Math.round(msValue))}ms`;
},
},
render(createElement) {
return createElement('performance-bar-app', {
Loading
Loading
Loading
Loading
@@ -32,6 +32,16 @@ export default class PerformanceBarStore {
return request;
}
 
setRequestDetailsData(requestId, metricKey, requestDetailsData) {
const selectedRequest = this.findRequest(requestId);
if (selectedRequest) {
selectedRequest.details = {
...selectedRequest.details,
[metricKey]: requestDetailsData,
};
}
}
requestsWithDetails() {
return this.requests.filter(request => request.details);
}
Loading
Loading
Loading
Loading
@@ -17,6 +17,7 @@ const handleUserPopoverMouseOut = event => {
renderedPopover.$destroy();
renderedPopover = null;
}
target.removeAttribute('aria-describedby');
};
 
/**
Loading
Loading
Loading
Loading
@@ -12,6 +12,12 @@
}
}
 
.file-title-name {
&.limited-width {
max-width: 80%;
}
}
.line_content.old::before {
content: none !important;
}
Loading
Loading
Loading
Loading
@@ -17,11 +17,11 @@ module Clusters
include ::Clusters::Concerns::ApplicationData
include AfterCommitQueue
 
alias_method :original_set_initial_status, :set_initial_status
def set_initial_status
return unless not_installable?
return unless verify_cluster?
return unless cluster&.platform_kubernetes_rbac?
 
self.status = status_states[:installable]
original_set_initial_status
end
 
state_machine :status do
Loading
Loading
@@ -131,10 +131,6 @@ module Clusters
 
[Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "-f", METRICS_CONFIG)]
end
def verify_cluster?
cluster&.application_helm_available? && cluster&.platform_kubernetes_rbac?
end
end
end
end
Loading
Loading
@@ -5,6 +5,7 @@ class Deployment < ApplicationRecord
include IidRoutes
include AfterCommitQueue
include UpdatedAtFilterable
include Gitlab::Utils::StrongMemoize
 
belongs_to :project, required: true
belongs_to :environment, required: true
Loading
Loading
@@ -126,6 +127,12 @@ class Deployment < ApplicationRecord
@scheduled_actions ||= deployable.try(:other_scheduled_actions)
end
 
def playable_build
strong_memoize(:playable_build) do
deployable.try(:playable?) ? deployable : nil
end
end
def includes_commit?(commit)
return false unless commit
 
Loading
Loading
Loading
Loading
@@ -78,7 +78,7 @@ class EnvironmentStatus
def self.build_environments_status(mr, user, pipeline)
return [] unless pipeline
 
pipeline.environments.available.map do |environment|
pipeline.environments.includes(:project).available.map do |environment|
next unless Ability.allowed?(user, :read_environment, environment)
 
EnvironmentStatus.new(pipeline.project, environment, mr, pipeline.sha)
Loading
Loading
Loading
Loading
@@ -2250,12 +2250,13 @@ class Project < ApplicationRecord
# Git objects are only poolable when the project is or has:
# - Hashed storage -> The object pool will have a remote to its members, using relative paths.
# If the repository path changes we would have to update the remote.
# - Public -> User will be able to fetch Git objects that might not exist
# in their own repository.
# - not private -> The visibility level or repository access level has to be greater than private
# to prevent fetching objects that might not exist
# - Repository -> Else the disk path will be empty, and there's nothing to pool
def git_objects_poolable?
hashed_storage?(:repository) &&
public? &&
visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
repository_access_level > ProjectFeature::PRIVATE &&
repository_exists? &&
Gitlab::CurrentSettings.hashed_storage_enabled
end
Loading
Loading
Loading
Loading
@@ -37,6 +37,9 @@ class DeploymentEntity < Grape::Entity
expose :commit, using: CommitEntity, if: -> (*) { include_details? }
expose :manual_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? }
expose :scheduled_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? }
expose :playable_build, expose_nil: false, if: -> (*) { include_details? && can_create_deployment? } do |deployment, options|
JobEntity.represent(deployment.playable_build, options.merge(only: [:play_path, :retry_path]))
end
 
expose :cluster, using: ClusterBasicEntity
 
Loading
Loading
@@ -47,7 +50,7 @@ class DeploymentEntity < Grape::Entity
end
 
def can_create_deployment?
can?(request.current_user, :create_deployment, request.project)
can?(request.current_user, :create_deployment, project)
end
 
def can_read_deployables?
Loading
Loading
@@ -56,6 +59,10 @@ class DeploymentEntity < Grape::Entity
# because it triggers a policy evaluation that involves multiple
# Gitaly calls that might not be cached.
#
can?(request.current_user, :read_build, request.project)
can?(request.current_user, :read_build, project)
end
def project
request.try(:project) || options[:project]
end
end
Loading
Loading
@@ -37,6 +37,10 @@ class EnvironmentStatusEntity < Grape::Entity
es.deployment.try(:formatted_deployment_time)
end
 
expose :deployment, as: :details do |es, options|
DeploymentEntity.represent(es.deployment, options.merge(project: es.project, only: [:playable_build]))
end
expose :changes
 
private
Loading
Loading
# frozen_string_literal: true
 
class MergeRequestPollWidgetEntity < IssuableEntity
class MergeRequestPollWidgetEntity < Grape::Entity
include RequestAwareEntity
expose :auto_merge_strategy
expose :available_auto_merge_strategies do |merge_request|
AutoMergeService.new(merge_request.project, current_user).available_strategies(merge_request) # rubocop: disable CodeReuse/ServiceClass
Loading
Loading
Loading
Loading
@@ -77,6 +77,10 @@ class PipelineEntity < Grape::Entity
cancel_project_pipeline_path(pipeline.project, pipeline)
end
 
expose :failed_builds, if: -> (*) { can_retry? }, using: JobEntity do |pipeline|
pipeline.builds.failed
end
private
 
alias_method :pipeline, :object
Loading
Loading
Loading
Loading
@@ -36,3 +36,5 @@ module Issues
end
end
end
Issues::BaseService.prepend_if_ee('EE::Issues::BaseService')
Loading
Loading
@@ -18,7 +18,7 @@
.col-lg-4
%h4.prepend-top-0= _('Notification events')
%p
- notification_link = link_to _('notification emails'), help_page_path('workflow/notifications'), target: '_blank'
- notification_link = link_to _('notification emails'), help_page_path('user/profile/notifications'), target: '_blank'
- paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
#{ paragraph.html_safe }
.col-lg-8
Loading
Loading
---
title: Make internal projects poolable
merge_request: 19295
author: briankabiro
type: changed
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment