Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9ea23bb2f6
commit
38ec668b55
|
@ -206,7 +206,7 @@ workflow:
|
|||
GLCI_PUSH_RAILS_TEST_FAILURE_ISSUES_TO_GCS: "false"
|
||||
|
||||
variables:
|
||||
PG_VERSION: "14"
|
||||
PG_VERSION: "16"
|
||||
DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/ci/${BUILD_OS}-${OS_VERSION}-slim-ruby-${RUBY_VERSION}-golang-${GO_VERSION}-node-${NODE_VERSION}-postgresql-${PG_VERSION}:rubygems-${RUBYGEMS_VERSION}-git-${GIT_VERSION}-lfs-${LFS_VERSION}-chrome-${CHROME_VERSION}-yarn-${YARN_VERSION}-graphicsmagick-${GRAPHICSMAGICK_VERSION}"
|
||||
DEFAULT_JOB_TAG: "gitlab-org"
|
||||
GITLAB_LARGE_RUNNER_OPTIONAL: "gitlab-org" # overridden just in gitlab-org/gitlab
|
||||
|
|
|
@ -1279,12 +1279,9 @@ RSpec/FactoryBot/LocalStaticAssignment:
|
|||
Rails/TransactionExitStatement:
|
||||
Enabled: true
|
||||
|
||||
Search/AvoidCheckingFinishedOnDeprecatedMigrations:
|
||||
Search/AvoidCheckingFinishedOnInvalidMigrations:
|
||||
Include:
|
||||
- 'ee/app/models/**/*.rb'
|
||||
- 'ee/lib/elastic/**/*.rb'
|
||||
- 'ee/lib/gitlab/elastic/**/*.rb'
|
||||
- 'ee/spec/support/helpers/elasticsearch_helpers.rb'
|
||||
- 'ee/**/*.rb'
|
||||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/407233
|
||||
Cop/ExperimentsTestCoverage:
|
||||
|
|
|
@ -32,7 +32,6 @@ InternalAffairs/ExampleHeredocDelimiter:
|
|||
- 'spec/rubocop/cop/scalability/cron_worker_context_spec.rb'
|
||||
- 'spec/rubocop/cop/scalability/file_uploads_spec.rb'
|
||||
- 'spec/rubocop/cop/scalability/idempotent_worker_spec.rb'
|
||||
- 'spec/rubocop/cop/search/avoid_checking_finished_on_deprecated_migrations_spec.rb'
|
||||
- 'spec/rubocop/cop/search/namespaced_class_spec.rb'
|
||||
- 'spec/rubocop/cop/sidekiq_api_usage_spec.rb'
|
||||
- 'spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb'
|
||||
|
|
|
@ -60,7 +60,6 @@ InternalAffairs/NodeMatcherDirective:
|
|||
- 'rubocop/cop/scalability/cron_worker_context.rb'
|
||||
- 'rubocop/cop/scalability/file_uploads.rb'
|
||||
- 'rubocop/cop/scalability/idempotent_worker.rb'
|
||||
- 'rubocop/cop/search/avoid_checking_finished_on_deprecated_migrations.rb'
|
||||
- 'rubocop/cop/sidekiq_api_usage.rb'
|
||||
- 'rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb'
|
||||
- 'rubocop/cop/sidekiq_options_queue.rb'
|
||||
|
|
|
@ -117,7 +117,6 @@ InternalAffairs/OnSendWithoutOnCSend:
|
|||
- 'rubocop/cop/scalability/bulk_perform_with_context.rb'
|
||||
- 'rubocop/cop/scalability/cron_worker_context.rb'
|
||||
- 'rubocop/cop/scalability/file_uploads.rb'
|
||||
- 'rubocop/cop/search/avoid_checking_finished_on_deprecated_migrations.rb'
|
||||
- 'rubocop/cop/sidekiq_api_usage.rb'
|
||||
- 'rubocop/cop/sidekiq_options_queue.rb'
|
||||
- 'rubocop/cop/sidekiq_redis_call.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
838089751c1ffa6700b328f3060ce869217393fe
|
||||
e30c5e007ce5c67d4dbbdfdf5e95634d0cc09344
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 312 B |
Binary file not shown.
After Width: | Height: | Size: 437 B |
Binary file not shown.
After Width: | Height: | Size: 259 B |
Binary file not shown.
After Width: | Height: | Size: 612 B |
Binary file not shown.
After Width: | Height: | Size: 375 B |
|
@ -109,6 +109,11 @@ export default {
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
isWidget: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -300,7 +305,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="gl-mt-5">
|
||||
<p data-testid="runner-token-message">
|
||||
<p v-if="!isWidget" data-testid="runner-token-message">
|
||||
<gl-icon name="information-o" variant="info" />
|
||||
<gl-sprintf :message="tokenMessage">
|
||||
<template #token>
|
||||
|
@ -326,11 +331,12 @@ export default {
|
|||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<hr v-if="!isWidget" />
|
||||
|
||||
<!-- start: before you begin -->
|
||||
<div>
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.beforeHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.beforeHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.beforeHeading }}</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<gl-sprintf :message="$options.i18n.permissionsText">
|
||||
|
@ -374,7 +380,8 @@ export default {
|
|||
<!-- end: before you begin -->
|
||||
|
||||
<!-- start: step one -->
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.stepOneHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.stepOneHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.stepOneHeading }}</h2>
|
||||
<p>{{ $options.i18n.stepOneDescription }}</p>
|
||||
|
||||
<google-cloud-field-group
|
||||
|
@ -504,7 +511,8 @@ export default {
|
|||
|
||||
<hr />
|
||||
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.stepOneNodePoolHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.stepOneNodePoolHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.stepOneNodePoolHeading }}</h2>
|
||||
<p>{{ $options.i18n.stepOneNodePoolDescription }}</p>
|
||||
|
||||
<template v-for="(nodePool, index) in nodePools">
|
||||
|
@ -527,7 +535,8 @@ export default {
|
|||
<!-- end: step 1.2 -->
|
||||
|
||||
<!-- start: step two -->
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.stepTwoHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.stepTwoHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.stepTwoHeading }}</h2>
|
||||
<p>{{ $options.i18n.stepTwoDescription }}</p>
|
||||
<gl-alert
|
||||
v-if="showAlert"
|
||||
|
@ -559,6 +568,6 @@ export default {
|
|||
:apply-terraform-script="applyTerraformScript"
|
||||
/>
|
||||
|
||||
<hr />
|
||||
<hr v-if="!isWidget" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -101,6 +101,11 @@ export default {
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
isWidget: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -245,7 +250,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="gl-mt-5">
|
||||
<p>
|
||||
<p v-if="!isWidget" data-testid="runner-token-message">
|
||||
<gl-icon name="information-o" variant="info" />
|
||||
<gl-sprintf :message="tokenMessage">
|
||||
<template #token>
|
||||
|
@ -271,11 +276,12 @@ export default {
|
|||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<hr v-if="!isWidget" />
|
||||
|
||||
<!-- start: before you begin -->
|
||||
<div>
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.beforeHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.beforeHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.beforeHeading }}</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<gl-sprintf :message="$options.i18n.permissionsText">
|
||||
|
@ -319,7 +325,8 @@ export default {
|
|||
<!-- end: before you begin -->
|
||||
|
||||
<!-- start: step one -->
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.stepOneHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.stepOneHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.stepOneHeading }}</h2>
|
||||
<p>{{ $options.i18n.stepOneDescription }}</p>
|
||||
|
||||
<google-cloud-field-group
|
||||
|
@ -501,7 +508,8 @@ export default {
|
|||
<!-- end: step one -->
|
||||
|
||||
<!-- start: step two -->
|
||||
<h2 class="gl-heading-2">{{ $options.i18n.stepTwoHeading }}</h2>
|
||||
<h3 v-if="isWidget" class="gl-heading-3">{{ $options.i18n.stepTwoHeading }}</h3>
|
||||
<h2 v-else class="gl-heading-2">{{ $options.i18n.stepTwoHeading }}</h2>
|
||||
<p>{{ $options.i18n.stepTwoDescription }}</p>
|
||||
<gl-alert
|
||||
v-if="showAlert"
|
||||
|
@ -533,6 +541,6 @@ export default {
|
|||
:apply-terraform-script="applyTerraformScript"
|
||||
/>
|
||||
|
||||
<hr />
|
||||
<hr v-if="!isWidget" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<script>
|
||||
import { GlIcon, GlLink, GlSprintf, GlBadge } from '@gitlab/ui';
|
||||
import CrudComponent from '~/vue_shared/components/crud_component.vue';
|
||||
import { EXECUTORS_HELP_URL, SERVICE_COMMANDS_HELP_URL } from '../../constants';
|
||||
import PlatformsDrawer from './platforms_drawer.vue';
|
||||
import CliCommand from './cli_command.vue';
|
||||
import { commandPrompt, registerCommand, runCommand } from './utils';
|
||||
|
||||
export default {
|
||||
name: 'OperatingSystemRegistrationInstructions',
|
||||
components: {
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlBadge,
|
||||
CrudComponent,
|
||||
CliCommand,
|
||||
PlatformsDrawer,
|
||||
},
|
||||
props: {
|
||||
token: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
platform: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
commandPrompt() {
|
||||
return commandPrompt({ platform: this.platform });
|
||||
},
|
||||
registerCommand() {
|
||||
return registerCommand({
|
||||
platform: this.platform,
|
||||
token: this.token,
|
||||
});
|
||||
},
|
||||
runCommand() {
|
||||
return runCommand({ platform: this.platform });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onToggleDrawer(val = !this.isDrawerOpen) {
|
||||
this.isDrawerOpen = val;
|
||||
},
|
||||
},
|
||||
EXECUTORS_HELP_URL,
|
||||
SERVICE_COMMANDS_HELP_URL,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<crud-component :is-collapsible="true" :collapsed="true">
|
||||
<template #title>
|
||||
{{ title }}
|
||||
<gl-badge variant="neutral">{{ s__('Runners|Operating System') }}</gl-badge>
|
||||
</template>
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'Runners|GitLab Runner must be installed before you can register a runner. %{linkStart}How do I install GitLab Runner?%{linkEnd}',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #link="{ content }">
|
||||
<gl-link data-testid="how-to-install-btn" @click="onToggleDrawer()">{{
|
||||
content
|
||||
}}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
|
||||
<section class="gl-mt-6" data-testid="step-1">
|
||||
<h3 class="gl-heading-3">{{ s__('Runners|Step 1') }}</h3>
|
||||
<p>
|
||||
{{
|
||||
s__(
|
||||
'Runners|Copy and paste the following command into your command line to register the runner.',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<cli-command :prompt="commandPrompt" :command="registerCommand" />
|
||||
</section>
|
||||
<section class="gl-mt-6" data-testid="step-2">
|
||||
<h3 class="gl-heading-3">{{ s__('Runners|Step 2') }}</h3>
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'Runners|Choose an executor when prompted by the command line. Executors run builds in different environments. %{linkStart}Not sure which one to select?%{linkEnd}',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #link="{ content }">
|
||||
<gl-link
|
||||
:href="$options.EXECUTORS_HELP_URL"
|
||||
target="_blank"
|
||||
data-testid="executors-help-link"
|
||||
>
|
||||
{{ content }} <gl-icon name="external-link" />
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</section>
|
||||
<section class="gl-mt-6" data-testid="step-3">
|
||||
<h3 class="gl-heading-3">{{ s__('Runners|Step 3 (optional)') }}</h3>
|
||||
<p>{{ s__('Runners|Manually verify that the runner is available to pick up jobs.') }}</p>
|
||||
<cli-command :prompt="commandPrompt" :command="runCommand" />
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'Runners|This may not be needed if you manage your runner as a %{linkStart}system or user service%{linkEnd}.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #link="{ content }">
|
||||
<gl-link
|
||||
:href="$options.SERVICE_COMMANDS_HELP_URL"
|
||||
target="_blank"
|
||||
data-testid="service-commands-help-link"
|
||||
>
|
||||
{{ content }} <gl-icon name="external-link" />
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<platforms-drawer :platform="platform" :open="isDrawerOpen" @close="onToggleDrawer(false)" />
|
||||
</crud-component>
|
||||
</template>
|
|
@ -3,29 +3,43 @@ import {
|
|||
GlFormGroup,
|
||||
GlFormInputGroup,
|
||||
GlButton,
|
||||
GlBadge,
|
||||
GlSprintf,
|
||||
GlAlert,
|
||||
GlLink,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { createAlert } from '~/alert';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import CrudComponent from '~/vue_shared/components/crud_component.vue';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
|
||||
import runnerForRegistrationQuery from '../graphql/register/runner_for_registration.query.graphql';
|
||||
import { I18N_FETCH_ERROR } from '../constants';
|
||||
import { DOCKER_HELP_URL, KUBERNETES_HELP_URL, I18N_FETCH_ERROR } from '../constants';
|
||||
import { captureException } from '../sentry_utils';
|
||||
import OperatingSystemInstruction from './registration/wizard_operating_system_instruction.vue';
|
||||
import GoogleCloudRegistrationInstructions from './registration/google_cloud_registration_instructions.vue';
|
||||
import GkeRegistrationInstructions from './registration/gke_registration_instructions.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlFormInputGroup,
|
||||
GlButton,
|
||||
GlBadge,
|
||||
GlSprintf,
|
||||
GlAlert,
|
||||
GlLink,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
MultiStepFormTemplate,
|
||||
ClipboardButton,
|
||||
CrudComponent,
|
||||
OperatingSystemInstruction,
|
||||
GoogleCloudRegistrationInstructions,
|
||||
GkeRegistrationInstructions,
|
||||
},
|
||||
props: {
|
||||
currentStep: {
|
||||
|
@ -81,6 +95,8 @@ export default {
|
|||
return this.$apollo.queries.runner.loading;
|
||||
},
|
||||
},
|
||||
DOCKER_HELP_URL,
|
||||
KUBERNETES_HELP_URL,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
@ -158,7 +174,96 @@ export default {
|
|||
</gl-sprintf>
|
||||
</gl-alert>
|
||||
|
||||
<!-- instructions will be added in https://gitlab.com/gitlab-org/gitlab/-/issues/396544 -->
|
||||
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
|
||||
<!-- Operating systems -->
|
||||
<operating-system-instruction
|
||||
platform="linux"
|
||||
:token="token"
|
||||
title="Linux"
|
||||
class="gl-rounded-b-none"
|
||||
/>
|
||||
|
||||
<operating-system-instruction
|
||||
platform="osx"
|
||||
:token="token"
|
||||
title="macOS"
|
||||
class="!gl-mt-0 gl-rounded-none gl-border-t-0"
|
||||
/>
|
||||
|
||||
<operating-system-instruction
|
||||
platform="windows"
|
||||
:token="token"
|
||||
title="Windows"
|
||||
class="!gl-mt-0 gl-rounded-none gl-border-t-0"
|
||||
/>
|
||||
|
||||
<!-- Clouds -->
|
||||
<crud-component
|
||||
is-collapsible
|
||||
:collapsed="true"
|
||||
:is-empty="false"
|
||||
class="!gl-mt-0 gl-rounded-none gl-border-t-0"
|
||||
>
|
||||
<template #title>
|
||||
Google Cloud
|
||||
<gl-badge variant="neutral">{{ s__('Runners|Cloud') }}</gl-badge>
|
||||
</template>
|
||||
<google-cloud-registration-instructions :is-widget="true" />
|
||||
</crud-component>
|
||||
|
||||
<crud-component
|
||||
is-collapsible
|
||||
:collapsed="true"
|
||||
:is-empty="false"
|
||||
class="!gl-mt-0 gl-rounded-none gl-border-t-0"
|
||||
>
|
||||
<template #title>
|
||||
GKE
|
||||
<gl-badge variant="neutral">{{ s__('Runners|Cloud') }}</gl-badge>
|
||||
</template>
|
||||
<gke-registration-instructions :is-widget="true" />
|
||||
</crud-component>
|
||||
|
||||
<!-- Containers -->
|
||||
<crud-component
|
||||
is-collapsible
|
||||
:collapsed="true"
|
||||
:is-empty="false"
|
||||
class="!gl-mt-0 gl-rounded-none gl-border-t-0"
|
||||
>
|
||||
<template #title>
|
||||
Docker
|
||||
<gl-badge variant="neutral">{{ s__('Runners|Container') }}</gl-badge>
|
||||
</template>
|
||||
<gl-sprintf :message="s__('Runners|View instructions in %{helpLink}.')">
|
||||
<template #helpLink>
|
||||
<gl-link :href="$options.DOCKER_HELP_URL" target="_blank">
|
||||
{{ s__('Runners|documentation') }}
|
||||
<gl-icon name="external-link" />
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</crud-component>
|
||||
|
||||
<crud-component
|
||||
is-collapsible
|
||||
:collapsed="true"
|
||||
:is-empty="false"
|
||||
class="!gl-mt-0 gl-rounded-t-none gl-border-t-0"
|
||||
>
|
||||
<template #title>
|
||||
Kubernetes
|
||||
<gl-badge variant="neutral">{{ s__('Runners|Container') }}</gl-badge>
|
||||
</template>
|
||||
<gl-sprintf :message="s__('Runners|View instructions in %{helpLink}.')">
|
||||
<template #helpLink>
|
||||
<gl-link :href="$options.KUBERNETES_HELP_URL" target="_blank">
|
||||
{{ s__('Runners|documentation') }}
|
||||
<gl-icon name="external-link" />
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</crud-component>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="!loading" #next>
|
||||
|
|
|
@ -263,10 +263,9 @@ export default {
|
|||
</template>
|
||||
</groups-app>
|
||||
</gl-tab>
|
||||
<template #tabs-end>
|
||||
<li class="gl-w-full">
|
||||
<template #toolbar-end>
|
||||
<filtered-search-and-sort
|
||||
class="gl-border-b-0"
|
||||
class="gl-w-full gl-border-b-0"
|
||||
:filtered-search-namespace="$options.filteredSearch.namespace"
|
||||
:filtered-search-tokens="$options.filteredSearch.tokens"
|
||||
:filtered-search-term-key="$options.filteredSearch.searchTermKey"
|
||||
|
@ -282,7 +281,6 @@ export default {
|
|||
@sort-direction-change="onSortDirectionChange"
|
||||
@sort-by-change="onSortByChange"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
</gl-tabs>
|
||||
</template>
|
||||
|
|
|
@ -581,10 +581,9 @@ export default {
|
|||
<template v-else>{{ tab.text }}</template>
|
||||
</gl-tab>
|
||||
|
||||
<template #tabs-end>
|
||||
<li class="gl-w-full">
|
||||
<template #toolbar-end>
|
||||
<filtered-search-and-sort
|
||||
class="gl-border-b-0"
|
||||
class="gl-w-full gl-border-b-0"
|
||||
:filtered-search-namespace="filteredSearchNamespace"
|
||||
:filtered-search-tokens="filteredSearchTokens"
|
||||
:filtered-search-term-key="filteredSearchTermKey"
|
||||
|
@ -598,7 +597,6 @@ export default {
|
|||
@sort-direction-change="onSortDirectionChange"
|
||||
@sort-by-change="onSortByChange"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
</gl-tabs>
|
||||
</template>
|
||||
|
|
|
@ -110,7 +110,7 @@ export default {
|
|||
/>
|
||||
<members-app v-else :namespace="tab.namespace" :tab-query-param-value="tab.queryParamValue" />
|
||||
</gl-tab>
|
||||
<template #tabs-end>
|
||||
<template #toolbar-end>
|
||||
<gl-button
|
||||
v-if="shouldShowExportButton"
|
||||
data-event-tracking="click_export_group_members_as_csv"
|
||||
|
|
|
@ -411,7 +411,7 @@ export default {
|
|||
/>
|
||||
</gl-tab>
|
||||
|
||||
<template #tabs-end>
|
||||
<template #toolbar-end>
|
||||
<div class="gl-ml-auto gl-flex gl-gap-2">
|
||||
<gl-button
|
||||
v-gl-modal="$options.uploadCsvModalId"
|
||||
|
|
|
@ -259,6 +259,7 @@ export default {
|
|||
<template #tabs-end>
|
||||
<li role="presentation" class="nav-item">
|
||||
<gl-link
|
||||
role="tab"
|
||||
:href="mergeRequestsSearchDashboardPath"
|
||||
class="nav-link gl-tab-nav-item !gl-no-underline"
|
||||
>
|
||||
|
|
|
@ -22,32 +22,32 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
|
|||
if new_chat_name.save
|
||||
flash[:notice] = safe_format(_("Authorized %{new_chat_name}"), new_chat_name: new_chat_name.chat_name)
|
||||
else
|
||||
flash[:alert] = _("Could not authorize chat nickname. Try again!")
|
||||
flash[:alert] = s_("Integrations|Could not authorize integration account nickname. Try again!")
|
||||
end
|
||||
|
||||
delete_chat_name_token
|
||||
redirect_to profile_chat_names_path
|
||||
redirect_to user_settings_integration_accounts_path
|
||||
end
|
||||
|
||||
def deny
|
||||
delete_chat_name_token
|
||||
|
||||
flash[:notice] =
|
||||
safe_format(_("Denied authorization of chat nickname %{user_name}."), user_name: chat_name_params[:user_name])
|
||||
safe_format(_("Denied authorization of account nickname %{user_name}."), user_name: chat_name_params[:user_name])
|
||||
|
||||
redirect_to profile_chat_names_path
|
||||
redirect_to user_settings_integration_accounts_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
@chat_name = chat_names.find(params[:id])
|
||||
|
||||
if @chat_name.destroy
|
||||
flash[:notice] = safe_format(_("Deleted chat nickname: %{chat_name}!"), chat_name: @chat_name.chat_name)
|
||||
flash[:notice] = safe_format(_("Deleted account nickname: %{chat_name}!"), chat_name: @chat_name.chat_name)
|
||||
else
|
||||
flash[:alert] = safe_format(_("Could not delete chat nickname %{chat_name}."), chat_name: @chat_name.chat_name)
|
||||
flash[:alert] = safe_format(_("Could not delete account nickname %{chat_name}."), chat_name: @chat_name.chat_name)
|
||||
end
|
||||
|
||||
redirect_to profile_chat_names_path, status: :found
|
||||
redirect_to user_settings_integration_accounts_path, status: :found
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -735,6 +735,7 @@ module Types
|
|||
field :languages, [Types::Projects::RepositoryLanguageType],
|
||||
null: true,
|
||||
description: "Programming languages used in the project.",
|
||||
scopes: [:api, :read_api, :ai_workflows],
|
||||
calls_gitaly: true
|
||||
|
||||
field :runners, Types::Ci::RunnerType.connection_type,
|
||||
|
|
|
@ -6,11 +6,17 @@ module Types
|
|||
class RepositoryLanguageType < BaseObject
|
||||
graphql_name 'RepositoryLanguage'
|
||||
|
||||
def self.authorization_scopes
|
||||
super + [:ai_workflows]
|
||||
end
|
||||
|
||||
field :name, GraphQL::Types::String, null: false,
|
||||
description: 'Name of the repository language.'
|
||||
description: 'Name of the repository language.',
|
||||
scopes: [:api, :read_api, :ai_workflows]
|
||||
|
||||
field :share, GraphQL::Types::Float, null: true,
|
||||
description: "Percentage of the repository's languages."
|
||||
description: "Percentage of the repository's languages.",
|
||||
scopes: [:api, :read_api, :ai_workflows]
|
||||
|
||||
field :color, Types::ColorType, null: true,
|
||||
description: 'Color to visualize the repository language.'
|
||||
|
|
|
@ -13,7 +13,7 @@ module ChatNames
|
|||
|
||||
token = request_token
|
||||
|
||||
new_profile_chat_name_url(token: token) if token
|
||||
new_user_settings_integration_account_url(token: token) if token
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
- if !Gitlab.com? || current_user.try(:onboarding_status_initial_registration_type) != "trial"
|
||||
= render 'devise/sessions/successful_verification', local_assigns
|
||||
- else
|
||||
- experiment(:lightweight_trial_registration_redesign, actor: current_user) do |e|
|
||||
- e.control do
|
||||
= render 'devise/sessions/successful_verification', local_assigns
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
%tbody
|
||||
- failed.each do |build|
|
||||
%tr.build-state
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #8c8c8c; font-weight: 500; font-size: 14px;" }
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #737278; font-weight: 500; font-size: 14px;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse: collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #d22f57; font-weight: 500; font-size: 16px; vertical-align: middle; padding-right: 8px; line-height: 10px" }
|
||||
%img{ alt: "✖", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display: block;", width: "10" }/
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #8c8c8c; font-weight: 500; font-size: 14px; vertical-align: middle;" }
|
||||
%img{ alt: "✖", height: "10", src: image_url('mailers/ci_pipeline_notif_v2/x-red.png'), style: "display: block;", width: "10" }/
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #737278; font-weight: 500; font-size: 14px; vertical-align: middle;" }
|
||||
= build.stage_name
|
||||
%td{ align: "right", style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #8c8c8c; font-weight: 500; font-size: 14px;" }
|
||||
%td{ align: "right", style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #737278; font-weight: 500; font-size: 14px;" }
|
||||
= render "notify/links/#{build.to_partial_path}", pipeline: pipeline, build: build
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
- title = local_assigns[:title]
|
||||
%tr.table-success
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#108548;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
|
||||
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%img{ alt: "✓", height: "16", src: image_url('mailers/ci_pipeline_notif_v2/check.png'), style: "display:block;", width: "16" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
= title
|
||||
%tr.spacer
|
||||
|
@ -16,9 +16,9 @@
|
|||
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;" }
|
||||
= _('Project')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
|
||||
|
@ -27,26 +27,26 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
|
||||
= @pipeline.source_ref
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Commit')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
- commit_link = content_tag(:a, @pipeline.short_sha, href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;").html_safe
|
||||
- if @merge_request
|
||||
|
@ -54,13 +54,13 @@
|
|||
= s_('Notify|%{commit_link} in %{mr_link}').html_safe % { commit_link: commit_link, mr_link: mr_link }
|
||||
- else
|
||||
= commit_link
|
||||
.commit{ style: "color:#5c5c5c;font-weight:300;" }
|
||||
.commit{ style: "color:#5c5c5c;font-weight:400;" }
|
||||
= @pipeline.git_commit_message.truncate(50)
|
||||
- commit = @pipeline.commit
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= s_('Notify|Commit Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
@ -75,9 +75,9 @@
|
|||
= commit.author_name
|
||||
- if commit.different_committer?
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= s_('Notify|Committed by')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
@ -115,7 +115,7 @@
|
|||
= _('API')
|
||||
|
||||
%tr
|
||||
%td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:300;line-height:1.4;padding:15px 5px;text-align:center;" }
|
||||
%td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
|
||||
- job_count = @pipeline.total_size
|
||||
- stage_count = @pipeline.stages_count
|
||||
= s_('Notify|successfully completed %{jobs} in %{stages}.').html_safe % { jobs: n_('%d job', '%d jobs', job_count) % job_count, stages: n_('%d stage', '%d stages', stage_count) % stage_count }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= user_label
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; margin: 0; padding: 14px 0 0px 5px; font-size: 15px; line-height: 1.4; color: #333333; font-weight: 400; width: 75%; border-top-style: solid; border-top-color: #ededed; border-top-width: 1px; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt;" }
|
||||
%ul.users-list{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 15px; line-height: 1.4; padding-right: 5px; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt;" }
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
%tr.success
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#108548;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
|
||||
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%img{ alt: "✓", height: "16", src: image_url('mailers/ci_pipeline_notif_v2/check.png'), style: "display:block;", width: "16" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
%span
|
||||
- if @merge_request.respond_to? :approvals_required
|
||||
|
@ -19,8 +19,8 @@
|
|||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
|
||||
%tbody
|
||||
%tr{ style: 'width:100%;' }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:16px;width:16px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @approved_by)
|
||||
%tr.spacer
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
|
||||
|
@ -30,8 +30,8 @@
|
|||
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "text-align:left;width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _("Project")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;" }= _("Project")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
|
||||
|
@ -40,19 +40,19 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Branch")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Branch")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%span.muted{ style: "color:#333333;text-decoration:none;" }
|
||||
= @merge_request.source_branch
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Author")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Author")
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
%tr.success
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#108548;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
|
||||
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }
|
||||
%img{ alt: "✓", height: "16", src: image_url('mailers/ci_pipeline_notif_v2/check.png'), style: "display:block;", width: "16" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
%span= _('Merge request was set to auto-merge')
|
||||
%tr.spacer
|
||||
|
@ -15,8 +15,8 @@
|
|||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
|
||||
%tbody
|
||||
%tr{ style: 'width:100%;' }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:16px;width:16px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
%span{ style: "font-weight: 600;color:#333333;" }= _('Merge request')
|
||||
%a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
|
||||
%span= _('was set to auto-merge by')
|
||||
|
@ -31,9 +31,9 @@
|
|||
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "text-align:left;width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _('Project')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;" }= _('Project')
|
||||
-# haml-lint:disable NoPlainNodes
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
|
||||
|
@ -42,19 +42,19 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%span.muted{ style: "color:#333333;text-decoration:none;" }
|
||||
= @merge_request.source_branch
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _('Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" }
|
||||
%img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%img{ alt: "✖", height: "16", src: image_url('mailers/ci_pipeline_notif_v2/x.png'), style: "display:block;", width: "16" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
= s_('Notify|Pipeline %{pipeline_name_or_id} has failed!') % { pipeline_name_or_id: sanitize_name(@pipeline.name) || "##{@pipeline.id}" }
|
||||
%tr.spacer
|
||||
|
@ -15,9 +15,9 @@
|
|||
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;" }
|
||||
= _('Project')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
|
||||
|
@ -26,26 +26,26 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
|
||||
= @pipeline.source_ref
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Commit')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
|
||||
= @pipeline.short_sha
|
||||
|
@ -53,13 +53,13 @@
|
|||
in
|
||||
%a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
|
||||
= @merge_request.to_reference
|
||||
.commit{ style: "color:#5c5c5c;font-weight:300;" }
|
||||
.commit{ style: "color:#5c5c5c;font-weight:400;" }
|
||||
= @pipeline.git_commit_message.truncate(50)
|
||||
- commit = @pipeline.commit
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= s_('Notify|Commit Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
@ -74,8 +74,8 @@
|
|||
= commit.author_name
|
||||
- if commit.different_committer?
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Committed by
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Committed by
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
%tbody
|
||||
%tr
|
||||
%td{ style: "vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" }
|
||||
%img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%img{ alt: "✖", height: "16", src: image_url('mailers/ci_pipeline_notif_v2/x.png'), style: "display:block;", width: "16" }/
|
||||
%td{ style: "vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
= s_('Notify|A remote mirror update has failed.')
|
||||
%tr.spacer{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
|
||||
|
@ -13,9 +13,9 @@
|
|||
%tr.section{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
|
||||
%td{ style: "padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
|
||||
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
|
||||
%tbody{ style: "font-size:15px;line-height:1.4;color:#8c8c8c;" }
|
||||
%tbody{ style: "font-size:15px;line-height:1.4;color:#737278;" }
|
||||
%tr
|
||||
%td{ style: "font-weight:300;padding:14px 0;margin:0;" }
|
||||
%td{ style: "font-weight:400;padding:14px 0;margin:0;" }
|
||||
= _('Source project')
|
||||
%td{ style: "font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
|
@ -25,12 +25,12 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= s_('Notify|Remote mirror')
|
||||
%td{ style: "font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
= @remote_mirror.safe_url
|
||||
%tr
|
||||
- update_at_start = '<td style="font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;">'.html_safe
|
||||
- update_at_start = '<td style="font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;">'.html_safe
|
||||
- update_at_mid = '</td><td style="font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;">'.html_safe
|
||||
- update_at_end = '</td>'.html_safe
|
||||
= html_escape(s_('Notify|%{update_at_start} Last update at %{update_at_mid} %{last_update_at} %{update_at_end}')) % {update_at_start: update_at_start, update_at_mid: update_at_mid, last_update_at: @remote_mirror.last_update_at, update_at_end: update_at_end}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
- default_font = "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"
|
||||
- default_style = "#{default_font}font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;"
|
||||
- default_style = "#{default_font}font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;"
|
||||
- spacer_style = "#{default_font};height:18px;font-size:18px;line-height:18px;"
|
||||
|
||||
%tr.alert
|
||||
%td{ style: "#{default_font}padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#FC6D26;" }
|
||||
%td{ style: "#{default_font}padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#d22f57;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
%tr.success
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#FC6D26;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#d22f57;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
|
||||
%img{ alt: "✗", height: "13", src: image_url('mailers/approval/icon-x-orange-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%img{ alt: "✗", height: "16", src: image_url('mailers/approval_v2/x.png'), style: "display:block;", width: "16" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
- if @merge_request.respond_to? :approvals_required
|
||||
%span
|
||||
|
@ -20,8 +20,8 @@
|
|||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;width:100%;" }
|
||||
%tbody
|
||||
%tr{ style: 'width:100%;' }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;text-align:center;" }
|
||||
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:16px;width:16px;margin-bottom:2px;vertical-align:bottom;", alt: "Merge request icon" }
|
||||
= s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @unapproved_by)
|
||||
%tr.spacer
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
|
||||
|
@ -31,9 +31,9 @@
|
|||
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "text-align:left;width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;" }
|
||||
= _('Project')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
|
||||
|
@ -42,21 +42,21 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
|
||||
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v2/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
|
||||
%span.muted{ style: "color:#333333;text-decoration:none;" }
|
||||
= @merge_request.source_branch
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#737278;font-weight:400;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
%tr
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
- default_font = "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"
|
||||
- default_style = "#{default_font}font-size:15px;line-height:1.4;color:#626168;font-weight:300;padding:14px 0;margin:0;"
|
||||
- default_style = "#{default_font}font-size:15px;line-height:1.4;color:#626168;font-weight:400;padding:14px 0;margin:0;"
|
||||
- spacer_style = "#{default_font};height:18px;font-size:18px;line-height:18px;"
|
||||
|
||||
%tr.alert
|
||||
%td{ style: "#{default_font}padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#FC6D26;" }
|
||||
%td{ style: "#{default_font}padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#3a383f;background-color:#fdf1dd;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "#{default_font}vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
%td{ style: "#{default_font}vertical-align:middle;color:#3a383f;text-align:center;" }
|
||||
%span
|
||||
= _("Someone signed in to your %{host} account from a new location") % { host: Gitlab.config.gitlab.host }
|
||||
%tr.spacer
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
= _('Never')
|
||||
|
||||
%td
|
||||
= link_button_to _('Remove'), profile_chat_name_path(chat_name), method: :delete, class: 'gl-float-right', aria: { label: _('Remove') }, data: { confirm: _('Are you sure you want to remove this nickname?'), confirm_btn_variant: 'danger' }, variant: :danger
|
||||
= link_button_to _('Remove'), user_settings_integration_account_path(chat_name), method: :delete, class: 'gl-float-right', aria: { label: _('Remove') }, data: { confirm: _('Are you sure you want to remove this nickname?'), confirm_btn_variant: 'danger' }, variant: :danger
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
- page_title _('Chat')
|
||||
- page_title s_('Integrations|Integration accounts')
|
||||
- @hide_search_settings = true
|
||||
- @force_desktop_expanded_sidebar = true
|
||||
|
||||
= render ::Layouts::SettingsSectionComponent.new(page_title) do |c|
|
||||
- c.with_description do
|
||||
= s_('Integrations|Manage your integration accounts in GitLab. Use these connected integrations to perform actions in GitLab.')
|
||||
- c.with_body do
|
||||
= render ::Layouts::CrudComponent.new(_('Active chat names'),
|
||||
= render ::Layouts::CrudComponent.new(s_('Integrations|Connected integration accounts'),
|
||||
count: @chat_names.size,
|
||||
icon: 'comment') do |c|
|
||||
icon: 'connected') do |c|
|
||||
- c.with_body do
|
||||
- if @chat_names.present?
|
||||
.table-responsive
|
||||
|
@ -20,4 +22,4 @@
|
|||
%tbody
|
||||
= render @chat_names
|
||||
- else
|
||||
.gl-text-subtle= _("You don't have any active chat names.")
|
||||
.gl-text-subtle= s_('Integrations|You do not have any connected integration accounts.')
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
= s_("SlackIntegration|You don't have to reauthorize this application if the permission scope changes in future releases.")
|
||||
- c.with_footer do
|
||||
.gl-flex
|
||||
= form_tag profile_chat_names_path, method: :post do
|
||||
= form_tag user_settings_integration_accounts_path, method: :post do
|
||||
= hidden_field_tag :token, @chat_name_token.token
|
||||
= render Pajamas::ButtonComponent.new(type: :submit, variant: :danger, button_options: { data: { testid: 'authorize-button' } }) do
|
||||
= _('Authorize')
|
||||
= form_tag deny_profile_chat_names_path, method: :delete do
|
||||
= form_tag deny_user_settings_integration_accounts_path, method: :delete do
|
||||
= hidden_field_tag :token, @chat_name_token.token
|
||||
= render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-ml-3' }) do
|
||||
= _('Deny')
|
||||
|
|
|
@ -51,14 +51,14 @@ resource :profile, only: [] do
|
|||
end
|
||||
end
|
||||
|
||||
resources :chat_names, only: [:index, :new, :create, :destroy] do
|
||||
collection do
|
||||
delete :deny
|
||||
end
|
||||
end
|
||||
|
||||
resource :avatar, only: [:destroy]
|
||||
|
||||
get 'chat_names', to: redirect('-/user_settings/integration_accounts')
|
||||
|
||||
get 'chat_names/new', to: redirect { |_params, request|
|
||||
"-/user_settings/integration_accounts/new?#{request.query_string}"
|
||||
}
|
||||
|
||||
resource :two_factor_auth, only: [:show, :create, :destroy] do
|
||||
member do
|
||||
post :codes
|
||||
|
|
|
@ -30,4 +30,9 @@ namespace :user_settings do
|
|||
delete :revoke
|
||||
end
|
||||
end
|
||||
resources :integration_accounts, only: [:index, :new, :create, :destroy], controller: '/profiles/chat_names' do
|
||||
collection do
|
||||
delete :deny
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
migration_job_name: BackfillOnboardingStatusRegistrationObjective
|
||||
description: Moves data from user_details.registration_objective to the new registration_objective field in user_details.onboarding_status
|
||||
description: Moves data from user_details.registration_objective to the new registration_objective
|
||||
field in user_details.onboarding_status
|
||||
feature_category: onboarding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176428
|
||||
milestone: '17.9'
|
||||
queued_migration_version: 20250117172734
|
||||
finalized_by:
|
||||
finalized_by: '20250708081911'
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
migration_job_name: BackfillOnboardingStatusSetupForCompany
|
||||
description: Moves data from user_preferences.setup_for_company to the new setup_for_company field in user_details.onboarding_status
|
||||
description: Moves data from user_preferences.setup_for_company to the new setup_for_company
|
||||
field in user_details.onboarding_status
|
||||
feature_category: onboarding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180602
|
||||
milestone: '17.10'
|
||||
queued_migration_version: 20250228155146
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20250713232042'
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
migration_job_name: FixUsernamespaceAuditEvents
|
||||
description: Some audit events are tagged with scope UserNamespace which is incorrect this migration corrects the scope
|
||||
description: Some audit events are tagged with scope UserNamespace which is incorrect
|
||||
this migration corrects the scope
|
||||
feature_category: audit_events
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/177095
|
||||
milestone: '17.9'
|
||||
queued_migration_version: 20250106104021
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20250709154335'
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddProjectIdToMergeRequestDiffCommits < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
|
||||
def change
|
||||
add_column :merge_request_diff_commits, :project_id, :bigint # rubocop:disable Migration/PreventAddingColumns -- Needed for future partitioning and sharding
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDeletedAtToAiCatalogItems < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.3'
|
||||
|
||||
def change
|
||||
add_column :ai_catalog_items, :deleted_at, :datetime_with_timezone
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexToAiCatalogItemsDeletedAt < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.3'
|
||||
|
||||
INDEX_NAME = 'index_ai_catalog_items_where_deleted_at_is_null'
|
||||
|
||||
def up
|
||||
add_concurrent_index :ai_catalog_items, :deleted_at, where: 'deleted_at IS NULL', name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :ai_catalog_items, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeHkBackfillOnboardingStatusRegistrationObjective < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillOnboardingStatusRegistrationObjective',
|
||||
table_name: :user_details,
|
||||
column_name: :user_id,
|
||||
job_arguments: [],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeHkFixUsernamespaceAuditEvents < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'FixUsernamespaceAuditEvents',
|
||||
table_name: :audit_events,
|
||||
column_name: :id,
|
||||
job_arguments: [],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeHkBackfillOnboardingStatusSetupForCompany < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillOnboardingStatusSetupForCompany',
|
||||
table_name: :user_details,
|
||||
column_name: :user_id,
|
||||
job_arguments: [],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
fb637948469c5747563202f5395dd1814f24bb92711d5355638f8b4f1fdf3429
|
|
@ -0,0 +1 @@
|
|||
23bd123c461e72a7d497fc10b99d2dc32be516cd518a0cde9655f3dd8d20ae5f
|
|
@ -0,0 +1 @@
|
|||
51a690472117d17ebf648351f10b8acdc23a95da2b623cc29a47e4faca2f1b78
|
|
@ -0,0 +1 @@
|
|||
3825f64cf8388cfdc4b48ba4573b6d90b46ce741df0ec6b6d07d646615d242ec
|
|
@ -0,0 +1 @@
|
|||
82a7340f7a4aa6c7996a795485bbe974f91e106b525697390ea57334036a3e6e
|
|
@ -0,0 +1 @@
|
|||
3235012c99576472c6b504866cfe78ba790260308a5b335eb73e6b7713849be6
|
|
@ -8295,6 +8295,7 @@ CREATE TABLE ai_catalog_items (
|
|||
description text NOT NULL,
|
||||
name text NOT NULL,
|
||||
public boolean DEFAULT false NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
CONSTRAINT check_7e02a4805b CHECK ((char_length(description) <= 1024)),
|
||||
CONSTRAINT check_edddd6e1fe CHECK ((char_length(name) <= 255))
|
||||
);
|
||||
|
@ -17504,7 +17505,8 @@ CREATE TABLE merge_request_diff_commits (
|
|||
trailers jsonb DEFAULT '{}'::jsonb,
|
||||
commit_author_id bigint,
|
||||
committer_id bigint,
|
||||
merge_request_commits_metadata_id bigint
|
||||
merge_request_commits_metadata_id bigint,
|
||||
project_id bigint
|
||||
);
|
||||
|
||||
CREATE TABLE merge_request_diff_details (
|
||||
|
@ -34537,6 +34539,8 @@ CREATE INDEX index_ai_catalog_items_on_project_id ON ai_catalog_items USING btre
|
|||
|
||||
CREATE INDEX index_ai_catalog_items_on_public ON ai_catalog_items USING btree (public);
|
||||
|
||||
CREATE INDEX index_ai_catalog_items_where_deleted_at_is_null ON ai_catalog_items USING btree (deleted_at) WHERE (deleted_at IS NULL);
|
||||
|
||||
CREATE INDEX index_ai_code_suggestion_events_on_organization_id ON ONLY ai_code_suggestion_events USING btree (organization_id);
|
||||
|
||||
CREATE INDEX index_ai_code_suggestion_events_on_user_id ON ONLY ai_code_suggestion_events USING btree (user_id);
|
||||
|
|
|
@ -18,7 +18,7 @@ like joining projects, commenting on issues, pushing changes to MRs, or closing
|
|||
For information about activity retention limits, see:
|
||||
|
||||
- [User activity time period limit](../user/profile/contributions_calendar.md#event-time-period-limit)
|
||||
- [Project activity time period limit](../user/project/working_with_projects.md#event-time-period-limit)
|
||||
- [Project activity time period limit](../user/project/working_with_projects.md#view-project-activity)
|
||||
|
||||
## List all events
|
||||
|
||||
|
|
|
@ -571,6 +571,27 @@ Get the list of all the compliance requirement controls.
|
|||
|
||||
Returns [`ComplianceRequirementControl`](#compliancerequirementcontrol).
|
||||
|
||||
### `Query.configuredAiCatalogItems`
|
||||
|
||||
AI Catalog items configured for use.
|
||||
|
||||
{{< details >}}
|
||||
**Introduced** in GitLab 18.2.
|
||||
**Status**: Experiment.
|
||||
{{< /details >}}
|
||||
|
||||
Returns [`AiCatalogItemConsumerConnection!`](#aicatalogitemconsumerconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="queryconfiguredaicatalogitemsprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | Project ID to retrieve configured AI Catalog items for. |
|
||||
|
||||
### `Query.containerRepository`
|
||||
|
||||
Find a container repository.
|
||||
|
@ -2277,6 +2298,33 @@ Input type: `AiCatalogAgentDeleteInput`
|
|||
| <a id="mutationaicatalogagentdeleteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
|
||||
| <a id="mutationaicatalogagentdeletesuccess"></a>`success` | [`Boolean!`](#boolean) | Returns true if catalog Agent was successfully deleted. |
|
||||
|
||||
### `Mutation.aiCatalogFlowCreate`
|
||||
|
||||
{{< details >}}
|
||||
**Introduced** in GitLab 18.3.
|
||||
**Status**: Experiment.
|
||||
{{< /details >}}
|
||||
|
||||
Input type: `AiCatalogFlowCreateInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationaicatalogflowcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationaicatalogflowcreatedescription"></a>`description` | [`String!`](#string) | Description for the flow. |
|
||||
| <a id="mutationaicatalogflowcreatename"></a>`name` | [`String!`](#string) | Name for the flow. |
|
||||
| <a id="mutationaicatalogflowcreateprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | Project for the flow. |
|
||||
| <a id="mutationaicatalogflowcreatepublic"></a>`public` | [`Boolean!`](#boolean) | Whether the flow is publicly visible in the catalog. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationaicatalogflowcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationaicatalogflowcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
|
||||
| <a id="mutationaicatalogflowcreateitem"></a>`item` | [`AiCatalogItem`](#aicatalogitem) | Item created. |
|
||||
|
||||
### `Mutation.aiDuoWorkflowCreate`
|
||||
|
||||
{{< details >}}
|
||||
|
@ -13966,6 +14014,30 @@ The connection type for [`AiCatalogItem`](#aicatalogitem).
|
|||
| <a id="aicatalogitemconnectionnodes"></a>`nodes` | [`[AiCatalogItem]`](#aicatalogitem) | A list of nodes. |
|
||||
| <a id="aicatalogitemconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `AiCatalogItemConsumerConnection`
|
||||
|
||||
The connection type for [`AiCatalogItemConsumer`](#aicatalogitemconsumer).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="aicatalogitemconsumerconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. |
|
||||
| <a id="aicatalogitemconsumerconnectionedges"></a>`edges` | [`[AiCatalogItemConsumerEdge]`](#aicatalogitemconsumeredge) | A list of edges. |
|
||||
| <a id="aicatalogitemconsumerconnectionnodes"></a>`nodes` | [`[AiCatalogItemConsumer]`](#aicatalogitemconsumer) | A list of nodes. |
|
||||
| <a id="aicatalogitemconsumerconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `AiCatalogItemConsumerEdge`
|
||||
|
||||
The edge type for [`AiCatalogItemConsumer`](#aicatalogitemconsumer).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="aicatalogitemconsumeredgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="aicatalogitemconsumeredgenode"></a>`node` | [`AiCatalogItemConsumer`](#aicatalogitemconsumer) | The item at the end of the edge. |
|
||||
|
||||
#### `AiCatalogItemEdge`
|
||||
|
||||
The edge type for [`AiCatalogItem`](#aicatalogitem).
|
||||
|
@ -21991,6 +22063,7 @@ An AI catalog agent.
|
|||
| <a id="aicatalogagentdescription"></a>`description` | [`String!`](#string) | Description of the item. |
|
||||
| <a id="aicatalogagentid"></a>`id` | [`ID!`](#id) | ID of the item. |
|
||||
| <a id="aicatalogagentitemtype"></a>`itemType` | [`AiCatalogItemType!`](#aicatalogitemtype) | Type of the item. |
|
||||
| <a id="aicatalogagentlatestversion"></a>`latestVersion` | [`AiCatalogItemVersion`](#aicatalogitemversion) | Latest version of the item. |
|
||||
| <a id="aicatalogagentname"></a>`name` | [`String!`](#string) | Name of the item. |
|
||||
| <a id="aicatalogagentproject"></a>`project` | [`Project`](#project) | Project for the item. |
|
||||
| <a id="aicatalogagentpublic"></a>`public` | [`Boolean!`](#boolean) | Whether the item is publicly visible in the catalog. |
|
||||
|
@ -22024,6 +22097,7 @@ An AI catalog flow.
|
|||
| <a id="aicatalogflowdescription"></a>`description` | [`String!`](#string) | Description of the item. |
|
||||
| <a id="aicatalogflowid"></a>`id` | [`ID!`](#id) | ID of the item. |
|
||||
| <a id="aicatalogflowitemtype"></a>`itemType` | [`AiCatalogItemType!`](#aicatalogitemtype) | Type of the item. |
|
||||
| <a id="aicatalogflowlatestversion"></a>`latestVersion` | [`AiCatalogItemVersion`](#aicatalogitemversion) | Latest version of the item. |
|
||||
| <a id="aicatalogflowname"></a>`name` | [`String!`](#string) | Name of the item. |
|
||||
| <a id="aicatalogflowproject"></a>`project` | [`Project`](#project) | Project for the item. |
|
||||
| <a id="aicatalogflowpublic"></a>`public` | [`Boolean!`](#boolean) | Whether the item is publicly visible in the catalog. |
|
||||
|
@ -22043,6 +22117,22 @@ An AI catalog flow version.
|
|||
| <a id="aicatalogflowversionupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the item version was updated. |
|
||||
| <a id="aicatalogflowversionversionname"></a>`versionName` | [`String`](#string) | Version name of the item version. |
|
||||
|
||||
### `AiCatalogItemConsumer`
|
||||
|
||||
An AI catalog item configuration.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="aicatalogitemconsumerenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether the catalog item is enabled or not. |
|
||||
| <a id="aicatalogitemconsumergroup"></a>`group` | [`Group`](#group) | Group in which the catalog item is configured. |
|
||||
| <a id="aicatalogitemconsumerid"></a>`id` | [`ID!`](#id) | ID of the configuration item. |
|
||||
| <a id="aicatalogitemconsumeritem"></a>`item` | [`AiCatalogItem`](#aicatalogitem) | Configuration catalog item. |
|
||||
| <a id="aicatalogitemconsumerlocked"></a>`locked` | [`Boolean!`](#boolean) | Indicates whether the catalog item configuration is locked or can be overridden. |
|
||||
| <a id="aicatalogitemconsumerorganization"></a>`organization` | [`Organization`](#organization) | Organization in which the catalog item is configured. |
|
||||
| <a id="aicatalogitemconsumerproject"></a>`project` | [`Project`](#project) | Project in which the catalog item is configured. |
|
||||
|
||||
### `AiConversationsThread`
|
||||
|
||||
Conversation thread of the AI feature.
|
||||
|
@ -27581,6 +27671,7 @@ GitLab Duo Agent Platform session.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="duoworkflowagentprivilegesnames"></a>`agentPrivilegesNames` | [`[String!]`](#string) | Privileges granted to the agent during execution. |
|
||||
| <a id="duoworkflowallowagenttorequestuser"></a>`allowAgentToRequestUser` | [`Boolean`](#boolean) | Allow the agent to request user input. |
|
||||
| <a id="duoworkflowarchived"></a>`archived` | [`Boolean`](#boolean) | Archived due to retention policy. |
|
||||
| <a id="duoworkflowcreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the session was created. |
|
||||
| <a id="duoworkflowenvironment"></a>`environment` | [`WorkflowEnvironment`](#workflowenvironment) | Environment, like IDE or web. |
|
||||
| <a id="duoworkflowfirstcheckpoint"></a>`firstCheckpoint` | [`DuoWorkflowEvent`](#duoworkflowevent) | First checkpoint of the session. |
|
||||
|
@ -27591,6 +27682,7 @@ GitLab Duo Agent Platform session.
|
|||
| <a id="duoworkflowpreapprovedagentprivilegesnames"></a>`preApprovedAgentPrivilegesNames` | [`[String!]`](#string) | Privileges pre-approved for the agent during execution. |
|
||||
| <a id="duoworkflowproject"></a>`project` | [`Project!`](#project) | Project that the session is in. |
|
||||
| <a id="duoworkflowprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project. |
|
||||
| <a id="duoworkflowstalled"></a>`stalled` | [`Boolean`](#boolean) | Workflow got created but has no checkpoints. |
|
||||
| <a id="duoworkflowstatus"></a>`status` | [`DuoWorkflowStatus`](#duoworkflowstatus) | Status of the session. |
|
||||
| <a id="duoworkflowstatusname"></a>`statusName` | [`String`](#string) | Status name of the session. |
|
||||
| <a id="duoworkflowupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the session was last updated. |
|
||||
|
@ -50170,6 +50262,7 @@ Implementations:
|
|||
| <a id="aicatalogitemdescription"></a>`description` | [`String!`](#string) | Description of the item. |
|
||||
| <a id="aicatalogitemid"></a>`id` | [`ID!`](#id) | ID of the item. |
|
||||
| <a id="aicatalogitemitemtype"></a>`itemType` | [`AiCatalogItemType!`](#aicatalogitemtype) | Type of the item. |
|
||||
| <a id="aicatalogitemlatestversion"></a>`latestVersion` | [`AiCatalogItemVersion`](#aicatalogitemversion) | Latest version of the item. |
|
||||
| <a id="aicatalogitemname"></a>`name` | [`String!`](#string) | Name of the item. |
|
||||
| <a id="aicatalogitemproject"></a>`project` | [`Project`](#project) | Project for the item. |
|
||||
| <a id="aicatalogitempublic"></a>`public` | [`Boolean!`](#boolean) | Whether the item is publicly visible in the catalog. |
|
||||
|
|
|
@ -952,7 +952,7 @@ Example response:
|
|||
|
||||
### List projects a user has contributed to
|
||||
|
||||
Returns a list of visible projects a given user has contributed to within the past year. For more information about what counts as a contribution, see [view projects you have contributed to](../user/project/working_with_projects.md#view-projects-you-have-contributed-to).
|
||||
Returns a list of visible projects a given user has contributed to within the past year. For more information about what counts as a contribution, [view projects you have contributed to](../user/project/working_with_projects.md#view-projects-you-work-with).
|
||||
|
||||
```plaintext
|
||||
GET /users/:user_id/contributed_projects
|
||||
|
|
|
@ -664,7 +664,7 @@ Prerequisites:
|
|||
- You must have at least the Maintainer role for the project.
|
||||
- The project must:
|
||||
- Be set as a [catalog project](#set-a-component-project-as-a-catalog-project).
|
||||
- Have a [project description](../../user/project/working_with_projects.md#edit-project-name-and-description) defined.
|
||||
- Have a [project description](../../user/project/working_with_projects.md#edit-a-project) defined.
|
||||
- Have a `README.md` file in the root directory for the commit SHA of the tag being released.
|
||||
- Have at least one [CI/CD component in the `templates/` directory](#directory-structure)
|
||||
for the commit SHA of the tag being released.
|
||||
|
@ -833,7 +833,7 @@ To mirror a GitLab.com component in your GitLab Self-Managed instance:
|
|||
1. Make sure that [network outbound requests](../../security/webhooks.md) are allowed for `gitlab.com`.
|
||||
1. [Create a group](../../user/group/_index.md#create-a-group) to host the component projects (recommended group: `components`).
|
||||
1. [Create a mirror of the component project](../../user/project/repository/mirror/pull.md) in the new group.
|
||||
1. Write a [project description](../../user/project/working_with_projects.md#edit-project-name-and-description)
|
||||
1. Write a [project description](../../user/project/working_with_projects.md#edit-a-project)
|
||||
for the component project mirror because mirroring repositories does not copy the description.
|
||||
1. [Set the self-hosted component project as a catalog resource](#set-a-component-project-as-a-catalog-project).
|
||||
1. Publish [a new release](../../user/project/releases/_index.md) in the self-hosted component project by
|
||||
|
|
|
@ -96,5 +96,5 @@ You can view the compute usage for your personal namespace:
|
|||
1. Select **Edit profile**.
|
||||
1. On the left sidebar, select **Usage Quotas**.
|
||||
|
||||
The projects list shows [personal projects](../../user/project/working_with_projects.md#view-personal-projects)
|
||||
The projects list shows [personal projects](../../user/project/working_with_projects.md)
|
||||
with compute usage or instance runners usage in the current month only.
|
||||
|
|
|
@ -57,19 +57,33 @@ The diagram below shows how protection rules are evaluated in the context of an
|
|||
%%{init: { "fontFamily": "GitLab Sans" }}%%
|
||||
graph TD
|
||||
accTitle: Evaluation of protected and immutable tag rules
|
||||
accDescr: An illustration of the evaluation process for protected and immutable tag rules during an image push.
|
||||
A[User attempts to push a tag] --> B{Does the user have the required role for push?}
|
||||
accDescr: Flow chart showing the evaluation process for protected and immutable tag rules during an image push.
|
||||
A[User attempts to push a tag] --> B{Protected tag check:<br/>Does user have required role<br/>to push this tag pattern?}
|
||||
B -- Yes --> C{Does the tag already exist?}
|
||||
B -- No --> D[Push denied: Insufficient permissions]
|
||||
C -- Yes --> E{Is the tag marked as immutable?}
|
||||
B -- No --> D[Push denied:<br/>Protected tag - insufficient permissions]
|
||||
C -- Yes --> E{Immutable tag check:<br/>Does tag match an<br/>immutable rule pattern?}
|
||||
C -- No --> F[Tag is created successfully]
|
||||
E -- Yes --> G[Push denied: Tag is immutable]
|
||||
E -- Yes --> G[Push denied:<br/>Tag is immutable]
|
||||
E -- No --> H[Tag is overwritten successfully]
|
||||
```
|
||||
|
||||
### Example scenarios
|
||||
|
||||
For a project with these rules:
|
||||
|
||||
- Protected tag rule: Pattern `v.*` requires at least the Maintainer role.
|
||||
- Immutable tag rule: Pattern `v\d+\.\d+\.\d+` protects semantic version tags.
|
||||
|
||||
| User role | Action | Protected tag check | Immutable tag check | Result |
|
||||
|-----------|--------|-------------------|-------------------|---------|
|
||||
| Developer | Push new tag `v1.0.0` | Denied | Not evaluated | Push denied. User lacks required role. |
|
||||
| Maintainer | Push new tag `v1.0.0` | Allowed | Not evaluated | Tag created. |
|
||||
| Maintainer | Overwrite existing tag `v1.0.0` | Allowed | Denied | Push denied. Tag is immutable. |
|
||||
| Maintainer | Push new tag `v-beta` | Allowed | Not evaluated | Tag created. |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use protected container tags, make sure the container registry is available:
|
||||
To use immutable container tags, make sure the container registry is available:
|
||||
|
||||
- In GitLab.com, the container registry is enabled by default.
|
||||
- In GitLab Self-Managed, [enable the metadata database](../../../administration/packages/container_registry_metadata_database.md).
|
||||
|
|
|
@ -19,7 +19,7 @@ You can assign a topic to several projects.
|
|||
|
||||
For example, you can create and assign the topics `python` and `hackathon` to all projects that use Python and are intended for Hackathon contributions.
|
||||
|
||||
Topics assigned to a project are displayed in the **Project overview** and [**Projects**](working_with_projects.md#view-all-projects-for-the-instance) lists, below the project information description.
|
||||
Topics assigned to a project are displayed in the **Project overview** and [**Projects**](working_with_projects.md#view-projects) lists, below the project information description.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ In GitLab 17.5 and later, you can also use `https://gitlab.example.com/-/p/<id>`
|
|||
|
||||
## Find the Project ID
|
||||
|
||||
You might also need the project ID if you want to interact with the project using the [GitLab API](../../api/_index.md).
|
||||
You might need the project ID if you want to interact with the project using the [GitLab API](../../api/_index.md).
|
||||
|
||||
To find the project ID:
|
||||
|
||||
|
@ -76,101 +76,171 @@ To find the project ID:
|
|||
1. On the project overview page, in the upper-right corner, select **Actions** ({{< icon name="ellipsis_v" >}}).
|
||||
1. Select **Copy project ID**.
|
||||
|
||||
## View all projects for the instance
|
||||
## View projects
|
||||
|
||||
To view all projects for the GitLab instance:
|
||||
Use the **Projects** list to view:
|
||||
|
||||
- All the projects on an instance
|
||||
- The projects you work with or own
|
||||
- Inactive projects, including archived projects and projects pending deletion
|
||||
|
||||
### View all projects on an instance
|
||||
|
||||
To view the projects on your GitLab instance:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Explore**.
|
||||
|
||||
On the left sidebar, **Projects** is selected.
|
||||
A list of all projects for the instance is displayed.
|
||||
1. Optional. Select a tab to filter which projects are displayed.
|
||||
|
||||
If you are not authenticated, the list shows public projects only.
|
||||
|
||||
## View projects you have contributed to
|
||||
### View projects you work with
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/13066) in GitLab 17.9 [with a flag](../../administration/feature_flags/_index.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from **Yours** to **Member** in GitLab 17.9 [with a flag](../../administration/feature_flags/_index.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
{{< alert type="flag" >}}
|
||||
|
||||
The availability of this feature is controlled by a feature flag. For more information, see the history.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
The **Contributed** tab displays projects where you have:
|
||||
To view the projects you have interacted with:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Optional. Select a tab to filter which projects are displayed:
|
||||
- **Contributed**: Projects where you have:
|
||||
- Created issues, merge requests, or epics
|
||||
- Commented on issues, merge requests, or epics
|
||||
- Closed issues, merge requests, or epics
|
||||
- Pushed commits
|
||||
- Approved merge requests
|
||||
- Merged merge requests
|
||||
- **Starred**: Projects you have [starred](#star-a-project)
|
||||
- **Personal**: Projects created under your personal namespace
|
||||
- **Member**: Projects you are a member of
|
||||
- **Inactive**: Archived projects and projects pending deletion
|
||||
|
||||
To view projects you have contributed to:
|
||||
You can also view your starred and personal projects from your personal profile:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select the **Contributed** tab.
|
||||
1. On the left sidebar, select your avatar and then your username.
|
||||
1. On the left sidebar, select **Starred projects** or **Personal projects**.
|
||||
|
||||
## View projects you are a member of
|
||||
### View inactive projects
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Yours" to "Member" in GitLab 17.9 [with a flag](../../administration/feature_flags/_index.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Pending deletion" to "Inactive" in GitLab 17.9 [with a flag](../../administration/feature_flags/_index.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
- [Moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
- [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in GitLab 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
To view projects you are a member of:
|
||||
A project is inactive when:
|
||||
|
||||
- It is pending deletion.
|
||||
- It has been archived.
|
||||
|
||||
To view all inactive projects:
|
||||
|
||||
1. Select either:
|
||||
- **View all my projects**, to filter your projects.
|
||||
- **Explore**, to filter all projects you can access.
|
||||
1. Select the **Inactive** tab.
|
||||
|
||||
Each project in the list shows:
|
||||
|
||||
- A badge indicating that the project is archived or marked for deletion.
|
||||
If the project is marked for deletion, the list also shows:
|
||||
- The time the project was marked for deletion.
|
||||
- The time the project is scheduled for final deletion.
|
||||
- A **Restore** action to stop the project being eventually deleted.
|
||||
|
||||
### View only projects you own
|
||||
|
||||
To view only the projects you are the owner of:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select the **Yours** tab.
|
||||
1. Select either:
|
||||
- **View all your projects**, to filter your projects.
|
||||
- **Explore**, to filter all projects you can access.
|
||||
1. Above the list of projects, select **Search or filter results**.
|
||||
1. From the **Role** dropdown list, select **Owner**.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
## View project activity
|
||||
|
||||
This tab appears as **Member** when the `your_work_projects_vue` feature flag is enabled.
|
||||
To view the activity of a project:
|
||||
|
||||
{{< /alert >}}
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Manage > Activity**.
|
||||
1. Optional. To filter activity by contribution type, select a tab:
|
||||
|
||||
## View personal projects
|
||||
- **All**: All contributions by project members.
|
||||
- **Push events**: Push events in the project.
|
||||
- **Merge events**: Accepted merge requests in the project.
|
||||
- **Issue events**: Issues opened and closed in the project.
|
||||
- **Comments**: Comments posted by project members.
|
||||
- **Designs**: Designs added, updated, and removed in the project.
|
||||
- **Team**: Members who joined and left the project.
|
||||
|
||||
Personal projects are projects created under your personal namespace.
|
||||
GitLab removes project activity events older than three years from the events table for performance reasons.
|
||||
|
||||
For example, if you create an account with the username `alex`, and create a project
|
||||
called `my-project` under your username, the project is created at `https://gitlab.example.com/alex/my-project`.
|
||||
## Filter projects by language
|
||||
|
||||
To view your personal projects:
|
||||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385465) in GitLab 15.9 [with a flag](../../administration/feature_flags/_index.md) named `project_language_search`. Enabled by default.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110956) in GitLab 15.9. Feature flag `project_language_search` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
You can filter projects by the programming language they use. To do this:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select the **Personal** tab.
|
||||
1. Select either:
|
||||
- **View all your projects**, to filter your projects.
|
||||
- **Explore**, to filter all projects you can access.
|
||||
1. Above the list of projects, select **Search or filter results**.
|
||||
1. From the **Language** dropdown list, select the language you want to filter projects by.
|
||||
|
||||
Or
|
||||
A list of projects that use the selected language is displayed.
|
||||
|
||||
1. On the left sidebar, select your avatar and then your username.
|
||||
1. On the left sidebar, select **Personal projects**.
|
||||
## Star a project
|
||||
|
||||
## View starred projects
|
||||
You can star projects you use frequently to make them easier to find.
|
||||
|
||||
To view projects you have [starred](#star-a-project):
|
||||
To star a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select the **Starred** tab.
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. In the upper-right corner of the page, select **Star**.
|
||||
|
||||
Or
|
||||
## Leave a project
|
||||
|
||||
1. On the left sidebar, select your avatar and then your username.
|
||||
1. On the left sidebar, select **Starred projects**.
|
||||
{{< history >}}
|
||||
|
||||
## Edit project name and description
|
||||
- The button to leave a project [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/431539) to the Actions menu in GitLab 16.7.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
When you leave a project:
|
||||
|
||||
- You are no longer a project member and cannot contribute.
|
||||
- All the issues and merge requests that were assigned
|
||||
to you are unassigned.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You can leave a project this way only when a project is part of a group under a [group namespace](../namespace/_index.md).
|
||||
- You must be a [direct member](members/_index.md#membership-types) of the project.
|
||||
|
||||
To leave a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. On the project overview page, in the upper-right corner, select **Actions** ({{< icon name="ellipsis_v" >}}).
|
||||
1. Select **Leave project**, then **Leave project** again.
|
||||
|
||||
## Edit a project
|
||||
|
||||
Use the project general settings to edit your project details.
|
||||
|
||||
|
@ -185,16 +255,40 @@ Prerequisites:
|
|||
Components published in the CI/CD catalog require a project description.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Add a project avatar
|
||||
### Rename a repository
|
||||
|
||||
A project's repository name defines its URL.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be an administrator or have the Maintainer or Owner role for the project.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
When you change the repository path, users may experience issues if they push to, or pull from, the old URL.
|
||||
For more information on redirect duration and its side-effects, see
|
||||
[redirects when renaming repositories](repository/_index.md#repository-path-changes).
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
To rename a repository:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Change path** text box, edit the path.
|
||||
1. Select **Change path**.
|
||||
|
||||
### Add a project avatar
|
||||
|
||||
Add a project avatar to help visually identify your project. If you do not add an avatar, GitLab displays the first letter of your project name as the default project avatar.
|
||||
|
||||
To add a project avatar, use one of the following methods:
|
||||
|
||||
- [Add a logo](#add-a-logo-to-your-repository) to your repository.
|
||||
- [Upload an avatar](#upload-an-avatar-in-project-settings) in your project settings.
|
||||
- Add a logo to your repository.
|
||||
- Upload an avatar in your project settings.
|
||||
|
||||
### Add a logo to your repository
|
||||
#### Add a logo to your repository
|
||||
|
||||
If you haven't uploaded an avatar to your project settings, GitLab looks for a file named `logo` in your repository to use as the default project avatar.
|
||||
|
||||
|
@ -209,7 +303,7 @@ To add a logo file to use as your project avatar:
|
|||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. In the root of your project repository, upload the logo file.
|
||||
|
||||
### Upload an avatar in project settings
|
||||
#### Upload an avatar in project settings
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
@ -231,14 +325,138 @@ To upload an avatar in your project settings:
|
|||
1. Select your avatar file.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Star a project
|
||||
## Delete a project
|
||||
|
||||
You can star projects you use frequently to make them easier to find.
|
||||
{{< history >}}
|
||||
|
||||
To star a project:
|
||||
- Default behavior [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/389557) to delayed project deletion for Premium and Ultimate tiers on [GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [GitLab Self-Managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in 16.0.
|
||||
- Option to delete projects immediately as a group setting removed [on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [on GitLab Self-Managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in GitLab 16.0.
|
||||
- Default behavior changed to delayed project deletion for [GitLab Free](https://gitlab.com/groups/gitlab-org/-/epics/17208) and [personal projects](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in 18.0.
|
||||
- Option to delete projects immediately [moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
You can schedule a project for deletion.
|
||||
By default, when you delete a project for the first time, it enters a pending deletion state.
|
||||
Delete a project again to remove it immediately.
|
||||
|
||||
On GitLab.com, after a project is deleted, its data is retained for 30 days.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Owner role for a project.
|
||||
- Owners must be [allowed to delete projects](../../administration/settings/visibility_and_access_controls.md#restrict-project-deletion-to-administrators).
|
||||
|
||||
To delete a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. In the upper-right corner of the page, select **Star**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Delete project** section, select **Delete project**.
|
||||
1. On the confirmation dialog, enter the project name and select **Yes, delete project**.
|
||||
1. Optional. To delete the project immediately, repeat these steps.
|
||||
|
||||
You can also [delete projects using the Rails console](troubleshooting.md#delete-a-project-using-console).
|
||||
|
||||
If the user who scheduled the project deletion loses access to the project before the deletion occurs
|
||||
(for example, by leaving the project, having their role downgraded, or being banned from the project),
|
||||
the deletion job restores the project. However, if the user regains access before the deletion job runs,
|
||||
the job removes the project permanently.
|
||||
|
||||
### Restore a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
- [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in GitLab 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Owner role for the project.
|
||||
|
||||
To restore a project pending deletion:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Restore project** section, select **Restore project**.
|
||||
|
||||
## Archive a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- Pages removal [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343109) in GitLab 17.5.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
When a project is archived, its fork relationship is removed and any open merge requests from forks
|
||||
targeting this project are automatically closed.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
When you archive a project, some features become read-only.
|
||||
These features are still accessible, but not writable.
|
||||
|
||||
- Repository
|
||||
- Packages
|
||||
- Issues
|
||||
- Merge requests
|
||||
- Feature flags
|
||||
- Pull mirroring
|
||||
- All other project features
|
||||
|
||||
Active pipeline schedules of archived projects don't become read-only.
|
||||
|
||||
If the project has deployed Pages, they are removed along with any custom domains,
|
||||
and the Pages link is no longer accessible.
|
||||
|
||||
Archived projects are:
|
||||
|
||||
- Labeled with an `archived` badge on the project page.
|
||||
- Listed in the **Inactive** tab on the group page, **Your work** page, and **Explore** page.
|
||||
- Read-only.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [Deactivate](../../ci/pipelines/schedules.md#edit-a-pipeline-schedule) or delete any active pipeline schedules for the project.
|
||||
<!-- LP: Remove this prerequisite after the issue is resolved (when a project is archived, active pipeline schedules continue to run). -->
|
||||
|
||||
To archive a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Archive project** section, select **Archive project**.
|
||||
1. To confirm, select **OK**.
|
||||
|
||||
### Unarchive a project
|
||||
|
||||
When you unarchive a project, the read-only restriction is removed,
|
||||
and the project becomes available in project lists.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be an administrator or have the Owner role for the project.
|
||||
|
||||
1. Find the archived project.
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select **Explore projects**.
|
||||
1. In the **Sort projects** dropdown list, select **Show archived projects**.
|
||||
1. In the **Filter by name** field, enter the project name.
|
||||
1. Select the project link.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Under **Advanced**, select **Expand**.
|
||||
1. In the **Unarchive project** section, select **Unarchive project**.
|
||||
1. To confirm, select **OK**.
|
||||
|
||||
The deployed Pages are not restored and you must rerun the pipeline.
|
||||
|
||||
When a project is unarchived, its pull mirroring process will automatically resume.
|
||||
|
||||
## Transfer a project
|
||||
|
||||
|
@ -325,280 +543,6 @@ When you transfer a project from a namespace licensed for GitLab.com Premium or
|
|||
- [Pipeline subscriptions](../../ci/pipelines/_index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt-deprecated)
|
||||
and [test cases](../../ci/test_cases/_index.md) are deleted.
|
||||
|
||||
## Delete a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- Default behavior [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/389557) to delayed project deletion for Premium and Ultimate tiers on [GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [GitLab Self-Managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in 16.0.
|
||||
- Option to delete projects immediately as a group setting removed [on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [on GitLab Self-Managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in GitLab 16.0.
|
||||
- Default behavior changed to delayed project deletion for [GitLab Free](https://gitlab.com/groups/gitlab-org/-/epics/17208) and [personal projects](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in 18.0.
|
||||
- Option to delete projects immediately [moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
You can schedule a project for deletion.
|
||||
By default, when you delete a project for the first time, it enters a pending deletion state.
|
||||
Delete a project again to remove it immediately.
|
||||
|
||||
On GitLab.com, after a project is deleted, its data is retained for seven days.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Owner role for a project.
|
||||
- Owners must be [allowed to delete projects](../../administration/settings/visibility_and_access_controls.md#restrict-project-deletion-to-administrators).
|
||||
|
||||
To delete a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Delete project** section, select **Delete project**.
|
||||
1. On the confirmation dialog, enter the project name and select **Yes, delete project**.
|
||||
1. Optional. To delete the project immediately, repeat these steps.
|
||||
|
||||
You can also [delete projects using the Rails console](troubleshooting.md#delete-a-project-using-console).
|
||||
|
||||
If the user who scheduled the project deletion loses access to the project before the deletion occurs
|
||||
(for example, by leaving the project, having their role downgraded, or being banned from the project),
|
||||
the deletion job restores the project. However, if the user regains access before the deletion job runs,
|
||||
the job removes the project permanently.
|
||||
|
||||
### View projects pending deletion
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Pending deletion" to "Inactive" in GitLab 17.9 [with a flag](../../administration/feature_flags/_index.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
- [Moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
- [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in GitLab 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
To view a list of all projects that are pending deletion:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select the **Inactive** tab.
|
||||
|
||||
Each project in the list shows:
|
||||
|
||||
- A badge indicating that the project has been marked for deletion.
|
||||
- The time the project was marked for deletion.
|
||||
- The time the project is scheduled for final deletion.
|
||||
- A **Restore** action to stop the project being eventually deleted.
|
||||
|
||||
### Restore a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Moved](https://gitlab.com/groups/gitlab-org/-/epics/17208) from GitLab Premium to GitLab Free in 18.0.
|
||||
- [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/issues/536244) in GitLab 18.0.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Owner role for the project.
|
||||
|
||||
To restore a project pending deletion:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Restore project** section, select **Restore project**.
|
||||
|
||||
## Archive a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- Pages removal [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343109) in GitLab 17.5.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
When a project is archived, its fork relationship is removed and any open merge requests from forks
|
||||
targeting this project are automatically closed.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
When you archive a project, some features become read-only.
|
||||
These features are still accessible, but not writable.
|
||||
|
||||
- Repository
|
||||
- Packages
|
||||
- Issues
|
||||
- Merge requests
|
||||
- Feature flags
|
||||
- Pull mirroring
|
||||
- All other project features
|
||||
|
||||
Active pipeline schedules of archived projects don't become read-only.
|
||||
|
||||
If the project has deployed Pages, they are removed along with any custom domains,
|
||||
and the Pages link is no longer accessible.
|
||||
|
||||
Archived projects are:
|
||||
|
||||
- Labeled with an `archived` badge on the project page.
|
||||
- Listed in the **Inactive** tab on the group page, **Your work** page, and **Explore** page.
|
||||
- Read-only.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [Deactivate](../../ci/pipelines/schedules.md#edit-a-pipeline-schedule) or delete any active pipeline schedules for the project.
|
||||
<!-- LP: Remove this prerequisite after the issue is resolved (when a project is archived, active pipeline schedules continue to run). -->
|
||||
|
||||
To archive a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Archive project** section, select **Archive project**.
|
||||
1. To confirm, select **OK**.
|
||||
|
||||
## Unarchive a project
|
||||
|
||||
When you unarchive a project, the read-only restriction is removed,
|
||||
and the project becomes available in project lists.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be an administrator or have the Owner role for the project.
|
||||
|
||||
1. Find the archived project.
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **View all my projects**.
|
||||
1. Select **Explore projects**.
|
||||
1. In the **Sort projects** dropdown list, select **Show archived projects**.
|
||||
1. In the **Filter by name** field, enter the project name.
|
||||
1. Select the project link.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Under **Advanced**, select **Expand**.
|
||||
1. In the **Unarchive project** section, select **Unarchive project**.
|
||||
1. To confirm, select **OK**.
|
||||
|
||||
The deployed Pages are not restored and you must rerun the pipeline.
|
||||
|
||||
When a project is unarchived, its pull mirroring process will automatically resume.
|
||||
|
||||
## View project activity
|
||||
|
||||
To view the activity of a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Manage > Activity**.
|
||||
1. Optional. To filter activity by contribution type, select a tab:
|
||||
|
||||
- **All**: All contributions by project members.
|
||||
- **Push events**: Push events in the project.
|
||||
- **Merge events**: Accepted merge requests in the project.
|
||||
- **Issue events**: Issues opened and closed in the project.
|
||||
- **Comments**: Comments posted by project members.
|
||||
- **Designs**: Designs added, updated, and removed in the project.
|
||||
- **Team**: Members who joined and left the project.
|
||||
|
||||
### Event time period limit
|
||||
|
||||
GitLab removes project activity events older than 3 years from the events table for performance reasons.
|
||||
|
||||
## Search in projects
|
||||
|
||||
To search through your projects, on the left sidebar, select **Search or go to**.
|
||||
GitLab filters as you type.
|
||||
|
||||
You can also look for the projects you [starred](#star-a-project) (**Starred projects**).
|
||||
|
||||
You can **Explore** all public and internal projects available in GitLab.com, from which you can filter by visibility,
|
||||
through **Trending**, best rated with **Most stars**, or **All** of them.
|
||||
|
||||
You can sort projects by:
|
||||
|
||||
- Name
|
||||
- Created date
|
||||
- Updated date
|
||||
- Stars
|
||||
|
||||
### Filter projects by language
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385465) in GitLab 15.9 [with a flag](../../administration/feature_flags/_index.md) named `project_language_search`. Enabled by default.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110956) in GitLab 15.9. Feature flag `project_language_search` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
You can filter projects by the programming language they use. To do this:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select either:
|
||||
- **View all your projects**, to filter your projects.
|
||||
- **Explore**, to filter all projects you can access.
|
||||
1. Above the list of projects, select **Search or filter results**.
|
||||
1. From the **Language** dropdown list, select the language you want to filter projects by.
|
||||
|
||||
A list of projects that use the selected language is displayed.
|
||||
|
||||
### View only projects you own
|
||||
|
||||
To view only the projects you are the owner of:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select either:
|
||||
- **View all your projects**, to filter your projects.
|
||||
- **Explore**, to filter all projects you can access.
|
||||
1. Above the list of projects, select **Search or filter results**.
|
||||
1. From the **Role** dropdown list, select **Owner**.
|
||||
|
||||
## Rename a repository
|
||||
|
||||
A project's repository name defines its URL.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be an administrator or have the Maintainer or Owner role for the project.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
When you change the repository path, users may experience issues if they push to, or pull from, the old URL.
|
||||
For more information on redirect duration and its side-effects, see
|
||||
[redirects when renaming repositories](repository/_index.md#repository-path-changes).
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
To rename a repository:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the **Change path** text box, edit the path.
|
||||
1. Select **Change path**.
|
||||
|
||||
## Leave a project
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- The button to leave a project [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/431539) to the Actions menu in GitLab 16.7.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
When you leave a project:
|
||||
|
||||
- You are no longer a project member and cannot contribute.
|
||||
- All the issues and merge requests that were assigned
|
||||
to you are unassigned.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You can leave a project this way only when a project is part of a group under a [group namespace](../namespace/_index.md).
|
||||
- You must be a [direct member](members/_index.md#membership-types) of the project.
|
||||
|
||||
To leave a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. On the project overview page, in the upper-right corner, select **Actions** ({{< icon name="ellipsis_v" >}}).
|
||||
1. Select **Leave project**, then **Leave project** again.
|
||||
|
||||
## Add a compliance framework to a project
|
||||
|
||||
{{< details >}}
|
||||
|
|
|
@ -16,12 +16,9 @@ rspec:
|
|||
services:
|
||||
- name: postgres:${POSTGRES_VERSION}
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
needs:
|
||||
- project: $CI_PROJECT_PATH
|
||||
job: "db:setup pg16"
|
||||
ref: "master"
|
||||
artifacts: true
|
||||
before_script:
|
||||
- |
|
||||
curl -o structure.sql -fsSL --retry 3 --header "JOB-TOKEN: $CI_JOB_TOKEN" "$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/files/db%2Fstructure.sql/raw?ref=master"
|
||||
- echo -e "\e[0Ksection_start:`date +%s`:postgresql16\r\e[0KInstalling PostgreSQL 16"
|
||||
- curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc|gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
|
||||
- echo "deb https://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo "$VERSION_CODENAME")-pgdg main" > /etc/apt/sources.list.d/pgdg.list
|
||||
|
@ -29,7 +26,8 @@ rspec:
|
|||
- echo -e "\e[0Ksection_end:`date +%s`:postgresql16\r\e[0K"
|
||||
- psql -h postgres -U $POSTGRES_USER -c 'create database gitlabhq_test;'
|
||||
- psql -h postgres -U $POSTGRES_USER -c 'create database gitlabhq_ci_test;'
|
||||
- psql -h postgres -U $POSTGRES_USER -q < pg_dumpall.sql > /dev/null
|
||||
- psql -h postgres -U $POSTGRES_USER -d gitlabhq_test -q < structure.sql > /dev/null
|
||||
- psql -h postgres -U $POSTGRES_USER -d gitlabhq_ci_test -q < structure.sql > /dev/null
|
||||
- cp gems/gitlab-backup-cli/spec/fixtures/config/database.yml config/
|
||||
- "sed -i \"s/username: postgres$/username: $POSTGRES_USER/g\" config/database.yml"
|
||||
- "sed -i \"s/password:\\s*$/password: $POSTGRES_PASSWORD/g\" config/database.yml"
|
||||
|
|
|
@ -15,6 +15,8 @@ require_relative 'validation/fixers/missing_index'
|
|||
require_relative 'validation/validators/different_definition_indexes'
|
||||
require_relative 'validation/validators/extra_indexes'
|
||||
require_relative 'validation/validators/missing_indexes'
|
||||
|
||||
require_relative 'validation/validators/different_sequence_owners'
|
||||
require_relative 'validation/validators/missing_sequences'
|
||||
|
||||
require_relative 'validation/validators/extra_table_columns'
|
||||
|
|
|
@ -26,7 +26,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def statement
|
||||
"CREATE SEQUENCE #{name}"
|
||||
statements = "CREATE SEQUENCE #{name};"
|
||||
statements += "\nALTER SEQUENCE #{name} OWNED BY #{owner}" if owner
|
||||
statements
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -23,7 +23,8 @@ module Gitlab
|
|||
DifferentDefinitionTables,
|
||||
DifferentDefinitionIndexes,
|
||||
DifferentDefinitionTriggers,
|
||||
DifferentDefinitionForeignKeys
|
||||
DifferentDefinitionForeignKeys,
|
||||
DifferentSequenceOwners
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Schema
|
||||
module Validation
|
||||
module Validators
|
||||
class DifferentSequenceOwners < Base
|
||||
ERROR_MESSAGE = "The sequence %s has a different owner between structure.sql and database"
|
||||
|
||||
def execute
|
||||
structure_sql.sequences.filter_map do |sequence|
|
||||
database_sequence = database.fetch_sequence_by_name(sequence.name)
|
||||
|
||||
next if database_sequence.nil?
|
||||
next if database_sequence.owner == sequence.owner
|
||||
|
||||
build_inconsistency(self.class, sequence, database_sequence)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,10 @@
|
|||
CREATE SEQUENCE missing_sequence;
|
||||
CREATE SEQUENCE shared_audit_event_id_seq;
|
||||
CREATE SEQUENCE abuse_events_id_seq;
|
||||
CREATE SEQUENCE zoekt_repositories_id_seq;
|
||||
|
||||
ALTER SEQUENCE abuse_events_id_seq OWNED by abuse_events.id;
|
||||
ALTER SEQUENCE zoekt_repositories_id_seq OWNED by zoekt_repositories.id;
|
||||
|
||||
CREATE INDEX missing_index ON events USING btree (created_at, author_id);
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ RSpec.describe Gitlab::Schema::Validation::Validators::Base, feature_category: :
|
|||
Gitlab::Schema::Validation::Validators::DifferentDefinitionTables,
|
||||
Gitlab::Schema::Validation::Validators::DifferentDefinitionIndexes,
|
||||
Gitlab::Schema::Validation::Validators::DifferentDefinitionTriggers,
|
||||
Gitlab::Schema::Validation::Validators::DifferentDefinitionForeignKeys
|
||||
Gitlab::Schema::Validation::Validators::DifferentDefinitionForeignKeys,
|
||||
Gitlab::Schema::Validation::Validators::DifferentSequenceOwners
|
||||
])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Schema::Validation::Validators::DifferentSequenceOwners, feature_category: :database do
|
||||
wrong_sequence_owners = %w[
|
||||
public.zoekt_repositories_id_seq
|
||||
]
|
||||
|
||||
include_examples 'sequence validators', described_class, wrong_sequence_owners
|
||||
end
|
|
@ -5,7 +5,6 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Schema::Validation::Validators::MissingSequences, feature_category: :database do
|
||||
missing_sequences = %w[
|
||||
public.missing_sequence
|
||||
public.shared_audit_event_id_seq
|
||||
public.abuse_events_id_seq
|
||||
]
|
||||
|
||||
|
|
|
@ -4,30 +4,68 @@ require 'spec_helper'
|
|||
|
||||
RSpec.shared_examples 'sequence validators' do |validator, expected_result|
|
||||
let(:structure_file_path) { 'spec/fixtures/structure.sql' }
|
||||
let(:database_sequences) do
|
||||
%w[
|
||||
wrong_sequence
|
||||
extra_sequence
|
||||
shared_audit_event_id_seq
|
||||
]
|
||||
end
|
||||
|
||||
let(:inconsistency_type) { validator.name }
|
||||
let(:connection_class) { class_double(Class, name: 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter') }
|
||||
# rubocop:disable RSpec/VerifiedDoubleReference
|
||||
let(:connection) do
|
||||
instance_double('connection', class: connection_class, current_schema: 'public')
|
||||
instance_double('connection', class: connection_class, exec_query: database_sequences, current_schema: 'public')
|
||||
end
|
||||
# rubocop:enable RSpec/VerifiedDoubleReference
|
||||
|
||||
let(:schema) { 'public' }
|
||||
let(:database) { Gitlab::Schema::Validation::Sources::Database.new(connection) }
|
||||
let(:structure_file) { Gitlab::Schema::Validation::Sources::StructureSql.new(structure_file_path, schema) }
|
||||
|
||||
before do
|
||||
allow(database).to receive(:sequence_exists?) do |sequence_name|
|
||||
database_sequences.include?(sequence_name)
|
||||
end
|
||||
let(:database_sequences) do
|
||||
[
|
||||
{
|
||||
'sequence_name' => 'wrong_sequence',
|
||||
'schema' => schema,
|
||||
'user_owner' => 'gitlab',
|
||||
'start_value' => '1',
|
||||
'increment_by' => '1',
|
||||
'min_value' => '1',
|
||||
'max_value' => '9223372036854775807',
|
||||
'cycle' => 'f',
|
||||
'cache_size' => '1',
|
||||
'owned_by_column' => 'some_table.id'
|
||||
},
|
||||
{
|
||||
'sequence_name' => 'extra_sequence',
|
||||
'schema' => schema,
|
||||
'user_owner' => 'gitlab',
|
||||
'start_value' => '1',
|
||||
'increment_by' => '1',
|
||||
'min_value' => '1',
|
||||
'max_value' => '9223372036854775807',
|
||||
'cycle' => 'f',
|
||||
'cache_size' => '1',
|
||||
'owned_by_column' => 'extra_table.id'
|
||||
},
|
||||
{
|
||||
'sequence_name' => 'zoekt_repositories_id_seq',
|
||||
'schema' => schema,
|
||||
'user_owner' => 'gitlab',
|
||||
'start_value' => '1',
|
||||
'increment_by' => '1',
|
||||
'min_value' => '1',
|
||||
'max_value' => '9223372036854775807',
|
||||
'cycle' => 'f',
|
||||
'cache_size' => '1',
|
||||
'owned_by_column' => "wrong_table.id"
|
||||
},
|
||||
{
|
||||
'sequence_name' => 'shared_audit_event_id_seq',
|
||||
'schema' => schema,
|
||||
'user_owner' => 'gitlab',
|
||||
'start_value' => '1',
|
||||
'increment_by' => '1',
|
||||
'min_value' => '1',
|
||||
'max_value' => '9223372036854775807',
|
||||
'cycle' => 'f',
|
||||
'cache_size' => '1',
|
||||
'owned_by_column' => nil
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
subject(:result) { validator.new(structure_file, database).execute }
|
||||
|
|
|
@ -8,17 +8,17 @@ module Sidebars
|
|||
|
||||
override :link
|
||||
def link
|
||||
profile_chat_names_path
|
||||
user_settings_integration_accounts_path
|
||||
end
|
||||
|
||||
override :title
|
||||
def title
|
||||
_('Chat')
|
||||
s_('Integrations|Integration accounts')
|
||||
end
|
||||
|
||||
override :sprite_icon
|
||||
def sprite_icon
|
||||
'comment'
|
||||
'connected'
|
||||
end
|
||||
|
||||
override :active_routes
|
||||
|
|
|
@ -3548,9 +3548,6 @@ msgstr ""
|
|||
msgid "Active Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active chat names"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active group access tokens"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12728,9 +12725,6 @@ msgstr ""
|
|||
msgid "Characters over limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Chat"
|
||||
msgstr ""
|
||||
|
||||
msgid "ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status} in %{duration}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18799,9 +18793,6 @@ msgstr ""
|
|||
msgid "Could not apply iteration command. There are multiple cadences but no cadence is specified."
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not authorize chat nickname. Try again!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not be moved. Select another project or try again."
|
||||
msgstr ""
|
||||
|
||||
|
@ -18826,7 +18817,7 @@ msgstr ""
|
|||
msgid "Could not create wiki template"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not delete chat nickname %{chat_name}."
|
||||
msgid "Could not delete account nickname %{chat_name}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not delete the label %{labelName}. Please try again."
|
||||
|
@ -21591,7 +21582,7 @@ msgstr ""
|
|||
msgid "Deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deleted chat nickname: %{chat_name}!"
|
||||
msgid "Deleted account nickname: %{chat_name}!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deleted commits:"
|
||||
|
@ -21633,7 +21624,7 @@ msgstr ""
|
|||
msgid "DeletionSettings|Period that deleted groups and projects will remain restorable for."
|
||||
msgstr ""
|
||||
|
||||
msgid "Denied authorization of chat nickname %{user_name}."
|
||||
msgid "Denied authorization of account nickname %{user_name}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Deny"
|
||||
|
@ -30389,6 +30380,9 @@ msgstr ""
|
|||
msgid "Group Git LFS status:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group Name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group Owner must have signed in with SAML before enabling Group Managed Accounts"
|
||||
msgstr ""
|
||||
|
||||
|
@ -30488,9 +30482,6 @@ msgstr ""
|
|||
msgid "Group milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group name (your organization)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33671,6 +33662,9 @@ msgstr ""
|
|||
msgid "Integrations|Confirm %{type} exclusion removal"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Connected integration accounts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Connection details"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33680,6 +33674,9 @@ msgstr ""
|
|||
msgid "Integrations|Connection successful."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Could not authorize integration account nickname. Try again!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Create new issue in Jira"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33758,6 +33755,9 @@ msgstr ""
|
|||
msgid "Integrations|Instance-level integration management"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Integration accounts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Integration cannot be reset."
|
||||
msgstr ""
|
||||
|
||||
|
@ -33770,6 +33770,9 @@ msgstr ""
|
|||
msgid "Integrations|Keep your PHP dependencies updated on Packagist."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Manage your integration accounts in GitLab. Use these connected integrations to perform actions in GitLab."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Mattermost slash commands"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33866,6 +33869,9 @@ msgstr ""
|
|||
msgid "Integrations|You can use this alias in your Slack commands"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You do not have any connected integration accounts."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You haven't activated any integrations yet."
|
||||
msgstr ""
|
||||
|
||||
|
@ -48460,6 +48466,9 @@ msgstr ""
|
|||
msgid "Project ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project Name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project Security Status"
|
||||
msgstr ""
|
||||
|
||||
|
@ -53232,6 +53241,9 @@ msgstr ""
|
|||
msgid "Runners|Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Container"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Containers"
|
||||
msgstr ""
|
||||
|
||||
|
@ -53648,6 +53660,9 @@ msgstr ""
|
|||
msgid "Runners|Only administrators can view this."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Operating System"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Operating systems"
|
||||
msgstr ""
|
||||
|
||||
|
@ -54151,6 +54166,9 @@ msgstr ""
|
|||
msgid "Runners|View installation instructions"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|View instructions in %{helpLink}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|View metrics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -54202,6 +54220,9 @@ msgstr ""
|
|||
msgid "Runners|Zone must have the right format."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|documentation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runner|1 day selected"
|
||||
msgid_plural "Runner|%d days selected"
|
||||
msgstr[0] ""
|
||||
|
@ -72607,9 +72628,6 @@ msgstr ""
|
|||
msgid "You don't have any WebAuthn devices registered yet."
|
||||
msgstr ""
|
||||
|
||||
msgid "You don't have any active chat names."
|
||||
msgstr ""
|
||||
|
||||
msgid "You don't have any applications."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Search
|
||||
# Cop that prevents checking migration_has_finished? on deprecated migrations
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# def disable_project_joins_for_blob?
|
||||
# Elastic::DataMigrationService
|
||||
# .migration_has_finished?(:backfill_archived_on_issues)
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# def disable_project_joins_for_blob?
|
||||
# Elastic::DataMigrationService.migration_has_finished?(:backfill_archived_on_issues)
|
||||
# end
|
||||
|
||||
class AvoidCheckingFinishedOnDeprecatedMigrations < RuboCop::Cop::Base
|
||||
MSG = 'Migration is deprecated and can not be used with `migration_has_finished?`.'
|
||||
DEPRECATED_MIGRATIONS = [
|
||||
:backfill_archived_on_issues
|
||||
].freeze
|
||||
|
||||
def_node_matcher :deprecated_migration?, <<~PATTERN
|
||||
(send
|
||||
(const (const {nil? cbase} :Elastic) :DataMigrationService) :migration_has_finished?
|
||||
(sym {#{DEPRECATED_MIGRATIONS.map { |m| ":#{m}" }.join(' ')}}))
|
||||
PATTERN
|
||||
|
||||
RESTRICT_ON_SEND = %i[migration_has_finished?].freeze
|
||||
|
||||
def on_send(node)
|
||||
add_offense(node) if deprecated_migration?(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,135 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Search
|
||||
# Cop that prevents checking migration_has_finished? on obsolete or non-existing migrations
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad - obsolete migration
|
||||
# def disable_project_joins_for_blob?
|
||||
# Elastic::DataMigrationService
|
||||
# .migration_has_finished?(:backfill_archived_on_issues)
|
||||
# end
|
||||
#
|
||||
# # bad - non-existing migration
|
||||
# def disable_project_joins_for_blob?
|
||||
# Elastic::DataMigrationService
|
||||
# .migration_has_finished?(:non_existing_migration)
|
||||
# end
|
||||
#
|
||||
# # good - valid migration
|
||||
# def disable_project_joins_for_blob?
|
||||
# Elastic::DataMigrationService.migration_has_finished?(:backfill_work_items_incorrect_data)
|
||||
# end
|
||||
|
||||
class AvoidCheckingFinishedOnInvalidMigrations < RuboCop::Cop::Base
|
||||
MSG_OBSOLETE = 'Migration is obsolete and can not be used with `migration_has_finished?`.'
|
||||
MSG_NON_EXISTING = 'Migration does not exist and can not be used with `migration_has_finished?`.'
|
||||
MSG_MISSING_IMPLEMENTATION = 'Migration implementation file is missing.'
|
||||
|
||||
DOCS_PATH = 'ee/elastic/docs'
|
||||
MIGRATIONS_PATH = 'ee/elastic/migrate'
|
||||
MIGRATION_REGEXP = /\A([0-9]+)_([_a-z0-9]*)\.rb\z/
|
||||
|
||||
# @!method migration_has_finished?(node)
|
||||
def_node_matcher :migration_has_finished?, <<~PATTERN
|
||||
(send
|
||||
(const (const {nil? cbase} :Elastic) :DataMigrationService) :migration_has_finished?
|
||||
(sym $_))
|
||||
PATTERN
|
||||
|
||||
RESTRICT_ON_SEND = %i[migration_has_finished?].freeze
|
||||
|
||||
def on_send(node)
|
||||
return unless migrations_available?
|
||||
|
||||
migration_has_finished?(node) do |migration_name|
|
||||
check_migration(node, migration_name)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :on_csend, :on_send
|
||||
|
||||
private
|
||||
|
||||
def migrations_available?
|
||||
Dir.exist?(DOCS_PATH) && Dir.exist?(MIGRATIONS_PATH)
|
||||
end
|
||||
|
||||
def check_migration(node, migration_name)
|
||||
migration_info = find_migration_info(migration_name)
|
||||
|
||||
if migration_info.nil?
|
||||
add_offense(node, message: MSG_NON_EXISTING)
|
||||
elsif migration_info[:obsolete]
|
||||
add_offense(node, message: MSG_OBSOLETE)
|
||||
elsif !migration_file_exists?(migration_name)
|
||||
add_offense(node, message: MSG_MISSING_IMPLEMENTATION)
|
||||
end
|
||||
end
|
||||
|
||||
def find_migration_info(migration_name)
|
||||
return @migration_cache[migration_name] if @migration_cache&.key?(migration_name)
|
||||
|
||||
@migration_cache ||= load_migrations
|
||||
@migration_cache[migration_name]
|
||||
end
|
||||
|
||||
def migration_file_exists?(migration_name)
|
||||
migration_files_cache.include?(migration_name)
|
||||
end
|
||||
|
||||
def migration_files_cache
|
||||
@migration_files_cache ||= load_migration_files
|
||||
end
|
||||
|
||||
def load_migration_files
|
||||
migration_files = Set.new
|
||||
|
||||
Dir.glob("#{MIGRATIONS_PATH}/*.rb").each do |file|
|
||||
filename = File.basename(file, '.rb')
|
||||
migration_files.add(::Regexp.last_match(1).to_sym) if filename =~ /\A[0-9]+_(.+)\z/
|
||||
end
|
||||
|
||||
migration_files
|
||||
end
|
||||
|
||||
def load_migrations
|
||||
migrations = {}
|
||||
|
||||
migration_files = Dir.glob("#{DOCS_PATH}/*.yml")
|
||||
|
||||
migration_files.each do |file|
|
||||
yaml_content = YAML.safe_load_file(file)
|
||||
next unless yaml_content.is_a?(Hash)
|
||||
|
||||
name = yaml_content['name']
|
||||
next unless name
|
||||
|
||||
# Convert CamelCase to snake_case to match the symbol format used in code
|
||||
snake_case_name = name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
||||
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
||||
.downcase
|
||||
.to_sym
|
||||
|
||||
migrations[snake_case_name] = {
|
||||
obsolete: yaml_content['obsolete'] == true,
|
||||
version: yaml_content['version'],
|
||||
milestone: yaml_content['milestone']
|
||||
}
|
||||
rescue StandardError => e
|
||||
# Skip files that can't be parsed, but log for debugging
|
||||
warn "Warning: Could not parse migration documentation file #{file}: #{e.message}"
|
||||
next
|
||||
end
|
||||
|
||||
migrations
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Profiles::ChatNamesController, feature_category: :integrations do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:token) { 'valid_token' }
|
||||
let(:chat_name_token_double) { instance_double(Gitlab::ChatNameToken) }
|
||||
let(:chat_name_params) do
|
||||
{
|
||||
team_id: 'T123',
|
||||
chat_id: 'U123',
|
||||
chat_name: 'test_user'
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Gitlab::ChatNameToken).to receive(:new).with(token).and_return(chat_name_token_double)
|
||||
allow(chat_name_token_double).to receive(:get).and_return(chat_name_params)
|
||||
allow(chat_name_token_double).to receive(:delete)
|
||||
end
|
||||
|
||||
context 'when save succeeds' do
|
||||
it 'sets success flash message and redirects' do
|
||||
post :create, params: { token: token }
|
||||
|
||||
expect(flash[:notice]).to include('Authorized test_user')
|
||||
expect(response).to redirect_to(user_settings_integration_accounts_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when save fails' do
|
||||
it 'sets error flash message and redirects' do
|
||||
allow_next_instance_of(ChatName) do |instance|
|
||||
allow(instance).to receive(:save).and_return(false)
|
||||
end
|
||||
|
||||
post :create, params: { token: token }
|
||||
|
||||
expect(flash[:alert]).to eq('Could not authorize integration account nickname. Try again!')
|
||||
expect(response).to redirect_to(user_settings_integration_accounts_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let_it_be(:chat_name) { create(:chat_name, user: user) }
|
||||
|
||||
it 'destroys the chat_name' do
|
||||
expect { delete :destroy, params: { id: chat_name.id } }
|
||||
.to change { ChatName.count }.by(-1)
|
||||
end
|
||||
|
||||
it 'redirects to the integration accounts page' do
|
||||
delete :destroy, params: { id: chat_name.id }
|
||||
|
||||
expect(response).to redirect_to(user_settings_integration_accounts_path)
|
||||
end
|
||||
|
||||
context 'when destroy fails' do
|
||||
it 'sets error flash message and redirects' do
|
||||
mock_chat_name = instance_double(ChatName)
|
||||
allow(mock_chat_name).to receive_messages(
|
||||
destroy: false,
|
||||
chat_name: chat_name.chat_name
|
||||
)
|
||||
|
||||
# As chat_names_controller finds the real db object, #destroy would rarely fail naturally
|
||||
# Hence, we're intercepting the controller-find process to return failing mock
|
||||
allow(controller).to receive(:chat_names).and_return(user.chat_names)
|
||||
allow(user.chat_names).to receive(:find).with(chat_name.id.to_s).and_return(mock_chat_name)
|
||||
|
||||
delete :destroy, params: { id: chat_name.id }
|
||||
|
||||
expect(flash[:alert]).to eq("Could not delete account nickname #{chat_name.chat_name}.")
|
||||
expect(response).to redirect_to(user_settings_integration_accounts_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -138,7 +138,7 @@ RSpec.describe 'Database schema',
|
|||
merge_requests_compliance_violations: %w[target_project_id],
|
||||
merge_request_diffs: %w[project_id],
|
||||
merge_request_diff_files: %w[project_id],
|
||||
merge_request_diff_commits: %w[commit_author_id committer_id merge_request_commits_metadata_id],
|
||||
merge_request_diff_commits: %w[project_id commit_author_id committer_id merge_request_commits_metadata_id],
|
||||
# merge_request_diff_commits_b5377a7a34 is the temporary table for the merge_request_diff_commits partitioning
|
||||
# backfill. It will get foreign keys after the partitioning is finished.
|
||||
merge_request_diff_commits_b5377a7a34: %w[merge_request_diff_id commit_author_id committer_id project_id],
|
||||
|
|
|
@ -62,7 +62,7 @@ RSpec.describe 'Profile > Chat', feature_category: :integrations do
|
|||
end
|
||||
|
||||
it 'goes to list of chat names and see chat account' do
|
||||
expect(page).to have_current_path(profile_chat_names_path, ignore_query: true)
|
||||
expect(page).to have_current_path(user_settings_integration_accounts_path, ignore_query: true)
|
||||
expect(page).to have_content('my_chat_team')
|
||||
expect(page).to have_content('my_chat_user')
|
||||
end
|
||||
|
@ -80,7 +80,7 @@ RSpec.describe 'Profile > Chat', feature_category: :integrations do
|
|||
end
|
||||
|
||||
it 'goes to list of chat names and do not see chat account' do
|
||||
expect(page).to have_current_path(profile_chat_names_path, ignore_query: true)
|
||||
expect(page).to have_current_path(user_settings_integration_accounts_path, ignore_query: true)
|
||||
expect(page).not_to have_content('my_chat_team')
|
||||
expect(page).not_to have_content('my_chat_user')
|
||||
end
|
||||
|
@ -97,7 +97,7 @@ RSpec.describe 'Profile > Chat', feature_category: :integrations do
|
|||
let_it_be(:chat_name) { create(:chat_name, user: user) }
|
||||
|
||||
before do
|
||||
visit profile_chat_names_path
|
||||
visit user_settings_integration_accounts_path
|
||||
end
|
||||
|
||||
it 'sees chat user' do
|
||||
|
@ -108,7 +108,7 @@ RSpec.describe 'Profile > Chat', feature_category: :integrations do
|
|||
it 'removes chat account' do
|
||||
click_link 'Remove'
|
||||
|
||||
expect(page).to have_content("You don't have any active chat names.")
|
||||
expect(page).to have_content("You do not have any connected integration accounts.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -182,7 +182,7 @@ RSpec.describe "Compare", :js, feature_category: :source_code_management do
|
|||
end
|
||||
|
||||
describe "compare view of tags" do
|
||||
it "compares tags" do
|
||||
it "compares tags", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/547846' do
|
||||
visit project_compare_index_path(project, from: "master", to: "master")
|
||||
|
||||
select_using_dropdown "from", "v1.0.0"
|
||||
|
|
|
@ -332,4 +332,20 @@ describe('GkeRegistrationInstructions', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when isWidget is true', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: { isWidget: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not display h2 headings', () => {
|
||||
expect(wrapper.find('h2').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not display token message', () => {
|
||||
expect(findTokenMessage().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -48,6 +48,7 @@ describe('GoogleCloudRegistrationInstructions', () => {
|
|||
const findZoneLink = () => wrapper.findByTestId('zone-link');
|
||||
const findMachineTypeLink = () => wrapper.findByTestId('machine-types-link');
|
||||
const findToken = () => wrapper.findByTestId('runner-token');
|
||||
const findTokenMessage = () => wrapper.findByTestId('runner-token-message');
|
||||
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findInstructionsButton = () => wrapper.findByTestId('show-instructions-button');
|
||||
|
@ -327,4 +328,20 @@ describe('GoogleCloudRegistrationInstructions', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when isWidget is true', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: { isWidget: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not display h2 headings', () => {
|
||||
expect(wrapper.find('h2').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not display token message', () => {
|
||||
expect(findTokenMessage().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -82,6 +82,7 @@ describe('RegistrationInstructions', () => {
|
|||
propsData: {
|
||||
runnerId: mockRunnerId,
|
||||
platform: DEFAULT_PLATFORM,
|
||||
isWidget: false,
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
|
@ -360,6 +361,7 @@ describe('RegistrationInstructions', () => {
|
|||
token: mockAuthenticationToken,
|
||||
groupPath: 'mock/group/path',
|
||||
projectPath: null,
|
||||
isWidget: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -377,6 +379,7 @@ describe('RegistrationInstructions', () => {
|
|||
token: mockAuthenticationToken,
|
||||
projectPath: 'mock/project/path',
|
||||
groupPath: null,
|
||||
isWidget: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -412,6 +415,7 @@ describe('RegistrationInstructions', () => {
|
|||
token: mockAuthenticationToken,
|
||||
groupPath: 'mock/group/path',
|
||||
projectPath: null,
|
||||
isWidget: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -429,6 +433,7 @@ describe('RegistrationInstructions', () => {
|
|||
token: mockAuthenticationToken,
|
||||
projectPath: 'mock/project/path',
|
||||
groupPath: null,
|
||||
isWidget: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import WizardOperatingSystemInstruction from '~/ci/runner/components/registration/wizard_operating_system_instruction.vue';
|
||||
import PlatformsDrawer from '~/ci/runner/components/registration/platforms_drawer.vue';
|
||||
import CliCommand from '~/ci/runner/components/registration/cli_command.vue';
|
||||
import { EXECUTORS_HELP_URL, SERVICE_COMMANDS_HELP_URL } from '~/ci/runner/constants';
|
||||
|
||||
describe('New Runner Registration Operation Systems Instructions', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(WizardOperatingSystemInstruction, {
|
||||
propsData: {
|
||||
token: 'token-123',
|
||||
title: 'Linux',
|
||||
platform: 'linux',
|
||||
},
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
const findPlatformsDrawer = () => wrapper.findComponent(PlatformsDrawer);
|
||||
const findStep1Section = () => wrapper.findByTestId('step-1');
|
||||
const findStep2Section = () => wrapper.findByTestId('step-2');
|
||||
const findStep3Section = () => wrapper.findByTestId('step-3');
|
||||
const findExecutorsHelpLink = () => wrapper.findByTestId('executors-help-link');
|
||||
const findServiceCommandsHelpLink = () => wrapper.findByTestId('service-commands-help-link');
|
||||
|
||||
describe('platform installation drawer instructions', () => {
|
||||
it('opens and closes the drawer', async () => {
|
||||
expect(findPlatformsDrawer().props('open')).toBe(false);
|
||||
|
||||
expect(wrapper.findByTestId('how-to-install-btn').exists()).toBe(true);
|
||||
|
||||
await wrapper.findByTestId('how-to-install-btn').vm.$emit('click');
|
||||
expect(findPlatformsDrawer().props('open')).toBe(true);
|
||||
|
||||
await findPlatformsDrawer().vm.$emit('close');
|
||||
expect(findPlatformsDrawer().props('open')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders step 1', () => {
|
||||
expect(findStep1Section().findComponent(CliCommand).props()).toMatchObject({
|
||||
command: ['gitlab-runner register', ` --url ${TEST_HOST}`, ` --token token-123`],
|
||||
prompt: '$',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders step 2', () => {
|
||||
expect(findStep2Section().exists()).toBe(true);
|
||||
expect(findExecutorsHelpLink().attributes('href')).toBe(EXECUTORS_HELP_URL);
|
||||
});
|
||||
|
||||
it('renders step 3', () => {
|
||||
expect(findStep3Section().findComponent(CliCommand).props()).toMatchObject({
|
||||
command: 'gitlab-runner run',
|
||||
prompt: '$',
|
||||
});
|
||||
expect(findServiceCommandsHelpLink().attributes('href')).toBe(SERVICE_COMMANDS_HELP_URL);
|
||||
});
|
||||
});
|
|
@ -1611,7 +1611,7 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj
|
|||
end
|
||||
|
||||
describe 'fields with :ai_workflows scope' do
|
||||
%w[id fullPath workItems].each do |field_name|
|
||||
%w[id fullPath workItems languages].each do |field_name|
|
||||
it "includes :ai_workflows scope for the #{field_name} field" do
|
||||
field = described_class.fields[field_name]
|
||||
expect(field.instance_variable_get(:@scopes)).to include(:ai_workflows)
|
||||
|
|
|
@ -12,4 +12,19 @@ RSpec.describe Types::Projects::RepositoryLanguageType do
|
|||
:color
|
||||
)
|
||||
end
|
||||
|
||||
describe '.authorization_scopes' do
|
||||
it 'allows ai_workflows scope token' do
|
||||
expect(described_class.authorization_scopes).to include(:ai_workflows)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'fields with :ai_workflows scope' do
|
||||
%w[name share].each do |field_name|
|
||||
it "includes :ai_workflows scope for the #{field_name} field" do
|
||||
field = described_class.fields[field_name]
|
||||
expect(field.instance_variable_get(:@scopes)).to include(:ai_workflows)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -929,6 +929,7 @@ project:
|
|||
- approval_policies
|
||||
- project_requirement_compliance_statuses
|
||||
- analyzer_statuses
|
||||
- configured_ai_catalog_items
|
||||
award_emoji:
|
||||
- awardable
|
||||
- user
|
||||
|
|
|
@ -4,9 +4,9 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Sidebars::UserSettings::Menus::ChatMenu, feature_category: :navigation do
|
||||
it_behaves_like 'User settings menu',
|
||||
link: '/-/profile/chat',
|
||||
title: _('Chat'),
|
||||
icon: 'comment',
|
||||
link: '/-/user_settings/integration_accounts',
|
||||
title: _('Integration accounts'),
|
||||
icon: 'connected',
|
||||
active_routes: { controller: :chat_names }
|
||||
|
||||
it_behaves_like 'User settings menu #render? method'
|
||||
|
|
|
@ -26,7 +26,7 @@ RSpec.describe Slack::BlockKit::AppHomeOpened, feature_category: :integrations d
|
|||
{
|
||||
type: 'button',
|
||||
text: include({ text: 'Connect your GitLab account' }),
|
||||
url: include(Gitlab::Routing.url_helpers.new_profile_chat_name_url)
|
||||
url: include(Gitlab::Routing.url_helpers.new_user_settings_integration_account_url)
|
||||
}
|
||||
)
|
||||
]
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require_relative '../../../../rubocop/cop/search/avoid_checking_finished_on_deprecated_migrations'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Search::AvoidCheckingFinishedOnDeprecatedMigrations, feature_category: :global_search do
|
||||
context 'when a deprecated class is used with migration_has_finished?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~SOURCE
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_archived_on_issues)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration is deprecated and can not be used with `migration_has_finished?`.
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non deprecated class is used with migration_has_finished?' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~SOURCE
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_project_permissions_in_blobs)
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when migration_has_finished? method is called on another class' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~SOURCE
|
||||
return if Klass.migration_has_finished?(:backfill_archived_on_issues)
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require_relative '../../../../rubocop/cop/search/avoid_checking_finished_on_invalid_migrations'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Search::AvoidCheckingFinishedOnInvalidMigrations, feature_category: :global_search do
|
||||
# Only run these tests if the migration directories are present
|
||||
if Dir.exist?('ee/elastic/docs') && Dir.exist?('ee/elastic/migrate')
|
||||
context 'when an obsolete migration is used with migration_has_finished?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~RUBY
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_archived_on_issues)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration is obsolete and can not be used with `migration_has_finished?`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non-existing migration is used with migration_has_finished?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~RUBY
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:non_existing_migration)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration does not exist and can not be used with `migration_has_finished?`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a valid migration is used with migration_has_finished?' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~RUBY
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_work_items_incorrect_data)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an obsolete migration (that was documented) is used' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~RUBY
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:apply_max_analyzed_offset)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration is obsolete and can not be used with `migration_has_finished?`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when migration_has_finished? method is called on another class' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~RUBY
|
||||
return if Klass.migration_has_finished?(:backfill_archived_on_issues)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when migration exists in docs but missing implementation file' do
|
||||
it 'flags it as an offense' do
|
||||
# Mock the migration info to exist but file to be missing
|
||||
allow(cop).to receive_messages(
|
||||
find_migration_info: { obsolete: false, version: '1.0', milestone: '14.0' },
|
||||
migration_file_exists?: false
|
||||
)
|
||||
|
||||
expect_offense <<~RUBY
|
||||
return if Elastic::DataMigrationService.migration_has_finished?(:missing_implementation)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration implementation file is missing.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when YAML parsing fails' do
|
||||
it 'handles errors gracefully' do
|
||||
# Mock Dir.glob to return a file that will cause YAML parsing to fail
|
||||
allow(Dir).to receive(:glob).with('ee/elastic/docs/*.yml').and_return(['invalid_file.yml'])
|
||||
allow(YAML).to receive(:safe_load_file).and_raise(StandardError.new('Invalid YAML'))
|
||||
|
||||
# Capture the warning message
|
||||
expect { cop.send(:load_migrations) }.to output(
|
||||
/Warning: Could not parse migration documentation file/
|
||||
).to_stderr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue