Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
717436a767
commit
977720d756
|
|
@ -1,7 +1,15 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { joinPaths, webIDEUrl } from '~/lib/utils/url_utility';
|
||||
import WebIdeButton from '~/vue_shared/components/web_ide_link.vue';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
export default ({ el, router }) => {
|
||||
if (!el) return;
|
||||
|
|
@ -15,6 +23,10 @@ export default ({ el, router }) => {
|
|||
new Vue({
|
||||
el,
|
||||
router,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
projectPath,
|
||||
},
|
||||
render(h) {
|
||||
return h(WebIdeButton, {
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
widgets() {
|
||||
return [window.gon?.features?.refactorSecurityExtension && 'MrSecurityWidget'].filter(
|
||||
(w) => w,
|
||||
);
|
||||
return ['MrSecurityWidget'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ export default {
|
|||
MrWidgetRebase: RebaseState,
|
||||
SourceBranchRemovalStatus,
|
||||
MrWidgetApprovals,
|
||||
SecurityReportsApp: () => import('~/vue_shared/security_reports/security_reports_app.vue'),
|
||||
MergeChecksFailed: () => import('./components/states/merge_checks_failed.vue'),
|
||||
ReadyToMerge: ReadyToMergeState,
|
||||
ReportWidgetContainer,
|
||||
|
|
@ -239,9 +238,6 @@ export default {
|
|||
this.mr.mergePipelinesEnabled && this.mr.sourceProjectId !== this.mr.targetProjectId,
|
||||
);
|
||||
},
|
||||
shouldRenderSecurityReport() {
|
||||
return Boolean(this.mr?.pipeline?.id);
|
||||
},
|
||||
shouldRenderTerraformPlans() {
|
||||
return Boolean(this.mr?.terraformReportsPath);
|
||||
},
|
||||
|
|
@ -275,9 +271,6 @@ export default {
|
|||
hasAlerts() {
|
||||
return this.hasMergeError || this.showMergePipelineForkWarning;
|
||||
},
|
||||
shouldShowSecurityExtension() {
|
||||
return window.gon?.features?.refactorSecurityExtension;
|
||||
},
|
||||
shouldShowMergeDetails() {
|
||||
if (this.mr.state === 'readyToMerge') return true;
|
||||
|
||||
|
|
@ -601,15 +594,7 @@ export default {
|
|||
<mr-widget-approvals v-if="shouldRenderApprovals" :mr="mr" :service="service" />
|
||||
<report-widget-container>
|
||||
<extensions-container v-if="hasExtensions" :mr="mr" />
|
||||
<widget-container v-if="mr && shouldShowSecurityExtension" :mr="mr" />
|
||||
<security-reports-app
|
||||
v-if="shouldRenderSecurityReport && !shouldShowSecurityExtension"
|
||||
:pipeline-id="mr.pipeline.id"
|
||||
:project-id="mr.sourceProjectId"
|
||||
:security-reports-docs-path="mr.securityReportsDocsPath"
|
||||
:target-project-full-path="mr.targetProjectFullPath"
|
||||
:mr-iid="mr.iid"
|
||||
/>
|
||||
<widget-container :mr="mr" />
|
||||
</report-widget-container>
|
||||
<div class="mr-section-container mr-widget-workflow">
|
||||
<div v-if="hasAlerts" class="gl-overflow-hidden mr-widget-alert-container">
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
<script>
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export const i18n = {
|
||||
btnText: __('Fork project'),
|
||||
title: __('Fork project?'),
|
||||
message: __(
|
||||
'You can’t edit files directly in this project. Fork this project and submit a merge request with your changes.',
|
||||
),
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'ConfirmForkModal',
|
||||
components: {
|
||||
GlModal,
|
||||
},
|
||||
model: {
|
||||
prop: 'visible',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
modalId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
forkPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
btnActions() {
|
||||
return {
|
||||
cancel: { text: __('Cancel') },
|
||||
primary: {
|
||||
text: this.$options.i18n.btnText,
|
||||
attributes: {
|
||||
href: this.forkPath,
|
||||
variant: 'confirm',
|
||||
'data-qa-selector': 'fork_project_button',
|
||||
'data-method': 'post',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
i18n,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-modal
|
||||
:visible="visible"
|
||||
data-qa-selector="confirm_fork_modal"
|
||||
:modal-id="modalId"
|
||||
:title="$options.i18n.title"
|
||||
:action-primary="btnActions.primary"
|
||||
:action-cancel="btnActions.cancel"
|
||||
@change="$emit('change', $event)"
|
||||
>
|
||||
<p>{{ $options.i18n.message }}</p>
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<script>
|
||||
import { GlModal, GlLoadingIcon, GlLink } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import getWritableForksQuery from './get_writable_forks.query.graphql';
|
||||
|
||||
export const i18n = {
|
||||
btnText: __('Create a new fork'),
|
||||
title: __('Fork project?'),
|
||||
message: __('You can’t edit files directly in this project.'),
|
||||
existingForksMessage: __(
|
||||
'To submit your changes in a merge request, switch to one of these forks or create a new fork.',
|
||||
),
|
||||
newForkMessage: __('To submit your changes in a merge request, create a new fork.'),
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'ConfirmForkModal',
|
||||
components: {
|
||||
GlModal,
|
||||
GlLoadingIcon,
|
||||
GlLink,
|
||||
},
|
||||
inject: {
|
||||
projectPath: {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
model: {
|
||||
prop: 'visible',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
modalId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
forkPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
forks: [],
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
forks: {
|
||||
query: getWritableForksQuery,
|
||||
variables() {
|
||||
return {
|
||||
projectPath: this.projectPath,
|
||||
};
|
||||
},
|
||||
update({ project } = {}) {
|
||||
return project?.visibleForks?.nodes.map((node) => {
|
||||
return {
|
||||
text: node.fullPath,
|
||||
href: node.webUrl,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isLoading() {
|
||||
return this.$apollo.queries.forks.loading;
|
||||
},
|
||||
hasWritableForks() {
|
||||
return this.forks.length;
|
||||
},
|
||||
btnActions() {
|
||||
return {
|
||||
cancel: { text: __('Cancel') },
|
||||
primary: {
|
||||
text: this.$options.i18n.btnText,
|
||||
attributes: {
|
||||
href: this.forkPath,
|
||||
variant: 'confirm',
|
||||
'data-qa-selector': 'fork_project_button',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
i18n,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-modal
|
||||
:visible="visible"
|
||||
data-qa-selector="confirm_fork_modal"
|
||||
:modal-id="modalId"
|
||||
:title="$options.i18n.title"
|
||||
:action-primary="btnActions.primary"
|
||||
:action-cancel="btnActions.cancel"
|
||||
@change="$emit('change', $event)"
|
||||
>
|
||||
<p>{{ $options.i18n.message }}</p>
|
||||
<gl-loading-icon v-if="isLoading" />
|
||||
<template v-else-if="hasWritableForks">
|
||||
<p>{{ $options.i18n.existingForksMessage }}</p>
|
||||
<div v-for="fork in forks" :key="fork.text">
|
||||
<gl-link :href="fork.href">{{ fork.text }}</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
<p v-else>{{ $options.i18n.newForkMessage }}</p>
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
query getWritableForks($projectPath: ID!) {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
visibleForks(minimumAccessLevel: DEVELOPER) {
|
||||
nodes {
|
||||
id
|
||||
fullPath
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
|
|||
import { s__, __ } from '~/locale';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import ActionsButton from '~/vue_shared/components/actions_button.vue';
|
||||
import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue';
|
||||
import ConfirmForkModal from '~/vue_shared/components/web_ide/confirm_fork_modal.vue';
|
||||
import { KEY_EDIT, KEY_WEB_IDE, KEY_GITPOD, KEY_PIPELINE_EDITOR } from './constants';
|
||||
|
||||
export const i18n = {
|
||||
|
|
@ -152,9 +152,7 @@ export default {
|
|||
return this.actions.length > 0;
|
||||
},
|
||||
editAction() {
|
||||
if (!this.showEditButton) {
|
||||
return null;
|
||||
}
|
||||
if (!this.showEditButton) return null;
|
||||
|
||||
const handleOptions = this.needsToFork
|
||||
? {
|
||||
|
|
@ -194,9 +192,7 @@ export default {
|
|||
return __('Web IDE');
|
||||
},
|
||||
webIdeAction() {
|
||||
if (!this.showWebIdeButton) {
|
||||
return null;
|
||||
}
|
||||
if (!this.showWebIdeButton) return null;
|
||||
|
||||
const handleOptions = this.needsToFork
|
||||
? {
|
||||
|
|
@ -298,6 +294,12 @@ export default {
|
|||
},
|
||||
};
|
||||
},
|
||||
mountForkModal() {
|
||||
const { disableForkModal, showWebIdeButton, showEditButton } = this;
|
||||
if (disableForkModal) return false;
|
||||
|
||||
return showWebIdeButton || showEditButton;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showModal(dataKey) {
|
||||
|
|
@ -330,7 +332,7 @@ export default {
|
|||
</gl-sprintf>
|
||||
</gl-modal>
|
||||
<confirm-fork-modal
|
||||
v-if="showWebIdeButton || showEditButton"
|
||||
v-if="mountForkModal"
|
||||
v-model="showForkModal"
|
||||
:modal-id="forkModalId"
|
||||
:fork-path="forkPath"
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ module SearchRateLimitable
|
|||
def safe_search_scope
|
||||
# Sometimes search scope can have abusive length or invalid keyword. We don't want
|
||||
# to send those to redis for rate limit checks, so we guard against that here.
|
||||
return if Feature.disabled?(:search_rate_limited_scopes) || abuse_detected?
|
||||
|
||||
params[:scope]
|
||||
params[:scope] unless abuse_detected?
|
||||
end
|
||||
|
||||
def abuse_detected?
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_force_frontend_feature_flag(:content_editor_on_issues, project&.content_editor_on_issues_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:core_security_mr_widget_counts, project)
|
||||
push_frontend_feature_flag(:issue_assignees_widget, @project)
|
||||
push_frontend_feature_flag(:refactor_security_extension, @project)
|
||||
push_frontend_feature_flag(:deprecate_vulnerabilities_feedback, @project)
|
||||
push_frontend_feature_flag(:moved_mr_sidebar, project)
|
||||
push_frontend_feature_flag(:mr_experience_survey, project)
|
||||
|
|
|
|||
|
|
@ -162,6 +162,41 @@ module Types
|
|||
extras: [:lookahead],
|
||||
resolver: ::Resolvers::Achievements::UserAchievementsResolver
|
||||
|
||||
field :bio,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Bio of the user.'
|
||||
|
||||
field :linkedin,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'LinkedIn profile name of the user.'
|
||||
|
||||
field :twitter,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Twitter username of the user.'
|
||||
|
||||
field :discord,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Discord ID of the user.'
|
||||
|
||||
field :organization,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Who the user represents or works for.'
|
||||
|
||||
field :job_title,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Job title of the user.'
|
||||
|
||||
field :created_at,
|
||||
type: Types::TimeType,
|
||||
null: true,
|
||||
description: 'Timestamp of when the user was created.'
|
||||
|
||||
definition_methods do
|
||||
def resolve_type(object, context)
|
||||
# in the absence of other information, we cannot tell - just default to
|
||||
|
|
|
|||
|
|
@ -157,17 +157,15 @@ module TreeHelper
|
|||
}
|
||||
end
|
||||
|
||||
def fork_modal_options(project, ref, path, blob)
|
||||
def fork_modal_options(project, blob)
|
||||
if show_edit_button?({ blob: blob })
|
||||
fork_path = fork_and_edit_path(project, ref, path)
|
||||
fork_modal_id = "modal-confirm-fork-edit"
|
||||
elsif show_web_ide_button?
|
||||
fork_path = ide_fork_and_edit_path(project, ref, path)
|
||||
fork_modal_id = "modal-confirm-fork-webide"
|
||||
end
|
||||
|
||||
{
|
||||
fork_path: fork_path,
|
||||
fork_path: new_namespace_project_fork_path(project_id: project.path, namespace_id: project.namespace.full_path),
|
||||
fork_modal_id: fork_modal_id
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ class PlanLimits < ApplicationRecord
|
|||
ignore_column :web_hook_calls_high, remove_with: '15.10', remove_after: '2022-02-22'
|
||||
ignore_column :ci_active_pipelines, remove_with: '16.3', remove_after: '2022-07-22'
|
||||
|
||||
attribute :limits_history, :ind_jsonb, default: -> { {} }
|
||||
validates :limits_history, json_schema: { filename: 'plan_limits_history' }
|
||||
|
||||
LimitUndefinedError = Class.new(StandardError)
|
||||
|
||||
belongs_to :plan
|
||||
|
|
@ -46,6 +49,34 @@ class PlanLimits < ApplicationRecord
|
|||
def dashboard_storage_limit_enabled?
|
||||
false
|
||||
end
|
||||
|
||||
def log_limits_changes(user, new_limits)
|
||||
new_limits.each do |attribute, value|
|
||||
limits_history[attribute] ||= []
|
||||
limits_history[attribute] << {
|
||||
user_id: user&.id,
|
||||
username: user&.username,
|
||||
timestamp: Time.current.utc.to_i,
|
||||
value: value
|
||||
}
|
||||
end
|
||||
|
||||
update(limits_history: limits_history)
|
||||
end
|
||||
|
||||
def limit_attribute_changes(attribute)
|
||||
limit_history = limits_history[attribute]
|
||||
return [] unless limit_history
|
||||
|
||||
limit_history.map do |entry|
|
||||
{
|
||||
timestamp: entry[:timestamp],
|
||||
value: entry[:value],
|
||||
username: entry[:username],
|
||||
user_id: entry[:user_id]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PlanLimits.prepend_mod_with('PlanLimits')
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ class UserDetail < ApplicationRecord
|
|||
extend ::Gitlab::Utils::Override
|
||||
|
||||
ignore_column :requires_credit_card_verification, remove_with: '16.1', remove_after: '2023-06-22'
|
||||
ignore_column :provisioned_by_group_at, remove_with: '16.3', remove_after: '2023-07-22'
|
||||
|
||||
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,6 @@ class Vulnerability < ApplicationRecord
|
|||
|
||||
scope :with_projects, -> { includes(:project) }
|
||||
|
||||
# Policy class inferring logic is causing performance
|
||||
# issues therefore we need to explicitly set it.
|
||||
def self.declarative_policy_class
|
||||
:VulnerabilityPolicy
|
||||
end
|
||||
|
||||
def self.link_reference_pattern
|
||||
nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enforcement_limit": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user_id",
|
||||
"username",
|
||||
"timestamp",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification_limit": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user_id",
|
||||
"username",
|
||||
"timestamp",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
},
|
||||
"storage_size_limit": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user_id",
|
||||
"username",
|
||||
"timestamp",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
},
|
||||
"dashboard_limit_enabled_at": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user_id",
|
||||
"username",
|
||||
"timestamp",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
@ -11,16 +11,16 @@
|
|||
= f.label :raw_blob_request_limit, _('Raw blob request rate limit per minute'), class: 'label-bold'
|
||||
= f.number_field :raw_blob_request_limit, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
= _('Maximum number of requests per minute for each raw path (default is 300). Set to 0 to disable throttling.')
|
||||
= _('Maximum number of requests per minute for each raw path (default is `300`). Set to `0` to disable throttling.')
|
||||
.form-group
|
||||
= f.label :push_event_hooks_limit, class: 'label-bold'
|
||||
= f.number_field :push_event_hooks_limit, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
= _('Maximum number of changes (branches or tags) in a single push for which webhooks and services trigger (default is 3).')
|
||||
= _('Maximum number of changes (branches or tags) in a single push above which webhooks and integrations are not triggered (default is `3`). Setting to `0` does not disable throttling.')
|
||||
.form-group
|
||||
= f.label :push_event_activities_limit, class: 'label-bold'
|
||||
= f.number_field :push_event_activities_limit, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
= _('Threshold number of changes (branches or tags) in a single push above which a bulk push event is created (default is 3).')
|
||||
= _('Maximum number of changes (branches or tags) in a single push above which a bulk push event is created (default is `3`). Setting to `0` does not disable throttling.')
|
||||
|
||||
= f.submit _('Save changes'), pajamas_button: true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- type = blob ? 'blob' : 'tree'
|
||||
- button_data = web_ide_button_data({ blob: blob })
|
||||
- fork_options = fork_modal_options(@project, @ref, @path, blob)
|
||||
- fork_options = fork_modal_options(@project, blob)
|
||||
|
||||
.gl-display-inline-block{ data: { options: button_data.merge(fork_options).to_json, web_ide_promo_popover_img: image_path('web-ide-promo-popover.svg') }, id: "js-#{type}-web-ide-link" }
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/413684
|
|||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::tenant scale
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: search_rate_limited_scopes
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118525
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/408521
|
||||
milestone: '16.0'
|
||||
name: purchase_code_suggestions
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123382
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415047
|
||||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::global search
|
||||
group: group::provision
|
||||
default_enabled: false
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: refactor_security_extension
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84896
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365320
|
||||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::threat insights
|
||||
default_enabled: true
|
||||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410121
|
|||
milestone: '16.0'
|
||||
type: development
|
||||
group: group::foundations
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -2,6 +2,31 @@
|
|||
|
||||
require 'declarative_policy'
|
||||
|
||||
# This module speeds up class resolution by caching it.
|
||||
#
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119924
|
||||
# See https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/issues/30
|
||||
module ClassForClassCache
|
||||
def self.prepended(base)
|
||||
class << base
|
||||
attr_accessor :class_for_class_cache
|
||||
end
|
||||
|
||||
base.class_for_class_cache = {}
|
||||
base.singleton_class.prepend(SingletonClassMethods)
|
||||
end
|
||||
|
||||
module SingletonClassMethods
|
||||
def class_for_class(subject_class)
|
||||
class_for_class_cache.fetch(subject_class) do
|
||||
class_for_class_cache[subject_class] = super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DeclarativePolicy.configure do
|
||||
named_policy :global, ::GlobalPolicy
|
||||
end
|
||||
|
||||
DeclarativePolicy.prepend(ClassForClassCache)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ gitlab_schemas:
|
|||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
- gitlab_main
|
||||
- gitlab_main_cell
|
||||
- gitlab_pm
|
||||
# Note that we use ActiveRecord::Base here and not ApplicationRecord.
|
||||
# This is deliberate, as:
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ feature_categories:
|
|||
description: Storing namespaces records for groups, users and projects
|
||||
introduced_by_url: https://github.com/gitlabhq/gitlabhq/pull/2051
|
||||
milestone: "<6.0"
|
||||
gitlab_schema: gitlab_main
|
||||
gitlab_schema: gitlab_main_cell
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ feature_categories:
|
|||
description: TODO
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
|
||||
milestone: "<6.0"
|
||||
gitlab_schema: gitlab_main
|
||||
gitlab_schema: gitlab_main_clusterwide
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
name: gitlab_ci
|
||||
description: Schema for all Cell-local CI tables, ex. ci_builds, etc.
|
||||
allow_cross_joins:
|
||||
- gitlab_shared
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
name: gitlab_main
|
||||
description: Schema for all Cell-local tables, ex. projects, issues, etc.
|
||||
description: Legacy schema for all Cell-local tables, ex. projects, issues, etc.
|
||||
allow_cross_joins:
|
||||
- gitlab_shared
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
name: gitlab_main_cell
|
||||
description: Schema for all Cell-local tables, ex. namespaces, projects, etc.
|
||||
allow_cross_joins:
|
||||
- gitlab_shared
|
||||
- gitlab_main
|
||||
# Temporarily allow cross-joins between clusterwide and cell schemas
|
||||
# This is to be removed once we annotate all cross-joins between those
|
||||
- gitlab_main_clusterwide
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
- gitlab_main
|
||||
# Temporarily allow cross-DB transactions between clusterwide and cell schemas
|
||||
# This is to be removed once we annotate all cross-DBs between those
|
||||
- gitlab_main_clusterwide
|
||||
allow_cross_foreign_keys:
|
||||
- gitlab_main
|
||||
# Temporarily allow FKs between clusterwide and cell schemas
|
||||
# This is to be removed once we remove all FKs between those
|
||||
- gitlab_main_clusterwide
|
||||
|
||||
|
|
@ -1,2 +1,17 @@
|
|||
name: gitlab_main_clusterwide
|
||||
description: Schema for all Cluster-wide tables, ex. application_settings, etc.
|
||||
allow_cross_joins:
|
||||
- gitlab_shared
|
||||
# temporarily allow cross-joins between clusterwide till all tables
|
||||
# are moved to either _clusterwide or _cell
|
||||
- gitlab_main
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
# temporarily allow cross-transaction between clusterwide till all tables
|
||||
# are moved to either _clusterwide or _cell
|
||||
- gitlab_main
|
||||
allow_cross_foreign_keys:
|
||||
# temporarily allow FKs between clusterwide till all tables
|
||||
# are moved to either _clusterwide or _cell
|
||||
- gitlab_main
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
name: gitlab_pm
|
||||
description: Schema for all Cell-local package management features.
|
||||
allow_cross_joins:
|
||||
- gitlab_shared
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
- gitlab_shared
|
||||
|
|
|
|||
|
|
@ -2,3 +2,7 @@ name: gitlab_shared
|
|||
description:
|
||||
Schema for all tables implementing shared features,
|
||||
ex. loose foreign keys, re-indexing, etc.
|
||||
allow_cross_joins:
|
||||
- gitlab_internal
|
||||
allow_cross_transactions:
|
||||
- gitlab_internal
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddLimitsHistoryToPlanLimits < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
add_column :plan_limits, :limits_history, :jsonb, default: {}, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
056459290c9fdced219b524418237e183540aa1aa97c9e3aeab637a005a04bea
|
||||
|
|
@ -20132,7 +20132,8 @@ CREATE TABLE plan_limits (
|
|||
web_hook_calls integer DEFAULT 0 NOT NULL,
|
||||
project_access_token_limit integer DEFAULT 0 NOT NULL,
|
||||
google_cloud_logging_configurations integer DEFAULT 5 NOT NULL,
|
||||
ml_model_max_file_size bigint DEFAULT '10737418240'::bigint NOT NULL
|
||||
ml_model_max_file_size bigint DEFAULT '10737418240'::bigint NOT NULL,
|
||||
limits_history jsonb DEFAULT '{}'::jsonb NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE plan_limits_id_seq
|
||||
|
|
|
|||
|
|
@ -17668,20 +17668,26 @@ A user assigned to a merge request.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mergerequestassigneeavatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="mergerequestassigneebio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="mergerequestassigneebot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="mergerequestassigneecallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneecommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="mergerequestassigneecreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="mergerequestassigneediscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="mergerequestassigneeemail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="mergerequestassigneeemails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneegitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="mergerequestassigneegroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="mergerequestassigneegroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneeid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="mergerequestassigneejobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="mergerequestassigneelinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="mergerequestassigneelocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="mergerequestassigneemergerequestinteraction"></a>`mergeRequestInteraction` | [`UserMergeRequestInteraction`](#usermergerequestinteraction) | Details of this user's interactions with the merge request. |
|
||||
| <a id="mergerequestassigneename"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="mergerequestassigneenamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="mergerequestassigneenamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneeorganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="mergerequestassigneepreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="mergerequestassigneeprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestassigneeprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -17689,6 +17695,7 @@ A user assigned to a merge request.
|
|||
| <a id="mergerequestassigneesavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestassigneestatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestassigneetwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="mergerequestassigneeuserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="mergerequestassigneeuserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="mergerequestassigneeusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
@ -17934,20 +17941,26 @@ The author of the merge request.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mergerequestauthoravatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="mergerequestauthorbio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="mergerequestauthorbot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="mergerequestauthorcallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestauthorcommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="mergerequestauthorcreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="mergerequestauthordiscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="mergerequestauthoremail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="mergerequestauthoremails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestauthorgitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="mergerequestauthorgroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="mergerequestauthorgroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestauthorid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="mergerequestauthorjobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="mergerequestauthorlinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="mergerequestauthorlocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="mergerequestauthormergerequestinteraction"></a>`mergeRequestInteraction` | [`UserMergeRequestInteraction`](#usermergerequestinteraction) | Details of this user's interactions with the merge request. |
|
||||
| <a id="mergerequestauthorname"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="mergerequestauthornamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="mergerequestauthornamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestauthororganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="mergerequestauthorpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="mergerequestauthorprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestauthorprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -17955,6 +17968,7 @@ The author of the merge request.
|
|||
| <a id="mergerequestauthorsavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestauthorstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestauthorstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestauthortwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="mergerequestauthoruserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="mergerequestauthoruserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="mergerequestauthorusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
@ -18234,20 +18248,26 @@ A user participating in a merge request.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mergerequestparticipantavatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="mergerequestparticipantbio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="mergerequestparticipantbot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="mergerequestparticipantcallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestparticipantcommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="mergerequestparticipantcreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="mergerequestparticipantdiscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="mergerequestparticipantemail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="mergerequestparticipantemails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestparticipantgitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="mergerequestparticipantgroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="mergerequestparticipantgroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestparticipantid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="mergerequestparticipantjobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="mergerequestparticipantlinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="mergerequestparticipantlocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="mergerequestparticipantmergerequestinteraction"></a>`mergeRequestInteraction` | [`UserMergeRequestInteraction`](#usermergerequestinteraction) | Details of this user's interactions with the merge request. |
|
||||
| <a id="mergerequestparticipantname"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="mergerequestparticipantnamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="mergerequestparticipantnamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestparticipantorganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="mergerequestparticipantpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="mergerequestparticipantprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestparticipantprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -18255,6 +18275,7 @@ A user participating in a merge request.
|
|||
| <a id="mergerequestparticipantsavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestparticipantstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestparticipantstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestparticipanttwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="mergerequestparticipantuserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="mergerequestparticipantuserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="mergerequestparticipantusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
@ -18519,20 +18540,26 @@ A user assigned to a merge request as a reviewer.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mergerequestrevieweravatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="mergerequestreviewerbio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="mergerequestreviewerbot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="mergerequestreviewercallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewercommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="mergerequestreviewercreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="mergerequestreviewerdiscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="mergerequestrevieweremail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="mergerequestrevieweremails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewergitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="mergerequestreviewergroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="mergerequestreviewergroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewerid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="mergerequestreviewerjobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="mergerequestreviewerlinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="mergerequestreviewerlocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="mergerequestreviewermergerequestinteraction"></a>`mergeRequestInteraction` | [`UserMergeRequestInteraction`](#usermergerequestinteraction) | Details of this user's interactions with the merge request. |
|
||||
| <a id="mergerequestreviewername"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="mergerequestreviewernamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="mergerequestreviewernamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewerorganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="mergerequestreviewerpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="mergerequestreviewerprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestreviewerprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -18540,6 +18567,7 @@ A user assigned to a merge request as a reviewer.
|
|||
| <a id="mergerequestreviewersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewerstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestreviewerstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestreviewertwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="mergerequestrevieweruserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="mergerequestrevieweruserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="mergerequestreviewerusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
@ -23026,19 +23054,25 @@ Core represention of a GitLab user.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="usercoreavatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="usercorebio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="usercorebot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="usercorecallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="usercorecommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="usercorecreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="usercorediscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="usercoreemail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="usercoreemails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="usercoregitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="usercoregroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="usercoregroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="usercoreid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="usercorejobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="usercorelinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="usercorelocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="usercorename"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="usercorenamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="usercorenamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="usercoreorganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="usercorepreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="usercoreprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="usercoreprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -23046,6 +23080,7 @@ Core represention of a GitLab user.
|
|||
| <a id="usercoresavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="usercorestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="usercorestatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="usercoretwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="usercoreuserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="usercoreuserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="usercoreusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
@ -28038,19 +28073,25 @@ Implementations:
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="useravatarurl"></a>`avatarUrl` | [`String`](#string) | URL of the user's avatar. |
|
||||
| <a id="userbio"></a>`bio` | [`String`](#string) | Bio of the user. |
|
||||
| <a id="userbot"></a>`bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. |
|
||||
| <a id="usercallouts"></a>`callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. (see [Connections](#connections)) |
|
||||
| <a id="usercommitemail"></a>`commitEmail` | [`String`](#string) | User's default commit email. |
|
||||
| <a id="usercreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the user was created. |
|
||||
| <a id="userdiscord"></a>`discord` | [`String`](#string) | Discord ID of the user. |
|
||||
| <a id="useremail"></a>`email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: [`User.publicEmail`](#userpublicemail). |
|
||||
| <a id="useremails"></a>`emails` | [`EmailConnection`](#emailconnection) | User's email addresses. (see [Connections](#connections)) |
|
||||
| <a id="usergitpodenabled"></a>`gitpodEnabled` | [`Boolean`](#boolean) | Whether Gitpod is enabled at the user level. |
|
||||
| <a id="usergroupcount"></a>`groupCount` | [`Int`](#int) | Group count for the user. |
|
||||
| <a id="usergroupmemberships"></a>`groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="userid"></a>`id` | [`ID!`](#id) | ID of the user. |
|
||||
| <a id="userjobtitle"></a>`jobTitle` | [`String`](#string) | Job title of the user. |
|
||||
| <a id="userlinkedin"></a>`linkedin` | [`String`](#string) | LinkedIn profile name of the user. |
|
||||
| <a id="userlocation"></a>`location` | [`String`](#string) | Location of the user. |
|
||||
| <a id="username"></a>`name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. |
|
||||
| <a id="usernamespace"></a>`namespace` | [`Namespace`](#namespace) | Personal namespace of the user. |
|
||||
| <a id="usernamespacecommitemails"></a>`namespaceCommitEmails` | [`NamespaceCommitEmailConnection`](#namespacecommitemailconnection) | User's custom namespace commit emails. (see [Connections](#connections)) |
|
||||
| <a id="userorganization"></a>`organization` | [`String`](#string) | Who the user represents or works for. |
|
||||
| <a id="userpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
|
||||
| <a id="userprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="userprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
|
|
@ -28058,6 +28099,7 @@ Implementations:
|
|||
| <a id="usersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
|
||||
| <a id="userstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="userstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="usertwitter"></a>`twitter` | [`String`](#string) | Twitter username of the user. |
|
||||
| <a id="useruserachievements"></a>`userAchievements` **{warning-solid}** | [`UserAchievementConnection`](#userachievementconnection) | **Introduced** in 15.10. This feature is an Experiment. It can be changed or removed at any time. Achievements for the user. Only returns for namespaces where the `achievements` feature flag is enabled. |
|
||||
| <a id="useruserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="userusername"></a>`username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. |
|
||||
|
|
|
|||
|
|
@ -461,10 +461,10 @@ listed in the descriptions of the relevant settings.
|
|||
| `projects_api_rate_limit_unauthenticated` | integer | no | [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112283) in GitLab 15.10. Max number of requests per 10 minutes per IP address for unauthenticated requests to the [list all projects API](projects.md#list-all-projects). Default: 400. To disable throttling set to 0.|
|
||||
| `prometheus_metrics_enabled` | boolean | no | Enable Prometheus metrics. |
|
||||
| `protected_ci_variables` | boolean | no | CI/CD variables are protected by default. |
|
||||
| `push_event_activities_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push events are created. [Bulk push events are created](../user/admin_area/settings/push_event_activities_limit.md) if it surpasses that value. |
|
||||
| `push_event_hooks_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether webhooks and services fire or not. Webhooks and services aren't submitted if it surpasses that value. |
|
||||
| `push_event_activities_limit` | integer | no | Maximum number of changes (branches or tags) in a single push above which a [bulk push event is created](../user/admin_area/settings/push_event_activities_limit.md). Setting to `0` does not disable throttling. |
|
||||
| `push_event_hooks_limit` | integer | no | Maximum number of changes (branches or tags) in a single push above which webhooks and integrations are not triggered. Setting to `0` does not disable throttling. |
|
||||
| `rate_limiting_response_text` | string | no | When rate limiting is enabled via the `throttle_*` settings, send this plain text response when a rate limit is exceeded. 'Retry later' is sent if this is blank. |
|
||||
| `raw_blob_request_limit` | integer | no | Max number of requests per minute for each raw path. Default: 300. To disable throttling set to 0.|
|
||||
| `raw_blob_request_limit` | integer | no | Maximum number of requests per minute for each raw path (default is `300`). Set to `0` to disable throttling.|
|
||||
| `user_email_lookup_limit` | integer | no | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80631/)** in GitLab 14.9 will be removed in 15.0. Replaced by `search_rate_limit`. Max number of requests per minute for email lookup. Default: 60. To disable throttling set to 0.|
|
||||
| `search_rate_limit` | integer | no | Max number of requests per minute for performing a search while authenticated. Default: 30. To disable throttling set to 0.|
|
||||
| `search_rate_limit_unauthenticated` | integer | no | Max number of requests per minute for performing a search while unauthenticated. Default: 10. To disable throttling set to 0.|
|
||||
|
|
|
|||
|
|
@ -414,8 +414,9 @@ scope.
|
|||
| Component | Milestone | Changes |
|
||||
|------------------|----------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| GitLab Rails app | `%16.0` | Adapt `register_{group|project}_runner` permissions to take [application setting](https://gitlab.com/gitlab-org/gitlab/-/issues/386712) in consideration. |
|
||||
| GitLab Rails app | `%16.1` | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `HTTP 410 Gone` if either `allow_runner_registration_token` setting disables registration tokens.<br/>A future v5 version of the API should return `HTTP 404 Not Found`. |
|
||||
| GitLab Rails app | `%16.1` | Add runner group metadata to the runner list. |
|
||||
| GitLab Rails app | | Add UI to allow disabling use of registration tokens in top-level group settings. |
|
||||
| GitLab Rails app | | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `HTTP 410 Gone` if either `allow_runner_registration_token` setting disables registration tokens.<br/>A future v5 version of the API should return `HTTP 404 Not Found`. |
|
||||
| GitLab Rails app | | Hide legacy UI showing registration with a registration token, if it disabled on in top-level group settings or by admins. |
|
||||
|
||||
### Stage 6 - Enforcement
|
||||
|
|
@ -425,18 +426,18 @@ scope.
|
|||
| GitLab Rails app | `%16.6` | Disable registration tokens for all groups by running database migration (only on GitLab.com) | |
|
||||
| GitLab Rails app | `%16.6` | Disable registration tokens on the instance level by running database migration (except GitLab.com) | |
|
||||
| GitLab Rails app | `%16.8` | Disable registration tokens on the instance level for GitLab.com | |
|
||||
| GitLab Rails app | | Implement new `:create_runner` PPGAT scope so that we don't require a full `api` scope. |
|
||||
| GitLab Rails app | `%16.3` | Implement new `:create_runner` PPGAT scope so that we don't require a full `api` scope. |
|
||||
| GitLab Rails app | | Document gotchas when [automatically rotating runner tokens](../../../ci/runners/configure_runners.md#automatically-rotate-authentication-tokens) with multiple machines. |
|
||||
|
||||
### Stage 7 - Removals
|
||||
|
||||
| Component | Milestone | Changes |
|
||||
|------------------|----------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| GitLab Rails app | `17.0` | Remove UI enabling registration tokens on the group and instance levels. |
|
||||
| GitLab Rails app | `17.0` | Remove legacy UI showing registration with a registration token. |
|
||||
| GitLab Runner | `17.0` | Remove runner model arguments from `register` command (for example `--run-untagged`, `--tag-list`, etc.) |
|
||||
| GitLab Rails app | `17.0` | Create database migrations to drop `allow_runner_registration_token` setting columns from `application_settings` and `namespace_settings` tables. |
|
||||
| GitLab Rails app | `17.0` | Create database migrations to drop:<br/>- `runners_registration_token`/`runners_registration_token_encrypted` columns from `application_settings`;<br/>- `runners_token`/`runners_token_encrypted` from `namespaces` table;<br/>- `runners_token`/`runners_token_encrypted` from `projects` table. |
|
||||
| GitLab Rails app | `17.0` | Remove UI enabling registration tokens on the group and instance levels. |
|
||||
| GitLab Rails app | `17.0` | Remove legacy UI showing registration with a registration token. |
|
||||
| GitLab Runner | `17.0` | Remove runner model arguments from `register` command (for example `--run-untagged`, `--tag-list`, etc.) |
|
||||
| GitLab Rails app | `17.0` | Create database migrations to drop `allow_runner_registration_token` setting columns from `application_settings` and `namespace_settings` tables. |
|
||||
| GitLab Rails app | `17.0` | Create database migrations to drop:<br/>- `runners_registration_token`/`runners_registration_token_encrypted` columns from `application_settings`;<br/>- `runners_token`/`runners_token_encrypted` from `namespaces` table;<br/>- `runners_token`/`runners_token_encrypted` from `projects` table. |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
|
|
|||
|
|
@ -190,19 +190,19 @@ You can check that you were successful:
|
|||
### Update the translation files
|
||||
|
||||
English UI strings are localized into many languages.
|
||||
These strings are saved in a `.pot` file, which you must update
|
||||
These strings are saved in a `.pot` file, which must be regenerated
|
||||
any time you update UI text.
|
||||
|
||||
To generate the localization file:
|
||||
To automatically regenerate the localization file:
|
||||
|
||||
1. Ensure you are in the `gitlab-development-kit/gitlab` directory.
|
||||
1. Run the following command:
|
||||
|
||||
```shell
|
||||
bin/rake gettext:compile
|
||||
tooling/bin/gettext_extractor locale/gitlab.pot
|
||||
```
|
||||
|
||||
After several minutes, a `.pot` file is generated in the `/locale` directory.
|
||||
The `.pot` file will be generated in the `/locale` directory.
|
||||
|
||||
Now, in the `gitlab-development-kit/gitlab` directory, if you type `git status`
|
||||
you should have both files listed:
|
||||
|
|
|
|||
|
|
@ -984,6 +984,13 @@ To open the Admin Area:
|
|||
1. Select **Admin Area**.
|
||||
```
|
||||
|
||||
To open the **Your work** menu item:
|
||||
|
||||
```markdown
|
||||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Your work**.
|
||||
```
|
||||
|
||||
To select your avatar:
|
||||
|
||||
```markdown
|
||||
|
|
|
|||
|
|
@ -112,71 +112,7 @@ ourselves, or because we think it would benefit the wider community.
|
|||
Extracting code to a gem also means that we can be sure that the gem
|
||||
does not contain any hidden dependencies on our application code.
|
||||
|
||||
In general, we want to think carefully before doing this as there are
|
||||
also disadvantages:
|
||||
|
||||
### Potential disadvantages
|
||||
|
||||
1. Gems - even those maintained by GitLab - do not necessarily go
|
||||
through the same [code review process](code_review.md) as the main
|
||||
Rails application.
|
||||
1. Extracting the code into a separate project means that we need a
|
||||
minimum of two merge requests to change functionality: one in the gem
|
||||
to make the functional change, and one in the Rails app to bump the
|
||||
version.
|
||||
1. Our needs for our own usage of the gem may not align with the wider
|
||||
community's needs. In general, if we are not using the latest version
|
||||
of our own gem, that might be a warning sign.
|
||||
|
||||
### Create and publish a Ruby gem
|
||||
|
||||
In the case where we do want to extract some library code we've written
|
||||
to a gem, go through these steps:
|
||||
|
||||
1. Determine a suitable name for the gem. If it's a GitLab-owned gem, prefix
|
||||
the gem name with `gitlab-`. For example, `gitlab-sidekiq-fetcher`.
|
||||
1. Create the gem or fork as necessary.
|
||||
1. Ensure the `gitlab_rubygems` group is an owner of the new gem by running:
|
||||
|
||||
```shell
|
||||
gem owner <gem-name> --add gitlab_rubygems
|
||||
```
|
||||
|
||||
1. [Publish the gem to rubygems.org](https://guides.rubygems.org/publishing/#publishing-to-rubygemsorg)
|
||||
1. Visit `https://rubygems.org/gems/<gem-name>` and verify that the gem published
|
||||
successfully and `gitlab_rubygems` is also an owner.
|
||||
1. Start with the code in the Rails application. Here it's fine to have
|
||||
the code in `lib/` and loaded automatically. We can skip this step if
|
||||
the step below makes more sense initially.
|
||||
1. Before extracting to its own project, move the gem to `vendor/gems` and
|
||||
load it in the `Gemfile` using the `path` option. This gives us a gem
|
||||
that can be published to RubyGems.org, with its own test suite and
|
||||
isolated set of dependencies, that is still in our main code tree and
|
||||
goes through the standard code review process.
|
||||
- For an example, see the [merge request !57805](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57805).
|
||||
1. Once the gem is stable - we have been using it in production for a
|
||||
while with few, if any, changes - extract to its own project under
|
||||
the [`gitlab-org/ruby/gems` namespace](https://gitlab.com/gitlab-org/ruby/gems/).
|
||||
|
||||
- To create this project:
|
||||
1. Follow the [instructions for new projects](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#creating-a-new-project).
|
||||
1. Follow the instructions for setting up a [CI/CD configuration](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#cicd-configuration).
|
||||
1. Follow the instructions for [publishing a project](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#publishing-a-project).
|
||||
- See [issue #325463](https://gitlab.com/gitlab-org/gitlab/-/issues/325463)
|
||||
for an example.
|
||||
- In some cases we may want to move a gem to its own namespace. Some
|
||||
examples might be that it will naturally have more than one project
|
||||
(say, something that has plugins as separate libraries), or that we
|
||||
expect non-GitLab-team-members to be maintainers on this project as
|
||||
well as GitLab team members.
|
||||
|
||||
The latter situation (maintainers from outside GitLab) could also
|
||||
apply if someone who currently works at GitLab wants to maintain
|
||||
the gem beyond their time working at GitLab.
|
||||
|
||||
When publishing a gem to RubyGems.org, also note the section on
|
||||
[gem owners](https://about.gitlab.com/handbook/developer-onboarding/#ruby-gems)
|
||||
in the handbook.
|
||||
Read more about [Gems development guidelines](gems.md).
|
||||
|
||||
## Upgrade Rails
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,321 @@
|
|||
---
|
||||
stage: none
|
||||
group: unassigned
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Gems development guidelines
|
||||
|
||||
GitLab uses Gems as a tool to improve code reusability and modularity
|
||||
in a monolithic codebase.
|
||||
|
||||
Sometimes we create libraries within our codebase that we want to
|
||||
extract, either because their functionality is highly isolated,
|
||||
we want to use them in other applications
|
||||
ourselves, or we think it would benefit the wider community.
|
||||
Extracting code to a gem also means that we can be sure that the gem
|
||||
does not contain any hidden dependencies on our application code.
|
||||
|
||||
## When to use Gems
|
||||
|
||||
Gems should be used always when implementing functions that can be considered isolated,
|
||||
that are decoupled from the business logic of GitLab and can be developed separately. Consider the
|
||||
following examples where Gem logic could be placed:
|
||||
|
||||
The best example where we can look for opportunities to introduce new gems
|
||||
is the [lib/](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/) folder.
|
||||
|
||||
The **lib/** folder is a mix of code that is generic/universal, GitLab-specific, and tightly integrated with the rest of the codebase.
|
||||
|
||||
If you cannot find a good place for your code in **lib/** you should strongly
|
||||
consider creating the new Gem [In the same repo](#in-the-same-repo).
|
||||
|
||||
## In the same repo
|
||||
|
||||
**Our GitLab Gems should be always put in `gems/` of GitLab monorepo.**
|
||||
|
||||
That gives us the advantages of gems (modular code, quicker to run tests in development).
|
||||
and prevents complexity (coordinating changes across repos, new permissions, multiple projects, etc.).
|
||||
|
||||
Gems stored in the same repo should be referenced in `Gemfile` with the `path:` syntax.
|
||||
They should not be published to RubyGems.
|
||||
|
||||
### Advantages
|
||||
|
||||
Using Gems can provide several benefits for code maintenance:
|
||||
|
||||
- Code Reusability - Gems are isolated libraries that serve single purpose. When using Gems, a common functions
|
||||
can be isolated in a simple package, that is well documented, tested, and re-used in different applications.
|
||||
|
||||
- Modularity - Gems help to create isolation by encapsulating specific functionality within self-contained library.
|
||||
This helps to better organize code, better define who is owner of a given module, makes it easier to maintain
|
||||
or update specific gems.
|
||||
|
||||
- Small - Gems by design due to implementing isolated set of functions are small. Small projects are much easier
|
||||
to comprehend, extend and maintain.
|
||||
|
||||
- Testing - Using Gems since they are small makes much faster to run all tests, or be very through with testing of the gem.
|
||||
Since the gem is packaged, not changed too often, it also allows us to run those tests less frequently improving
|
||||
CI testing time.
|
||||
|
||||
### To Do
|
||||
|
||||
#### Desired use cases
|
||||
|
||||
The `gitlab-utils` is a Gem containing as of set of class that implement common intrisic functions
|
||||
used by GitLab developers, like `strong_memoize` or `Gitlab::Utils.to_boolean`.
|
||||
|
||||
The `gitlab-database-schema-migrations` is a potential Gem containing our extensions to Rails
|
||||
framework improving how database migrations are stored in repository. This builds on top of Rails
|
||||
and is not specific to GitLab the application, and could be generally used for other projects
|
||||
or potentially be upstreamed.
|
||||
|
||||
The `gitlab-database-load-balancing` similar to previous is a potential Gem to implement GitLab specific
|
||||
load balancing to Rails database handling. Since this is rather complex and highly specific code
|
||||
maintaing it's complexity in a isolated and well tested Gem would help with removing this complexity
|
||||
from a big monolithic codebase.
|
||||
|
||||
The `gitlab-flipper` is another potential Gem implementing all our custom extensions to support feature
|
||||
flags in a codebase. Over-time the monolithic codebase did grow with the check for feature flags
|
||||
usage, adding consistency checks and various helpers to track owners of feature flags added. This is
|
||||
not really part of GitLab business logic and could be used to better track our implementation
|
||||
of Flipper and possibly much easier change it to dogfood [GitLab Feature Flags](../operations/feature_flags.md).
|
||||
|
||||
The `gitlab-ci-reports-parsers` is a potential Gem that could implement all various parsers for various formats.
|
||||
The parsed output would be transformed into objects that could then be used by GitLab the application
|
||||
to store it in the database. This functionality could be an additional Gem since it is isolated,
|
||||
rarely changed, and GitLab Rails only consumes the data.
|
||||
|
||||
The same pattern could be applied to all other type of parsers, like security vulnerabilities, or any
|
||||
other complex structures that need to be transformed into a form that is consumed by GitLab Rails.
|
||||
|
||||
The `gitlab-active_record` is a gem adding GitLab specific Active Record patches.
|
||||
It is very well desired for such to be managed separately to isolate complexity.
|
||||
|
||||
#### Other potential use cases
|
||||
|
||||
The `gitlab-ci-config` is a potential Gem containing all our CI code used to parse `.gitlab-ci.yml`.
|
||||
This code is today lightly interlocked with GitLab the application due to lack of proper abstractions.
|
||||
However, moving this to dedicated Gem could allow us to build various adapters to handle integration
|
||||
with GitLab the application. The interface would for example define an adapter to resolve `includes:`.
|
||||
Once we would have a `gitlab-ci-config` Gem it could be used within GitLab and outside of GitLab Rails
|
||||
and [GitLab CLI](https://gitlab.com/gitlab-org/cli).
|
||||
|
||||
### Create and use a new Gem
|
||||
|
||||
You can see example adding new Gem: [!121676](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121676).
|
||||
|
||||
1. Create a new Ruby Gem in `gems/gitlab-<name-of-gem>` with `bundle gem gems/gitlab-<name-of-gem> --no-exe --no-coc --no-ext --no-mit`.
|
||||
1. Edit or remove `gitlab-<name-of-gem>/README.md` to provide a simple one paragraph description of the Gem.
|
||||
1. Edit `gitlab-<name-of-gem>/gitlab-<name-of-gem>.gemspec` and fill the details about the Gem as in the following example:
|
||||
|
||||
```ruby
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "gitlab-<name-of-gem>"
|
||||
spec.version = Gitlab::NameOfGem::VERSION
|
||||
spec.authors = ["group::tenant-scale"]
|
||||
spec.email = ["engineering@gitlab.com"]
|
||||
|
||||
spec.summary = "GitLab's RSpec extensions"
|
||||
spec.description = "A set of useful helpers to configure RSpec with various stubs and CI configs."
|
||||
spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/gitlab-<name-of-gem>"
|
||||
spec.required_ruby_version = ">= 2.6.0"
|
||||
end
|
||||
```
|
||||
|
||||
1. Update `gems/gitlab-<name-of-gem>/.rubocop` with:
|
||||
|
||||
```yaml
|
||||
inherit_from:
|
||||
- ../../.rubocop.yml
|
||||
|
||||
CodeReuse/ActiveRecord:
|
||||
Enabled: false
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.0
|
||||
|
||||
Naming/FileName:
|
||||
Exclude:
|
||||
- spec/**/*.rb
|
||||
```
|
||||
|
||||
1. Configure CI for a newly added Gem:
|
||||
|
||||
- Add `gems/gitlab-<name-of-gem>/.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
|
||||
rspec:
|
||||
image: "ruby:${RUBY_VERSION}"
|
||||
cache:
|
||||
key: gitlab-<name-of-gem>
|
||||
paths:
|
||||
- gitlab-<name-of-gem>/vendor/ruby
|
||||
before_script:
|
||||
- cd vendor/gems/bundler-checksum
|
||||
- ruby -v # Print out ruby version for debugging
|
||||
- gem install bundler --no-document # Bundler is not installed with the image
|
||||
- bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
|
||||
- bundle config set with 'development'
|
||||
- bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
|
||||
- bundle config # Show bundler configuration
|
||||
- bundle install -j $(nproc)
|
||||
script:
|
||||
- bundle exec rspec
|
||||
parallel:
|
||||
matrix:
|
||||
- RUBY_VERSION: ["2.7", "3.0", "3.1", "3.2"]
|
||||
```
|
||||
|
||||
- To `.gitlab/ci/rules.gitlab-ci.yml` add:
|
||||
|
||||
```yaml
|
||||
.gems:rules:gitlab-<name-of-gem>:
|
||||
rules:
|
||||
- <<: *if-merge-request
|
||||
changes: ["gems/gitlab-<name-of-gem>/**/*"]
|
||||
```
|
||||
|
||||
- To `.gitlab/ci/gitlab-gems.gitlab-ci.yml` add:
|
||||
|
||||
```yaml
|
||||
gems gitlab-<name-of-gem>:
|
||||
extends:
|
||||
- .gems:rules:gitlab-<name-of-gem>
|
||||
needs: []
|
||||
trigger:
|
||||
include: gems/gitlab-<name-of-gem>/.gitlab-ci.yml
|
||||
strategy: depend
|
||||
```
|
||||
|
||||
1. Reference Gem in `Gemfile` with:
|
||||
|
||||
```ruby
|
||||
gem 'gitlab-<name-of-gem>', path: 'gems/gitlab-<name-of-gem>'
|
||||
```
|
||||
|
||||
## In the external repo
|
||||
|
||||
In general, we want to think carefully before doing this as there are
|
||||
severe disadvantages.
|
||||
|
||||
Gems stored in the external repo MUST be referenced in `Gemfile` with `version` syntax.
|
||||
They MUST be always published to RubyGems.
|
||||
|
||||
### Examples
|
||||
|
||||
At GitLab we use a number of external gems:
|
||||
|
||||
- [LabKit Ruby](https://gitlab.com/gitlab-org/labkit-ruby)
|
||||
- [GitLab Ruby Gems](https://gitlab.com/gitlab-org/ruby/gems)
|
||||
|
||||
### Potential disadvantages
|
||||
|
||||
- Gems - even those maintained by GitLab - do not necessarily go
|
||||
through the same [code review process](code_review.md) as the main
|
||||
Rails application. This is particularly critical for Application Security.
|
||||
- Requires setting up CI/CD from scratch, including tools like Danger that
|
||||
support consistent code review standards.
|
||||
- Extracting the code into a separate project means that we need a
|
||||
minimum of two merge requests to change functionality: one in the gem
|
||||
to make the functional change, and one in the Rails app to bump the
|
||||
version.
|
||||
- Integration with `gitlab-rails` requiring a second MR means integration problems
|
||||
may be discovered late.
|
||||
- With a smaller pool of reviewers and maintainers compared to `gitlab-rails`,
|
||||
it may take longer to get code reviewed and the impact of "bus factor" increases.
|
||||
- Inconsistent workflows for how a new gem version is released. It is currently at
|
||||
the discretion of library maintainers to decide how it works.
|
||||
- Promotes knowledge silos because code has less visibility and exposure than `gitlab-rails`.
|
||||
- We have a well defined process for promoting GitLab reviewers to maintainers.
|
||||
This is not true for extracted libraries, increasing the risk of lowering the bar for code reviews,
|
||||
and increasing the risk of shipping a change.
|
||||
- Our needs for our own usage of the gem may not align with the wider
|
||||
community's needs. In general, if we are not using the latest version
|
||||
of our own gem, that might be a warning sign.
|
||||
|
||||
### Potential advantages
|
||||
|
||||
- Faster feedback loops, since CI/CD runs against smaller repositories.
|
||||
- Ability to expose the project to the wider community and benefit from external contributions.
|
||||
- Repository owners are most likely the best audience to review a change, which reduces
|
||||
the necessity of finding the right reviewers in `gitlab-rails`.
|
||||
|
||||
### Create and publish a Ruby gem
|
||||
|
||||
The project for a new Gem should always be created in [`gitlab-org/ruby/gems` namespace](https://gitlab.com/gitlab-org/ruby/gems/):
|
||||
|
||||
1. Determine a suitable name for the gem. If it's a GitLab-owned gem, prefix
|
||||
the gem name with `gitlab-`. For example, `gitlab-sidekiq-fetcher`.
|
||||
1. Create the gem or fork as necessary.
|
||||
1. Ensure the `gitlab_rubygems` group is an owner of the new gem by running:
|
||||
|
||||
```shell
|
||||
gem owner <gem-name> --add gitlab_rubygems
|
||||
```
|
||||
|
||||
1. [Publish the gem to rubygems.org](https://guides.rubygems.org/publishing/#publishing-to-rubygemsorg)
|
||||
1. Visit `https://rubygems.org/gems/<gem-name>` and verify that the gem published
|
||||
successfully and `gitlab_rubygems` is also an owner.
|
||||
1. Create a project in [`gitlab-org/ruby/gems` namespace](https://gitlab.com/gitlab-org/ruby/gems/).
|
||||
|
||||
- To create this project:
|
||||
1. Follow the [instructions for new projects](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#creating-a-new-project).
|
||||
1. Follow the instructions for setting up a [CI/CD configuration](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#cicd-configuration).
|
||||
1. Follow the instructions for [publishing a project](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#publishing-a-project).
|
||||
- See [issue #325463](https://gitlab.com/gitlab-org/gitlab/-/issues/325463)
|
||||
for an example.
|
||||
- In some cases we may want to move a gem to its own namespace. Some
|
||||
examples might be that it will naturally have more than one project
|
||||
(say, something that has plugins as separate libraries), or that we
|
||||
expect users outside GitLab to be maintainers on this project as
|
||||
well as GitLab team members.
|
||||
|
||||
The latter situation (maintainers from outside GitLab) could also
|
||||
apply if someone who currently works at GitLab wants to maintain
|
||||
the gem beyond their time working at GitLab.
|
||||
|
||||
When publishing a gem to RubyGems.org, also note the section on
|
||||
[gem owners](https://about.gitlab.com/handbook/developer-onboarding/#ruby-gems)
|
||||
in the handbook.
|
||||
|
||||
## The `vendor/gems/`
|
||||
|
||||
The purpose of `vendor/` is to pull into GitLab monorepo external dependencies,
|
||||
which do have external repositories, but for the sake of simplicity we want
|
||||
to store them in monorepo:
|
||||
|
||||
- The `vendor/gems/` MUST ONLY be used if we are pulling from external repository either via script, or manually.
|
||||
- The `vendor/gems/` MUST NOT be used for storing in-house gems.
|
||||
- The `vendor/gems/` MAY accept fixes to make them buildable with GitLab monorepo
|
||||
- The `gems/` MUST be used for storing all in-house gems that are part of GitLab monorepo.
|
||||
- The **RubyGems** MUST be used for all externally stored dependencies that are not in `gems/` in GitLab monorepo.
|
||||
|
||||
### Handling of an existing gems in `vendor/gems`
|
||||
|
||||
- For in-house Gems that do not have external repository and are currently stored in `vendor/gems/`:
|
||||
|
||||
- For Gems that are used by other repositories:
|
||||
|
||||
- We will migrate it into its own repository.
|
||||
- We will start or continue publishing them via RubyGems.
|
||||
- Those Gems will be referenced via version in `Gemfile` and fetched from RubyGems.
|
||||
|
||||
- For Gems that are only used by monorepo:
|
||||
|
||||
- We will stop publishing new versions to RubyGems.
|
||||
- We will not pull from RubyGems already published versions since there might
|
||||
be applications depedent on those.
|
||||
- We will move those gems to `gems/`.
|
||||
- Those Gems will be referenced via `path:` in `Gemfile`.
|
||||
|
||||
- For `vendor/gems/` that are external and vendored in monorepo:
|
||||
|
||||
- We will maintain them in the repository if they require some fixes that cannot be or are not yet upstreamed.
|
||||
- It is expected that vendored gems might be published by third-party.
|
||||
- Those Gems will not be published by us to RubyGems.
|
||||
- Those Gems will be referenced via `path:` in `Gemfile`, since we cannot depend on RubyGems.
|
||||
|
|
@ -6,9 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# GitLab Dedicated
|
||||
|
||||
NOTE:
|
||||
GitLab Dedicated is currently in limited availability. You can learn more and join the waitlist [on our website](https://about.gitlab.com/single-tenant-saas).
|
||||
|
||||
GitLab Dedicated is a fully isolated, single-tenant SaaS service that is:
|
||||
|
||||
- Hosted and managed by GitLab, Inc.
|
||||
|
|
@ -164,6 +161,6 @@ The following AWS regions are not available:
|
|||
For more information about the planned improvements to GitLab Dedicated,
|
||||
see the [category direction page](https://about.gitlab.com/direction/saas-platforms/dedicated/).
|
||||
|
||||
## Join the GitLab Dedicated waitlist
|
||||
## Interested in GitLab Dedicated?
|
||||
|
||||
As we scale this new offering, we are making GitLab Dedicated available by inviting customers to [join our waitlist](https://about.gitlab.com/dedicated/).
|
||||
Learn more about GitLab Dedicated and [talk to an expert](https://about.gitlab.com/dedicated/).
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ To turn off this sidebar, return to your avatar and turn off the toggle.
|
|||
## Layout of the left sidebar
|
||||
|
||||
At the top of the left sidebar are several shortcuts. Use these shortcuts to
|
||||
quickly create new items, view your profile, search, and view your list of issues,
|
||||
show and hide the left sidebar, create new items, search, and view your profile. You can also view your list of issues,
|
||||
merge requests, and to-do items.
|
||||
|
||||

|
||||
|
||||
If you have hidden the left sidebar, you can display it temporarily by hovering your cursor over the left edge of the GitLab window.
|
||||
|
||||
The next area of the left sidebar changes based on the information you're viewing. For example,
|
||||
you might be viewing a project, exploring projects or groups, or viewing your profile.
|
||||
Use this area to switch to other areas of the left sidebar.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,6 @@ To modify this setting:
|
|||
- Through the [Application settings API](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)
|
||||
as `push_event_activities_limit`.
|
||||
|
||||
The default value is 3, but it can be greater than or equal 0.
|
||||
The default value is `3`, but the value can be greater than or equal to `0`. Setting this value to `0` does not disable throttling.
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ projects.
|
|||
|
||||
To view the instance level Kubernetes clusters:
|
||||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Kubernetes**.
|
||||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Admin Area**.
|
||||
1. Select **Kubernetes**.
|
||||
|
||||
## Cluster precedence
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
The Operations Dashboard provides a summary of each project's operational health,
|
||||
including pipeline and alert status.
|
||||
|
||||
To access the dashboard, on the top bar, select **Main menu > Operations**.
|
||||
To access the dashboard:
|
||||
|
||||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Your work**.
|
||||
1. Select **Operations**.
|
||||
|
||||
## Adding a project to the dashboard
|
||||
|
||||
|
|
|
|||
|
|
@ -1110,6 +1110,9 @@ and later, the pipeline webhook returns only the latest jobs.
|
|||
In [GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89546)
|
||||
and later, pipeline webhooks triggered by blocked users are not processed.
|
||||
|
||||
In [GitLab 16.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123639)
|
||||
and later, pipeline webhooks started to expose `object_attributes.name`.
|
||||
|
||||
Request header:
|
||||
|
||||
```plaintext
|
||||
|
|
@ -1124,6 +1127,7 @@ Payload example:
|
|||
"object_attributes":{
|
||||
"id": 31,
|
||||
"iid": 3,
|
||||
"name": "Pipeline for branch: master",
|
||||
"ref": "master",
|
||||
"tag": false,
|
||||
"sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
|
||||
|
|
|
|||
|
|
@ -14,59 +14,72 @@ These commits are displayed on the merge request's **Commits** tab.
|
|||
From this tab, you can review commit messages and copy a commit's SHA when you need to
|
||||
[cherry-pick changes](cherry_pick_changes.md).
|
||||
|
||||
## Navigate merge request commits
|
||||
## View commits in a merge request
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18140) in GitLab 13.0.
|
||||
|
||||
To navigate commits in a merge request:
|
||||
|
||||
1. Select the **Commits** tab.
|
||||
1. Select the commit link. The most recent commit is displayed.
|
||||
1. Navigate through the commits by either:
|
||||
|
||||
- Selecting **Prev** and **Next** buttons below the tab buttons.
|
||||
- Using the <kbd>X</kbd> and <kbd>C</kbd> keyboard shortcuts.
|
||||
|
||||

|
||||
|
||||
## Add a comment to a commit
|
||||
|
||||
You can add comments and threads to a particular commit.
|
||||
To see the commits included in a merge request:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Repository > Commits**.
|
||||
1. Below the commits, in the **Comment** field, enter a comment.
|
||||
1. Select **Comment** or select the down arrow (**{chevron-down}**) to select **Start thread**.
|
||||
1. On the left sidebar, select **Merge requests**, then select your merge request.
|
||||
1. To show a list of the commits in the merge request, newest first, select **Commits** .
|
||||
To read more about the commit, select **Toggle commit description** (**{ellipsis_h}**)
|
||||
on any commit.
|
||||
1. To view the changes in the commit, select the title of the commit link.
|
||||
1. To view other commits in the merge request, either:
|
||||
|
||||
- Select **Prev** or **Next**.
|
||||
- Use keyboard shortcuts: <kbd>X</kbd> (previous commit) and <kbd>C</kbd> (next commit).
|
||||
|
||||
If your merge request builds upon a previous merge request, you might
|
||||
need to [include more commits for context](#show-commits-from-previous-merge-requests).
|
||||
|
||||
### Show commits from previous merge requests
|
||||
|
||||
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.8.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.9. [Feature flag `context_commits`](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) removed.
|
||||
|
||||
When you review a merge request, you might need information from previous commits
|
||||
to help understand the commits you're reviewing. You might need more context
|
||||
if another merge request:
|
||||
|
||||
- Changed files your current merge request doesn't modify, so those files aren't shown
|
||||
in your current merge request's diff.
|
||||
- Changed files that you're modifying in your current merge request, and you need
|
||||
to see the progression of work.
|
||||
|
||||
To add previously merged commits to a merge request for more context:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests**, then select your merge request.
|
||||
1. Select **Commits**.
|
||||
1. Scroll to the end of the list of commits, and select **Add previously merged commits**.
|
||||
1. Select the commits that you want to add.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Add a comment to a commit
|
||||
|
||||
WARNING:
|
||||
Threads created this way are lost if the commit ID changes after a
|
||||
force push.
|
||||
|
||||
## View merge request commits in context
|
||||
To add discussion to a specific commit:
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29274) in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `context_commits`. Enabled by default.
|
||||
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.8.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.9. [Feature flag `context_commits`](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) removed.
|
||||
|
||||
When reviewing a merge request, it helps to have more context about the changes
|
||||
made. That includes unchanged lines in unchanged files, and previous commits
|
||||
that have already merged that the change is built on.
|
||||
|
||||
To add previously merged commits to a merge request for more context:
|
||||
|
||||
1. Go to your merge request.
|
||||
1. Select the **Commits** tab.
|
||||
1. Scroll to the end of the list of commits, and select **Add previously merged commits**:
|
||||
1. Select the commits that you want to add.
|
||||
1. Select **Save changes**.
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Repository > Commits**.
|
||||
1. Below the commits, in the **Comment** field, enter a comment.
|
||||
1. Save your comment as either a standalone comment, or a thread:
|
||||
- To add a comment, select **Comment**.
|
||||
- To start a thread, select the down arrow (**{chevron-down}**), then select **Start thread**.
|
||||
|
||||
## View diffs between commits
|
||||
|
||||
To view the changes between previously merged commits:
|
||||
|
||||
1. On your merge request, select the **Changes** tab.
|
||||
1. By **Compare**, select the commit you want to view:
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests**, then select your merge request.
|
||||
1. Select **Changes**.
|
||||
1. By **Compare** (**{file-tree}**), select the commits to compare:
|
||||
|
||||

|
||||
|
||||
If you selected to add previously merged commits, they are displayed in the list.
|
||||
If you selected to add previously merged commits for context, those commits are
|
||||
also shown in the list.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Incubation
|
||||
stage: Plan
|
||||
group: Knowledge
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ Each workspace includes its own set of dependencies, libraries, and tools, which
|
|||
|
||||
- You must have at least the Developer role in the root group.
|
||||
- In each public project you want to use this feature for, create a [devfile](#devfile):
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project
|
||||
1. In the root directory of your project, create a file named `.devfile.yaml`. You can use one of the [example configurations](#example-configurations).
|
||||
- Ensure the container images used in the devfile support [arbitrary user IDs](#arbitrary-user-ids).
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# GitLab Gems
|
||||
|
||||
This directory contains all GitLab built monorepo Gems.
|
||||
|
||||
## Development guidelines
|
||||
|
||||
The Gems created in this repository should adhere to the following rules:
|
||||
|
||||
- MUST: Contain `.gitlab-ci.yml`.
|
||||
- MUST: Contain `.rubocop.yml` and be based on `gitlab-styles`.
|
||||
- MUST: Be added to `.gitlab/ci/gitlab-gems.gitlab-ci.yml`.
|
||||
- MUST NOT: Reference source code outside of `gems/<gem-name>/` with `require_relative "../../lib"`.
|
||||
- MUST NOT: Require other gems that would result in circular dependencies.
|
||||
- MAY: Reference other Gems in `gems/` folder or `vendor/gems/` with `gem <name>, path: "../gitlab-rspec"`.
|
||||
- MAY: Define in `.gemspec` the owning group, like `group::tenant scale`.
|
||||
- RECOMMENDED: Namespaced with `Gitlab::<GemName>`.
|
||||
- RECOMMENDED: Be added to `CODEOWNERS`.
|
||||
- MUST NOT: Have an active associated project created in [gitlab-org/ruby/gems/](https://gitlab.com/gitlab-org/ruby/gems).
|
||||
|
|
@ -132,8 +132,8 @@ module API
|
|||
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
|
||||
optional :project_export_enabled, type: Boolean, desc: 'Enable project export'
|
||||
optional :prometheus_metrics_enabled, type: Boolean, desc: 'Enable Prometheus metrics'
|
||||
optional :push_event_hooks_limit, type: Integer, desc: "Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value."
|
||||
optional :push_event_activities_limit, type: Integer, desc: 'Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push event will be created. Bulk push event will be created if it surpasses that value.'
|
||||
optional :push_event_hooks_limit, type: Integer, desc: "Maximum number of changes (branches or tags) in a single push above which webhooks and integrations are not triggered. Setting to `0` does not disable throttling."
|
||||
optional :push_event_activities_limit, type: Integer, desc: 'Maximum number of changes (branches or tags) in a single push above which a bulk push event is created. Setting to `0` does not disable throttling.'
|
||||
optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
|
||||
given recaptcha_enabled: ->(val) { val } do
|
||||
requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ module Gitlab
|
|||
{
|
||||
id: pipeline.id,
|
||||
iid: pipeline.iid,
|
||||
name: pipeline.name,
|
||||
ref: pipeline.source_ref,
|
||||
tag: pipeline.tag,
|
||||
sha: pipeline.sha,
|
||||
|
|
|
|||
|
|
@ -80,6 +80,26 @@ module Gitlab
|
|||
# rubocop:enable Gitlab/DocUrl
|
||||
end
|
||||
|
||||
private_class_method def self.cross_access_allowed?(type, table_schemas)
|
||||
table_schemas.any? do |schema|
|
||||
extra_schemas = table_schemas - [schema]
|
||||
extra_schemas -= Gitlab::Database.all_gitlab_schemas[schema]&.public_send(type) || [] # rubocop:disable GitlabSecurity/PublicSend
|
||||
extra_schemas.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def self.cross_joins_allowed?(table_schemas)
|
||||
table_schemas.empty? || self.cross_access_allowed?(:allow_cross_joins, table_schemas)
|
||||
end
|
||||
|
||||
def self.cross_transactions_allowed?(table_schemas)
|
||||
table_schemas.empty? || self.cross_access_allowed?(:allow_cross_transactions, table_schemas)
|
||||
end
|
||||
|
||||
def self.cross_foreign_key_allowed?(table_schemas)
|
||||
self.cross_access_allowed?(:allow_cross_foreign_keys, table_schemas)
|
||||
end
|
||||
|
||||
def self.dictionary_paths
|
||||
Gitlab::Database.all_database_connections
|
||||
.values.map(&:db_docs_dir).uniq
|
||||
|
|
|
|||
|
|
@ -5,12 +5,18 @@ module Gitlab
|
|||
GitlabSchemaInfo = Struct.new(
|
||||
:name,
|
||||
:description,
|
||||
:allow_cross_joins,
|
||||
:allow_cross_transactions,
|
||||
:allow_cross_foreign_keys,
|
||||
:file_path,
|
||||
keyword_init: true
|
||||
) do
|
||||
def initialize(*)
|
||||
super
|
||||
self.name = name.to_sym
|
||||
self.allow_cross_joins = allow_cross_joins&.map(&:to_sym)&.freeze
|
||||
self.allow_cross_transactions = allow_cross_transactions&.map(&:to_sym)&.freeze
|
||||
self.allow_cross_foreign_keys = allow_cross_foreign_keys&.map(&:to_sym)&.freeze
|
||||
end
|
||||
|
||||
def self.load_file(yaml_file)
|
||||
|
|
|
|||
|
|
@ -111,15 +111,15 @@ module Gitlab
|
|||
context[:modified_tables_by_db][database].merge(tables)
|
||||
all_tables = context[:modified_tables_by_db].values.flat_map(&:to_a)
|
||||
schemas = ::Gitlab::Database::GitlabSchema.table_schemas!(all_tables)
|
||||
|
||||
schemas += ApplicationRecord.gitlab_transactions_stack
|
||||
|
||||
if schemas.many?
|
||||
messages = ["Cross-database data modification of '#{schemas.to_a.join(", ")}' were detected within " \
|
||||
"a transaction modifying the '#{all_tables.to_a.join(", ")}' tables. " \
|
||||
"Please refer to https://docs.gitlab.com/ee/development/database/multiple_databases.html#removing-cross-database-transactions for details on how to resolve this exception."]
|
||||
|
||||
messages << cleaned_queries
|
||||
unless ::Gitlab::Database::GitlabSchema.cross_transactions_allowed?(schemas)
|
||||
messages = []
|
||||
messages << "Cross-database data modification of '#{schemas.to_a.join(", ")}' were detected within " \
|
||||
"a transaction modifying the '#{all_tables.to_a.join(", ")}' tables. "
|
||||
messages << "Please refer to https://docs.gitlab.com/ee/development/database/multiple_databases.html#removing-cross-database-transactions " \
|
||||
"for details on how to resolve this exception."
|
||||
messages += cleaned_queries
|
||||
|
||||
raise CrossDatabaseModificationAcrossUnsupportedTablesError, messages.join("\n\n")
|
||||
end
|
||||
|
|
@ -182,11 +182,11 @@ module Gitlab
|
|||
end
|
||||
|
||||
def self.cleaned_queries
|
||||
return '' unless dev_or_test_env?
|
||||
return [] unless dev_or_test_env?
|
||||
|
||||
context[:queries].last(QUERY_LIMIT).each_with_index.map do |sql, i|
|
||||
"#{i}: #{sql}"
|
||||
end.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
def self.in_transaction?
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ module Gitlab
|
|||
gitlab_internal: nil,
|
||||
|
||||
# Pods specific changes
|
||||
gitlab_main_clusterwide: :gitlab_main
|
||||
gitlab_main_clusterwide: :gitlab_main,
|
||||
gitlab_main_cell: :gitlab_main
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
|
|
@ -61,6 +62,7 @@ module Gitlab
|
|||
def restrict_to_ddl_only(parsed)
|
||||
tables = self.dml_tables(parsed)
|
||||
schemas = self.dml_schemas(tables)
|
||||
schemas = self.map_schemas(schemas)
|
||||
|
||||
if schemas.any?
|
||||
self.raise_dml_not_allowed_error("Modifying of '#{tables}' (#{schemas.to_a}) with '#{parsed.sql}'")
|
||||
|
|
@ -78,8 +80,10 @@ module Gitlab
|
|||
|
||||
tables = self.dml_tables(parsed)
|
||||
schemas = self.dml_schemas(tables)
|
||||
schemas = self.map_schemas(schemas)
|
||||
allowed_schemas = self.map_schemas(self.allowed_gitlab_schemas)
|
||||
|
||||
if (schemas - self.allowed_gitlab_schemas).any?
|
||||
if (schemas - allowed_schemas).any?
|
||||
raise DMLAccessDeniedError, \
|
||||
"Select/DML queries (SELECT/UPDATE/DELETE) do access '#{tables}' (#{schemas.to_a}) " \
|
||||
"which is outside of list of allowed schemas: '#{self.allowed_gitlab_schemas}'. " \
|
||||
|
|
@ -100,15 +104,19 @@ module Gitlab
|
|||
end
|
||||
|
||||
def dml_schemas(tables)
|
||||
extra_schemas = ::Gitlab::Database::GitlabSchema.table_schemas!(tables)
|
||||
::Gitlab::Database::GitlabSchema.table_schemas!(tables)
|
||||
end
|
||||
|
||||
SCHEMA_MAPPING.each do |schema, mapped_schema|
|
||||
next unless extra_schemas.delete?(schema)
|
||||
def map_schemas(schemas)
|
||||
schemas = schemas.to_set
|
||||
|
||||
extra_schemas.add(mapped_schema) if mapped_schema
|
||||
SCHEMA_MAPPING.each do |in_schema, mapped_schema|
|
||||
next unless schemas.delete?(in_schema)
|
||||
|
||||
schemas.add(mapped_schema) if mapped_schema
|
||||
end
|
||||
|
||||
extra_schemas
|
||||
schemas
|
||||
end
|
||||
|
||||
def raise_dml_not_allowed_error(message)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ module Gitlab
|
|||
@logger ||= Gitlab::AppLogger
|
||||
end
|
||||
|
||||
PATH_TRAVERSAL_REGEX = %r{(\A(\.{1,2})\z|\A\.\.[/\\]|[/\\]\.\.\z|[/\\]\.\.[/\\]|\n)}
|
||||
|
||||
# Ensure that the relative path will not traverse outside the base directory
|
||||
# We url decode the path to avoid passing invalid paths forward in url encoded format.
|
||||
# Also see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24223#note_284122580
|
||||
|
|
@ -20,9 +22,8 @@ module Gitlab
|
|||
raise PathTraversalAttackError, 'Invalid path' unless path.is_a?(String)
|
||||
|
||||
path = ::Gitlab::Utils.decode_path(path)
|
||||
path_regex = %r{(\A(\.{1,2})\z|\A\.\.[/\\]|[/\\]\.\.\z|[/\\]\.\.[/\\]|\n)}
|
||||
|
||||
if path.match?(path_regex)
|
||||
if path.match?(PATH_TRAVERSAL_REGEX)
|
||||
logger.warn(message: "Potential path traversal attempt detected", path: path.to_s)
|
||||
raise PathTraversalAttackError, 'Invalid path'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9608,16 +9608,16 @@ msgstr ""
|
|||
msgid "CiCatalog|About this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiCatalog|CI/CD Catalog resource"
|
||||
msgid "CiCatalog|CI/CD Catalog"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiCatalog|CI/CD catalog"
|
||||
msgid "CiCatalog|CI/CD Catalog resource"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiCatalog|Create a pipeline component repository and make reusing pipeline configurations faster and easier."
|
||||
msgstr ""
|
||||
|
||||
msgid "CiCatalog|Get started with the CI/CD catalog"
|
||||
msgid "CiCatalog|Get started with the CI/CD Catalog"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiCatalog|Go to the project"
|
||||
|
|
@ -11795,9 +11795,6 @@ msgstr ""
|
|||
msgid "Compute quota:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confidence"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confidential"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -12974,6 +12971,9 @@ msgstr ""
|
|||
msgid "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes."
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new fork"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new issue"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -27994,7 +27994,10 @@ msgstr ""
|
|||
msgid "Maximum number of %{name} (%{count}) exceeded"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of changes (branches or tags) in a single push for which webhooks and services trigger (default is 3)."
|
||||
msgid "Maximum number of changes (branches or tags) in a single push above which a bulk push event is created (default is `3`). Setting to `0` does not disable throttling."
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of changes (branches or tags) in a single push above which webhooks and integrations are not triggered (default is `3`). Setting to `0` does not disable throttling."
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of comments exceeded"
|
||||
|
|
@ -28015,7 +28018,7 @@ msgstr ""
|
|||
msgid "Maximum number of requests per minute for an unauthenticated IP address"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of requests per minute for each raw path (default is 300). Set to 0 to disable throttling."
|
||||
msgid "Maximum number of requests per minute for each raw path (default is `300`). Set to `0` to disable throttling."
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of unique IP addresses per user."
|
||||
|
|
@ -47258,9 +47261,6 @@ msgstr ""
|
|||
msgid "Threshold in bytes at which to reject Sidekiq jobs. Set this to 0 to if you don't want to limit Sidekiq jobs."
|
||||
msgstr ""
|
||||
|
||||
msgid "Threshold number of changes (branches or tags) in a single push above which a bulk push event is created (default is 3)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Throughput"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -47810,6 +47810,12 @@ msgstr ""
|
|||
msgid "To start using GitLab Enterprise Edition, upload the %{codeOpen}.gitlab-license%{codeClose} file or enter the license key you have received from GitLab Inc."
|
||||
msgstr ""
|
||||
|
||||
msgid "To submit your changes in a merge request, create a new fork."
|
||||
msgstr ""
|
||||
|
||||
msgid "To submit your changes in a merge request, switch to one of these forks or create a new fork."
|
||||
msgstr ""
|
||||
|
||||
msgid "To unsubscribe from this issue, please paste the following link into your browser:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -52506,6 +52512,9 @@ msgstr ""
|
|||
msgid "You cannot write to this read-only GitLab instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can’t edit files directly in this project."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ module QA
|
|||
element :delete_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/confirm_fork_modal.vue' do
|
||||
view 'app/assets/javascripts/vue_shared/components/web_ide/confirm_fork_modal.vue' do
|
||||
element :fork_project_button
|
||||
element :confirm_fork_modal
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,19 +58,6 @@ RSpec.describe SearchController, feature_category: :global_search do
|
|||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :show, params: { search: 'hello', scope: 'blobs' * 1000 }
|
||||
end
|
||||
|
||||
context 'when search_rate_limited_scopes feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(search_rate_limited_scopes: false)
|
||||
end
|
||||
|
||||
it 'uses just current_user' do
|
||||
%w[projects blobs users issues merge_requests].each do |scope|
|
||||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :show, params: { search: 'hello', scope: scope }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'uses the right partials depending on scope' do
|
||||
|
|
@ -395,19 +382,6 @@ RSpec.describe SearchController, feature_category: :global_search do
|
|||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :count, params: { search: 'hello', scope: 'blobs' * 1000 }
|
||||
end
|
||||
|
||||
context 'when search_rate_limited_scopes feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(search_rate_limited_scopes: false)
|
||||
end
|
||||
|
||||
it 'uses just current_user' do
|
||||
%w[projects blobs users issues merge_requests].each do |scope|
|
||||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :count, params: { search: 'hello', scope: scope }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'raises an error if search term is missing' do
|
||||
|
|
@ -486,19 +460,6 @@ RSpec.describe SearchController, feature_category: :global_search do
|
|||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :autocomplete, params: { term: 'hello', scope: 'blobs' * 1000 }
|
||||
end
|
||||
|
||||
context 'when search_rate_limited_scopes feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(search_rate_limited_scopes: false)
|
||||
end
|
||||
|
||||
it 'uses just current_user' do
|
||||
%w[projects blobs users issues merge_requests].each do |scope|
|
||||
expect(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(:search_rate_limit, scope: [user])
|
||||
get :autocomplete, params: { term: 'hello', scope: scope }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Project (GraphQL fixtures)', feature_category: :groups_and_projects do
|
||||
describe GraphQL::Query, type: :request do
|
||||
include ApiHelpers
|
||||
include GraphqlHelpers
|
||||
include JavaScriptFixturesHelpers
|
||||
include ProjectForksHelper
|
||||
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
describe 'writable forks' do
|
||||
writeable_forks_query_path = 'vue_shared/components/web_ide/get_writable_forks.query.graphql'
|
||||
|
||||
let(:query) { get_graphql_query_as_string(writeable_forks_query_path) }
|
||||
|
||||
subject { post_graphql(query, current_user: current_user, variables: { projectPath: project.full_path }) }
|
||||
|
||||
before do
|
||||
project.add_developer(current_user)
|
||||
end
|
||||
|
||||
context 'with none' do
|
||||
it "graphql/#{writeable_forks_query_path}_none.json" do
|
||||
subject
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with some' do
|
||||
let_it_be(:fork1) { fork_project(project, nil, repository: true) }
|
||||
let_it_be(:fork2) { fork_project(project, nil, repository: true) }
|
||||
|
||||
before_all do
|
||||
fork1.add_developer(current_user)
|
||||
fork2.add_developer(current_user)
|
||||
end
|
||||
|
||||
it "graphql/#{writeable_forks_query_path}_some.json" do
|
||||
subject
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -12,8 +12,8 @@ describe('MR Widget App', () => {
|
|||
});
|
||||
};
|
||||
|
||||
it('does not mount if widgets array is empty', () => {
|
||||
it('renders widget container', () => {
|
||||
createComponent();
|
||||
expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(false);
|
||||
expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import getStateQueryResponse from 'test_fixtures/graphql/merge_requests/get_stat
|
|||
import readyToMergeResponse from 'test_fixtures/graphql/merge_requests/states/ready_to_merge.query.graphql.json';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { securityReportMergeRequestDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data';
|
||||
import api from '~/api';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK, HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
|
|
@ -30,7 +29,6 @@ import Approvals from '~/vue_merge_request_widget/components/approvals/approvals
|
|||
import Preparing from '~/vue_merge_request_widget/components/states/mr_widget_preparing.vue';
|
||||
import WidgetContainer from '~/vue_merge_request_widget/components/widget/app.vue';
|
||||
import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
|
||||
import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql';
|
||||
import getStateQuery from '~/vue_merge_request_widget/queries/get_state.query.graphql';
|
||||
import getStateSubscription from '~/vue_merge_request_widget/queries/get_state.subscription.graphql';
|
||||
import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
|
||||
|
|
@ -76,11 +74,6 @@ describe('MrWidgetOptions', () => {
|
|||
const COLLABORATION_MESSAGE = 'Members who can merge are allowed to add commits';
|
||||
const findApprovalsWidget = () => wrapper.findComponent(Approvals);
|
||||
const findPreparingWidget = () => wrapper.findComponent(Preparing);
|
||||
const findWidgetContainer = () => wrapper.findComponent(WidgetContainer);
|
||||
const findExtensionToggleButton = () =>
|
||||
wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]');
|
||||
const findExtensionLink = (linkHref) =>
|
||||
wrapper.find(`[data-testid="widget-extension"] [href="${linkHref}"]`);
|
||||
|
||||
beforeEach(() => {
|
||||
gl.mrWidgetData = { ...mockData };
|
||||
|
|
@ -163,9 +156,13 @@ describe('MrWidgetOptions', () => {
|
|||
return axios.waitForAll();
|
||||
};
|
||||
|
||||
const findExtensionToggleButton = () =>
|
||||
wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]');
|
||||
const findExtensionLink = (linkHref) =>
|
||||
wrapper.find(`[data-testid="widget-extension"] [href="${linkHref}"]`);
|
||||
const findSuggestPipeline = () => wrapper.find('[data-testid="mr-suggest-pipeline"]');
|
||||
const findSuggestPipelineButton = () => findSuggestPipeline().find('button');
|
||||
const findSecurityMrWidget = () => wrapper.find('[data-testid="security-mr-widget"]');
|
||||
const findWidgetContainer = () => wrapper.findComponent(WidgetContainer);
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -870,47 +867,6 @@ describe('MrWidgetOptions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('security widget', () => {
|
||||
const setup = (hasPipeline) => {
|
||||
const mrData = {
|
||||
...mockData,
|
||||
...(hasPipeline ? {} : { pipeline: null }),
|
||||
};
|
||||
|
||||
// Override top-level mocked requests, which always use a fresh copy of
|
||||
// mockData, which always includes the full pipeline object.
|
||||
mock.onGet(mockData.merge_request_widget_path).reply(() => [HTTP_STATUS_OK, mrData]);
|
||||
mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [HTTP_STATUS_OK, mrData]);
|
||||
|
||||
return createComponent(mrData, {
|
||||
apolloMock: [
|
||||
[
|
||||
securityReportMergeRequestDownloadPathsQuery,
|
||||
jest
|
||||
.fn()
|
||||
.mockResolvedValue({ data: securityReportMergeRequestDownloadPathsQueryResponse }),
|
||||
],
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
describe('with a pipeline', () => {
|
||||
it('renders the security widget', async () => {
|
||||
await setup(true);
|
||||
|
||||
expect(findSecurityMrWidget().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with no pipeline', () => {
|
||||
it('does not render the security widget', async () => {
|
||||
await setup(false);
|
||||
|
||||
expect(findSecurityMrWidget().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('suggestPipeline', () => {
|
||||
beforeEach(() => {
|
||||
mock.onAny().reply(HTTP_STATUS_OK);
|
||||
|
|
@ -1179,7 +1135,7 @@ describe('MrWidgetOptions', () => {
|
|||
await nextTick();
|
||||
await waitForPromises();
|
||||
|
||||
expect(Sentry.captureException).toHaveBeenCalledTimes(2);
|
||||
expect(Sentry.captureException).toHaveBeenCalledTimes(1);
|
||||
expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error'));
|
||||
expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed');
|
||||
});
|
||||
|
|
@ -1271,18 +1227,12 @@ describe('MrWidgetOptions', () => {
|
|||
expect(api.trackRedisCounterEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('widget container', () => {
|
||||
it('should not be displayed when the refactor_security_extension feature flag is turned off', () => {
|
||||
createComponent();
|
||||
expect(findWidgetContainer().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be displayed when the refactor_security_extension feature flag is turned on', () => {
|
||||
window.gon.features.refactorSecurityExtension = true;
|
||||
createComponent();
|
||||
expect(findWidgetContainer().exists()).toBe(true);
|
||||
});
|
||||
describe('widget container', () => {
|
||||
it('renders the widget container when there is MR data', async () => {
|
||||
await createComponent(mockData);
|
||||
expect(findWidgetContainer().props('mr')).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlModal } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import getNoWritableForksResponse from 'test_fixtures/graphql/vue_shared/components/web_ide/get_writable_forks.query.graphql_none.json';
|
||||
import getSomeWritableForksResponse from 'test_fixtures/graphql/vue_shared/components/web_ide/get_writable_forks.query.graphql_some.json';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ConfirmForkModal, { i18n } from '~/vue_shared/components/confirm_fork_modal.vue';
|
||||
import ConfirmForkModal, { i18n } from '~/vue_shared/components/web_ide/confirm_fork_modal.vue';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import getWritableForksQuery from '~/vue_shared/components/web_ide/get_writable_forks.query.graphql';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
describe('vue_shared/components/confirm_fork_modal', () => {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
let wrapper = null;
|
||||
|
||||
const forkPath = '/fake/fork/path';
|
||||
|
|
@ -13,13 +22,18 @@ describe('vue_shared/components/confirm_fork_modal', () => {
|
|||
const findModalProp = (prop) => findModal().props(prop);
|
||||
const findModalActionProps = () => findModalProp('actionPrimary');
|
||||
|
||||
const createComponent = (props = {}) =>
|
||||
shallowMountExtended(ConfirmForkModal, {
|
||||
const createComponent = (props = {}, getWritableForksResponse = getNoWritableForksResponse) => {
|
||||
const fakeApollo = createMockApollo([
|
||||
[getWritableForksQuery, jest.fn().mockResolvedValue(getWritableForksResponse)],
|
||||
]);
|
||||
return shallowMountExtended(ConfirmForkModal, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
apolloProvider: fakeApollo,
|
||||
});
|
||||
};
|
||||
|
||||
describe('visible = false', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -73,4 +87,45 @@ describe('vue_shared/components/confirm_fork_modal', () => {
|
|||
expect(wrapper.emitted('change')).toEqual([[false]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('writable forks', () => {
|
||||
describe('when loading', () => {
|
||||
it('shows loading spinner', () => {
|
||||
wrapper = createComponent();
|
||||
|
||||
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with no writable forks', () => {
|
||||
it('contains `newForkMessage`', async () => {
|
||||
wrapper = createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.text()).toContain(i18n.newForkMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with writable forks', () => {
|
||||
it('contains `existingForksMessage`', async () => {
|
||||
wrapper = createComponent(null, getSomeWritableForksResponse);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.text()).toContain(i18n.existingForksMessage);
|
||||
});
|
||||
|
||||
it('renders links to the forks', async () => {
|
||||
wrapper = createComponent(null, getSomeWritableForksResponse);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
const forks = getSomeWritableForksResponse.data.project.visibleForks.nodes;
|
||||
|
||||
expect(wrapper.findByText(forks[0].fullPath).attributes('href')).toBe(forks[0].webUrl);
|
||||
expect(wrapper.findByText(forks[1].fullPath).attributes('href')).toBe(forks[1].webUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { nextTick } from 'vue';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
||||
import getWritableForksResponse from 'test_fixtures/graphql/vue_shared/components/web_ide/get_writable_forks.query.graphql_none.json';
|
||||
import ActionsButton from '~/vue_shared/components/actions_button.vue';
|
||||
import WebIdeLink, { i18n } from '~/vue_shared/components/web_ide_link.vue';
|
||||
import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue';
|
||||
import ConfirmForkModal from '~/vue_shared/components/web_ide/confirm_fork_modal.vue';
|
||||
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import getWritableForksQuery from '~/vue_shared/components/web_ide/get_writable_forks.query.graphql';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
|
||||
|
|
@ -77,9 +81,14 @@ const ACTION_PIPELINE_EDITOR = {
|
|||
};
|
||||
|
||||
describe('vue_shared/components/web_ide_link', () => {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
let wrapper;
|
||||
|
||||
function createComponent(props, { mountFn = shallowMountExtended, glFeatures = {} } = {}) {
|
||||
const fakeApollo = createMockApollo([
|
||||
[getWritableForksQuery, jest.fn().mockResolvedValue(getWritableForksResponse)],
|
||||
]);
|
||||
wrapper = mountFn(WebIdeLink, {
|
||||
propsData: {
|
||||
editUrl: TEST_EDIT_URL,
|
||||
|
|
@ -102,6 +111,7 @@ describe('vue_shared/components/web_ide_link', () => {
|
|||
</div>`,
|
||||
}),
|
||||
},
|
||||
apolloProvider: fakeApollo,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,14 @@ RSpec.describe GitlabSchema.types['User'], feature_category: :user_profile do
|
|||
profileEnableGitpodPath
|
||||
savedReplies
|
||||
savedReply
|
||||
user_achievements
|
||||
userAchievements
|
||||
bio
|
||||
linkedin
|
||||
twitter
|
||||
discord
|
||||
organization
|
||||
jobTitle
|
||||
createdAt
|
||||
]
|
||||
|
||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||
|
|
|
|||
|
|
@ -271,4 +271,42 @@ RSpec.describe TreeHelper do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.fork_modal_options' do
|
||||
let_it_be(:blob) { project.repository.blob_at('refs/heads/master', @path) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
subject { helper.fork_modal_options(project, blob) }
|
||||
|
||||
it 'returns correct fork path' do
|
||||
expect(subject).to match a_hash_including(fork_path: '/namespace1/project-1/-/forks/new', fork_modal_id: nil)
|
||||
end
|
||||
|
||||
context 'when show_edit_button true' do
|
||||
before do
|
||||
allow(helper).to receive(:show_edit_button?).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns correct fork path and modal id' do
|
||||
expect(subject).to match a_hash_including(
|
||||
fork_path: '/namespace1/project-1/-/forks/new',
|
||||
fork_modal_id: 'modal-confirm-fork-edit')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when show_web_ide_button true' do
|
||||
before do
|
||||
allow(helper).to receive(:show_web_ide_button?).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns correct fork path and modal id' do
|
||||
expect(subject).to match a_hash_including(
|
||||
fork_path: '/namespace1/project-1/-/forks/new',
|
||||
fork_modal_id: 'modal-confirm-fork-webide')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::DataBuilder::Pipeline do
|
||||
RSpec.describe Gitlab::DataBuilder::Pipeline, feature_category: :continuous_integration do
|
||||
let_it_be(:user) { create(:user, :public_email) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
|
|
@ -26,6 +26,7 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
|
|||
|
||||
it 'has correct attributes', :aggregate_failures do
|
||||
expect(attributes).to be_a(Hash)
|
||||
expect(attributes[:name]).to be_nil
|
||||
expect(attributes[:ref]).to eq(pipeline.ref)
|
||||
expect(attributes[:sha]).to eq(pipeline.sha)
|
||||
expect(attributes[:tag]).to eq(pipeline.tag)
|
||||
|
|
@ -54,6 +55,16 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
|
|||
expect(data[:source_pipeline]).to be_nil
|
||||
end
|
||||
|
||||
context 'pipeline with metadata' do
|
||||
let_it_be_with_reload(:pipeline_metadata) do
|
||||
create(:ci_pipeline_metadata, pipeline: pipeline, name: "My Pipeline")
|
||||
end
|
||||
|
||||
it 'has pipeline name', :aggregate_failures do
|
||||
expect(attributes[:name]).to eq("My Pipeline")
|
||||
end
|
||||
end
|
||||
|
||||
context 'build with runner' do
|
||||
let_it_be(:tag_names) { %w(tag-1 tag-2) }
|
||||
let_it_be(:ci_runner) { create(:ci_runner, tag_list: tag_names.map { |n| ActsAsTaggableOn::Tag.create!(name: n) }) }
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ RSpec.describe Gitlab::Database::GitlabSchema, feature_category: :database do
|
|||
end
|
||||
|
||||
describe '.table_schemas!' do
|
||||
let(:tables) { %w[namespaces projects ci_builds] }
|
||||
let(:tables) { %w[projects issues ci_builds] }
|
||||
|
||||
subject { described_class.table_schemas!(tables) }
|
||||
|
||||
|
|
@ -199,4 +199,82 @@ RSpec.describe Gitlab::Database::GitlabSchema, feature_category: :database do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when testing cross schema access' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:all_gitlab_schemas).and_return(
|
||||
[
|
||||
Gitlab::Database::GitlabSchemaInfo.new(
|
||||
name: "gitlab_main_clusterwide",
|
||||
allow_cross_joins: %i[gitlab_shared gitlab_main],
|
||||
allow_cross_transactions: %i[gitlab_internal gitlab_shared gitlab_main],
|
||||
allow_cross_foreign_keys: %i[gitlab_main]
|
||||
),
|
||||
Gitlab::Database::GitlabSchemaInfo.new(
|
||||
name: "gitlab_main",
|
||||
allow_cross_joins: %i[gitlab_shared],
|
||||
allow_cross_transactions: %i[gitlab_internal gitlab_shared],
|
||||
allow_cross_foreign_keys: %i[]
|
||||
),
|
||||
Gitlab::Database::GitlabSchemaInfo.new(
|
||||
name: "gitlab_ci",
|
||||
allow_cross_joins: %i[gitlab_shared],
|
||||
allow_cross_transactions: %i[gitlab_internal gitlab_shared],
|
||||
allow_cross_foreign_keys: %i[]
|
||||
)
|
||||
].index_by(&:name)
|
||||
)
|
||||
end
|
||||
|
||||
describe '.cross_joins_allowed?' do
|
||||
where(:schemas, :result) do
|
||||
%i[] | true
|
||||
%i[gitlab_main_clusterwide gitlab_main] | true
|
||||
%i[gitlab_main_clusterwide gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_main gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_internal] | false
|
||||
%i[gitlab_main gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_main gitlab_shared] | true
|
||||
%i[gitlab_main_clusterwide gitlab_shared] | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { expect(described_class.cross_joins_allowed?(schemas)).to eq(result) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.cross_transactions_allowed?' do
|
||||
where(:schemas, :result) do
|
||||
%i[] | true
|
||||
%i[gitlab_main_clusterwide gitlab_main] | true
|
||||
%i[gitlab_main_clusterwide gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_main gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_internal] | true
|
||||
%i[gitlab_main gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_main gitlab_shared] | true
|
||||
%i[gitlab_main_clusterwide gitlab_shared] | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { expect(described_class.cross_transactions_allowed?(schemas)).to eq(result) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.cross_foreign_key_allowed?' do
|
||||
where(:schemas, :result) do
|
||||
%i[] | false
|
||||
%i[gitlab_main_clusterwide gitlab_main] | true
|
||||
%i[gitlab_main_clusterwide gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_internal] | false
|
||||
%i[gitlab_main gitlab_ci] | false
|
||||
%i[gitlab_main_clusterwide gitlab_shared] | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { expect(described_class.cross_foreign_key_allowed?(schemas)).to eq(result) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ RSpec.describe 'cross-database foreign keys' do
|
|||
end
|
||||
|
||||
def is_cross_db?(fk_record)
|
||||
Gitlab::Database::GitlabSchema.table_schemas!([fk_record.from_table, fk_record.to_table]).many?
|
||||
table_schemas = Gitlab::Database::GitlabSchema.table_schemas!([fk_record.from_table, fk_record.to_table])
|
||||
|
||||
!Gitlab::Database::GitlabSchema.cross_foreign_key_allowed?(table_schemas)
|
||||
end
|
||||
|
||||
it 'onlies have allowed list of cross-database foreign keys', :aggregate_failures do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas, query_analyzers: false do
|
||||
RSpec.describe Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas,
|
||||
query_analyzers: false, feature_category: :database do
|
||||
let(:analyzer) { described_class }
|
||||
|
||||
context 'properly analyzes queries' do
|
||||
|
|
@ -15,14 +16,38 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas, query_a
|
|||
expected_allowed_gitlab_schemas: {
|
||||
no_schema: :dml_not_allowed,
|
||||
gitlab_main: :success,
|
||||
gitlab_main_clusterwide: :success,
|
||||
gitlab_main_cell: :success,
|
||||
gitlab_ci: :dml_access_denied # cross-schema access
|
||||
}
|
||||
},
|
||||
"for INSERT" => {
|
||||
"for SELECT on namespaces" => {
|
||||
sql: "SELECT 1 FROM namespaces",
|
||||
expected_allowed_gitlab_schemas: {
|
||||
no_schema: :dml_not_allowed,
|
||||
gitlab_main: :success,
|
||||
gitlab_main_clusterwide: :success,
|
||||
gitlab_main_cell: :success,
|
||||
gitlab_ci: :dml_access_denied # cross-schema access
|
||||
}
|
||||
},
|
||||
"for INSERT on projects" => {
|
||||
sql: "INSERT INTO projects VALUES (1)",
|
||||
expected_allowed_gitlab_schemas: {
|
||||
no_schema: :dml_not_allowed,
|
||||
gitlab_main: :success,
|
||||
gitlab_main_clusterwide: :success,
|
||||
gitlab_main_cell: :success,
|
||||
gitlab_ci: :dml_access_denied # cross-schema access
|
||||
}
|
||||
},
|
||||
"for INSERT on namespaces" => {
|
||||
sql: "INSERT INTO namespaces VALUES (1)",
|
||||
expected_allowed_gitlab_schemas: {
|
||||
no_schema: :dml_not_allowed,
|
||||
gitlab_main: :success,
|
||||
gitlab_main_clusterwide: :success,
|
||||
gitlab_main_cell: :success,
|
||||
gitlab_ci: :dml_access_denied # cross-schema access
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -44,6 +44,18 @@ RSpec.describe Gitlab::PathTraversal, feature_category: :shared do
|
|||
expect { check_path_traversal!('foo\\..') }.to raise_error(/Invalid path/)
|
||||
end
|
||||
|
||||
it 'detects path traversal in string with encoded chars' do
|
||||
expect { check_path_traversal!('foo%2F..%2Fbar') }.to raise_error(/Invalid path/)
|
||||
expect { check_path_traversal!('foo%2F%2E%2E%2Fbar') }.to raise_error(/Invalid path/)
|
||||
end
|
||||
|
||||
it 'detects double encoded chars' do
|
||||
expect { check_path_traversal!('foo%252F..%2Fbar') }
|
||||
.to raise_error(Gitlab::Utils::DoubleEncodingError, /is not allowed/)
|
||||
expect { check_path_traversal!('foo%252F%2E%2E%2Fbar') }
|
||||
.to raise_error(Gitlab::Utils::DoubleEncodingError, /is not allowed/)
|
||||
end
|
||||
|
||||
it 'does nothing for a safe string' do
|
||||
expect(check_path_traversal!('./foo')).to eq('./foo')
|
||||
expect(check_path_traversal!('.test/foo')).to eq('.test/foo')
|
||||
|
|
|
|||
|
|
@ -120,9 +120,9 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
|
|||
# Because test cases are run inside a transaction, if any query raise and error all queries that follows
|
||||
# it are automatically canceled by PostgreSQL, to avoid that problem, and to provide exhaustive information
|
||||
# about every metric, queries are wrapped explicitly in sub transactions.
|
||||
table = PgQuery.parse(query).tables.first
|
||||
gitlab_schema = Gitlab::Database::GitlabSchema.tables_to_schema[table]
|
||||
base_model = gitlab_schema == :gitlab_main ? ApplicationRecord : Ci::ApplicationRecord
|
||||
table_name = PgQuery.parse(query).tables.first
|
||||
gitlab_schema = Gitlab::Database::GitlabSchema.table_schema!(table_name)
|
||||
base_model = Gitlab::Database.schemas_to_base_models.fetch(gitlab_schema).first
|
||||
|
||||
base_model.transaction do
|
||||
base_model.connection.execute(query)&.first&.values&.first
|
||||
|
|
|
|||
|
|
@ -15,6 +15,46 @@ RSpec.describe PlanLimits do
|
|||
describe 'validations' do
|
||||
it { is_expected.to validate_numericality_of(:notification_limit).only_integer.is_greater_than_or_equal_to(0) }
|
||||
it { is_expected.to validate_numericality_of(:enforcement_limit).only_integer.is_greater_than_or_equal_to(0) }
|
||||
|
||||
describe 'limits_history' do
|
||||
context 'when does not match the JSON schema' do
|
||||
it 'does not allow invalid json' do
|
||||
expect(subject).not_to allow_value({
|
||||
invalid_key: {
|
||||
enforcement_limit: [
|
||||
{
|
||||
username: 'mhamda',
|
||||
timestamp: 1686140606000,
|
||||
value: 5000
|
||||
}
|
||||
],
|
||||
another_invalid: [
|
||||
{
|
||||
username: 'mhamda',
|
||||
timestamp: 1686140606000,
|
||||
value: 5000
|
||||
}
|
||||
]
|
||||
}
|
||||
}).for(:limits_history)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when matches the JSON schema' do
|
||||
it 'allows valid json' do
|
||||
expect(subject).to allow_value({
|
||||
enforcement_limit: [
|
||||
{
|
||||
user_id: 1,
|
||||
username: 'mhamda',
|
||||
timestamp: 1686140606000,
|
||||
value: 5000
|
||||
}
|
||||
]
|
||||
}).for(:limits_history)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exceeded?' do
|
||||
|
|
@ -233,12 +273,17 @@ RSpec.describe PlanLimits do
|
|||
%w[dashboard_limit_enabled_at]
|
||||
end
|
||||
|
||||
it "has positive values for enabled limits" do
|
||||
let(:history_columns) do
|
||||
%w[limits_history]
|
||||
end
|
||||
|
||||
it 'has positive values for enabled limits' do
|
||||
attributes = plan_limits.attributes
|
||||
attributes = attributes.except(described_class.primary_key)
|
||||
attributes = attributes.except(described_class.reflections.values.map(&:foreign_key))
|
||||
attributes = attributes.except(*columns_with_zero)
|
||||
attributes = attributes.except(*datetime_columns)
|
||||
attributes = attributes.except(*history_columns)
|
||||
|
||||
expect(attributes).to all(include(be_positive))
|
||||
end
|
||||
|
|
@ -256,4 +301,95 @@ RSpec.describe PlanLimits do
|
|||
expect(plan_limits.dashboard_storage_limit_enabled?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#log_limits_changes', :freeze_time do
|
||||
let(:user) { create(:user) }
|
||||
let(:plan_limits) { create(:plan_limits) }
|
||||
let(:current_timestamp) { Time.current.utc.to_i }
|
||||
let(:history) { plan_limits.limits_history }
|
||||
|
||||
it 'logs a single attribute change' do
|
||||
plan_limits.log_limits_changes(user, enforcement_limit: 5_000)
|
||||
|
||||
expect(history).to eq(
|
||||
{ 'enforcement_limit' => [{ 'user_id' => user.id, 'username' => user.username,
|
||||
'timestamp' => current_timestamp, 'value' => 5_000 }] }
|
||||
)
|
||||
end
|
||||
|
||||
it 'logs multiple attribute changes' do
|
||||
plan_limits.log_limits_changes(user, enforcement_limit: 10_000, notification_limit: 20_000)
|
||||
|
||||
expect(history).to eq(
|
||||
{ 'enforcement_limit' => [{ 'user_id' => user.id, 'username' => user.username,
|
||||
'timestamp' => current_timestamp, 'value' => 10_000 }],
|
||||
'notification_limit' => [{ 'user_id' => user.id, 'username' => user.username,
|
||||
'timestamp' => current_timestamp,
|
||||
'value' => 20_000 }] }
|
||||
)
|
||||
end
|
||||
|
||||
it 'allows logging dashboard_limit_enabled_at from console (without user)' do
|
||||
plan_limits.log_limits_changes(nil, dashboard_limit_enabled_at: current_timestamp)
|
||||
|
||||
expect(history).to eq(
|
||||
{ 'dashboard_limit_enabled_at' => [{ 'user_id' => nil, 'username' => nil, 'timestamp' => current_timestamp,
|
||||
'value' => current_timestamp }] }
|
||||
)
|
||||
end
|
||||
|
||||
context 'with previous history avilable' do
|
||||
let(:plan_limits) do
|
||||
create(:plan_limits,
|
||||
limits_history: { 'enforcement_limit' => [{ user_id: user.id, username: user.username,
|
||||
timestamp: current_timestamp,
|
||||
value: 20_000 },
|
||||
{ user_id: user.id, username: user.username, timestamp: current_timestamp,
|
||||
value: 50_000 }] })
|
||||
end
|
||||
|
||||
it 'appends to it' do
|
||||
plan_limits.log_limits_changes(user, enforcement_limit: 60_000)
|
||||
expect(history).to eq(
|
||||
{
|
||||
'enforcement_limit' => [
|
||||
{ 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp,
|
||||
'value' => 20_000 },
|
||||
{ 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp,
|
||||
'value' => 50_000 },
|
||||
{ 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp, 'value' => 60_000 }
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#limit_attribute_changes', :freeze_time do
|
||||
let(:user) { create(:user) }
|
||||
let(:current_timestamp) { Time.current.utc.to_i }
|
||||
let(:plan_limits) do
|
||||
create(:plan_limits,
|
||||
limits_history: { 'enforcement_limit' => [
|
||||
{ user_id: user.id, username: user.username, timestamp: current_timestamp,
|
||||
value: 20_000 }, { user_id: user.id, username: user.username, timestamp: current_timestamp,
|
||||
value: 50_000 }
|
||||
] })
|
||||
end
|
||||
|
||||
it 'returns an empty array for attribute with no changes' do
|
||||
changes = plan_limits.limit_attribute_changes(:notification_limit)
|
||||
|
||||
expect(changes).to eq([])
|
||||
end
|
||||
|
||||
it 'returns the changes for a specific attribute' do
|
||||
changes = plan_limits.limit_attribute_changes(:enforcement_limit)
|
||||
|
||||
expect(changes).to eq(
|
||||
[{ timestamp: current_timestamp, value: 20_000, username: user.username, user_id: user.id },
|
||||
{ timestamp: current_timestamp, value: 50_000, username: user.username, user_id: user.id }]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ module Database
|
|||
|
||||
ALLOW_THREAD_KEY = :allow_cross_joins_across_databases
|
||||
ALLOW_ANNOTATE_KEY = ALLOW_THREAD_KEY.to_s.freeze
|
||||
IGNORED_SCHEMAS = %i[gitlab_shared gitlab_internal].freeze
|
||||
|
||||
def self.validate_cross_joins!(sql)
|
||||
return if Thread.current[ALLOW_THREAD_KEY] || sql.include?(ALLOW_ANNOTATE_KEY)
|
||||
|
|
@ -41,9 +40,8 @@ module Database
|
|||
end
|
||||
|
||||
schemas = ::Gitlab::Database::GitlabSchema.table_schemas!(tables)
|
||||
schemas.subtract(IGNORED_SCHEMAS)
|
||||
|
||||
if schemas.many?
|
||||
unless ::Gitlab::Database::GitlabSchema.cross_joins_allowed?(schemas)
|
||||
Thread.current[:has_cross_join_exception] = true
|
||||
raise CrossJoinAcrossUnsupportedTablesError,
|
||||
"Unsupported cross-join across '#{tables.join(", ")}' querying '#{schemas.to_a.join(", ")}' discovered " \
|
||||
|
|
|
|||
|
|
@ -42,7 +42,14 @@ RSpec.shared_examples "a user type with merge request interaction type" do
|
|||
profileEnableGitpodPath
|
||||
savedReplies
|
||||
savedReply
|
||||
user_achievements
|
||||
userAchievements
|
||||
bio
|
||||
linkedin
|
||||
twitter
|
||||
discord
|
||||
organization
|
||||
jobTitle
|
||||
createdAt
|
||||
]
|
||||
|
||||
# TODO: 'workspaces' needs to be included, but only when this spec is run in EE context, to account for the
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Vendored Gems
|
||||
|
||||
This folder is used to store externally pulled dependencies.
|
||||
|
||||
## Development guidelines
|
||||
|
||||
The data stored in this directory should adhere to the following rules:
|
||||
|
||||
- MUST: Contain `GITLAB.md` to indicate where this data was pulled
|
||||
from with a description of what changes were made.
|
||||
- MUST: Be added to `.gitlab/ci/vendored-gems.gitlab-ci.yml`.
|
||||
- MUST NOT: Reference source code from outside of `vendor/gems/` or `require_relative "../../lib"`.
|
||||
- MUST NOT: Require other gems that would result in circular dependencies.
|
||||
- SHOULD NOT: Be published to RubyGems under our name.
|
||||
- SHOULD: Be used with `gem <name>, path: "vendor/mail-smtp_pool"`.
|
||||
- RECOMMENDED: Be added to `CODEOWNERS`.
|
||||
- MAY: Reference other Gems in `vendor/gems/` with `gem <name>, path: "../mail-smtp_pool"`.
|
||||
- MAY: Contain our patches to make them work with the GitLab monorepo, for example to continue to support deprecated or unmaintained dependences.
|
||||
Loading…
Reference in New Issue