Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
da602b32e4
commit
7c221ba5ce
119 changed files with 1206 additions and 401 deletions
|
@ -23,14 +23,10 @@ import {
|
|||
} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { convertToSnakeCase } from '~/lib/utils/text_utility';
|
||||
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
|
||||
import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
|
||||
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
|
||||
import {
|
||||
ALERTS_STATUS_TABS,
|
||||
ALERTS_SEVERITY_LABELS,
|
||||
trackAlertListViewsOptions,
|
||||
} from '../constants';
|
||||
import AlertStatus from './alert_status.vue';
|
||||
import { ALERTS_STATUS_TABS, SEVERITY_LEVELS, trackAlertListViewsOptions } from '../constants';
|
||||
|
||||
const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' };
|
||||
|
||||
|
@ -96,7 +92,7 @@ export default {
|
|||
sortable: true,
|
||||
},
|
||||
],
|
||||
severityLabels: ALERTS_SEVERITY_LABELS,
|
||||
severityLabels: SEVERITY_LEVELS,
|
||||
statusTabs: ALERTS_STATUS_TABS,
|
||||
components: {
|
||||
GlAlert,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const ALERTS_SEVERITY_LABELS = {
|
||||
CRITICAL: s__('AlertManagement|Critical'),
|
||||
HIGH: s__('AlertManagement|High'),
|
||||
MEDIUM: s__('AlertManagement|Medium'),
|
||||
LOW: s__('AlertManagement|Low'),
|
||||
INFO: s__('AlertManagement|Info'),
|
||||
UNKNOWN: s__('AlertManagement|Unknown'),
|
||||
export const SEVERITY_LEVELS = {
|
||||
CRITICAL: s__('severity|Critical'),
|
||||
HIGH: s__('severity|High'),
|
||||
MEDIUM: s__('severity|Medium'),
|
||||
LOW: s__('severity|Low'),
|
||||
INFO: s__('severity|Info'),
|
||||
UNKNOWN: s__('severity|Unknown'),
|
||||
};
|
||||
|
||||
export const ALERTS_STATUS_TABS = [
|
||||
|
@ -46,20 +46,3 @@ export const trackAlertListViewsOptions = {
|
|||
category: 'Alert Management',
|
||||
action: 'view_alerts_list',
|
||||
};
|
||||
|
||||
/**
|
||||
* Tracks snowplow event when user views alert details
|
||||
*/
|
||||
export const trackAlertsDetailsViewsOptions = {
|
||||
category: 'Alert Management',
|
||||
action: 'view_alert_details',
|
||||
};
|
||||
|
||||
/**
|
||||
* Tracks snowplow event when alert status is updated
|
||||
*/
|
||||
export const trackAlertStatusUpdateOptions = {
|
||||
category: 'Alert Management',
|
||||
action: 'update_alert_status',
|
||||
label: 'Status',
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
|
|||
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
|
||||
import AlertManagementList from './components/alert_management_list_wrapper.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
@ -59,6 +60,7 @@ export default () => {
|
|||
populatingAlertsHelpUrl,
|
||||
emptyAlertSvgPath,
|
||||
alertManagementEnabled: parseBoolean(alertManagementEnabled),
|
||||
trackAlertStatusUpdateOptions: PAGE_CONFIG.OPERATIONS.TRACK_ALERT_STATUS_UPDATE_OPTIONS,
|
||||
userCanEnableAlertManagement: parseBoolean(userCanEnableAlertManagement),
|
||||
},
|
||||
apolloProvider,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/author.fragment.graphql"
|
||||
#import "./author.fragment.graphql"
|
||||
|
||||
fragment AlertNote on Note {
|
||||
id
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/alert_note.fragment.graphql"
|
||||
#import "../fragments/alert_note.fragment.graphql"
|
||||
|
||||
mutation updateAlertStatus($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {
|
||||
updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {
|
|
@ -1,3 +1,3 @@
|
|||
import AlertDetails from '~/alert_management/details';
|
||||
import AlertDetails from '~/vue_shared/alert_details';
|
||||
|
||||
AlertDetails('#js-alert_details');
|
||||
|
|
|
@ -78,11 +78,7 @@ export default {
|
|||
return `ci-badge-${escape(group.name)}`;
|
||||
},
|
||||
isFadedOut(jobName) {
|
||||
return (
|
||||
this.jobHovered &&
|
||||
this.highlightedJobs.length > 1 &&
|
||||
!this.highlightedJobs.includes(jobName)
|
||||
);
|
||||
return this.highlightedJobs.length > 1 && !this.highlightedJobs.includes(jobName);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -126,12 +122,9 @@ export default {
|
|||
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
|
||||
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
|
||||
/>
|
||||
<job-group-dropdown
|
||||
v-else
|
||||
:group="group"
|
||||
:pipeline-id="pipelineId"
|
||||
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
|
||||
/>
|
||||
<div v-else :class="{ 'gl-opacity-3': isFadedOut(group.name) }">
|
||||
<job-group-dropdown :group="group" :pipeline-id="pipelineId" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</main-graph-wrapper>
|
||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
|||
helpPath: this.codequalityHelpPath,
|
||||
});
|
||||
|
||||
this.fetchReports(this.glFeatures.codequalityMrDiff);
|
||||
this.fetchReports(this.glFeatures.codequalityBackendComparison);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchReports', 'setPaths']),
|
||||
|
|
|
@ -21,11 +21,11 @@ import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
|
|||
import Tracking from '~/tracking';
|
||||
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
|
||||
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
|
||||
import alertQuery from '../graphql/queries/details.query.graphql';
|
||||
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
|
||||
import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants';
|
||||
import createIssueMutation from '../graphql/mutations/create_issue_from_alert.mutation.graphql';
|
||||
import toggleSidebarStatusMutation from '../graphql/mutations/toggle_sidebar_status.mutation.graphql';
|
||||
import alertQuery from '../graphql/queries/alert_details.query.graphql';
|
||||
import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
|
||||
import { SEVERITY_LEVELS } from '../constants';
|
||||
import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
|
||||
import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
|
||||
import SystemNote from './system_notes/system_note.vue';
|
||||
import AlertSidebar from './alert_sidebar.vue';
|
||||
import AlertMetrics from './alert_metrics.vue';
|
||||
|
@ -44,7 +44,7 @@ export default {
|
|||
directives: {
|
||||
SafeHtml: GlSafeHtmlDirective,
|
||||
},
|
||||
severityLabels: ALERTS_SEVERITY_LABELS,
|
||||
severityLabels: SEVERITY_LEVELS,
|
||||
tabsConfig: [
|
||||
{
|
||||
id: 'overview',
|
||||
|
@ -89,6 +89,9 @@ export default {
|
|||
projectIssuesPath: {
|
||||
default: '',
|
||||
},
|
||||
trackAlertsDetailsViewsOptions: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
alert: {
|
||||
|
@ -155,7 +158,9 @@ export default {
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
this.trackPageViews();
|
||||
if (this.trackAlertsDetailsViewsOptions) {
|
||||
this.trackPageViews();
|
||||
}
|
||||
toggleContainerClasses(containerEl, {
|
||||
'issuable-bulk-update-sidebar': true,
|
||||
'right-sidebar-expanded': true,
|
||||
|
@ -217,7 +222,7 @@ export default {
|
|||
return joinPaths(this.projectIssuesPath, issueId);
|
||||
},
|
||||
trackPageViews() {
|
||||
const { category, action } = trackAlertsDetailsViewsOptions;
|
||||
const { category, action } = this.trackAlertsDetailsViewsOptions;
|
||||
Tracking.event(category, action);
|
||||
},
|
||||
},
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
|
||||
import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
|
||||
import SidebarHeader from './sidebar/sidebar_header.vue';
|
||||
import SidebarTodo from './sidebar/sidebar_todo.vue';
|
||||
import SidebarStatus from './sidebar/sidebar_status.vue';
|
|
@ -2,8 +2,7 @@
|
|||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import updateAlertStatusMutation from '~/graphql_shared/mutations/update_alert_status.mutation.graphql';
|
||||
import { trackAlertStatusUpdateOptions } from '../constants';
|
||||
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
|
@ -21,6 +20,11 @@ export default {
|
|||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
},
|
||||
inject: {
|
||||
trackAlertStatusUpdateOptions: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
projectPath: {
|
||||
type: String,
|
||||
|
@ -58,7 +62,9 @@ export default {
|
|||
},
|
||||
})
|
||||
.then((resp) => {
|
||||
this.trackStatusUpdate(status);
|
||||
if (this.trackAlertStatusUpdateOptions) {
|
||||
this.trackStatusUpdate(status);
|
||||
}
|
||||
const errors = resp.data?.updateAlertStatus?.errors || [];
|
||||
|
||||
if (errors[0]) {
|
||||
|
@ -81,7 +87,7 @@ export default {
|
|||
});
|
||||
},
|
||||
trackStatusUpdate(status) {
|
||||
const { category, action, label } = trackAlertStatusUpdateOptions;
|
||||
const { category, action, label } = this.trackAlertStatusUpdateOptions;
|
||||
Tracking.event(category, action, { label, property: status });
|
||||
},
|
||||
},
|
|
@ -4,7 +4,7 @@ import { s__ } from '~/locale';
|
|||
import Todo from '~/sidebar/components/todo_toggle/todo.vue';
|
||||
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
|
||||
import createAlertTodoMutation from '../../graphql/mutations/alert_todo_create.mutation.graphql';
|
||||
import alertQuery from '../../graphql/queries/details.query.graphql';
|
||||
import alertQuery from '../../graphql/queries/alert_details.query.graphql';
|
||||
|
||||
export default {
|
||||
i18n: {
|
29
app/assets/javascripts/vue_shared/alert_details/constants.js
Normal file
29
app/assets/javascripts/vue_shared/alert_details/constants.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const SEVERITY_LEVELS = {
|
||||
CRITICAL: s__('severity|Critical'),
|
||||
HIGH: s__('severity|High'),
|
||||
MEDIUM: s__('severity|Medium'),
|
||||
LOW: s__('severity|Low'),
|
||||
INFO: s__('severity|Info'),
|
||||
UNKNOWN: s__('severity|Unknown'),
|
||||
};
|
||||
|
||||
export const DEFAULT_PAGE = 'OPERATIONS';
|
||||
|
||||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
export const PAGE_CONFIG = {
|
||||
OPERATIONS: {
|
||||
// Tracks snowplow event when user views alert details
|
||||
TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: {
|
||||
category: 'Alert Management',
|
||||
action: 'view_alert_details',
|
||||
},
|
||||
// Tracks snowplow event when alert status is updated
|
||||
TRACK_ALERT_STATUS_UPDATE_OPTIONS: {
|
||||
category: 'Alert Management',
|
||||
action: 'update_alert_status',
|
||||
label: 'Status',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
#import "../fragments/detail_item.fragment.graphql"
|
||||
#import "../fragments/alert_detail_item.fragment.graphql"
|
||||
|
||||
mutation alertTodoCreate($projectPath: ID!, $iid: String!) {
|
||||
alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) {
|
|
@ -1,4 +1,4 @@
|
|||
#import "../fragments/detail_item.fragment.graphql"
|
||||
#import "../fragments/alert_detail_item.fragment.graphql"
|
||||
|
||||
query alertDetails($fullPath: ID!, $alertId: String) {
|
||||
project(fullPath: $fullPath) {
|
|
@ -4,14 +4,15 @@ import Vue from 'vue';
|
|||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import AlertDetails from './components/alert_details.vue';
|
||||
import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql';
|
||||
import sidebarStatusQuery from './graphql/queries/alert_sidebar_status.query.graphql';
|
||||
import createRouter from './router';
|
||||
import { DEFAULT_PAGE, PAGE_CONFIG } from './constants';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export default (selector) => {
|
||||
const domEl = document.querySelector(selector);
|
||||
const { alertId, projectPath, projectIssuesPath, projectId } = domEl.dataset;
|
||||
const { alertId, projectPath, projectIssuesPath, projectId, page = DEFAULT_PAGE } = domEl.dataset;
|
||||
const router = createRouter();
|
||||
|
||||
const resolvers = {
|
||||
|
@ -48,18 +49,28 @@ export default (selector) => {
|
|||
},
|
||||
});
|
||||
|
||||
const provide = {
|
||||
projectPath,
|
||||
alertId,
|
||||
projectIssuesPath,
|
||||
projectId,
|
||||
};
|
||||
|
||||
if (page === DEFAULT_PAGE) {
|
||||
const { TRACK_ALERTS_DETAILS_VIEWS_OPTIONS, TRACK_ALERT_STATUS_UPDATE_OPTIONS } = PAGE_CONFIG[
|
||||
page
|
||||
];
|
||||
provide.trackAlertsDetailsViewsOptions = TRACK_ALERTS_DETAILS_VIEWS_OPTIONS;
|
||||
provide.trackAlertStatusUpdateOptions = TRACK_ALERT_STATUS_UPDATE_OPTIONS;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: selector,
|
||||
components: {
|
||||
AlertDetails,
|
||||
},
|
||||
provide: {
|
||||
projectPath,
|
||||
alertId,
|
||||
projectIssuesPath,
|
||||
projectId,
|
||||
},
|
||||
provide,
|
||||
apolloProvider,
|
||||
router,
|
||||
render(createElement) {
|
|
@ -16,7 +16,12 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
|
|||
result = DependencyProxy::FindOrCreateManifestService.new(group, image, tag, token).execute
|
||||
|
||||
if result[:status] == :success
|
||||
send_upload(result[:manifest].file)
|
||||
response.headers['Docker-Content-Digest'] = result[:manifest].digest
|
||||
response.headers['Content-Length'] = result[:manifest].size
|
||||
response.headers['Docker-Distribution-Api-Version'] = DependencyProxy::DISTRIBUTION_API_VERSION
|
||||
response.headers['Etag'] = "\"#{result[:manifest].digest}\""
|
||||
|
||||
send_upload(result[:manifest].file, send_params: { type: result[:manifest].content_type })
|
||||
else
|
||||
render status: result[:http_status], json: result[:message]
|
||||
end
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
module Ci
|
||||
module PrometheusMetrics
|
||||
class HistogramsController < Projects::ApplicationController
|
||||
feature_category :pipeline_authoring
|
||||
|
||||
respond_to :json, only: [:create]
|
||||
|
||||
def create
|
||||
result = ::Ci::PrometheusMetrics::ObserveHistogramsService.new(project, permitted_params).execute
|
||||
|
||||
render json: result.payload, status: result.http_status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permitted_params
|
||||
params.permit(histograms: [:name, :value])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -41,7 +41,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
|
||||
push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:codequality_mr_diff, @project)
|
||||
push_frontend_feature_flag(:codequality_backend_comparison, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:suggestions_custom_commit, @project)
|
||||
push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
|
||||
|
|
|
@ -5,7 +5,7 @@ module Types
|
|||
graphql_name 'MilestoneStateEnum'
|
||||
description 'Current state of milestone'
|
||||
|
||||
value 'active', 'Milestone is currently active'
|
||||
value 'closed', 'Milestone is closed'
|
||||
value 'active', description: 'Milestone is currently active'
|
||||
value 'closed', description: 'Milestone is closed'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
module DependencyProxy
|
||||
URL_SUFFIX = '/dependency_proxy/containers'
|
||||
DISTRIBUTION_API_VERSION = 'registry/2.0'
|
||||
|
||||
def self.table_name_prefix
|
||||
'dependency_proxy_'
|
||||
|
|
|
@ -12,5 +12,10 @@ class DependencyProxy::Manifest < ApplicationRecord
|
|||
|
||||
mount_file_store_uploader DependencyProxy::FileUploader
|
||||
|
||||
scope :find_or_initialize_by_file_name, ->(file_name) { find_or_initialize_by(file_name: file_name) }
|
||||
def self.find_or_initialize_by_file_name_or_digest(file_name:, digest:)
|
||||
result = find_by(file_name: file_name) || find_by(digest: digest)
|
||||
return result if result
|
||||
|
||||
new(file_name: file_name, digest: digest)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1503,7 +1503,7 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
|
||||
def has_codequality_reports?
|
||||
return false unless ::Gitlab::Ci::Features.display_quality_on_mr_diff?(project)
|
||||
return false unless ::Gitlab::Ci::Features.display_codequality_backend_comparison?(project)
|
||||
|
||||
actual_head_pipeline&.has_reports?(Ci::JobArtifact.codequality_reports)
|
||||
end
|
||||
|
|
|
@ -960,8 +960,8 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ServiceClass
|
||||
def refresh_authorized_projects
|
||||
Users::RefreshAuthorizedProjectsService.new(self).execute
|
||||
def refresh_authorized_projects(source: nil)
|
||||
Users::RefreshAuthorizedProjectsService.new(self, source: source).execute
|
||||
end
|
||||
# rubocop: enable CodeReuse/ServiceClass
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module AuthorizedProjectUpdate
|
|||
|
||||
def execute
|
||||
User.where(id: start_user_id..end_user_id).select(:id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
|
||||
Users::RefreshAuthorizedProjectsService.new(user).execute
|
||||
Users::RefreshAuthorizedProjectsService.new(user, source: self.class.name).execute
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -38,10 +38,15 @@ module Ci
|
|||
|
||||
# mark builds that are retried
|
||||
if latest_statuses.any?
|
||||
pipeline.latest_statuses
|
||||
.where(name: latest_statuses.map(&:second))
|
||||
.where.not(id: latest_statuses.map(&:first))
|
||||
.update_all(retried: true)
|
||||
updated_count = pipeline.latest_statuses
|
||||
.where(name: latest_statuses.map(&:second))
|
||||
.where.not(id: latest_statuses.map(&:first))
|
||||
.update_all(retried: true)
|
||||
|
||||
# This counter is temporary. It will be used to check whether if we still use this method or not
|
||||
# after setting correct value of `GenericCommitStatus#retried`.
|
||||
# More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50465#note_491657115
|
||||
metrics.legacy_update_jobs_counter.increment if updated_count > 0
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
module PrometheusMetrics
|
||||
class ObserveHistogramsService
|
||||
class << self
|
||||
def available_histograms
|
||||
@available_histograms ||= [
|
||||
histogram(:pipeline_graph_link_calculation_duration_seconds, 'Total time spent calculating links, in seconds', {}, [0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2]),
|
||||
histogram(:pipeline_graph_links_total, 'Number of links per graph', {}, [1, 5, 10, 25, 50, 100, 200]),
|
||||
histogram(:pipeline_graph_links_per_job_ratio, 'Ratio of links to job per graph', {}, [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1])
|
||||
].to_h
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def histogram(name, *attrs)
|
||||
[name.to_s, proc { Gitlab::Metrics.histogram(name, *attrs) }]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(project, params)
|
||||
@project = project
|
||||
@params = params
|
||||
end
|
||||
|
||||
def execute
|
||||
return ServiceResponse.success(http_status: :accepted) unless enabled?
|
||||
|
||||
params
|
||||
.fetch(:histograms, [])
|
||||
.each(&method(:observe))
|
||||
|
||||
ServiceResponse.success(http_status: :created)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :params
|
||||
|
||||
def observe(data)
|
||||
histogram = find_histogram(data[:name])
|
||||
histogram.observe({ project: project.full_path }, data[:value].to_f)
|
||||
end
|
||||
|
||||
def find_histogram(name)
|
||||
self.class.available_histograms
|
||||
.fetch(name) { raise ActiveRecord::RecordNotFound }
|
||||
.call
|
||||
end
|
||||
|
||||
def enabled?
|
||||
::Feature.enabled?(:ci_accept_frontend_prometheus_metrics, project, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ module DependencyProxy
|
|||
|
||||
def execute
|
||||
@manifest = @group.dependency_proxy_manifests
|
||||
.find_or_initialize_by_file_name(@file_name)
|
||||
.find_or_initialize_by_file_name_or_digest(file_name: @file_name, digest: @tag)
|
||||
|
||||
head_result = DependencyProxy::HeadManifestService.new(@image, @tag, @token).execute
|
||||
|
||||
|
@ -30,6 +30,7 @@ module DependencyProxy
|
|||
def pull_new_manifest
|
||||
DependencyProxy::PullManifestService.new(@image, @tag, @token).execute_with_manifest do |new_manifest|
|
||||
@manifest.update!(
|
||||
content_type: new_manifest[:content_type],
|
||||
digest: new_manifest[:digest],
|
||||
file: new_manifest[:file],
|
||||
size: new_manifest[:file].size
|
||||
|
@ -38,7 +39,9 @@ module DependencyProxy
|
|||
end
|
||||
|
||||
def cached_manifest_matches?(head_result)
|
||||
@manifest && @manifest.digest == head_result[:digest]
|
||||
return false if head_result[:status] == :error
|
||||
|
||||
@manifest && @manifest.digest == head_result[:digest] && @manifest.content_type == head_result[:content_type]
|
||||
end
|
||||
|
||||
def respond
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module DependencyProxy
|
||||
class HeadManifestService < DependencyProxy::BaseService
|
||||
ACCEPT_HEADERS = ::ContainerRegistry::Client::ACCEPTED_TYPES.join(',')
|
||||
|
||||
def initialize(image, tag, token)
|
||||
@image = image
|
||||
@tag = tag
|
||||
|
@ -9,10 +11,10 @@ module DependencyProxy
|
|||
end
|
||||
|
||||
def execute
|
||||
response = Gitlab::HTTP.head(manifest_url, headers: auth_headers)
|
||||
response = Gitlab::HTTP.head(manifest_url, headers: auth_headers.merge(Accept: ACCEPT_HEADERS))
|
||||
|
||||
if response.success?
|
||||
success(digest: response.headers['docker-content-digest'])
|
||||
success(digest: response.headers['docker-content-digest'], content_type: response.headers['content-type'])
|
||||
else
|
||||
error(response.body, response.code)
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module DependencyProxy
|
|||
def execute_with_manifest
|
||||
raise ArgumentError, 'Block must be provided' unless block_given?
|
||||
|
||||
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers)
|
||||
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers.merge(Accept: ::ContainerRegistry::Client::ACCEPTED_TYPES.join(',')))
|
||||
|
||||
if response.success?
|
||||
file = Tempfile.new
|
||||
|
@ -20,7 +20,7 @@ module DependencyProxy
|
|||
file.write(response)
|
||||
file.flush
|
||||
|
||||
yield(success(file: file, digest: response.headers['docker-content-digest']))
|
||||
yield(success(file: file, digest: response.headers['docker-content-digest'], content_type: response.headers['content-type']))
|
||||
ensure
|
||||
file.close
|
||||
file.unlink
|
||||
|
|
|
@ -17,8 +17,21 @@ class IssueRebalancingService
|
|||
|
||||
start = RelativePositioning::START_POSITION - (gaps / 2) * gap_size
|
||||
|
||||
Issue.transaction do
|
||||
indexed_ids.each_slice(100) { |pairs| assign_positions(start, pairs) }
|
||||
if Feature.enabled?(:issue_rebalancing_optimization)
|
||||
Issue.transaction do
|
||||
assign_positions(start, indexed_ids)
|
||||
.sort_by(&:first)
|
||||
.each_slice(100) do |pairs_with_position|
|
||||
update_positions(pairs_with_position, 'rebalance issue positions in batches ordered by id')
|
||||
end
|
||||
end
|
||||
else
|
||||
Issue.transaction do
|
||||
indexed_ids.each_slice(100) do |pairs|
|
||||
pairs_with_position = assign_positions(start, pairs)
|
||||
update_positions(pairs_with_position, 'rebalance issue positions')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,13 +45,22 @@ class IssueRebalancingService
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def assign_positions(start, positions)
|
||||
values = positions.map do |id, index|
|
||||
"(#{id}, #{start + (index * gap_size)})"
|
||||
def assign_positions(start, pairs)
|
||||
pairs.map do |id, index|
|
||||
[id, start + (index * gap_size)]
|
||||
end
|
||||
end
|
||||
|
||||
def update_positions(pairs_with_position, query_name)
|
||||
values = pairs_with_position.map do |id, index|
|
||||
"(#{id}, #{index})"
|
||||
end.join(', ')
|
||||
|
||||
Issue.connection.exec_query(<<~SQL, "rebalance issue positions")
|
||||
run_update_query(values, query_name)
|
||||
end
|
||||
|
||||
def run_update_query(values, query_name)
|
||||
Issue.connection.exec_query(<<~SQL, query_name)
|
||||
WITH cte(cte_id, new_pos) AS (
|
||||
SELECT *
|
||||
FROM (VALUES #{values}) as t (id, pos)
|
||||
|
@ -49,7 +71,6 @@ class IssueRebalancingService
|
|||
WHERE cte_id = id
|
||||
SQL
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def issue_count
|
||||
@issue_count ||= base.count
|
||||
|
|
|
@ -14,13 +14,14 @@ module Users
|
|||
# service = Users::RefreshAuthorizedProjectsService.new(some_user)
|
||||
# service.execute
|
||||
class RefreshAuthorizedProjectsService
|
||||
attr_reader :user
|
||||
attr_reader :user, :source
|
||||
|
||||
LEASE_TIMEOUT = 1.minute.to_i
|
||||
|
||||
# user - The User for which to refresh the authorized projects.
|
||||
def initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
|
||||
def initialize(user, source: nil, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
|
||||
@user = user
|
||||
@source = source
|
||||
@incorrect_auth_found_callback = incorrect_auth_found_callback
|
||||
@missing_auth_found_callback = missing_auth_found_callback
|
||||
|
||||
|
@ -91,6 +92,8 @@ module Users
|
|||
# remove - The IDs of the authorization rows to remove.
|
||||
# add - Rows to insert in the form `[user id, project id, access level]`
|
||||
def update_authorizations(remove = [], add = [])
|
||||
log_refresh_details(remove.length, add.length)
|
||||
|
||||
User.transaction do
|
||||
user.remove_project_authorizations(remove) unless remove.empty?
|
||||
ProjectAuthorization.insert_authorizations(add) unless add.empty?
|
||||
|
@ -101,6 +104,13 @@ module Users
|
|||
user.reset
|
||||
end
|
||||
|
||||
def log_refresh_details(rows_deleted, rows_added)
|
||||
Gitlab::AppJsonLogger.info(event: 'authorized_projects_refresh',
|
||||
'authorized_projects_refresh.source': source,
|
||||
'authorized_projects_refresh.rows_deleted': rows_deleted,
|
||||
'authorized_projects_refresh.rows_added': rows_added)
|
||||
end
|
||||
|
||||
def fresh_access_levels_per_project
|
||||
fresh_authorizations.each_with_object({}) do |row, hash|
|
||||
hash[row.project_id] = row.access_level
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
|
||||
.control
|
||||
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do
|
||||
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full gl-inset-border-1-gray-200!', spellcheck: false }
|
||||
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
|
||||
.control.d-none.d-md-block
|
||||
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-svg' do
|
||||
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-default btn-icon' do
|
||||
= sprite_icon('rss', css_class: 'qa-rss-icon')
|
||||
|
||||
= render_if_exists 'projects/commits/mirror_status'
|
||||
|
|
|
@ -25,7 +25,7 @@ class AuthorizedProjectsWorker
|
|||
def perform(user_id)
|
||||
user = User.find_by(id: user_id)
|
||||
|
||||
user&.refresh_authorized_projects
|
||||
user&.refresh_authorized_projects(source: self.class.name)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
5
changelogs/unreleased/290944-pull-by-digest.yml
Normal file
5
changelogs/unreleased/290944-pull-by-digest.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Pull-by-digest and Docker 20.x support for the Dependency Proxy
|
||||
merge_request: 52805
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/btn-default-rss.yml
Normal file
5
changelogs/unreleased/btn-default-rss.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Apply new GitLab UI for RSS button in commits page
|
||||
merge_request: 52900
|
||||
author: Yogi (@yo)
|
||||
type: other
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_accept_frontend_prometheus_metrics
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52820
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300770
|
||||
milestone: '13.9'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: codequality_backend_comparison
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53068
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300796
|
||||
milestone: '13.9'
|
||||
type: development
|
||||
group: group::testing
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: issue_rebalancing_optimization
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53384
|
||||
rollout_issue_url:
|
||||
milestone: '13.9'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: false
|
|
@ -87,6 +87,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
resource :lint, only: [:show, :create]
|
||||
resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor'
|
||||
resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ }
|
||||
namespace :prometheus_metrics do
|
||||
resources :histograms, only: [:create], constraints: { format: 'json' }
|
||||
end
|
||||
end
|
||||
|
||||
namespace :settings do
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddContentTypeToDependencyProxyManifests < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
# rubocop:disable Migration/AddLimitToTextColumns
|
||||
# limit is added in 20210128140232_add_text_limit_to_dependency_proxy_manifests_content_type.rb
|
||||
def change
|
||||
add_column :dependency_proxy_manifests, :content_type, :text
|
||||
end
|
||||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTextLimitToDependencyProxyManifestsContentType < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_text_limit :dependency_proxy_manifests, :content_type, 255
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :dependency_proxy_manifests, :content_type
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210128140157
Normal file
1
db/schema_migrations/20210128140157
Normal file
|
@ -0,0 +1 @@
|
|||
0fa84b2038f33e27e549bdb3eb137e1813f604b6e81abc67a49a54d3e1e4bcf5
|
1
db/schema_migrations/20210128140232
Normal file
1
db/schema_migrations/20210128140232
Normal file
|
@ -0,0 +1 @@
|
|||
93f337364eb5ca5c67f4d4767d1aee9972bfe0596c89f006317dd6103558e35c
|
|
@ -11775,7 +11775,9 @@ CREATE TABLE dependency_proxy_manifests (
|
|||
file_name text NOT NULL,
|
||||
file text NOT NULL,
|
||||
digest text NOT NULL,
|
||||
content_type text,
|
||||
CONSTRAINT check_079b293a7b CHECK ((char_length(file) <= 255)),
|
||||
CONSTRAINT check_167a9a8a91 CHECK ((char_length(content_type) <= 255)),
|
||||
CONSTRAINT check_c579e3f586 CHECK ((char_length(file_name) <= 255)),
|
||||
CONSTRAINT check_f5d9996bf1 CHECK ((char_length(digest) <= 255))
|
||||
);
|
||||
|
|
|
@ -4,7 +4,7 @@ group: Compliance
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Audit Events **(STARTER)**
|
||||
# Audit Events **(PREMIUM)**
|
||||
|
||||
GitLab offers a way to view the changes made within the GitLab server for owners and administrators on a [paid plan](https://about.gitlab.com/pricing/).
|
||||
|
||||
|
@ -42,7 +42,7 @@ There are two kinds of events logged:
|
|||
|
||||
Impersonation is where an administrator uses credentials to perform an action as a different user.
|
||||
|
||||
### Group events **(STARTER)**
|
||||
### Group events **(PREMIUM)**
|
||||
|
||||
A user with a Owner role (or above) can retrieve group audit events of all users.
|
||||
A user with a Developer or Maintainer role is limited to group audit events based on their individual actions.
|
||||
|
@ -72,7 +72,7 @@ From there, you can see the following actions:
|
|||
|
||||
Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)
|
||||
|
||||
### Project events **(STARTER)**
|
||||
### Project events **(PREMIUM)**
|
||||
|
||||
A user with a Maintainer role (or above) can retrieve project audit events of all users.
|
||||
A user with a Developer role is limited to project audit events based on their individual actions.
|
||||
|
|
|
@ -119,6 +119,9 @@ The following metrics are available:
|
|||
| `gitlab_external_http_duration_seconds` | Counter | 13.8 | Duration in seconds spent on each HTTP call to external systems | |
|
||||
| `gitlab_external_http_exception_total` | Counter | 13.8 | Total number of exceptions raised when making external HTTP calls | |
|
||||
| `ci_report_parser_duration_seconds` | Histogram | 13.9 | Time to parse CI/CD report artifacts | `parser` |
|
||||
| `pipeline_graph_link_calculation_duration_seconds` | Histogram | 13.9 | Total time spent calculating links, in seconds | `project` |
|
||||
| `pipeline_graph_links_total` | Histogram | 13.9 | Number of links per graph | `project` |
|
||||
| `pipeline_graph_links_per_job_ratio` | Histogram | 13.9 | Ratio of links to job per graph | `project` |
|
||||
|
||||
## Metrics controlled by a feature flag
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
type: reference, howto
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
group: Editor
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Group Audit Events **(STARTER)**
|
||||
## Group Audit Events **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34078) in GitLab 12.5.
|
||||
|
||||
|
@ -233,7 +233,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Project Audit Events **(STARTER)**
|
||||
## Project Audit Events **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219238) in GitLab 13.1.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
|
||||
|
||||
Deletes the cached manifests and blobs for a group. This endpoint requires group admin access.
|
||||
Deletes the cached manifests and blobs for a group. This endpoint requires group owner access.
|
||||
|
||||
WARNING:
|
||||
[A bug exists](https://gitlab.com/gitlab-org/gitlab/-/issues/277161) for this API.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
group: Editor
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
type: reference, api
|
||||
---
|
||||
|
|
|
@ -123,7 +123,7 @@ Parameters:
|
|||
| `tag_name` | string | yes | The name of a tag |
|
||||
| `ref` | string | yes | Create tag using commit SHA, another tag name, or branch name |
|
||||
| `message` | string | no | Creates annotated tag |
|
||||
| `release_description` | string | no | Add release notes to the Git tag and store it in the GitLab database |
|
||||
| `release_description` | string | no | This parameter is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766) for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311) in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead. |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/tags?tag_name=test&ref=master"
|
||||
|
@ -186,6 +186,11 @@ Parameters:
|
|||
|
||||
## Create a new release
|
||||
|
||||
WARNING:
|
||||
This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766)
|
||||
for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311)
|
||||
in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead.
|
||||
|
||||
Add release notes to the existing Git tag. If there
|
||||
already exists a release for the given tag, status code `409` is returned.
|
||||
|
||||
|
@ -221,6 +226,11 @@ Response:
|
|||
|
||||
## Update a release
|
||||
|
||||
WARNING:
|
||||
This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766)
|
||||
for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311)
|
||||
in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead.
|
||||
|
||||
Updates the release notes of a given release.
|
||||
|
||||
```plaintext
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
group: Editor
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
type: reference, api
|
||||
---
|
||||
|
|
|
@ -209,7 +209,7 @@ as well as encourage inner sourcing.
|
|||
|
||||
In self-managed GitLab instances, you can build an [Instance Template Repository](../../user/admin_area/settings/instance_template_repository.md).
|
||||
Development teams across the whole organization can select templates from a dropdown menu.
|
||||
A group administrator is able to set a group to use as the source for the
|
||||
A group maintainer or a group owner is able to set a group to use as the source for the
|
||||
[custom project templates](../../user/admin_area/custom_project_templates.md), which can
|
||||
be used by all projects in the group. An instance administrator can set a group as
|
||||
the source for [instance project templates](../../user/group/custom_project_templates.md),
|
||||
|
|
|
@ -262,6 +262,23 @@ To [prevent duplicate pipelines](yaml/README.md#prevent-duplicate-pipelines), us
|
|||
[`workflow: rules`](yaml/README.md#workflowrules) or rewrite your rules to control
|
||||
which pipelines can run.
|
||||
|
||||
### Console workaround if job using resource_group gets stuck
|
||||
|
||||
```ruby
|
||||
# find resource group by name
|
||||
resource_group = Project.find_by_full_path('...').resource_groups.find_by(key: 'the-group-name')
|
||||
busy_resources = resource_group.resources.where('build_id IS NOT NULL')
|
||||
|
||||
# identify which builds are occupying the resource
|
||||
# (I think it should be 1 as of today)
|
||||
busy_resources.pluck(:build_id)
|
||||
|
||||
# it's good to check why this build is holding the resource.
|
||||
# Is it stuck? Has it been forcefully dropped by the system?
|
||||
# free up busy resources
|
||||
busy_resources.update_all(build_id: nil)
|
||||
```
|
||||
|
||||
## How to get help
|
||||
|
||||
If you are unable to resolve pipeline issues, you can get help from:
|
||||
|
|
|
@ -997,7 +997,8 @@ To link to internal documentation:
|
|||
- Use relative links to Markdown files in the same repository.
|
||||
- Do not use absolute URLs or URLs from `docs.gitlab.com`.
|
||||
- Use `../` to navigate to higher-level directories.
|
||||
- Don't prepend `./` to links to files or directories.
|
||||
- Don't prepend `./` to links to files or directories. To link to a file in the
|
||||
same directory or one of its sub-directories, use the syntax `path/to/file.md`.
|
||||
- Don't link relative to root. For example, `/ee/user/gitlab_com/index.md`.
|
||||
|
||||
Don't:
|
||||
|
@ -1022,6 +1023,7 @@ To link to internal documentation:
|
|||
- `../../merge_requests/index.md`
|
||||
- `../../issues/tags.md`
|
||||
- `../../issues/tags.md#stages`
|
||||
- `issues/tags.md`
|
||||
|
||||
NOTE:
|
||||
Using the Markdown extension is necessary for the [`/help`](../index.md#gitlab-help)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
type: reference, dev
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
group: Editor
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
description: "GitLab's development guidelines for Wikis"
|
||||
---
|
||||
|
|
|
@ -56,7 +56,7 @@ If you're using:
|
|||
- Self-managed GitLab, self-managed Jira, or both, configure the integration using
|
||||
[Jira's DVCS Connector](#jira-dvcs-configuration), which syncs data hourly.
|
||||
|
||||
We recommend that a GitLab group administrator or instance administrator (in the case of
|
||||
We recommend that a GitLab group maintainer or group owner, or instance administrator (in the case of
|
||||
self-managed GitLab) set up the integration to simplify administration.
|
||||
|
||||
### Jira DVCS configuration
|
||||
|
|
|
@ -242,7 +242,7 @@ For each runner, the following attributes are listed:
|
|||
| Projects | Projects to which the runner is assigned |
|
||||
| Jobs | Total of jobs run by the runner |
|
||||
| Tags | Tags associated with the runner |
|
||||
| Last contact | Timestamp indicating when the GitLab instance last contacted the runner |
|
||||
| Last contact | Timestamp indicating when the runner last contacted the GitLab instance |
|
||||
|
||||
You can also edit, pause, or remove each runner.
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
---
|
||||
|
||||
|
||||
# Code Review Analytics **(STARTER)**
|
||||
# Code Review Analytics **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38062) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.7.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Optimize
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Merge Request Analytics **(STARTER)**
|
||||
# Merge Request Analytics **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229045) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.3.
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ stage: Manage
|
|||
group: Optimize
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
# Contribution Analytics **(STARTER)**
|
||||
# Contribution Analytics **(PREMIUM)**
|
||||
|
||||
> - Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3090) for subgroups in GitLab 12.2.
|
||||
|
|
|
@ -560,7 +560,7 @@ You can change settings that are specific to repositories in your group.
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43290) in GitLab 13.6.
|
||||
|
||||
By default, when you create a new project in GitLab, the initial branch is called `master`.
|
||||
For groups, a group administrator can customize the initial branch name to something
|
||||
For groups, a group owner can customize the initial branch name to something
|
||||
else. This way, every new project created under that group from then on will start from the custom branch name rather than `master`. To do so:
|
||||
|
||||
1. Go to the **Group page > Settings > Repository** and expand **Default initial
|
||||
|
|
|
@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
WARNING:
|
||||
This [Closed Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#sts=Closed%20Beta) feature is being re-evaluated in favor of a different
|
||||
[identity model](https://gitlab.com/groups/gitlab-org/-/epics/4345) that does not require separate accounts.
|
||||
We recommend that group administrators who haven't yet implemented this feature wait for
|
||||
We recommend that group owners who haven't yet implemented this feature wait for
|
||||
the new solution.
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/709) in GitLab 12.1.
|
||||
|
|
|
@ -7,9 +7,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Dependency Proxy
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
|
||||
> - [Support for private groups](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - Anonymous access to images in public groups is no longer available starting in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
|
||||
> - [Support for private groups](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - Anonymous access to images in public groups is no longer available starting in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - [Support for pull-by-digest and Docker version 20.x](https://gitlab.com/gitlab-org/gitlab/-/issues/290944) in [GitLab Free](https://about.gitlab.com/pricing/) 13.9.
|
||||
|
||||
The GitLab Dependency Proxy is a local proxy you can use for your frequently-accessed
|
||||
upstream images.
|
||||
|
@ -17,11 +18,6 @@ upstream images.
|
|||
In the case of CI/CD, the Dependency Proxy receives a request and returns the
|
||||
upstream image from a registry, acting as a pull-through cache.
|
||||
|
||||
NOTE:
|
||||
The Dependency Proxy is not compatible with Docker version 20.x and later.
|
||||
If you are using the Dependency Proxy, Docker version 19.x.x is recommended until
|
||||
[issue #290944](https://gitlab.com/gitlab-org/gitlab/-/issues/290944) is resolved.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The Dependency Proxy must be [enabled by an administrator](../../../administration/packages/dependency_proxy.md).
|
||||
|
@ -60,7 +56,7 @@ Prerequisites:
|
|||
|
||||
### Authenticate with the Dependency Proxy
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
|
@ -162,7 +158,7 @@ the [Dependency Proxy API](../../../api/dependency_proxy.md).
|
|||
|
||||
## Docker Hub rate limits and the Dependency Proxy
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241639) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241639) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
Watch how to [use the Dependency Proxy to help avoid Docker Hub rate limits](https://youtu.be/Nc4nUo7Pq08).
|
||||
|
|
|
@ -68,7 +68,7 @@ To use the GitLab endpoint for NuGet Packages, choose an option:
|
|||
|
||||
- **Project-level**: Use when you have few NuGet packages and they are not in
|
||||
the same GitLab group.
|
||||
- **Group-level**: Use when you have many NuGet packages in different within the
|
||||
- **Group-level**: Use when you have many NuGet packages in different projects within the
|
||||
same GitLab group.
|
||||
|
||||
Some features such as [publishing](#publish-a-nuget-package) a package are only available on the project-level endpoint.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
stage: Plan
|
||||
group: Product Planning
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
---
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ For a demonstration, see [Pages access controls](https://www.youtube.com/watch?v
|
|||
- **Only project members**: Only project members are able to browse the website.
|
||||
- **Everyone with access**: Everyone, both logged into and logged out of GitLab, is able to browse the website, no matter their project membership.
|
||||
|
||||
1. Click **Save changes**.
|
||||
1. Click **Save changes**. Note that your changes may not take effect immediately. GitLab Pages uses
|
||||
a caching mechanism for efficiency. Your changes may not take effect until that cache is
|
||||
invalidated, which usually takes less than a minute.
|
||||
|
||||
The next time someone tries to access your website and the access control is
|
||||
enabled, they're presented with a page to sign into GitLab and verify they
|
||||
|
|
|
@ -266,7 +266,7 @@ This action:
|
|||
|
||||
- Deletes a project including all associated resources (issues, merge requests etc).
|
||||
- From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on [Premium](https://about.gitlab.com/pricing/) or higher tiers,
|
||||
group administrators can [configure](../../group/index.md#enabling-delayed-project-removal) projects within a group
|
||||
group owners can [configure](../../group/index.md#enabling-delayed-project-removal) projects within a group
|
||||
to be deleted after a delayed period.
|
||||
When enabled, actual deletion happens after number of days
|
||||
specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
group: Editor
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
type: reference, how-to
|
||||
---
|
||||
|
|
|
@ -71,6 +71,10 @@ module Gitlab
|
|||
def self.display_quality_on_mr_diff?(project)
|
||||
::Feature.enabled?(:codequality_mr_diff, project, default_enabled: false)
|
||||
end
|
||||
|
||||
def self.display_codequality_backend_comparison?(project)
|
||||
::Feature.enabled?(:codequality_backend_comparison, project, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,6 +45,15 @@ module Gitlab
|
|||
Gitlab::Metrics.counter(name, comment)
|
||||
end
|
||||
end
|
||||
|
||||
def legacy_update_jobs_counter
|
||||
strong_memoize(:legacy_update_jobs_counter) do
|
||||
name = :ci_legacy_update_jobs_as_retried_total
|
||||
comment = 'Counter of occurrences when jobs were not being set as retried before update_retried'
|
||||
|
||||
Gitlab::Metrics.counter(name, comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2483,9 +2483,6 @@ msgstr ""
|
|||
msgid "AlertManagement|Create incident"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Critical"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2498,24 +2495,12 @@ msgstr ""
|
|||
msgid "AlertManagement|Events"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|High"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Incident"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Info"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Key"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Low"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Medium"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Metrics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2597,9 +2582,6 @@ msgstr ""
|
|||
msgid "AlertManagement|Triggered"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Unknown"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Value"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24544,6 +24526,9 @@ msgstr ""
|
|||
msgid "Reports|Actions"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reports|Activity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reports|An error occured while loading report"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25805,6 +25790,9 @@ msgstr ""
|
|||
msgid "SecurityReports|Add projects to your group"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|All"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25883,6 +25871,12 @@ msgstr ""
|
|||
msgid "SecurityReports|More information"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|No activity"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|No longer detected"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|No vulnerabilities found"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26008,6 +26002,9 @@ msgstr ""
|
|||
msgid "SecurityReports|While it's rare to have no vulnerabilities, it can happen. In any event, we ask that you double check your settings to make sure you've set up your dashboard correctly."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|With issues"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Won't fix / Accept risk"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.182.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "27.1.5",
|
||||
"@gitlab/ui": "27.4.3",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
|
|
|
@ -130,7 +130,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
}
|
||||
end
|
||||
|
||||
it 'proxies status from the remote token request' do
|
||||
it 'proxies status from the remote token request', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
|
@ -147,7 +147,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
}
|
||||
end
|
||||
|
||||
it 'proxies status from the remote manifest request' do
|
||||
it 'proxies status from the remote manifest request', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
|
@ -156,15 +156,19 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
end
|
||||
|
||||
it 'sends a file' do
|
||||
expect(controller).to receive(:send_file).with(manifest.file.path, {})
|
||||
expect(controller).to receive(:send_file).with(manifest.file.path, type: manifest.content_type)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns Content-Disposition: attachment' do
|
||||
it 'returns Content-Disposition: attachment', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.headers['Docker-Content-Digest']).to eq(manifest.digest)
|
||||
expect(response.headers['Content-Length']).to eq(manifest.size)
|
||||
expect(response.headers['Docker-Distribution-Api-Version']).to eq(DependencyProxy::DISTRIBUTION_API_VERSION)
|
||||
expect(response.headers['Etag']).to eq("\"#{manifest.digest}\"")
|
||||
expect(response.headers['Content-Disposition']).to match(/^attachment/)
|
||||
end
|
||||
end
|
||||
|
@ -207,7 +211,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
}
|
||||
end
|
||||
|
||||
it 'proxies status from the remote blob request' do
|
||||
it 'proxies status from the remote blob request', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
|
@ -221,7 +225,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
subject
|
||||
end
|
||||
|
||||
it 'returns Content-Disposition: attachment' do
|
||||
it 'returns Content-Disposition: attachment', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
|
|
@ -10,7 +10,8 @@ FactoryBot.define do
|
|||
factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
|
||||
group
|
||||
file { fixture_file_upload('spec/fixtures/dependency_proxy/manifest') }
|
||||
digest { 'sha256:5ab5a6872b264fe4fd35d63991b9b7d8425f4bc79e7cf4d563c10956581170c9' }
|
||||
digest { 'sha256:d0710affa17fad5f466a70159cc458227bd25d4afb39514ef662ead3e6c99515' }
|
||||
file_name { 'alpine:latest.json' }
|
||||
content_type { 'application/vnd.docker.distribution.manifest.v2+json' }
|
||||
end
|
||||
end
|
||||
|
|
44
spec/fixtures/dependency_proxy/manifest
vendored
44
spec/fixtures/dependency_proxy/manifest
vendored
|
@ -1,38 +1,16 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "library/alpine",
|
||||
"tag": "latest",
|
||||
"architecture": "amd64",
|
||||
"fsLayers": [
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 1472,
|
||||
"digest": "sha256:7731472c3f2a25edbb9c085c78f42ec71259f2b83485aa60648276d408865839"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
|
||||
},
|
||||
{
|
||||
"blobSum": "sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964"
|
||||
}
|
||||
],
|
||||
"history": [
|
||||
{
|
||||
"v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container\":\"8c59eb170e19b8c3768b8d06c91053b0debf4a6fa6a452df394145fe9b885ea5\",\"container_config\":{\"Hostname\":\"8c59eb170e19\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"/bin/sh\\\"]\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2020-10-22T02:19:24.499382102Z\",\"docker_version\":\"18.09.7\",\"id\":\"c5f1aab5bb88eaf1aa62bea08ea6654547d43fd4d15b1a476c77e705dd5385ba\",\"os\":\"linux\",\"parent\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"throwaway\":true}"
|
||||
},
|
||||
{
|
||||
"v1Compatibility": "{\"id\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"created\":\"2020-10-22T02:19:24.33416307Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:f17f65714f703db9012f00e5ec98d0b2541ff6147c2633f7ab9ba659d0c507f4 in / \"]}}"
|
||||
}
|
||||
],
|
||||
"signatures": [
|
||||
{
|
||||
"header": {
|
||||
"jwk": {
|
||||
"crv": "P-256",
|
||||
"kid": "XOTE:DZ4C:YBPJ:3O3L:YI4B:NYXU:T4VR:USH6:CXXN:SELU:CSCC:FVPE",
|
||||
"kty": "EC",
|
||||
"x": "cR1zye_3354mdbD7Dn-mtXNXvtPtmLlUVDa5vH6Lp74",
|
||||
"y": "rldUXSllLit6_2BW6AV8aqkwWJXHoYPG9OwkIBouwxQ"
|
||||
},
|
||||
"alg": "ES256"
|
||||
},
|
||||
"signature": "DYB2iB-XKIisqp5Q0OXFOBIOlBOuRV7pnZuKy0cxVB2Qj1VFRhWX4Tq336y0VMWbF6ma1he5A1E_Vk4jazrJ9g",
|
||||
"protected": "eyJmb3JtYXRMZW5ndGgiOjIxMzcsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMC0xMS0yNFQyMjowMTo1MVoifQ"
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 2810825,
|
||||
"digest": "sha256:596ba82af5aaa3e2fd9d6f955b8b94f0744a2b60710e3c243ba3e4a467f051d1"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,11 +2,11 @@ import { mount } from '@vue/test-utils';
|
|||
import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@gitlab/ui';
|
||||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import mockAlerts from 'jest/vue_shared/alert_details/mocks/alerts.json';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import AlertManagementTable from '~/alert_management/components/alert_management_table.vue';
|
||||
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
||||
import mockAlerts from '../mocks/alerts.json';
|
||||
import defaultProvideValues from '../mocks/alerts_provide_config.json';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility', () => ({
|
||||
|
|
|
@ -1,72 +1,307 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
|
||||
"<form class=\\"gl-mt-6\\">
|
||||
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
|
||||
<div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
|
||||
<div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select mw-100 custom-select\\" id=\\"__BVID__8\\">
|
||||
<option value=\\"\\">Select integration type</option>
|
||||
<option value=\\"HTTP\\">HTTP Endpoint</option>
|
||||
<option value=\\"PROMETHEUS\\">External Prometheus</option>
|
||||
<form
|
||||
class="gl-mt-6"
|
||||
>
|
||||
<h5
|
||||
class="gl-font-lg gl-my-5"
|
||||
>
|
||||
Add new integrations
|
||||
</h5>
|
||||
|
||||
<div
|
||||
class="form-group gl-form-group"
|
||||
id="integration-type"
|
||||
role="group"
|
||||
>
|
||||
<label
|
||||
class="d-block col-form-label"
|
||||
for="integration-type"
|
||||
id="integration-type__BV_label_"
|
||||
>
|
||||
1. Select integration type
|
||||
</label>
|
||||
<div
|
||||
class="bv-no-focus-ring"
|
||||
>
|
||||
<select
|
||||
class="gl-form-select mw-100 custom-select"
|
||||
id="__BVID__8"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
Select integration type
|
||||
</option>
|
||||
<option
|
||||
value="HTTP"
|
||||
>
|
||||
HTTP Endpoint
|
||||
</option>
|
||||
<option
|
||||
value="PROMETHEUS"
|
||||
>
|
||||
External Prometheus
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<transition-stub css=\\"true\\" enterclass=\\"\\" leaveclass=\\"collapse show\\" entertoclass=\\"collapse show\\" leavetoclass=\\"collapse\\" enteractiveclass=\\"collapsing\\" leaveactiveclass=\\"collapsing\\" class=\\"gl-mt-3\\">
|
||||
<div class=\\"collapse\\" style=\\"display: none;\\" id=\\"__BVID__10\\">
|
||||
|
||||
<transition-stub
|
||||
class="gl-mt-3"
|
||||
css="true"
|
||||
enteractiveclass="collapsing"
|
||||
enterclass=""
|
||||
entertoclass="collapse show"
|
||||
leaveactiveclass="collapsing"
|
||||
leaveclass="collapse show"
|
||||
leavetoclass="collapse"
|
||||
>
|
||||
<div
|
||||
class="collapse"
|
||||
id="__BVID__10"
|
||||
style="display: none;"
|
||||
>
|
||||
<div>
|
||||
<div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
|
||||
<div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__15\\">
|
||||
<div
|
||||
class="form-group gl-form-group"
|
||||
id="name-integration"
|
||||
role="group"
|
||||
>
|
||||
<label
|
||||
class="d-block col-form-label"
|
||||
for="name-integration"
|
||||
id="name-integration__BV_label_"
|
||||
>
|
||||
2. Name integration
|
||||
</label>
|
||||
<div
|
||||
class="bv-no-focus-ring"
|
||||
>
|
||||
<input
|
||||
class="gl-form-input form-control"
|
||||
id="__BVID__15"
|
||||
placeholder="Enter integration name"
|
||||
type="text"
|
||||
/>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
|
||||
<div class=\\"bv-no-focus-ring\\"><span>Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
|
||||
<div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
|
||||
<!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" aria-hidden=\\"true\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
|
||||
|
||||
<div
|
||||
class="form-group gl-form-group"
|
||||
id="integration-webhook"
|
||||
role="group"
|
||||
>
|
||||
<label
|
||||
class="d-block col-form-label"
|
||||
for="integration-webhook"
|
||||
id="integration-webhook__BV_label_"
|
||||
>
|
||||
3. Set up webhook
|
||||
</label>
|
||||
<div
|
||||
class="bv-no-focus-ring"
|
||||
>
|
||||
<span>
|
||||
Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the
|
||||
<a
|
||||
class="gl-link gl-display-inline-block"
|
||||
href="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
GitLab documentation
|
||||
</a>
|
||||
to learn more about configuring your endpoint.
|
||||
</span>
|
||||
|
||||
<label
|
||||
class="gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal"
|
||||
>
|
||||
<span
|
||||
class="gl-toggle-wrapper"
|
||||
>
|
||||
<span
|
||||
class="gl-toggle-label"
|
||||
data-testid="toggle-label"
|
||||
>
|
||||
Active
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
|
||||
<button
|
||||
aria-label="Active"
|
||||
class="gl-toggle"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="toggle-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16"
|
||||
data-testid="close-icon"
|
||||
>
|
||||
<use
|
||||
href="#close"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
</label>
|
||||
|
||||
<!---->
|
||||
<div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
|
||||
|
||||
<div
|
||||
class="gl-my-4"
|
||||
>
|
||||
<span
|
||||
class="gl-font-weight-bold"
|
||||
>
|
||||
|
||||
Webhook URL
|
||||
</span>
|
||||
<div id=\\"url\\" readonly=\\"readonly\\">
|
||||
<div role=\\"group\\" class=\\"input-group\\">
|
||||
|
||||
</span>
|
||||
|
||||
<div
|
||||
id="url"
|
||||
readonly="readonly"
|
||||
>
|
||||
<div
|
||||
class="input-group"
|
||||
role="group"
|
||||
>
|
||||
<!---->
|
||||
<!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
||||
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
||||
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
|
||||
<use href=\\"#copy-to-clipboard\\"></use>
|
||||
<!---->
|
||||
|
||||
<input
|
||||
class="gl-form-input form-control"
|
||||
id="url"
|
||||
readonly="readonly"
|
||||
type="text"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="input-group-append"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy this value"
|
||||
class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
|
||||
data-clipboard-text=""
|
||||
title="Copy"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-button-icon gl-icon s16"
|
||||
data-testid="copy-to-clipboard-icon"
|
||||
>
|
||||
<use
|
||||
href="#copy-to-clipboard"
|
||||
/>
|
||||
</svg>
|
||||
<!----></button></div>
|
||||
|
||||
<!---->
|
||||
</button>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
|
||||
|
||||
<div
|
||||
class="gl-my-4"
|
||||
>
|
||||
<span
|
||||
class="gl-font-weight-bold"
|
||||
>
|
||||
|
||||
Authorization key
|
||||
</span>
|
||||
<div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-3\\">
|
||||
<div role=\\"group\\" class=\\"input-group\\">
|
||||
|
||||
</span>
|
||||
|
||||
<div
|
||||
class="gl-mb-3"
|
||||
id="authorization-key"
|
||||
readonly="readonly"
|
||||
>
|
||||
<div
|
||||
class="input-group"
|
||||
role="group"
|
||||
>
|
||||
<!---->
|
||||
<!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
||||
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
||||
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
|
||||
<use href=\\"#copy-to-clipboard\\"></use>
|
||||
<!---->
|
||||
|
||||
<input
|
||||
class="gl-form-input form-control"
|
||||
id="authorization-key"
|
||||
readonly="readonly"
|
||||
type="text"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="input-group-append"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy this value"
|
||||
class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
|
||||
data-clipboard-text=""
|
||||
title="Copy"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-button-icon gl-icon s16"
|
||||
data-testid="copy-to-clipboard-icon"
|
||||
>
|
||||
<use
|
||||
href="#copy-to-clipboard"
|
||||
/>
|
||||
</svg>
|
||||
<!----></button></div>
|
||||
|
||||
<!---->
|
||||
</button>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-default btn-md disabled gl-button\\">
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn btn-default btn-md disabled gl-button"
|
||||
disabled="disabled"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
<!----> <span class=\\"gl-button-text\\">
|
||||
|
||||
<!---->
|
||||
|
||||
<span
|
||||
class="gl-button-text"
|
||||
>
|
||||
|
||||
Reset Key
|
||||
</span></button>
|
||||
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
|
@ -74,25 +309,98 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
|
|||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Sample alert payload (optional)</label>
|
||||
<div class=\\"bv-no-focus-ring\\"><span>Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"{ "events": [{ "application": "Name of application" }] }\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-3 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
|
||||
|
||||
<div
|
||||
class="form-group gl-form-group"
|
||||
id="test-integration"
|
||||
role="group"
|
||||
>
|
||||
<label
|
||||
class="d-block col-form-label"
|
||||
for="test-integration"
|
||||
id="test-integration__BV_label_"
|
||||
>
|
||||
4. Sample alert payload (optional)
|
||||
</label>
|
||||
<div
|
||||
class="bv-no-focus-ring"
|
||||
>
|
||||
<span>
|
||||
Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).
|
||||
</span>
|
||||
|
||||
<textarea
|
||||
class="gl-form-input gl-form-textarea gl-my-3 form-control is-valid"
|
||||
disabled="disabled"
|
||||
id="test-payload"
|
||||
placeholder="{ \\"events\\": [{ \\"application\\": \\"Name of application\\" }] }"
|
||||
style="resize: none; overflow-y: scroll;"
|
||||
wrap="soft"
|
||||
/>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
<div class=\\"gl-display-flex gl-justify-content-start gl-py-3\\"><button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
|
||||
|
||||
<div
|
||||
class="gl-display-flex gl-justify-content-start gl-py-3"
|
||||
>
|
||||
<button
|
||||
class="btn js-no-auto-disable btn-success btn-md gl-button"
|
||||
data-testid="integration-form-submit"
|
||||
type="submit"
|
||||
>
|
||||
<!---->
|
||||
<!----> <span class=\\"gl-button-text\\">Save integration
|
||||
</span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary\\">
|
||||
|
||||
<!---->
|
||||
<!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button type=\\"reset\\" class=\\"btn js-no-auto-disable btn-default btn-md gl-button\\">
|
||||
|
||||
<span
|
||||
class="gl-button-text"
|
||||
>
|
||||
Save integration
|
||||
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary"
|
||||
data-testid="integration-test-and-submit"
|
||||
disabled="disabled"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
<!----> <span class=\\"gl-button-text\\">Cancel</span></button></div>
|
||||
|
||||
<!---->
|
||||
|
||||
<span
|
||||
class="gl-button-text"
|
||||
>
|
||||
Save and test payload
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn js-no-auto-disable btn-default btn-md gl-button"
|
||||
type="reset"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<!---->
|
||||
|
||||
<span
|
||||
class="gl-button-text"
|
||||
>
|
||||
Cancel
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition-stub>
|
||||
</form>"
|
||||
</form>
|
||||
`;
|
||||
|
|
|
@ -81,7 +81,7 @@ describe('AlertsSettingsFormNew', () => {
|
|||
});
|
||||
|
||||
it('renders the initial template', () => {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render the initial form with only an integration type dropdown', () => {
|
||||
|
|
|
@ -60,8 +60,8 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
|
|||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16 gl-new-dropdown-item-check-icon"
|
||||
data-testid="mobile-issue-close-icon"
|
||||
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-mt-3 gl-align-self-start"
|
||||
data-testid="dropdown-item-checkbox"
|
||||
>
|
||||
<use
|
||||
href="#mobile-issue-close"
|
||||
|
@ -115,8 +115,8 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
|
|||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden"
|
||||
data-testid="mobile-issue-close-icon"
|
||||
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden gl-mt-3 gl-align-self-start"
|
||||
data-testid="dropdown-item-checkbox"
|
||||
>
|
||||
<use
|
||||
href="#mobile-issue-close"
|
||||
|
|
|
@ -22,6 +22,13 @@ describe('graph component', () => {
|
|||
pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
|
||||
};
|
||||
|
||||
const defaultData = {
|
||||
measurements: {
|
||||
width: 800,
|
||||
height: 800,
|
||||
},
|
||||
};
|
||||
|
||||
const createComponent = ({
|
||||
data = {},
|
||||
mountFn = shallowMount,
|
||||
|
@ -34,7 +41,10 @@ describe('graph component', () => {
|
|||
...props,
|
||||
},
|
||||
data() {
|
||||
return { ...data };
|
||||
return {
|
||||
...defaultData,
|
||||
...data,
|
||||
};
|
||||
},
|
||||
provide: {
|
||||
dataMethod: GRAPHQL,
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('Codequality Reports actions', () => {
|
|||
let mock;
|
||||
let diffFeatureFlagEnabled;
|
||||
|
||||
describe('with codequalityMrDiff feature flag enabled', () => {
|
||||
describe('with codequalityBackendComparison feature flag enabled', () => {
|
||||
beforeEach(() => {
|
||||
diffFeatureFlagEnabled = true;
|
||||
localState.reportsPath = `${TEST_HOST}/codequality_reports.json`;
|
||||
|
@ -112,7 +112,7 @@ describe('Codequality Reports actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('with codequalityMrDiff feature flag disabled', () => {
|
||||
describe('with codequalityBackendComparison feature flag disabled', () => {
|
||||
beforeEach(() => {
|
||||
diffFeatureFlagEnabled = false;
|
||||
localState.headPath = `${TEST_HOST}/head.json`;
|
||||
|
|
|
@ -4,17 +4,14 @@ import axios from 'axios';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import AlertDetails from '~/alert_management/components/alert_details.vue';
|
||||
import AlertSummaryRow from '~/alert_management/components/alert_summary_row.vue';
|
||||
import {
|
||||
ALERTS_SEVERITY_LABELS,
|
||||
trackAlertsDetailsViewsOptions,
|
||||
} from '~/alert_management/constants';
|
||||
import createIssueMutation from '~/alert_management/graphql/mutations/create_issue_from_alert.mutation.graphql';
|
||||
import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue';
|
||||
import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue';
|
||||
import { SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
|
||||
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import Tracking from '~/tracking';
|
||||
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
|
||||
import mockAlerts from '../mocks/alerts.json';
|
||||
import mockAlerts from './mocks/alerts.json';
|
||||
|
||||
const mockAlert = mockAlerts[0];
|
||||
const environmentName = 'Production';
|
||||
|
@ -29,7 +26,13 @@ describe('AlertDetails', () => {
|
|||
const projectId = '1';
|
||||
const $router = { replace: jest.fn() };
|
||||
|
||||
function mountComponent({ data, loading = false, mountMethod = shallowMount, stubs = {} } = {}) {
|
||||
function mountComponent({
|
||||
data,
|
||||
loading = false,
|
||||
mountMethod = shallowMount,
|
||||
provide = {},
|
||||
stubs = {},
|
||||
} = {}) {
|
||||
wrapper = extendedWrapper(
|
||||
mountMethod(AlertDetails, {
|
||||
provide: {
|
||||
|
@ -37,6 +40,7 @@ describe('AlertDetails', () => {
|
|||
projectPath,
|
||||
projectIssuesPath,
|
||||
projectId,
|
||||
...provide,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -112,9 +116,7 @@ describe('AlertDetails', () => {
|
|||
});
|
||||
|
||||
it('renders severity', () => {
|
||||
expect(wrapper.findByTestId('severity').text()).toBe(
|
||||
ALERTS_SEVERITY_LABELS[mockAlert.severity],
|
||||
);
|
||||
expect(wrapper.findByTestId('severity').text()).toBe(SEVERITY_LEVELS[mockAlert.severity]);
|
||||
});
|
||||
|
||||
it('renders a title', () => {
|
||||
|
@ -321,16 +323,27 @@ describe('AlertDetails', () => {
|
|||
});
|
||||
|
||||
describe('Snowplow tracking', () => {
|
||||
const mountOptions = {
|
||||
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
|
||||
data: { alert: mockAlert },
|
||||
loading: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Tracking, 'event');
|
||||
mountComponent({
|
||||
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
|
||||
data: { alert: mockAlert },
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should track alert details page views', () => {
|
||||
it('should not track alert details page views when the tracking options do not exist', () => {
|
||||
mountComponent(mountOptions);
|
||||
expect(Tracking.event).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should track alert details page views when the tracking options exist', () => {
|
||||
const trackAlertsDetailsViewsOptions = {
|
||||
category: 'Alert Management',
|
||||
action: 'view_alert_details',
|
||||
};
|
||||
mountComponent({ ...mountOptions, provide: { trackAlertsDetailsViewsOptions } });
|
||||
const { category, action } = trackAlertsDetailsViewsOptions;
|
||||
expect(Tracking.event).toHaveBeenCalledWith(category, action);
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import SidebarTodo from '~/alert_management/components/sidebar/sidebar_todo.vue';
|
||||
import createAlertTodoMutation from '~/alert_management/graphql/mutations/alert_todo_create.mutation.graphql';
|
||||
import SidebarTodo from '~/vue_shared/alert_details/components/sidebar/sidebar_todo.vue';
|
||||
import createAlertTodoMutation from '~/vue_shared/alert_details/graphql/mutations/alert_todo_create.mutation.graphql';
|
||||
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
|
||||
import mockAlerts from '../mocks/alerts.json';
|
||||
import mockAlerts from './mocks/alerts.json';
|
||||
|
||||
const mockAlert = mockAlerts[0];
|
||||
|
|
@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import AlertMetrics from '~/alert_management/components/alert_metrics.vue';
|
||||
import AlertMetrics from '~/vue_shared/alert_details/components/alert_metrics.vue';
|
||||
import MetricEmbed from '~/monitoring/components/embeds/metric_embed.vue';
|
||||
|
||||
jest.mock('~/monitoring/stores', () => ({
|
|
@ -1,11 +1,10 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { trackAlertStatusUpdateOptions } from '~/alert_management/constants';
|
||||
import AlertManagementStatus from '~/alert_management/components/alert_status.vue';
|
||||
import updateAlertStatusMutation from '~/graphql_shared/mutations/update_alert_status.mutation.graphql';
|
||||
import AlertManagementStatus from '~/vue_shared/alert_details/components/alert_status.vue';
|
||||
import updateAlertStatusMutation from '~/graphql_shared//mutations/alert_status_update.mutation.graphql';
|
||||
import Tracking from '~/tracking';
|
||||
import mockAlerts from '../mocks/alerts.json';
|
||||
import mockAlerts from './mocks/alerts.json';
|
||||
|
||||
const mockAlert = mockAlerts[0];
|
||||
|
||||
|
@ -20,7 +19,7 @@ describe('AlertManagementStatus', () => {
|
|||
return waitForPromises();
|
||||
};
|
||||
|
||||
function mountComponent({ props = {}, loading = false, stubs = {} } = {}) {
|
||||
function mountComponent({ props = {}, provide = {}, loading = false, stubs = {} } = {}) {
|
||||
wrapper = shallowMount(AlertManagementStatus, {
|
||||
propsData: {
|
||||
alert: { ...mockAlert },
|
||||
|
@ -28,6 +27,7 @@ describe('AlertManagementStatus', () => {
|
|||
isSidebar: false,
|
||||
...props,
|
||||
},
|
||||
provide,
|
||||
mocks: {
|
||||
$apollo: {
|
||||
mutate: jest.fn(),
|
||||
|
@ -134,10 +134,25 @@ describe('AlertManagementStatus', () => {
|
|||
describe('Snowplow tracking', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Tracking, 'event');
|
||||
mountComponent({});
|
||||
});
|
||||
|
||||
it('should track alert status updates', () => {
|
||||
it('should not track alert status updates when the tracking options do not exist', () => {
|
||||
mountComponent({});
|
||||
Tracking.event.mockClear();
|
||||
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
|
||||
findFirstStatusOption().vm.$emit('click');
|
||||
setImmediate(() => {
|
||||
expect(Tracking.event).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should track alert status updates when the tracking options exist', () => {
|
||||
const trackAlertStatusUpdateOptions = {
|
||||
category: 'Alert Management',
|
||||
action: 'update_alert_status',
|
||||
label: 'Status',
|
||||
};
|
||||
mountComponent({ provide: { trackAlertStatusUpdateOptions } });
|
||||
Tracking.event.mockClear();
|
||||
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
|
||||
findFirstStatusOption().vm.$emit('click');
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue