Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
20eb71551a
commit
6bbf79d852
|
|
@ -35,7 +35,7 @@ review-docs-cleanup:
|
|||
|
||||
.docs-markdown-lint-image:
|
||||
# When updating the image version here, update it in /scripts/lint-doc.sh too.
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/technical-writing/docs-gitlab-com/lint-markdown:alpine-3.21-vale-3.9.3-markdownlint2-0.17.1-lychee-0.18.0
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/technical-writing/docs-gitlab-com/lint-markdown:alpine-3.21-vale-3.11.2-markdownlint2-0.17.2-lychee-0.18.1
|
||||
|
||||
docs-lint markdown:
|
||||
extends:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
StylesPath = doc/.vale
|
||||
MinAlertLevel = suggestion
|
||||
|
||||
IgnoredScopes = code, text.frontmatter.redirect_to
|
||||
|
||||
[*.md]
|
||||
BasedOnStyles = gitlab_base, gitlab_docs
|
||||
|
||||
|
|
|
|||
|
|
@ -631,7 +631,7 @@
|
|||
{"name":"rubocop-rspec","version":"3.0.5","platform":"ruby","checksum":"c6a8e29fb1b00d227c32df159e92f5ebb9e0ff734e52955fb13aff5c74977e0f"},
|
||||
{"name":"rubocop-rspec_rails","version":"2.30.0","platform":"ruby","checksum":"888112e83f9d7ef7ad2397e9d69a0b9614a4bae24f072c399804a180f80c4c46"},
|
||||
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
|
||||
{"name":"ruby-lsp","version":"0.23.10","platform":"ruby","checksum":"71dfb08ff3bdc66f92c18e49f7ce3fe772b25804bcd08a4369f70bcad1534d6c"},
|
||||
{"name":"ruby-lsp","version":"0.23.13","platform":"ruby","checksum":"a1875a9905a79a41c63d8df52bd016f238d635b64c8f0aac3639336bcf659f48"},
|
||||
{"name":"ruby-lsp-rails","version":"0.3.31","platform":"ruby","checksum":"670aed466e54b5632e4907b8dedb91d8b144917c42513e013d656af175bf8c76"},
|
||||
{"name":"ruby-lsp-rspec","version":"0.1.22","platform":"ruby","checksum":"e982edf5cd6ec1530c3f5fa7e423624ad00532ebeff7fc94e02c7516a9b759c0"},
|
||||
{"name":"ruby-magic","version":"0.6.0","platform":"ruby","checksum":"7b2138877b7d23aff812c95564eba6473b74b815ef85beb0eb792e729a2b6101"},
|
||||
|
|
@ -754,7 +754,7 @@
|
|||
{"name":"typhoeus","version":"1.4.1","platform":"ruby","checksum":"1c17db8364bd45ab302dc61e460173c3e69835896be88a3df07c206d5c55ef7c"},
|
||||
{"name":"tzinfo","version":"2.0.6","platform":"ruby","checksum":"8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b"},
|
||||
{"name":"uber","version":"0.1.0","platform":"ruby","checksum":"5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc"},
|
||||
{"name":"undercover","version":"0.6.3","platform":"ruby","checksum":"a74c4246bc3ed0a506681f9cc41e2cf353c12f1544bb2b7798807e81f2cb65fa"},
|
||||
{"name":"undercover","version":"0.6.4","platform":"ruby","checksum":"3c34fcf129b52a4993065c52612a65e5e05e77f0cac3f4f8f388114fb129ec1a"},
|
||||
{"name":"unf","version":"0.1.4","platform":"java","checksum":"49a5972ec0b3d091d3b0b2e00113f2f342b9b212f0db855eb30a629637f6d302"},
|
||||
{"name":"unf","version":"0.1.4","platform":"ruby","checksum":"4999517a531f2a955750f8831941891f6158498ec9b6cb1c81ce89388e63022e"},
|
||||
{"name":"unf_ext","version":"0.0.8.2","platform":"ruby","checksum":"90b9623ee359cc4878461c5d2eab7d3d3ce5801a680a9e7ac83b8040c5b742fa"},
|
||||
|
|
|
|||
|
|
@ -1698,7 +1698,7 @@ GEM
|
|||
ruby-fogbugz (0.3.0)
|
||||
crack (~> 0.4)
|
||||
multipart-post (~> 2.0)
|
||||
ruby-lsp (0.23.10)
|
||||
ruby-lsp (0.23.13)
|
||||
language_server-protocol (~> 3.17.0)
|
||||
prism (>= 1.2, < 2.0)
|
||||
rbs (>= 3, < 4)
|
||||
|
|
@ -1910,11 +1910,12 @@ GEM
|
|||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
undercover (0.6.3)
|
||||
undercover (0.6.4)
|
||||
base64
|
||||
bigdecimal
|
||||
imagen (>= 0.2.0)
|
||||
rainbow (>= 2.1, < 4.0)
|
||||
rugged (>= 0.27, < 1.8)
|
||||
rugged (>= 0.27, < 1.10)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
|
|
|
|||
|
|
@ -641,7 +641,7 @@
|
|||
{"name":"rubocop-rspec","version":"3.0.5","platform":"ruby","checksum":"c6a8e29fb1b00d227c32df159e92f5ebb9e0ff734e52955fb13aff5c74977e0f"},
|
||||
{"name":"rubocop-rspec_rails","version":"2.30.0","platform":"ruby","checksum":"888112e83f9d7ef7ad2397e9d69a0b9614a4bae24f072c399804a180f80c4c46"},
|
||||
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
|
||||
{"name":"ruby-lsp","version":"0.23.10","platform":"ruby","checksum":"71dfb08ff3bdc66f92c18e49f7ce3fe772b25804bcd08a4369f70bcad1534d6c"},
|
||||
{"name":"ruby-lsp","version":"0.23.13","platform":"ruby","checksum":"a1875a9905a79a41c63d8df52bd016f238d635b64c8f0aac3639336bcf659f48"},
|
||||
{"name":"ruby-lsp-rails","version":"0.3.31","platform":"ruby","checksum":"670aed466e54b5632e4907b8dedb91d8b144917c42513e013d656af175bf8c76"},
|
||||
{"name":"ruby-lsp-rspec","version":"0.1.22","platform":"ruby","checksum":"e982edf5cd6ec1530c3f5fa7e423624ad00532ebeff7fc94e02c7516a9b759c0"},
|
||||
{"name":"ruby-magic","version":"0.6.0","platform":"ruby","checksum":"7b2138877b7d23aff812c95564eba6473b74b815ef85beb0eb792e729a2b6101"},
|
||||
|
|
@ -767,7 +767,7 @@
|
|||
{"name":"typhoeus","version":"1.4.1","platform":"ruby","checksum":"1c17db8364bd45ab302dc61e460173c3e69835896be88a3df07c206d5c55ef7c"},
|
||||
{"name":"tzinfo","version":"2.0.6","platform":"ruby","checksum":"8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b"},
|
||||
{"name":"uber","version":"0.1.0","platform":"ruby","checksum":"5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc"},
|
||||
{"name":"undercover","version":"0.6.3","platform":"ruby","checksum":"a74c4246bc3ed0a506681f9cc41e2cf353c12f1544bb2b7798807e81f2cb65fa"},
|
||||
{"name":"undercover","version":"0.6.4","platform":"ruby","checksum":"3c34fcf129b52a4993065c52612a65e5e05e77f0cac3f4f8f388114fb129ec1a"},
|
||||
{"name":"unf","version":"0.1.4","platform":"java","checksum":"49a5972ec0b3d091d3b0b2e00113f2f342b9b212f0db855eb30a629637f6d302"},
|
||||
{"name":"unf","version":"0.1.4","platform":"ruby","checksum":"4999517a531f2a955750f8831941891f6158498ec9b6cb1c81ce89388e63022e"},
|
||||
{"name":"unf_ext","version":"0.0.8.2","platform":"ruby","checksum":"90b9623ee359cc4878461c5d2eab7d3d3ce5801a680a9e7ac83b8040c5b742fa"},
|
||||
|
|
|
|||
|
|
@ -1730,7 +1730,7 @@ GEM
|
|||
ruby-fogbugz (0.3.0)
|
||||
crack (~> 0.4)
|
||||
multipart-post (~> 2.0)
|
||||
ruby-lsp (0.23.10)
|
||||
ruby-lsp (0.23.13)
|
||||
language_server-protocol (~> 3.17.0)
|
||||
prism (>= 1.2, < 2.0)
|
||||
rbs (>= 3, < 4)
|
||||
|
|
@ -1944,11 +1944,12 @@ GEM
|
|||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
undercover (0.6.3)
|
||||
undercover (0.6.4)
|
||||
base64
|
||||
bigdecimal
|
||||
imagen (>= 0.2.0)
|
||||
rainbow (>= 2.1, < 4.0)
|
||||
rugged (>= 0.27, < 1.8)
|
||||
rugged (>= 0.27, < 1.10)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { __, s__, sprintf } from '~/locale';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import CrudComponent from '~/vue_shared/components/crud_component.vue';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import { convertEnvironmentScope } from '~/ci/common/private/ci_environments_dropdown';
|
||||
import {
|
||||
DEFAULT_EXCEEDS_VARIABLE_LIMIT_TEXT,
|
||||
|
|
@ -69,6 +70,7 @@ export default {
|
|||
GlTable,
|
||||
GlModal,
|
||||
CrudComponent,
|
||||
ClipboardButton,
|
||||
},
|
||||
directives: {
|
||||
GlModalDirective,
|
||||
|
|
@ -273,15 +275,12 @@ export default {
|
|||
class="gl-inline-block gl-max-w-full gl-break-anywhere"
|
||||
>{{ item.key }}</span
|
||||
>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
category="tertiary"
|
||||
icon="copy-to-clipboard"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
size="small"
|
||||
<clipboard-button
|
||||
:text="item.key"
|
||||
:title="__('Copy key')"
|
||||
:data-clipboard-text="item.key"
|
||||
:aria-label="__('Copy to clipboard')"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="item.description" class="gl-mt-2 gl-text-sm gl-text-subtle">
|
||||
|
|
@ -304,7 +303,7 @@ export default {
|
|||
v-if="!item.hidden"
|
||||
class="-gl-mr-3 gl-flex gl-items-start gl-justify-end md:gl-justify-start"
|
||||
>
|
||||
<span v-if="areValuesHidden" data-testid="hiddenValue">*****</span>
|
||||
<span v-if="areValuesHidden" data-testid="hiddenValue">•••••</span>
|
||||
<span
|
||||
v-else
|
||||
:id="`ci-variable-value-${item.id}`"
|
||||
|
|
@ -312,15 +311,12 @@ export default {
|
|||
data-testid="revealedValue"
|
||||
>{{ item.value }}</span
|
||||
>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
category="tertiary"
|
||||
icon="copy-to-clipboard"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
size="small"
|
||||
<clipboard-button
|
||||
:text="item.value"
|
||||
:title="__('Copy value')"
|
||||
:data-clipboard-text="item.value"
|
||||
:aria-label="__('Copy to clipboard')"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -331,15 +327,12 @@ export default {
|
|||
class="gl-inline-block gl-max-w-full gl-break-anywhere"
|
||||
>{{ convertEnvironmentScopeValue(item.environmentScope) }}</span
|
||||
>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
category="tertiary"
|
||||
icon="copy-to-clipboard"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
size="small"
|
||||
<clipboard-button
|
||||
:text="convertEnvironmentScopeValue(item.environmentScope)"
|
||||
:title="__('Copy environment')"
|
||||
:data-clipboard-text="convertEnvironmentScopeValue(item.environmentScope)"
|
||||
:aria-label="__('Copy to clipboard')"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
class="-gl-my-2 gl-ml-2"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -309,18 +309,22 @@ export default {
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<gl-button
|
||||
v-if="showAction"
|
||||
v-gl-tooltip
|
||||
:title="action.ariaLabel"
|
||||
:loading="isActionLoading"
|
||||
:icon="action.icon"
|
||||
class="gl-h-7 gl-w-7 !gl-rounded-full"
|
||||
:aria-label="action.ariaLabel"
|
||||
@click="action.method"
|
||||
@mouseover="setActionTooltip(true)"
|
||||
@mouseout="setActionTooltip(false)"
|
||||
/>
|
||||
<div>
|
||||
<gl-button
|
||||
v-if="showAction"
|
||||
v-gl-tooltip
|
||||
:title="action.ariaLabel"
|
||||
:loading="isActionLoading"
|
||||
size="small"
|
||||
class="!gl-rounded-full !gl-p-0"
|
||||
:aria-label="action.ariaLabel"
|
||||
@click="action.method"
|
||||
@mouseover="setActionTooltip(true)"
|
||||
@mouseout="setActionTooltip(false)"
|
||||
>
|
||||
<gl-icon :name="action.icon" size="12" />
|
||||
</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
v-if="hasSourceJob"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import RunnerGoogleCloudOption from '~/ci/runner/components/runner_google_cloud_
|
|||
|
||||
import runnerForRegistrationQuery from '../../graphql/register/runner_for_registration.query.graphql';
|
||||
import {
|
||||
STATUS_ONLINE,
|
||||
CREATION_STATE_FINISHED,
|
||||
EXECUTORS_HELP_URL,
|
||||
SERVICE_COMMANDS_HELP_URL,
|
||||
RUNNER_REGISTRATION_POLLING_INTERVAL_MS,
|
||||
|
|
@ -92,7 +92,7 @@ export default {
|
|||
captureException({ error, component: this.$options.name });
|
||||
},
|
||||
pollInterval() {
|
||||
if (this.isRunnerOnline) {
|
||||
if (this.isRunnerRegistered) {
|
||||
// stop polling
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -141,8 +141,8 @@ export default {
|
|||
runCommand() {
|
||||
return runCommand({ platform: this.platform });
|
||||
},
|
||||
isRunnerOnline() {
|
||||
return this.runner?.status === STATUS_ONLINE;
|
||||
isRunnerRegistered() {
|
||||
return this.runner?.creationState === CREATION_STATE_FINISHED;
|
||||
},
|
||||
showGoogleCloudRegistration() {
|
||||
return this.platform === GOOGLE_CLOUD_PLATFORM;
|
||||
|
|
@ -152,7 +152,7 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
isRunnerOnline(newVal, oldVal) {
|
||||
isRunnerRegistered(newVal, oldVal) {
|
||||
if (!oldVal && newVal) {
|
||||
this.$emit('runnerRegistered');
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ export default {
|
|||
this.isDrawerOpen = val;
|
||||
},
|
||||
onBeforeunload(event) {
|
||||
if (this.isRunnerOnline) {
|
||||
if (this.isRunnerRegistered) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ export default {
|
|||
<platforms-drawer :platform="platform" :open="isDrawerOpen" @close="onToggleDrawer(false)" />
|
||||
</template>
|
||||
|
||||
<section v-if="isRunnerOnline" class="gl-mt-6">
|
||||
<section v-if="isRunnerRegistered" class="gl-mt-6">
|
||||
<h2 class="gl-heading-2">🎉 {{ s__("Runners|You've registered a new runner!") }}</h2>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -150,6 +150,11 @@ export const GROUP_TYPE = 'GROUP_TYPE';
|
|||
export const PROJECT_TYPE = 'PROJECT_TYPE';
|
||||
export const RUNNER_TYPES = [INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE];
|
||||
|
||||
// CiRunnerCreationState
|
||||
|
||||
export const CREATION_STATE_STARTED = 'STARTED';
|
||||
export const CREATION_STATE_FINISHED = 'FINISHED';
|
||||
|
||||
// CiRunnerStatus
|
||||
|
||||
export const STATUS_ONLINE = 'ONLINE';
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ query getRunnerForRegistration($id: CiRunnerID!) {
|
|||
id
|
||||
description
|
||||
ephemeralAuthenticationToken
|
||||
status
|
||||
creationState
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ export default {
|
|||
<diffs-file-tree
|
||||
v-if="renderFileTree"
|
||||
class="gl-px-5"
|
||||
:total-files-count="numTotalFiles"
|
||||
@clickFile="goToFile({ path: $event.path })"
|
||||
/>
|
||||
<div class="col-12 col-md-auto diff-files-holder">
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
totalFilesCount: {
|
||||
type: [Number, String],
|
||||
default: undefined,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const treeWidth =
|
||||
|
|
@ -133,6 +138,7 @@ export default {
|
|||
<tree-list
|
||||
:hide-file-stats="hideFileStats"
|
||||
:loaded-files="loadedFiles"
|
||||
:total-files-count="totalFilesCount"
|
||||
@clickFile="onFileClick"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ export default {
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
totalFilesCount: {
|
||||
type: [Number, String],
|
||||
default: undefined,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -52,7 +57,6 @@ export default {
|
|||
'renderTreeList',
|
||||
'currentDiffFileId',
|
||||
'viewedDiffFileIds',
|
||||
'realSize',
|
||||
'fileTree',
|
||||
'allBlobs',
|
||||
'linkedFile',
|
||||
|
|
@ -191,7 +195,9 @@ export default {
|
|||
<div class="tree-list-holder flex-column gl-flex" data-testid="file-tree-container">
|
||||
<div class="gl-mb-3 gl-flex gl-items-center">
|
||||
<h5 class="gl-my-0 gl-inline-block">{{ __('Files') }}</h5>
|
||||
<gl-badge class="gl-ml-2" data-testid="file-count">{{ realSize }}</gl-badge>
|
||||
<gl-badge v-if="totalFilesCount != null" class="gl-ml-2" data-testid="file-count">{{
|
||||
totalFilesCount
|
||||
}}</gl-badge>
|
||||
<gl-button-group class="gl-ml-auto">
|
||||
<gl-button
|
||||
v-gl-tooltip.hover
|
||||
|
|
|
|||
|
|
@ -115,13 +115,13 @@ export default {
|
|||
>
|
||||
<template #form>
|
||||
<group-select
|
||||
class="gl-w-full"
|
||||
:label="__('Group')"
|
||||
:initial-selection="namespaceId"
|
||||
:description="__('Choose the top-level group for your repository imports.')"
|
||||
input-name="group"
|
||||
input-id="group"
|
||||
block
|
||||
fluid-width
|
||||
@input="handleGroupSelected"
|
||||
@clear="handleGroupSelected"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { mapState } from 'pinia';
|
|||
import DiffsFileTree from '~/diffs/components/diffs_file_tree.vue';
|
||||
import { useDiffsList } from '~/rapid_diffs/stores/diffs_list';
|
||||
import { useFileBrowser } from '~/diffs/stores/file_browser';
|
||||
import { useDiffsView } from '~/rapid_diffs/stores/diffs_view';
|
||||
|
||||
export default {
|
||||
name: 'FileBrowser',
|
||||
|
|
@ -10,6 +11,7 @@ export default {
|
|||
DiffsFileTree,
|
||||
},
|
||||
computed: {
|
||||
...mapState(useDiffsView, ['totalFilesCount']),
|
||||
...mapState(useDiffsList, ['loadedFiles']),
|
||||
...mapState(useFileBrowser, ['fileBrowserVisible']),
|
||||
},
|
||||
|
|
@ -26,6 +28,7 @@ export default {
|
|||
v-if="fileBrowserVisible"
|
||||
floating-resize
|
||||
:loaded-files="loadedFiles"
|
||||
:total-files-count="totalFilesCount"
|
||||
@clickFile="clickFile"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -25,14 +25,11 @@ class RapidDiffsFacade {
|
|||
document.querySelector('[data-diffs-list]'),
|
||||
this.DiffFileImplementation,
|
||||
);
|
||||
const { reloadStreamUrl, metadataEndpoint, diffFilesEndpoint } =
|
||||
const { reloadStreamUrl, diffsStatsEndpoint, diffFilesEndpoint } =
|
||||
document.querySelector('[data-rapid-diffs]').dataset;
|
||||
useDiffsView(pinia).metadataEndpoint = metadataEndpoint;
|
||||
useDiffsView(pinia).diffsStatsEndpoint = diffsStatsEndpoint;
|
||||
useDiffsView(pinia)
|
||||
.loadMetadata()
|
||||
.then(() => {
|
||||
initHiddenFilesWarning();
|
||||
})
|
||||
.loadDiffsStats()
|
||||
.catch((error) => {
|
||||
createAlert({
|
||||
message: __('Failed to load additional diffs information. Try reloading the page.'),
|
||||
|
|
@ -46,6 +43,7 @@ class RapidDiffsFacade {
|
|||
});
|
||||
});
|
||||
initViewSettings({ pinia, streamUrl: reloadStreamUrl });
|
||||
initHiddenFilesWarning();
|
||||
document.addEventListener(DIFF_FILE_MOUNTED, useDiffsList(pinia).addLoadedFile);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@ export async function initHiddenFilesWarning() {
|
|||
el,
|
||||
pinia,
|
||||
computed: {
|
||||
...mapState(useDiffsView, ['diffStats']),
|
||||
...mapState(useDiffsView, ['overflow', 'totalFilesCount']),
|
||||
},
|
||||
render(h) {
|
||||
if (!this.diffStats?.renderOverflowWarning) return null;
|
||||
if (!this.overflow) return null;
|
||||
|
||||
return h(HiddenFilesWarning, {
|
||||
props: {
|
||||
total: this.diffStats?.realSize,
|
||||
visible: this.diffStats?.size,
|
||||
plainDiffPath: this.diffStats?.plainDiffPath,
|
||||
emailPatchPath: this.diffStats?.emailPatchPath,
|
||||
total: this.totalFilesCount,
|
||||
visible: this.overflow?.visibleCount,
|
||||
plainDiffPath: this.overflow?.diffPath,
|
||||
emailPatchPath: this.overflow?.emailPath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const initSettingsApp = (el, pinia) => {
|
|||
'viewType',
|
||||
'fileByFileMode',
|
||||
'singleFileMode',
|
||||
'diffStats',
|
||||
'diffsStats',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -40,9 +40,9 @@ const initSettingsApp = (el, pinia) => {
|
|||
diffViewType: this.viewType,
|
||||
viewDiffsFileByFile: this.singleFileMode,
|
||||
isLoading: this.isLoading,
|
||||
addedLines: this.diffStats?.addedLines,
|
||||
removedLines: this.diffStats?.removedLines,
|
||||
diffsCount: this.diffStats?.diffsCount,
|
||||
addedLines: this.diffsStats?.addedLines,
|
||||
removedLines: this.diffsStats?.removedLines,
|
||||
diffsCount: this.diffsStats?.diffsCount,
|
||||
},
|
||||
on: {
|
||||
updateDiffViewType: this.updateViewType,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { queueRedisHllEvents } from '~/diffs/utils/queue_events';
|
|||
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { useDiffsList } from '~/rapid_diffs/stores/diffs_list';
|
||||
import store from '~/mr_notes/stores';
|
||||
|
||||
export const useDiffsView = defineStore('diffsView', {
|
||||
state() {
|
||||
|
|
@ -21,28 +20,38 @@ export const useDiffsView = defineStore('diffsView', {
|
|||
singleFileMode: false,
|
||||
updateUserEndpoint: undefined,
|
||||
streamUrl: undefined,
|
||||
metadataEndpoint: undefined,
|
||||
diffStats: null,
|
||||
diffsStatsEndpoint: undefined,
|
||||
diffsStats: null,
|
||||
overflow: null,
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
async loadMetadata() {
|
||||
// TODO: refactor this to our own Pinia stores
|
||||
store.state.diffs.endpointMetadata = this.metadataEndpoint;
|
||||
store.state.diffs.diffViewType = this.viewType;
|
||||
store.state.diffs.showWhitespace = this.showWhitespace;
|
||||
await store.dispatch('diffs/fetchDiffFilesMeta');
|
||||
this.diffStats = {
|
||||
addedLines: store.state.diffs.addedLines,
|
||||
removedLines: store.state.diffs.removedLines,
|
||||
size: store.state.diffs.size,
|
||||
realSize: store.state.diffs.realSize,
|
||||
plainDiffPath: store.state.diffs.plainDiffPath,
|
||||
emailPatchPath: store.state.diffs.emailPatchPath,
|
||||
renderOverflowWarning: store.state.diffs.renderOverflowWarning,
|
||||
// we will be using a number for that after refactoring
|
||||
diffsCount: parseInt(store.state.diffs.realSize, 10),
|
||||
async loadDiffsStats() {
|
||||
const { data } = await axios.get(this.diffsStatsEndpoint);
|
||||
const {
|
||||
added_lines: addedLines,
|
||||
removed_lines: removedLines,
|
||||
diffs_count: diffsCount,
|
||||
} = data.diffs_stats;
|
||||
this.diffsStats = {
|
||||
addedLines,
|
||||
removedLines,
|
||||
diffsCount,
|
||||
};
|
||||
if (data.overflow) {
|
||||
const {
|
||||
visible_count: visibleCount,
|
||||
email_path: emailPath,
|
||||
diff_path: diffPath,
|
||||
} = data.overflow || {};
|
||||
this.overflow = {
|
||||
visibleCount,
|
||||
emailPath,
|
||||
diffPath,
|
||||
};
|
||||
} else {
|
||||
this.overflow = null;
|
||||
}
|
||||
},
|
||||
updateDiffView() {
|
||||
if (this.singleFileMode) {
|
||||
|
|
@ -68,7 +77,6 @@ export const useDiffsView = defineStore('diffsView', {
|
|||
// we don't have to wait for the setting to be saved since whitespace param is passed explicitly
|
||||
axios.put(this.updateUserEndpoint, { show_whitespace_in_diffs: value });
|
||||
}
|
||||
this.loadMetadata();
|
||||
this.updateDiffView();
|
||||
},
|
||||
},
|
||||
|
|
@ -77,5 +85,8 @@ export const useDiffsView = defineStore('diffsView', {
|
|||
// w: '1' means ignore whitespace, app/helpers/diff_helper.rb#hide_whitespace?
|
||||
return { view: this.viewType, w: this.showWhitespace ? '0' : '1' };
|
||||
},
|
||||
totalFilesCount() {
|
||||
return this.diffsStats?.diffsCount;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlModalDirective, GlTooltipDirective, GlIcon, GlButton } from '@gitlab/ui';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import { GlModalDirective, GlIcon, GlButton, GlSprintf } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import {
|
||||
|
|
@ -19,23 +19,18 @@ export default {
|
|||
GlIcon,
|
||||
SearchModal,
|
||||
GlButton,
|
||||
GlSprintf,
|
||||
},
|
||||
i18n: {
|
||||
searchBtnText: __('Search or go to…'),
|
||||
searchKbdHelp: sprintf(
|
||||
s__('GlobalSearch|Type %{kbdOpen}/%{kbdClose} to search'),
|
||||
{ kbdOpen: '<kbd>', kbdClose: '</kbd>' },
|
||||
false,
|
||||
),
|
||||
searchKbdHelp: s__('GlobalSearch|Search or go to… (or use the / keyboard shortcut)'),
|
||||
searchBtnText: s__('GlobalSearch|Search or go to… %{kbdStart}/%{kbdEnd}'),
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
GlModal: GlModalDirective,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin(), trackingMixin],
|
||||
data() {
|
||||
return {
|
||||
searchTooltip: this.$options.i18n.searchKbdHelp,
|
||||
isNarrowScreen: false,
|
||||
};
|
||||
},
|
||||
|
|
@ -50,12 +45,6 @@ export default {
|
|||
handleNarrowScreenChange({ matches }) {
|
||||
this.isNarrowScreen = matches;
|
||||
},
|
||||
hideSearchTooltip() {
|
||||
this.searchTooltip = '';
|
||||
},
|
||||
showSearchTooltip() {
|
||||
this.searchTooltip = this.$options.i18n.searchKbdHelp;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -63,26 +52,33 @@ export default {
|
|||
<template>
|
||||
<div
|
||||
v-if="glFeatures.searchButtonTopRight"
|
||||
:class="{ 'border-0 gl-max-w-26 gl-rounded-base': !isNarrowScreen }"
|
||||
:class="{ 'border-0 gl-rounded-base': !isNarrowScreen }"
|
||||
>
|
||||
<gl-button
|
||||
id="super-sidebar-search"
|
||||
v-gl-tooltip.bottom.html="searchTooltip"
|
||||
v-gl-modal="$options.SEARCH_MODAL_ID"
|
||||
class="focus:!gl-focus"
|
||||
:title="$options.i18n.searchBtnText"
|
||||
:aria-label="$options.i18n.searchBtnText"
|
||||
class="gl-relative focus:!gl-focus"
|
||||
:title="$options.i18n.searchKbdHelp"
|
||||
:aria-label="$options.i18n.searchKbdHelp"
|
||||
:class="
|
||||
isNarrowScreen
|
||||
? 'shadow-none bg-transparent gl-border gl-w-6 !gl-p-0'
|
||||
: 'user-bar-button gl-w-full !gl-justify-start !gl-pr-7'
|
||||
: 'user-bar-button gl-w-full !gl-justify-start !gl-pr-15'
|
||||
"
|
||||
data-testid="super-sidebar-search-button"
|
||||
@click="trackEvent('click_search_button_to_activate_command_palette', { label: 'top_right' })"
|
||||
>
|
||||
<gl-icon name="search" />
|
||||
<span v-if="!isNarrowScreen">{{ $options.i18n.searchBtnText }}</span>
|
||||
<span v-if="!isNarrowScreen">
|
||||
<gl-sprintf :message="$options.i18n.searchBtnText">
|
||||
<template #kbd="{ content }">
|
||||
<span class="gl-absolute gl-right-4"
|
||||
><kbd>{{ content }}</kbd></span
|
||||
>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</gl-button>
|
||||
<search-modal @shown="hideSearchTooltip" @hidden="showSearchTooltip" />
|
||||
<search-modal />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
fluidWidth: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -207,6 +212,7 @@ export default {
|
|||
ref="listbox"
|
||||
v-model="selected"
|
||||
:block="block"
|
||||
:fluid-width="fluidWidth"
|
||||
:header-text="headerText"
|
||||
:reset-button-label="resetButtonLabel"
|
||||
:toggle-text="toggleText"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
fluidWidth: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -141,6 +146,7 @@ export default {
|
|||
:fetch-items="fetchGroups"
|
||||
:fetch-initial-selection="fetchInitialGroup"
|
||||
:block="block"
|
||||
:fluid-width="fluidWidth"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template #error>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
// Do not change or manually disable this
|
||||
// Instead, call `- disable_fixed_body_scroll` inside your page's HAML template if you need to hide the scroll
|
||||
// Custom class is used instead of Tailwind so people can discover this, do not replace this with Tailwind analog
|
||||
.body-fixed-scrollbar {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
&.limit-container-width {
|
||||
.flash-container.sticky {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- if !@lazy
|
||||
- helpers.add_page_startup_api_call @metadata_endpoint
|
||||
- helpers.add_page_startup_api_call @diffs_stats_endpoint
|
||||
- helpers.add_page_startup_api_call @diff_files_endpoint
|
||||
- if @stream_url
|
||||
- helpers.content_for :startup_js do
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
streamRequest: fetch('#{Gitlab::UrlSanitizer.sanitize(@stream_url)}', { signal: controller.signal })
|
||||
}
|
||||
|
||||
.rd-app{ data: { rapid_diffs: true, reload_stream_url: @reload_stream_url, metadata_endpoint: @metadata_endpoint, diff_files_endpoint: @diff_files_endpoint } }
|
||||
.rd-app{ data: { rapid_diffs: true, reload_stream_url: @reload_stream_url, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint } }
|
||||
.rd-app-header
|
||||
.rd-app-file-browser-toggle
|
||||
%div{ data: { file_browser_toggle: true } }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module RapidDiffs
|
|||
show_whitespace:,
|
||||
diff_view:,
|
||||
update_user_endpoint:,
|
||||
metadata_endpoint:,
|
||||
diffs_stats_endpoint:,
|
||||
diff_files_endpoint:,
|
||||
lazy: false
|
||||
)
|
||||
|
|
@ -21,7 +21,7 @@ module RapidDiffs
|
|||
@show_whitespace = show_whitespace
|
||||
@diff_view = diff_view
|
||||
@update_user_endpoint = update_user_endpoint
|
||||
@metadata_endpoint = metadata_endpoint
|
||||
@diffs_stats_endpoint = diffs_stats_endpoint
|
||||
@diff_files_endpoint = diff_files_endpoint
|
||||
@lazy = lazy
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
before_action only: [:index] do
|
||||
push_frontend_feature_flag(:importer_user_mapping, current_user)
|
||||
push_frontend_feature_flag(:importer_user_mapping_reassignment_csv, current_user)
|
||||
push_frontend_feature_flag(:importer_user_mapping_allow_bypass_of_confirmation, @group)
|
||||
push_frontend_feature_flag(:service_accounts_crud, @group)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
@stream_url = diffs_stream_url(@commit, streaming_offset, diff_view)
|
||||
@diffs_slice = @commit.first_diffs_slice(streaming_offset, commit_diff_options)
|
||||
@diff_files_endpoint = diff_files_metadata_namespace_project_commit_path
|
||||
@diffs_stats_endpoint = diffs_stats_namespace_project_commit_path
|
||||
|
||||
show
|
||||
end
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class Projects::CompareController < Projects::ApplicationController
|
|||
@show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs
|
||||
@reload_stream_url = diffs_stream_namespace_project_compare_index_path(**compare_params)
|
||||
@diff_files_endpoint = diff_files_metadata_namespace_project_compare_index_path(**compare_params)
|
||||
@diffs_stats_endpoint = diffs_stats_namespace_project_compare_index_path(**compare_params)
|
||||
@update_current_user_path = expose_path(api_v4_user_preferences_path)
|
||||
|
||||
show
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
|
|||
@stream_url = project_new_merge_request_diffs_stream_path(@project, merge_request: merge_request)
|
||||
@reload_stream_url = project_new_merge_request_diffs_stream_path(@project, merge_request: merge_request)
|
||||
@diff_files_endpoint = project_new_merge_request_diff_files_metadata_path(@project, merge_request: merge_request)
|
||||
@diffs_stats_endpoint = project_new_merge_request_diffs_stats_path(@project, merge_request: merge_request)
|
||||
|
||||
define_new_vars
|
||||
end
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
@stream_url = diffs_stream_url(@merge_request, streaming_offset, diff_view)
|
||||
@diffs_slice = @merge_request.first_diffs_slice(streaming_offset)
|
||||
@diff_files_endpoint = diff_files_metadata_namespace_project_merge_request_path
|
||||
@diffs_stats_endpoint = diffs_stats_namespace_project_merge_request_path
|
||||
|
||||
show_merge_request
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Mutations
|
||||
module Ci
|
||||
module Runner
|
||||
class BulkPause < BaseMutation
|
||||
graphql_name 'RunnerBulkPause'
|
||||
|
||||
RunnerID = ::Types::GlobalIDType[::Ci::Runner]
|
||||
|
||||
argument :ids, [RunnerID],
|
||||
required: true,
|
||||
description: 'IDs of the runners to pause or unpause.'
|
||||
|
||||
argument :paused, GraphQL::Types::Boolean,
|
||||
required: true,
|
||||
description: 'Indicates the runner is not allowed to receive jobs.'
|
||||
|
||||
field :updated_count,
|
||||
::GraphQL::Types::Int,
|
||||
null: true,
|
||||
description: 'Number of records effectively updated. ' \
|
||||
'Only present if operation was performed synchronously.'
|
||||
|
||||
field :updated_runners, # rubocop:disable GraphQL/ExtractType -- Same as bulk_delete
|
||||
[Types::Ci::RunnerType],
|
||||
null: true,
|
||||
description: 'Runners after mutation.'
|
||||
|
||||
def resolve(**runner_attrs)
|
||||
response = { updated_count: 0, updated_runners: [], errors: [] }
|
||||
ids = runner_attrs[:ids]
|
||||
runner_ids = model_ids_of(ids)
|
||||
runners = find_all_runners_by_ids(runner_ids)
|
||||
if runners.any?
|
||||
result = ::Ci::Runners::BulkPauseRunnersService
|
||||
.new(runners: runners, current_user: current_user, paused: runner_attrs[:paused])
|
||||
.execute
|
||||
result.payload.slice(:updated_count, :updated_runners, :errors)
|
||||
else
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_ids_of(global_ids)
|
||||
global_ids.filter_map { |gid| gid.model_id.to_i }
|
||||
end
|
||||
|
||||
def find_all_runners_by_ids(ids)
|
||||
return ::Ci::Runner.none if ids.blank?
|
||||
|
||||
limit = ::Ci::Runners::BulkPauseRunnersService::RUNNER_LIMIT
|
||||
::Ci::Runner.id_in(ids).limit(limit + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,10 +6,10 @@ module Types
|
|||
graphql_name 'CiRunnerCreationState'
|
||||
|
||||
value 'STARTED',
|
||||
description: 'Applies to a runner that has been created, but not is not yet registered and running.',
|
||||
description: 'Applies to a runner that has been created, but is not yet registered and running.',
|
||||
value: 'started'
|
||||
value 'FINISHED',
|
||||
description: 'Applies to a runner that has been registered and has polled for CI jobs at least once.',
|
||||
description: 'Applies to a runner that has been registered and has polled for CI/CD jobs at least once.',
|
||||
value: 'finished'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ module Types
|
|||
mount_mutation Mutations::Ci::PipelineTrigger::Update, experiment: { milestone: '16.3' }
|
||||
mount_mutation Mutations::Ci::ProjectCiCdSettingsUpdate
|
||||
mount_mutation Mutations::Ci::Runner::BulkDelete, experiment: { milestone: '15.3' }
|
||||
mount_mutation Mutations::Ci::Runner::BulkPause, experiment: { milestone: '17.11' }
|
||||
mount_mutation Mutations::Ci::Runner::Cache::Clear
|
||||
mount_mutation Mutations::Ci::Runner::Create, experiment: { milestone: '15.10' }
|
||||
mount_mutation Mutations::Ci::Runner::Delete
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ module Types
|
|||
GraphQL::Types::Int,
|
||||
null: false,
|
||||
description: 'Number of downvotes the work item has received.'
|
||||
field :new_custom_emoji_path,
|
||||
GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Path to create a new custom emoji.'
|
||||
field :upvotes,
|
||||
GraphQL::Types::Int,
|
||||
null: false,
|
||||
|
|
@ -34,6 +38,12 @@ module Types
|
|||
BatchLoaders::AwardEmojiVotesBatchLoader
|
||||
.load_upvotes(object.work_item, awardable_class: 'Issue')
|
||||
end
|
||||
|
||||
def new_custom_emoji_path
|
||||
return unless context[:current_user]&.can?(:create_custom_emoji, object.work_item.project.namespace)
|
||||
|
||||
::Gitlab::Routing.url_helpers.new_group_custom_emoji_path(object.work_item.project.namespace)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Graphql/AuthorizeTypes
|
||||
end
|
||||
|
|
|
|||
|
|
@ -331,6 +331,18 @@ module ApplicationHelper
|
|||
class_names
|
||||
end
|
||||
|
||||
def disable_fixed_body_scroll
|
||||
content_for :disable_fixed_body_scroll, true
|
||||
end
|
||||
|
||||
def body_scroll_classes
|
||||
return '' unless Feature.enabled?(:force_scrollbar, current_user, type: :beta)
|
||||
return '' if content_for(:disable_fixed_body_scroll).present?
|
||||
|
||||
# Custom class is used instead of Tailwind so people can discover this, do not replace this with Tailwind analog
|
||||
'body-fixed-scrollbar'
|
||||
end
|
||||
|
||||
def system_message_class
|
||||
class_names = []
|
||||
|
||||
|
|
|
|||
|
|
@ -495,14 +495,15 @@ module Ci
|
|||
ensure_runner_queue_value == value if value.present?
|
||||
end
|
||||
|
||||
def heartbeat
|
||||
def heartbeat(creation_state: nil)
|
||||
##
|
||||
# We can safely ignore writes performed by a runner heartbeat. We do
|
||||
# not want to upgrade database connection proxy to use the primary
|
||||
# database after heartbeat write happens.
|
||||
#
|
||||
::Gitlab::Database::LoadBalancing::SessionMap.current(load_balancer).without_sticky_writes do
|
||||
values = { contacted_at: Time.current, creation_state: :finished }
|
||||
values = { contacted_at: Time.current }
|
||||
values[:creation_state] = creation_state if creation_state.present?
|
||||
|
||||
merge_cache_attributes(values)
|
||||
|
||||
|
|
@ -511,13 +512,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def clear_heartbeat
|
||||
cleared_attributes = { contacted_at: nil }
|
||||
|
||||
merge_cache_attributes(cleared_attributes)
|
||||
update_columns(cleared_attributes)
|
||||
end
|
||||
|
||||
def pick_build!(build)
|
||||
tick_runner_queue if matches_build?(build)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ module Ci
|
|||
read_attribute(:contacted_at)
|
||||
end
|
||||
|
||||
def heartbeat(values, update_contacted_at: true)
|
||||
def heartbeat(values)
|
||||
##
|
||||
# We can safely ignore writes performed by a runner heartbeat. We do
|
||||
# not want to upgrade database connection proxy to use the primary
|
||||
|
|
@ -150,7 +150,7 @@ module Ci
|
|||
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config,
|
||||
:executor, :runtime_features) || {}
|
||||
|
||||
values.merge!(contacted_at: Time.current, creation_state: :finished) if update_contacted_at
|
||||
values.merge!(contacted_at: Time.current, creation_state: :finished)
|
||||
|
||||
if values.include?(:executor)
|
||||
values[:executor_type] = EXECUTOR_NAME_TO_TYPES.fetch(values.delete(:executor), :unknown)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ module Clusters
|
|||
completed: 1,
|
||||
failed: 2
|
||||
}
|
||||
|
||||
enum :deletion_strategy, {
|
||||
never: 0,
|
||||
on_stop: 1,
|
||||
on_delete: 2
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ module Ci
|
|||
|
||||
def status
|
||||
return :stale if stale?
|
||||
return :never_contacted if contacted_at.nil?
|
||||
|
||||
# NOTE: We can't use finished_creation_state? here as we need to check cached value
|
||||
return :never_contacted unless creation_state == 'finished'
|
||||
return :online if online? && creation_state == 'finished'
|
||||
|
||||
online? ? :online : :offline
|
||||
:offline
|
||||
end
|
||||
|
||||
def online?
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ module UseSqlFunctionForPrimaryKeyLookups
|
|||
|
||||
class_methods do
|
||||
def _query_by_sql(sql, ...)
|
||||
return super unless Feature.enabled?(:use_sql_functions_for_primary_key_lookups, Feature.current_request)
|
||||
|
||||
replaced = try_replace_with_function_call(sql)
|
||||
|
||||
return super unless replaced
|
||||
|
|
@ -14,9 +12,7 @@ module UseSqlFunctionForPrimaryKeyLookups
|
|||
super(replaced.arel, ...)
|
||||
end
|
||||
|
||||
def cached_find_by_statement(key, &block)
|
||||
return super unless Feature.enabled?(:use_sql_functions_for_primary_key_lookups, Feature.current_request)
|
||||
|
||||
def cached_find_by_statement(key, &_block)
|
||||
transformed_block = proc do |params|
|
||||
original = yield(params)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
module Runners
|
||||
class BulkPauseRunnersService
|
||||
attr_reader :current_user, :runners
|
||||
|
||||
RUNNER_LIMIT = 50
|
||||
|
||||
# @param runners [Array<Ci::Runner>] the runners to pause or unpause
|
||||
# @param current_user [User] the user performing the operation
|
||||
# @param paused [Boolean] action of pausing or unpausing
|
||||
def initialize(runners:, current_user:, paused:)
|
||||
@runners = runners
|
||||
@current_user = current_user
|
||||
@paused = paused
|
||||
end
|
||||
|
||||
def execute
|
||||
if runners.present?
|
||||
# pause the runners
|
||||
return pause_runners(@paused)
|
||||
end
|
||||
|
||||
ServiceResponse.success(payload: { updated_count: 0, updated_runners: [], errors: [] })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pause_runners(paused)
|
||||
active = !paused
|
||||
runner_count = runners.limit(RUNNER_LIMIT + 1).count
|
||||
authorized_runners_ids, unauthorized_runners_ids = compute_authorized_runners
|
||||
runners_to_be_updated = Ci::Runner.id_in(authorized_runners_ids)
|
||||
runners_to_be_updated.update(active: active)
|
||||
ServiceResponse.success(
|
||||
payload: {
|
||||
updated_count: runners_to_be_updated.count,
|
||||
updated_runners: runners_to_be_updated,
|
||||
errors: error_messages(runner_count, authorized_runners_ids, unauthorized_runners_ids)
|
||||
})
|
||||
end
|
||||
|
||||
def compute_authorized_runners
|
||||
current_user.ci_owned_runners.load # preload the owned runners to avoid an N+1
|
||||
|
||||
authorized_runners, unauthorized_runners =
|
||||
runners.limit(RUNNER_LIMIT)
|
||||
.partition { |runner| Ability.allowed?(current_user, :update_runner, runner) }
|
||||
[authorized_runners.map(&:id), unauthorized_runners.map(&:id)]
|
||||
end
|
||||
|
||||
def error_messages(runner_count, authorized_runners_ids, unauthorized_runners_ids)
|
||||
errors = []
|
||||
|
||||
if runner_count > RUNNER_LIMIT
|
||||
errors << "Can only pause up to #{RUNNER_LIMIT} runners per call. Ignored the remaining runner(s)."
|
||||
end
|
||||
|
||||
if authorized_runners_ids.empty?
|
||||
errors << "User does not have permission to update / pause any of the runners"
|
||||
elsif unauthorized_runners_ids.any?
|
||||
failed_ids = unauthorized_runners_ids.map { |runner_id| "##{runner_id}" }.join(', ')
|
||||
errors << "User does not have permission to update / pause runner(s) #{failed_ids}"
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -20,8 +20,6 @@ module Ci
|
|||
runner_manager = runner.runner_managers.find_by_system_xid!(system_id)
|
||||
runner_manager.destroy!
|
||||
|
||||
runner.clear_heartbeat if runner.runner_managers.empty?
|
||||
|
||||
ServiceResponse.success
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- disable_fixed_body_scroll
|
||||
- page_title _("IDE"), @project.full_name
|
||||
- add_page_specific_style 'page_bundles/web_ide_loader'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- page_classes = page_class << @html_class
|
||||
- page_classes = [user_application_color_mode, user_application_theme, page_classes.flatten.compact]
|
||||
- body_classes = [user_tab_width, @body_class, client_class_list, *custom_diff_color_classes]
|
||||
- body_classes = [user_tab_width, @body_class, client_class_list, body_scroll_classes, *custom_diff_color_classes]
|
||||
|
||||
!!! 5
|
||||
%html{ lang: I18n.locale, class: page_classes }
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@
|
|||
.container-fluid{ class: [container_class] }
|
||||
= render "commit_box"
|
||||
= render "ci_menu"
|
||||
- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, metadata_endpoint: @endpoint_metadata_url, diff_files_endpoint: @diff_files_endpoint }
|
||||
- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint }
|
||||
= render ::RapidDiffs::AppComponent.new(**args)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
.container-fluid{ class: [container_class] }
|
||||
= render "projects/commits/commit_list" unless hide_commit_list
|
||||
.container-fluid
|
||||
- args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: nil, show_whitespace: @show_whitespace_default, diff_view: diff_view, metadata_endpoint: nil, update_user_endpoint: @update_current_user_path, diff_files_endpoint: @diff_files_endpoint, lazy: true }
|
||||
- args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: nil, show_whitespace: @show_whitespace_default, diff_view: diff_view, diffs_stats_endpoint: @diffs_stats_endpoint, update_user_endpoint: @update_current_user_path, diff_files_endpoint: @diff_files_endpoint, lazy: true }
|
||||
= render ::RapidDiffs::AppComponent.new(**args)
|
||||
- else
|
||||
.container-fluid
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
- add_page_specific_style 'page_bundles/merge_request_creation_rapid_diffs'
|
||||
|
||||
= render "page" do
|
||||
- args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: diff_view, update_user_endpoint: expose_path(api_v4_user_preferences_path), metadata_endpoint: nil, diff_files_endpoint: @diff_files_endpoint, lazy: true }
|
||||
- args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: diff_view, update_user_endpoint: expose_path(api_v4_user_preferences_path), diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, lazy: true }
|
||||
= render ::RapidDiffs::AppComponent.new(**args)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
- @content_class = 'rd-page-container diffs-container-limited'
|
||||
|
||||
= render 'page'
|
||||
- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, metadata_endpoint: @endpoint_metadata_url, diff_files_endpoint: @diff_files_endpoint }
|
||||
- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint }
|
||||
= render ::RapidDiffs::AppComponent.new(**args) do |c|
|
||||
- c.with_diffs_list do
|
||||
= render RapidDiffs::MergeRequestDiffFileComponent.with_collection(@diffs_slice, merge_request: @merge_request, parallel_view: @diff_view == :parallel)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- disable_fixed_body_scroll
|
||||
- board = local_assigns.fetch(:board, nil)
|
||||
- @no_container = true
|
||||
- @content_wrapper_class = "#{@content_wrapper_class} gl-relative gl-pb-0"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
name: use_sql_functions_for_primary_key_lookups
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135196
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429479
|
||||
milestone: '16.6'
|
||||
type: development
|
||||
group: group::database frameworks
|
||||
name: force_scrollbar
|
||||
feature_issue_url:
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186498
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/534572
|
||||
milestone: '17.11'
|
||||
group: group::personal productivity
|
||||
type: beta
|
||||
default_enabled: false
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: search_sidekiq_default_concurrency_limit
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/498212
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/169034
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/498992
|
||||
milestone: '17.6'
|
||||
group: group::global search
|
||||
type: ops
|
||||
default_enabled: true
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: importer_user_mapping_allow_bypass_of_confirmation
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/534328
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/187616
|
||||
rollout_issue_url:
|
||||
milestone: '17.11'
|
||||
group: group::import and integrate
|
||||
type: wip
|
||||
default_enabled: false
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
key_path: counts.orphaned_namespaces
|
||||
description: Whether orphaned namespaces are present
|
||||
product_group: organizations
|
||||
value_type: number
|
||||
value_type: boolean
|
||||
status: active
|
||||
milestone: "17.6"
|
||||
instrumentation_class: OrphanedNamespacesMetric
|
||||
|
|
|
|||
|
|
@ -8,14 +8,6 @@ description: Persists escalation status information for incidents
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65206
|
||||
milestone: '14.2'
|
||||
gitlab_schema: gitlab_main_cell
|
||||
desired_sharding_key:
|
||||
namespace_id:
|
||||
references: namespaces
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: issue_id
|
||||
table: issues
|
||||
sharding_key: namespace_id
|
||||
belongs_to: issue
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
||||
table_size: small
|
||||
desired_sharding_key_migration_job_name: BackfillIncidentManagementIssuableEscalationStatusesNamespaceId
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDeletionStrategyToClustersManagedResources < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
|
||||
def change
|
||||
add_column :clusters_managed_resources, :deletion_strategy, :integer, limit: 2, null: false, default: 0
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIncidentManagementIssuableEscalationStatusesNamespaceIdNotNull < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_not_null_constraint :incident_management_issuable_escalation_statuses, :namespace_id
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint :incident_management_issuable_escalation_statuses, :namespace_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
cc4d89c14aca832615742df8bf39df63b9b577f7d18a06bdc3db077c87184ba2
|
||||
|
|
@ -0,0 +1 @@
|
|||
3bd2e48758b0c19d847b7ca71be04196a527d7f206e45863349c61dfa00cca08
|
||||
|
|
@ -12602,6 +12602,7 @@ CREATE TABLE clusters_managed_resources (
|
|||
status smallint DEFAULT 0 NOT NULL,
|
||||
template_name text,
|
||||
tracked_objects jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
deletion_strategy smallint DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT check_4f81a98847 CHECK ((char_length(template_name) <= 1024))
|
||||
);
|
||||
|
||||
|
|
@ -15663,7 +15664,8 @@ CREATE TABLE incident_management_issuable_escalation_statuses (
|
|||
escalations_started_at timestamp with time zone,
|
||||
resolved_at timestamp with time zone,
|
||||
status smallint DEFAULT 0 NOT NULL,
|
||||
namespace_id bigint
|
||||
namespace_id bigint,
|
||||
CONSTRAINT check_ad48232311 CHECK ((namespace_id IS NOT NULL))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE incident_management_issuable_escalation_statuses_id_seq
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ level: warning
|
|||
link: https://docs.gitlab.com/development/documentation/topic_types/concept/#concept-topic-titles
|
||||
ignorecase: true
|
||||
nonword: true
|
||||
scope: raw
|
||||
scope: heading
|
||||
tokens:
|
||||
- '\#+ How it works'
|
||||
- '\#+ Limitations'
|
||||
- '\#+ Overview'
|
||||
- '\#+ Use cases?'
|
||||
- '\#+ Important notes?'
|
||||
- 'How it works'
|
||||
- 'Limitations'
|
||||
- 'Overview'
|
||||
- 'Use cases?'
|
||||
- 'Important notes?'
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ vocab: false
|
|||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '\n<<<<<<< .+\n|\n=======\n|\n>>>>>>> .+\n'
|
||||
- '\n(?:<<<<<<< .+|=======|>>>>>>> .+)\n'
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ ignorecase: true
|
|||
link: https://docs.gitlab.com/development/documentation/styleguide/#punctuation
|
||||
scope: raw
|
||||
raw:
|
||||
- '[ ]'
|
||||
- '[\u202F\u00A0\u200B]'
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ level: suggestion
|
|||
nonword: true
|
||||
ignorecase: true
|
||||
tokens:
|
||||
- "GitLab v?(2[^[0-9]]|4|5|6|7|8|9|10|11|12|13|14)"
|
||||
- "GitLab v?(2[^0-9]|[4-9]|1[0-4])"
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ vocab: false
|
|||
level: suggestion
|
||||
scope: raw
|
||||
raw:
|
||||
- '!\[[^\]]*\]\([^\)]*_v(1[01234]|[345789])[^\)]*\)'
|
||||
- '!\[[^\]]*\]\([^\)]*_v(1[0-4]|[3-9])[^\)]*\)'
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ When a server-side backup is collected, the restore process defaults to use the
|
|||
node that hosts each repository is responsible for pulling the necessary backup data directly from object storage.
|
||||
|
||||
1. [Configure a server-side backup destination in Gitaly](../gitaly/configure_gitaly.md#configure-server-side-backups).
|
||||
1. Start a server-side backup restore process and specifying the ID of the backup you wish to restore:
|
||||
1. Start a server-side backup restore process and specifying the [ID of the backup](backup_archive_process.md#backup-id) you wish to restore:
|
||||
|
||||
{{< tabs >}}
|
||||
|
||||
|
|
@ -529,7 +529,7 @@ sudo -u git -H bundle exec rake gitlab:backup:restore BACKUP=11493107454_2018_04
|
|||
{{< tab title="Helm chart (Kubernetes)" >}}
|
||||
|
||||
```shell
|
||||
kubectl exec <Toolbox pod name> -it -- backup-utility --restore BACKUP=11493107454_2018_04_25_10.6.4-ce --repositories-server-side
|
||||
kubectl exec <Toolbox pod name> -it -- backup-utility --restore -t <backup_ID> --repositories-server-side
|
||||
```
|
||||
|
||||
When using [cron-based backups](https://docs.gitlab.com/charts/backup-restore/backup.html#cron-based-backup),
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ Follow the [Geo database replication instructions](../setup/database.md).
|
|||
If using an external PostgreSQL instance, refer also to
|
||||
[Geo with external PostgreSQL instances](../setup/external_database.md).
|
||||
|
||||
After streaming replication is enabled in the secondary Geo site's read-replica database, then commands such as `gitlab-rake db:migrate:status:geo` will fail, until [configuration of the secondary site is complete](#step-7-copy-secrets-and-add-the-secondary-site-in-the-application), specifically [Geo configuration - Step 3. Add the secondary site](configuration.md#step-3-add-the-secondary-site).
|
||||
After enabling streaming replication, `gitlab-rake db:migrate:status:geo` fails until [configuration of the secondary site is complete](#step-7-copy-secrets-and-add-the-secondary-site-in-the-application), specifically [Geo configuration - Step 3. Add the secondary site](configuration.md#step-3-add-the-secondary-site).
|
||||
|
||||
### Step 4: Configure the frontend application nodes on the Geo **secondary** site
|
||||
|
||||
|
|
@ -290,18 +290,18 @@ then make the following modifications:
|
|||
```
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
`postgresql['sql_user_password'] = 'md5 digest of secret'`
|
||||
If you had set up PostgreSQL cluster using the Linux package and had set
|
||||
`postgresql['sql_user_password'] = 'md5 digest of secret'`, keep in
|
||||
mind that `gitlab_rails['db_password']` and `geo_secondary['db_password']`
|
||||
contains the plaintext passwords. This is used to let the Rails
|
||||
contains the plaintext passwords. These configurations are used to let the Rails
|
||||
nodes connect to the databases.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
Make sure that current node's IP is listed in
|
||||
Ensure that the current node's IP is listed in
|
||||
`postgresql['md5_auth_cidr_addresses']` setting of the read-replica database to
|
||||
allow Rails on this node to connect to PostgreSQL.
|
||||
{{< /alert >}}
|
||||
|
|
|
|||
|
|
@ -945,7 +945,7 @@ On each node perform the following:
|
|||
|
||||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
certificates aren't present, NGINX won't start. For more information, see
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl/).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Use this API to retrieve details about arbitrary tokens and to revoke them. Unli
|
|||
|
||||
## Token prefixes
|
||||
|
||||
When making a request, `personal`, `project` or `group access` tokens must begin with `glpat` or the current [custom prefix](../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix). If the token begins with a previous custom prefix, the operation will fail. Interest in support for previous custom prefixes is tracked in [issue 165663](https://gitlab.com/gitlab-org/gitlab/-/issues/165663).
|
||||
When making a request, `personal`, `project` or `group access` tokens must begin with `glpat` or the current [custom prefix](../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix). If the token begins with a previous custom prefix, the operation fails. Interest in support for previous custom prefixes is tracked in [issue 165663](https://gitlab.com/gitlab-org/gitlab/-/issues/165663).
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
|
|||
|
|
@ -10026,6 +10026,32 @@ Input type: `RestorePagesDeploymentInput`
|
|||
| <a id="mutationrestorepagesdeploymenterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationrestorepagesdeploymentpagesdeployment"></a>`pagesDeployment` | [`PagesDeployment!`](#pagesdeployment) | Restored Pages Deployment. |
|
||||
|
||||
### `Mutation.runnerBulkPause`
|
||||
|
||||
{{< details >}}
|
||||
**Introduced** in GitLab 17.11.
|
||||
**Status**: Experiment.
|
||||
{{< /details >}}
|
||||
|
||||
Input type: `RunnerBulkPauseInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationrunnerbulkpauseclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationrunnerbulkpauseids"></a>`ids` | [`[CiRunnerID!]!`](#cirunnerid) | IDs of the runners to pause or unpause. |
|
||||
| <a id="mutationrunnerbulkpausepaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is not allowed to receive jobs. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationrunnerbulkpauseclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationrunnerbulkpauseerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationrunnerbulkpauseupdatedcount"></a>`updatedCount` | [`Int`](#int) | Number of records effectively updated. Only present if operation was performed synchronously. |
|
||||
| <a id="mutationrunnerbulkpauseupdatedrunners"></a>`updatedRunners` | [`[CiRunner!]`](#cirunner) | Runners after mutation. |
|
||||
|
||||
### `Mutation.runnerCacheClear`
|
||||
|
||||
Input type: `RunnerCacheClearInput`
|
||||
|
|
@ -40910,6 +40936,7 @@ Represents the emoji reactions widget.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="workitemwidgetawardemojiawardemoji"></a>`awardEmoji` | [`AwardEmojiConnection`](#awardemojiconnection) | Emoji reactions on the work item. (see [Connections](#connections)) |
|
||||
| <a id="workitemwidgetawardemojidownvotes"></a>`downvotes` | [`Int!`](#int) | Number of downvotes the work item has received. |
|
||||
| <a id="workitemwidgetawardemojinewcustomemojipath"></a>`newCustomEmojiPath` | [`String`](#string) | Path to create a new custom emoji. |
|
||||
| <a id="workitemwidgetawardemojitype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
|
||||
| <a id="workitemwidgetawardemojiupvotes"></a>`upvotes` | [`Int!`](#int) | Number of upvotes the work item has received. |
|
||||
|
||||
|
|
@ -42230,8 +42257,8 @@ Runner cloud provider.
|
|||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="cirunnercreationstatefinished"></a>`FINISHED` | Applies to a runner that has been registered and has polled for CI jobs at least once. |
|
||||
| <a id="cirunnercreationstatestarted"></a>`STARTED` | Applies to a runner that has been created, but not is not yet registered and running. |
|
||||
| <a id="cirunnercreationstatefinished"></a>`FINISHED` | Applies to a runner that has been registered and has polled for CI/CD jobs at least once. |
|
||||
| <a id="cirunnercreationstatestarted"></a>`STARTED` | Applies to a runner that has been created, but is not yet registered and running. |
|
||||
|
||||
### `CiRunnerJobExecutionStatus`
|
||||
|
||||
|
|
|
|||
|
|
@ -90,9 +90,9 @@ the database may be ready.
|
|||
|
||||
#### Add support to Omnibus GitLab and the Cloud Native GitLab charts
|
||||
|
||||
Before you add a new secret to
|
||||
Before adding a new secret to
|
||||
[`config/initializers/01_secret_token.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/initializers/01_secret_token.rb),
|
||||
make sure you also update Omnibus GitLab and the Cloud Native GitLab charts, or the update will fail.
|
||||
ensure you also update the GitLab Linux package and the Cloud Native GitLab charts, or the update will fail.
|
||||
Both installation methods are responsible for writing the `config/secrets.yml` file.
|
||||
If if they don't know about a secret, Rails attempts to write to the file, and fails because it doesn't
|
||||
have write access.
|
||||
|
|
@ -100,7 +100,7 @@ have write access.
|
|||
**Examples**
|
||||
|
||||
- [Change for self-compiled installation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175154)
|
||||
- [Change for Omnibus GitLab installation](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/8026)
|
||||
- [Change for Linux package installation](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/8026)
|
||||
- [Change for Cloud Native installation](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/3988)
|
||||
|
||||
#### Populate the secrets in live environments
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Verify
|
||||
group: Pipeline Authoring
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
title: Documenting the `.gitlab-ci.yml` keywords
|
||||
title: Documenting pipeline configuration keywords
|
||||
---
|
||||
|
||||
The [CI/CD YAML syntax reference](../../ci/yaml/_index.md) uses a standard style to make it easier to use and update.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Systems
|
||||
group: Distribution
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
description: GitLab's development guidelines for Distribution
|
||||
description: Development guidelines for Distribution
|
||||
title: Contribute to GitLab Distribution
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Foundations
|
||||
group: Import and Integrate
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
description: GitLab's development guidelines for Integrations
|
||||
description: Development guidelines for Integrations
|
||||
title: Integration development guidelines
|
||||
---
|
||||
|
||||
|
|
@ -185,6 +185,27 @@ attribute :wiki_page_events, default: false
|
|||
If an event attribute for an existing integration changes to `true`,
|
||||
this requires a data migration to back-fill the attribute value for old records.
|
||||
|
||||
### Define metrics
|
||||
|
||||
Every new integration should have five [metrics](../internal_analytics/metrics/_index.md):
|
||||
|
||||
- Count of active projects with the given integration
|
||||
- Count of active projects inheriting the given integration
|
||||
- Count of active groups with the given integration
|
||||
- Count of active groups inheriting the given integration
|
||||
- Count of active instance-level integrations for the given integration
|
||||
|
||||
Metrics require the model class of the integration to work. You can add metrics only together with or after the model.
|
||||
|
||||
To create metric definitions:
|
||||
|
||||
1. Copy the metrics created for an existing active integration.
|
||||
1. Replace all occurrences of the previous integration's name with the new integration's name.
|
||||
1. Replace `milestone` with the current milestone and `introduced_by_url` with the merge request link.
|
||||
1. Verify all other attributes have correct values by checking the [metrics guide](../internal_analytics/metrics/metrics_dictionary.md#metrics-definition-and-validation).
|
||||
|
||||
For example, to create metric definitions for the Slack integration, you copy the metrics [1](https://gitlab.com/gitlab-org/gitlab/blob/master/config/metrics/counts_all/20210216180122_projects_slack_active.yml), [2](https://gitlab.com/gitlab-org/gitlab/blob/master/config/metrics/counts_all/20210216180124_groups_slack_active.yml), [3](https://gitlab.com/gitlab-org/gitlab/blob/master/config/metrics/counts_all/20210216180127_instances_slack_active.yml), [4](https://gitlab.com/gitlab-org/gitlab/blob/master/config/metrics/counts_all/20210216180127_instances_slack_active.yml), and [5](https://gitlab.com/gitlab-org/gitlab/blob/master/config/metrics/counts_all/20210216180129_projects_inheriting_slack_active.yml)), then replace `Slack` with the name of the new integration.
|
||||
|
||||
### Security requirements
|
||||
|
||||
#### All HTTP calls must use `Gitlab::HTTP`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Plan
|
||||
group: Knowledge
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
description: GitLab's development guidelines for GitLab Pages
|
||||
description: Development guidelines for GitLab Pages
|
||||
title: Contribute to GitLab Pages development
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ See also the issue for [support running Jest tests in browsers](https://gitlab.c
|
|||
|
||||
### Debugging Jest tests
|
||||
|
||||
Running `yarn jest-debug` runs Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/troubleshooting#tests-are-failing-and-you-don-t-know-why).
|
||||
Running `yarn jest-debug` runs Jest in debug mode, allowing you to debug/inspect as described in the [Jest documentation](https://jestjs.io/docs/troubleshooting#tests-are-failing-and-you-don-t-know-why).
|
||||
|
||||
### Timeout error
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Foundations
|
||||
group: Import and Integrate
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
description: GitLab's development guidelines for webhooks
|
||||
description: Development guidelines for webhooks
|
||||
title: Webhooks developer guide
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Plan
|
||||
group: Knowledge
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
description: GitLab's development guidelines for Wikis
|
||||
description: Development guidelines for Wikis
|
||||
title: Wikis development guidelines
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ Now edit the role and add the policy:
|
|||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"q:CreateOAuthAppConnection",
|
||||
"q:DeleteOAuthAppConnection",
|
||||
"q:DeleteOAuthAppConnection"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ module API
|
|||
JOB_TOKEN_PARAM = :token
|
||||
LEGACY_SYSTEM_XID = '<legacy>'
|
||||
|
||||
def authenticate_runner!(ensure_runner_manager: true, update_contacted_at: true)
|
||||
def authenticate_runner!(ensure_runner_manager: true, creation_state: nil)
|
||||
track_runner_authentication
|
||||
forbidden! unless current_runner
|
||||
|
||||
current_runner.heartbeat if update_contacted_at
|
||||
current_runner.heartbeat(creation_state: creation_state) if ensure_runner_manager
|
||||
return unless ensure_runner_manager
|
||||
|
||||
runner_details = get_runner_details_from_request
|
||||
current_runner_manager&.heartbeat(runner_details, update_contacted_at: update_contacted_at)
|
||||
current_runner_manager&.heartbeat(runner_details)
|
||||
end
|
||||
|
||||
def get_runner_details_from_request
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ module API
|
|||
requires :token, type: String, desc: "The runner's authentication token"
|
||||
end
|
||||
delete '/', urgency: :low, feature_category: :runner do
|
||||
authenticate_runner!(ensure_runner_manager: false, update_contacted_at: false)
|
||||
authenticate_runner!(ensure_runner_manager: false)
|
||||
|
||||
destroy_conditionally!(current_runner) do
|
||||
::Ci::Runners::UnregisterRunnerService.new(current_runner, params[:token]).execute
|
||||
|
|
@ -120,11 +120,7 @@ module API
|
|||
optional :system_id, type: String, desc: "The runner's system identifier"
|
||||
end
|
||||
post '/verify', urgency: :low, feature_category: :runner do
|
||||
# For runners that were created in the UI, we want to update the contacted_at value
|
||||
# only when it starts polling for jobs
|
||||
registering_created_runner = params[:token].start_with?(::Ci::Runner::CREATED_RUNNER_TOKEN_PREFIX)
|
||||
|
||||
authenticate_runner!(update_contacted_at: !registering_created_runner)
|
||||
authenticate_runner!
|
||||
status 200
|
||||
|
||||
present current_runner, with: Entities::Ci::RunnerRegistrationDetails
|
||||
|
|
@ -194,7 +190,7 @@ module API
|
|||
parser :build_json, ::Grape::Parser::Json
|
||||
|
||||
post '/request', urgency: :low, feature_category: :continuous_integration do
|
||||
authenticate_runner!
|
||||
authenticate_runner!(creation_state: :finished)
|
||||
|
||||
unless current_runner.active?
|
||||
header 'X-GitLab-Last-Update', current_runner.ensure_runner_queue_value
|
||||
|
|
|
|||
|
|
@ -26,7 +26,17 @@ module Gitlab
|
|||
managed_resource.update!(status: :failed)
|
||||
raise ManagedResourceError, format_error_message(response.errors)
|
||||
else
|
||||
managed_resource.update!(status: :completed, tracked_objects: response.objects.map(&:to_h))
|
||||
managed_resource.assign_attributes(
|
||||
status: :completed,
|
||||
template_name: get_template.name,
|
||||
tracked_objects: response.objects.map(&:to_h)
|
||||
)
|
||||
|
||||
deletion_strategy = template_yaml['delete_resources']
|
||||
managed_resource.deletion_strategy = deletion_strategy if deletion_strategy.present?
|
||||
|
||||
managed_resource.save!
|
||||
|
||||
Gitlab::InternalEvents.track_event(
|
||||
'ensure_environment_for_managed_resource',
|
||||
user: build.user,
|
||||
|
|
@ -52,14 +62,8 @@ module Gitlab
|
|||
end
|
||||
|
||||
def ensure_environment
|
||||
template = begin
|
||||
get_custom_environment_template
|
||||
rescue GRPC::NotFound
|
||||
kas_client.get_default_environment_template
|
||||
end
|
||||
|
||||
rendered_template = kas_client.render_environment_template(
|
||||
template: template,
|
||||
template: get_template,
|
||||
environment: environment,
|
||||
build: build)
|
||||
|
||||
|
|
@ -69,6 +73,18 @@ module Gitlab
|
|||
build: build)
|
||||
end
|
||||
|
||||
def get_template
|
||||
get_custom_environment_template
|
||||
rescue GRPC::NotFound
|
||||
kas_client.get_default_environment_template
|
||||
end
|
||||
strong_memoize_attr :get_template
|
||||
|
||||
def template_yaml
|
||||
YAML.safe_load(get_template.data)
|
||||
end
|
||||
strong_memoize_attr :template_yaml
|
||||
|
||||
def get_custom_environment_template
|
||||
kas_client.get_environment_template(agent: environment.cluster_agent, template_name: DEFAULT_TEMPLATE_NAME)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ module Gitlab
|
|||
authorize_params: { gl_auth_type: 'login' }
|
||||
}
|
||||
when ->(provider_name) { AuthHelper.saml_providers.include?(provider_name.to_sym) }
|
||||
{ attribute_statements: ::Gitlab::Auth::Saml::Config.default_attribute_statements }
|
||||
{
|
||||
assertion_consumer_service_url: saml_acs_url(provider_name),
|
||||
attribute_statements: ::Gitlab::Auth::Saml::Config.default_attribute_statements
|
||||
}
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
|
@ -39,6 +42,10 @@ module Gitlab
|
|||
def full_host
|
||||
proc { |_env| Settings.gitlab['base_url'] }
|
||||
end
|
||||
|
||||
def saml_acs_url(provider_name)
|
||||
"#{full_host}/users/auth/#{provider_name}/callback"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@ module Gitlab
|
|||
def initialize(metric_definition)
|
||||
super
|
||||
|
||||
type = options[:type]
|
||||
return if options[:type]
|
||||
|
||||
return if type.in?(allowed_types)
|
||||
raise ArgumentError, "'type' option is required"
|
||||
end
|
||||
|
||||
prefix = "Invalid type #{type}. " if type
|
||||
raise ArgumentError, "#{prefix}Type must be one of: #{allowed_types.join(', ')}"
|
||||
def available?
|
||||
options[:type].in?(allowed_types)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -33,59 +33,6 @@ module Gitlab
|
|||
recorded_at
|
||||
].freeze
|
||||
|
||||
MIGRATED_INTEGRATIONS = %w[
|
||||
amazon_q
|
||||
apple_app_store
|
||||
asana
|
||||
assembla
|
||||
bamboo
|
||||
beyond_identity
|
||||
bugzilla
|
||||
buildkite
|
||||
campfire
|
||||
clickup
|
||||
confluence
|
||||
custom_issue_tracker
|
||||
datadog
|
||||
diffblue_cover
|
||||
discord
|
||||
drone_ci
|
||||
emails_on_push
|
||||
ewm
|
||||
external_wiki
|
||||
git_guardian
|
||||
github
|
||||
gitlab_slack_application
|
||||
google_play
|
||||
hangouts_chat
|
||||
harbor
|
||||
irker
|
||||
jenkins
|
||||
jira
|
||||
jira_cloud_app
|
||||
matrix
|
||||
mattermost
|
||||
mattermost_slash_commands
|
||||
microsoft_teams
|
||||
packagist
|
||||
phorge
|
||||
pipelines_email
|
||||
pivotaltracker
|
||||
prometheus
|
||||
pumble
|
||||
pushover
|
||||
redmine
|
||||
slack
|
||||
slack_slash_commands
|
||||
squash_tm
|
||||
teamcity
|
||||
telegram
|
||||
unify_circuit
|
||||
webex_teams
|
||||
youtrack
|
||||
zentao
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
include Gitlab::Utils::UsageData
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
|
@ -175,7 +122,6 @@ module Gitlab
|
|||
merge_requests: count(MergeRequest),
|
||||
notes: count(Note)
|
||||
}.merge(
|
||||
integrations_usage,
|
||||
user_preferences_usage,
|
||||
service_desk_counts
|
||||
)
|
||||
|
|
@ -267,31 +213,6 @@ module Gitlab
|
|||
Gitlab::UsageData::Topology.new.topology_usage_data
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def integrations_usage
|
||||
# rubocop: disable UsageData/LargeTable:
|
||||
available_integrations.each_with_object({}) do |name, response|
|
||||
next if MIGRATED_INTEGRATIONS.include?(name)
|
||||
|
||||
type = Integration.integration_name_to_type(name)
|
||||
|
||||
response[:"projects_#{name}_active"] = count(Integration.active.where.not(project: nil).where(type: type))
|
||||
response[:"groups_#{name}_active"] = count(Integration.active.where.not(group: nil).where(type: type))
|
||||
response[:"instances_#{name}_active"] = count(Integration.active.where(instance: true, type: type))
|
||||
response[:"projects_inheriting_#{name}_active"] = count(Integration.active.where.not(project: nil).where.not(inherit_from_id: nil).where(type: type))
|
||||
response[:"groups_inheriting_#{name}_active"] = count(Integration.active.where.not(group: nil).where.not(inherit_from_id: nil).where(type: type))
|
||||
end
|
||||
end
|
||||
|
||||
def successful_deployments_with_cluster(scope)
|
||||
scope
|
||||
.joins(cluster: :deployments)
|
||||
.merge(::Clusters::Cluster.enabled)
|
||||
.merge(Deployment.success)
|
||||
end
|
||||
# rubocop: enable UsageData/LargeTable
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# augmented in EE
|
||||
def user_preferences_usage
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14586,6 +14586,9 @@ msgstr ""
|
|||
msgid "CodeownersValidation|Entries with spaces"
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeownersValidation|Group has no members with permission to approve merge requests"
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeownersValidation|Group needs at least Developer access to the project for its members to approve merge requests"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28593,6 +28596,12 @@ msgstr ""
|
|||
msgid "GlobalSearch|Search labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "GlobalSearch|Search or go to… %{kbdStart}/%{kbdEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "GlobalSearch|Search or go to… (or use the / keyboard shortcut)"
|
||||
msgstr ""
|
||||
|
||||
msgid "GlobalSearch|Search results are loading"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -56242,6 +56251,9 @@ msgstr ""
|
|||
msgid "ServiceAccounts|Name"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|No service accounts"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|Service Accounts"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -66699,6 +66711,9 @@ msgstr ""
|
|||
msgid "Vulnerability|Additional Info"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerability|Archive pending"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerability|Assert:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -66912,6 +66927,9 @@ msgstr ""
|
|||
msgid "Vulnerability|Vulnerability identifiers"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerability|Vulnerability will be archived on %{date}. %{linkStart}Why will this vulnerability be archived?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerability|Vulnerable class:"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
"@gitlab/application-sdk-browser": "^0.3.4",
|
||||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/cluster-client": "^2.5.0",
|
||||
"@gitlab/duo-ui": "^8.9.1",
|
||||
"@gitlab/duo-ui": "^8.10.0",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/fonts": "^1.3.0",
|
||||
"@gitlab/query-language-rust": "0.5.2",
|
||||
|
|
@ -295,7 +295,7 @@
|
|||
"jest-jasmine2": "^29.7.0",
|
||||
"jest-junit": "^12.3.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"markdownlint-cli2": "^0.17.1",
|
||||
"markdownlint-cli2": "^0.17.2",
|
||||
"miragejs": "^0.1.40",
|
||||
"mock-apollo-client": "1.2.0",
|
||||
"nodemon": "^2.0.19",
|
||||
|
|
@ -306,7 +306,7 @@
|
|||
"swagger-cli": "^4.0.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"timezone-mock": "^1.0.8",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite-plugin-ruby": "^5.1.1",
|
||||
"vue-loader-vue3": "npm:vue-loader@17.4.2",
|
||||
"vue-test-utils-compat": "0.0.14",
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@ module QA
|
|||
end
|
||||
|
||||
let(:runner) do
|
||||
Resource::Ci::ProjectRunner.fabricate! do |runner|
|
||||
runner.name = "qa-runner-#{SecureRandom.hex(6)}"
|
||||
runner.tags = ["runner-for-#{imported_project.name}"]
|
||||
runner.executor = :docker
|
||||
runner.project = imported_project
|
||||
end
|
||||
create(:project_runner,
|
||||
name: "qa-runner-#{SecureRandom.hex(6)}",
|
||||
tags: ["runner-for-#{imported_project.name}"],
|
||||
executor: :docker,
|
||||
project: imported_project)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ then
|
|||
# shellcheck disable=2059
|
||||
printf "${COLOR_RED}ERROR: The number of README.md files has changed!${COLOR_RESET} Use index.md instead of README.md.\n" >&2
|
||||
printf "If removing a README.md file, update NUMBER_READMES in lint-doc.sh.\n" >&2
|
||||
printf "https://docs.gitlab.com/ee/development/documentation/site_architecture/folder_structure.html#work-with-directories-and-files\n"
|
||||
printf "https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n"
|
||||
((ERRORCODE++))
|
||||
fi
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ then
|
|||
# shellcheck disable=2059
|
||||
printf "${COLOR_RED}ERROR: The number of directory names containing dashes has changed!${COLOR_RESET} Use underscores instead of dashes for the directory names.\n" >&2
|
||||
printf "If removing a directory containing dashes, update NUMBER_DASHES in lint-doc.sh.\n" >&2
|
||||
printf "https://docs.gitlab.com/ee/development/documentation/site_architecture/folder_structure.html#work-with-directories-and-files\n"
|
||||
printf "https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n"
|
||||
((ERRORCODE++))
|
||||
fi
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ then
|
|||
# shellcheck disable=2059
|
||||
printf "${COLOR_RED}ERROR: The number of filenames containing dashes has changed!${COLOR_RESET} Use underscores instead of dashes for the filenames.\n" >&2
|
||||
printf "If removing a file containing dashes, update the filename NUMBER_DASHES in lint-doc.sh.\n" >&2
|
||||
printf "https://docs.gitlab.com/ee/development/documentation/site_architecture/folder_structure.html#work-with-directories-and-files\n"
|
||||
printf "https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n"
|
||||
((ERRORCODE++))
|
||||
fi
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ if echo "${FIND_UPPERCASE_DIRS}" | grep . &>/dev/null
|
|||
then
|
||||
# shellcheck disable=2059
|
||||
printf "${COLOR_RED}ERROR: Found one or more directories with an uppercase letter in their name!${COLOR_RESET} Use lowercase instead of uppercase for the directory names.\n" >&2
|
||||
printf "https://docs.gitlab.com/ee/development/documentation/site_architecture/folder_structure.html#work-with-directories-and-files\n" >&2
|
||||
printf "https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n" >&2
|
||||
echo "${FIND_UPPERCASE_DIRS}"
|
||||
((ERRORCODE++))
|
||||
fi
|
||||
|
|
@ -133,23 +133,11 @@ if echo "${FIND_UPPERCASE_FILES}" | grep . &>/dev/null
|
|||
then
|
||||
# shellcheck disable=2059
|
||||
printf "${COLOR_RED}ERROR: Found one or more file names with an uppercase letter in their name!${COLOR_RESET} Use lowercase instead of uppercase for the file names.\n" >&2
|
||||
printf "https://docs.gitlab.com/ee/development/documentation/site_architecture/folder_structure.html#work-with-directories-and-files\n" >&2
|
||||
printf "https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n" >&2
|
||||
echo "${FIND_UPPERCASE_FILES}"
|
||||
((ERRORCODE++))
|
||||
fi
|
||||
|
||||
FIND_ALL_DOCS_DIRECTORIES=$(find doc -type d)
|
||||
# shellcheck disable=2059
|
||||
printf "${COLOR_GREEN}INFO: Checking for documentation path clashes...${COLOR_RESET}\n"
|
||||
for directory in $FIND_ALL_DOCS_DIRECTORIES; do
|
||||
# Markdown files should not have the same path as a directory with an index.md file in it
|
||||
if [[ -f "${directory}.md" ]] && [[ -f "${directory}/index.md" ]]; then
|
||||
# shellcheck disable=2059
|
||||
printf "${COLOR_YELLOW}WARNING: File ${directory}.md clashes with file ${directory}/index.md!${COLOR_RESET} "
|
||||
printf "For more information, see https://gitlab.com/gitlab-org/gitlab-docs/-/issues/1792.\n"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run Vale and Markdownlint only on changed files. Only works on merged results
|
||||
# pipelines, so first checks if a merged results CI variable is present. If not present,
|
||||
# runs test on all files.
|
||||
|
|
@ -195,7 +183,7 @@ function run_locally_or_in_container() {
|
|||
local cmd=$1
|
||||
local args=$2
|
||||
local files=$3
|
||||
local registry_url="registry.gitlab.com/gitlab-org/technical-writing/docs-gitlab-com/lint-markdown:alpine-3.21-vale-3.9.3-markdownlint2-0.17.1-lychee-0.18.0"
|
||||
local registry_url="registry.gitlab.com/gitlab-org/technical-writing/docs-gitlab-com/lint-markdown:alpine-3.21-vale-3.11.2-markdownlint2-0.17.2-lychee-0.18.1"
|
||||
|
||||
if hash "${cmd}" 2>/dev/null
|
||||
then
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co
|
|||
let(:show_whitespace) { true }
|
||||
let(:diff_view) { 'inline' }
|
||||
let(:update_user_endpoint) { '/update_user' }
|
||||
let(:metadata_endpoint) { '/metadata' }
|
||||
let(:diffs_stats_endpoint) { '/diffs_stats' }
|
||||
let(:diff_files_endpoint) { '/diff_files_metadata' }
|
||||
|
||||
it "renders diffs slice" do
|
||||
|
|
@ -22,7 +22,7 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co
|
|||
app = page.find('[data-rapid-diffs]')
|
||||
expect(app).not_to be_nil
|
||||
expect(app['data-reload-stream-url']).to eq(reload_stream_url)
|
||||
expect(app['data-metadata-endpoint']).to eq(metadata_endpoint)
|
||||
expect(app['data-diffs-stats-endpoint']).to eq(diffs_stats_endpoint)
|
||||
expect(app['data-diff-files-endpoint']).to eq(diff_files_endpoint)
|
||||
end
|
||||
|
||||
|
|
@ -86,7 +86,8 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co
|
|||
it 'preloads' do
|
||||
instance = create_instance
|
||||
render_inline(instance)
|
||||
expect(instance.helpers.page_startup_api_calls).to include(metadata_endpoint)
|
||||
expect(instance.helpers.page_startup_api_calls).to include(diffs_stats_endpoint)
|
||||
expect(instance.helpers.page_startup_api_calls).to include(diff_files_endpoint)
|
||||
expect(vc_test_controller.view_context.content_for?(:startup_js)).not_to be_nil
|
||||
end
|
||||
|
||||
|
|
@ -113,7 +114,7 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co
|
|||
show_whitespace:,
|
||||
diff_view:,
|
||||
update_user_endpoint:,
|
||||
metadata_endpoint:,
|
||||
diffs_stats_endpoint:,
|
||||
diff_files_endpoint:,
|
||||
lazy:
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ RSpec.describe 'Global search', :js, feature_category: :global_search do
|
|||
|
||||
shared_examples 'header search' do
|
||||
it 'renders search button' do
|
||||
expect(page).to have_button('Search or go to…')
|
||||
expect(page).to have_selector('[data-testid="super-sidebar-search-button"]')
|
||||
end
|
||||
|
||||
it 'opens search modal when shortcut "s" is pressed' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Projects > Files > Open MRs dropdown', :js, feature_category: :source_code_management do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
def create_mr(branch_name, title)
|
||||
project.repository.create_branch(branch_name)
|
||||
|
||||
project.repository.commit_files(
|
||||
user,
|
||||
branch_name: branch_name,
|
||||
message: "Update readme file",
|
||||
actions: [{ action: :update, file_path: file_path, content: "Updated file content" }]
|
||||
)
|
||||
|
||||
create(:merge_request, source_project: project, target_branch: target_branch, source_branch: branch_name,
|
||||
title: title)
|
||||
end
|
||||
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:user) { project.creator }
|
||||
let_it_be(:file_path) { 'README.md' }
|
||||
let_it_be(:mr_title) { 'Update README.md' }
|
||||
let_it_be(:source_branch) { 'open-mrs-badge-test-1' }
|
||||
let_it_be(:another_mr_title) { 'Second update to README.md' }
|
||||
let_it_be(:another_source_branch) { 'open-mrs-badge-test-2' }
|
||||
let_it_be(:target_branch) { 'master' }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
create_mr(source_branch, mr_title)
|
||||
create_mr(another_source_branch, another_mr_title)
|
||||
end
|
||||
|
||||
context 'when feature flags are enabled' do
|
||||
before do
|
||||
stub_feature_flags(
|
||||
filter_blob_path: true,
|
||||
blob_repository_vue_header_app: true
|
||||
)
|
||||
end
|
||||
|
||||
it 'shows correct count and lists all MRs in dropdown' do
|
||||
visit project_blob_path(project, "master/#{file_path}")
|
||||
|
||||
badge = find_by_testid('open-mr-badge')
|
||||
expect(badge).to have_content('2 Open')
|
||||
|
||||
badge.click
|
||||
wait_for_requests
|
||||
|
||||
within_testid('disclosure-content') do
|
||||
expect(page).to have_content(mr_title)
|
||||
expect(page).to have_content(another_mr_title)
|
||||
|
||||
click_button mr_title
|
||||
end
|
||||
|
||||
expect(page).to have_content(mr_title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flags are disabled' do
|
||||
before do
|
||||
stub_feature_flags(
|
||||
filter_blob_path: false,
|
||||
blob_repository_vue_header_app: false
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not display the open MRs badge' do
|
||||
visit project_blob_path(project, "master/#{file_path}")
|
||||
|
||||
expect(page).not_to have_testid('open-mr-badge')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -45,7 +45,7 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat
|
|||
|
||||
context 'when clicking the search button' do
|
||||
before do
|
||||
click_button "Search or go to…"
|
||||
find_by_testid('super-sidebar-search-button').click
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import {
|
|||
DEFAULT_PLATFORM,
|
||||
EXECUTORS_HELP_URL,
|
||||
SERVICE_COMMANDS_HELP_URL,
|
||||
STATUS_NEVER_CONTACTED,
|
||||
STATUS_ONLINE,
|
||||
CREATION_STATE_STARTED,
|
||||
CREATION_STATE_FINISHED,
|
||||
RUNNER_REGISTRATION_POLLING_INTERVAL_MS,
|
||||
WINDOWS_PLATFORM,
|
||||
GOOGLE_CLOUD_PLATFORM,
|
||||
|
|
@ -216,7 +216,7 @@ describe('RegistrationInstructions', () => {
|
|||
it('when runner is online, stops polling and announces runner is registered', async () => {
|
||||
expect(wrapper.emitted('runnerRegistered')).toBeUndefined();
|
||||
|
||||
mockResolvedRunner({ ...mockRunner, status: STATUS_ONLINE });
|
||||
mockResolvedRunner({ ...mockRunner, creationState: CREATION_STATE_FINISHED });
|
||||
await waitForPolling();
|
||||
|
||||
expect(wrapper.emitted('runnerRegistered')).toHaveLength(1);
|
||||
|
|
@ -288,7 +288,7 @@ describe('RegistrationInstructions', () => {
|
|||
|
||||
await waitForPolling();
|
||||
|
||||
mockResolvedRunner({ ...mockRunner, status: STATUS_NEVER_CONTACTED });
|
||||
mockResolvedRunner({ ...mockRunner, creationState: CREATION_STATE_STARTED });
|
||||
|
||||
await waitForPolling();
|
||||
});
|
||||
|
|
@ -317,7 +317,7 @@ describe('RegistrationInstructions', () => {
|
|||
createComponent();
|
||||
await waitForPolling();
|
||||
|
||||
mockResolvedRunner({ ...mockRunner, status: STATUS_ONLINE });
|
||||
mockResolvedRunner({ ...mockRunner, creationState: CREATION_STATE_FINISHED });
|
||||
await waitForPolling();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -542,9 +542,11 @@ describe('diffs/components/app', () => {
|
|||
|
||||
describe('File browser', () => {
|
||||
it('should render file browser when files are present', () => {
|
||||
store.realSize = '20';
|
||||
store.treeEntries = { 111: { type: 'blob', fileHash: '111', path: '111.js' } };
|
||||
createComponent();
|
||||
expect(wrapper.findComponent(DiffsFileTree).exists()).toBe(true);
|
||||
expect(wrapper.findComponent(DiffsFileTree).props('totalFilesCount')).toBe('20');
|
||||
});
|
||||
|
||||
it('should not render file browser without files', async () => {
|
||||
|
|
|
|||
|
|
@ -194,9 +194,11 @@ describe('DiffsFileTree', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('passes down loadedFiles table to tree list', () => {
|
||||
it('passes down props to tree list', () => {
|
||||
const loadedFiles = { foo: true };
|
||||
createComponent({ loadedFiles });
|
||||
const totalFilesCount = '20';
|
||||
createComponent({ loadedFiles, totalFilesCount });
|
||||
expect(wrapper.findComponent(TreeList).props('loadedFiles')).toBe(loadedFiles);
|
||||
expect(wrapper.findComponent(TreeList).props('totalFilesCount')).toBe(totalFilesCount);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ describe('Diffs tree list component', () => {
|
|||
useLegacyDiffs().addedLines = 10;
|
||||
useLegacyDiffs().addedLines = 20;
|
||||
useLegacyDiffs().mergeRequestDiff = {};
|
||||
useLegacyDiffs().realSize = '20';
|
||||
useLegacyDiffs().setTreeOpen.mockReturnValue();
|
||||
});
|
||||
|
||||
|
|
@ -165,7 +164,7 @@ describe('Diffs tree list component', () => {
|
|||
});
|
||||
|
||||
it('renders file count', () => {
|
||||
createComponent();
|
||||
createComponent({ totalFilesCount: '20' });
|
||||
|
||||
expect(wrapper.findByTestId('file-count').text()).toBe('20');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,19 +28,6 @@ describe('Rapid Diffs App', () => {
|
|||
app = createRapidDiffsApp(options);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createTestingPinia();
|
||||
useDiffsView(pinia).loadMetadata.mockResolvedValue();
|
||||
initFileBrowser.mockResolvedValue();
|
||||
setHTMLFixture(
|
||||
`
|
||||
<div data-rapid-diffs data-reload-stream-url="/reload" data-metadata-endpoint="/metadata" data-diff-files-endpoint="/diff-files-metadata">
|
||||
<div id="js-stream-container" data-diffs-stream-url="/stream"></div>
|
||||
</div>
|
||||
`,
|
||||
);
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'customElements', {
|
||||
value: { define: jest.fn() },
|
||||
|
|
@ -48,23 +35,28 @@ describe('Rapid Diffs App', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('initializes the app', async () => {
|
||||
let res;
|
||||
const mock = useDiffsView().loadMetadata.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
res = resolve;
|
||||
}),
|
||||
beforeEach(() => {
|
||||
createTestingPinia();
|
||||
useDiffsView().loadDiffsStats.mockResolvedValue();
|
||||
initFileBrowser.mockResolvedValue();
|
||||
setHTMLFixture(
|
||||
`
|
||||
<div data-rapid-diffs data-reload-stream-url="/reload" data-diffs-stats-endpoint="/stats" data-diff-files-endpoint="/diff-files-metadata">
|
||||
<div id="js-stream-container" data-diffs-stream-url="/stream"></div>
|
||||
</div>
|
||||
`,
|
||||
);
|
||||
});
|
||||
|
||||
it('initializes the app', () => {
|
||||
createApp();
|
||||
app.init();
|
||||
expect(useDiffsView().metadataEndpoint).toBe('/metadata');
|
||||
expect(mock).toHaveBeenCalled();
|
||||
expect(useDiffsView().diffsStatsEndpoint).toBe('/stats');
|
||||
expect(useDiffsView().loadDiffsStats).toHaveBeenCalled();
|
||||
expect(initViewSettings).toHaveBeenCalledWith({ pinia, streamUrl: '/reload' });
|
||||
expect(window.customElements.define).toHaveBeenCalledWith('diff-file', DiffFile);
|
||||
expect(window.customElements.define).toHaveBeenCalledWith('diff-file-mounted', DiffFileMounted);
|
||||
expect(window.customElements.define).toHaveBeenCalledWith('streaming-error', StreamingError);
|
||||
await res();
|
||||
expect(initHiddenFilesWarning).toHaveBeenCalled();
|
||||
expect(fixWebComponentsStreamingOnSafari).toHaveBeenCalled();
|
||||
expect(initFileBrowser).toHaveBeenCalledWith('/diff-files-metadata');
|
||||
|
|
|
|||
|
|
@ -1,34 +1,44 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vue from 'vue';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import FileBrowser from '~/rapid_diffs/app/file_browser.vue';
|
||||
import DiffsFileTree from '~/diffs/components/diffs_file_tree.vue';
|
||||
import store from '~/mr_notes/stores';
|
||||
import { useDiffsList } from '~/rapid_diffs/stores/diffs_list';
|
||||
import { useFileBrowser } from '~/diffs/stores/file_browser';
|
||||
import { useDiffsView } from '~/rapid_diffs/stores/diffs_view';
|
||||
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('FileBrowser', () => {
|
||||
let wrapper;
|
||||
let pinia;
|
||||
|
||||
const createComponent = () => {
|
||||
const pinia = createTestingPinia();
|
||||
useDiffsList();
|
||||
useFileBrowser();
|
||||
wrapper = shallowMount(FileBrowser, {
|
||||
store,
|
||||
pinia,
|
||||
});
|
||||
};
|
||||
|
||||
it('passes down loaded files', async () => {
|
||||
beforeEach(() => {
|
||||
pinia = createTestingPinia();
|
||||
useDiffsList();
|
||||
useDiffsView();
|
||||
useFileBrowser();
|
||||
});
|
||||
|
||||
it('passes down props', () => {
|
||||
const loadedFiles = { foo: 1 };
|
||||
createComponent();
|
||||
const totalFilesCount = 20;
|
||||
useDiffsList().loadedFiles = loadedFiles;
|
||||
await nextTick();
|
||||
expect(wrapper.findComponent(DiffsFileTree).props('loadedFiles')).toStrictEqual(loadedFiles);
|
||||
useDiffsView().diffsStats = { diffsCount: totalFilesCount };
|
||||
createComponent();
|
||||
const tree = wrapper.findComponent(DiffsFileTree);
|
||||
expect(tree.props('loadedFiles')).toStrictEqual(loadedFiles);
|
||||
expect(tree.props('totalFilesCount')).toStrictEqual(totalFilesCount);
|
||||
expect(tree.props('floatingResize')).toBe(true);
|
||||
});
|
||||
|
||||
it('uses floating resize', () => {
|
||||
|
|
@ -41,10 +51,9 @@ describe('FileBrowser', () => {
|
|||
expect(wrapper.findComponent(DiffsFileTree).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('hides file browser', async () => {
|
||||
createComponent();
|
||||
it('hides file browser', () => {
|
||||
useFileBrowser().fileBrowserVisible = false;
|
||||
await nextTick();
|
||||
createComponent();
|
||||
expect(wrapper.findComponent(DiffsFileTree).exists()).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ describe('View settings', () => {
|
|||
|
||||
it('sets diff app controls props', () => {
|
||||
useDiffsList().loadedFiles = { foo: true };
|
||||
useDiffsView().diffStats = {
|
||||
useDiffsView().diffsStats = {
|
||||
addedLines: 1,
|
||||
removedLines: 2,
|
||||
diffsCount: 3,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue