Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5bc6fcec0e
commit
eaf41d710d
|
|
@ -49,7 +49,6 @@
|
|||
/lib/registry/
|
||||
/lib/policy/
|
||||
/lib/feature/
|
||||
/lib/flowdock/
|
||||
/lib/generators/
|
||||
/lib/gitaly/
|
||||
/lib/api/
|
||||
|
|
|
|||
|
|
@ -565,7 +565,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'lib/container_registry/client.rb'
|
||||
- 'lib/container_registry/gitlab_api_client.rb'
|
||||
- 'lib/container_registry/tag.rb'
|
||||
- 'lib/flowdock/git/builder.rb'
|
||||
- 'lib/gitlab/alert_management/alert_status_counts.rb'
|
||||
- 'lib/gitlab/alert_management/payload/base.rb'
|
||||
- 'lib/gitlab/alert_management/payload/managed_prometheus.rb'
|
||||
|
|
|
|||
|
|
@ -391,7 +391,6 @@ Layout/LineLength:
|
|||
- 'app/models/integrations/emails_on_push.rb'
|
||||
- 'app/models/integrations/ewm.rb'
|
||||
- 'app/models/integrations/external_wiki.rb'
|
||||
- 'app/models/integrations/flowdock.rb'
|
||||
- 'app/models/integrations/hangouts_chat.rb'
|
||||
- 'app/models/integrations/harbor.rb'
|
||||
- 'app/models/integrations/jenkins.rb'
|
||||
|
|
|
|||
|
|
@ -3,23 +3,6 @@
|
|||
Layout/SpaceInLambdaLiteral:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/controllers/concerns/issuable_actions.rb'
|
||||
- 'app/controllers/projects/ci/daily_build_group_report_results_controller.rb'
|
||||
- 'app/controllers/projects/merge_requests_controller.rb'
|
||||
- 'app/finders/releases/group_releases_finder.rb'
|
||||
- 'app/graphql/mutations/ci/runner/update.rb'
|
||||
- 'app/models/abuse_report.rb'
|
||||
- 'app/models/alert_management/alert.rb'
|
||||
- 'app/models/alert_management/http_integration.rb'
|
||||
- 'app/models/analytics/cycle_analytics/aggregation.rb'
|
||||
- 'app/models/analytics/usage_trends/measurement.rb'
|
||||
- 'app/models/application_setting.rb'
|
||||
- 'app/models/audit_event.rb'
|
||||
- 'app/models/award_emoji.rb'
|
||||
- 'app/models/board_group_recent_visit.rb'
|
||||
- 'app/models/board_project_recent_visit.rb'
|
||||
- 'app/models/bulk_import.rb'
|
||||
- 'app/models/bulk_imports/entity.rb'
|
||||
- 'app/models/bulk_imports/tracker.rb'
|
||||
- 'app/models/ci/build.rb'
|
||||
- 'app/models/ci/daily_build_group_report_result.rb'
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ Style/FormatString:
|
|||
- 'app/models/integrations/emails_on_push.rb'
|
||||
- 'app/models/integrations/ewm.rb'
|
||||
- 'app/models/integrations/external_wiki.rb'
|
||||
- 'app/models/integrations/flowdock.rb'
|
||||
- 'app/models/integrations/hangouts_chat.rb'
|
||||
- 'app/models/integrations/irker.rb'
|
||||
- 'app/models/integrations/jenkins.rb'
|
||||
|
|
@ -281,7 +280,6 @@ Style/FormatString:
|
|||
- 'lib/api/helpers/packages/conan/api_helpers.rb'
|
||||
- 'lib/bulk_imports/network_error.rb'
|
||||
- 'lib/bulk_imports/users_mapper.rb'
|
||||
- 'lib/flowdock/git/builder.rb'
|
||||
- 'lib/gitlab/bitbucket_server_import/importer.rb'
|
||||
- 'lib/gitlab/checks/push_file_count_check.rb'
|
||||
- 'lib/gitlab/ci/ansi2json/line.rb'
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ Style/PercentLiteralDelimiters:
|
|||
- 'app/models/integrations/emails_on_push.rb'
|
||||
- 'app/models/integrations/external_wiki.rb'
|
||||
- 'app/models/integrations/field.rb'
|
||||
- 'app/models/integrations/flowdock.rb'
|
||||
- 'app/models/integrations/jenkins.rb'
|
||||
- 'app/models/integrations/jira.rb'
|
||||
- 'app/models/integrations/packagist.rb'
|
||||
|
|
@ -485,7 +484,6 @@ Style/PercentLiteralDelimiters:
|
|||
- 'lib/bitbucket/representation/issue.rb'
|
||||
- 'lib/container_registry/path.rb'
|
||||
- 'lib/feature.rb'
|
||||
- 'lib/flowdock/git/builder.rb'
|
||||
- 'lib/generators/gitlab/usage_metric_definition_generator.rb'
|
||||
- 'lib/generators/gitlab/usage_metric_generator.rb'
|
||||
- 'lib/gitlab.rb'
|
||||
|
|
|
|||
3
Gemfile
3
Gemfile
|
|
@ -264,9 +264,6 @@ gem 'discordrb-webhooks', '~> 3.4', require: false
|
|||
gem 'jira-ruby', '~> 2.1.4'
|
||||
gem 'atlassian-jwt', '~> 0.2.0'
|
||||
|
||||
# Flowdock integration
|
||||
gem 'flowdock', '~> 0.7'
|
||||
|
||||
# Slack integration
|
||||
gem 'slack-messenger', '~> 2.3.4'
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@
|
|||
{"name":"flipper","version":"0.25.0","platform":"ruby","checksum":"ccb2776752b8378bc994c9d873ccde290c090341940761b873494695ee697add"},
|
||||
{"name":"flipper-active_record","version":"0.25.0","platform":"ruby","checksum":"85a5c99465e2cc6a09e91931a9998b0dbd463cd6c80dd513129377132e3eb67f"},
|
||||
{"name":"flipper-active_support_cache_store","version":"0.25.0","platform":"ruby","checksum":"7282bf994b08d1a076b65c6f3b51e3dc04fcb00fa6e7b20089e60db25c7b531b"},
|
||||
{"name":"flowdock","version":"0.7.1","platform":"ruby","checksum":"cfa95b2ac96e5f883f6e419d7a891f76cfcc17a28c416b6b714bbdffc8dbd912"},
|
||||
{"name":"fog-aliyun","version":"0.3.3","platform":"ruby","checksum":"d0aa317f7c1473a1d684fff51699f216bb9cb78b9ee9ce55a81c9bcc93fb85ee"},
|
||||
{"name":"fog-aws","version":"3.15.0","platform":"ruby","checksum":"09752931ea0c6165b018e1a89253248d86b246645086ccf19bc44fabe3381e8c"},
|
||||
{"name":"fog-core","version":"2.1.0","platform":"ruby","checksum":"53e5d793554d7080d015ef13cd44b54027e421d924d9dba4ce3d83f95f37eda9"},
|
||||
|
|
|
|||
|
|
@ -484,9 +484,6 @@ GEM
|
|||
flipper-active_support_cache_store (0.25.0)
|
||||
activesupport (>= 4.2, < 8)
|
||||
flipper (~> 0.25.0)
|
||||
flowdock (0.7.1)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
fog-aliyun (0.3.3)
|
||||
fog-core
|
||||
fog-json
|
||||
|
|
@ -1649,7 +1646,6 @@ DEPENDENCIES
|
|||
flipper (~> 0.25.0)
|
||||
flipper-active_record (~> 0.25.0)
|
||||
flipper-active_support_cache_store (~> 0.25.0)
|
||||
flowdock (~> 0.7)
|
||||
fog-aliyun (~> 0.3)
|
||||
fog-aws (~> 3.15)
|
||||
fog-core (= 2.1.0)
|
||||
|
|
|
|||
|
|
@ -85,32 +85,25 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<article
|
||||
class="draft-note-component note-wrapper"
|
||||
@mouseenter="handleMouseEnter(draft)"
|
||||
@mouseleave="handleMouseLeave(draft)"
|
||||
<noteable-note
|
||||
:note="draft"
|
||||
:line="line"
|
||||
:discussion-root="true"
|
||||
:class="{ 'gl-mb-0!': glFeatures.mrReviewSubmitComment }"
|
||||
class="draft-note-component draft-note"
|
||||
@handleEdit="handleEditing"
|
||||
@cancelForm="handleNotEditing"
|
||||
@updateSuccess="handleNotEditing"
|
||||
@handleDeleteNote="deleteDraft"
|
||||
@handleUpdateNote="update"
|
||||
@toggleResolveStatus="toggleResolveDiscussion(draft.id)"
|
||||
@mouseenter.native="handleMouseEnter(draft)"
|
||||
@mouseleave.native="handleMouseLeave(draft)"
|
||||
>
|
||||
<ul class="notes draft-notes">
|
||||
<noteable-note
|
||||
:note="draft"
|
||||
:line="line"
|
||||
:discussion-root="true"
|
||||
:class="{ 'gl-mb-0!': glFeatures.mrReviewSubmitComment }"
|
||||
class="draft-note"
|
||||
@handleEdit="handleEditing"
|
||||
@cancelForm="handleNotEditing"
|
||||
@updateSuccess="handleNotEditing"
|
||||
@handleDeleteNote="deleteDraft"
|
||||
@handleUpdateNote="update"
|
||||
@toggleResolveStatus="toggleResolveDiscussion(draft.id)"
|
||||
>
|
||||
<template #note-header-info>
|
||||
<gl-badge variant="warning" class="gl-mr-2">{{ __('Pending') }}</gl-badge>
|
||||
</template>
|
||||
</noteable-note>
|
||||
</ul>
|
||||
|
||||
<template v-if="!isEditingDraft">
|
||||
<template #note-header-info>
|
||||
<gl-badge variant="warning" class="gl-mr-2">{{ __('Pending') }}</gl-badge>
|
||||
</template>
|
||||
<template v-if="!isEditingDraft" #after-note-body>
|
||||
<div
|
||||
v-if="draftCommands"
|
||||
v-safe-html:[$options.safeHtmlConfig]="draftCommands"
|
||||
|
|
@ -134,5 +127,5 @@ export default {
|
|||
</gl-button>
|
||||
</p>
|
||||
</template>
|
||||
</article>
|
||||
</noteable-note>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
|||
import RunnerName from '../runner_name.vue';
|
||||
import RunnerTags from '../runner_tags.vue';
|
||||
import RunnerTypeBadge from '../runner_type_badge.vue';
|
||||
import RunnerJobStatusBadge from '../runner_job_status_badge.vue';
|
||||
|
||||
import { formatJobCount } from '../../utils';
|
||||
import {
|
||||
|
|
@ -25,6 +26,7 @@ export default {
|
|||
RunnerName,
|
||||
RunnerTags,
|
||||
RunnerTypeBadge,
|
||||
RunnerJobStatusBadge,
|
||||
RunnerUpgradeStatusIcon: () =>
|
||||
import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'),
|
||||
TooltipOnTruncate,
|
||||
|
|
@ -81,6 +83,8 @@ export default {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<runner-job-status-badge :job-status="runner.jobExecutionStatus" />
|
||||
|
||||
<runner-summary-field icon="clock">
|
||||
<gl-sprintf :message="$options.i18n.I18N_LAST_CONTACT_LABEL">
|
||||
<template #timeAgo>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
<script>
|
||||
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
|
||||
import {
|
||||
I18N_JOB_STATUS_RUNNING,
|
||||
I18N_JOB_STATUS_IDLE,
|
||||
JOB_STATUS_RUNNING,
|
||||
JOB_STATUS_IDLE,
|
||||
} from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlBadge,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
jobStatus: {
|
||||
required: false,
|
||||
default: null,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
badge() {
|
||||
switch (this.jobStatus) {
|
||||
case JOB_STATUS_RUNNING:
|
||||
return {
|
||||
classes: 'gl-text-blue-600! gl-border gl-border-blue-600!',
|
||||
label: I18N_JOB_STATUS_RUNNING,
|
||||
};
|
||||
case JOB_STATUS_IDLE:
|
||||
return {
|
||||
classes: 'gl-text-gray-700! gl-border gl-border-gray-500!',
|
||||
label: I18N_JOB_STATUS_IDLE,
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-badge
|
||||
v-if="badge"
|
||||
size="sm"
|
||||
class="gl-mr-3 gl-bg-transparent!"
|
||||
variant="muted"
|
||||
:class="badge.classes"
|
||||
>
|
||||
{{ badge.label }}
|
||||
</gl-badge>
|
||||
</template>
|
||||
|
|
@ -32,6 +32,10 @@ export const I18N_STATUS_NEVER_CONTACTED = s__('Runners|Never contacted');
|
|||
export const I18N_STATUS_OFFLINE = s__('Runners|Offline');
|
||||
export const I18N_STATUS_STALE = s__('Runners|Stale');
|
||||
|
||||
// Executor Status
|
||||
export const I18N_JOB_STATUS_RUNNING = s__('Runners|Running');
|
||||
export const I18N_JOB_STATUS_IDLE = s__('Runners|Idle');
|
||||
|
||||
// Status help popover
|
||||
export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
|
||||
|
||||
|
|
@ -134,6 +138,11 @@ export const STATUS_NEVER_CONTACTED = 'NEVER_CONTACTED';
|
|||
export const STATUS_OFFLINE = 'OFFLINE';
|
||||
export const STATUS_STALE = 'STALE';
|
||||
|
||||
// CiRunnerJobExecutionStatus
|
||||
|
||||
export const JOB_STATUS_RUNNING = 'RUNNING';
|
||||
export const JOB_STATUS_IDLE = 'IDLE';
|
||||
|
||||
// CiRunnerAccessLevel
|
||||
|
||||
export const ACCESS_LEVEL_NOT_PROTECTED = 'NOT_PROTECTED';
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ fragment ListItemShared on CiRunner {
|
|||
createdAt
|
||||
contactedAt
|
||||
status(legacyMode: null)
|
||||
jobExecutionStatus
|
||||
userPermissions {
|
||||
updateRunner
|
||||
deleteRunner
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
<script>
|
||||
import { GlAlert, GlFormInputGroup, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { GlAlert, GlFormInputGroup, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
|
||||
import CodeBlock from '~/vue_shared/components/code_block.vue';
|
||||
import { generateAgentRegistrationCommand } from '../clusters_util';
|
||||
import { I18N_AGENT_TOKEN } from '../constants';
|
||||
import { I18N_AGENT_TOKEN, HELM_VERSION_POLICY_URL } from '../constants';
|
||||
|
||||
export default {
|
||||
i18n: I18N_AGENT_TOKEN,
|
||||
advancedInstallPath: helpPagePath('user/clusters/agent/install/index', {
|
||||
anchor: 'advanced-installation-method',
|
||||
}),
|
||||
HELM_VERSION_POLICY_URL,
|
||||
components: {
|
||||
GlAlert,
|
||||
CodeBlock,
|
||||
GlFormInputGroup,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
ModalCopyButton,
|
||||
},
|
||||
inject: ['kasAddress', 'kasVersion'],
|
||||
|
|
@ -77,6 +79,11 @@ export default {
|
|||
|
||||
<p>
|
||||
{{ $options.i18n.basicInstallBody }}
|
||||
<gl-sprintf :message="$options.i18n.helmVersionText">
|
||||
<template #link="{ content }"
|
||||
><gl-link :href="$options.HELM_VERSION_POLICY_URL" target="_blank"
|
||||
>{{ content }} <gl-icon name="external-link" :size="12" /></gl-link></template
|
||||
></gl-sprintf>
|
||||
</p>
|
||||
|
||||
<p class="gl-display-flex gl-align-items-flex-start">
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
<script>
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlDropdownText,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { GlCollapsibleListbox, GlButton, GlSprintf } from '@gitlab/ui';
|
||||
import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'AvailableAgentsDropdown',
|
||||
i18n: I18N_AVAILABLE_AGENTS_DROPDOWN,
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlDropdownText,
|
||||
GlSearchBoxByType,
|
||||
GlCollapsibleListbox,
|
||||
GlButton,
|
||||
GlSprintf,
|
||||
},
|
||||
props: {
|
||||
|
|
@ -46,13 +36,21 @@ export default {
|
|||
|
||||
return this.selectedAgent;
|
||||
},
|
||||
dropdownItems() {
|
||||
return this.availableAgents.map((agent) => {
|
||||
return {
|
||||
value: agent,
|
||||
text: agent,
|
||||
};
|
||||
});
|
||||
},
|
||||
shouldRenderCreateButton() {
|
||||
return this.searchTerm && !this.availableAgents.includes(this.searchTerm);
|
||||
},
|
||||
filteredResults() {
|
||||
const lowerCasedSearchTerm = this.searchTerm.toLowerCase();
|
||||
return this.availableAgents.filter((resultString) =>
|
||||
resultString.toLowerCase().includes(lowerCasedSearchTerm),
|
||||
return this.dropdownItems.filter((item) =>
|
||||
item.value.toLowerCase().includes(lowerCasedSearchTerm),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
@ -60,59 +58,48 @@ export default {
|
|||
selectAgent(agent) {
|
||||
this.$emit('agentSelected', agent);
|
||||
this.selectedAgent = agent;
|
||||
this.clearSearch();
|
||||
},
|
||||
isSelected(agent) {
|
||||
return this.selectedAgent === agent;
|
||||
},
|
||||
clearSearch() {
|
||||
this.searchTerm = '';
|
||||
},
|
||||
focusSearch() {
|
||||
this.$refs.searchInput.focusInput();
|
||||
},
|
||||
handleShow() {
|
||||
this.clearSearch();
|
||||
this.focusSearch();
|
||||
|
||||
this.$refs.dropdown.closeAndFocus();
|
||||
},
|
||||
onKeyEnter() {
|
||||
if (!this.searchTerm?.length) {
|
||||
return;
|
||||
}
|
||||
this.$refs.dropdown.hide();
|
||||
this.selectAgent(this.searchTerm);
|
||||
},
|
||||
searchAgent(searchQuery) {
|
||||
this.searchTerm = searchQuery;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-dropdown ref="dropdown" :text="dropdownText" :loading="isRegistering" @shown="handleShow">
|
||||
<template #header>
|
||||
<gl-search-box-by-type
|
||||
ref="searchInput"
|
||||
v-model.trim="searchTerm"
|
||||
@keydown.enter.stop.prevent="onKeyEnter"
|
||||
/>
|
||||
</template>
|
||||
<gl-dropdown-item
|
||||
v-for="agent in filteredResults"
|
||||
:key="agent"
|
||||
:is-checked="isSelected(agent)"
|
||||
is-check-item
|
||||
@click="selectAgent(agent)"
|
||||
<div @keydown.enter.stop.prevent="onKeyEnter">
|
||||
<gl-collapsible-listbox
|
||||
ref="dropdown"
|
||||
v-model="selectedAgent"
|
||||
class="gl-w-full"
|
||||
toggle-class="select-agent-dropdown"
|
||||
:items="filteredResults"
|
||||
:toggle-text="dropdownText"
|
||||
:loading="isRegistering"
|
||||
:searchable="true"
|
||||
:no-results-text="$options.i18n.noResults"
|
||||
@search="searchAgent"
|
||||
@select="selectAgent"
|
||||
>
|
||||
{{ agent }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-text v-if="!filteredResults.length" ref="noMatchingResults">{{
|
||||
$options.i18n.noResults
|
||||
}}</gl-dropdown-text>
|
||||
<template v-if="shouldRenderCreateButton">
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item data-testid="create-config-button" @click="selectAgent(searchTerm)">
|
||||
<gl-sprintf :message="$options.i18n.createButton">
|
||||
<template #searchTerm>{{ searchTerm }}</template>
|
||||
</gl-sprintf>
|
||||
</gl-dropdown-item>
|
||||
</template>
|
||||
</gl-dropdown>
|
||||
<template v-if="shouldRenderCreateButton" #footer>
|
||||
<gl-button
|
||||
category="tertiary"
|
||||
class="gl-justify-content-start! gl-border-t-1! gl-border-t-solid gl-border-t-gray-200 gl-pl-7! gl-rounded-top-left-none! gl-rounded-top-right-none!"
|
||||
:class="{ 'gl-mt-3': !filteredResults.length }"
|
||||
@click="selectAgent(searchTerm)"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.createButton">
|
||||
<template #searchTerm>{{ searchTerm }}</template>
|
||||
</gl-sprintf>
|
||||
</gl-button>
|
||||
</template>
|
||||
</gl-collapsible-listbox>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ export const I18N_AGENT_TOKEN = {
|
|||
basicInstallBody: s__(
|
||||
'ClusterAgents|From a terminal, connect to your cluster and run this command. The token is included in the command.',
|
||||
),
|
||||
helmVersionText: s__(
|
||||
'ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd}).',
|
||||
),
|
||||
|
||||
advancedInstallTitle: s__('ClusterAgents|Advanced installation methods'),
|
||||
advancedInstallBody: s__(
|
||||
|
|
@ -108,6 +111,8 @@ export const I18N_AGENT_TOKEN = {
|
|||
),
|
||||
};
|
||||
|
||||
export const HELM_VERSION_POLICY_URL = 'https://helm.sh/docs/topics/version_skew/';
|
||||
|
||||
export const I18N_AGENT_MODAL = {
|
||||
registerAgentButton: s__('ClusterAgents|Register'),
|
||||
close: __('Close'),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
|
||||
import { NEW_CODE_QUALITY_FINDINGS } from '../i18n';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
newFindings: NEW_CODE_QUALITY_FINDINGS,
|
||||
},
|
||||
components: { GlButton, GlIcon },
|
||||
props: {
|
||||
codeQuality: {
|
||||
|
|
@ -22,22 +26,33 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div data-testid="diff-codequality" class="gl-relative">
|
||||
<ul
|
||||
class="gl-list-style-none gl-mb-0 gl-p-0 codequality-findings-list gl-border-top-1 gl-border-bottom-1 gl-bg-gray-10"
|
||||
<div
|
||||
data-testid="diff-codequality"
|
||||
class="gl-relative codequality-findings-list gl-border-top-1 gl-border-bottom-1 gl-bg-gray-10 gl-pl-5 gl-pt-4 gl-pb-4"
|
||||
>
|
||||
<h4
|
||||
data-testid="diff-codequality-findings-heading"
|
||||
class="gl-mt-0 gl-mb-0 gl-font-base gl-font-regular"
|
||||
>
|
||||
{{ $options.i18n.newFindings }}
|
||||
</h4>
|
||||
<ul class="gl-list-style-none gl-mb-0 gl-p-0">
|
||||
<li
|
||||
v-for="finding in codeQuality"
|
||||
:key="finding.description"
|
||||
class="gl-pt-1 gl-pb-1 gl-pl-3 gl-border-solid gl-border-bottom-0 gl-border-right-0 gl-border-1 gl-border-gray-100 gl-font-regular"
|
||||
class="gl-pt-1 gl-pb-1 gl-font-regular gl-display-flex"
|
||||
>
|
||||
<gl-icon
|
||||
:size="12"
|
||||
:name="severityIcon(finding.severity)"
|
||||
:class="severityClass(finding.severity)"
|
||||
class="codequality-severity-icon"
|
||||
/>
|
||||
{{ finding.description }}
|
||||
<span class="gl-mr-3">
|
||||
<gl-icon
|
||||
:size="12"
|
||||
:name="severityIcon(finding.severity)"
|
||||
:class="severityClass(finding.severity)"
|
||||
class="codequality-severity-icon"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span class="severity-copy">{{ finding.severity }}</span> - {{ finding.description }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<gl-button
|
||||
|
|
|
|||
|
|
@ -303,7 +303,11 @@ export default {
|
|||
class="diff-td notes-content parallel old"
|
||||
>
|
||||
<div v-for="draft in lineDrafts(line, 'left')" :key="draft.id" class="content">
|
||||
<draft-note :draft="draft" :line="line.left" />
|
||||
<article class="note-wrapper">
|
||||
<ul class="notes draft-notes">
|
||||
<draft-note :draft="draft" :line="line.left" />
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -311,7 +315,11 @@ export default {
|
|||
class="diff-td notes-content parallel new"
|
||||
>
|
||||
<div v-for="draft in lineDrafts(line, 'right')" :key="draft.id" class="content">
|
||||
<draft-note :draft="draft" :line="line.right" />
|
||||
<article class="note-wrapper">
|
||||
<ul class="notes draft-notes">
|
||||
<draft-note :draft="draft" :line="line.right" />
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -49,3 +49,5 @@ export const CONFLICT_TEXT = {
|
|||
};
|
||||
|
||||
export const HIDE_COMMENTS = __('Hide comments');
|
||||
|
||||
export const NEW_CODE_QUALITY_FINDINGS = __('New code quality findings');
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ export default {
|
|||
:draft="draftForDiscussion(discussion.reply_id)"
|
||||
:line="line"
|
||||
/>
|
||||
<div
|
||||
<li
|
||||
v-else-if="canShowReplyActions && showReplies"
|
||||
:class="{ 'is-replying': isReplying }"
|
||||
class="discussion-reply-holder gl-border-t-0! clearfix"
|
||||
|
|
@ -334,7 +334,7 @@ export default {
|
|||
@cancelForm="cancelReplyForm"
|
||||
/>
|
||||
<note-signed-out-widget v-if="!isLoggedIn" />
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</discussion-notes>
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ export default {
|
|||
</gl-avatar-link>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!isDraft" class="timeline-avatar gl-float-left">
|
||||
<div v-else class="timeline-avatar gl-float-left">
|
||||
<gl-avatar-link :href="author.path">
|
||||
<gl-avatar
|
||||
:src="author.avatar_url"
|
||||
|
|
@ -516,6 +516,9 @@ export default {
|
|||
@handleFormUpdate="formUpdateHandler"
|
||||
@cancelForm="formCancelHandler"
|
||||
/>
|
||||
<div class="timeline-discussion-body-footer">
|
||||
<slot name="after-note-body"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</timeline-entry-item>
|
||||
|
|
|
|||
|
|
@ -58,11 +58,21 @@ export default {
|
|||
<collapsed-reviewer-list :users="sortedReviewers" :issuable-type="issuableType" />
|
||||
|
||||
<div class="value hide-collapsed">
|
||||
<template v-if="hasNoUsers">
|
||||
<span class="no-value">
|
||||
{{ __('None') }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="hasNoUsers" class="no-value" data-testid="no-value">
|
||||
{{ __('None') }}
|
||||
<template v-if="editable">
|
||||
-
|
||||
<button
|
||||
type="button"
|
||||
class="gl-button btn-link gl-reset-color!"
|
||||
data-testid="assign-yourself"
|
||||
data-qa-selector="assign_yourself_button"
|
||||
@click="assignSelf"
|
||||
>
|
||||
{{ __('assign yourself') }}
|
||||
</button>
|
||||
</template>
|
||||
</span>
|
||||
|
||||
<uncollapsed-reviewer-list
|
||||
v-else
|
||||
|
|
|
|||
|
|
@ -143,6 +143,13 @@ export default {
|
|||
eventHub.$off('sidebar.saveReviewers', this.saveReviewers);
|
||||
},
|
||||
methods: {
|
||||
reviewBySelf() {
|
||||
// Notify gl dropdown that we are now assigning to current user
|
||||
this.$el.parentElement.dispatchEvent(new Event('assignYourself'));
|
||||
|
||||
this.mediator.addSelfReview();
|
||||
this.saveReviewers();
|
||||
},
|
||||
saveReviewers() {
|
||||
this.loading = true;
|
||||
|
||||
|
|
@ -181,6 +188,7 @@ export default {
|
|||
:editable="canUpdate"
|
||||
:issuable-type="issuableType"
|
||||
@request-review="requestReview"
|
||||
@assign-self="reviewBySelf"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ export default class SidebarMediator {
|
|||
assignYourself() {
|
||||
this.store.addAssignee(this.store.currentUser);
|
||||
}
|
||||
addSelfReview() {
|
||||
this.store.addReviewer(this.store.currentUser);
|
||||
}
|
||||
|
||||
async saveAssignees(field) {
|
||||
const selected = this.store.assignees.map((u) => u.id);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { s__ } from '~/locale';
|
|||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { getParameterByName } from '~/lib/utils/url_utility';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
|
||||
import {
|
||||
i18n,
|
||||
|
|
@ -25,7 +24,6 @@ import {
|
|||
WIDGET_TYPE_START_AND_DUE_DATE,
|
||||
WIDGET_TYPE_WEIGHT,
|
||||
WIDGET_TYPE_HIERARCHY,
|
||||
WORK_ITEM_VIEWED_STORAGE_KEY,
|
||||
WIDGET_TYPE_MILESTONE,
|
||||
WIDGET_TYPE_ITERATION,
|
||||
WORK_ITEM_TYPE_VALUE_ISSUE,
|
||||
|
|
@ -49,7 +47,6 @@ import WorkItemDueDate from './work_item_due_date.vue';
|
|||
import WorkItemAssignees from './work_item_assignees.vue';
|
||||
import WorkItemLabels from './work_item_labels.vue';
|
||||
import WorkItemMilestone from './work_item_milestone.vue';
|
||||
import WorkItemInformation from './work_item_information.vue';
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
|
|
@ -72,8 +69,6 @@ export default {
|
|||
WorkItemTitle,
|
||||
WorkItemState,
|
||||
WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'),
|
||||
WorkItemInformation,
|
||||
LocalStorageSync,
|
||||
WorkItemTypeIcon,
|
||||
WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'),
|
||||
WorkItemMilestone,
|
||||
|
|
@ -108,7 +103,6 @@ export default {
|
|||
error: undefined,
|
||||
updateError: undefined,
|
||||
workItem: {},
|
||||
showInfoBanner: true,
|
||||
updateInProgress: false,
|
||||
};
|
||||
},
|
||||
|
|
@ -276,18 +270,10 @@ export default {
|
|||
};
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
/** make sure that if the user has not even dismissed the alert ,
|
||||
* should no be able to see the information next time and update the local storage * */
|
||||
this.dismissBanner();
|
||||
},
|
||||
methods: {
|
||||
isWidgetPresent(type) {
|
||||
return this.workItem?.widgets?.find((widget) => widget.type === type);
|
||||
},
|
||||
dismissBanner() {
|
||||
this.showInfoBanner = false;
|
||||
},
|
||||
toggleConfidentiality(confidentialStatus) {
|
||||
this.updateInProgress = true;
|
||||
let updateMutation = updateWorkItemMutation;
|
||||
|
|
@ -341,7 +327,6 @@ export default {
|
|||
document.title = s__('404|Not found');
|
||||
},
|
||||
},
|
||||
WORK_ITEM_VIEWED_STORAGE_KEY,
|
||||
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
};
|
||||
</script>
|
||||
|
|
@ -431,16 +416,6 @@ export default {
|
|||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<local-storage-sync
|
||||
v-model="showInfoBanner"
|
||||
:storage-key="$options.WORK_ITEM_VIEWED_STORAGE_KEY"
|
||||
>
|
||||
<work-item-information
|
||||
v-if="showInfoBanner && !error"
|
||||
:show-info-banner="showInfoBanner"
|
||||
@work-item-banner-dismissed="dismissBanner"
|
||||
/>
|
||||
</local-storage-sync>
|
||||
<work-item-title
|
||||
v-if="workItem.title"
|
||||
:work-item-id="workItem.id"
|
||||
|
|
@ -485,7 +460,7 @@ export default {
|
|||
:work-item-type="workItemType"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<template v-if="workItemsMvc2Enabled">
|
||||
<template v-if="workItemsMvcEnabled">
|
||||
<work-item-milestone
|
||||
v-if="workItemMilestone"
|
||||
:work-item-id="workItem.id"
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
<script>
|
||||
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
learnTasksLinkText: s__('WorkItem|Learn about tasks.'),
|
||||
tasksInformationTitle: s__('WorkItem|Introducing tasks'),
|
||||
tasksInformationBody: s__(
|
||||
'WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}',
|
||||
),
|
||||
},
|
||||
helpPageLinks: {
|
||||
tasksDocLinkPath: helpPagePath('user/tasks'),
|
||||
},
|
||||
components: {
|
||||
GlAlert,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
},
|
||||
props: {
|
||||
showInfoBanner: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ['work-item-banner-dismissed'],
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="gl-display-block gl-mb-2">
|
||||
<gl-alert
|
||||
v-if="showInfoBanner"
|
||||
variant="tip"
|
||||
:title="$options.i18n.tasksInformationTitle"
|
||||
data-testid="work-item-information"
|
||||
class="gl-mt-3"
|
||||
@dismiss="$emit('work-item-banner-dismissed')"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.tasksInformationBody">
|
||||
<template #learnMoreLink>
|
||||
<gl-link :href="$options.helpPageLinks.tasksDocLinkPath">{{
|
||||
$options.i18n.learnTasksLinkText
|
||||
}}</gl-link>
|
||||
</template>
|
||||
></gl-sprintf
|
||||
>
|
||||
</gl-alert>
|
||||
</section>
|
||||
</template>
|
||||
|
|
@ -168,7 +168,7 @@ export default {
|
|||
return this.parentMilestone?.id;
|
||||
},
|
||||
associateMilestone() {
|
||||
return this.parentMilestoneId && this.workItemsMvc2Enabled;
|
||||
return this.parentMilestoneId && this.workItemsMvcEnabled;
|
||||
},
|
||||
isSubmitButtonDisabled() {
|
||||
return this.isCreateForm ? this.search.length === 0 : this.workItemsToAdd.length === 0;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ export const WIDGET_TYPE_HIERARCHY = 'HIERARCHY';
|
|||
export const WIDGET_TYPE_MILESTONE = 'MILESTONE';
|
||||
export const WIDGET_TYPE_ITERATION = 'ITERATION';
|
||||
|
||||
export const WORK_ITEM_VIEWED_STORAGE_KEY = 'gl-show-work-item-banner';
|
||||
|
||||
export const WORK_ITEM_TYPE_ENUM_INCIDENT = 'INCIDENT';
|
||||
export const WORK_ITEM_TYPE_ENUM_ISSUE = 'ISSUE';
|
||||
export const WORK_ITEM_TYPE_ENUM_TASK = 'TASK';
|
||||
|
|
|
|||
|
|
@ -24,3 +24,9 @@
|
|||
.cluster-button-container:focus-within {
|
||||
@include gl-focus;
|
||||
}
|
||||
|
||||
.select-agent-dropdown {
|
||||
.gl-button-text {
|
||||
@include gl-flex-grow-1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ $system-note-svg-size: 1rem;
|
|||
padding: $gl-padding-4 $gl-padding-8;
|
||||
}
|
||||
|
||||
&.draft-note .timeline-content:not(.flash-container) {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.note-header-info {
|
||||
min-height: 2rem;
|
||||
display: flex;
|
||||
|
|
@ -94,6 +98,7 @@ $system-note-svg-size: 1rem;
|
|||
}
|
||||
|
||||
&.draft-note .timeline-content:not(.flash-container) {
|
||||
margin-left: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
|
@ -104,10 +109,14 @@ $system-note-svg-size: 1rem;
|
|||
border-right: 1px solid $border-color;
|
||||
background-color: $white;
|
||||
|
||||
.timeline-content {
|
||||
.timeline-content:not(.flash-container) {
|
||||
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
|
||||
}
|
||||
|
||||
.timeline-discussion-body-footer {
|
||||
padding: 0 $gl-padding-8 0;
|
||||
}
|
||||
|
||||
.timeline-avatar {
|
||||
margin: $gl-padding-8 0 0 $gl-padding;
|
||||
}
|
||||
|
|
@ -116,6 +125,12 @@ $system-note-svg-size: 1rem;
|
|||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type .timeline-entry-inner {
|
||||
border-bottom: 1px solid $border-color;
|
||||
border-bottom-left-radius: $gl-border-radius-base;
|
||||
border-bottom-right-radius: $gl-border-radius-base;
|
||||
}
|
||||
}
|
||||
|
||||
.diff-content {
|
||||
|
|
@ -1055,7 +1070,7 @@ $system-note-svg-size: 1rem;
|
|||
padding-left: 0;
|
||||
|
||||
ul.notes li.note-wrapper {
|
||||
.timeline-content {
|
||||
.timeline-content:not(.flash-container) {
|
||||
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
|
||||
}
|
||||
|
||||
|
|
@ -1071,7 +1086,7 @@ $system-note-svg-size: 1rem;
|
|||
border-right: 0;
|
||||
}
|
||||
|
||||
div.discussion-reply-holder {
|
||||
.discussion-reply-holder {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1102,7 +1117,7 @@ $system-note-svg-size: 1rem;
|
|||
}
|
||||
}
|
||||
|
||||
.draft-note-component .draft-note.timeline-entry {
|
||||
.draft-note-component.draft-note.timeline-entry {
|
||||
.timeline-content:not(.flash-container) {
|
||||
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@
|
|||
|
||||
.repository-languages-bar {
|
||||
height: 8px;
|
||||
margin-bottom: $gl-padding-8;
|
||||
margin-bottom: $gl-padding;
|
||||
background-color: $white;
|
||||
border-radius: $border-radius-default;
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ module IssuableActions
|
|||
def render_cached_discussions(discussions, serializer, cache_context)
|
||||
render_cached(discussions,
|
||||
with: serializer,
|
||||
cache_context: -> (_) { cache_context },
|
||||
cache_context: ->(_) { cache_context },
|
||||
context: self)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
|
|||
{
|
||||
date: 'date',
|
||||
group_name: 'group_name',
|
||||
param_type => -> (record) { record.data[param_type] }
|
||||
param_type => ->(record) { record.data[param_type] }
|
||||
}
|
||||
).render
|
||||
end
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
|
||||
render_cached(@merge_request,
|
||||
with: serializer,
|
||||
cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
|
||||
cache_context: ->(_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
|
||||
serializer: params[:serializer])
|
||||
else
|
||||
render json: serializer.represent(@merge_request, serializer: params[:serializer])
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ module Releases
|
|||
Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder.new(
|
||||
scope: releases_scope,
|
||||
array_scope: Project.for_group_and_its_subgroups(parent).select(:id),
|
||||
array_mapping_scope: -> (project_id_expression) { Release.where(Release.arel_table[:project_id].eq(project_id_expression)) },
|
||||
finder_query: -> (order_by, id_expression) { Release.where(Release.arel_table[:id].eq(id_expression)) }
|
||||
array_mapping_scope: ->(project_id_expression) { Release.where(Release.arel_table[:project_id].eq(project_id_expression)) },
|
||||
finder_query: ->(order_by, id_expression) { Release.where(Release.arel_table[:id].eq(id_expression)) }
|
||||
)
|
||||
.execute
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ module Mutations
|
|||
argument :associated_projects, [::Types::GlobalIDType[::Project]],
|
||||
required: false,
|
||||
description: 'Projects associated with the runner. Available only for project runners.',
|
||||
prepare: -> (global_ids, ctx) { global_ids&.filter_map { |gid| gid.model_id.to_i } }
|
||||
prepare: ->(global_ids, ctx) { global_ids&.filter_map { |gid| gid.model_id.to_i } }
|
||||
|
||||
field :runner,
|
||||
Types::Ci::RunnerType,
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def project_search_tabs?(tab)
|
||||
return false unless @project.present?
|
||||
|
||||
abilities = Array(search_tab_ability_map[tab])
|
||||
|
||||
abilities.any? { |ability| can?(current_user, ability, @project) }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module Routing
|
|||
tab
|
||||
glm_source
|
||||
glm_content
|
||||
_gl
|
||||
].freeze
|
||||
|
||||
def initialize(request_object, group, project)
|
||||
|
|
|
|||
|
|
@ -447,20 +447,38 @@ module SearchHelper
|
|||
result
|
||||
end
|
||||
|
||||
def code_tab_condition
|
||||
return true if project_search_tabs?(:blobs)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)
|
||||
end
|
||||
|
||||
def wiki_tab_condition
|
||||
return true if project_search_tabs?(:wiki)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_wiki_tab)
|
||||
end
|
||||
|
||||
def commits_tab_condition
|
||||
return true if project_search_tabs?(:commits)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)
|
||||
end
|
||||
|
||||
# search page scope navigation
|
||||
def search_navigation
|
||||
{
|
||||
projects: { sort: 1, label: _("Projects"), data: { qa_selector: 'projects_tab' }, condition: @project.nil? },
|
||||
blobs: { sort: 2, label: _("Code"), data: { qa_selector: 'code_tab' }, condition: project_search_tabs?(:blobs) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)) },
|
||||
blobs: { sort: 2, label: _("Code"), data: { qa_selector: 'code_tab' }, condition: code_tab_condition },
|
||||
# sort: 3 is reserved for EE items
|
||||
issues: { sort: 4, label: _("Issues"), condition: project_search_tabs?(:issues) || feature_flag_tab_enabled?(:global_search_issues_tab) },
|
||||
merge_requests: { sort: 5, label: _("Merge requests"), condition: project_search_tabs?(:merge_requests) || feature_flag_tab_enabled?(:global_search_merge_requests_tab) },
|
||||
wiki_blobs: { sort: 6, label: _("Wiki"), condition: project_search_tabs?(:wiki) || search_service.show_elasticsearch_tabs? },
|
||||
commits: { sort: 7, label: _("Commits"), condition: project_search_tabs?(:commits) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)) },
|
||||
wiki_blobs: { sort: 6, label: _("Wiki"), condition: wiki_tab_condition },
|
||||
commits: { sort: 7, label: _("Commits"), condition: commits_tab_condition },
|
||||
notes: { sort: 8, label: _("Comments"), condition: project_search_tabs?(:notes) || search_service.show_elasticsearch_tabs? },
|
||||
milestones: { sort: 9, label: _("Milestones"), condition: project_search_tabs?(:milestones) || @project.nil? },
|
||||
users: { sort: 10, label: _("Users"), condition: show_user_search_tab? },
|
||||
snippet_titles: { sort: 11, label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: @show_snippets.present? && @project.nil? }
|
||||
users: { sort: 10, label: _("Users"), condition: show_user_search_tab? },
|
||||
snippet_titles: { sort: 11, label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: @show_snippets.present? && @project.nil? }
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -584,7 +602,7 @@ module SearchHelper
|
|||
end
|
||||
|
||||
def feature_flag_tab_enabled?(flag)
|
||||
@group || Feature.enabled?(flag, current_user, type: :ops)
|
||||
@group.present? || Feature.enabled?(flag, current_user, type: :ops)
|
||||
end
|
||||
|
||||
def sanitized_search_params
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class AbuseReport < ApplicationRecord
|
|||
validates :message, presence: true
|
||||
validates :user_id, uniqueness: { message: 'has already been reported' }
|
||||
|
||||
scope :by_user, -> (user) { where(user_id: user) }
|
||||
scope :by_user, ->(user) { where(user_id: user) }
|
||||
scope :with_users, -> { includes(:reporter, :user) }
|
||||
|
||||
# For CacheMarkdownField
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ module AlertManagement
|
|||
validates :fingerprint, allow_blank: true, uniqueness: {
|
||||
scope: :project,
|
||||
conditions: -> { not_resolved },
|
||||
message: -> (object, data) { _('Cannot have multiple unresolved alerts') }
|
||||
message: ->(object, data) { _('Cannot have multiple unresolved alerts') }
|
||||
}, unless: :resolved?
|
||||
validate :hosts_format
|
||||
|
||||
|
|
@ -74,23 +74,23 @@ module AlertManagement
|
|||
delegate :iid, to: :issue, prefix: true, allow_nil: true
|
||||
delegate :details_url, to: :present
|
||||
|
||||
scope :for_iid, -> (iid) { where(iid: iid) }
|
||||
scope :for_fingerprint, -> (project, fingerprint) { where(project: project, fingerprint: fingerprint) }
|
||||
scope :for_environment, -> (environment) { where(environment: environment) }
|
||||
scope :for_assignee_username, -> (assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
|
||||
scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
|
||||
scope :for_iid, ->(iid) { where(iid: iid) }
|
||||
scope :for_fingerprint, ->(project, fingerprint) { where(project: project, fingerprint: fingerprint) }
|
||||
scope :for_environment, ->(environment) { where(environment: environment) }
|
||||
scope :for_assignee_username, ->(assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
|
||||
scope :search, ->(query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
|
||||
scope :not_resolved, -> { without_status(:resolved) }
|
||||
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
|
||||
scope :with_operations_alerts, -> { where(domain: :operations) }
|
||||
|
||||
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
|
||||
scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) }
|
||||
scope :order_event_count, -> (sort_order) { order(events: sort_order) }
|
||||
scope :order_start_time, ->(sort_order) { order(started_at: sort_order) }
|
||||
scope :order_end_time, ->(sort_order) { order(ended_at: sort_order) }
|
||||
scope :order_event_count, ->(sort_order) { order(events: sort_order) }
|
||||
|
||||
# Ascending sort order sorts severity from less critical to more critical.
|
||||
# Descending sort order sorts severity from more critical to less critical.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
|
||||
scope :order_severity, -> (sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
|
||||
scope :order_severity, ->(sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
|
||||
scope :order_severity_with_open_prometheus_alert, -> { open.with_prometheus_alert.order(severity: :asc, started_at: :desc) }
|
||||
|
||||
scope :counts_by_project_id, -> { group(:project_id).count }
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ module AlertManagement
|
|||
before_validation :ensure_token
|
||||
before_validation :ensure_payload_example_not_nil
|
||||
|
||||
scope :for_endpoint_identifier, -> (endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
|
||||
scope :for_endpoint_identifier, ->(endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :ordered_by_id, -> { order(:id) }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord
|
|||
|
||||
validates :incremental_runtimes_in_seconds, :incremental_processed_records, :full_runtimes_in_seconds, :full_processed_records, presence: true, length: { maximum: 10 }, allow_blank: true
|
||||
|
||||
scope :priority_order, -> (column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
|
||||
scope :priority_order, ->(column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
|
||||
scope :enabled, -> { where('enabled IS TRUE') }
|
||||
|
||||
def cursor_for(mode, model)
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ module Analytics
|
|||
validates :recorded_at, uniqueness: { scope: :identifier }
|
||||
|
||||
scope :order_by_latest, -> { order(recorded_at: :desc) }
|
||||
scope :with_identifier, -> (identifier) { where(identifier: identifier) }
|
||||
scope :recorded_after, -> (date) { where(self.model.arel_table[:recorded_at].gteq(date)) if date.present? }
|
||||
scope :recorded_before, -> (date) { where(self.model.arel_table[:recorded_at].lteq(date)) if date.present? }
|
||||
scope :with_identifier, ->(identifier) { where(identifier: identifier) }
|
||||
scope :recorded_after, ->(date) { where(self.model.arel_table[:recorded_at].gteq(date)) if date.present? }
|
||||
scope :recorded_before, ->(date) { where(self.model.arel_table[:recorded_at].lteq(date)) if date.present? }
|
||||
|
||||
def self.identifier_query_mapping
|
||||
{
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ class ApplicationSetting < ApplicationRecord
|
|||
|
||||
validates :external_auth_client_key,
|
||||
presence: true,
|
||||
if: -> (setting) { setting.external_auth_client_cert.present? }
|
||||
if: ->(setting) { setting.external_auth_client_cert.present? }
|
||||
|
||||
validates :lets_encrypt_notification_email,
|
||||
devise_email: true,
|
||||
|
|
@ -486,17 +486,17 @@ class ApplicationSetting < ApplicationRecord
|
|||
|
||||
validates :eks_access_key_id,
|
||||
length: { in: 16..128 },
|
||||
if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
|
||||
if: ->(setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
|
||||
|
||||
validates :eks_secret_access_key,
|
||||
presence: true,
|
||||
if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
|
||||
if: ->(setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
|
||||
|
||||
validates_with X509CertificateCredentialsValidator,
|
||||
certificate: :external_auth_client_cert,
|
||||
pkey: :external_auth_client_key,
|
||||
pass: :external_auth_client_key_pass,
|
||||
if: -> (setting) { setting.external_auth_client_cert.present? }
|
||||
if: ->(setting) { setting.external_auth_client_cert.present? }
|
||||
|
||||
validates :default_ci_config_path,
|
||||
format: { without: %r{(\.{2}|\A/)},
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ class AuditEvent < ApplicationRecord
|
|||
validates :entity_type, presence: true
|
||||
validates :ip_address, ip_address: true
|
||||
|
||||
scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) }
|
||||
scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) }
|
||||
scope :by_author_id, -> (author_id) { where(author_id: author_id) }
|
||||
scope :by_entity_username, -> (username) { where(entity_id: find_user_id(username)) }
|
||||
scope :by_author_username, -> (username) { where(author_id: find_user_id(username)) }
|
||||
scope :by_entity_type, ->(entity_type) { where(entity_type: entity_type) }
|
||||
scope :by_entity_id, ->(entity_id) { where(entity_id: entity_id) }
|
||||
scope :by_author_id, ->(author_id) { where(author_id: author_id) }
|
||||
scope :by_entity_username, ->(username) { where(entity_id: find_user_id(username)) }
|
||||
scope :by_author_username, ->(username) { where(author_id: find_user_id(username)) }
|
||||
|
||||
after_initialize :initialize_details
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ class AwardEmoji < ApplicationRecord
|
|||
|
||||
scope :downvotes, -> { named(DOWNVOTE_NAME) }
|
||||
scope :upvotes, -> { named(UPVOTE_NAME) }
|
||||
scope :named, -> (names) { where(name: names) }
|
||||
scope :awarded_by, -> (users) { where(user: users) }
|
||||
scope :named, ->(names) { where(name: names) }
|
||||
scope :awarded_by, ->(users) { where(user: users) }
|
||||
|
||||
after_save :expire_cache
|
||||
after_destroy :expire_cache
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class BoardGroupRecentVisit < ApplicationRecord
|
|||
validates :group, presence: true
|
||||
validates :board, presence: true
|
||||
|
||||
scope :by_user_parent, -> (user, group) { where(user: user, group: group) }
|
||||
scope :by_user_parent, ->(user, group) { where(user: user, group: group) }
|
||||
|
||||
def self.board_parent_relation
|
||||
:group
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class BoardProjectRecentVisit < ApplicationRecord
|
|||
validates :project, presence: true
|
||||
validates :board, presence: true
|
||||
|
||||
scope :by_user_parent, -> (user, project) { where(user: user, project: project) }
|
||||
scope :by_user_parent, ->(user, project) { where(user: user, project: project) }
|
||||
|
||||
def self.board_parent_relation
|
||||
:project
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class BulkImport < ApplicationRecord
|
|||
enum source_type: { gitlab: 0 }
|
||||
|
||||
scope :stale, -> { where('created_at < ?', 8.hours.ago).where(status: [0, 1]) }
|
||||
scope :order_by_created_at, -> (direction) { order(created_at: direction) }
|
||||
scope :order_by_created_at, ->(direction) { order(created_at: direction) }
|
||||
|
||||
state_machine :status, initial: :created do
|
||||
state :created, value: 0
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class BulkImports::Entity < ApplicationRecord
|
|||
scope :by_user_id, ->(user_id) { joins(:bulk_import).where(bulk_imports: { user_id: user_id }) }
|
||||
scope :stale, -> { where('created_at < ?', 8.hours.ago).where(status: [0, 1]) }
|
||||
scope :by_bulk_import_id, ->(bulk_import_id) { where(bulk_import_id: bulk_import_id) }
|
||||
scope :order_by_created_at, -> (direction) { order(created_at: direction) }
|
||||
scope :order_by_created_at, ->(direction) { order(created_at: direction) }
|
||||
|
||||
alias_attribute :destination_slug, :destination_name
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
# This model represents metadata for a running build.
|
||||
# Despite the generic RunningBuild name, in this first iteration it applies only to shared runners
|
||||
# (see Ci::RunningBuild.upsert_shared_runner_build!).
|
||||
# The decision to insert all of the running builds here was deferred to avoid the pressure on the database as
|
||||
# at this time that was not necessary.
|
||||
# We can reconsider the decision to limit this only to shared runners when there is more evidence that inserting all
|
||||
# of the running builds there is worth the additional pressure.
|
||||
class RunningBuild < Ci::ApplicationRecord
|
||||
include Ci::Partitionable
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Integration < ApplicationRecord
|
|||
|
||||
INTEGRATION_NAMES = %w[
|
||||
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
|
||||
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat harbor irker jira
|
||||
drone_ci emails_on_push ewm external_wiki hangouts_chat harbor irker jira
|
||||
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
|
||||
pivotaltracker prometheus pumble pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack zentao
|
||||
].freeze
|
||||
|
|
|
|||
|
|
@ -1,28 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This integration is scheduled for removal.
|
||||
# All records must be deleted before the class can be removed.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/379197
|
||||
module Integrations
|
||||
class Flowdock < Integration
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
field :token,
|
||||
type: 'password',
|
||||
help: -> { s_('FlowdockService|Enter your Flowdock token.') },
|
||||
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
|
||||
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
|
||||
placeholder: '1b609b52537...',
|
||||
required: true
|
||||
|
||||
def title
|
||||
'Flowdock'
|
||||
end
|
||||
|
||||
def description
|
||||
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
|
||||
end
|
||||
|
||||
def help
|
||||
docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
|
||||
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
|
||||
def readonly?
|
||||
true
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
|
|
@ -30,22 +14,7 @@ module Integrations
|
|||
end
|
||||
|
||||
def self.supported_events
|
||||
%w(push)
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
::Flowdock::Git.post(
|
||||
data[:ref],
|
||||
data[:before],
|
||||
data[:after],
|
||||
token: token,
|
||||
repo: project.repository,
|
||||
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
|
||||
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
|
||||
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
|
||||
)
|
||||
%w[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -196,7 +196,6 @@ class Project < ApplicationRecord
|
|||
has_one :emails_on_push_integration, class_name: 'Integrations::EmailsOnPush'
|
||||
has_one :ewm_integration, class_name: 'Integrations::Ewm'
|
||||
has_one :external_wiki_integration, class_name: 'Integrations::ExternalWiki'
|
||||
has_one :flowdock_integration, class_name: 'Integrations::Flowdock'
|
||||
has_one :hangouts_chat_integration, class_name: 'Integrations::HangoutsChat'
|
||||
has_one :harbor_integration, class_name: 'Integrations::Harbor'
|
||||
has_one :irker_integration, class_name: 'Integrations::Irker'
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ class BasePolicy < DeclarativePolicy::Base
|
|||
rule { default }.enable :read_cross_project
|
||||
|
||||
condition(:is_gitlab_com, score: 0, scope: :global) { ::Gitlab.com? }
|
||||
|
||||
condition(:is_bot?) { @user&.bot? }
|
||||
end
|
||||
|
||||
BasePolicy.prepend_mod_with('BasePolicy')
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ class MergeRequestPolicy < IssuablePolicy
|
|||
enable :approve_merge_request
|
||||
end
|
||||
|
||||
rule { can?(:approve_merge_request) & is_bot? }.policy do
|
||||
enable :reset_merge_request_approvals
|
||||
end
|
||||
|
||||
rule { ~anonymous & can?(:read_merge_request) }.policy do
|
||||
enable :create_todo
|
||||
enable :update_subscription
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ module Projects
|
|||
private
|
||||
|
||||
def filter_out_latest!(tags)
|
||||
return unless keep_latest
|
||||
|
||||
tags.reject!(&:latest?)
|
||||
end
|
||||
|
||||
|
|
@ -84,6 +86,10 @@ module Projects
|
|||
params['keep_n']
|
||||
end
|
||||
|
||||
def keep_latest
|
||||
params.fetch('keep_latest', true)
|
||||
end
|
||||
|
||||
def project
|
||||
container_repository.project
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ module Projects
|
|||
class DestroyService < BaseService
|
||||
CLEANUP_TAGS_SERVICE_PARAMS = {
|
||||
'name_regex_delete' => '.*',
|
||||
'container_expiration_policy' => true # to avoid permissions checks
|
||||
'container_expiration_policy' => true, # to avoid permissions checks
|
||||
'keep_latest' => false
|
||||
}.freeze
|
||||
|
||||
def execute(container_repository, disable_timeout: true)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form', id: 'terminal-settings' } do |f|
|
||||
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form', id: 'terminal-settings' } do |f|
|
||||
= form_errors(@application_setting)
|
||||
|
||||
%fieldset
|
||||
|
|
@ -7,4 +7,4 @@
|
|||
= f.number_field :terminal_max_session_time, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
= _('Maximum time, in seconds, for a web terminal websocket connection. 0 for unlimited.')
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
= f.submit _('Save changes'), pajamas_button: true
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
= c.body do
|
||||
= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
|
||||
= c.actions do
|
||||
%a.gl-button.btn-confirm.text-decoration-none{ href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', rel: 'noopener noreferrer' }
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', button_options: { rel: 'noopener noreferrer' }) do
|
||||
= s_("ClusterIntegration|Apply for credit")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,17 @@
|
|||
'wait_for_update': 500
|
||||
});
|
||||
|
||||
window.geofeed = (options) => {
|
||||
dataLayer.push({
|
||||
'event' : 'OneTrustCountryLoad',
|
||||
'oneTrustCountryId': options.country.toString()
|
||||
})
|
||||
}
|
||||
|
||||
const json = document.createElement('script');
|
||||
json.setAttribute('src', 'https://geolocation.onetrust.com/cookieconsentpub/v1/geo/location/geofeed');
|
||||
document.head.appendChild(json);
|
||||
|
||||
- if Feature.enabled?(:gtm_nonce, type: :ops)
|
||||
= javascript_tag nonce: content_security_policy_nonce do
|
||||
:plain
|
||||
|
|
|
|||
|
|
@ -18,22 +18,24 @@
|
|||
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. Don\'t worry, this information isn\'t shared outside of your self-managed GitLab instance.')) % { gitlab_experience_text: gitlab_experience_text }
|
||||
= gitlab_ui_form_for(current_user,
|
||||
url: users_sign_up_welcome_path(glm_tracking_params),
|
||||
html: { class: 'card gl-w-full! gl-p-5 js-users-signup-welcome',
|
||||
html: { class: 'gl-w-full! gl-p-5 js-users-signup-welcome',
|
||||
'aria-live' => 'assertive',
|
||||
data: { testid: 'welcome-form' } }) do |f|
|
||||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: current_user
|
||||
.row
|
||||
.form-group.col-sm-12
|
||||
= f.label :role, _('Role'), class: 'label-bold'
|
||||
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
|
||||
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
|
||||
= render_if_exists "registrations/welcome/setup_for_company", f: f
|
||||
= render_if_exists "registrations/welcome/joining_project"
|
||||
= render 'devise/shared/email_opted_in', f: f
|
||||
.row
|
||||
.form-group.col-sm-12.gl-mb-0
|
||||
- if partial_exists? "registrations/welcome/button"
|
||||
= render "registrations/welcome/button"
|
||||
- else
|
||||
= f.submit _('Get started!'), class: 'btn-confirm gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.body do
|
||||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: current_user
|
||||
.row
|
||||
.form-group.col-sm-12
|
||||
= f.label :role, _('Role'), class: 'label-bold'
|
||||
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
|
||||
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
|
||||
= render_if_exists "registrations/welcome/setup_for_company", f: f
|
||||
= render_if_exists "registrations/welcome/joining_project"
|
||||
= render 'devise/shared/email_opted_in', f: f
|
||||
.row
|
||||
.form-group.col-sm-12.gl-mb-0
|
||||
- if partial_exists? "registrations/welcome/button"
|
||||
= render "registrations/welcome/button"
|
||||
- else
|
||||
= f.submit _('Get started!'), class: 'btn-confirm gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
.js-sidebar-todo-widget-root{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
|
||||
|
||||
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
|
||||
.block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
|
||||
.block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container', testid: 'assignee-block-container' } }
|
||||
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
|
||||
|
||||
- if issuable_sidebar[:supports_severity]
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@ module ContainerRegistry
|
|||
MAX_CAPACITY = 2
|
||||
CLEANUP_TAGS_SERVICE_PARAMS = {
|
||||
'name_regex_delete' => '.*',
|
||||
'keep_latest' => false,
|
||||
'container_expiration_policy' => true # to avoid permissions checks
|
||||
}.freeze
|
||||
|
||||
def perform_work
|
||||
return unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
|
||||
return unless next_container_repository
|
||||
|
||||
result = delete_tags
|
||||
|
|
@ -38,6 +40,8 @@ module ContainerRegistry
|
|||
end
|
||||
|
||||
def remaining_work_count
|
||||
return 0 unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
|
||||
|
||||
::ContainerRepository.delete_scheduled.limit(max_running_jobs + 1).count
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ product_stage: manage
|
|||
product_group: integrations
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: active
|
||||
status: removed
|
||||
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
|
||||
milestone_removed: '15.7'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ product_stage: manage
|
|||
product_group: integrations
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: active
|
||||
status: removed
|
||||
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
|
||||
milestone_removed: '15.7'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ product_stage: manage
|
|||
product_group: integrations
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: active
|
||||
status: removed
|
||||
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
|
||||
milestone_removed: '15.7'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ product_stage: manage
|
|||
product_group: integrations
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: active
|
||||
status: removed
|
||||
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
|
||||
milestone_removed: '15.7'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ product_stage: manage
|
|||
product_group: integrations
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: active
|
||||
status: removed
|
||||
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
|
||||
milestone_removed: '15.7'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
- title: "Flowdock integration" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
|
||||
announcement_milestone: "15.7" # (required) The milestone when this feature was deprecated.
|
||||
announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
|
||||
removal_milestone: "15.7" # (required) The milestone when this feature is being removed.
|
||||
removal_date: "2022-12-22" # (required) This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed.
|
||||
breaking_change: false # (required) Change to true if this removal is a breaking change.
|
||||
reporter: arturoherrero # (required) GitLab username of the person reporting the removal
|
||||
stage: manage # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379197 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
As of December 22, 2022, we are removing the Flowdock integration because the service was shut down on August 15, 2022.
|
||||
#
|
||||
# OPTIONAL FIELDS
|
||||
#
|
||||
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
documentation_url: # (optional) This is a link to the current documentation page
|
||||
image_url: # (optional) This is a link to a thumbnail image depicting the feature
|
||||
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
||||
|
|
@ -4,7 +4,7 @@ classes:
|
|||
- Ci::PendingBuild
|
||||
feature_categories:
|
||||
- continuous_integration
|
||||
description: TODO
|
||||
description: Pending builds metadata
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61581
|
||||
milestone: '14.0'
|
||||
gitlab_schema: gitlab_ci
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ classes:
|
|||
- Ci::RunningBuild
|
||||
feature_categories:
|
||||
- continuous_integration
|
||||
description: TODO
|
||||
description: >
|
||||
Running builds metadata.
|
||||
Despite the generic `RunningBuild` name, in this first iteration it applies only to shared runners.
|
||||
The decision to insert all of the running builds here was deferred to avoid the pressure on the database as
|
||||
at this time that was not necessary.
|
||||
We can reconsider the decision to limit this only to shared runners when there is more evidence that inserting all
|
||||
of the running builds there is worth the additional pressure.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62912
|
||||
milestone: '14.0'
|
||||
gitlab_schema: gitlab_ci
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ classes:
|
|||
- Integrations::EmailsOnPush
|
||||
- Integrations::Ewm
|
||||
- Integrations::ExternalWiki
|
||||
- Integrations::Flowdock
|
||||
- Integrations::Github
|
||||
- Integrations::GitlabSlackApplication
|
||||
- Integrations::HangoutsChat
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddLastSeatRefreshAtToGitlabSubscriptions < Gitlab::Database::Migration[2.0]
|
||||
enable_lock_retries!
|
||||
|
||||
TABLE_NAME = 'gitlab_subscriptions'
|
||||
COLUMN_NAME = 'last_seat_refresh_at'
|
||||
|
||||
def up
|
||||
add_column(TABLE_NAME, COLUMN_NAME, :datetime_with_timezone)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column(TABLE_NAME, COLUMN_NAME)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
1621f0ac141f24c15beef34f5f411158c1eb8a89f5022dd426533d705aa859fe
|
||||
|
|
@ -15982,6 +15982,7 @@ CREATE TABLE gitlab_subscriptions (
|
|||
seats_owed integer DEFAULT 0 NOT NULL,
|
||||
trial_extension_type smallint,
|
||||
max_seats_used_changed_at timestamp with time zone,
|
||||
last_seat_refresh_at timestamp with time zone,
|
||||
CONSTRAINT check_77fea3f0e7 CHECK ((namespace_id IS NOT NULL))
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -348,7 +348,6 @@ Flamegraph
|
|||
flamegraphs
|
||||
Flawfinder
|
||||
Flickr
|
||||
Flowdock
|
||||
Fluentd
|
||||
Flutterwave
|
||||
Flycheck
|
||||
|
|
|
|||
|
|
@ -16885,7 +16885,7 @@ Represents vulnerability finding of a security report on the pipeline.
|
|||
| <a id="pipelinesecurityreportfindingsolution"></a>`solution` | [`String`](#string) | URL to the vulnerability's details page. |
|
||||
| <a id="pipelinesecurityreportfindingstate"></a>`state` | [`VulnerabilityState`](#vulnerabilitystate) | Finding status. |
|
||||
| <a id="pipelinesecurityreportfindingtitle"></a>`title` | [`String`](#string) | Title of the vulnerability finding. |
|
||||
| <a id="pipelinesecurityreportfindinguuid"></a>`uuid` | [`String`](#string) | Name of the vulnerability finding. |
|
||||
| <a id="pipelinesecurityreportfindinguuid"></a>`uuid` | [`String`](#string) | UUIDv5 digest based on the vulnerability's report type, primary identifier, location, fingerprint, project identifier. |
|
||||
|
||||
### `PreviewBillableUserChange`
|
||||
|
||||
|
|
@ -22509,7 +22509,6 @@ State of a Sentry error.
|
|||
| <a id="servicetypeemails_on_push_service"></a>`EMAILS_ON_PUSH_SERVICE` | EmailsOnPushService type. |
|
||||
| <a id="servicetypeewm_service"></a>`EWM_SERVICE` | EwmService type. |
|
||||
| <a id="servicetypeexternal_wiki_service"></a>`EXTERNAL_WIKI_SERVICE` | ExternalWikiService type. |
|
||||
| <a id="servicetypeflowdock_service"></a>`FLOWDOCK_SERVICE` | FlowdockService type. |
|
||||
| <a id="servicetypegithub_service"></a>`GITHUB_SERVICE` | GithubService type. |
|
||||
| <a id="servicetypegitlab_slack_application_service"></a>`GITLAB_SLACK_APPLICATION_SERVICE` | GitlabSlackApplicationService type (Gitlab.com only). |
|
||||
| <a id="servicetypehangouts_chat_service"></a>`HANGOUTS_CHAT_SERVICE` | HangoutsChatService type. |
|
||||
|
|
|
|||
|
|
@ -755,42 +755,6 @@ Get External wiki integration settings for a project.
|
|||
GET /projects/:id/integrations/external-wiki
|
||||
```
|
||||
|
||||
## Flowdock
|
||||
|
||||
Flowdock is a ChatOps application for collaboration in software engineering
|
||||
companies. You can send notifications from GitLab events to Flowdock flows.
|
||||
For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
|
||||
|
||||
### Create/Edit Flowdock integration
|
||||
|
||||
Set Flowdock integration for a project.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/integrations/flowdock
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `token` | string | true | Flowdock Git source token |
|
||||
|
||||
### Disable Flowdock integration
|
||||
|
||||
Disable the Flowdock integration for a project. Integration settings are preserved.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/integrations/flowdock
|
||||
```
|
||||
|
||||
### Get Flowdock integration settings
|
||||
|
||||
Get Flowdock integration settings for a project.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/integrations/flowdock
|
||||
```
|
||||
|
||||
## GitHub **(PREMIUM)**
|
||||
|
||||
Code collaboration software.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ All Work Item types share the same pool of predefined widgets and are customized
|
|||
| description | |
|
||||
| hierarchy | |
|
||||
| [iteration](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) | |
|
||||
| [milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) | work_items_mvc_2 |
|
||||
| [milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) | work_items_mvc |
|
||||
| labels | |
|
||||
| start and due date | |
|
||||
| status\* | |
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ You can integrate GitLab with external services for enhanced functionality.
|
|||
|
||||
## Services
|
||||
|
||||
Services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack
|
||||
Services such as Campfire, Jira, Pivotal Tracker, and Slack
|
||||
are available as [integrations](../user/project/integrations/index.md).
|
||||
|
||||
## Issue trackers
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ If you want to preserve this functionality, you can follow one of these two path
|
|||
1. Fork the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) into the `chart/` path within your project
|
||||
1. Set `AUTO_DEPLOY_IMAGE_VERSION` and `DAST_AUTO_DEPLOY_IMAGE_VERSION` to the most recent version of the image that included the CiliumNetworkPolicy
|
||||
|
||||
## Removed in 15.7
|
||||
|
||||
### Flowdock integration
|
||||
|
||||
As of December 22, 2022, we are removing the Flowdock integration because the service was shut down on August 15, 2022.
|
||||
|
||||
## Removed in 15.6
|
||||
|
||||
### NFS as Git repository storage is no longer supported. Migrate to Gitaly Cluster as soon as possible
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ You can configure the following integrations.
|
|||
| [Emails on push](emails_on_push.md) | Send commits and diff of each push by email. | **{dotted-circle}** No |
|
||||
| [EWM](ewm.md) | Use IBM Engineering Workflow Management as the issue tracker. | **{dotted-circle}** No |
|
||||
| [External wiki](../wiki/index.md#link-an-external-wiki) | Link an external wiki. | **{dotted-circle}** No |
|
||||
| [Flowdock](../../../api/integrations.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No |
|
||||
| [GitHub](github.md) | Obtain statuses for commits and pull requests. | **{dotted-circle}** No |
|
||||
| [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat. | **{dotted-circle}** No |
|
||||
| [Harbor](harbor.md) | Use Harbor as the container registry. | **{dotted-circle}** No |
|
||||
|
|
|
|||
|
|
@ -207,10 +207,12 @@ To set a start date:
|
|||
|
||||
## Add a task to a milestone
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) to feature flag named `work_items_mvc` in GitLab 15.7. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc_2`. On GitLab.com, this feature is not available. The feature is not ready for production use.
|
||||
On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc`.
|
||||
On GitLab.com, this feature is not available. The feature is not ready for production use.
|
||||
|
||||
You can add a task to a [milestone](project/milestones/index.md).
|
||||
You can see the milestone title when you view a task.
|
||||
|
|
|
|||
|
|
@ -415,14 +415,6 @@ module API
|
|||
desc: 'The URL of the external wiki'
|
||||
}
|
||||
],
|
||||
'flowdock' => [
|
||||
{
|
||||
required: true,
|
||||
name: :token,
|
||||
type: String,
|
||||
desc: 'Flowdock token'
|
||||
}
|
||||
],
|
||||
'hangouts-chat' => [
|
||||
{
|
||||
required: true,
|
||||
|
|
@ -893,7 +885,6 @@ module API
|
|||
::Integrations::EmailsOnPush,
|
||||
::Integrations::Ewm,
|
||||
::Integrations::ExternalWiki,
|
||||
::Integrations::Flowdock,
|
||||
::Integrations::HangoutsChat,
|
||||
::Integrations::Harbor,
|
||||
::Integrations::Irker,
|
||||
|
|
|
|||
|
|
@ -88,6 +88,23 @@ module API
|
|||
|
||||
present_approval(merge_request)
|
||||
end
|
||||
desc 'Remove all merge request approvals' do
|
||||
detail 'Clear all approvals of merge request. This feature was added in GitLab 15.4'
|
||||
failure [
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
tags %w[merge_requests]
|
||||
end
|
||||
put 'reset_approvals', urgency: :low do
|
||||
merge_request = find_project_merge_request(params[:merge_request_iid])
|
||||
|
||||
unauthorized! unless current_user.can?(:reset_merge_request_approvals, merge_request)
|
||||
|
||||
merge_request.approvals.delete_all
|
||||
|
||||
status :accepted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -733,25 +733,6 @@ module API
|
|||
rescue ::MergeRequest::RebaseLockTimeout => e
|
||||
render_api_error!(e.message, 409)
|
||||
end
|
||||
|
||||
desc 'Reset approvals of a merge request' do
|
||||
detail 'Clear all approvals of merge request. This feature was added in GitLab 15.4'
|
||||
failure [
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
tags %w[merge_requests]
|
||||
end
|
||||
put ':id/merge_requests/:merge_request_iid/reset_approvals', feature_category: :code_review, urgency: :low do
|
||||
merge_request = find_project_merge_request(params[:merge_request_iid])
|
||||
|
||||
unauthorized! unless current_user.bot? && merge_request.eligible_for_approval_by?(current_user)
|
||||
|
||||
merge_request.approvals.delete_all
|
||||
|
||||
status :accepted
|
||||
end
|
||||
|
||||
desc 'List issues that close on merge' do
|
||||
detail 'Get all the issues that would be closed by merging the provided merge request.'
|
||||
success Entities::MRNote
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
require 'flowdock'
|
||||
require 'flowdock/git/builder'
|
||||
|
||||
module Flowdock
|
||||
class Git
|
||||
TokenError = Class.new(StandardError)
|
||||
|
||||
DEFAULT_PERMANENT_REFS = [
|
||||
Regexp.new('refs/heads/master')
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
def post(ref, from, to, options = {})
|
||||
Git.new(ref, from, to, options).post
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(ref, from, to, options = {})
|
||||
raise TokenError, "Flowdock API token not found" unless options[:token]
|
||||
|
||||
@ref = ref
|
||||
@from = from
|
||||
@to = to
|
||||
@options = options
|
||||
@token = options[:token]
|
||||
@commit_url = options[:commit_url]
|
||||
@diff_url = options[:diff_url]
|
||||
@repo_url = options[:repo_url]
|
||||
@repo_name = options[:repo_name]
|
||||
@permanent_refs = options.fetch(:permanent_refs, DEFAULT_PERMANENT_REFS)
|
||||
end
|
||||
|
||||
# Send git push notification to Flowdock
|
||||
def post
|
||||
messages.each do |message|
|
||||
::Flowdock::Client.new(flow_token: @token).post_to_thread(message)
|
||||
end
|
||||
end
|
||||
|
||||
def repo
|
||||
@options[:repo]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def messages
|
||||
Git::Builder.new(repo: repo,
|
||||
ref: @ref,
|
||||
before: @from,
|
||||
after: @to,
|
||||
commit_url: @commit_url,
|
||||
branch_url: @branch_url,
|
||||
diff_url: @diff_url,
|
||||
repo_url: @repo_url,
|
||||
repo_name: @repo_name,
|
||||
permanent_refs: @permanent_refs,
|
||||
tags: tags
|
||||
).to_hashes
|
||||
end
|
||||
|
||||
# Flowdock tags attached to the push notification
|
||||
def tags
|
||||
Array(@options[:tags]).map { |tag| CGI.escape(tag) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
module Flowdock
|
||||
class Git
|
||||
class Commit
|
||||
def initialize(external_thread_id, thread, tags, commit)
|
||||
@commit = commit
|
||||
@external_thread_id = external_thread_id
|
||||
@thread = thread
|
||||
@tags = tags
|
||||
end
|
||||
|
||||
def to_hash
|
||||
hash = {
|
||||
external_thread_id: @external_thread_id,
|
||||
event: "activity",
|
||||
author: {
|
||||
name: @commit[:author][:name],
|
||||
email: @commit[:author][:email]
|
||||
},
|
||||
title: title,
|
||||
thread: @thread,
|
||||
body: body
|
||||
}
|
||||
hash[:tags] = @tags if @tags
|
||||
encode(hash)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def encode(hash)
|
||||
return hash unless "".respond_to?(:encode)
|
||||
|
||||
encode_as_utf8(hash)
|
||||
end
|
||||
|
||||
# This only works on Ruby 1.9
|
||||
def encode_as_utf8(obj)
|
||||
if obj.is_a? Hash
|
||||
obj.each_pair do |key, val|
|
||||
encode_as_utf8(val)
|
||||
end
|
||||
elsif obj.is_a?(Array)
|
||||
obj.each do |val|
|
||||
encode_as_utf8(val)
|
||||
end
|
||||
elsif obj.is_a?(String) && obj.encoding != Encoding::UTF_8
|
||||
unless obj.force_encoding("UTF-8").valid_encoding?
|
||||
obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def body
|
||||
content = @commit[:message][first_line.size..]
|
||||
content.strip! if content
|
||||
"<pre>#{content}</pre>" unless content.empty?
|
||||
end
|
||||
|
||||
def first_line
|
||||
@first_line ||= (@commit[:message].split("\n")[0] || @commit[:message])
|
||||
end
|
||||
|
||||
def title
|
||||
commit_id = @commit[:id][0, 7]
|
||||
if @commit[:url]
|
||||
"<a href=\"#{@commit[:url]}\">#{commit_id}</a> #{message_title}"
|
||||
else
|
||||
"#{commit_id} #{message_title}"
|
||||
end
|
||||
end
|
||||
|
||||
def message_title
|
||||
CGI.escape_html(first_line.strip)
|
||||
end
|
||||
end
|
||||
|
||||
# Class used to build Git payload
|
||||
class Builder
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(opts)
|
||||
@repo = opts[:repo]
|
||||
@ref = opts[:ref]
|
||||
@before = opts[:before]
|
||||
@after = opts[:after]
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def commits
|
||||
@repo.commits_between(@before, @after).map do |commit|
|
||||
{
|
||||
url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil,
|
||||
id: commit.sha,
|
||||
message: commit.message,
|
||||
author: {
|
||||
name: commit.author_name,
|
||||
email: commit.author_email
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def ref_name
|
||||
@ref.to_s.sub(%r{\Arefs/(heads|tags)/}, '')
|
||||
end
|
||||
|
||||
def to_hashes
|
||||
commits.map do |commit|
|
||||
Commit.new(external_thread_id, thread, @opts[:tags], commit).to_hash
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def thread
|
||||
@thread ||= {
|
||||
title: thread_title,
|
||||
external_url: @opts[:repo_url]
|
||||
}
|
||||
end
|
||||
|
||||
def permanent?
|
||||
strong_memoize(:permanent) do
|
||||
@opts[:permanent_refs].any? { |regex| regex.match(@ref) }
|
||||
end
|
||||
end
|
||||
|
||||
def thread_title
|
||||
action = "updated" if permanent?
|
||||
type = @ref =~ %r(^refs/heads/) ? "branch" : "tag"
|
||||
|
||||
[@opts[:repo_name], type, ref_name, action].compact.join(" ")
|
||||
end
|
||||
|
||||
def external_thread_id
|
||||
@external_thread_id ||=
|
||||
if permanent?
|
||||
SecureRandom.hex
|
||||
else
|
||||
@ref
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -50,5 +50,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::Ci::Build::Context::Build.prepend_mod_with('Gitlab::Ci::Build::Context::Build')
|
||||
|
|
|
|||
|
|
@ -40,15 +40,6 @@ module Gitlab
|
|||
pids.each { |pid| signal(pid, signal) }
|
||||
end
|
||||
|
||||
# Waits for the given process to complete using a separate thread.
|
||||
def self.wait_async(pid)
|
||||
Thread.new do
|
||||
Process.wait(pid)
|
||||
rescue StandardError
|
||||
nil # There is no reason to return `Errno::ECHILD` if it catches a `TypeError`
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if all the processes are alive.
|
||||
def self.all_alive?(pids)
|
||||
pids.each do |pid|
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative './daemon'
|
||||
|
||||
module Gitlab
|
||||
# Given a set of process IDs, the supervisor can monitor processes
|
||||
# for being alive and invoke a callback if some or all should go away.
|
||||
|
|
|
|||
|
|
@ -18,11 +18,16 @@ module Gitlab
|
|||
def verification_status
|
||||
strong_memoize(:verification_status) do
|
||||
next :unverified unless all_attributes_present?
|
||||
next :unverified unless valid_signature_blob? && committer
|
||||
next :unverified unless valid_signature_blob?
|
||||
next :unknown_key unless signed_by_key
|
||||
next :other_user unless committer
|
||||
next :other_user unless signed_by_key.user == committer
|
||||
|
||||
:verified
|
||||
if signed_by_user_email_verified?
|
||||
:verified
|
||||
else
|
||||
:unverified
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -55,7 +60,11 @@ module Gitlab
|
|||
def committer
|
||||
# Lookup by email because users can push verified commits that were made
|
||||
# by someone else. For example: Doing a rebase.
|
||||
strong_memoize(:committer) { User.find_by_any_email(@committer_email, confirmed: true) }
|
||||
strong_memoize(:committer) { User.find_by_any_email(@committer_email) }
|
||||
end
|
||||
|
||||
def signed_by_user_email_verified?
|
||||
signed_by_key.user.verified_emails.include?(@committer_email)
|
||||
end
|
||||
|
||||
def signature
|
||||
|
|
|
|||
|
|
@ -1279,12 +1279,18 @@ msgstr ""
|
|||
msgid "(Group Managed Account)"
|
||||
msgstr ""
|
||||
|
||||
msgid "(Limited to %{quota} pipeline minutes per month)"
|
||||
msgstr ""
|
||||
|
||||
msgid "(No changes)"
|
||||
msgstr ""
|
||||
|
||||
msgid "(UTC %{offset}) %{timezone}"
|
||||
msgstr ""
|
||||
|
||||
msgid "(Unlimited pipeline minutes)"
|
||||
msgstr ""
|
||||
|
||||
msgid "(check progress)"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9256,6 +9262,9 @@ msgstr ""
|
|||
msgid "ClusterAgents|Unknown user"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Valid access token"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17295,15 +17304,6 @@ msgstr ""
|
|||
msgid "FloC|Participate in FLoC"
|
||||
msgstr ""
|
||||
|
||||
msgid "FlowdockService|Enter your Flowdock token."
|
||||
msgstr ""
|
||||
|
||||
msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
|
||||
msgstr ""
|
||||
|
||||
msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Focus filter bar"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -27117,6 +27117,9 @@ msgstr ""
|
|||
msgid "New branch unavailable"
|
||||
msgstr ""
|
||||
|
||||
msgid "New code quality findings"
|
||||
msgstr ""
|
||||
|
||||
msgid "New confidential epic title "
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31336,6 +31339,9 @@ msgstr ""
|
|||
msgid "Proceed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Product Analytics|Onboarding view"
|
||||
msgstr ""
|
||||
|
||||
msgid "Product analytics"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -35502,6 +35508,9 @@ msgstr ""
|
|||
msgid "Runners|IP Address"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Idle"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Install a runner"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -35708,6 +35717,9 @@ msgstr ""
|
|||
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Running"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Runs untagged jobs"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -38056,6 +38068,9 @@ msgstr ""
|
|||
msgid "Shared Runners"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shared Runners:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shared projects"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -46704,9 +46719,6 @@ msgstr ""
|
|||
msgid "WorkItem|Incident"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Introducing tasks"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Issue"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -46716,9 +46728,6 @@ msgstr ""
|
|||
msgid "WorkItem|Key result"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Learn about tasks."
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Milestone"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -46836,9 +46845,6 @@ msgstr ""
|
|||
msgid "WorkItem|Undo"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|View current version"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def start_and_supervise_workers(queue_groups)
|
||||
worker_pids = SidekiqCluster.start(
|
||||
wait_threads = SidekiqCluster.start(
|
||||
queue_groups,
|
||||
env: @environment,
|
||||
directory: @rails_path,
|
||||
|
|
@ -135,6 +135,7 @@ module Gitlab
|
|||
)
|
||||
|
||||
metrics_server_pid = start_metrics_server
|
||||
worker_pids = wait_threads.map(&:pid)
|
||||
supervisor.supervise(worker_pids + Array(metrics_server_pid)) do |dead_pids|
|
||||
# If we're not in the process of shutting down the cluster,
|
||||
# and the metrics server died, restart it.
|
||||
|
|
@ -149,6 +150,13 @@ module Gitlab
|
|||
[]
|
||||
end
|
||||
end
|
||||
|
||||
exit_statuses = wait_threads.map do |thread|
|
||||
thread.join
|
||||
thread.value
|
||||
end
|
||||
|
||||
exit 1 unless exit_statuses.compact.all?(&:success?)
|
||||
end
|
||||
|
||||
def start_metrics_server
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../lib/gitlab/process_management'
|
||||
require_relative '../lib/gitlab/process_supervisor'
|
||||
|
||||
module Gitlab
|
||||
module SidekiqCluster
|
||||
|
|
@ -33,7 +34,8 @@ module Gitlab
|
|||
#
|
||||
# directory - The directory of the Rails application.
|
||||
#
|
||||
# Returns an Array containing the PIDs of the started processes.
|
||||
# Returns an Array containing the waiter threads (from Process.detach) of
|
||||
# the started processes.
|
||||
def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, timeout: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false)
|
||||
queues.map.with_index do |pair, index|
|
||||
start_sidekiq(pair, env: env,
|
||||
|
|
@ -82,9 +84,7 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
|
||||
ProcessManagement.wait_async(pid)
|
||||
|
||||
pid
|
||||
Process.detach(pid)
|
||||
end
|
||||
|
||||
def self.count_by_queue(queues)
|
||||
|
|
|
|||
|
|
@ -299,11 +299,11 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
|
|||
end
|
||||
|
||||
context 'starting the server' do
|
||||
context 'without --dryrun' do
|
||||
before do
|
||||
allow(Gitlab::SidekiqCluster).to receive(:start).and_return([])
|
||||
end
|
||||
before do
|
||||
allow(Gitlab::SidekiqCluster).to receive(:start).and_return([])
|
||||
end
|
||||
|
||||
context 'without --dryrun' do
|
||||
it 'wipes the metrics directory before starting workers' do
|
||||
expect(metrics_cleanup_service).to receive(:execute).ordered
|
||||
expect(Gitlab::SidekiqCluster).to receive(:start).ordered.and_return([])
|
||||
|
|
@ -403,9 +403,42 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
|
|||
let(:sidekiq_exporter_enabled) { true }
|
||||
let(:metrics_server_pid) { 99 }
|
||||
let(:sidekiq_worker_pids) { [2, 42] }
|
||||
let(:waiter_threads) { [instance_double('Process::Waiter'), instance_double('Process::Waiter')] }
|
||||
let(:process_status) { instance_double('Process::Status') }
|
||||
|
||||
before do
|
||||
allow(Gitlab::SidekiqCluster).to receive(:start).and_return(sidekiq_worker_pids)
|
||||
allow(Gitlab::SidekiqCluster).to receive(:start).and_return(waiter_threads)
|
||||
allow(process_status).to receive(:success?).and_return(true)
|
||||
allow(cli).to receive(:exit)
|
||||
|
||||
waiter_threads.each.with_index do |thread, i|
|
||||
allow(thread).to receive(:join)
|
||||
allow(thread).to receive(:pid).and_return(sidekiq_worker_pids[i])
|
||||
allow(thread).to receive(:value).and_return(process_status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when one of the workers has been terminated gracefully' do
|
||||
it 'stops the entire process cluster' do
|
||||
expect(MetricsServer).to receive(:start_for_sidekiq).once.and_return(metrics_server_pid)
|
||||
expect(supervisor).to receive(:supervise).and_yield([2, 99])
|
||||
expect(supervisor).to receive(:shutdown)
|
||||
expect(cli).not_to receive(:exit).with(1)
|
||||
|
||||
cli.run(%w(foo))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when one of the workers has failed' do
|
||||
it 'stops the entire process cluster and exits with a non-zero code' do
|
||||
expect(MetricsServer).to receive(:start_for_sidekiq).once.and_return(metrics_server_pid)
|
||||
expect(supervisor).to receive(:supervise).and_yield([2, 99])
|
||||
expect(supervisor).to receive(:shutdown)
|
||||
expect(process_status).to receive(:success?).and_return(false)
|
||||
expect(cli).to receive(:exit).with(1)
|
||||
|
||||
cli.run(%w(foo))
|
||||
end
|
||||
end
|
||||
|
||||
it 'stops the entire process cluster if one of the workers has been terminated' do
|
||||
|
|
|
|||
|
|
@ -2,16 +2,31 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
# Flaky spec warning: the queries in this file routinely exceed the defined GraphQL query limit of 100.
|
||||
# Until those queries are optimized, we need to disable query limit checking in order for these tests
|
||||
# to pass consistently. Note that removing the disabling code can lead to flaky failures locally and in CI.
|
||||
#
|
||||
# In addition, it seems as though the use of `let_it_be` might be causing some of the
|
||||
# flakiness, as discussed in https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md#modifiers.
|
||||
# `reload: true` has been added to all `let_it_be` statements.
|
||||
#
|
||||
# See:
|
||||
# - https://gitlab.com/gitlab-org/gitlab/-/issues/323426
|
||||
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56458#note_535900110
|
||||
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102719
|
||||
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105849
|
||||
# - https://gitlab.com/gitlab-org/gitlab/-/issues/383970
|
||||
#
|
||||
RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
||||
include DragTo
|
||||
include MobileHelpers
|
||||
include BoardHelpers
|
||||
|
||||
let_it_be(:group) { create(:group, :nested) }
|
||||
let_it_be(:project) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:board) { create(:board, project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:user2) { create(:user) }
|
||||
let_it_be(:group, reload: true) { create(:group, :nested) }
|
||||
let_it_be(:project, reload: true) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:board, reload: true) { create(:board, project: project) }
|
||||
let_it_be(:user, reload: true) { create(:user) }
|
||||
let_it_be(:user2, reload: true) { create(:user) }
|
||||
|
||||
let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') }
|
||||
let(:filter_input) { find('.gl-filtered-search-term-input') }
|
||||
|
|
@ -47,31 +62,31 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
context 'with lists' do
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:milestone, reload: true) { create(:milestone, project: project) }
|
||||
|
||||
let_it_be(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') }
|
||||
let_it_be(:development) { create(:label, project: project, name: 'Development') }
|
||||
let_it_be(:testing) { create(:label, project: project, name: 'Testing') }
|
||||
let_it_be(:bug) { create(:label, project: project, name: 'Bug') }
|
||||
let_it_be(:backlog) { create(:label, project: project, name: 'Backlog') }
|
||||
let_it_be(:closed) { create(:label, project: project, name: 'Closed') }
|
||||
let_it_be(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
|
||||
let_it_be(:a_plus) { create(:label, project: project, name: 'A+') }
|
||||
let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) }
|
||||
let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
|
||||
let_it_be(:backlog_list) { create(:backlog_list, board: board) }
|
||||
let_it_be(:planning, reload: true) { create(:label, project: project, name: 'Planning', description: 'Test') }
|
||||
let_it_be(:development, reload: true) { create(:label, project: project, name: 'Development') }
|
||||
let_it_be(:testing, reload: true) { create(:label, project: project, name: 'Testing') }
|
||||
let_it_be(:bug, reload: true) { create(:label, project: project, name: 'Bug') }
|
||||
let_it_be(:backlog, reload: true) { create(:label, project: project, name: 'Backlog') }
|
||||
let_it_be(:closed, reload: true) { create(:label, project: project, name: 'Closed') }
|
||||
let_it_be(:accepting, reload: true) { create(:label, project: project, name: 'Accepting Merge Requests') }
|
||||
let_it_be(:a_plus, reload: true) { create(:label, project: project, name: 'A+') }
|
||||
let_it_be(:list1, reload: true) { create(:list, board: board, label: planning, position: 0) }
|
||||
let_it_be(:list2, reload: true) { create(:list, board: board, label: development, position: 1) }
|
||||
let_it_be(:backlog_list, reload: true) { create(:backlog_list, board: board) }
|
||||
|
||||
let_it_be(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
|
||||
let_it_be(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
|
||||
let_it_be(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
|
||||
let_it_be(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
|
||||
let_it_be(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
|
||||
let_it_be(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
|
||||
let_it_be(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
|
||||
let_it_be(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
|
||||
let_it_be(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
|
||||
let_it_be(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
|
||||
let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
|
||||
let_it_be(:confidential_issue, reload: true) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
|
||||
let_it_be(:issue1, reload: true) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
|
||||
let_it_be(:issue2, reload: true) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
|
||||
let_it_be(:issue3, reload: true) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
|
||||
let_it_be(:issue4, reload: true) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
|
||||
let_it_be(:issue5, reload: true) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
|
||||
let_it_be(:issue6, reload: true) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
|
||||
let_it_be(:issue7, reload: true) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
|
||||
let_it_be(:issue8, reload: true) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
|
||||
let_it_be(:issue9, reload: true) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
|
||||
let_it_be(:issue10, reload: true) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
|
||||
|
||||
before do
|
||||
visit_project_board(project, board)
|
||||
|
|
@ -125,7 +140,7 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
|||
it 'infinite scrolls list' do
|
||||
create_list(:labeled_issue, 30, project: project, labels: [planning])
|
||||
|
||||
visit_project_board(project, board)
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
|
||||
page.within(find('.board:nth-child(2)')) do
|
||||
expect(page.find('.board-header')).to have_content('38')
|
||||
|
|
@ -204,31 +219,26 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
|||
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
||||
# Make sure list positions are preserved after a reload
|
||||
visit_project_board(project, board)
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
end
|
||||
|
||||
context 'without backlog and closed lists' do
|
||||
let_it_be(:board) { create(:board, project: project, hide_backlog_list: true, hide_closed_list: true) }
|
||||
let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) }
|
||||
let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
|
||||
let_it_be(:board, reload: true) { create(:board, project: project, hide_backlog_list: true, hide_closed_list: true) }
|
||||
let_it_be(:list1, reload: true) { create(:list, board: board, label: planning, position: 0) }
|
||||
let_it_be(:list2, reload: true) { create(:list, board: board, label: development, position: 1) }
|
||||
|
||||
it 'changes position of list' do
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
|
||||
drag(list_from_index: 0, list_to_index: 1, selector: '.board-header')
|
||||
|
||||
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
# Make sure list positions are preserved after a reload
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
|
||||
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
|
@ -531,7 +541,7 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
context 'as guest user' do
|
||||
let_it_be(:user_guest) { create(:user) }
|
||||
let_it_be(:user_guest, reload: true) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
|
|
@ -601,4 +611,10 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
|
|||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
def visit_project_board_path_without_query_limit(project, board)
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue