Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
811f549164
commit
6b19945915
|
@ -14,6 +14,7 @@
|
|||
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_ADMIN_USERNAME: "root"
|
||||
GITLAB_ADMIN_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_QA_ADMIN_ACCESS_TOKEN: "${REVIEW_APPS_ROOT_TOKEN}"
|
||||
GITHUB_ACCESS_TOKEN: "${REVIEW_APPS_QA_GITHUB_ACCESS_TOKEN}"
|
||||
EE_LICENSE: "${REVIEW_APPS_EE_LICENSE}"
|
||||
SIGNUP_DISABLED: "true"
|
||||
|
|
|
@ -54,6 +54,7 @@ export default class CreateMergeRequestDropdown {
|
|||
this.isCreatingBranch = false;
|
||||
this.isCreatingMergeRequest = false;
|
||||
this.isGettingRef = false;
|
||||
this.refCancelToken = null;
|
||||
this.mergeRequestCreated = false;
|
||||
this.refDebounce = debounce((value, target) => this.getRef(value, target), 500);
|
||||
this.refIsValid = true;
|
||||
|
@ -101,9 +102,18 @@ export default class CreateMergeRequestDropdown {
|
|||
'click',
|
||||
this.onClickCreateMergeRequestButton.bind(this),
|
||||
);
|
||||
this.branchInput.addEventListener('input', this.onChangeInput.bind(this));
|
||||
this.branchInput.addEventListener('keyup', this.onChangeInput.bind(this));
|
||||
this.dropdownToggle.addEventListener('click', this.onClickSetFocusOnBranchNameInput.bind(this));
|
||||
// Detect for example when user pastes ref using the mouse
|
||||
this.refInput.addEventListener('input', this.onChangeInput.bind(this));
|
||||
// Detect for example when user presses right arrow to apply the suggested ref
|
||||
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
|
||||
// Detect when user clicks inside the input to apply the suggested ref
|
||||
this.refInput.addEventListener('click', this.onChangeInput.bind(this));
|
||||
// Detect when user clicks outside the input to apply the suggested ref
|
||||
this.refInput.addEventListener('blur', this.onChangeInput.bind(this));
|
||||
// Detect when user presses tab to apply the suggested ref
|
||||
this.refInput.addEventListener('keydown', CreateMergeRequestDropdown.processTab.bind(this));
|
||||
}
|
||||
|
||||
|
@ -247,8 +257,12 @@ export default class CreateMergeRequestDropdown {
|
|||
getRef(ref, target = 'all') {
|
||||
if (!ref) return false;
|
||||
|
||||
this.refCancelToken = axios.CancelToken.source();
|
||||
|
||||
return axios
|
||||
.get(`${createEndpoint(this.projectPath, this.refsPath)}${encodeURIComponent(ref)}`)
|
||||
.get(`${createEndpoint(this.projectPath, this.refsPath)}${encodeURIComponent(ref)}`, {
|
||||
cancelToken: this.refCancelToken.token,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const branches = data[Object.keys(data)[0]];
|
||||
const tags = data[Object.keys(data)[1]];
|
||||
|
@ -267,7 +281,10 @@ export default class CreateMergeRequestDropdown {
|
|||
|
||||
return this.updateInputState(target, ref, result);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((thrown) => {
|
||||
if (axios.isCancel(thrown)) {
|
||||
return false;
|
||||
}
|
||||
this.unavailable();
|
||||
this.disable();
|
||||
createFlash({
|
||||
|
@ -325,14 +342,23 @@ export default class CreateMergeRequestDropdown {
|
|||
let target;
|
||||
let value;
|
||||
|
||||
// User changed input, cancel to prevent previous request from interfering
|
||||
if (this.refCancelToken !== null) {
|
||||
this.refCancelToken.cancel();
|
||||
}
|
||||
|
||||
if (event.target === this.branchInput) {
|
||||
target = 'branch';
|
||||
({ value } = this.branchInput);
|
||||
} else if (event.target === this.refInput) {
|
||||
target = 'ref';
|
||||
if (event.target === document.activeElement) {
|
||||
value =
|
||||
event.target.value.slice(0, event.target.selectionStart) +
|
||||
event.target.value.slice(event.target.selectionEnd);
|
||||
} else {
|
||||
value = event.target.value;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -358,6 +384,7 @@ export default class CreateMergeRequestDropdown {
|
|||
|
||||
this.enable();
|
||||
this.showAvailableMessage(target);
|
||||
this.refDebounce(value, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -414,7 +441,8 @@ export default class CreateMergeRequestDropdown {
|
|||
if (!selectedText || this.refInput.dataset.value === this.suggestedRef) return;
|
||||
|
||||
event.preventDefault();
|
||||
window.getSelection().removeAllRanges();
|
||||
const caretPositionEnd = this.refInput.value.length;
|
||||
this.refInput.setSelectionRange(caretPositionEnd, caretPositionEnd);
|
||||
}
|
||||
|
||||
removeMessage(target) {
|
||||
|
|
|
@ -392,8 +392,6 @@ export default {
|
|||
diffsApp.instrument();
|
||||
},
|
||||
created() {
|
||||
this.mergeRequestContainers = document.querySelectorAll('.merge-request-container');
|
||||
|
||||
this.adjustView();
|
||||
this.subscribeToEvents();
|
||||
|
||||
|
@ -521,13 +519,6 @@ export default {
|
|||
} else {
|
||||
this.removeEventListeners();
|
||||
}
|
||||
|
||||
if (!this.isFluidLayout && this.glFeatures.mrChangesFluidLayout) {
|
||||
this.mergeRequestContainers.forEach((el) => {
|
||||
el.classList.toggle('limit-container-width', !this.shouldShow);
|
||||
el.classList.toggle('container-limited', !this.shouldShow);
|
||||
});
|
||||
}
|
||||
},
|
||||
setEventListeners() {
|
||||
Mousetrap.bind(keysFor(MR_PREVIOUS_FILE_IN_DIFF), () => this.jumpToFile(-1));
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
import $ from 'jquery';
|
||||
import eventHub from '~/projects/new/event_hub';
|
||||
|
||||
function setVisibilityOptions(namespaceSelector) {
|
||||
if (!namespaceSelector || !('selectedIndex' in namespaceSelector)) {
|
||||
// Values are from lib/gitlab/visibility_level.rb
|
||||
const visibilityLevel = {
|
||||
private: 0,
|
||||
internal: 10,
|
||||
public: 20,
|
||||
};
|
||||
|
||||
function setVisibilityOptions({ name, visibility, showPath, editPath }) {
|
||||
document.querySelectorAll('.visibility-level-setting .form-check').forEach((option) => {
|
||||
// Don't change anything if the option is restricted by admin
|
||||
if (option.classList.contains('restricted')) {
|
||||
return;
|
||||
}
|
||||
const selectedNamespace = namespaceSelector.options[namespaceSelector.selectedIndex];
|
||||
const { name, visibility, visibilityLevel, showPath, editPath } = selectedNamespace.dataset;
|
||||
|
||||
document.querySelectorAll('.visibility-level-setting .form-check').forEach((option) => {
|
||||
const optionInput = option.querySelector('input[type=radio]');
|
||||
const optionValue = optionInput ? optionInput.value : 0;
|
||||
const optionTitle = option.querySelector('.option-title');
|
||||
const optionName = optionTitle ? optionTitle.innerText.toLowerCase() : '';
|
||||
const optionValue = optionInput ? parseInt(optionInput.value, 10) : 0;
|
||||
|
||||
// don't change anything if the option is restricted by admin
|
||||
if (!option.classList.contains('restricted')) {
|
||||
if (visibilityLevel < optionValue) {
|
||||
if (visibilityLevel[visibility] < optionValue) {
|
||||
option.classList.add('disabled');
|
||||
optionInput.disabled = true;
|
||||
const reason = option.querySelector('.option-disabled-reason');
|
||||
if (reason) {
|
||||
const optionTitle = option.querySelector('.option-title');
|
||||
const optionName = optionTitle ? optionTitle.innerText.toLowerCase() : '';
|
||||
reason.innerHTML = `This project cannot be ${optionName} because the visibility of
|
||||
<a href="${showPath}">${name}</a> is ${visibility}. To make this project
|
||||
${optionName}, you must first <a href="${editPath}">change the visibility</a>
|
||||
|
@ -29,14 +34,25 @@ function setVisibilityOptions(namespaceSelector) {
|
|||
option.classList.remove('disabled');
|
||||
optionInput.disabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelect2DropdownChange(namespaceSelector) {
|
||||
if (!namespaceSelector || !('selectedIndex' in namespaceSelector)) {
|
||||
return;
|
||||
}
|
||||
const selectedNamespace = namespaceSelector.options[namespaceSelector.selectedIndex];
|
||||
setVisibilityOptions(selectedNamespace.dataset);
|
||||
}
|
||||
|
||||
export default function initProjectVisibilitySelector() {
|
||||
eventHub.$on('update-visibility', setVisibilityOptions);
|
||||
|
||||
const namespaceSelector = document.querySelector('select.js-select-namespace');
|
||||
if (namespaceSelector) {
|
||||
$('.select2.js-select-namespace').on('change', () => setVisibilityOptions(namespaceSelector));
|
||||
setVisibilityOptions(namespaceSelector);
|
||||
$('.select2.js-select-namespace').on('change', () =>
|
||||
handleSelect2DropdownChange(namespaceSelector),
|
||||
);
|
||||
handleSelect2DropdownChange(namespaceSelector);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import {
|
|||
GlDropdownItem,
|
||||
GlDropdownText,
|
||||
GlDropdownSectionHeader,
|
||||
GlLoadingIcon,
|
||||
GlSearchBoxByType,
|
||||
} from '@gitlab/ui';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import { MINIMUM_SEARCH_LENGTH } from '~/graphql_shared/constants';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import Tracking from '~/tracking';
|
||||
|
@ -24,7 +24,6 @@ export default {
|
|||
GlDropdownItem,
|
||||
GlDropdownText,
|
||||
GlDropdownSectionHeader,
|
||||
GlLoadingIcon,
|
||||
GlSearchBoxByType,
|
||||
},
|
||||
mixins: [Tracking.mixin()],
|
||||
|
@ -103,6 +102,15 @@ export default {
|
|||
focusInput() {
|
||||
this.$refs.search.focusInput();
|
||||
},
|
||||
handleDropdownItemClick(namespace) {
|
||||
eventHub.$emit('update-visibility', {
|
||||
name: namespace.name,
|
||||
visibility: namespace.visibility,
|
||||
showPath: namespace.webUrl,
|
||||
editPath: joinPaths(namespace.webUrl, '-', 'edit'),
|
||||
});
|
||||
this.setNamespace(namespace);
|
||||
},
|
||||
handleSelectTemplate(groupId) {
|
||||
this.groupToFilterBy = this.userGroups.find(
|
||||
(group) => getIdFromGraphQLId(group.id) === groupId,
|
||||
|
@ -134,23 +142,23 @@ export default {
|
|||
<gl-search-box-by-type
|
||||
ref="search"
|
||||
v-model.trim="search"
|
||||
:is-loading="$apollo.queries.currentUser.loading"
|
||||
data-qa-selector="select_namespace_dropdown_search_field"
|
||||
/>
|
||||
<gl-loading-icon v-if="$apollo.queries.currentUser.loading" />
|
||||
<template v-else>
|
||||
<template v-if="!$apollo.queries.currentUser.loading">
|
||||
<template v-if="hasGroupMatches">
|
||||
<gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-for="group of filteredGroups"
|
||||
:key="group.id"
|
||||
@click="setNamespace(group)"
|
||||
@click="handleDropdownItemClick(group)"
|
||||
>
|
||||
{{ group.fullPath }}
|
||||
</gl-dropdown-item>
|
||||
</template>
|
||||
<template v-if="hasNamespaceMatches">
|
||||
<gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item @click="setNamespace(userNamespace)">
|
||||
<gl-dropdown-item @click="handleDropdownItemClick(userNamespace)">
|
||||
{{ userNamespace.fullPath }}
|
||||
</gl-dropdown-item>
|
||||
</template>
|
||||
|
@ -158,6 +166,11 @@ export default {
|
|||
</template>
|
||||
</gl-dropdown>
|
||||
|
||||
<input type="hidden" name="project[namespace_id]" :value="selectedNamespace.id" />
|
||||
<input
|
||||
id="project_namespace_id"
|
||||
type="hidden"
|
||||
name="project[namespace_id]"
|
||||
:value="selectedNamespace.id"
|
||||
/>
|
||||
</gl-button-group>
|
||||
</template>
|
||||
|
|
|
@ -4,6 +4,9 @@ query searchNamespacesWhereUserCanCreateProjects($search: String) {
|
|||
nodes {
|
||||
id
|
||||
fullPath
|
||||
name
|
||||
visibility
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
namespace {
|
||||
|
|
|
@ -69,8 +69,7 @@ export default {
|
|||
if (isScopedLabel(candidateLabel)) {
|
||||
const scopedKeyWithDelimiter = `${scopedLabelKey(candidateLabel)}${SCOPED_LABEL_DELIMITER}`;
|
||||
const currentActiveScopedLabel = state.labels.find(
|
||||
({ set, title }) =>
|
||||
set && title.startsWith(scopedKeyWithDelimiter) && title !== candidateLabel.title,
|
||||
({ title }) => title.startsWith(scopedKeyWithDelimiter) && title !== candidateLabel.title,
|
||||
);
|
||||
|
||||
if (currentActiveScopedLabel) {
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'WorkItemRoot',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const widgetTypes = {
|
||||
title: 'TITLE',
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
{"__schema":{"types":[{"kind":"INTERFACE","name":"WorkItemWidget","possibleTypes":[{"name":"TitleWidget"}]}]}}
|
|
@ -0,0 +1,54 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import workItemQuery from './work_item.query.graphql';
|
||||
import introspectionQueryResultData from './fragmentTypes.json';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
export function createApolloProvider() {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const defaultClient = createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
assumeImmutableResults: true,
|
||||
},
|
||||
);
|
||||
|
||||
defaultClient.cache.writeQuery({
|
||||
query: workItemQuery,
|
||||
variables: {
|
||||
id: '1',
|
||||
},
|
||||
data: {
|
||||
workItem: {
|
||||
__typename: 'WorkItem',
|
||||
id: '1',
|
||||
type: 'FEATURE',
|
||||
widgets: {
|
||||
__typename: 'WorkItemWidgetConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'TitleWidget',
|
||||
type: 'TITLE',
|
||||
enabled: true,
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
contentText: 'Test',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return new VueApollo({
|
||||
defaultClient,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
enum WorkItemType {
|
||||
FEATURE
|
||||
}
|
||||
|
||||
enum WidgetType {
|
||||
TITLE
|
||||
}
|
||||
|
||||
interface WorkItemWidget {
|
||||
type: WidgetType!
|
||||
}
|
||||
|
||||
# Replicating Relay connection type for client schema
|
||||
type WorkItemWidgetEdge {
|
||||
cursor: String!
|
||||
node: WorkItemWidget
|
||||
}
|
||||
|
||||
type WorkItemWidgetConnection {
|
||||
edges: [WorkItemWidgetEdge]
|
||||
nodes: [WorkItemWidget]
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type TitleWidget implements WorkItemWidget {
|
||||
type: WidgetType!
|
||||
contentText: String!
|
||||
}
|
||||
|
||||
type WorkItem {
|
||||
id: ID!
|
||||
type: WorkItemType!
|
||||
widgets: [WorkItemWidgetConnection]
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
workItem(id: ID!): WorkItem!
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fragment WidgetBase on WorkItemWidget {
|
||||
type
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#import './widget.fragment.graphql'
|
||||
|
||||
query WorkItem($id: ID!) {
|
||||
workItem(id: $id) @client {
|
||||
id
|
||||
type
|
||||
widgets {
|
||||
nodes {
|
||||
...WidgetBase
|
||||
... on TitleWidget {
|
||||
contentText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
import Vue from 'vue';
|
||||
import App from './components/app.vue';
|
||||
import { createRouter } from './router';
|
||||
import { createApolloProvider } from './graphql/provider';
|
||||
|
||||
export const initWorkItemsRoot = () => {
|
||||
const el = document.querySelector('#js-work-items');
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
router: createRouter(el.dataset.fullPath),
|
||||
apolloProvider: createApolloProvider(),
|
||||
render(createElement) {
|
||||
return createElement(App);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script>
|
||||
import workItemQuery from '../graphql/work_item.query.graphql';
|
||||
import { widgetTypes } from '../constants';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
workItem: null,
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
workItem: {
|
||||
query: workItemQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.id,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
titleWidgetData() {
|
||||
return this.workItem?.widgets?.nodes?.find((widget) => widget.type === widgetTypes.title);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<!-- Title widget placeholder -->
|
||||
<div>
|
||||
<h2 v-if="titleWidgetData" class="title" data-testid="title">
|
||||
{{ titleWidgetData.contentText }}
|
||||
</h2>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
|
@ -0,0 +1,14 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import { routes } from './routes';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
export function createRouter(fullPath) {
|
||||
return new VueRouter({
|
||||
routes,
|
||||
mode: 'history',
|
||||
base: joinPaths(fullPath, '-', 'work_items'),
|
||||
});
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const routes = [
|
||||
{
|
||||
path: '/:id',
|
||||
name: 'work_item',
|
||||
component: () => import('../pages/work_item_root.vue'),
|
||||
props: true,
|
||||
},
|
||||
];
|
|
@ -163,7 +163,8 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
payload[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id
|
||||
payload[:metadata] = @current_context
|
||||
|
||||
payload[:request_urgency] = urgency&.name
|
||||
payload[:target_duration_s] = urgency&.duration
|
||||
logged_user = auth_user
|
||||
if logged_user.present?
|
||||
payload[:user_id] = logged_user.try(:id)
|
||||
|
|
|
@ -38,6 +38,6 @@ module WorkhorseAuthorization
|
|||
end
|
||||
|
||||
def file_extension_whitelist
|
||||
ImportExportUploader::EXTENSION_WHITELIST
|
||||
ImportExportUploader::EXTENSION_ALLOWLIST
|
||||
end
|
||||
end
|
||||
|
|
|
@ -780,6 +780,10 @@ module Ci
|
|||
strong_memoize(:legacy_trigger) { trigger_requests.first }
|
||||
end
|
||||
|
||||
def variables_builder
|
||||
@variables_builder ||= ::Gitlab::Ci::Variables::Builder.new(self)
|
||||
end
|
||||
|
||||
def persisted_variables
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
break variables unless persisted?
|
||||
|
@ -1254,6 +1258,12 @@ module Ci
|
|||
self.builds.latest.build_matchers(project)
|
||||
end
|
||||
|
||||
def predefined_vars_in_builder_enabled?
|
||||
strong_memoize(:predefined_vars_in_builder_enabled) do
|
||||
Feature.enabled?(:ci_predefined_vars_in_builder, project, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_message(severity, content)
|
||||
|
|
|
@ -10,8 +10,10 @@ module Ci
|
|||
# Variables in the environment name scope.
|
||||
#
|
||||
def scoped_variables(environment: expanded_environment_name, dependencies: true)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.concat(predefined_variables)
|
||||
track_duration do
|
||||
variables = pipeline.variables_builder.scoped_variables(self, environment: environment, dependencies: dependencies)
|
||||
|
||||
variables.concat(predefined_variables) unless pipeline.predefined_vars_in_builder_enabled?
|
||||
variables.concat(project.predefined_variables)
|
||||
variables.concat(pipeline.predefined_variables)
|
||||
variables.concat(runner.predefined_variables) if runnable? && runner
|
||||
|
@ -25,9 +27,23 @@ module Ci
|
|||
variables.concat(trigger_request.user_variables) if trigger_request
|
||||
variables.concat(pipeline.variables)
|
||||
variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
|
||||
|
||||
variables
|
||||
end
|
||||
end
|
||||
|
||||
def track_duration
|
||||
start_time = ::Gitlab::Metrics::System.monotonic_time
|
||||
result = yield
|
||||
duration = ::Gitlab::Metrics::System.monotonic_time - start_time
|
||||
|
||||
::Gitlab::Ci::Pipeline::Metrics
|
||||
.pipeline_builder_scoped_variables_histogram
|
||||
.observe({}, duration.seconds)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
##
|
||||
# Variables that do not depend on the environment name.
|
||||
#
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
module BulkImports
|
||||
class ExportUploader < ImportExportUploader
|
||||
EXTENSION_WHITELIST = %w[ndjson.gz].freeze
|
||||
EXTENSION_ALLOWLIST = %w[ndjson.gz].freeze
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ImportExportUploader < AttachmentUploader
|
||||
EXTENSION_WHITELIST = %w[tar.gz gz].freeze
|
||||
EXTENSION_ALLOWLIST = %w[tar.gz gz].freeze
|
||||
|
||||
def self.workhorse_local_upload_path
|
||||
File.join(options.storage_path, 'uploads', TMP_UPLOAD_PATH)
|
||||
end
|
||||
|
||||
def extension_whitelist
|
||||
EXTENSION_WHITELIST
|
||||
EXTENSION_ALLOWLIST
|
||||
end
|
||||
|
||||
def move_to_cache
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
- page_title s_('WorkItem|Work Items')
|
||||
|
||||
#js-work-items
|
||||
#js-work-items{ data: { full_path: @project.full_path } }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
= visibility_level_label(level)
|
||||
.option-description
|
||||
= visibility_level_description(level, form_model)
|
||||
.option-disabled-reason
|
||||
|
||||
.text-muted
|
||||
- if all_visibility_levels_restricted?
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_predefined_vars_in_builder
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72348
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/231300
|
||||
milestone: '14.4'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -19,7 +19,10 @@ Marginalia::Comment.components = [:application, :correlation_id, :jid, :endpoint
|
|||
# adding :line has some overhead because a regexp on the backtrace has
|
||||
# to be run on every SQL query. Only enable this in development because
|
||||
# we've seen it slow things down.
|
||||
Marginalia::Comment.components << :line if Rails.env.development?
|
||||
if Rails.env.development?
|
||||
Marginalia::Comment.components << :line
|
||||
Marginalia::Comment.lines_to_ignore = Regexp.union(Gitlab::BacktraceCleaner::IGNORE_BACKTRACES + %w(lib/ruby/gems/ lib/gem_extensions/ lib/ruby/))
|
||||
end
|
||||
|
||||
Gitlab::Marginalia.set_application_name
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
get 'details', on: :member
|
||||
end
|
||||
|
||||
resources :work_items, only: [:index]
|
||||
get 'work_items/*work_items_path' => 'work_items#index', as: :work_items
|
||||
|
||||
resource :tracing, only: [:show]
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ The following metrics are available:
|
|||
| `gitlab_cache_misses_total` | Counter | 10.2 | Cache read miss | `controller`, `action` |
|
||||
| `gitlab_cache_operation_duration_seconds` | Histogram | 10.2 | Cache access time | |
|
||||
| `gitlab_cache_operations_total` | Counter | 12.2 | Cache operations by controller or action | `controller`, `action`, `operation` |
|
||||
| `gitlab_ci_pipeline_builder_scoped_variables_duration` | Histogram | 14.5 | Time in seconds it takes to create the scoped variables for a CI/CD job
|
||||
| `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | |
|
||||
| `gitlab_ci_pipeline_size_builds` | Histogram | 13.1 | Total number of builds within a pipeline grouped by a pipeline source | `source` |
|
||||
| `job_waiter_started_total` | Counter | 12.9 | Number of batches of jobs started where a web request is waiting for the jobs to complete | `worker` |
|
||||
|
|
|
@ -4042,6 +4042,7 @@ Input type: `ScanExecutionPolicyCommitInput`
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationscanexecutionpolicycommitclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationscanexecutionpolicycommitname"></a>`name` | [`String`](#string) | Name of the policy. If the name is null, the `name` field from `policy_yaml` is used. |
|
||||
| <a id="mutationscanexecutionpolicycommitoperationmode"></a>`operationMode` | [`MutationOperationMode!`](#mutationoperationmode) | Changes the operation mode. |
|
||||
| <a id="mutationscanexecutionpolicycommitpolicyyaml"></a>`policyYaml` | [`String!`](#string) | YAML snippet of the policy. |
|
||||
| <a id="mutationscanexecutionpolicycommitprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project. |
|
||||
|
|
|
@ -94,9 +94,9 @@ Parameters:
|
|||
| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Asana integration
|
||||
### Disable Asana integration
|
||||
|
||||
Delete Asana integration for a project.
|
||||
Disable the Asana integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/asana
|
||||
|
@ -130,9 +130,9 @@ Parameters:
|
|||
| `subdomain` | string | false | The subdomain setting |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Assembla integration
|
||||
### Disable Assembla integration
|
||||
|
||||
Delete Assembla integration for a project.
|
||||
Disable the Assembla integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/assembla
|
||||
|
@ -170,9 +170,9 @@ Parameters:
|
|||
| `password` | string | true | Password of the user |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Atlassian Bamboo CI integration
|
||||
### Disable Atlassian Bamboo CI integration
|
||||
|
||||
Delete Atlassian Bamboo CI integration for a project.
|
||||
Disable the Atlassian Bamboo CI integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/bamboo
|
||||
|
@ -209,9 +209,9 @@ Parameters:
|
|||
| `title` | string | false | Title |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Bugzilla integration
|
||||
### Disable Bugzilla integration
|
||||
|
||||
Delete Bugzilla integration for a project.
|
||||
Disable the Bugzilla integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/bugzilla
|
||||
|
@ -246,9 +246,9 @@ Parameters:
|
|||
| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Buildkite integration
|
||||
### Disable Buildkite integration
|
||||
|
||||
Delete Buildkite integration for a project.
|
||||
Disable the Buildkite integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/buildkite
|
||||
|
@ -284,9 +284,9 @@ Parameters:
|
|||
| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
|
||||
| `push_events` | boolean | false | Enable notifications for push events. |
|
||||
|
||||
### Delete Campfire integration
|
||||
### Disable Campfire integration
|
||||
|
||||
Delete Campfire integration for a project.
|
||||
Disable the Campfire integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/campfire
|
||||
|
@ -322,9 +322,9 @@ Parameters:
|
|||
| `datadog_service` | string | false | Name of this GitLab instance that all data will be tagged with |
|
||||
| `datadog_env` | string | false | The environment tag that traces will be tagged with |
|
||||
|
||||
### Delete Datadog integration
|
||||
### Disable Datadog integration
|
||||
|
||||
Delete Datadog integration for a project.
|
||||
Disable the Datadog integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/datadog
|
||||
|
@ -367,9 +367,9 @@ Parameters:
|
|||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
|
||||
|
||||
### Delete Unify Circuit integration
|
||||
### Disable Unify Circuit integration
|
||||
|
||||
Delete Unify Circuit integration for a project.
|
||||
Disable the Unify Circuit integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/unify-circuit
|
||||
|
@ -412,9 +412,9 @@ Parameters:
|
|||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
|
||||
|
||||
### Delete Webex Teams integration
|
||||
### Disable Webex Teams integration
|
||||
|
||||
Delete Webex Teams integration for a project.
|
||||
Disable the Webex Teams integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/webex-teams
|
||||
|
@ -451,9 +451,9 @@ Parameters:
|
|||
| `title` | string | false | Title |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Custom Issue Tracker integration
|
||||
### Disable Custom Issue Tracker integration
|
||||
|
||||
Delete Custom Issue Tracker integration for a project.
|
||||
Disable the Custom Issue Tracker integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/custom-issue-tracker
|
||||
|
@ -485,9 +485,9 @@ Parameters:
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhook` | string | true | Discord webhook. For example, `https://discord.com/api/webhooks/…` |
|
||||
|
||||
### Delete Discord integration
|
||||
### Disable Discord integration
|
||||
|
||||
Delete Discord integration for a project.
|
||||
Disable the Discord integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/discord
|
||||
|
@ -524,9 +524,9 @@ Parameters:
|
|||
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
|
||||
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
|
||||
|
||||
### Delete Drone CI integration
|
||||
### Disable Drone CI integration
|
||||
|
||||
Delete Drone CI integration for a project.
|
||||
Disable the Drone CI integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/drone-ci
|
||||
|
@ -563,9 +563,9 @@ Parameters:
|
|||
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
|
||||
| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
|
||||
|
||||
### Delete Emails on Push integration
|
||||
### Disable Emails on Push integration
|
||||
|
||||
Delete Emails on Push integration for a project.
|
||||
Disable the Emails on Push integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/emails-on-push
|
||||
|
@ -599,9 +599,9 @@ Parameters:
|
|||
| `project_url` | string | true | The URL to the project in EWM |
|
||||
| `issues_url` | string | true | The URL to view an issue in EWM. Must contain `:id` |
|
||||
|
||||
### Delete EWM integration
|
||||
### Disable EWM integration
|
||||
|
||||
Delete EWM integration for a project.
|
||||
Disable the EWM integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/ewm
|
||||
|
@ -635,9 +635,9 @@ Parameters:
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. |
|
||||
|
||||
### Delete Confluence integration
|
||||
### Disable Confluence integration
|
||||
|
||||
Delete Confluence integration for a project.
|
||||
Disable the Confluence integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/confluence
|
||||
|
@ -669,9 +669,9 @@ Parameters:
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `external_wiki_url` | string | true | The URL of the external wiki |
|
||||
|
||||
### Delete External wiki integration
|
||||
### Disable External wiki integration
|
||||
|
||||
Delete External wiki integration for a project.
|
||||
Disable the External wiki integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/external-wiki
|
||||
|
@ -706,9 +706,9 @@ Parameters:
|
|||
| `token` | string | true | Flowdock Git source token |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Flowdock integration
|
||||
### Disable Flowdock integration
|
||||
|
||||
Delete Flowdock integration for a project.
|
||||
Disable the Flowdock integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/flowdock
|
||||
|
@ -742,9 +742,9 @@ Parameters:
|
|||
| `repository_url` | string | true | GitHub repository URL |
|
||||
| `static_context` | boolean | false | Append instance name instead of branch to [status check name](../user/project/integrations/github.md#static--dynamic-status-check-names) |
|
||||
|
||||
### Delete GitHub integration
|
||||
### Disable GitHub integration
|
||||
|
||||
Delete GitHub integration for a project.
|
||||
Disable the GitHub integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/github
|
||||
|
@ -788,9 +788,9 @@ Parameters:
|
|||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
|
||||
|
||||
### Delete Hangouts Chat integration
|
||||
### Disable Hangouts Chat integration
|
||||
|
||||
Delete Hangouts Chat integration for a project.
|
||||
Disable the Hangouts Chat integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/hangouts-chat
|
||||
|
@ -829,9 +829,9 @@ Parameters:
|
|||
| `colorize_messages` | boolean | false | Colorize messages |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Irker (IRC gateway) integration
|
||||
### Disable Irker (IRC gateway) integration
|
||||
|
||||
Delete Irker (IRC gateway) integration for a project.
|
||||
Disable the Irker (IRC gateway) integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/irker
|
||||
|
@ -880,9 +880,9 @@ Parameters:
|
|||
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
|
||||
| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
|
||||
|
||||
### Delete Jira integration
|
||||
### Disable Jira integration
|
||||
|
||||
Remove all previously Jira integrations from a project.
|
||||
Disable the Jira integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/jira
|
||||
|
@ -939,9 +939,9 @@ Parameters:
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `token` | string | yes | The Slack token |
|
||||
|
||||
### Delete Slack Slash Command integration
|
||||
### Disable Slack Slash Command integration
|
||||
|
||||
Delete Slack Slash Command integration for a project.
|
||||
Disable the Slack Slash Command integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/slack-slash-commands
|
||||
|
@ -974,9 +974,9 @@ Parameters:
|
|||
| `token` | string | yes | The Mattermost token |
|
||||
| `username` | string | no | The username to use to post the message |
|
||||
|
||||
### Delete Mattermost Slash Command integration
|
||||
### Disable Mattermost Slash Command integration
|
||||
|
||||
Delete Mattermost Slash Command integration for a project.
|
||||
Disable the Mattermost Slash Command integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/mattermost-slash-commands
|
||||
|
@ -1005,9 +1005,9 @@ Parameters:
|
|||
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
|
||||
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
|
||||
|
||||
### Delete Packagist integration
|
||||
### Disable Packagist integration
|
||||
|
||||
Delete Packagist integration for a project.
|
||||
Disable the Packagist integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/packagist
|
||||
|
@ -1044,9 +1044,9 @@ Parameters:
|
|||
| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
|
||||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
|
||||
### Delete Pipeline-Emails integration
|
||||
### Disable Pipeline-Emails integration
|
||||
|
||||
Delete Pipeline-Emails integration for a project.
|
||||
Disable the Pipeline-Emails integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/pipelines-email
|
||||
|
@ -1082,9 +1082,9 @@ Parameters:
|
|||
| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Pivotal Tracker integration
|
||||
### Disable Pivotal Tracker integration
|
||||
|
||||
Delete Pivotal Tracker integration for a project.
|
||||
Disable the Pivotal Tracker integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/pivotaltracker
|
||||
|
@ -1118,9 +1118,9 @@ Parameters:
|
|||
| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
|
||||
| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
|
||||
|
||||
### Delete Prometheus integration
|
||||
### Disable Prometheus integration
|
||||
|
||||
Delete Prometheus integration for a project.
|
||||
Disable the Prometheus integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/prometheus
|
||||
|
@ -1157,9 +1157,9 @@ Parameters:
|
|||
| `sound` | string | false | The sound of the notification |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Pushover integration
|
||||
### Disable Pushover integration
|
||||
|
||||
Delete Pushover integration for a project.
|
||||
Disable the Pushover integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/pushover
|
||||
|
@ -1195,9 +1195,9 @@ Parameters:
|
|||
| `description` | string | false | Description |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete Redmine integration
|
||||
### Disable Redmine integration
|
||||
|
||||
Delete Redmine integration for a project.
|
||||
Disable the Redmine integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/redmine
|
||||
|
@ -1256,9 +1256,9 @@ Parameters:
|
|||
| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
|
||||
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
|
||||
|
||||
### Delete Slack integration
|
||||
### Disable Slack integration
|
||||
|
||||
Delete Slack integration for a project.
|
||||
Disable the Slack integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/slack
|
||||
|
@ -1302,9 +1302,9 @@ Parameters:
|
|||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
|
||||
|
||||
### Delete Microsoft Teams integration
|
||||
### Disable Microsoft Teams integration
|
||||
|
||||
Delete Microsoft Teams integration for a project.
|
||||
Disable the Microsoft Teams integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/microsoft-teams
|
||||
|
@ -1359,9 +1359,9 @@ Parameters:
|
|||
| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
|
||||
| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
|
||||
|
||||
### Delete Mattermost notifications integration
|
||||
### Disable Mattermost notifications integration
|
||||
|
||||
Delete Mattermost notifications integration for a project.
|
||||
Disable the Mattermost notifications integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/mattermost
|
||||
|
@ -1399,9 +1399,9 @@ Parameters:
|
|||
| `password` | string | true | The password of the user |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete JetBrains TeamCity CI integration
|
||||
### Disable JetBrains TeamCity CI integration
|
||||
|
||||
Delete JetBrains TeamCity CI integration for a project.
|
||||
Disable the JetBrains TeamCity CI integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/teamcity
|
||||
|
@ -1439,9 +1439,9 @@ Parameters:
|
|||
| `merge_requests_events` | boolean | false | Enable notifications for merge request events. |
|
||||
| `tag_push_events` | boolean | false | Enable notifications for tag push events. |
|
||||
|
||||
### Delete Jenkins CI integration
|
||||
### Disable Jenkins CI integration
|
||||
|
||||
Delete Jenkins CI integration for a project.
|
||||
Disable the Jenkins CI integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/jenkins
|
||||
|
@ -1476,9 +1476,9 @@ Parameters:
|
|||
- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
|
||||
- `pass_unstable` (optional) - Unstable builds are treated as passing
|
||||
|
||||
### Delete Jenkins CI (Deprecated) integration
|
||||
### Disable Jenkins CI (Deprecated) integration
|
||||
|
||||
Delete Jenkins CI (Deprecated) integration for a project.
|
||||
Disable the Jenkins CI (Deprecated) integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/jenkins-deprecated
|
||||
|
@ -1512,9 +1512,9 @@ Parameters:
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `mock_service_url` | string | true | `http://localhost:4004` |
|
||||
|
||||
### Delete MockCI integration
|
||||
### Disable MockCI integration
|
||||
|
||||
Delete MockCI integration for a project.
|
||||
Disable the MockCI integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/mock-ci
|
||||
|
@ -1549,9 +1549,9 @@ Parameters:
|
|||
| `description` | string | false | Description |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
|
||||
### Delete YouTrack integration
|
||||
### Disable YouTrack integration
|
||||
|
||||
Delete YouTrack integration for a project.
|
||||
Disable the YouTrack integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/youtrack
|
||||
|
|
|
@ -27,7 +27,8 @@ module API
|
|||
Gitlab::GrapeLogging::Loggers::PerfLogger.new,
|
||||
Gitlab::GrapeLogging::Loggers::CorrelationIdLogger.new,
|
||||
Gitlab::GrapeLogging::Loggers::ContextLogger.new,
|
||||
Gitlab::GrapeLogging::Loggers::ContentLogger.new
|
||||
Gitlab::GrapeLogging::Loggers::ContentLogger.new,
|
||||
Gitlab::GrapeLogging::Loggers::UrgencyLogger.new
|
||||
]
|
||||
|
||||
allow_access_with_scope :api
|
||||
|
|
|
@ -5,7 +5,7 @@ module API
|
|||
module FileUploadHelpers
|
||||
def file_is_valid?
|
||||
filename = params[:file]&.original_filename
|
||||
filename && ImportExportUploader::EXTENSION_WHITELIST.include?(File.extname(filename).delete('.'))
|
||||
filename && ImportExportUploader::EXTENSION_ALLOWLIST.include?(File.extname(filename).delete('.'))
|
||||
end
|
||||
|
||||
def validate_file!
|
||||
|
|
|
@ -51,6 +51,15 @@ module Gitlab
|
|||
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
|
||||
end
|
||||
|
||||
def self.pipeline_builder_scoped_variables_histogram
|
||||
name = :gitlab_ci_pipeline_builder_scoped_variables_duration
|
||||
comment = 'Pipeline variables builder scoped_variables duration'
|
||||
labels = {}
|
||||
buckets = [0.01, 0.05, 0.1, 0.3, 0.5, 1, 2, 5, 10, 30, 60, 120]
|
||||
|
||||
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
|
||||
end
|
||||
|
||||
def self.pipeline_processing_events_counter
|
||||
name = :gitlab_ci_pipeline_processing_events_total
|
||||
comment = 'Total amount of pipeline processing events'
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Variables
|
||||
class Builder
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(pipeline)
|
||||
@pipeline = pipeline
|
||||
end
|
||||
|
||||
def scoped_variables(job, environment:, dependencies:)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.concat(predefined_variables(job)) if pipeline.predefined_vars_in_builder_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :pipeline
|
||||
|
||||
def predefined_variables(job)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.append(key: 'CI_JOB_NAME', value: job.name)
|
||||
variables.append(key: 'CI_JOB_STAGE', value: job.stage)
|
||||
variables.append(key: 'CI_JOB_MANUAL', value: 'true') if job.action?
|
||||
variables.append(key: 'CI_PIPELINE_TRIGGERED', value: 'true') if job.trigger_request
|
||||
|
||||
variables.append(key: 'CI_NODE_INDEX', value: job.options[:instance].to_s) if job.options&.include?(:instance)
|
||||
variables.append(key: 'CI_NODE_TOTAL', value: ci_node_total_value(job).to_s)
|
||||
|
||||
# legacy variables
|
||||
variables.append(key: 'CI_BUILD_NAME', value: job.name)
|
||||
variables.append(key: 'CI_BUILD_STAGE', value: job.stage)
|
||||
variables.append(key: 'CI_BUILD_TRIGGERED', value: 'true') if job.trigger_request
|
||||
variables.append(key: 'CI_BUILD_MANUAL', value: 'true') if job.action?
|
||||
end
|
||||
end
|
||||
|
||||
def ci_node_total_value(job)
|
||||
parallel = job.options&.dig(:parallel)
|
||||
parallel = parallel.dig(:total) if parallel.is_a?(Hash)
|
||||
parallel || 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GrapeLogging
|
||||
module Loggers
|
||||
class UrgencyLogger < ::GrapeLogging::Loggers::Base
|
||||
def parameters(request, _)
|
||||
endpoint = request.env['api.endpoint']
|
||||
return {} unless endpoint
|
||||
|
||||
urgency = endpoint.options[:for].try(:urgency_for_app, endpoint)
|
||||
return {} unless urgency
|
||||
|
||||
{ request_urgency: urgency.name, target_duration_s: urgency.duration }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class CacheCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_cache_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::Cache.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class QueuesCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_queues_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::Queues.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class RateLimitingCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_rate_limiting_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::RateLimiting.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module HealthChecks
|
||||
module Redis
|
||||
module RedisAbstractCheck
|
||||
include SimpleAbstractCheck
|
||||
|
||||
def check_up
|
||||
successful?(check)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redis_instance_class_name
|
||||
Gitlab::Redis.const_get(redis_instance_name.camelize, false)
|
||||
end
|
||||
|
||||
def metric_prefix
|
||||
"redis_#{redis_instance_name}_ping"
|
||||
end
|
||||
|
||||
def redis_instance_name
|
||||
name.sub(/_check$/, '')
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
redis_instance_class_name.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,16 +14,22 @@ module Gitlab
|
|||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
result == true
|
||||
end
|
||||
|
||||
def check
|
||||
::Gitlab::HealthChecks::Redis::CacheCheck.check_up &&
|
||||
::Gitlab::HealthChecks::Redis::QueuesCheck.check_up &&
|
||||
::Gitlab::HealthChecks::Redis::SharedStateCheck.check_up &&
|
||||
::Gitlab::HealthChecks::Redis::TraceChunksCheck.check_up &&
|
||||
::Gitlab::HealthChecks::Redis::RateLimitingCheck.check_up &&
|
||||
::Gitlab::HealthChecks::Redis::SessionsCheck.check_up
|
||||
redis_health_checks.all?(&:check_up)
|
||||
end
|
||||
|
||||
def redis_health_checks
|
||||
[
|
||||
Gitlab::HealthChecks::Redis::CacheCheck,
|
||||
Gitlab::HealthChecks::Redis::QueuesCheck,
|
||||
Gitlab::HealthChecks::Redis::SharedStateCheck,
|
||||
Gitlab::HealthChecks::Redis::TraceChunksCheck,
|
||||
Gitlab::HealthChecks::Redis::RateLimitingCheck,
|
||||
Gitlab::HealthChecks::Redis::SessionsCheck
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class SessionsCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_sessions_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::Sessions.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class SharedStateCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_shared_state_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::SharedState.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,31 +4,7 @@ module Gitlab
|
|||
module HealthChecks
|
||||
module Redis
|
||||
class TraceChunksCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
def check_up
|
||||
check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'redis_trace_chunks_ping'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result == 'PONG'
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def check
|
||||
catch_timeout 10.seconds do
|
||||
Gitlab::Redis::TraceChunks.with(&:ping)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
extend RedisAbstractCheck
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,11 +56,21 @@ module Gitlab
|
|||
end
|
||||
|
||||
def download(url, upload_path)
|
||||
File.open(upload_path, 'w') do |file|
|
||||
# Download (stream) file from the uploader's location
|
||||
IO.copy_stream(URI.parse(url).open, file)
|
||||
File.open(upload_path, 'wb') do |file|
|
||||
Gitlab::HTTP.get(url, stream_body: true) do |fragment|
|
||||
if [301, 302, 307].include?(fragment.code)
|
||||
Gitlab::Import::Logger.warn(message: "received redirect fragment", fragment_code: fragment.code)
|
||||
elsif fragment.code == 200
|
||||
file.write(fragment)
|
||||
else
|
||||
raise Gitlab::ImportExport::Error, "unsupported response downloading fragment #{fragment.code}"
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue StandardError => e
|
||||
@shared.error(e) # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
raise e
|
||||
end
|
||||
|
||||
def tar_with_options(archive:, dir:, options:)
|
||||
execute_cmd(%W(tar -#{options} #{archive} -C #{dir} .))
|
||||
|
|
|
@ -7,6 +7,8 @@ module Gitlab
|
|||
|
||||
LIMITED_ARRAY_SENTINEL = { key: 'truncated', value: '...' }.freeze
|
||||
IGNORE_PARAMS = Set.new(%w(controller action format)).freeze
|
||||
KNOWN_PAYLOAD_PARAMS = [:remote_ip, :user_id, :username, :ua, :queue_duration_s,
|
||||
:etag_route, :request_urgency, :target_duration_s] + CLOUDFLARE_CUSTOM_HEADERS.values
|
||||
|
||||
def self.call(event)
|
||||
params = event
|
||||
|
@ -14,24 +16,17 @@ module Gitlab
|
|||
.each_with_object([]) { |(k, v), array| array << { key: k, value: v } unless IGNORE_PARAMS.include?(k) }
|
||||
payload = {
|
||||
time: Time.now.utc.iso8601(3),
|
||||
params: Gitlab::Utils::LogLimitedArray.log_limited_array(params, sentinel: LIMITED_ARRAY_SENTINEL),
|
||||
remote_ip: event.payload[:remote_ip],
|
||||
user_id: event.payload[:user_id],
|
||||
username: event.payload[:username],
|
||||
ua: event.payload[:ua]
|
||||
params: Gitlab::Utils::LogLimitedArray.log_limited_array(params, sentinel: LIMITED_ARRAY_SENTINEL)
|
||||
}
|
||||
|
||||
payload.merge!(event.payload[:metadata]) if event.payload[:metadata]
|
||||
optional_payload_params = event.payload.slice(*KNOWN_PAYLOAD_PARAMS).compact
|
||||
payload.merge!(optional_payload_params)
|
||||
|
||||
::Gitlab::InstrumentationHelper.add_instrumentation_data(payload)
|
||||
|
||||
payload[:queue_duration_s] = event.payload[:queue_duration_s] if event.payload[:queue_duration_s]
|
||||
payload[:etag_route] = event.payload[:etag_route] if event.payload[:etag_route]
|
||||
payload[Labkit::Correlation::CorrelationId::LOG_KEY] = event.payload[Labkit::Correlation::CorrelationId::LOG_KEY] || Labkit::Correlation::CorrelationId.current_id
|
||||
|
||||
CLOUDFLARE_CUSTOM_HEADERS.each do |_, value|
|
||||
payload[value] = event.payload[value] if event.payload[value]
|
||||
end
|
||||
|
||||
# https://github.com/roidrage/lograge#logging-errors--exceptions
|
||||
exception = event.payload[:exception_object]
|
||||
|
||||
|
|
|
@ -30,10 +30,12 @@ module Gitlab
|
|||
endpoint_id = API::Base.endpoint_id_for_route(route)
|
||||
route_class = route.app.options[:for]
|
||||
feature_category = route_class.feature_category_for_app(route.app)
|
||||
request_urgency = route_class.urgency_for_app(route.app)
|
||||
|
||||
{
|
||||
endpoint_id: endpoint_id,
|
||||
feature_category: feature_category
|
||||
feature_category: feature_category,
|
||||
request_urgency: request_urgency.name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -42,7 +44,8 @@ module Gitlab
|
|||
Gitlab::RequestEndpoints.all_controller_actions.map do |controller, action|
|
||||
{
|
||||
endpoint_id: controller.endpoint_id_for_action(action),
|
||||
feature_category: controller.feature_category_for_action(action)
|
||||
feature_category: controller.feature_category_for_action(action),
|
||||
request_urgency: controller.urgency_for_action(action).name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -116,9 +116,11 @@ module Gitlab
|
|||
def record_apdex_if_needed(env, elapsed)
|
||||
return unless Gitlab::Metrics::RailsSlis.request_apdex_counters_enabled?
|
||||
|
||||
urgency = urgency_for_env(env)
|
||||
|
||||
Gitlab::Metrics::RailsSlis.request_apdex.increment(
|
||||
labels: labels_from_context,
|
||||
success: satisfactory?(env, elapsed)
|
||||
labels: labels_from_context.merge(request_urgency: urgency.name),
|
||||
success: elapsed < urgency.duration
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -129,17 +131,15 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def satisfactory?(env, elapsed)
|
||||
target =
|
||||
def urgency_for_env(env)
|
||||
endpoint_urgency =
|
||||
if env['api.endpoint'].present?
|
||||
env['api.endpoint'].options[:for].try(:urgency_for_app, env['api.endpoint'])
|
||||
elsif env['action_controller.instance'].present? && env['action_controller.instance'].respond_to?(:urgency)
|
||||
env['action_controller.instance'].urgency
|
||||
end
|
||||
|
||||
target ||= Gitlab::EndpointAttributes::DEFAULT_URGENCY
|
||||
|
||||
elapsed < target.duration
|
||||
endpoint_urgency || Gitlab::EndpointAttributes::DEFAULT_URGENCY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -81,7 +81,7 @@ module QA
|
|||
result = yield.tap do
|
||||
fabrication_time = Time.now - start
|
||||
|
||||
Support::FabricationTracker.save_fabrication(:"#{method}_fabrication", fabrication_time * 1000)
|
||||
Support::FabricationTracker.save_fabrication(:"#{method}_fabrication", fabrication_time)
|
||||
Runtime::Logger.debug do
|
||||
msg = ["==#{'=' * parents.size}>"]
|
||||
msg << "Built a #{name}"
|
||||
|
|
|
@ -27,7 +27,9 @@ module QA
|
|||
:import_error
|
||||
|
||||
attribute :group do
|
||||
Group.fabricate!
|
||||
Group.fabricate! do |group|
|
||||
group.api_client = api_client
|
||||
end
|
||||
end
|
||||
|
||||
attribute :path_with_namespace do
|
||||
|
|
|
@ -104,7 +104,10 @@ module QA
|
|||
source_issue # fabricate source group, project, issue
|
||||
end
|
||||
|
||||
it 'successfully imports issue' do
|
||||
it(
|
||||
'successfully imports issue',
|
||||
testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/2325'
|
||||
) do
|
||||
expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
|
||||
|
||||
aggregate_failures do
|
||||
|
|
|
@ -501,11 +501,16 @@ RSpec.describe ApplicationController do
|
|||
describe '#append_info_to_payload' do
|
||||
controller(described_class) do
|
||||
attr_reader :last_payload
|
||||
urgency :high, [:foo]
|
||||
|
||||
def index
|
||||
render html: 'authenticated'
|
||||
end
|
||||
|
||||
def foo
|
||||
render html: ''
|
||||
end
|
||||
|
||||
def append_info_to_payload(payload)
|
||||
super
|
||||
|
||||
|
@ -513,6 +518,13 @@ RSpec.describe ApplicationController do
|
|||
end
|
||||
end
|
||||
|
||||
before do
|
||||
routes.draw do
|
||||
get 'index' => 'anonymous#index'
|
||||
get 'foo' => 'anonymous#foo'
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not log errors with a 200 response' do
|
||||
get :index
|
||||
|
||||
|
@ -534,6 +546,22 @@ RSpec.describe ApplicationController do
|
|||
|
||||
expect(controller.last_payload[:metadata]).to include('meta.user' => user.username)
|
||||
end
|
||||
|
||||
context 'urgency information' do
|
||||
it 'adds default urgency information to the payload' do
|
||||
get :index
|
||||
|
||||
expect(controller.last_payload[:request_urgency]).to eq(:default)
|
||||
expect(controller.last_payload[:target_duration_s]).to eq(1)
|
||||
end
|
||||
|
||||
it 'adds customized urgency information to the payload' do
|
||||
get :foo
|
||||
|
||||
expect(controller.last_payload[:request_urgency]).to eq(:high)
|
||||
expect(controller.last_payload[:target_duration_s]).to eq(0.25)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#access_denied' do
|
||||
|
|
|
@ -46,7 +46,10 @@ describe('CreateMergeRequestDropdown', () => {
|
|||
dropdown
|
||||
.getRef('contains#hash')
|
||||
.then(() => {
|
||||
expect(axios.get).toHaveBeenCalledWith(endpoint);
|
||||
expect(axios.get).toHaveBeenCalledWith(
|
||||
endpoint,
|
||||
expect.objectContaining({ cancelToken: expect.anything() }),
|
||||
);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
|
|
@ -702,23 +702,4 @@ describe('diffs/components/app', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fluid layout', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(
|
||||
'<div><div class="merge-request-container limit-container-width container-limited"></div></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('removes limited container classes when on diffs tab', () => {
|
||||
createComponent({ isFluidLayout: false, shouldShow: true }, () => {}, {
|
||||
glFeatures: { mrChangesFluidLayout: true },
|
||||
});
|
||||
|
||||
const containerClassList = document.querySelector('.merge-request-container').classList;
|
||||
|
||||
expect(containerClassList).not.toContain('container-limited');
|
||||
expect(containerClassList).not.toContain('limit-container-width');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,14 +24,23 @@ describe('NewProjectUrlSelect component', () => {
|
|||
{
|
||||
id: 'gid://gitlab/Group/26',
|
||||
fullPath: 'flightjs',
|
||||
name: 'Flight JS',
|
||||
visibility: 'public',
|
||||
webUrl: 'http://127.0.0.1:3000/flightjs',
|
||||
},
|
||||
{
|
||||
id: 'gid://gitlab/Group/28',
|
||||
fullPath: 'h5bp',
|
||||
name: 'H5BP',
|
||||
visibility: 'public',
|
||||
webUrl: 'http://127.0.0.1:3000/h5bp',
|
||||
},
|
||||
{
|
||||
id: 'gid://gitlab/Group/30',
|
||||
fullPath: 'h5bp/subgroup',
|
||||
name: 'H5BP Subgroup',
|
||||
visibility: 'private',
|
||||
webUrl: 'http://127.0.0.1:3000/h5bp/subgroup',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -79,6 +88,10 @@ describe('NewProjectUrlSelect component', () => {
|
|||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findInput = () => wrapper.findComponent(GlSearchBoxByType);
|
||||
const findHiddenInput = () => wrapper.find('input');
|
||||
const clickDropdownItem = async () => {
|
||||
wrapper.findComponent(GlDropdownItem).vm.$emit('click');
|
||||
await wrapper.vm.$nextTick();
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -127,7 +140,6 @@ describe('NewProjectUrlSelect component', () => {
|
|||
|
||||
it('focuses on the input when the dropdown is opened', async () => {
|
||||
wrapper = mountComponent({ mountFn: mount });
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
@ -140,7 +152,6 @@ describe('NewProjectUrlSelect component', () => {
|
|||
|
||||
it('renders expected dropdown items', async () => {
|
||||
wrapper = mountComponent({ mountFn: mount });
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
@ -160,7 +171,6 @@ describe('NewProjectUrlSelect component', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
wrapper = mountComponent({ mountFn: mount });
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
@ -195,23 +205,38 @@ describe('NewProjectUrlSelect component', () => {
|
|||
};
|
||||
|
||||
wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount });
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.find('li').text()).toBe('No matches found');
|
||||
});
|
||||
|
||||
it('updates hidden input with selected namespace', async () => {
|
||||
it('emits `update-visibility` event to update the visibility radio options', async () => {
|
||||
wrapper = mountComponent();
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
wrapper.findComponent(GlDropdownItem).vm.$emit('click');
|
||||
const spy = jest.spyOn(eventHub, '$emit');
|
||||
|
||||
await clickDropdownItem();
|
||||
|
||||
const namespace = data.currentUser.groups.nodes[0];
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('update-visibility', {
|
||||
name: namespace.name,
|
||||
visibility: namespace.visibility,
|
||||
showPath: namespace.webUrl,
|
||||
editPath: `${namespace.webUrl}/-/edit`,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates hidden input with selected namespace', async () => {
|
||||
wrapper = mountComponent();
|
||||
jest.runOnlyPendingTimers();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
await clickDropdownItem();
|
||||
|
||||
expect(findHiddenInput().attributes()).toMatchObject({
|
||||
name: 'project[namespace_id]',
|
||||
value: getIdFromGraphQLId(data.currentUser.groups.nodes[0].id).toString(),
|
||||
|
|
|
@ -159,9 +159,8 @@ describe('LabelsSelect Mutations', () => {
|
|||
labels = [
|
||||
{ id: 1, title: 'scoped' },
|
||||
{ id: 2, title: 'scoped::one', set: false },
|
||||
{ id: 3, title: 'scoped::two', set: false },
|
||||
{ id: 4, title: 'scoped::three', set: true },
|
||||
{ id: 5, title: '' },
|
||||
{ id: 3, title: 'scoped::test', set: true },
|
||||
{ id: 4, title: '' },
|
||||
];
|
||||
});
|
||||
|
||||
|
@ -192,9 +191,8 @@ describe('LabelsSelect Mutations', () => {
|
|||
expect(state.labels).toEqual([
|
||||
{ id: 1, title: 'scoped' },
|
||||
{ id: 2, title: 'scoped::one', set: true, touched: true },
|
||||
{ id: 3, title: 'scoped::two', set: false },
|
||||
{ id: 4, title: 'scoped::three', set: false },
|
||||
{ id: 5, title: '' },
|
||||
{ id: 3, title: 'scoped::test', set: false },
|
||||
{ id: 4, title: '' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import App from '~/work_items/components/app.vue';
|
||||
|
||||
describe('Work Items Application', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(App, {
|
||||
stubs: {
|
||||
'router-view': true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders a component', () => {
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
export const workItemQueryResponse = {
|
||||
workItem: {
|
||||
__typename: 'WorkItem',
|
||||
id: '1',
|
||||
type: 'FEATURE',
|
||||
widgets: {
|
||||
__typename: 'WorkItemWidgetConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'TitleWidget',
|
||||
type: 'TITLE',
|
||||
contentText: 'Test',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
|
||||
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
|
||||
import { workItemQueryResponse } from '../mock_data';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(VueApollo);
|
||||
|
||||
const WORK_ITEM_ID = '1';
|
||||
|
||||
describe('Work items root component', () => {
|
||||
let wrapper;
|
||||
let fakeApollo;
|
||||
|
||||
const findTitle = () => wrapper.find('[data-testid="title"]');
|
||||
|
||||
const createComponent = ({ queryResponse = workItemQueryResponse } = {}) => {
|
||||
fakeApollo = createMockApollo();
|
||||
fakeApollo.clients.defaultClient.cache.writeQuery({
|
||||
query: workItemQuery,
|
||||
variables: {
|
||||
id: WORK_ITEM_ID,
|
||||
},
|
||||
data: queryResponse,
|
||||
});
|
||||
|
||||
wrapper = shallowMount(WorkItemsRoot, {
|
||||
propsData: {
|
||||
id: WORK_ITEM_ID,
|
||||
},
|
||||
localVue,
|
||||
apolloProvider: fakeApollo,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
fakeApollo = null;
|
||||
});
|
||||
|
||||
it('renders the title if title is in the widgets list', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findTitle().exists()).toBe(true);
|
||||
expect(findTitle().text()).toBe('Test');
|
||||
});
|
||||
|
||||
it('does not render the title if title is not in the widgets list', () => {
|
||||
const queryResponse = {
|
||||
workItem: {
|
||||
...workItemQueryResponse.workItem,
|
||||
widgets: {
|
||||
__typename: 'WorkItemWidgetConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'SomeOtherWidget',
|
||||
type: 'OTHER',
|
||||
contentText: 'Test',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
createComponent({ queryResponse });
|
||||
|
||||
expect(findTitle().exists()).toBe(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import App from '~/work_items/components/app.vue';
|
||||
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
|
||||
import { createRouter } from '~/work_items/router';
|
||||
|
||||
describe('Work items router', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = async (routeArg) => {
|
||||
const router = createRouter('/work_item');
|
||||
if (routeArg !== undefined) {
|
||||
await router.push(routeArg);
|
||||
}
|
||||
|
||||
wrapper = mount(App, {
|
||||
router,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
window.location.hash = '';
|
||||
});
|
||||
|
||||
it('renders work item on `/1` route', async () => {
|
||||
await createComponent('/1');
|
||||
|
||||
expect(wrapper.find(WorkItemsRoot).exists()).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Variables::Builder do
|
||||
let(:builder) { described_class.new(pipeline) }
|
||||
let(:pipeline) { create(:ci_pipeline) }
|
||||
let(:job) { create(:ci_build, pipeline: pipeline) }
|
||||
|
||||
describe '#scoped_variables' do
|
||||
let(:environment) { job.expanded_environment_name }
|
||||
let(:dependencies) { true }
|
||||
|
||||
subject { builder.scoped_variables(job, environment: environment, dependencies: dependencies) }
|
||||
|
||||
it 'returns the expected variables' do
|
||||
keys = %w[CI_JOB_NAME
|
||||
CI_JOB_STAGE
|
||||
CI_NODE_TOTAL
|
||||
CI_BUILD_NAME
|
||||
CI_BUILD_STAGE]
|
||||
|
||||
subject.map { |env| env[:key] }.tap do |names|
|
||||
expect(names).to include(*keys)
|
||||
end
|
||||
end
|
||||
|
||||
context 'feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_predefined_vars_in_builder: false)
|
||||
end
|
||||
|
||||
it 'returns no variables' do
|
||||
expect(subject.map { |env| env[:key] }).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GrapeLogging::Loggers::UrgencyLogger do
|
||||
def endpoint(options, namespace: '')
|
||||
Struct.new(:options, :namespace).new(options, namespace)
|
||||
end
|
||||
|
||||
let(:api_class) do
|
||||
Class.new(API::Base) do
|
||||
namespace 'testing' do
|
||||
# rubocop:disable Rails/HttpPositionalArguments
|
||||
# This is not the get that performs a request, but the one from Grape
|
||||
get 'test', urgency: :high do
|
||||
{}
|
||||
end
|
||||
# rubocop:enable Rails/HttpPositionalArguments
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".parameters" do
|
||||
where(:request_env, :expected_parameters) do
|
||||
[
|
||||
[{}, {}],
|
||||
[{ 'api.endpoint' => endpoint({}) }, {}],
|
||||
[{ 'api.endpoint' => endpoint({ for: 'something weird' }) }, {}],
|
||||
[
|
||||
{ 'api.endpoint' => endpoint({ for: api_class, path: [] }) },
|
||||
{ request_urgency: :default, target_duration_s: 1 }
|
||||
],
|
||||
[
|
||||
{ 'api.endpoint' => endpoint({ for: api_class, path: ['test'] }, namespace: '/testing') },
|
||||
{ request_urgency: :high, target_duration_s: 0.25 }
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:request) { double('request', env: request_env) }
|
||||
|
||||
subject { described_class.new.parameters(request, nil) }
|
||||
|
||||
it { is_expected.to eq(expected_parameters) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,5 +4,5 @@ require 'spec_helper'
|
|||
require_relative '../simple_check_shared'
|
||||
|
||||
RSpec.describe Gitlab::HealthChecks::Redis::RedisCheck do
|
||||
include_examples 'simple_check', 'redis_ping', 'Redis', 'PONG'
|
||||
include_examples 'simple_check', 'redis_ping', 'Redis', true
|
||||
end
|
||||
|
|
|
@ -17,6 +17,10 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
|||
def initialize
|
||||
@shared = Gitlab::ImportExport::Shared.new(nil)
|
||||
end
|
||||
|
||||
def download(url, upload_path)
|
||||
super(url, upload_path)
|
||||
end
|
||||
end.new
|
||||
end
|
||||
|
||||
|
@ -101,4 +105,44 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#download' do
|
||||
before do
|
||||
stub_request(:get, loc)
|
||||
.to_return(
|
||||
status: 200,
|
||||
body: content
|
||||
)
|
||||
end
|
||||
|
||||
context 'a non-localhost uri' do
|
||||
let(:loc) { 'https://gitlab.com' }
|
||||
let(:content) { File.open('spec/fixtures/rails_sample.tif') }
|
||||
|
||||
it 'gets the contents' do
|
||||
Tempfile.create("foo") do |f|
|
||||
subject.download(loc, f.path)
|
||||
expect(f.read).to eq(File.open('spec/fixtures/rails_sample.tif').read)
|
||||
end
|
||||
end
|
||||
|
||||
it 'streams the contents' do
|
||||
expect(Gitlab::HTTP).to receive(:get).with(loc, hash_including(stream_body: true))
|
||||
Tempfile.create("foo") do |f|
|
||||
subject.download(loc, f.path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'a localhost uri' do
|
||||
let(:loc) { 'https://localhost:8081/foo/bar' }
|
||||
let(:content) { 'foo' }
|
||||
|
||||
it 'throws a blocked url error' do
|
||||
Tempfile.create("foo") do |f|
|
||||
expect { subject.download(loc, f.path) }.to raise_error(Gitlab::HTTP::BlockedUrlError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,13 @@ RSpec.describe Gitlab::Lograge::CustomOptions do
|
|||
user_id: 'test',
|
||||
cf_ray: SecureRandom.hex,
|
||||
cf_request_id: SecureRandom.hex,
|
||||
metadata: { 'meta.user' => 'jane.doe' }
|
||||
metadata: { 'meta.user' => 'jane.doe' },
|
||||
request_urgency: :default,
|
||||
target_duration_s: 1,
|
||||
remote_ip: '192.168.1.2',
|
||||
ua: 'Nyxt',
|
||||
queue_duration_s: 0.2,
|
||||
etag_route: '/etag'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -66,6 +72,18 @@ RSpec.describe Gitlab::Lograge::CustomOptions do
|
|||
end
|
||||
end
|
||||
|
||||
context 'trusted payload' do
|
||||
it { is_expected.to include(event_payload.slice(*described_class::KNOWN_PAYLOAD_PARAMS)) }
|
||||
|
||||
context 'payload with rejected fields' do
|
||||
let(:event_payload) { { params: {}, request_urgency: :high, something: 'random', username: nil } }
|
||||
|
||||
it { is_expected.to include({ request_urgency: :high }) }
|
||||
it { is_expected.not_to include({ something: 'random' }) }
|
||||
it { is_expected.not_to include({ username: nil }) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when correlation_id is overridden' do
|
||||
let(:correlation_id_key) { Labkit::Correlation::CorrelationId::LOG_KEY }
|
||||
|
||||
|
|
|
@ -17,11 +17,13 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
|
|||
possible_labels = [
|
||||
{
|
||||
endpoint_id: "GET /api/:version/version",
|
||||
feature_category: :not_owned
|
||||
feature_category: :not_owned,
|
||||
request_urgency: :default
|
||||
},
|
||||
{
|
||||
endpoint_id: "ProjectsController#show",
|
||||
feature_category: :projects
|
||||
feature_category: :projects,
|
||||
request_urgency: :default
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
it 'tracks request count and duration' do
|
||||
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown')
|
||||
expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(labels: { feature_category: 'unknown', endpoint_id: 'unknown' }, success: true)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
|
||||
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
@ -122,7 +123,7 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'issue_tracking')
|
||||
expect(described_class).not_to receive(:http_health_requests_total)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex)
|
||||
.to receive(:increment).with(labels: { feature_category: 'issue_tracking', endpoint_id: 'IssuesController#show' }, success: true)
|
||||
.to receive(:increment).with(labels: { feature_category: 'issue_tracking', endpoint_id: 'IssuesController#show', request_urgency: :default }, success: true)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
@ -156,7 +157,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
it 'sets the required labels to unknown' do
|
||||
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown')
|
||||
expect(described_class).not_to receive(:http_health_requests_total)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(labels: { feature_category: 'unknown', endpoint_id: 'unknown' }, success: true)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
|
||||
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
@ -206,7 +208,11 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
|
||||
it "captures SLI metrics" do
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'hello_world', endpoint_id: 'GET /projects/:id/archive' },
|
||||
labels: {
|
||||
feature_category: 'hello_world',
|
||||
endpoint_id: 'GET /projects/:id/archive',
|
||||
request_urgency: request_urgency_name
|
||||
},
|
||||
success: success
|
||||
)
|
||||
subject.call(env)
|
||||
|
@ -235,7 +241,11 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
|
||||
it "captures SLI metrics" do
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'hello_world', endpoint_id: 'AnonymousController#index' },
|
||||
labels: {
|
||||
feature_category: 'hello_world',
|
||||
endpoint_id: 'AnonymousController#index',
|
||||
request_urgency: request_urgency_name
|
||||
},
|
||||
success: success
|
||||
)
|
||||
subject.call(env)
|
||||
|
@ -255,17 +265,25 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
|
||||
let(:api_handler) { Class.new(::API::Base) }
|
||||
|
||||
it "falls back request's expectation to medium (1 second)" do
|
||||
it "falls back request's expectation to default (1 second)" do
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 100.9)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: true
|
||||
)
|
||||
subject.call(env)
|
||||
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: false
|
||||
)
|
||||
subject.call(env)
|
||||
|
@ -281,17 +299,25 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
{ 'action_controller.instance' => controller_instance, 'REQUEST_METHOD' => 'GET' }
|
||||
end
|
||||
|
||||
it "falls back request's expectation to medium (1 second)" do
|
||||
it "falls back request's expectation to default (1 second)" do
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 100.9)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: true
|
||||
)
|
||||
subject.call(env)
|
||||
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: false
|
||||
)
|
||||
subject.call(env)
|
||||
|
@ -303,17 +329,25 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
|
|||
{ 'REQUEST_METHOD' => 'GET' }
|
||||
end
|
||||
|
||||
it "falls back request's expectation to medium (1 second)" do
|
||||
it "falls back request's expectation to default (1 second)" do
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 100.9)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: true
|
||||
)
|
||||
subject.call(env)
|
||||
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
|
||||
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).with(
|
||||
labels: { feature_category: 'unknown', endpoint_id: 'unknown' },
|
||||
labels: {
|
||||
feature_category: 'unknown',
|
||||
endpoint_id: 'unknown',
|
||||
request_urgency: :default
|
||||
},
|
||||
success: false
|
||||
)
|
||||
subject.call(env)
|
||||
|
|
|
@ -2759,7 +2759,10 @@ RSpec.describe Ci::Build do
|
|||
let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } }
|
||||
|
||||
before do
|
||||
allow(build).to receive(:predefined_variables) { [build_pre_var] }
|
||||
allow_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder|
|
||||
allow(builder).to receive(:predefined_variables) { [build_pre_var] }
|
||||
end
|
||||
|
||||
allow(build).to receive(:yaml_variables) { [build_yaml_var] }
|
||||
allow(build).to receive(:persisted_variables) { [] }
|
||||
allow(build).to receive(:job_jwt_variables) { [job_jwt_var] }
|
||||
|
@ -3411,6 +3414,22 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
|
||||
describe '#scoped_variables' do
|
||||
before do
|
||||
pipeline.clear_memoization(:predefined_vars_in_builder_enabled)
|
||||
end
|
||||
|
||||
it 'records a prometheus metric' do
|
||||
histogram = double(:histogram)
|
||||
expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_builder_scoped_variables_histogram)
|
||||
.and_return(histogram)
|
||||
|
||||
expect(histogram).to receive(:observe)
|
||||
.with({}, a_kind_of(ActiveSupport::Duration))
|
||||
|
||||
build.scoped_variables
|
||||
end
|
||||
|
||||
shared_examples 'calculates scoped_variables' do
|
||||
context 'when build has not been persisted yet' do
|
||||
let(:build) do
|
||||
described_class.new(
|
||||
|
@ -3483,6 +3502,37 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'calculates scoped_variables'
|
||||
|
||||
it 'delegates to the variable builders' do
|
||||
expect_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder|
|
||||
expect(builder)
|
||||
.to receive(:scoped_variables).with(build, hash_including(:environment, :dependencies))
|
||||
.and_call_original
|
||||
|
||||
expect(builder).to receive(:predefined_variables).and_call_original
|
||||
end
|
||||
|
||||
build.scoped_variables
|
||||
end
|
||||
|
||||
context 'when ci builder feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_predefined_vars_in_builder: false)
|
||||
end
|
||||
|
||||
it 'does not delegate to the variable builders' do
|
||||
expect_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder|
|
||||
expect(builder).not_to receive(:predefined_variables)
|
||||
end
|
||||
|
||||
build.scoped_variables
|
||||
end
|
||||
|
||||
it_behaves_like 'calculates scoped_variables'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#simple_variables_without_dependencies' do
|
||||
it 'does not load dependencies' do
|
||||
expect(build).not_to receive(:dependency_variables)
|
||||
|
|
Loading…
Reference in New Issue