Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8f55aaede8
commit
7f73b108d4
|
@ -2290,6 +2290,7 @@
|
|||
rules:
|
||||
- <<: *if-merge-request
|
||||
changes: ["**/*click_house*"]
|
||||
- <<: *if-merge-request-labels-run-all-rspec
|
||||
|
||||
#########################
|
||||
# Static analysis rules #
|
||||
|
|
|
@ -2,54 +2,6 @@
|
|||
# Cop supports --autocorrect.
|
||||
Style/RedundantFreeze:
|
||||
Exclude:
|
||||
- 'app/controllers/help_controller.rb'
|
||||
- 'app/controllers/import/bitbucket_server_controller.rb'
|
||||
- 'app/finders/issuable_finder.rb'
|
||||
- 'app/finders/repositories/changelog_commits_finder.rb'
|
||||
- 'app/helpers/auth_helper.rb'
|
||||
- 'app/helpers/colors_helper.rb'
|
||||
- 'app/helpers/sidekiq_helper.rb'
|
||||
- 'app/models/application_setting_implementation.rb'
|
||||
- 'app/models/badge.rb'
|
||||
- 'app/models/blob_viewer/go_mod.rb'
|
||||
- 'app/models/ci/runner.rb'
|
||||
- 'app/models/commit.rb'
|
||||
- 'app/models/commit_range.rb'
|
||||
- 'app/models/concerns/ci/maskable.rb'
|
||||
- 'app/models/concerns/pg_full_text_searchable.rb'
|
||||
- 'app/models/concerns/redactable.rb'
|
||||
- 'app/models/concerns/taskable.rb'
|
||||
- 'app/models/custom_emoji.rb'
|
||||
- 'app/models/environment_status.rb'
|
||||
- 'app/models/error_tracking/project_error_tracking_setting.rb'
|
||||
- 'app/models/hooks/web_hook.rb'
|
||||
- 'app/models/integrations/apple_app_store.rb'
|
||||
- 'app/models/integrations/chat_message/base_message.rb'
|
||||
- 'app/models/integrations/confluence.rb'
|
||||
- 'app/models/integrations/datadog.rb'
|
||||
- 'app/models/integrations/discord.rb'
|
||||
- 'app/models/integrations/teamcity.rb'
|
||||
- 'app/models/license_template.rb'
|
||||
- 'app/models/members/group_member.rb'
|
||||
- 'app/models/members/project_member.rb'
|
||||
- 'app/models/merge_request.rb'
|
||||
- 'app/models/namespaces/randomized_suffix_path.rb'
|
||||
- 'app/models/note.rb'
|
||||
- 'app/models/packages/debian.rb'
|
||||
- 'app/models/packages/debian/file_entry.rb'
|
||||
- 'app/models/personal_access_token.rb'
|
||||
- 'app/models/releases/link.rb'
|
||||
- 'app/models/snippet_repository.rb'
|
||||
- 'app/models/terraform/state.rb'
|
||||
- 'app/services/clusters/agent_tokens/track_usage_service.rb'
|
||||
- 'app/services/import/validate_remote_git_endpoint_service.rb'
|
||||
- 'app/services/issues/base_service.rb'
|
||||
- 'app/services/projects/import_error_filter.rb'
|
||||
- 'app/services/projects/lfs_pointers/lfs_object_download_list_service.rb'
|
||||
- 'app/uploaders/file_uploader.rb'
|
||||
- 'app/validators/certificate_fingerprint_validator.rb'
|
||||
- 'app/validators/json_schema_validator.rb'
|
||||
- 'app/validators/line_code_validator.rb'
|
||||
- 'lib/api/api.rb'
|
||||
- 'lib/api/debian_group_packages.rb'
|
||||
- 'lib/api/go_proxy.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
f1d2afbee25a73855d931ad2d406e04e2062dc96
|
||||
5688f0d78dfb631f600868b80e8bfa9f13834f65
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -496,7 +496,7 @@ group :test do
|
|||
gem 'gitlab_quality-test_tooling', '~> 0.9.3', require: false
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 4.15'
|
||||
gem 'octokit', '~> 6.0'
|
||||
|
||||
gem 'gitlab-mail_room', '~> 0.0.23', require: 'mail_room'
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
{"name":"crystalball","version":"0.7.0","platform":"ruby","checksum":"6e729f372a5071daec877adb40c5df4cb25fe21f350635e2a9624373fc151ef2"},
|
||||
{"name":"css_parser","version":"1.14.0","platform":"ruby","checksum":"f2ce6148cd505297b07bdbe7a5db4cce5cf530071f9b732b9a23538d6cdc0113"},
|
||||
{"name":"cvss-suite","version":"3.0.1","platform":"ruby","checksum":"b5ca9e9e94032a42fd0dc28c1e305378b62c949e35ed7111fc4a1d76f68ad3f9"},
|
||||
{"name":"danger","version":"8.6.1","platform":"ruby","checksum":"d95eb58b41f68d3aaa9bbef697916b6b4d161a38819517c98562531be75cdfd8"},
|
||||
{"name":"danger","version":"9.3.1","platform":"ruby","checksum":"9070fbac181eb45fb9b69ea25e6ea4faa86796ef33bf8d00346cab4385e51df5"},
|
||||
{"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"},
|
||||
{"name":"database_cleaner","version":"1.7.0","platform":"ruby","checksum":"bdf833c197afac7054015bcde2567c3834c366bbfe6a377c30151ca984b32016"},
|
||||
{"name":"date","version":"3.3.3","platform":"java","checksum":"584e0a582d1eb2207b4eaac089d8a43f2ca10bea02682f286099642f15c56cce"},
|
||||
|
@ -204,7 +204,7 @@
|
|||
{"name":"gettext","version":"3.3.6","platform":"ruby","checksum":"ee6bbd1b2f833ee52d7797fa68acbfecc4726aec6b6280fd7eab92aa0190b413"},
|
||||
{"name":"gettext_i18n_rails","version":"1.11.0","platform":"ruby","checksum":"e19c7e4a256c500f7f38396dca44a282b9838ae278f57c362993a54964b22bbe"},
|
||||
{"name":"gettext_i18n_rails_js","version":"1.3.0","platform":"ruby","checksum":"5d10afe4be3639bff78c50a56768c20f39aecdabc580c08aa45573911c2bd687"},
|
||||
{"name":"git","version":"1.11.0","platform":"ruby","checksum":"7e95ba4da8298a0373ef1a6862aa22007d761f3c8274b675aa787966fecea0f1"},
|
||||
{"name":"git","version":"1.18.0","platform":"ruby","checksum":"c9b80462e4565cd3d7a9ba8440c41d2c52244b17b0dad0bfddb46de70630c465"},
|
||||
{"name":"gitaly","version":"16.3.0.pre.rc1","platform":"ruby","checksum":"55d9cc414a4f3859588f3770bd88d7c67c0f5454a1178b018b7a6f6913674c43"},
|
||||
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
|
||||
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
|
||||
|
@ -406,7 +406,7 @@
|
|||
{"name":"numerizer","version":"0.2.0","platform":"ruby","checksum":"e58076d5ee5370417b7e52d9cb25836d62acd1b8d9a194c308707986c1705d7b"},
|
||||
{"name":"oauth","version":"0.5.6","platform":"ruby","checksum":"4085fe28e0c5e2434135e00a6555294fd2a4ff96a98d1bdecdcd619fc6368dff"},
|
||||
{"name":"oauth2","version":"2.0.9","platform":"ruby","checksum":"b21f9defcf52dc1610e0dfab4c868342173dcd707fd15c777d9f4f04e153f7fb"},
|
||||
{"name":"octokit","version":"4.25.1","platform":"ruby","checksum":"c02092ee82dcdfe84db0e0ea630a70d32becc54245a4f0bacfd21c010df09b96"},
|
||||
{"name":"octokit","version":"6.1.1","platform":"ruby","checksum":"920e4a9d820205f70738f58de6a7e6ef0e2f25b27db954b5806a63105207b0bf"},
|
||||
{"name":"ohai","version":"17.9.0","platform":"ruby","checksum":"c59cf16124c0a6481fb85013ec7ec5b398651b6abed782d3e06ab058ce9a5406"},
|
||||
{"name":"oj","version":"3.13.23","platform":"ruby","checksum":"206dfdc4020ad9974705037f269cfba211d61b7662a58c717cce771829ccef51"},
|
||||
{"name":"oj-introspect","version":"0.7.2","platform":"ruby","checksum":"c415a44567ed2870d8e963a69421d9322128e194fab7867e37e54d5a25d5333d"},
|
||||
|
|
15
Gemfile.lock
15
Gemfile.lock
|
@ -389,18 +389,18 @@ GEM
|
|||
css_parser (1.14.0)
|
||||
addressable
|
||||
cvss-suite (3.0.1)
|
||||
danger (8.6.1)
|
||||
danger (9.3.1)
|
||||
claide (~> 1.0)
|
||||
claide-plugins (>= 0.9.2)
|
||||
colored2 (~> 3.1)
|
||||
cork (~> 0.1)
|
||||
faraday (>= 0.9.0, < 2.0)
|
||||
faraday (>= 0.9.0, < 3.0)
|
||||
faraday-http-cache (~> 2.0)
|
||||
git (~> 1.7)
|
||||
git (~> 1.13)
|
||||
kramdown (~> 2.3)
|
||||
kramdown-parser-gfm (~> 1.0)
|
||||
no_proxy_fix
|
||||
octokit (~> 4.7)
|
||||
octokit (~> 6.0)
|
||||
terminal-table (>= 1, < 4)
|
||||
danger-gitlab (8.0.0)
|
||||
danger
|
||||
|
@ -634,7 +634,8 @@ GEM
|
|||
gettext_i18n_rails (>= 0.7.1)
|
||||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
git (1.11.0)
|
||||
git (1.18.0)
|
||||
addressable (~> 2.8)
|
||||
rchardet (~> 1.8)
|
||||
gitaly (16.3.0.pre.rc1)
|
||||
grpc (~> 1.0)
|
||||
|
@ -1074,7 +1075,7 @@ GEM
|
|||
rack (>= 1.2, < 4)
|
||||
snaky_hash (~> 2.0)
|
||||
version_gem (~> 1.1)
|
||||
octokit (4.25.1)
|
||||
octokit (6.1.1)
|
||||
faraday (>= 1, < 3)
|
||||
sawyer (~> 0.9)
|
||||
ohai (17.9.0)
|
||||
|
@ -1911,7 +1912,7 @@ DEPENDENCIES
|
|||
net-protocol (~> 0.1.3)
|
||||
nokogiri (~> 1.15, >= 1.15.4)
|
||||
oauth2 (~> 2.0)
|
||||
octokit (~> 4.15)
|
||||
octokit (~> 6.0)
|
||||
ohai (~> 17.9)
|
||||
oj (~> 3.13.21)
|
||||
oj-introspect (~> 0.7)
|
||||
|
|
|
@ -33,10 +33,16 @@ export default async function initPipelineDetailsBundle() {
|
|||
|
||||
if (tabsEl) {
|
||||
const { dataset } = tabsEl;
|
||||
const dismissalDescriptions = JSON.parse(dataset.dismissalDescriptions || '{}');
|
||||
const { createAppOptions } = await import('ee_else_ce/ci/pipeline_details/pipeline_tabs');
|
||||
const { createPipelineTabs } = await import('./pipeline_tabs');
|
||||
const { routes } = await import('ee_else_ce/ci/pipeline_details/routes');
|
||||
|
||||
const securityRoute = routes.find((route) => route.path === '/security');
|
||||
if (securityRoute) {
|
||||
securityRoute.props = { dismissalDescriptions };
|
||||
}
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: dataset.pipelinePath,
|
||||
|
|
|
@ -52,7 +52,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-display-flex gl-flex-wrap gl-gap-3">
|
||||
<div class="gl-display-flex gl-flex-wrap gl-gap-3 gl-mb-4">
|
||||
<gl-button
|
||||
v-if="showFileTreeToggle"
|
||||
id="file-tree-toggle"
|
||||
|
|
|
@ -33,7 +33,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<gl-card
|
||||
class="gl-new-card gl-mb-3"
|
||||
class="gl-new-card gl-mb-3 gl-mt-0"
|
||||
header-class="gl-new-card-header"
|
||||
body-class="gl-new-card-body gl-py-4 gl-px-5"
|
||||
>
|
||||
|
|
|
@ -23,9 +23,6 @@ export default {
|
|||
currentProject: {
|
||||
default: () => ({}),
|
||||
},
|
||||
currentBranch: {
|
||||
default: () => ({}),
|
||||
},
|
||||
inputs: {
|
||||
default: () => ({}),
|
||||
},
|
||||
|
@ -39,6 +36,13 @@ export default {
|
|||
default: '',
|
||||
},
|
||||
},
|
||||
props: {
|
||||
currentBranch: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedProject: this.currentProject,
|
||||
|
@ -57,6 +61,12 @@ export default {
|
|||
return this.commitHtml || this.loading || !this.selectedBranch.value;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentBranch(newVal) {
|
||||
this.selectedBranch = newVal;
|
||||
this.fetchCommit();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchCommit();
|
||||
},
|
||||
|
@ -67,6 +77,7 @@ export default {
|
|||
selectBranch(branch) {
|
||||
this.selectedBranch = branch;
|
||||
this.fetchCommit();
|
||||
this.$emit('select-branch', branch.value);
|
||||
},
|
||||
async fetchCommit() {
|
||||
if (!this.selectedBranch.value) return;
|
||||
|
@ -109,6 +120,7 @@ export default {
|
|||
:default="currentBranch"
|
||||
:toggle-class="toggleClass.branch"
|
||||
:qa-selector="branchQaSelector"
|
||||
data-testid="compare-dropdown"
|
||||
@selected="selectBranch"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -70,6 +70,12 @@ export default {
|
|||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
default(newVal) {
|
||||
this.current = newVal;
|
||||
this.selected = newVal.value;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
if (!this.endpoint) return;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export const findTargetBranch = async () => {};
|
|
@ -2,6 +2,8 @@ import Vue from 'vue';
|
|||
|
||||
import { mountMarkdownEditor } from 'ee_else_ce/vue_shared/components/markdown/mount_markdown_editor';
|
||||
|
||||
import { findTargetBranch } from 'ee_else_ce/pages/projects/merge_requests/creations/new/branch_finder';
|
||||
|
||||
import initPipelines from '~/commit/pipelines/pipelines_bundle';
|
||||
import MergeRequest from '~/merge_request';
|
||||
import CompareApp from '~/merge_requests/components/compare_app.vue';
|
||||
|
@ -13,14 +15,15 @@ if (mrNewCompareNode) {
|
|||
const targetCompareEl = document.getElementById('js-target-project-dropdown');
|
||||
const sourceCompareEl = document.getElementById('js-source-project-dropdown');
|
||||
const compareEl = document.querySelector('.js-merge-request-new-compare');
|
||||
const targetBranch = Vue.observable({ name: '' });
|
||||
|
||||
const currentSourceBranch = JSON.parse(sourceCompareEl.dataset.currentBranch);
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: sourceCompareEl,
|
||||
name: 'SourceCompareApp',
|
||||
provide: {
|
||||
currentProject: JSON.parse(sourceCompareEl.dataset.currentProject),
|
||||
currentBranch: JSON.parse(sourceCompareEl.dataset.currentBranch),
|
||||
branchCommitPath: compareEl.dataset.sourceBranchUrl,
|
||||
inputs: {
|
||||
project: {
|
||||
|
@ -42,18 +45,34 @@ if (mrNewCompareNode) {
|
|||
},
|
||||
branchQaSelector: 'source_branch_dropdown',
|
||||
},
|
||||
methods: {
|
||||
async selectedBranch(branchName) {
|
||||
const targetBranchName = await findTargetBranch(branchName);
|
||||
|
||||
if (targetBranchName) {
|
||||
targetBranch.name = targetBranchName;
|
||||
}
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
return h(CompareApp);
|
||||
return h(CompareApp, {
|
||||
props: {
|
||||
currentBranch: currentSourceBranch,
|
||||
},
|
||||
on: {
|
||||
'select-branch': this.selectedBranch,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const currentTargetBranch = JSON.parse(targetCompareEl.dataset.currentBranch);
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: targetCompareEl,
|
||||
name: 'TargetCompareApp',
|
||||
provide: {
|
||||
currentProject: JSON.parse(targetCompareEl.dataset.currentProject),
|
||||
currentBranch: JSON.parse(targetCompareEl.dataset.currentBranch),
|
||||
projectsPath: targetCompareEl.dataset.targetProjectsPath,
|
||||
branchCommitPath: compareEl.dataset.targetBranchUrl,
|
||||
inputs: {
|
||||
|
@ -75,8 +94,17 @@ if (mrNewCompareNode) {
|
|||
branch: 'js-target-branch gl-font-monospace',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentBranch() {
|
||||
if (targetBranch.name) {
|
||||
return { text: targetBranch.name, value: targetBranch.name };
|
||||
}
|
||||
|
||||
return currentTargetBranch;
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
return h(CompareApp);
|
||||
return h(CompareApp, { props: { currentBranch: this.currentBranch } });
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
<script>
|
||||
import { GlButton, GlForm, GlFormGroup, GlFormInputGroup, GlFormInput } from '@gitlab/ui';
|
||||
import {
|
||||
GlButton,
|
||||
GlForm,
|
||||
GlFormGroup,
|
||||
GlFormInputGroup,
|
||||
GlFormInput,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { isEmptyValue, hasMinimumLength, isIntegerGreaterThan, isEmail } from '~/lib/utils/forms';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import {
|
||||
|
@ -23,6 +32,9 @@ import {
|
|||
} from '../custom_email_constants';
|
||||
|
||||
export default {
|
||||
customEmailHelpUrl: helpPagePath('user/project/service_desk/configure.html', {
|
||||
anchor: 'custom-email-address',
|
||||
}),
|
||||
components: {
|
||||
ClipboardButton,
|
||||
GlButton,
|
||||
|
@ -30,6 +42,8 @@ export default {
|
|||
GlFormGroup,
|
||||
GlFormInputGroup,
|
||||
GlFormInput,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
},
|
||||
I18N_FORM_INTRODUCTION_PARAGRAPH,
|
||||
I18N_FORM_CUSTOM_EMAIL_LABEL,
|
||||
|
@ -137,7 +151,19 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ $options.I18N_FORM_INTRODUCTION_PARAGRAPH }}</p>
|
||||
<p>
|
||||
<gl-sprintf :message="$options.I18N_FORM_INTRODUCTION_PARAGRAPH">
|
||||
<template #link="{ content }">
|
||||
<gl-link
|
||||
:href="$options.customEmailHelpUrl"
|
||||
class="gl-display-inline-block"
|
||||
target="_blank"
|
||||
>
|
||||
{{ content }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<gl-form class="js-quick-submit" @submit.prevent="onSubmit">
|
||||
<gl-form-group
|
||||
:label="$options.I18N_FORM_FORWARDING_LABEL"
|
||||
|
|
|
@ -233,6 +233,7 @@ export default {
|
|||
<gl-link
|
||||
:href="$options.FEEDBACK_ISSUE_URL"
|
||||
target="_blank"
|
||||
data-testid="feedback-link"
|
||||
class="gl-text-blue-600 font-size-inherit"
|
||||
>{{ content }}
|
||||
</gl-link>
|
||||
|
|
|
@ -17,7 +17,7 @@ export const I18N_TOAST_ENABLED = s__('ServiceDesk|Custom email enabled.');
|
|||
export const I18N_TOAST_DISABLED = s__('ServiceDesk|Custom email disabled.');
|
||||
|
||||
export const I18N_FORM_INTRODUCTION_PARAGRAPH = s__(
|
||||
'ServiceDesk|Connect a custom email address your customers can use to create Service Desk issues. Forward all emails from your custom email address to the Service Desk email address of this project. GitLab will send Service Desk emails from the custom address on your behalf using your SMTP credentials.',
|
||||
'ServiceDesk|Connect a custom email address your customers can use to create Service Desk issues. Forward all emails from your custom email address to the Service Desk email address of this project. GitLab will send Service Desk emails from the custom address on your behalf using your SMTP credentials. %{linkStart}Learn more about prerequisites and the verification process%{linkEnd}.',
|
||||
);
|
||||
export const I18N_FORM_FORWARDING_LABEL = s__(
|
||||
'ServiceDesk|Service Desk email address to forward emails to',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlAvatar, GlButton, GlIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import {
|
||||
CLICK_MENU_ITEM_ACTION,
|
||||
CLICK_PINNED_MENU_ITEM_ACTION,
|
||||
|
@ -12,7 +12,9 @@ import NavItemRouterLink from './nav_item_router_link.vue';
|
|||
|
||||
export default {
|
||||
i18n: {
|
||||
pin: s__('Navigation|Pin %{title}'),
|
||||
pinItem: s__('Navigation|Pin item'),
|
||||
unpin: s__('Navigation|Unpin %{title}'),
|
||||
unpinItem: s__('Navigation|Unpin item'),
|
||||
},
|
||||
name: 'NavItem',
|
||||
|
@ -143,6 +145,16 @@ export default {
|
|||
avatarShape() {
|
||||
return this.item.avatar_shape || 'rect';
|
||||
},
|
||||
pinAriaLabel() {
|
||||
return sprintf(this.$options.i18n.pin, {
|
||||
title: this.item.title,
|
||||
});
|
||||
},
|
||||
unpinAriaLabel() {
|
||||
return sprintf(this.$options.i18n.unpin, {
|
||||
title: this.item.title,
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.item.is_active) {
|
||||
|
@ -151,26 +163,27 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
togglePointerEvents() {
|
||||
if (this.isMouseIn) {
|
||||
this.canClickPinButton = true;
|
||||
} else {
|
||||
this.canClickPinButton = false;
|
||||
}
|
||||
this.canClickPinButton = this.isMouseIn;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li @mouseenter="isMouseIn = true" @mouseleave="isMouseIn = false">
|
||||
<li
|
||||
class="gl-relative show-on-focus-or-hover--context hide-on-focus-or-hover--context transition-opacity-on-hover--context"
|
||||
data-testid="nav-item"
|
||||
@mouseenter="isMouseIn = true"
|
||||
@mouseleave="isMouseIn = false"
|
||||
>
|
||||
<component
|
||||
:is="navItemLinkComponent"
|
||||
#default="{ isActive }"
|
||||
v-bind="linkProps"
|
||||
class="nav-item-link gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus show-on-focus-or-hover--context"
|
||||
class="gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus show-on-focus-or-hover--control hide-on-focus-or-hover--control"
|
||||
:class="computedLinkClasses"
|
||||
data-qa-selector="nav_item_link"
|
||||
data-testid="nav-item-link"
|
||||
data-qa-selector="nav_item_link"
|
||||
>
|
||||
<div
|
||||
:class="[isActive ? 'gl-opacity-10' : 'gl-opacity-0']"
|
||||
|
@ -204,41 +217,47 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<slot name="actions"></slot>
|
||||
<span v-if="hasPill || isPinnable" class="gl-text-right gl-relative gl-min-w-8">
|
||||
<span v-if="hasPill" class="gl-text-right gl-relative gl-min-w-8">
|
||||
<gl-badge
|
||||
v-if="hasPill"
|
||||
size="sm"
|
||||
variant="neutral"
|
||||
class="gl-bg-t-gray-a-08!"
|
||||
:class="{ 'nav-item-badge gl-absolute gl-right-0 gl-top-2': isPinnable }"
|
||||
:class="{
|
||||
'hide-on-focus-or-hover--target transition-opacity-on-hover--target': isPinnable,
|
||||
}"
|
||||
>
|
||||
{{ pillData }}
|
||||
</gl-badge>
|
||||
<gl-button
|
||||
v-if="isPinnable && !isPinned"
|
||||
v-gl-tooltip.ds500.right.viewport="$options.i18n.pinItem"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
icon="thumbtack"
|
||||
class="show-on-focus-or-hover--target"
|
||||
:class="{ 'gl-pointer-events-none': !canClickPinButton }"
|
||||
:aria-label="$options.i18n.pinItem"
|
||||
@click.prevent="$emit('pin-add', item.id)"
|
||||
@transitionend="togglePointerEvents"
|
||||
/>
|
||||
<gl-button
|
||||
v-else-if="isPinnable && isPinned"
|
||||
v-gl-tooltip.ds500.right.viewport="$options.i18n.unpinItem"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
:aria-label="$options.i18n.unpinItem"
|
||||
icon="thumbtack-solid"
|
||||
class="show-on-focus-or-hover--target"
|
||||
:class="{ 'gl-pointer-events-none': !canClickPinButton }"
|
||||
@click.prevent="$emit('pin-remove', item.id)"
|
||||
@transitionend="togglePointerEvents"
|
||||
/>
|
||||
</span>
|
||||
</component>
|
||||
<template v-if="isPinnable">
|
||||
<gl-button
|
||||
v-if="isPinned"
|
||||
v-gl-tooltip.noninteractive.ds500.right.viewport="$options.i18n.unpinItem"
|
||||
:aria-label="unpinAriaLabel"
|
||||
category="tertiary"
|
||||
class="show-on-focus-or-hover--target transition-opacity-on-hover--target always-animate gl-absolute gl-right-3 gl-top-2"
|
||||
:class="{ 'gl-pointer-events-none': !canClickPinButton }"
|
||||
data-testid="nav-item-unpin"
|
||||
icon="thumbtack-solid"
|
||||
size="small"
|
||||
@click="$emit('pin-remove', item.id)"
|
||||
@transitionend="togglePointerEvents"
|
||||
/>
|
||||
<gl-button
|
||||
v-else
|
||||
v-gl-tooltip.noninteractive.ds500.right.viewport="$options.i18n.pinItem"
|
||||
:aria-label="pinAriaLabel"
|
||||
category="tertiary"
|
||||
class="show-on-focus-or-hover--target transition-opacity-on-hover--target always-animate gl-absolute gl-right-3 gl-top-2"
|
||||
:class="{ 'gl-pointer-events-none': !canClickPinButton }"
|
||||
data-testid="nav-item-pin"
|
||||
icon="thumbtack"
|
||||
size="small"
|
||||
@click="$emit('pin-add', item.id)"
|
||||
@transitionend="togglePointerEvents"
|
||||
/>
|
||||
</template>
|
||||
</li>
|
||||
</template>
|
||||
|
|
|
@ -150,7 +150,7 @@ export default {
|
|||
<user-bar :has-collapse-button="!showOverlay" :sidebar-data="sidebarData" />
|
||||
<div v-if="showTrialStatusWidget" class="gl-px-2 gl-py-2">
|
||||
<trial-status-widget
|
||||
class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-mb-1 gl-px-3 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! nav-item-link gl-py-3"
|
||||
class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-mb-1 gl-px-3 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-py-3"
|
||||
/>
|
||||
<trial-status-popover />
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
*:not(
|
||||
/* Keep transition enabled where it would otherwise break specs */
|
||||
.nav-item-link .show-on-focus-or-hover--target /* for spec/features/nav/pinned_nav_items_spec.rb */
|
||||
.always-animate
|
||||
) {
|
||||
/* stylelint-disable property-no-vendor-prefix */
|
||||
-o-transition: none !important;
|
||||
|
|
|
@ -158,23 +158,6 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
|
|||
}
|
||||
}
|
||||
|
||||
.nav-item-link {
|
||||
button.show-on-focus-or-hover--target {
|
||||
transition: opacity $gl-transition-duration-fast;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.nav-item-badge {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
button.show-on-focus-or-hover--target {
|
||||
transition-delay: $gl-transition-duration-slow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#trial-status-sidebar-widget:hover {
|
||||
text-decoration: none;
|
||||
@include gl-text-contrast-light;
|
||||
|
@ -320,19 +303,71 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
|
|||
}
|
||||
}
|
||||
|
||||
.transition-opacity-on-hover--context {
|
||||
.transition-opacity-on-hover--target {
|
||||
transition: opacity $gl-transition-duration-fast linear;
|
||||
|
||||
&:hover {
|
||||
transition-delay: $gl-transition-duration-slow;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.transition-opacity-on-hover--target {
|
||||
transition-delay: $gl-transition-duration-slow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-on-focus-or-hover--context {
|
||||
.show-on-focus-or-hover--target {
|
||||
opacity: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
&:focus-within {
|
||||
.show-on-focus-or-hover--control {
|
||||
@include gl-bg-t-gray-a-08;
|
||||
}
|
||||
|
||||
.show-on-focus-or-hover--target {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.show-on-focus-or-hover--target:focus {
|
||||
opacity: 1;
|
||||
.show-on-focus-or-hover--control {
|
||||
&:hover,
|
||||
&:focus {
|
||||
+ .show-on-focus-or-hover--target {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide-on-focus-or-hover--context {
|
||||
.hide-on-focus-or-hover--target {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.hide-on-focus-or-hover--target {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hide-on-focus-or-hover--control {
|
||||
&:hover,
|
||||
&:focus {
|
||||
.hide-on-focus-or-hover--target {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class HelpController < ApplicationController
|
|||
|
||||
# Taken from Jekyll
|
||||
# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13
|
||||
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
||||
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
|
||||
|
||||
def index
|
||||
@help_index = get_markdown_without_frontmatter(path_to_doc('index.md'))
|
||||
|
|
|
@ -22,8 +22,8 @@ class Import::BitbucketServerController < Import::BaseController
|
|||
# (https://community.atlassian.com/t5/Answers-Developer-Questions/stash-repository-names/qaq-p/499054)
|
||||
#
|
||||
# Bitbucket Server starts personal project names with a tilde.
|
||||
VALID_BITBUCKET_PROJECT_CHARS = /\A~?[\w\-\.\s]+\z/.freeze
|
||||
VALID_BITBUCKET_CHARS = /\A[\w\-\.\s]+\z/.freeze
|
||||
VALID_BITBUCKET_PROJECT_CHARS = /\A~?[\w\-\.\s]+\z/
|
||||
VALID_BITBUCKET_CHARS = /\A[\w\-\.\s]+\z/
|
||||
|
||||
def new
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ class IssuableFinder
|
|||
requires_cross_project_access unless: -> { params.project? }
|
||||
|
||||
FULL_TEXT_SEARCH_TERM_PATTERN = '[\u0000-\u02FF\u1E00-\u1EFF\u2070-\u218F]*'
|
||||
FULL_TEXT_SEARCH_TERM_REGEX = /\A#{FULL_TEXT_SEARCH_TERM_PATTERN}\z/.freeze
|
||||
FULL_TEXT_SEARCH_TERM_REGEX = /\A#{FULL_TEXT_SEARCH_TERM_PATTERN}\z/
|
||||
NEGATABLE_PARAMS_HELPER_KEYS = %i[project_id scope status include_subgroups].freeze
|
||||
|
||||
attr_accessor :current_user, :params
|
||||
|
|
|
@ -21,7 +21,7 @@ module Repositories
|
|||
COMMITS_PER_PAGE = 1024
|
||||
|
||||
# The regex to use for extracting the SHA of a reverted commit.
|
||||
REVERT_REGEX = /^This reverts commit (?<sha>[0-9a-f]{40})/i.freeze
|
||||
REVERT_REGEX = /^This reverts commit (?<sha>[0-9a-f]{40})/i
|
||||
|
||||
# The `project` argument specifies the project for which to obtain the
|
||||
# commits.
|
||||
|
|
|
@ -19,7 +19,7 @@ module AuthHelper
|
|||
shibboleth
|
||||
twitter
|
||||
].freeze
|
||||
LDAP_PROVIDER = /\Aldap/.freeze
|
||||
LDAP_PROVIDER = /\Aldap/
|
||||
POPULAR_PROVIDERS = %w[google_oauth2 github].freeze
|
||||
|
||||
delegate :slack_app_id, to: :'Gitlab::CurrentSettings.current_application_settings'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ColorsHelper
|
||||
HEX_COLOR_PATTERN = /\A\#(?:[0-9A-Fa-f]{3}){1,2}\Z/.freeze
|
||||
HEX_COLOR_PATTERN = /\A\#(?:[0-9A-Fa-f]{3}){1,2}\Z/
|
||||
|
||||
def hex_color_to_rgb_array(hex_color)
|
||||
unless hex_color.is_a?(String) && HEX_COLOR_PATTERN.match?(hex_color)
|
||||
|
|
|
@ -226,6 +226,13 @@ module MergeRequestsHelper
|
|||
}
|
||||
end
|
||||
|
||||
def mr_compare_form_data(_, merge_request)
|
||||
{
|
||||
source_branch_url: project_new_merge_request_branch_from_path(merge_request.source_project),
|
||||
target_branch_url: project_new_merge_request_branch_to_path(merge_request.source_project)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def review_requested_merge_requests_count
|
||||
|
|
|
@ -8,7 +8,7 @@ module SidekiqHelper
|
|||
(?<state>[DIEKNRSTVWXZLpsl\+<>/\d]+)\s+
|
||||
(?<start>.+?)\s+
|
||||
(?<command>(?:ruby\d+:\s+)?sidekiq.*\].*)
|
||||
\z}x.freeze
|
||||
\z}x
|
||||
|
||||
def parse_sidekiq_ps(line)
|
||||
match = line.strip.match(SIDEKIQ_PS_REGEXP)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analytics
|
||||
module CycleAnalytics
|
||||
class RuntimeLimiter
|
||||
delegate :monotonic_time, to: :'Gitlab::Metrics::System'
|
||||
|
||||
DEFAULT_MAX_RUNTIME = 200.seconds
|
||||
|
||||
attr_reader :max_runtime, :start_time
|
||||
|
||||
def initialize(max_runtime = DEFAULT_MAX_RUNTIME)
|
||||
@start_time = monotonic_time
|
||||
@max_runtime = max_runtime
|
||||
end
|
||||
|
||||
def elapsed_time
|
||||
monotonic_time - start_time
|
||||
end
|
||||
|
||||
def over_time?
|
||||
@last_check = elapsed_time >= max_runtime
|
||||
end
|
||||
|
||||
def was_over_time?
|
||||
!!@last_check
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,7 @@ module ApplicationSettingImplementation
|
|||
\s # any whitespace character
|
||||
| # or
|
||||
[\r\n] # any number of newline characters
|
||||
}x.freeze
|
||||
}x
|
||||
|
||||
# Setting a key restriction to `-1` means that all keys of this type are
|
||||
# forbidden.
|
||||
|
|
|
@ -18,7 +18,7 @@ class Badge < ApplicationRecord
|
|||
# This regex is built dynamically using the keys from the PLACEHOLDER struct.
|
||||
# So, we can easily add new placeholder just by modifying the PLACEHOLDER hash.
|
||||
# This regex will build the new PLACEHOLDER_REGEX with the new information
|
||||
PLACEHOLDERS_REGEX = /(#{PLACEHOLDERS.keys.join('|')})/.freeze
|
||||
PLACEHOLDERS_REGEX = /(#{PLACEHOLDERS.keys.join('|')})/
|
||||
|
||||
default_scope { order_created_at_asc } # rubocop:disable Cop/DefaultScope
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module BlobViewer
|
|||
(?<name>.*?) (?# module name)
|
||||
\s*(?://.*)? (?# comment)
|
||||
(?:\n|\z) (?# newline or end of file)
|
||||
}x.freeze
|
||||
}x
|
||||
|
||||
self.file_types = %i[go_mod go_sum]
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ module BulkImports
|
|||
|
||||
event :start do
|
||||
transition created: :started
|
||||
# To avoid errors when re-starting a pipeline in case of network errors
|
||||
transition started: :started
|
||||
end
|
||||
|
||||
event :retry do
|
||||
|
|
|
@ -52,7 +52,7 @@ module Ci
|
|||
RUNNER_QUEUE_EXPIRY_TIME = 1.hour
|
||||
|
||||
# The `UPDATE_CONTACT_COLUMN_EVERY` defines how often the Runner DB entry can be updated
|
||||
UPDATE_CONTACT_COLUMN_EVERY = (40.minutes..55.minutes).freeze
|
||||
UPDATE_CONTACT_COLUMN_EVERY = (40.minutes..55.minutes)
|
||||
|
||||
# The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner will be considered stale
|
||||
STALE_TIMEOUT = 3.months
|
||||
|
|
|
@ -30,10 +30,10 @@ class Commit
|
|||
|
||||
MIN_SHA_LENGTH = Gitlab::Git::Commit::MIN_SHA_LENGTH
|
||||
MAX_SHA_LENGTH = Gitlab::Git::Commit::MAX_SHA_LENGTH
|
||||
COMMIT_SHA_PATTERN = Gitlab::Git::Commit::SHA_PATTERN.freeze
|
||||
EXACT_COMMIT_SHA_PATTERN = /\A#{COMMIT_SHA_PATTERN}\z/.freeze
|
||||
COMMIT_SHA_PATTERN = Gitlab::Git::Commit::SHA_PATTERN
|
||||
EXACT_COMMIT_SHA_PATTERN = /\A#{COMMIT_SHA_PATTERN}\z/
|
||||
# Used by GFM to match and present link extensions on node texts and hrefs.
|
||||
LINK_EXTENSION_PATTERN = /(patch)/.freeze
|
||||
LINK_EXTENSION_PATTERN = /(patch)/
|
||||
|
||||
DEFAULT_MAX_DIFF_LINES_SETTING = 50_000
|
||||
DEFAULT_MAX_DIFF_FILES_SETTING = 1_000
|
||||
|
@ -539,7 +539,7 @@ class Commit
|
|||
# added by `git commit --fixup` which is used by some community members.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/342937#note_892065311
|
||||
#
|
||||
DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze
|
||||
DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/
|
||||
|
||||
def draft?
|
||||
!!(title =~ DRAFT_REGEX)
|
||||
|
|
|
@ -28,11 +28,11 @@ class CommitRange
|
|||
|
||||
# The beginning and ending refs can be named or SHAs, and
|
||||
# the range notation can be double- or triple-dot.
|
||||
REF_PATTERN = /[0-9a-zA-Z][0-9a-zA-Z_.-]*[0-9a-zA-Z\^]/.freeze
|
||||
PATTERN = /#{REF_PATTERN}\.{2,3}#{REF_PATTERN}/.freeze
|
||||
REF_PATTERN = /[0-9a-zA-Z][0-9a-zA-Z_.-]*[0-9a-zA-Z\^]/
|
||||
PATTERN = /#{REF_PATTERN}\.{2,3}#{REF_PATTERN}/
|
||||
|
||||
# In text references, the beginning and ending refs can only be valid SHAs.
|
||||
STRICT_PATTERN = /#{Gitlab::Git::Commit::RAW_SHA_PATTERN}\.{2,3}#{Gitlab::Git::Commit::RAW_SHA_PATTERN}/.freeze
|
||||
STRICT_PATTERN = /#{Gitlab::Git::Commit::RAW_SHA_PATTERN}\.{2,3}#{Gitlab::Git::Commit::RAW_SHA_PATTERN}/
|
||||
|
||||
def self.reference_prefix
|
||||
'@'
|
||||
|
|
|
@ -11,12 +11,12 @@ module Ci
|
|||
# * Minimal length of 8 characters
|
||||
# * Characters must be from the Base64 alphabet (RFC4648) with the addition of '@', ':', '.', and '~'
|
||||
# * Absolutely no fun is allowed
|
||||
REGEX = %r{\A[a-zA-Z0-9_+=/@:.~-]{8,}\z}.freeze
|
||||
REGEX = %r{\A[a-zA-Z0-9_+=/@:.~-]{8,}\z}
|
||||
# * Single line
|
||||
# * No spaces
|
||||
# * Minimal length of 8 characters
|
||||
# * Some fun is allowed
|
||||
MASK_AND_RAW_REGEX = %r{\A\S{8,}\z}.freeze
|
||||
MASK_AND_RAW_REGEX = %r{\A\S{8,}\z}
|
||||
|
||||
included do
|
||||
validates :masked, inclusion: { in: [true, false] }
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
module PgFullTextSearchable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
LONG_WORDS_REGEX = %r([A-Za-z0-9+/@]{50,}).freeze
|
||||
LONG_WORDS_REGEX = %r([A-Za-z0-9+/@]{50,})
|
||||
TSVECTOR_MAX_LENGTH = 1.megabyte.freeze
|
||||
TEXT_SEARCH_DICTIONARY = 'english'
|
||||
URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}.freeze
|
||||
TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-_"]}.freeze
|
||||
URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}
|
||||
TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-_"]}
|
||||
|
||||
def update_search_data!
|
||||
tsvector_sql_nodes = self.class.pg_full_text_searchable_columns.map do |column, weight|
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
module Redactable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
UNSUBSCRIBE_PATTERN = %r{/sent_notifications/\h{32}/unsubscribe}.freeze
|
||||
UNSUBSCRIBE_PATTERN = %r{/sent_notifications/\h{32}/unsubscribe}
|
||||
|
||||
class_methods do
|
||||
def redact_field(field)
|
||||
|
|
|
@ -11,8 +11,8 @@ require 'task_list/filter'
|
|||
module Taskable
|
||||
COMPLETED = 'completed'
|
||||
INCOMPLETE = 'incomplete'
|
||||
COMPLETE_PATTERN = /\[[xX]\]/.freeze
|
||||
INCOMPLETE_PATTERN = /\[[[:space:]]\]/.freeze
|
||||
COMPLETE_PATTERN = /\[[xX]\]/
|
||||
INCOMPLETE_PATTERN = /\[[[:space:]]\]/
|
||||
ITEM_PATTERN = %r{
|
||||
^
|
||||
(?:(?:>\s{0,4})*) # optional blockquote characters
|
||||
|
@ -22,7 +22,7 @@ module Taskable
|
|||
#{COMPLETE_PATTERN}|#{INCOMPLETE_PATTERN}
|
||||
)
|
||||
(\s.+) # followed by whitespace and some text.
|
||||
}x.freeze
|
||||
}x
|
||||
|
||||
ITEM_PATTERN_UNTRUSTED =
|
||||
'^' \
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomEmoji < ApplicationRecord
|
||||
NAME_REGEXP = /[a-z0-9_-]+/.freeze
|
||||
NAME_REGEXP = /[a-z0-9_-]+/
|
||||
|
||||
belongs_to :namespace, inverse_of: :custom_emoji
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ class EnvironmentStatus
|
|||
|
||||
private
|
||||
|
||||
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
|
||||
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i
|
||||
|
||||
def deployment_metrics
|
||||
@deployment_metrics ||= DeploymentMetrics.new(project, deployment)
|
||||
|
|
|
@ -19,7 +19,7 @@ module ErrorTracking
|
|||
(?<project>[^/]+)/*
|
||||
)?
|
||||
\z
|
||||
}x.freeze
|
||||
}x
|
||||
|
||||
self.reactive_cache_key = ->(setting) { [setting.class.model_name.singular, setting.project_id] }
|
||||
self.reactive_cache_work_type = :external_dependency
|
||||
|
|
|
@ -10,6 +10,7 @@ class Event < ApplicationRecord
|
|||
include UsageStatistics
|
||||
include ShaAttribute
|
||||
include IgnorableColumns
|
||||
include EachBatch
|
||||
|
||||
ignore_column :target_id_convert_to_bigint, remove_with: '16.4', remove_after: '2023-09-22'
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ class WebHook < ApplicationRecord
|
|||
end
|
||||
|
||||
# See app/validators/json_schemas/web_hooks_url_variables.json
|
||||
VARIABLE_REFERENCE_RE = /\{([A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*)\}/.freeze
|
||||
VARIABLE_REFERENCE_RE = /\{([A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*)\}/
|
||||
|
||||
def interpolated_url(url = self.url, url_variables = self.url_variables)
|
||||
return url unless url.include?('{')
|
||||
|
|
|
@ -4,8 +4,8 @@ require 'app_store_connect'
|
|||
|
||||
module Integrations
|
||||
class AppleAppStore < Integration
|
||||
ISSUER_ID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/.freeze
|
||||
KEY_ID_REGEX = /\A(?=.*[A-Z])(?=.*[0-9])[A-Z0-9]+\z/.freeze
|
||||
ISSUER_ID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/
|
||||
KEY_ID_REGEX = /\A(?=.*[A-Z])(?=.*[0-9])[A-Z0-9]+\z/
|
||||
IS_KEY_CONTENT_BASE64 = "true"
|
||||
|
||||
SECTION_TYPE_APPLE_APP_STORE = 'apple_app_store'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Integrations
|
||||
module ChatMessage
|
||||
class BaseMessage
|
||||
RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/uploads/[^\)]*)\)}.freeze
|
||||
RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/uploads/[^\)]*)\)}
|
||||
|
||||
attr_reader :markdown
|
||||
attr_reader :user_full_name
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
module Integrations
|
||||
class Confluence < BaseThirdPartyWiki
|
||||
VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze
|
||||
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
|
||||
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
|
||||
VALID_SCHEME_MATCH = %r{\Ahttps?\Z}
|
||||
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}
|
||||
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}
|
||||
|
||||
validates :confluence_url, presence: true, if: :activated?
|
||||
validate :validate_confluence_url_is_cloud, if: :activated?
|
||||
|
|
|
@ -12,7 +12,7 @@ module Integrations
|
|||
pipeline build archive_trace
|
||||
].freeze
|
||||
|
||||
TAG_KEY_VALUE_RE = %r{\A [\w-]+ : .*\S.* \z}x.freeze
|
||||
TAG_KEY_VALUE_RE = %r{\A [\w-]+ : .*\S.* \z}x
|
||||
|
||||
field :datadog_site,
|
||||
exposes_secrets: true,
|
||||
|
|
|
@ -4,7 +4,7 @@ require "discordrb/webhooks"
|
|||
|
||||
module Integrations
|
||||
class Discord < BaseChatNotification
|
||||
ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze
|
||||
ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/
|
||||
|
||||
field :webhook,
|
||||
section: SECTION_TYPE_CONNECTION,
|
||||
|
|
|
@ -6,7 +6,7 @@ module Integrations
|
|||
include ReactivelyCached
|
||||
prepend EnableSslVerification
|
||||
|
||||
TEAMCITY_SAAS_HOSTNAME = /\A[^\.]+\.teamcity\.com\z/i.freeze
|
||||
TEAMCITY_SAAS_HOSTNAME = /\A[^\.]+\.teamcity\.com\z/i
|
||||
|
||||
field :teamcity_url,
|
||||
title: -> { s_('ProjectService|TeamCity server URL') },
|
||||
|
|
|
@ -5,12 +5,12 @@ class LicenseTemplate
|
|||
%r{[\<\{\[]
|
||||
(project|description|
|
||||
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
|
||||
[\>\}\]]}xi.freeze
|
||||
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
|
||||
[\>\}\]]}xi
|
||||
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i
|
||||
FULLNAME_TEMPLATE_REGEX =
|
||||
%r{[\<\{\[]
|
||||
(fullname|name\sof\s(author|copyright\sowner))
|
||||
[\>\}\]]}xi.freeze
|
||||
[\>\}\]]}xi
|
||||
|
||||
attr_reader :key, :name, :project, :category, :nickname, :url, :meta
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class GroupMember < Member
|
|||
include CreatedAtFilterable
|
||||
|
||||
SOURCE_TYPE = 'Namespace'
|
||||
SOURCE_TYPE_FORMAT = /\ANamespace\z/.freeze
|
||||
SOURCE_TYPE_FORMAT = /\ANamespace\z/
|
||||
|
||||
belongs_to :group, foreign_key: 'source_id'
|
||||
alias_attribute :namespace_id, :source_id
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ProjectMember < Member
|
||||
SOURCE_TYPE = 'Project'
|
||||
SOURCE_TYPE_FORMAT = /\AProject\z/.freeze
|
||||
SOURCE_TYPE_FORMAT = /\AProject\z/
|
||||
|
||||
belongs_to :project, foreign_key: 'source_id'
|
||||
|
||||
|
|
|
@ -643,7 +643,7 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
DRAFT_REGEX = /\A*#{Gitlab::Regex.merge_request_draft}+\s*/i.freeze
|
||||
DRAFT_REGEX = /\A*#{Gitlab::Regex.merge_request_draft}+\s*/i
|
||||
|
||||
def self.draft?(title)
|
||||
!!(title =~ DRAFT_REGEX)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Namespaces
|
||||
class RandomizedSuffixPath
|
||||
MAX_TRIES = 4
|
||||
LEADING_ZEROS = /^0+/.freeze
|
||||
LEADING_ZEROS = /^0+/
|
||||
|
||||
def initialize(path)
|
||||
@path = path
|
||||
|
|
|
@ -28,7 +28,7 @@ class Note < ApplicationRecord
|
|||
|
||||
ignore_column :id_convert_to_bigint, remove_with: '16.3', remove_after: '2023-08-22'
|
||||
|
||||
ISSUE_TASK_SYSTEM_NOTE_PATTERN = /\A.*marked\sthe\stask.+as\s(completed|incomplete).*\z/.freeze
|
||||
ISSUE_TASK_SYSTEM_NOTE_PATTERN = /\A.*marked\sthe\stask.+as\s(completed|incomplete).*\z/
|
||||
|
||||
cache_markdown_field :note, pipeline: :note, issuable_reference_expansion_enabled: true
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ module Packages
|
|||
module Debian
|
||||
TEMPORARY_PACKAGE_NAME = 'debian-temporary-package'
|
||||
|
||||
DISTRIBUTION_REGEX = %r{[a-z0-9][a-z0-9.-]*}i.freeze
|
||||
DISTRIBUTION_REGEX = %r{[a-z0-9][a-z0-9.-]*}i
|
||||
COMPONENT_REGEX = DISTRIBUTION_REGEX.freeze
|
||||
ARCHITECTURE_REGEX = %r{[a-z0-9][-a-z0-9]*}.freeze
|
||||
ARCHITECTURE_REGEX = %r{[a-z0-9][-a-z0-9]*}
|
||||
|
||||
LETTER_REGEX = %r{(lib)?[a-z0-9]}.freeze
|
||||
LETTER_REGEX = %r{(lib)?[a-z0-9]}
|
||||
|
||||
EMPTY_FILE_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'.freeze
|
||||
EMPTY_FILE_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
||||
|
||||
INCOMING_PACKAGE_NAME = 'incoming'
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module Packages
|
|||
include ActiveModel::Model
|
||||
|
||||
DIGESTS = %i[md5 sha1 sha256].freeze
|
||||
FILENAME_REGEX = %r{\A[a-zA-Z0-9][a-zA-Z0-9_.~+-]*\z}.freeze
|
||||
FILENAME_REGEX = %r{\A[a-zA-Z0-9][a-zA-Z0-9_.~+-]*\z}
|
||||
|
||||
attr_accessor :filename,
|
||||
:size,
|
||||
|
|
|
@ -14,7 +14,7 @@ class PersonalAccessToken < ApplicationRecord
|
|||
format_with_prefix: :prefix_from_application_current_settings
|
||||
|
||||
# PATs are 20 characters + optional configurable settings prefix (0..20)
|
||||
TOKEN_LENGTH_RANGE = (20..40).freeze
|
||||
TOKEN_LENGTH_RANGE = (20..40)
|
||||
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS = 365
|
||||
|
||||
serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
|
|
@ -8,7 +8,7 @@ module Releases
|
|||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/218753
|
||||
# Regex modified to prevent catastrophic backtracking
|
||||
FILEPATH_REGEX = %r{\A\/[^\/](?!.*\/\/.*)[\-\.\w\/]+[\da-zA-Z]+\z}.freeze
|
||||
FILEPATH_REGEX = %r{\A\/[^\/](?!.*\/\/.*)[\-\.\w\/]+[\da-zA-Z]+\z}
|
||||
FILEPATH_MAX_LENGTH = 128
|
||||
|
||||
validates :url, presence: true, addressable_url: { schemes: %w[http https ftp] }, uniqueness: { scope: :release }
|
||||
|
|
|
@ -31,6 +31,10 @@ class Review < ApplicationRecord
|
|||
def user_mentions
|
||||
merge_request.user_mentions.where.not(note_id: nil)
|
||||
end
|
||||
|
||||
def from_merge_request_author?
|
||||
merge_request.author_id == author_id
|
||||
end
|
||||
end
|
||||
|
||||
Review.prepend_mod
|
||||
|
|
|
@ -5,7 +5,7 @@ class SnippetRepository < ApplicationRecord
|
|||
include Shardable
|
||||
|
||||
DEFAULT_EMPTY_FILE_NAME = 'snippetfile'
|
||||
EMPTY_FILE_PATTERN = /^#{DEFAULT_EMPTY_FILE_NAME}(\d+)\.txt$/.freeze
|
||||
EMPTY_FILE_PATTERN = /^#{DEFAULT_EMPTY_FILE_NAME}(\d+)\.txt$/
|
||||
|
||||
CommitError = Class.new(StandardError)
|
||||
InvalidPathError = Class.new(CommitError)
|
||||
|
|
|
@ -5,7 +5,7 @@ module Terraform
|
|||
include UsageStatistics
|
||||
include AfterCommitQueue
|
||||
|
||||
HEX_REGEXP = %r{\A\h+\z}.freeze
|
||||
HEX_REGEXP = %r{\A\h+\z}
|
||||
UUID_LENGTH = 32
|
||||
|
||||
self.locking_column = :activerecord_lock_version
|
||||
|
|
|
@ -4,7 +4,7 @@ module Clusters
|
|||
module AgentTokens
|
||||
class TrackUsageService
|
||||
# The `UPDATE_USED_COLUMN_EVERY` defines how often the token DB entry can be updated
|
||||
UPDATE_USED_COLUMN_EVERY = (40.minutes..55.minutes).freeze
|
||||
UPDATE_USED_COLUMN_EVERY = (40.minutes..55.minutes)
|
||||
|
||||
delegate :agent, to: :token
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ module Import
|
|||
|
||||
GIT_SERVICE_NAME = "git-upload-pack"
|
||||
GIT_EXPECTED_FIRST_PACKET_LINE = "# service=#{GIT_SERVICE_NAME}"
|
||||
GIT_BODY_MESSAGE_REGEXP = /^[0-9a-f]{4}#{GIT_EXPECTED_FIRST_PACKET_LINE}/.freeze
|
||||
GIT_BODY_MESSAGE_REGEXP = /^[0-9a-f]{4}#{GIT_EXPECTED_FIRST_PACKET_LINE}/
|
||||
# https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt#L56-L59
|
||||
GIT_PROTOCOL_PKT_LEN = 4
|
||||
GIT_MINIMUM_RESPONSE_LENGTH = GIT_PROTOCOL_PKT_LEN + GIT_EXPECTED_FIRST_PACKET_LINE.length
|
||||
|
|
|
@ -21,7 +21,7 @@ module Issues
|
|||
Issues::CloseService
|
||||
end
|
||||
|
||||
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999)).freeze
|
||||
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999))
|
||||
|
||||
def rebalance_if_needed(issue)
|
||||
return unless issue
|
||||
|
|
|
@ -4,7 +4,7 @@ module Projects
|
|||
# Used by project imports, it removes any potential paths
|
||||
# included in an error message that could be stored in the DB
|
||||
class ImportErrorFilter
|
||||
ERROR_MESSAGE_FILTER = /[^\s]*#{File::SEPARATOR}[^\s]*(?=(\s|\z))/.freeze
|
||||
ERROR_MESSAGE_FILTER = /[^\s]*#{File::SEPARATOR}[^\s]*(?=(\s|\z))/
|
||||
FILTER_MESSAGE = '[FILTERED]'
|
||||
|
||||
def self.filter_message(message)
|
||||
|
|
|
@ -9,7 +9,7 @@ module Projects
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
HEAD_REV = 'HEAD'
|
||||
LFS_ENDPOINT_PATTERN = /^\t?url\s*=\s*(.+)$/.freeze
|
||||
LFS_ENDPOINT_PATTERN = /^\t?url\s*=\s*(.+)$/
|
||||
LFS_BATCH_API_ENDPOINT = '/info/lfs/objects/batch'
|
||||
|
||||
LfsObjectDownloadListError = Class.new(StandardError)
|
||||
|
|
|
@ -20,8 +20,8 @@ class FileUploader < GitlabUploader
|
|||
'!?\[.*?\]\(/uploads/(?P<secret>[0-9a-f]{32})/(?P<file>.*?)\)'
|
||||
)
|
||||
|
||||
DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\b(\h{10}|\h{32}))\/(?<identifier>.*)}.freeze
|
||||
VALID_SECRET_PATTERN = %r{\A\h{10,32}\z}.freeze
|
||||
DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\b(\h{10}|\h{32}))\/(?<identifier>.*)}
|
||||
VALID_SECRET_PATTERN = %r{\A\h{10,32}\z}
|
||||
|
||||
InvalidSecret = Class.new(StandardError)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CertificateFingerprintValidator < ActiveModel::EachValidator
|
||||
FINGERPRINT_PATTERN = /\A([a-zA-Z0-9]{2}[\s\-:]?){16,}\z/.freeze
|
||||
FINGERPRINT_PATTERN = /\A([a-zA-Z0-9]{2}[\s\-:]?){16,}\z/
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
unless value.try(:match, FINGERPRINT_PATTERN)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# end
|
||||
#
|
||||
class JsonSchemaValidator < ActiveModel::EachValidator
|
||||
FILENAME_ALLOWED = /\A[a-z0-9_-]*\Z/.freeze
|
||||
FILENAME_ALLOWED = /\A[a-z0-9_-]*\Z/
|
||||
FilenameError = Class.new(StandardError)
|
||||
BASE_DIRECTORY = %w[app validators json_schemas].freeze
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
# Custom validator for GitLab line codes.
|
||||
class LineCodeValidator < ActiveModel::EachValidator
|
||||
PATTERN = /\A[a-z0-9]+_\d+_\d+\z/.freeze
|
||||
PATTERN = /\A[a-z0-9]+_\d+_\d+\z/
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
unless PATTERN.match?(value)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
= gitlab_ui_form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
|
||||
- if params[:nav_source].present?
|
||||
= hidden_field_tag(:nav_source, params[:nav_source])
|
||||
.js-merge-request-new-compare.row{ 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) }
|
||||
.js-merge-request-new-compare.row{ data: mr_compare_form_data(current_user, @merge_request) }
|
||||
.col-lg-6
|
||||
.card-new-merge-request
|
||||
%h2.gl-font-size-h2
|
||||
|
|
|
@ -13,15 +13,5 @@ class BuildSuccessWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
queue_namespace :pipeline_processing
|
||||
urgency :high
|
||||
|
||||
def perform(build_id)
|
||||
Ci::Build.find_by_id(build_id).try do |build|
|
||||
stop_environment(build) if build.stops_environment? && build.stop_action_successful?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stop_environment(build)
|
||||
build.persisted_environment.fire_state_event(:stop_complete)
|
||||
end
|
||||
def perform(build_id); end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,47 @@ module ClickHouse
|
|||
|
||||
# the job is scheduled every 3 minutes and we will allow maximum 2.5 minutes runtime
|
||||
MAX_TTL = 2.5.minutes.to_i
|
||||
MAX_RUNTIME = 120.seconds
|
||||
BATCH_SIZE = 500
|
||||
INSERT_BATCH_SIZE = 5000
|
||||
CSV_MAPPING = {
|
||||
id: :id,
|
||||
path: :path,
|
||||
author_id: :author_id,
|
||||
target_id: :target_id,
|
||||
target_type: :target_type,
|
||||
action: :raw_action,
|
||||
created_at: :casted_created_at,
|
||||
updated_at: :casted_updated_at
|
||||
}.freeze
|
||||
|
||||
# transforms the traversal_ids to a String:
|
||||
# Example: group_id/subgroup_id/group_or_projectnamespace_id/
|
||||
PATH_COLUMN = <<~SQL
|
||||
(
|
||||
CASE
|
||||
WHEN project_id IS NOT NULL THEN (SELECT array_to_string(traversal_ids, '/') || '/' FROM namespaces WHERE id = (SELECT project_namespace_id FROM projects WHERE id = events.project_id LIMIT 1) LIMIT 1)
|
||||
WHEN group_id IS NOT NULL THEN (SELECT array_to_string(traversal_ids, '/') || '/' FROM namespaces WHERE id = events.group_id LIMIT 1)
|
||||
ELSE ''
|
||||
END
|
||||
) AS path
|
||||
SQL
|
||||
|
||||
EVENT_PROJECTIONS = [
|
||||
:id,
|
||||
PATH_COLUMN,
|
||||
:author_id,
|
||||
:target_id,
|
||||
:target_type,
|
||||
'action AS raw_action',
|
||||
'EXTRACT(epoch FROM created_at) AS casted_created_at',
|
||||
'EXTRACT(epoch FROM updated_at) AS casted_updated_at'
|
||||
].freeze
|
||||
|
||||
INSERT_EVENTS_QUERY = <<~SQL.squish
|
||||
INSERT INTO events (#{CSV_MAPPING.keys.join(', ')})
|
||||
SETTINGS async_insert=1, wait_for_async_insert=1 FORMAT CSV
|
||||
SQL
|
||||
|
||||
def perform
|
||||
unless enabled?
|
||||
|
@ -22,12 +63,15 @@ module ClickHouse
|
|||
|
||||
metadata = { status: :processed }
|
||||
|
||||
# Prevent parallel jobs
|
||||
begin
|
||||
# Prevent parallel jobs
|
||||
in_lock(self.class.to_s, ttl: MAX_TTL, retries: 0) do
|
||||
true
|
||||
end
|
||||
loop { break unless next_batch }
|
||||
|
||||
metadata.merge!(records_inserted: context.total_record_count, reached_end_of_table: context.no_more_records?)
|
||||
|
||||
ClickHouse::SyncCursor.update_cursor_for(:events, context.last_processed_id) if context.last_processed_id
|
||||
end
|
||||
rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
|
||||
# Skip retrying, just let the next worker to start after a few minutes
|
||||
metadata = { status: :skipped }
|
||||
|
@ -38,8 +82,51 @@ module ClickHouse
|
|||
|
||||
private
|
||||
|
||||
def context
|
||||
@context ||= ClickHouse::RecordSyncContext.new(
|
||||
last_record_id: ClickHouse::SyncCursor.cursor_for(:events),
|
||||
max_records_per_batch: INSERT_BATCH_SIZE,
|
||||
runtime_limiter: Analytics::CycleAnalytics::RuntimeLimiter.new(MAX_RUNTIME)
|
||||
)
|
||||
end
|
||||
|
||||
def enabled?
|
||||
ClickHouse::Client.configuration.databases[:main].present? && Feature.enabled?(:event_sync_worker_for_click_house)
|
||||
end
|
||||
|
||||
def next_batch
|
||||
context.new_batch!
|
||||
|
||||
CsvBuilder::Gzip.new(process_batch(context), CSV_MAPPING).render do |tempfile, rows_written|
|
||||
unless rows_written == 0
|
||||
ClickHouse::Client.insert_csv(INSERT_EVENTS_QUERY, File.open(tempfile.path),
|
||||
:main)
|
||||
end
|
||||
end
|
||||
|
||||
!(context.over_time? || context.no_more_records?)
|
||||
end
|
||||
|
||||
def process_batch(context)
|
||||
Enumerator.new do |yielder|
|
||||
has_data = false
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
Event.where(Event.arel_table[:id].gt(context.last_record_id)).each_batch(of: BATCH_SIZE) do |relation|
|
||||
has_data = true
|
||||
|
||||
relation.select(*EVENT_PROJECTIONS).each do |row|
|
||||
yielder << row
|
||||
context.last_processed_id = row.id
|
||||
|
||||
break if context.record_limit_reached?
|
||||
end
|
||||
|
||||
break if context.over_time? || context.record_limit_reached?
|
||||
end
|
||||
|
||||
context.no_more_records! if has_data == false
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: ci_commit_statuses_api_exclusive_lock
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129164
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421657
|
||||
milestone: '16.4'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -5,5 +5,5 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329990
|
|||
milestone: '15.9'
|
||||
type: development
|
||||
group: group::incubation
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
log_state_changes: true
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/423880
|
|||
milestone: '16.4'
|
||||
type: development
|
||||
group: group::incubation
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Gitlab::Database.check_single_connection_and_print_warning
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
data_category: optional
|
||||
data_category: operational
|
||||
key_path: redis_hll_counters.pipeline_authoring.o_pipeline_authoring_unique_users_committing_ciconfigfile_weekly
|
||||
description: Weekly unique user count doing commits which contains the CI config file
|
||||
product_section: ops
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
data_category: optional
|
||||
data_category: operational
|
||||
key_path: counts.ci_pipeline_config_repository
|
||||
description: Total Pipelines from CI files in repository
|
||||
product_section: ops
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
data_category: optional
|
||||
data_category: operational
|
||||
key_path: usage_activity_by_stage.verify.ci_pipeline_config_repository
|
||||
description: Total count of unique users creating pipelines from CI files in the repository
|
||||
product_section: ops
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
data_category: optional
|
||||
data_category: operational
|
||||
key_path: usage_activity_by_stage.verify.ci_pipeline_schedules
|
||||
description: Distinct users creating pipeline schedules
|
||||
product_section: ops
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE sync_cursors
|
||||
(
|
||||
table_name LowCardinality(String) DEFAULT '',
|
||||
primary_key_value UInt64 DEFAULT 0,
|
||||
recorded_at DateTime64(6, 'UTC') DEFAULT now()
|
||||
)
|
||||
ENGINE = ReplacingMergeTree(recorded_at)
|
||||
ORDER BY (table_name)
|
||||
PRIMARY KEY (table_name)
|
|
@ -274,7 +274,7 @@ The runner authentication token displays in the UI for only a short period of ti
|
|||
|
||||
WARNING:
|
||||
The ability to pass a runner registration token, and support for certain configuration arguments was
|
||||
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6 and will be removed in GitLab 17.0. Authentication tokens
|
||||
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6 and will be removed in GitLab 18.0. Authentication tokens
|
||||
should be used instead. For more information, see [Migrating to the new runner registration workflow](new_creation_workflow.md).
|
||||
|
||||
You must have the Owner role for the group.
|
||||
|
|
|
@ -178,6 +178,40 @@ Example response:
|
|||
|
||||
- GitLab Shell
|
||||
|
||||
## Authorized Certs
|
||||
|
||||
This endpoint is called by the GitLab Shell to get the namespace that has a particular CA SSH certificate
|
||||
configured. It also accepts `user_identifier` to return a GitLab user for specified identifier.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------------------|:-------|:---------|:------------|
|
||||
| `key` | string | yes | The fingerprint of the SSH certificate. |
|
||||
| `user_identifier` | string | yes | The identifier of the user to whom the SSH certificate has been issued (username or primary email). |
|
||||
|
||||
```plaintext
|
||||
GET /internal/authorized_certs
|
||||
```
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request GET --header "Gitlab-Shell-Api-Request: <JWT token>" "http://localhost:3001/api/v4/internal/authorized_certs?key=<key>&user_identifier=<user_identifier>"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"namespace": "gitlab-org",
|
||||
"username": "root"
|
||||
}
|
||||
```
|
||||
|
||||
### Known consumers
|
||||
|
||||
- GitLab Shell
|
||||
|
||||
## Get user for user ID or key
|
||||
|
||||
This endpoint is used when a user performs `ssh git@gitlab.com`. It
|
||||
|
|
|
@ -29,15 +29,16 @@ The following data sources are configured for analytics dashboards:
|
|||
## Built-in dashboards
|
||||
|
||||
To help you get started with analytics, GitLab provides built-in dashboards with predefined visualizations.
|
||||
These dashboards are labeled **By GitLab**, and you cannot edit them.
|
||||
Instead, you can create a custom dashboard with a similar style.
|
||||
|
||||
### Product analytics
|
||||
|
||||
When [product analytics](../product_analytics/index.md) is enabled and onboarded, two built-in dashboard are added:
|
||||
|
||||
- **Audience** displays metrics related to traffic, such as the number of users and sessions.
|
||||
- **Behavior** displays metrics related to user activity, such as the number of page views and events.
|
||||
|
||||
These dashboards are labeled **By GitLab**, and you cannot edit them.
|
||||
Instead, you can create a custom dashboard with a similar style.
|
||||
|
||||
### Value Stream Management
|
||||
|
||||
- **Value Streams Dashboard** displays metrics related to [DevOps performance, security exposure, and workstream optimization](../analytics/value_streams_dashboard.md#devsecops-metrics-comparison-panel).
|
||||
|
@ -64,9 +65,6 @@ On self-managed GitLab, by default this feature is not available. To make it ava
|
|||
On GitLab.com, this feature is not available.
|
||||
This feature is not ready for production use.
|
||||
|
||||
NOTE:
|
||||
This feature does not work in conjunction with the `product_analytics_snowplow_support` feature flag.
|
||||
|
||||
You can use the dashboard designer to:
|
||||
|
||||
- Create custom dashboards.
|
||||
|
|
|
@ -10,18 +10,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> - `cube_api_proxy` revised to only reference the [Product Analytics API](../../api/product_analytics.md) in GitLab 15.6.
|
||||
> - `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
|
||||
> - `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
|
||||
> - Snowplow integration introduced in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `product_analytics_snowplow_support`. Disabled by default.
|
||||
> - Snowplow integration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398253) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `product_analytics_snowplow_support`. Disabled by default.
|
||||
> - Snowplow integration feature flag `product_analytics_snowplow_support` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130228) in GitLab 16.4.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `product_analytics_dashboards`.
|
||||
On GitLab.com, this feature is not available.
|
||||
This feature is not ready for production use.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default the Snowplow integration is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `product_analytics_snowplow_support`.
|
||||
On GitLab.com, this feature is not available.
|
||||
This feature is not ready for production use.
|
||||
|
||||
This page is a work in progress, and we're updating the information as we add more features.
|
||||
For more information, see the [group direction page](https://about.gitlab.com/direction/analytics/product-analytics/).
|
||||
To leave feedback about Product Analytics bugs or functionality, please comment in [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970) or open a new issue with the label `group::product analytics`.
|
||||
|
@ -67,7 +63,7 @@ flowchart TB
|
|||
> - `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flags](../../administration/feature_flags.md) named `product_analytics_dashboards`, `product_analytics_admin_settings`, `product_analytics_snowplow_support`, and `combined_analytics_dashboards`.
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flags](../../administration/feature_flags.md) named `product_analytics_dashboards`, `product_analytics_admin_settings`, and `combined_analytics_dashboards`.
|
||||
On GitLab.com, this feature is not available.
|
||||
This feature is not ready for production use.
|
||||
|
||||
|
|
|
@ -157,12 +157,13 @@ To edit the custom email display name:
|
|||
|
||||
## Custom email address **(BETA)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329990) in GitLab 16.3 [with a flag](../../../administration/feature_flags.md) named `service_desk_custom_email`. Disabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329990) in GitLab 16.3 [with a flag](../../../administration/feature_flags.md) named `service_desk_custom_email`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/387003) in GitLab 16.4.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per project or for your
|
||||
entire instance, an administrator can [enable the feature flag](../../../administration/feature_flags.md)
|
||||
named `service_desk_custom_email`. On GitLab.com, this feature is not available. The feature is not ready for production use.
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature per project or for
|
||||
your entire instance, an administrator can [disable the feature flag](../../../administration/feature_flags.md)
|
||||
named `service_desk_custom_email`. On GitLab.com, this feature is available.
|
||||
|
||||
Configure a custom email address to show as the sender of your support communication.
|
||||
Maintain brand identity and instill confidence among support requesters with a domain they recognize.
|
||||
|
|
|
@ -35,7 +35,8 @@ When you [create a workspace](configuration.md#set-up-a-workspace), you must:
|
|||
- Assign the workspace to a specific project.
|
||||
- Select a project with a [`.devfile.yaml`](#devfile) file.
|
||||
|
||||
The workspace can then interact with the GitLab API based on the permissions granted to the current user.
|
||||
The workspace can interact with the GitLab API, with the access level defined by current user permissions.
|
||||
A running workspace remains accessible even if user permissions are later revoked.
|
||||
|
||||
### Open and manage workspaces from a project
|
||||
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
module CsvBuilder
|
||||
class Gzip < CsvBuilder::Builder
|
||||
# Writes the CSV file compressed and yields the written tempfile.
|
||||
# Writes the CSV file compressed and yields the written tempfile and rows written.
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
# > CsvBuilder::Gzip.new(Issue, { title: -> (row) { row.title.upcase }, id: :id }).render do |tempfile|
|
||||
# > CsvBuilder::Gzip.new(Issue, { title: -> (row) { row.title.upcase }, id: :id }).render do |tempfile, rows|
|
||||
# > puts tempfile.path
|
||||
# > puts `zcat #{tempfile.path}`
|
||||
# > puts rows
|
||||
# > end
|
||||
def render
|
||||
Tempfile.open(['csv_builder_gzip', '.csv.gz']) do |tempfile|
|
||||
|
@ -16,7 +18,7 @@ module CsvBuilder
|
|||
write_csv csv, until_condition: -> {} # truncation must be handled outside of the CsvBuilder
|
||||
|
||||
csv.close
|
||||
yield tempfile
|
||||
yield tempfile, @rows_written
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,13 @@ RSpec.describe CsvBuilder::Gzip do
|
|||
])
|
||||
end
|
||||
|
||||
it 'yields the number of written rows as the second argument' do
|
||||
row_count = 0
|
||||
builder.render { |_, rows| row_count = rows }
|
||||
|
||||
expect(row_count).to eq(2)
|
||||
end
|
||||
|
||||
it 'requires a block' do
|
||||
expect { builder.render }.to raise_error(LocalJumpError)
|
||||
end
|
||||
|
|
|
@ -80,85 +80,14 @@ module API
|
|||
post ':id/statuses/:sha' do
|
||||
authorize! :create_commit_status, user_project
|
||||
|
||||
if Feature.enabled?(:ci_commit_statuses_api_exclusive_lock, user_project)
|
||||
response =
|
||||
::Ci::CreateCommitStatusService
|
||||
.new(user_project, current_user, params)
|
||||
.execute(optional_commit_status_params: optional_commit_status_params)
|
||||
response =
|
||||
::Ci::CreateCommitStatusService
|
||||
.new(user_project, current_user, params)
|
||||
.execute(optional_commit_status_params: optional_commit_status_params)
|
||||
|
||||
if response.error?
|
||||
render_api_error!(response.message, response.http_status)
|
||||
else
|
||||
present response.payload[:job], with: Entities::CommitStatus
|
||||
end
|
||||
if response.error?
|
||||
render_api_error!(response.message, response.http_status)
|
||||
else
|
||||
not_found! 'Commit' unless commit
|
||||
|
||||
# Since the CommitStatus is attached to ::Ci::Pipeline (in the future Pipeline)
|
||||
# We need to always have the pipeline object
|
||||
# To have a valid pipeline object that can be attached to specific MR
|
||||
# Other CI service needs to send `ref`
|
||||
# If we don't receive it, we will attach the CommitStatus to
|
||||
# the first found branch on that commit
|
||||
|
||||
pipeline = all_matching_pipelines.first
|
||||
|
||||
ref = params[:ref]
|
||||
ref ||= pipeline&.ref
|
||||
ref ||= user_project.repository.branch_names_contains(commit.sha).first
|
||||
not_found! 'References for commit' unless ref
|
||||
|
||||
name = params[:name] || params[:context] || 'default'
|
||||
|
||||
pipeline ||= user_project.ci_pipelines.build(
|
||||
source: :external,
|
||||
sha: commit.sha,
|
||||
ref: ref,
|
||||
user: current_user,
|
||||
protected: user_project.protected_for?(ref))
|
||||
|
||||
pipeline.ensure_project_iid!
|
||||
pipeline.save!
|
||||
|
||||
authorize! :update_pipeline, pipeline
|
||||
|
||||
# rubocop: disable Performance/ActiveRecordSubtransactionMethods
|
||||
stage = pipeline.stages.safe_find_or_create_by!(name: 'external') do |stage|
|
||||
stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX
|
||||
stage.project = pipeline.project
|
||||
end
|
||||
# rubocop: enable Performance/ActiveRecordSubtransactionMethods
|
||||
|
||||
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
|
||||
project: user_project,
|
||||
pipeline: pipeline,
|
||||
name: name,
|
||||
ref: ref,
|
||||
user: current_user,
|
||||
protected: user_project.protected_for?(ref),
|
||||
ci_stage: stage,
|
||||
stage_idx: stage.position,
|
||||
stage: 'external'
|
||||
)
|
||||
|
||||
status.assign_attributes(optional_commit_status_params)
|
||||
|
||||
render_validation_error!(status) unless status.valid?
|
||||
|
||||
response = ::Ci::Pipelines::AddJobService.new(pipeline).execute!(status) do |job|
|
||||
apply_job_state!(job)
|
||||
rescue ::StateMachines::InvalidTransition => e
|
||||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
render_validation_error!(response.payload[:job]) unless response.success?
|
||||
|
||||
if pipeline.latest?
|
||||
MergeRequest
|
||||
.where(source_project: user_project, source_branch: ref)
|
||||
.update_all(head_pipeline_id: pipeline.id)
|
||||
end
|
||||
|
||||
present response.payload[:job], with: Entities::CommitStatus
|
||||
end
|
||||
end
|
||||
|
|
|
@ -54,6 +54,10 @@ module Bitbucket
|
|||
target_branch.dig('commit', 'hash')
|
||||
end
|
||||
|
||||
def merge_commit_sha
|
||||
raw['merge_commit']&.dig('hash')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def source_branch
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ClickHouse
|
||||
class RecordSyncContext
|
||||
attr_reader :last_record_id, :last_processed_id, :total_record_count, :record_count_in_current_batch
|
||||
|
||||
def initialize(
|
||||
last_record_id:, max_records_per_batch:,
|
||||
runtime_limiter: Analytics::CycleAnalytics::RuntimeLimiter.new)
|
||||
@last_record_id = last_record_id
|
||||
@runtime_limiter = runtime_limiter
|
||||
@max_records_per_batch = max_records_per_batch
|
||||
@last_processed_id = nil
|
||||
@record_count_in_current_batch = 0
|
||||
@total_record_count = 0
|
||||
end
|
||||
|
||||
delegate :over_time?, to: :@runtime_limiter
|
||||
|
||||
def new_batch!
|
||||
@record_count_in_current_batch = 0
|
||||
end
|
||||
|
||||
def no_more_records!
|
||||
@no_more_records = true
|
||||
end
|
||||
|
||||
def no_more_records?
|
||||
!!@no_more_records
|
||||
end
|
||||
|
||||
def last_processed_id=(value)
|
||||
@record_count_in_current_batch += 1
|
||||
@total_record_count += 1
|
||||
@last_processed_id = value
|
||||
@last_record_id = value
|
||||
end
|
||||
|
||||
def record_limit_reached?
|
||||
@record_count_in_current_batch == @max_records_per_batch
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ClickHouse
|
||||
class SyncCursor
|
||||
QUERY = <<~SQL
|
||||
SELECT argMax(primary_key_value, recorded_at) AS primary_key_value
|
||||
FROM sync_cursors
|
||||
WHERE table_name = {table_name:String}
|
||||
LIMIT 1
|
||||
SQL
|
||||
|
||||
INSERT_CURSOR_QUERY = <<~SQL
|
||||
INSERT INTO sync_cursors
|
||||
(primary_key_value, table_name, recorded_at)
|
||||
VALUES ({primary_key_value:UInt64}, {table_name:String}, {recorded_at:DateTime64})
|
||||
SQL
|
||||
|
||||
def self.cursor_for(identifier)
|
||||
query = ClickHouse::Client::Query.new(
|
||||
raw_query: QUERY,
|
||||
placeholders: { table_name: identifier.to_s }
|
||||
)
|
||||
|
||||
# The query returns the default value (0) when no records are present.
|
||||
ClickHouse::Client.select(query, :main).first['primary_key_value']
|
||||
end
|
||||
|
||||
def self.update_cursor_for(identifier, value)
|
||||
query = ClickHouse::Client::Query.new(
|
||||
raw_query: INSERT_CURSOR_QUERY,
|
||||
placeholders: {
|
||||
primary_key_value: value,
|
||||
table_name: identifier.to_s,
|
||||
recorded_at: Time.current.to_f
|
||||
}
|
||||
)
|
||||
|
||||
ClickHouse::Client.execute(query, :main)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -210,7 +210,11 @@ module Gitlab
|
|||
|
||||
source_branch_sha = pull_request.source_branch_sha
|
||||
target_branch_sha = pull_request.target_branch_sha
|
||||
source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha
|
||||
|
||||
source_sha_from_commit_sha = project.repository.commit(source_branch_sha)&.sha
|
||||
source_sha_from_merge_sha = project.repository.commit(pull_request.merge_commit_sha)&.sha
|
||||
|
||||
source_branch_sha = source_sha_from_commit_sha || source_sha_from_merge_sha || source_branch_sha
|
||||
target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha
|
||||
|
||||
merge_request = project.merge_requests.create!(
|
||||
|
|
|
@ -65,6 +65,10 @@ variables:
|
|||
|
||||
DOCKER_TLS_CERTDIR: "" # https://gitlab.com/gitlab-org/gitlab-runner/issues/4501
|
||||
|
||||
# License-Scanning job is removed from GitLab 16.3
|
||||
# This is the fix for https://gitlab.com/gitlab-org/gitlab/-/issues/422791
|
||||
LICENSE_MANAGEMENT_DISABLED: "true"
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
|
|
@ -216,6 +216,28 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def self.check_single_connection_and_print_warning
|
||||
return if Gitlab::Runtime.rails_runner?
|
||||
return unless database_mode == MODE_SINGLE_DATABASE
|
||||
|
||||
Kernel.warn ERB.new(Rainbow.new.wrap(<<~EOS).red).result
|
||||
|
||||
██ ██ █████ ██████ ███ ██ ██ ███ ██ ██████
|
||||
██ ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ██
|
||||
██ █ ██ ███████ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ███
|
||||
██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
███ ███ ██ ██ ██ ██ ██ ████ ██ ██ ████ ██████
|
||||
|
||||
******************************************************************************
|
||||
Your database has a single connection, and single connections were
|
||||
deprecated in GitLab 15.9 https://docs.gitlab.com/ee/update/deprecations.html#single-database-connection-is-deprecated.
|
||||
|
||||
Please add a :ci section to your database, following these instructions:
|
||||
https://docs.gitlab.com/ee/install/installation.html#configure-gitlab-db-settings.
|
||||
******************************************************************************
|
||||
EOS
|
||||
end
|
||||
|
||||
def self.random
|
||||
"RANDOM()"
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue