Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
15c1cc886c
commit
c3ccd2fdf1
|
|
@ -5,10 +5,7 @@ Gitlab/AvoidGitlabInstanceChecks:
|
|||
- 'app/controllers/admin/runners_controller.rb'
|
||||
- 'app/controllers/explore/groups_controller.rb'
|
||||
- 'app/controllers/jira_connect/oauth_application_ids_controller.rb'
|
||||
- 'app/helpers/application_helper.rb'
|
||||
- 'app/helpers/application_settings_helper.rb'
|
||||
- 'app/helpers/auth_helper.rb'
|
||||
- 'app/helpers/ci/pipelines_helper.rb'
|
||||
- 'app/helpers/groups_helper.rb'
|
||||
- 'app/helpers/integrations_helper.rb'
|
||||
- 'app/helpers/packages_helper.rb'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 16.5.2 (2023-11-14)
|
||||
|
||||
### Fixed (4 changes)
|
||||
|
||||
- [Fix assign security check permission checks](gitlab-org/gitlab@41d0d45a2abd8621ef55042eb0bc54343a48f2da) ([merge request](gitlab-org/gitlab!136434)) **GitLab Enterprise Edition**
|
||||
- [Create group wiki repo if absent when verifying on primary](gitlab-org/gitlab@0b490bdbda06febdda47b2e0e0cea1d9e52dd381) ([merge request](gitlab-org/gitlab!136243)) **GitLab Enterprise Edition**
|
||||
- [Fix broken issue rendering when initial ID is null](gitlab-org/gitlab@9f9fafbe202c5f5f591f689e295f62b01b8d40fc) ([merge request](gitlab-org/gitlab!136065))
|
||||
- [Backport artifacts page breadcrumb fixes](gitlab-org/gitlab@4f15baf84a8de9068fa5c2c6af47fc74d2a46df8) ([merge request](gitlab-org/gitlab!135195))
|
||||
|
||||
## 16.5.1 (2023-10-30)
|
||||
|
||||
### Fixed (1 change)
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -504,7 +504,7 @@ group :test do
|
|||
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
|
||||
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.5.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.5.2', require: false, feature_category: :tooling
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 6.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@
|
|||
{"name":"devise","version":"4.9.3","platform":"ruby","checksum":"480638d6c51b97f56da6e28d4f3e2a1b8e606681b316aa594b87c6ab94923488"},
|
||||
{"name":"devise-two-factor","version":"4.1.1","platform":"ruby","checksum":"c95f5b07533e62217aaed3c386874d94e2d472fb5f2b6598afe8600fc17a8b95"},
|
||||
{"name":"diff-lcs","version":"1.5.0","platform":"ruby","checksum":"49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67"},
|
||||
{"name":"diff_match_patch","version":"0.1.0","platform":"ruby","checksum":"b36057bfcfeaedf19dcb7b2c28c19ee625bd6ec6d0d182717d3ef22b3879c40e"},
|
||||
{"name":"diffy","version":"3.4.2","platform":"ruby","checksum":"36b42ffbe5138ddc56182107c24ad8d6b066ecfd2876829f391e3a4993d89ae1"},
|
||||
{"name":"digest-crc","version":"0.6.4","platform":"ruby","checksum":"a283a60ad466f621b130c516a664df744207ee1b0283ec821fc1223eca54eab3"},
|
||||
{"name":"discordrb-webhooks","version":"3.4.2","platform":"ruby","checksum":"cfdba8a4b28236b6ab34e37389f881a59c241aeb5be0a4447249efd4e4383c6e"},
|
||||
|
|
@ -222,7 +221,7 @@
|
|||
{"name":"gitlab-styles","version":"11.0.0","platform":"ruby","checksum":"0dd8ec066ce9955ac51d3616c6bfded30f75bb526f39ff392ece6f43d5b9406b"},
|
||||
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
|
||||
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.5.0","platform":"ruby","checksum":"7ce31d48462290f39c2c9bf8ae99b39b31e3a5eba0546bac058cdb6f7f88afd3"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.5.2","platform":"ruby","checksum":"1fe87d513f005fa2ad6c35ca2bef10a262e6ab3cca3754ff5ccf121622eb45f9"},
|
||||
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
|
||||
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
|
||||
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},
|
||||
|
|
|
|||
|
|
@ -716,7 +716,7 @@ GEM
|
|||
omniauth (>= 1.3, < 3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
gitlab_quality-test_tooling (1.5.0)
|
||||
gitlab_quality-test_tooling (1.5.2)
|
||||
activesupport (>= 6.1, < 7.2)
|
||||
amatch (~> 0.4.1)
|
||||
gitlab (~> 4.19)
|
||||
|
|
@ -1886,7 +1886,7 @@ DEPENDENCIES
|
|||
gitlab-utils!
|
||||
gitlab_chronic_duration (~> 0.12)
|
||||
gitlab_omniauth-ldap (~> 2.2.0)
|
||||
gitlab_quality-test_tooling (~> 1.5.0)
|
||||
gitlab_quality-test_tooling (~> 1.5.2)
|
||||
gon (~> 6.4.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.34.0)
|
||||
google-apis-cloudbilling_v1 (~> 0.21.0)
|
||||
|
|
|
|||
|
|
@ -112,11 +112,9 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-justify-content-space-between"
|
||||
class="gl-display-flex gl-sm-flex-direction-column gl-justify-content-space-between gl-font-sm"
|
||||
>
|
||||
<span class="gl-display-flex gl-flex-basis-two-thirds gl-font-sm">{{
|
||||
resource.description
|
||||
}}</span>
|
||||
<span class="gl-display-flex gl-flex-basis-two-thirds">{{ resource.description }}</span>
|
||||
<div class="gl-display-flex gl-justify-content-end">
|
||||
<span v-if="hasReleasedVersion">
|
||||
<gl-sprintf :message="$options.i18n.releasedMessage">
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function initDiffsApp(store = notesStore) {
|
|||
iid: dataset.iid || '',
|
||||
endpointCoverage: dataset.endpointCoverage || '',
|
||||
endpointCodequality: dataset.endpointCodequality || '',
|
||||
sastReportAvailable: dataset.endpointSast,
|
||||
sastReportAvailable: parseBoolean(dataset.sastReportAvailable),
|
||||
helpPagePath: dataset.helpPagePath,
|
||||
currentUser: JSON.parse(dataset.currentUserData) || {},
|
||||
changesEmptyStateIllustration: dataset.changesEmptyStateIllustration,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import DeleteEnvironmentModal from '../components/delete_environment_modal.vue';
|
|||
import StopEnvironmentModal from '../components/stop_environment_modal.vue';
|
||||
import environmentsMixin from '../mixins/environments_mixin';
|
||||
import EnvironmentsPaginationApiMixin from '../mixins/environments_pagination_api_mixin';
|
||||
import ConfirmRollbackModal from '../components/confirm_rollback_modal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -12,6 +13,7 @@ export default {
|
|||
GlTab,
|
||||
GlTabs,
|
||||
StopEnvironmentModal,
|
||||
ConfirmRollbackModal,
|
||||
},
|
||||
|
||||
mixins: [environmentsMixin, EnvironmentsPaginationApiMixin],
|
||||
|
|
@ -42,6 +44,7 @@ export default {
|
|||
<div :class="cssContainerClass">
|
||||
<stop-environment-modal :environment="environmentInStopModal" />
|
||||
<delete-environment-modal :environment="environmentInDeleteModal" />
|
||||
<confirm-rollback-modal :environment="environmentInRollbackModal" />
|
||||
|
||||
<h4 class="gl-font-weight-normal" data-testid="folder-name">
|
||||
{{ s__('Environments|Environments') }} /
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const STATUS_MERGED = 'merged';
|
|||
export const STATUS_OPEN = 'opened';
|
||||
export const STATUS_REOPENED = 'reopened';
|
||||
export const STATUS_LOCKED = 'locked';
|
||||
export const STATUS_EMPTY = 'empty';
|
||||
|
||||
export const TITLE_LENGTH_MAX = 255;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ export default {
|
|||
RefSelector,
|
||||
},
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
persistedDefaultBranch: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -26,6 +31,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<ref-selector
|
||||
:disabled="disabled"
|
||||
:value="persistedDefaultBranch"
|
||||
class="gl-w-full"
|
||||
:project-id="projectId"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import DefaultBranchSelector from './components/default_branch_selector.vue';
|
||||
|
||||
export default (el) => {
|
||||
|
|
@ -6,13 +7,14 @@ export default (el) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { projectId, defaultBranch } = el.dataset;
|
||||
const { projectId, defaultBranch, disabled } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(DefaultBranchSelector, {
|
||||
props: {
|
||||
disabled: parseBoolean(disabled),
|
||||
persistedDefaultBranch: defaultBranch,
|
||||
projectId,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,12 +28,17 @@ export default {
|
|||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
enabledRefTypes: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => ALL_REF_TYPES,
|
||||
validator: (val) =>
|
||||
// It has to be an arrray
|
||||
// It has to be an array
|
||||
isArray(val) &&
|
||||
// with at least one item
|
||||
val.length > 0 &&
|
||||
|
|
@ -234,6 +239,10 @@ export default {
|
|||
this.debouncedSearch();
|
||||
},
|
||||
selectRef(ref) {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setSelectedRef(ref);
|
||||
this.$emit('input', this.selectedRef);
|
||||
},
|
||||
|
|
@ -262,6 +271,7 @@ export default {
|
|||
:toggle-class="extendedToggleButtonClass"
|
||||
:toggle-text="buttonText"
|
||||
:icon="dropdownIcon"
|
||||
:disabled="disabled"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@hidden="$emit('hide')"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { STATUS_CLOSED, STATUS_MERGED } from '~/issues/constants';
|
||||
import { STATUS_CLOSED, STATUS_MERGED, STATUS_EMPTY } from '~/issues/constants';
|
||||
import StatusIcon from './extensions/status_icon.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -24,6 +24,9 @@ export default {
|
|||
isMerged() {
|
||||
return this.status === STATUS_MERGED;
|
||||
},
|
||||
isEmpty() {
|
||||
return this.status === STATUS_EMPTY;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -33,6 +36,7 @@ export default {
|
|||
<gl-icon v-if="isMerged" name="merge" :size="16" class="gl-text-blue-500" />
|
||||
<gl-icon v-else-if="isClosed" name="merge-request-close" :size="16" class="gl-text-red-500" />
|
||||
<gl-icon v-else-if="status === 'approval'" name="approval" :size="16" />
|
||||
<status-icon v-else-if="isEmpty" icon-name="neutral" :level="1" class="gl-m-0!" />
|
||||
<status-icon v-else :is-loading="isLoading" :icon-name="status" :level="1" class="gl-m-0!" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export default {
|
|||
<template>
|
||||
<div class="mr-widget-body media">
|
||||
<status-icon status="success" />
|
||||
<p class="media-body gl-m-0! gl-font-weight-bold gl-text-gray-900!">
|
||||
<p class="media-body gl-mt-1 gl-mb-0! gl-font-weight-bold gl-text-gray-900!">
|
||||
<template v-if="canMerge">
|
||||
{{ __('Ready to merge!') }}
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
<script>
|
||||
import { GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-merge-requests-md.svg?url';
|
||||
import { STATUS_EMPTY } from '~/issues/constants';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import StatusIcon from '../mr_widget_status_icon.vue';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetNothingToMerge',
|
||||
components: {
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
StatusIcon,
|
||||
},
|
||||
computed: {
|
||||
statusEmpty() {
|
||||
return STATUS_EMPTY;
|
||||
},
|
||||
},
|
||||
ciHelpPage: helpPagePath('ci/quick_start/index.html'),
|
||||
EMPTY_STATE_SVG_URL,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mr-widget-body mr-widget-empty-state">
|
||||
<div class="row">
|
||||
<div
|
||||
class="col-md-3 col-12 text-center d-flex justify-content-center align-items-center svg-content svg-130 pb-0 pt-0"
|
||||
>
|
||||
<img :src="$options.EMPTY_STATE_SVG_URL" :alt="''" />
|
||||
</div>
|
||||
<div class="text col-md-9 col-12">
|
||||
<p class="highlight mt-3">
|
||||
{{ s__('mrWidgetNothingToMerge|Merge request contains no changes') }}
|
||||
</p>
|
||||
<p data-testid="nothing-to-merge-body">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'mrWidgetNothingToMerge|Use merge requests to propose changes to your project and discuss them with your team. To make changes, use the %{boldStart}Code%{boldEnd} dropdown list above, then test them with %{linkStart}CI/CD%{linkEnd} before merging.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #bold="{ content }">
|
||||
<b>{{ content }}</b>
|
||||
</template>
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="$options.ciHelpPage" target="_blank">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mr-widget-body media">
|
||||
<status-icon :status="statusEmpty" />
|
||||
<div>
|
||||
<p class="media-body gl-mt-1 gl-mb-1 gl-font-weight-bold gl-text-gray-900!">
|
||||
{{ s__('mrWidgetNothingToMerge|Merge request contains no changes') }}
|
||||
</p>
|
||||
<p class="gl-m-0! gl-text-secondary" data-testid="nothing-to-merge-body">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'mrWidgetNothingToMerge|Use merge requests to propose changes to your project and discuss them with your team. To make changes, use the %{boldStart}Code%{boldEnd} dropdown list above, then test them with %{linkStart}CI/CD%{linkEnd} before merging.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #bold="{ content }">
|
||||
<b>{{ content }}</b>
|
||||
</template>
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="$options.ciHelpPage" target="_blank">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ module Emails
|
|||
include ::ServiceDesk::CustomEmails::Logger
|
||||
|
||||
EMAIL_ATTACHMENTS_SIZE_LIMIT = 10.megabytes.freeze
|
||||
VERIFICATION_EMAIL_TIMEOUT = 7
|
||||
|
||||
included do
|
||||
layout 'service_desk', only: [:service_desk_thank_you_email, :service_desk_new_note_email]
|
||||
|
|
@ -146,7 +147,15 @@ module Emails
|
|||
|
||||
log_info(project: @project)
|
||||
|
||||
mail.delivery_method(::Mail::SMTP, @service_desk_setting.custom_email_credential.delivery_options)
|
||||
delivery_options = @service_desk_setting.custom_email_credential.delivery_options
|
||||
# We force the use of custom email settings when sending out the verification email.
|
||||
# If the credentials aren't correct some servers tend to take a while to answer
|
||||
# which leads to some Net::ReadTimeout errors which disguises the
|
||||
# real configuration issue.
|
||||
# We increase the timeout for verification emails only.
|
||||
delivery_options[:read_timeout] = VERIFICATION_EMAIL_TIMEOUT if force
|
||||
|
||||
mail.delivery_method(::Mail::SMTP, delivery_options)
|
||||
end
|
||||
|
||||
def service_desk_custom_email_enabled?
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ class ProjectSetting < ApplicationRecord
|
|||
|
||||
scope :for_projects, ->(projects) { where(project_id: projects) }
|
||||
|
||||
ignore_column :jitsu_key, remove_with: '16.7', remove_after: '2023-11-17'
|
||||
|
||||
attr_encrypted :cube_api_key,
|
||||
mode: :per_attribute_iv,
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ class Vulnerability < ApplicationRecord
|
|||
include EachBatch
|
||||
include IgnorableColumns
|
||||
|
||||
ignore_column :milestone_id, remove_with: '16.9', remove_after: '2023-01-13'
|
||||
|
||||
alias_attribute :vulnerability_id, :id
|
||||
|
||||
scope :with_projects, -> { includes(:project) }
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ module BulkImports
|
|||
|
||||
entity.start!
|
||||
|
||||
BulkImports::ExportRequestWorker.perform_async(entity.id)
|
||||
Gitlab::ApplicationContext.with_context(bulk_import_entity_id: entity.id) do
|
||||
BulkImports::ExportRequestWorker.perform_async(entity.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
- change_default_disabled = @default_branch_blocked_by_security_policy
|
||||
- popover_data = {}
|
||||
|
||||
- if change_default_disabled
|
||||
- tag_pair_security_policies_page = tag_pair(link_to('', namespace_project_security_policies_path, target: '_blank', rel: 'noopener noreferrer'), :security_policies_link_start, :security_policies_link_end)
|
||||
- tag_pair_security_policies_docs = tag_pair(link_to('', help_page_path('user/application_security/policies/scan-result-policies'), target: '_blank', rel: 'noopener noreferrer'), :learn_more_link_start, :learn_more_link_end)
|
||||
- popover_content = safe_format(s_("SecurityOrchestration|You can't change the default branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."), tag_pair_security_policies_docs, tag_pair_security_policies_page)
|
||||
- popover_title = s_("SecurityOrchestration|Security policy overwrites this setting")
|
||||
- popover_data = { container: 'body', toggle: 'popover', html: 'true', triggers: 'hover', title: popover_title, content: popover_content }
|
||||
|
||||
%fieldset#default-branch-settings
|
||||
- if @project.empty_repo?
|
||||
.text-secondary
|
||||
|
|
@ -6,8 +16,8 @@
|
|||
.form-group
|
||||
= f.label :default_branch, _("Default branch"), class: 'label-bold'
|
||||
%p= s_('ProjectSettings|All merge requests and commits are made against this branch unless you specify a different one.')
|
||||
.gl-form-input-xl
|
||||
.js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id } }
|
||||
.gl-form-input-xl{ data: { **popover_data } }
|
||||
.js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id, disabled: change_default_disabled.to_s } }
|
||||
|
||||
.form-group
|
||||
- help_text = _("When merge requests and commits in the default branch close, any issues they reference also close.")
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ module BulkImports
|
|||
attr_reader :entity
|
||||
|
||||
def re_enqueue
|
||||
BulkImports::EntityWorker.perform_in(PERFORM_DELAY, entity.id)
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
BulkImports::EntityWorker.perform_in(PERFORM_DELAY, entity.id)
|
||||
end
|
||||
end
|
||||
|
||||
def running_tracker
|
||||
|
|
@ -66,11 +68,13 @@ module BulkImports
|
|||
next_pipeline_trackers.each_with_index do |pipeline_tracker, index|
|
||||
log_info(message: 'Stage starting', entity_stage: pipeline_tracker.stage) if index == 0
|
||||
|
||||
BulkImports::PipelineWorker.perform_async(
|
||||
pipeline_tracker.id,
|
||||
pipeline_tracker.stage,
|
||||
entity.id
|
||||
)
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
BulkImports::PipelineWorker.perform_async(
|
||||
pipeline_tracker.id,
|
||||
pipeline_tracker.stage,
|
||||
entity.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ module BulkImports
|
|||
set_source_xid
|
||||
request_export
|
||||
|
||||
BulkImports::EntityWorker.perform_async(entity_id)
|
||||
with_context(bulk_import_entity_id: entity_id) do
|
||||
BulkImports::EntityWorker.perform_async(entity_id)
|
||||
end
|
||||
end
|
||||
|
||||
def perform_failure(exception, entity_id)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ module BulkImports
|
|||
attr_reader :tracker
|
||||
|
||||
def re_enqueue
|
||||
self.class.perform_in(REQUEUE_DELAY, tracker.id)
|
||||
with_context(bulk_import_entity_id: tracker.entity.id) do
|
||||
self.class.perform_in(REQUEUE_DELAY, tracker.id)
|
||||
end
|
||||
end
|
||||
|
||||
def import_in_progress?
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ module BulkImports
|
|||
@batch = ::BulkImports::BatchTracker.find(batch_id)
|
||||
|
||||
@tracker = @batch.tracker
|
||||
@entity = @tracker.entity
|
||||
@pending_retry = false
|
||||
|
||||
return unless process_batch?
|
||||
|
|
@ -50,7 +51,11 @@ module BulkImports
|
|||
|
||||
try_obtain_lease { run }
|
||||
ensure
|
||||
::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id) unless pending_retry
|
||||
unless pending_retry
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def perform_failure(batch_id, exception)
|
||||
|
|
@ -62,7 +67,7 @@ module BulkImports
|
|||
|
||||
private
|
||||
|
||||
attr_reader :batch, :tracker, :pending_retry
|
||||
attr_reader :batch, :tracker, :pending_retry, :entity
|
||||
|
||||
def run
|
||||
return batch.skip! if tracker.failed? || tracker.finished?
|
||||
|
|
@ -83,7 +88,7 @@ module BulkImports
|
|||
Gitlab::ErrorTracking.track_exception(exception, log_attributes(message: 'Batch tracker failed'))
|
||||
|
||||
BulkImports::Failure.create(
|
||||
bulk_import_entity_id: batch.tracker.entity.id,
|
||||
bulk_import_entity_id: tracker.entity.id,
|
||||
pipeline_class: tracker.pipeline_name,
|
||||
pipeline_step: 'pipeline_batch_worker_run',
|
||||
exception_class: exception.class.to_s,
|
||||
|
|
@ -91,7 +96,9 @@ module BulkImports
|
|||
correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id
|
||||
)
|
||||
|
||||
::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
|
||||
with_context(bulk_import_entity_id: tracker.entity.id) do
|
||||
::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
|
||||
end
|
||||
end
|
||||
|
||||
def context
|
||||
|
|
@ -115,7 +122,9 @@ module BulkImports
|
|||
def re_enqueue(delay = FILE_EXTRACTION_PIPELINE_PERFORM_DELAY)
|
||||
log_extra_metadata_on_done(:re_enqueue, true)
|
||||
|
||||
self.class.perform_in(delay, batch.id)
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
self.class.perform_in(delay, batch.id)
|
||||
end
|
||||
end
|
||||
|
||||
def process_batch?
|
||||
|
|
|
|||
|
|
@ -124,12 +124,14 @@ module BulkImports
|
|||
def re_enqueue(delay = FILE_EXTRACTION_PIPELINE_PERFORM_DELAY)
|
||||
log_extra_metadata_on_done(:re_enqueue, true)
|
||||
|
||||
self.class.perform_in(
|
||||
delay,
|
||||
pipeline_tracker.id,
|
||||
pipeline_tracker.stage,
|
||||
entity.id
|
||||
)
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
self.class.perform_in(
|
||||
delay,
|
||||
pipeline_tracker.id,
|
||||
pipeline_tracker.stage,
|
||||
entity.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def context
|
||||
|
|
@ -218,7 +220,9 @@ module BulkImports
|
|||
1.upto(export_status.batches_count) do |batch_number|
|
||||
batch = pipeline_tracker.batches.find_or_create_by!(batch_number: batch_number) # rubocop:disable CodeReuse/ActiveRecord
|
||||
|
||||
::BulkImports::PipelineBatchWorker.perform_async(batch.id)
|
||||
with_context(bulk_import_entity_id: entity.id) do
|
||||
::BulkImports::PipelineBatchWorker.perform_async(batch.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module ClickHouse
|
|||
class EventsSyncWorker
|
||||
include ApplicationWorker
|
||||
include Gitlab::ExclusiveLeaseHelpers
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
idempotent!
|
||||
queue_namespace :cronjob
|
||||
|
|
@ -91,6 +92,11 @@ module ClickHouse
|
|||
)
|
||||
end
|
||||
|
||||
def last_event_id_in_postgresql
|
||||
Event.maximum(:id)
|
||||
end
|
||||
strong_memoize_attr :last_event_id_in_postgresql
|
||||
|
||||
def enabled?
|
||||
ClickHouse::Client.configuration.databases[:main].present? && Feature.enabled?(:event_sync_worker_for_click_house)
|
||||
end
|
||||
|
|
@ -110,24 +116,34 @@ module ClickHouse
|
|||
|
||||
def process_batch(context)
|
||||
Enumerator.new do |yielder|
|
||||
has_data = false
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
Event.where(Event.arel_table[:id].gt(context.last_record_id)).each_batch(of: BATCH_SIZE) do |relation|
|
||||
has_data = true
|
||||
|
||||
relation.select(*EVENT_PROJECTIONS).each do |row|
|
||||
has_more_data = false
|
||||
batching_scope.each_batch(of: BATCH_SIZE) do |relation|
|
||||
records = relation.select(*EVENT_PROJECTIONS).to_a
|
||||
has_more_data = records.size == BATCH_SIZE
|
||||
records.each do |row|
|
||||
yielder << row
|
||||
context.last_processed_id = row.id
|
||||
|
||||
break if context.record_limit_reached?
|
||||
end
|
||||
|
||||
break if context.over_time? || context.record_limit_reached?
|
||||
break if context.over_time? || context.record_limit_reached? || !has_more_data
|
||||
end
|
||||
|
||||
context.no_more_records! if has_data == false
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
context.no_more_records! unless has_more_data
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def batching_scope
|
||||
return Event.none unless last_event_id_in_postgresql
|
||||
|
||||
table = Event.arel_table
|
||||
|
||||
Event
|
||||
.where(table[:id].gt(context.last_record_id))
|
||||
.where(table[:id].lteq(last_event_id_in_postgresql))
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: skip_code_generation_instruction_extraction
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136343
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431246
|
||||
milestone: '16.7'
|
||||
type: development
|
||||
group: group::code creation
|
||||
default_enabled: false
|
||||
|
|
@ -9,6 +9,10 @@ value_type: boolean
|
|||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabConfigMetric
|
||||
options:
|
||||
config:
|
||||
registry: enabled
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ value_type: boolean
|
|||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabConfigMetric
|
||||
options:
|
||||
config:
|
||||
dependency_proxy: enabled
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ value_type: boolean
|
|||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabConfigMetric
|
||||
options:
|
||||
config:
|
||||
gitlab_ci: shared_runners_enabled
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ value_type: boolean
|
|||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabConfigMetric
|
||||
options:
|
||||
config:
|
||||
ldap: enabled
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ value_type: boolean
|
|||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabConfigMetric
|
||||
options:
|
||||
config:
|
||||
mattermost: enabled
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ require_relative '../../tooling/danger/outdated_todo'
|
|||
module Danger
|
||||
class Todos < ::Danger::Plugin
|
||||
def check_outdated_todos(filenames)
|
||||
Tooling::Danger::OutdatedTodo.new(filenames, context: self).check
|
||||
Tooling::Danger::OutdatedTodo.new(filenames, context: self, allow_fail: from_lefthook?).check
|
||||
end
|
||||
|
||||
def from_lefthook?
|
||||
%w[1 true].include?(ENV['FROM_LEFTHOOK'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPreReceiveSecretDetectionEnabledToApplicationSettings < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.7'
|
||||
|
||||
def change
|
||||
add_column :application_settings, :pre_receive_secret_detection_enabled, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupCiPipelineVariablesPipelineIdBigint < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE = :ci_pipeline_variables
|
||||
REFERENCING_TABLE = :ci_pipelines
|
||||
COLUMNS = [:pipeline_id]
|
||||
INDEX_NAME = :index_ci_pipeline_variables_on_pipeline_id_bigint_and_key
|
||||
FK_NAME = :temp_fk_rails_8d3b04e3e1
|
||||
|
||||
def up
|
||||
with_lock_retries(raise_on_exhaustion: true) do
|
||||
lock_tables(:ci_pipelines, TABLE)
|
||||
cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
restore_conversion_of_integer_to_bigint(TABLE, COLUMNS)
|
||||
|
||||
add_concurrent_index(
|
||||
TABLE, [:pipeline_id_convert_to_bigint, :key],
|
||||
name: INDEX_NAME, unique: true
|
||||
)
|
||||
add_concurrent_foreign_key(
|
||||
TABLE, REFERENCING_TABLE,
|
||||
column: :pipeline_id_convert_to_bigint, name: FK_NAME,
|
||||
on_delete: :cascade, validate: true, reverse_lock_order: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropIndexNamespacesOnRequireTwoFactorAuthentication < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.7'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :namespaces
|
||||
INDEX_NAME = :index_namespaces_on_require_two_factor_authentication
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index TABLE_NAME, :require_two_factor_authentication,
|
||||
name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
842fcd7c485ec4757810444172f2a0b7ec69ea0eda14662e0418ee3befdcaadc
|
||||
|
|
@ -0,0 +1 @@
|
|||
057503cc1306afe9dea3a3d01a2fd8eeb240c33d292a6e3f2bd8ba52b38cfa62
|
||||
|
|
@ -0,0 +1 @@
|
|||
ade28e4300509e182d6ab751b4412382c8e00803b102fc4f25bb0a0d049bcc30
|
||||
|
|
@ -565,15 +565,6 @@ BEGIN
|
|||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION trigger_7f3d66a7d7f5() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW."pipeline_id_convert_to_bigint" := NEW."pipeline_id";
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION trigger_b2d852e1e2cb() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
|
|
@ -12122,6 +12113,7 @@ CREATE TABLE application_settings (
|
|||
enable_artifact_external_redirect_warning_page boolean DEFAULT true NOT NULL,
|
||||
allow_project_creation_for_guest_and_below boolean DEFAULT true NOT NULL,
|
||||
update_namespace_name_rate_limit smallint DEFAULT 120 NOT NULL,
|
||||
pre_receive_secret_detection_enabled boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
|
|
@ -14242,7 +14234,6 @@ CREATE TABLE ci_pipeline_variables (
|
|||
encrypted_value text,
|
||||
encrypted_value_salt character varying,
|
||||
encrypted_value_iv character varying,
|
||||
pipeline_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
|
||||
variable_type smallint DEFAULT 1 NOT NULL,
|
||||
partition_id bigint NOT NULL,
|
||||
raw boolean DEFAULT false NOT NULL,
|
||||
|
|
@ -32041,8 +32032,6 @@ CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules
|
|||
|
||||
CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key);
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_bigint_and_key ON ci_pipeline_variables USING btree (pipeline_id_convert_to_bigint, key);
|
||||
|
||||
CREATE INDEX index_ci_pipelines_config_on_pipeline_id ON ci_pipelines_config USING btree (pipeline_id);
|
||||
|
||||
CREATE INDEX index_ci_pipelines_for_ondemand_dast_scans ON ci_pipelines USING btree (id) WHERE (source = 13);
|
||||
|
|
@ -33467,8 +33456,6 @@ CREATE INDEX index_namespaces_on_path_trigram ON namespaces USING gin (path gin_
|
|||
|
||||
CREATE UNIQUE INDEX index_namespaces_on_push_rule_id ON namespaces USING btree (push_rule_id);
|
||||
|
||||
CREATE INDEX index_namespaces_on_require_two_factor_authentication ON namespaces USING btree (require_two_factor_authentication);
|
||||
|
||||
CREATE UNIQUE INDEX index_namespaces_on_runners_token_encrypted ON namespaces USING btree (runners_token_encrypted);
|
||||
|
||||
CREATE INDEX index_namespaces_on_traversal_ids ON namespaces USING gin (traversal_ids);
|
||||
|
|
@ -36927,8 +36914,6 @@ CREATE TRIGGER trigger_10ee1357e825 BEFORE INSERT OR UPDATE ON p_ci_builds FOR E
|
|||
|
||||
CREATE TRIGGER trigger_1bd97da9c1a4 BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_1bd97da9c1a4();
|
||||
|
||||
CREATE TRIGGER trigger_7f3d66a7d7f5 BEFORE INSERT OR UPDATE ON ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION trigger_7f3d66a7d7f5();
|
||||
|
||||
CREATE TRIGGER trigger_b2d852e1e2cb BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_b2d852e1e2cb();
|
||||
|
||||
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
|
||||
|
|
@ -39948,9 +39933,6 @@ ALTER TABLE issue_search_data
|
|||
ALTER TABLE product_analytics_events_experimental
|
||||
ADD CONSTRAINT product_analytics_events_experimental_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_pipeline_variables
|
||||
ADD CONSTRAINT temp_fk_rails_8d3b04e3e1 FOREIGN KEY (pipeline_id_convert_to_bigint) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY user_follow_users
|
||||
ADD CONSTRAINT user_follow_users_followee_id_fkey FOREIGN KEY (followee_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -346,6 +346,70 @@ The resulting file is named `dump_gitlab_backup.tar`. This is useful for
|
|||
systems that make use of rsync and incremental backups, and results in
|
||||
considerably faster transfer speeds.
|
||||
|
||||
#### Backup compression
|
||||
|
||||
By default, Gzip fast compression is applied during backup of:
|
||||
|
||||
- [PostgreSQL database](#postgresql-databases) dumps.
|
||||
- [blobs](#blobs), for example uploads, job artifacts, external merge request diffs.
|
||||
|
||||
The default command is `gzip -c -1`. You can override this command with `COMPRESS_CMD`.
|
||||
|
||||
Caveats:
|
||||
|
||||
- The compression command is used in a pipeline, so your custom command must output to stdout.
|
||||
- If you specify a command that is not packaged with GitLab, then you must install it yourself.
|
||||
- The resultant filenames will still end in `.gz`.
|
||||
- The default decompression command, used during restore, is `gzip -cd`. Therefore if you override the compression command to use a format that cannot be decompressed by `gzip -cd`, you must override the decompression command during restore.
|
||||
|
||||
### Default compression: Gzip with fastest method
|
||||
|
||||
```shell
|
||||
gitlab-backup create
|
||||
```
|
||||
|
||||
### Gzip with slowest method
|
||||
|
||||
```shell
|
||||
gitlab-backup create COMPRESS_CMD="gzip -c --best"
|
||||
```
|
||||
|
||||
If `gzip` was used for backup, then restore does not require any options:
|
||||
|
||||
```shell
|
||||
gitlab-backup restore
|
||||
```
|
||||
|
||||
### No compression
|
||||
|
||||
If your backup destination has built-in automatic compression, then you may wish to skip compression.
|
||||
|
||||
The `tee` command pipes `stdin` to `stdout`.
|
||||
|
||||
```shell
|
||||
gitlab-backup create COMPRESS_CMD=tee
|
||||
```
|
||||
|
||||
And on restore:
|
||||
|
||||
```shell
|
||||
gitlab-backup restore DECOMPRESS_CMD=tee
|
||||
```
|
||||
|
||||
### Replace Gzip
|
||||
|
||||
This is an example of how to use a compression tool which you installed manually:
|
||||
|
||||
```shell
|
||||
gitlab-backup create COMPRESS_CMD="foo --bar --baz"
|
||||
```
|
||||
|
||||
Similarly, on restore:
|
||||
|
||||
```shell
|
||||
gitlab-backup restore DECOMPRESS_CMD="foo --quz"
|
||||
```
|
||||
|
||||
#### Confirm archive can be transferred
|
||||
|
||||
To ensure the generated archive is transferable by rsync, you can set the `GZIP_RSYNCABLE=yes`
|
||||
|
|
|
|||
|
|
@ -1212,6 +1212,7 @@ Input type: `AdminSidekiqQueuesDeleteJobsInput`
|
|||
| <a id="mutationadminsidekiqqueuesdeletejobsartifactusedcdn"></a>`artifactUsedCdn` | [`String`](#string) | Delete jobs matching artifact_used_cdn in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobsartifactsdependenciescount"></a>`artifactsDependenciesCount` | [`String`](#string) | Delete jobs matching artifacts_dependencies_count in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobsartifactsdependenciessize"></a>`artifactsDependenciesSize` | [`String`](#string) | Delete jobs matching artifacts_dependencies_size in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobsbulkimportentityid"></a>`bulkImportEntityId` | [`String`](#string) | Delete jobs matching bulk_import_entity_id in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobscallerid"></a>`callerId` | [`String`](#string) | Delete jobs matching caller_id in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobsclientid"></a>`clientId` | [`String`](#string) | Delete jobs matching client_id in the context metadata. |
|
||||
| <a id="mutationadminsidekiqqueuesdeletejobsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
|
|
|
|||
|
|
@ -130,14 +130,17 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
|
||||
Rotate a group access token. Revokes the previous token and creates a new token that expires in one week.
|
||||
|
||||
In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
|
||||
|
||||
```plaintext
|
||||
POST /groups/:id/access_tokens/:token_id/rotate
|
||||
```
|
||||
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|---------|----------|---------------------|
|
||||
| `id` | integer or string | yes | ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
|
||||
| `token_id` | integer or string | yes | ID of the project access token |
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|------------|----------|---------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
|
||||
| `token_id` | integer/string | yes | ID of the access token |
|
||||
| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/<group_id>/access_tokens/<token_id>/rotate"
|
||||
|
|
|
|||
|
|
@ -211,13 +211,16 @@ Example response:
|
|||
|
||||
Rotate a personal access token. Revokes the previous token and creates a new token that expires in one week.
|
||||
|
||||
In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
|
||||
|
||||
```plaintext
|
||||
POST /personal_access_tokens/:id/rotate
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|---------|----------|---------------------|
|
||||
| `id` | integer/string | yes | ID of personal access token |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|-----------|----------|---------------------|
|
||||
| `id` | integer/string | yes | ID of personal access token |
|
||||
| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
|
||||
|
||||
NOTE:
|
||||
Non-administrators can rotate their own tokens. Administrators can rotate tokens of any user.
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
|
||||
Rotate a project access token. Revokes the previous token and creates a new token that expires in one week.
|
||||
|
||||
In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
|
||||
|
||||
WARNING:
|
||||
When you rotate a project access token, the new token retains the expiry date of the old token. For more information, see [issue 423362](https://gitlab.com/gitlab-org/gitlab/-/issues/423362).
|
||||
|
||||
|
|
@ -146,10 +148,11 @@ When you rotate a project access token, the new token retains the expiry date of
|
|||
POST /projects/:id/access_tokens/:token_id/rotate
|
||||
```
|
||||
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|---------|----------|---------------------|
|
||||
| `id` | integer or string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) |
|
||||
| `token_id` | integer or string | yes | ID of the project access token |
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|------------|----------|---------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) |
|
||||
| `token_id` | integer/string | yes | ID of the project access token |
|
||||
| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/access_tokens/<token_id>/rotate"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Use CI/CD pipelines to automatically build, test, and deploy your code.
|
|||
| [Create and run your first GitLab CI/CD pipeline](../ci/quick_start/index.md) | Create a `.gitlab-ci.yml` file and start a pipeline. | **{star}** |
|
||||
| [Create a complex pipeline](../ci/quick_start/tutorial.md) | Learn about the most commonly used GitLab CI/CD keywords by building an increasingly complex pipeline. | |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Get started: Learn about CI/CD](https://www.youtube.com/watch?v=sIegJaLy2ug) (9m 02s) | Learn about the `.gitlab-ci.yml` file and how it's used. | **{star}** |
|
||||
| [GitLab CI/CD](https://levelup.gitlab.com/courses/continuous-integration-and-delivery-ci-cd-with-gitlab) | Learn about GitLab CI/CD and build a pipeline in this self-paced course. | **{star}** |
|
||||
| [GitLab CI Fundamentals](https://levelup.gitlab.com/learn/learning-path/gitlab-ci-fundamentals) | Learn about GitLab CI/CD and build a pipeline in this self-paced course. | **{star}** |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [CI deep dive](https://www.youtube.com/watch?v=ZVUbmVac-m8&list=PL05JrBw4t0KorkxIFgZGnzzxjZRCGROt_&index=27) (22m 51s) | Take a closer look at pipelines and continuous integration concepts. | |
|
||||
| [Set up CI/CD in the cloud](../ci/examples/index.md#cicd-in-the-cloud) | Learn how to set up CI/CD in different cloud-based environments. | |
|
||||
| [Find CI/CD examples and templates](../ci/examples/index.md#cicd-examples) | Use these examples and templates to set up CI/CD for your use case. | |
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ pre-push:
|
|||
- ref: master
|
||||
commands:
|
||||
danger:
|
||||
run: bundle exec rake danger_local
|
||||
files: git diff --name-only $(git merge-base origin/master HEAD)..HEAD
|
||||
# We need to specify {files} as part of the command, otherwise it won't execute the hook
|
||||
run: echo {files} >/dev/null && FROM_LEFTHOOK=1 bundle exec rake danger_local
|
||||
eslint:
|
||||
tags: frontend style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ module Backup
|
|||
pg_env = backup_model.config[:pg_env]
|
||||
success = with_transient_pg_env(pg_env) do
|
||||
decompress_rd, decompress_wr = IO.pipe
|
||||
decompress_pid = spawn(*%w[gzip -cd], out: decompress_wr, in: db_file_name)
|
||||
decompress_pid = spawn(decompress_cmd, out: decompress_wr, in: db_file_name)
|
||||
decompress_wr.close
|
||||
|
||||
status, @errors =
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module Backup
|
|||
|
||||
def dump(database_name, output_file, pgsql_args)
|
||||
compress_rd, compress_wr = IO.pipe
|
||||
compress_pid = spawn(gzip_cmd, in: compress_rd, out: [output_file, 'w', FILE_PERMISSION])
|
||||
compress_pid = spawn(compress_cmd, in: compress_rd, out: [output_file, 'w', FILE_PERMISSION])
|
||||
compress_rd.close
|
||||
|
||||
dump_pid = Process.spawn('pg_dump', *pgsql_args, database_name, out: compress_wr)
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ module Backup
|
|||
end
|
||||
|
||||
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{backup_files_realpath} -cf - .]].flatten
|
||||
status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
|
||||
status_list, output = run_pipeline!([tar_cmd, compress_cmd], out: [backup_tarball, 'w', 0600])
|
||||
FileUtils.rm_rf(backup_files_realpath)
|
||||
else
|
||||
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{app_files_realpath} -cf - .]].flatten
|
||||
status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
|
||||
status_list, output = run_pipeline!([tar_cmd, compress_cmd], out: [backup_tarball, 'w', 0600])
|
||||
end
|
||||
|
||||
unless pipeline_succeeded?(tar_status: status_list[0], gzip_status: status_list[1], output: output)
|
||||
unless pipeline_succeeded?(tar_status: status_list[0], compress_status: status_list[1], output: output)
|
||||
raise_custom_error(backup_tarball)
|
||||
end
|
||||
end
|
||||
|
|
@ -56,9 +56,9 @@ module Backup
|
|||
def restore(backup_tarball, backup_id)
|
||||
backup_existing_files_dir(backup_tarball)
|
||||
|
||||
cmd_list = [%w[gzip -cd], %W[#{tar} --unlink-first --recursive-unlink -C #{app_files_realpath} -xf -]]
|
||||
cmd_list = [decompress_cmd, %W[#{tar} --unlink-first --recursive-unlink -C #{app_files_realpath} -xf -]]
|
||||
status_list, output = run_pipeline!(cmd_list, in: backup_tarball)
|
||||
unless pipeline_succeeded?(gzip_status: status_list[0], tar_status: status_list[1], output: output)
|
||||
unless pipeline_succeeded?(compress_status: status_list[0], tar_status: status_list[1], output: output)
|
||||
raise Backup::Error, "Restore operation failed: #{output}"
|
||||
end
|
||||
end
|
||||
|
|
@ -108,8 +108,8 @@ module Backup
|
|||
noncritical_warnings.map { |w| warning =~ w }.any?
|
||||
end
|
||||
|
||||
def pipeline_succeeded?(tar_status:, gzip_status:, output:)
|
||||
return false unless gzip_status&.success?
|
||||
def pipeline_succeeded?(tar_status:, compress_status:, output:)
|
||||
return false unless compress_status&.success?
|
||||
|
||||
tar_status&.success? || tar_ignore_non_success?(tar_status.exitstatus, output)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Backup
|
||||
module Helper
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
def access_denied_error(path)
|
||||
message = <<~EOS
|
||||
|
||||
|
|
@ -30,12 +32,27 @@ module Backup
|
|||
raise message
|
||||
end
|
||||
|
||||
def gzip_cmd
|
||||
@gzip_cmd ||= if ENV['GZIP_RSYNCABLE'] == 'yes'
|
||||
"gzip --rsyncable -c -1"
|
||||
else
|
||||
"gzip -c -1"
|
||||
end
|
||||
def compress_cmd
|
||||
if ENV['COMPRESS_CMD'].present?
|
||||
puts "Using custom COMPRESS_CMD '#{ENV['COMPRESS_CMD']}'"
|
||||
puts "Ignoring GZIP_RSYNCABLE" if ENV['GZIP_RSYNCABLE'] == 'yes'
|
||||
ENV['COMPRESS_CMD']
|
||||
elsif ENV['GZIP_RSYNCABLE'] == 'yes'
|
||||
"gzip --rsyncable -c -1"
|
||||
else
|
||||
"gzip -c -1"
|
||||
end
|
||||
end
|
||||
strong_memoize_attr :compress_cmd
|
||||
|
||||
def decompress_cmd
|
||||
if ENV['DECOMPRESS_CMD'].present?
|
||||
puts "Using custom DECOMPRESS_CMD '#{ENV['DECOMPRESS_CMD']}'"
|
||||
ENV['DECOMPRESS_CMD']
|
||||
else
|
||||
"gzip -cd"
|
||||
end
|
||||
end
|
||||
strong_memoize_attr :decompress_cmd
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ module Gitlab
|
|||
:artifacts_dependencies_size,
|
||||
:artifacts_dependencies_count,
|
||||
:root_caller_id,
|
||||
:merge_action_status
|
||||
:merge_action_status,
|
||||
:bulk_import_entity_id
|
||||
].freeze
|
||||
private_constant :KNOWN_KEYS
|
||||
|
||||
|
|
@ -45,7 +46,8 @@ module Gitlab
|
|||
Attribute.new(:artifacts_dependencies_size, Integer),
|
||||
Attribute.new(:artifacts_dependencies_count, Integer),
|
||||
Attribute.new(:root_caller_id, String),
|
||||
Attribute.new(:merge_action_status, String)
|
||||
Attribute.new(:merge_action_status, String),
|
||||
Attribute.new(:bulk_import_entity_id, Integer)
|
||||
].freeze
|
||||
private_constant :APPLICATION_ATTRIBUTES
|
||||
|
||||
|
|
@ -95,6 +97,7 @@ module Gitlab
|
|||
|
||||
# rubocop: disable Metrics/CyclomaticComplexity
|
||||
# rubocop: disable Metrics/PerceivedComplexity
|
||||
# rubocop: disable Metrics/AbcSize
|
||||
def to_lazy_hash
|
||||
{}.tap do |hash|
|
||||
assign_hash_if_value(hash, :caller_id)
|
||||
|
|
@ -106,6 +109,7 @@ module Gitlab
|
|||
assign_hash_if_value(hash, :artifacts_dependencies_size)
|
||||
assign_hash_if_value(hash, :artifacts_dependencies_count)
|
||||
assign_hash_if_value(hash, :merge_action_status)
|
||||
assign_hash_if_value(hash, :bulk_import_entity_id)
|
||||
|
||||
hash[:user] = -> { username } if include_user?
|
||||
hash[:user_id] = -> { user_id } if include_user?
|
||||
|
|
@ -115,10 +119,12 @@ module Gitlab
|
|||
hash[:pipeline_id] = -> { job&.pipeline_id } if set_values.include?(:job)
|
||||
hash[:job_id] = -> { job&.id } if set_values.include?(:job)
|
||||
hash[:artifact_size] = -> { artifact&.size } if set_values.include?(:artifact)
|
||||
hash[:bulk_import_entity_id] = -> { bulk_import_entity_id } if set_values.include?(:bulk_import_entity_id)
|
||||
end
|
||||
end
|
||||
# rubocop: enable Metrics/CyclomaticComplexity
|
||||
# rubocop: enable Metrics/PerceivedComplexity
|
||||
# rubocop: enable Metrics/AbcSize
|
||||
|
||||
def use
|
||||
Labkit::Context.with_context(to_lazy_hash) { yield }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Usage
|
||||
module Metrics
|
||||
module Instrumentations
|
||||
class GitlabConfigMetric < GenericMetric
|
||||
value do
|
||||
method_name_array = config_hash_to_method_array(options[:config])
|
||||
|
||||
method_name_array.inject(Gitlab.config, :public_send)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def config_hash_to_method_array(object)
|
||||
object.each_with_object([]) do |(key, value), result|
|
||||
result.append(key)
|
||||
|
||||
if value.is_a?(Hash)
|
||||
result.concat(config_hash_to_method_array(value))
|
||||
else
|
||||
result.append(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -163,11 +163,6 @@ module Gitlab
|
|||
|
||||
def features_usage_data_ce
|
||||
{
|
||||
container_registry_enabled: alt_usage_data(fallback: nil) { Gitlab.config.registry.enabled },
|
||||
dependency_proxy_enabled: Gitlab.config.try(:dependency_proxy)&.enabled,
|
||||
gitlab_shared_runners_enabled: alt_usage_data(fallback: nil) { Gitlab.config.gitlab_ci.shared_runners_enabled },
|
||||
ldap_enabled: alt_usage_data(fallback: nil) { Gitlab.config.ldap.enabled },
|
||||
mattermost_enabled: alt_usage_data(fallback: nil) { Gitlab.config.mattermost.enabled },
|
||||
omniauth_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth.omniauth_enabled? },
|
||||
prometheus_enabled: alt_usage_data(fallback: nil) { Gitlab::Prometheus::Internal.prometheus_enabled? },
|
||||
prometheus_metrics_enabled: alt_usage_data(fallback: nil) { Gitlab::Metrics.prometheus_metrics_enabled? },
|
||||
|
|
|
|||
|
|
@ -43315,6 +43315,9 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|Security Scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|Security policy overwrites this setting"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|Security policy project was linked successfully"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -43465,6 +43468,9 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|You already have the maximum %{maximumAllowed} %{policyType} policies."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|You can't change the default branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|You can't unprotect this branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ module RuboCop
|
|||
MSG = 'Avoid the use of `%{name}`. Use Gitlab::Saas.feature_available?. ' \
|
||||
'See https://docs.gitlab.com/ee/development/ee_features.html#saas-only-feature'
|
||||
RESTRICT_ON_SEND = %i[
|
||||
com? com_except_jh? com_and_canary? com_but_not_canary? org_or_com? should_check_namespace_plan?
|
||||
com? com_except_jh? com_and_canary? com_but_not_canary? org_or_com? should_check_namespace_plan? enabled?
|
||||
].freeze
|
||||
|
||||
# @!method gitlab?(node)
|
||||
|
|
@ -43,8 +43,16 @@ module RuboCop
|
|||
{nil? (cbase)} :Gitlab) :CurrentSettings) :should_check_namespace_plan?)
|
||||
PATTERN
|
||||
|
||||
# @!method saas_enabled?(node)
|
||||
def_node_matcher :saas_enabled?, <<~PATTERN
|
||||
(send
|
||||
(const
|
||||
(const
|
||||
{nil? (cbase)} :Gitlab) :Saas) :enabled?)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return unless gitlab?(node) || should_check_namespace_plan?(node)
|
||||
return unless gitlab?(node) || should_check_namespace_plan?(node) || saas_enabled?(node)
|
||||
|
||||
add_offense(node, message: format(MSG, name: node.method_name))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { removeBreakLine, removeWhitespace } from 'helpers/text_helper';
|
||||
import EnvironmentTable from '~/environments/components/environments_table.vue';
|
||||
import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
|
||||
import EnvironmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
|
|
@ -91,6 +92,10 @@ describe('Environments Folder View', () => {
|
|||
).toContain('Environments / review');
|
||||
});
|
||||
|
||||
it('should render the confirm rollback modal', () => {
|
||||
expect(wrapper.findComponent(ConfirmRollbackModal).exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('pagination', () => {
|
||||
it('should render pagination', () => {
|
||||
expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import RefSelector from '~/ref/components/ref_selector.vue';
|
|||
import { REF_TYPE_BRANCHES } from '~/ref/constants';
|
||||
|
||||
describe('projects/settings/components/default_branch_selector', () => {
|
||||
const disabled = true;
|
||||
const persistedDefaultBranch = 'main';
|
||||
const projectId = '123';
|
||||
let wrapper;
|
||||
|
|
@ -13,6 +14,7 @@ describe('projects/settings/components/default_branch_selector', () => {
|
|||
const buildWrapper = () => {
|
||||
wrapper = shallowMount(DefaultBranchSelector, {
|
||||
propsData: {
|
||||
disabled,
|
||||
persistedDefaultBranch,
|
||||
projectId,
|
||||
},
|
||||
|
|
@ -25,6 +27,7 @@ describe('projects/settings/components/default_branch_selector', () => {
|
|||
|
||||
it('displays a RefSelector component', () => {
|
||||
expect(findRefSelector().props()).toEqual({
|
||||
disabled,
|
||||
value: persistedDefaultBranch,
|
||||
enabledRefTypes: [REF_TYPE_BRANCHES],
|
||||
projectId,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe('Ref selector component', () => {
|
|||
let commitApiCallSpy;
|
||||
let requestSpies;
|
||||
|
||||
const createComponent = (mountOverrides = {}, propsData = {}) => {
|
||||
const createComponent = ({ overrides = {}, propsData = {} } = {}) => {
|
||||
wrapper = mountExtended(
|
||||
RefSelector,
|
||||
merge(
|
||||
|
|
@ -64,7 +64,7 @@ describe('Ref selector component', () => {
|
|||
},
|
||||
store: createStore(),
|
||||
},
|
||||
mountOverrides,
|
||||
overrides,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
|
@ -211,7 +211,7 @@ describe('Ref selector component', () => {
|
|||
const id = 'git-ref';
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({ attrs: { id } });
|
||||
createComponent({ overrides: { attrs: { id } } });
|
||||
|
||||
return waitForRequests();
|
||||
});
|
||||
|
|
@ -326,7 +326,7 @@ describe('Ref selector component', () => {
|
|||
describe('branches', () => {
|
||||
describe('when the branches search returns results', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, { useSymbolicRefNames: true });
|
||||
createComponent({ propsData: { useSymbolicRefNames: true } });
|
||||
|
||||
return waitForRequests();
|
||||
});
|
||||
|
|
@ -389,7 +389,7 @@ describe('Ref selector component', () => {
|
|||
describe('tags', () => {
|
||||
describe('when the tags search returns results', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, { useSymbolicRefNames: true });
|
||||
createComponent({ propsData: { useSymbolicRefNames: true } });
|
||||
|
||||
return waitForRequests();
|
||||
});
|
||||
|
|
@ -569,6 +569,20 @@ describe('Ref selector component', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled', () => {
|
||||
it('does not disable the dropdown', () => {
|
||||
createComponent();
|
||||
expect(findListbox().props('disabled')).toBe(false);
|
||||
});
|
||||
|
||||
it('disables the dropdown', async () => {
|
||||
createComponent({ propsData: { disabled: true } });
|
||||
expect(findListbox().props('disabled')).toBe(true);
|
||||
await selectFirstBranch();
|
||||
expect(wrapper.emitted('input')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with non-default ref types', () => {
|
||||
|
|
@ -691,9 +705,7 @@ describe('Ref selector component', () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
scopedSlots: { footer: createFooter },
|
||||
});
|
||||
createComponent({ overrides: { scopedSlots: { footer: createFooter } } });
|
||||
|
||||
updateQuery('abcd1234');
|
||||
|
||||
|
|
|
|||
|
|
@ -54,5 +54,12 @@ describe('MR widget status icon component', () => {
|
|||
expect(findIcon().exists()).toBe(true);
|
||||
expect(findIcon().props().name).toBe('merge-request-close');
|
||||
});
|
||||
|
||||
it('renders empty status icon', () => {
|
||||
createWrapper({ status: 'empty' });
|
||||
|
||||
expect(findStatusIcon().exists()).toBe(true);
|
||||
expect(findStatusIcon().props().iconName).toBe('neutral');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ exports[`New ready to merge state component renders permission text if canMerge
|
|||
status="success"
|
||||
/>
|
||||
<p
|
||||
class="gl-font-weight-bold gl-m-0! gl-text-gray-900! media-body"
|
||||
class="gl-font-weight-bold gl-mb-0! gl-mt-1 gl-text-gray-900! media-body"
|
||||
>
|
||||
Ready to merge by members who can write to the target branch.
|
||||
</p>
|
||||
|
|
@ -23,7 +23,7 @@ exports[`New ready to merge state component renders permission text if canMerge
|
|||
status="success"
|
||||
/>
|
||||
<p
|
||||
class="gl-font-weight-bold gl-m-0! gl-text-gray-900! media-body"
|
||||
class="gl-font-weight-bold gl-mb-0! gl-mt-1 gl-text-gray-900! media-body"
|
||||
>
|
||||
Ready to merge!
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue';
|
||||
|
||||
describe('NothingToMerge', () => {
|
||||
|
|
@ -14,6 +15,7 @@ describe('NothingToMerge', () => {
|
|||
};
|
||||
|
||||
const findNothingToMergeTextBody = () => wrapper.findByTestId('nothing-to-merge-body');
|
||||
const findHelpLink = () => wrapper.findComponent(GlLink);
|
||||
|
||||
describe('With Blob link', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -26,5 +28,9 @@ describe('NothingToMerge', () => {
|
|||
'Use merge requests to propose changes to your project and discuss them with your team. To make changes, use the Code dropdown list above, then test them with CI/CD before merging.',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders text with link to CI Help Page', () => {
|
||||
expect(findHelpLink().attributes('href')).toBe(helpPagePath('ci/quick_start/index.html'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Backup::Database, :reestablished_active_record_base, feature_category: :backup_restore do
|
||||
let(:progress) { StringIO.new }
|
||||
let(:output) { progress.string }
|
||||
let(:progress_output) { progress.string }
|
||||
let(:backup_id) { 'some_id' }
|
||||
let(:one_database_configured?) { base_models_for_backup.one? }
|
||||
let(:timeout_service) do
|
||||
|
|
@ -223,7 +223,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
|
|||
|
||||
subject.restore(backup_dir, backup_id)
|
||||
|
||||
expect(output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
|
||||
expect(progress_output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
|
||||
end
|
||||
|
||||
it 'has a pre restore warning' do
|
||||
|
|
@ -241,9 +241,21 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
|
|||
|
||||
subject.restore(backup_dir, backup_id)
|
||||
|
||||
expect(output).to include("Restoring PostgreSQL database")
|
||||
expect(output).to include("[DONE]")
|
||||
expect(output).not_to include("ERRORS")
|
||||
expect(progress_output).to include("Restoring PostgreSQL database")
|
||||
expect(progress_output).to include("[DONE]")
|
||||
expect(progress_output).not_to include("ERRORS")
|
||||
end
|
||||
|
||||
context 'when DECOMPRESS_CMD is set to tee' do
|
||||
before do
|
||||
stub_env('DECOMPRESS_CMD', 'tee')
|
||||
end
|
||||
|
||||
it 'outputs a message about DECOMPRESS_CMD' do
|
||||
expect do
|
||||
subject.restore(backup_dir, backup_id)
|
||||
end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -277,9 +289,9 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
|
|||
|
||||
subject.restore(backup_dir, backup_id)
|
||||
|
||||
expect(output).to include("ERRORS")
|
||||
expect(output).not_to include(noise)
|
||||
expect(output).to include(visible_error)
|
||||
expect(progress_output).to include("ERRORS")
|
||||
expect(progress_output).not_to include(noise)
|
||||
expect(progress_output).to include(visible_error)
|
||||
expect(subject.post_restore_warning).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,5 +32,25 @@ RSpec.describe Backup::Dump::Postgres, feature_category: :backup_restore do
|
|||
|
||||
expect(File.exist?(db_file_name)).to eq(true)
|
||||
end
|
||||
|
||||
context 'when COMPRESS_CMD is set to tee' do
|
||||
let(:tee_pid) { spawn('tee', in: pipes[0], out: [db_file_name, 'w', 0o600]) }
|
||||
|
||||
before do
|
||||
stub_env('COMPRESS_CMD', 'tee')
|
||||
end
|
||||
|
||||
it 'passes through tee instead of gzip' do
|
||||
expect(subject).to receive(:spawn).with('tee', in: pipes.first,
|
||||
out: [db_file_name, 'w', 0o600]).and_return(tee_pid)
|
||||
expect(Process).to receive(:spawn).with('pg_dump', *args, pg_database, out: pipes[1]).and_return(pg_dump_pid)
|
||||
|
||||
expect do
|
||||
subject.dump(pg_database, db_file_name, args)
|
||||
end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
|
||||
|
||||
expect(File.exist?(db_file_name)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
it 'calls tar command with unlink' do
|
||||
expect(subject).to receive(:tar).and_return('blabla-tar')
|
||||
|
||||
expect(subject).to receive(:run_pipeline!).with([%w[gzip -cd], %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]], any_args)
|
||||
expect(subject).to receive(:run_pipeline!).with(["gzip -cd", %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]], any_args)
|
||||
expect(subject).to receive(:pipeline_succeeded?).and_return(true)
|
||||
subject.restore('registry.tar.gz', 'backup_id')
|
||||
end
|
||||
|
|
@ -107,6 +107,21 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
expect { subject.restore('registry.tar.gz', 'backup_id') }.to raise_error(/is a mountpoint/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with DECOMPRESS_CMD' do
|
||||
before do
|
||||
stub_env('DECOMPRESS_CMD', 'tee')
|
||||
allow(subject).to receive(:pipeline_succeeded?).and_return(true)
|
||||
end
|
||||
|
||||
it 'passes through tee instead of gzip' do
|
||||
expect(subject).to receive(:run_pipeline!).with(['tee', anything], any_args).and_return([[true, true], ''])
|
||||
|
||||
expect do
|
||||
subject.restore('registry.tar.gz', 'backup_id')
|
||||
end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dump' do
|
||||
|
|
@ -173,6 +188,37 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
.and raise_error(/Failed to create compressed file/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with COMPRESS_CMD' do
|
||||
before do
|
||||
stub_env('COMPRESS_CMD', 'tee')
|
||||
end
|
||||
|
||||
it 'passes through tee instead of gzip' do
|
||||
expect(subject).to receive(:run_pipeline!).with([anything, 'tee'], any_args)
|
||||
expect do
|
||||
subject.dump('registry.tar.gz', 'backup_id')
|
||||
end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when GZIP_RSYNCABLE is "yes"' do
|
||||
before do
|
||||
stub_env('GZIP_RSYNCABLE', 'yes')
|
||||
end
|
||||
|
||||
it 'gzips the files with rsyncable option' do
|
||||
expect(subject).to receive(:run_pipeline!).with([anything, 'gzip --rsyncable -c -1'], any_args)
|
||||
subject.dump('registry.tar.gz', 'backup_id')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when GZIP_RSYNCABLE is not set' do
|
||||
it 'gzips the files without the rsyncable option' do
|
||||
expect(subject).to receive(:run_pipeline!).with([anything, 'gzip -c -1'], any_args)
|
||||
subject.dump('registry.tar.gz', 'backup_id')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exclude_dirs' do
|
||||
|
|
@ -226,13 +272,13 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
|
||||
it 'returns true if both tar and gzip succeeeded' do
|
||||
expect(
|
||||
subject.pipeline_succeeded?(tar_status: status_0, gzip_status: status_0, output: 'any_output')
|
||||
subject.pipeline_succeeded?(tar_status: status_0, compress_status: status_0, output: 'any_output')
|
||||
).to be_truthy
|
||||
end
|
||||
|
||||
it 'returns false if gzip failed' do
|
||||
expect(
|
||||
subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_1, output: 'any_output')
|
||||
subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_1, output: 'any_output')
|
||||
).to be_falsey
|
||||
end
|
||||
|
||||
|
|
@ -243,7 +289,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
|
||||
it 'returns true' do
|
||||
expect(
|
||||
subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_0, output: 'any_output')
|
||||
subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
|
||||
).to be_truthy
|
||||
end
|
||||
end
|
||||
|
|
@ -255,7 +301,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
|
|||
|
||||
it 'returns false' do
|
||||
expect(
|
||||
subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_0, output: 'any_output')
|
||||
subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
|
||||
).to be_falsey
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -210,6 +210,14 @@ RSpec.describe Gitlab::ApplicationContext do
|
|||
expect(result(context)).to include(job_id: job.id, project: project.full_path, pipeline_id: job.pipeline_id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using bulk import context' do
|
||||
it 'sets expected bulk_import_entity_id value' do
|
||||
context = described_class.new(bulk_import_entity_id: 1)
|
||||
|
||||
expect(result(context)).to include(bulk_import_entity_id: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#use' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::GitlabConfigMetric, feature_category: :service_ping do
|
||||
describe 'config metric' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:config_value, :expected_value) do
|
||||
false | false
|
||||
true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
stub_config(artifacts: { object_store: { enabled: config_value } })
|
||||
end
|
||||
|
||||
it_behaves_like 'a correct instrumented metric value', {
|
||||
time_frame: 'none',
|
||||
options: {
|
||||
config: {
|
||||
artifacts: {
|
||||
object_store: 'enabled'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -571,13 +571,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
|
|||
subject { described_class.features_usage_data_ce }
|
||||
|
||||
it 'gathers feature usage data', :aggregate_failures do
|
||||
expect(subject[:mattermost_enabled]).to eq(Gitlab.config.mattermost.enabled)
|
||||
expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
|
||||
expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
|
||||
expect(subject[:reply_by_email_enabled]).to eq(Gitlab::Email::IncomingEmail.enabled?)
|
||||
expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
|
||||
expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled)
|
||||
expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
|
||||
end
|
||||
|
||||
context 'with embedded Prometheus' do
|
||||
|
|
|
|||
|
|
@ -179,6 +179,11 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
it 'uses SMTP delivery method and custom email settings' do
|
||||
expect_service_desk_custom_email_delivery_options(service_desk_setting)
|
||||
|
||||
# Don't use ActionMailer::Base.smtp_settings, because it only contains explicitly set values.
|
||||
merged_default_settings = Mail::SMTP.new({}).settings
|
||||
# When forcibly used the configuration has a higher timeout. Ensure it's the default!
|
||||
expect(subject.delivery_method.settings[:read_timeout]).to eq(merged_default_settings[:read_timeout])
|
||||
|
||||
expect(Gitlab::AppLogger).to have_received(:info).with({ category: 'custom_email' })
|
||||
end
|
||||
|
||||
|
|
@ -537,7 +542,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
}
|
||||
end
|
||||
|
||||
subject { Notify.service_desk_custom_email_verification_email(service_desk_setting) }
|
||||
subject(:mail) { Notify.service_desk_custom_email_verification_email(service_desk_setting) }
|
||||
|
||||
it_behaves_like 'a custom email verification process email'
|
||||
|
||||
|
|
@ -547,6 +552,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
|
||||
it 'forcibly uses SMTP delivery method and has correct settings' do
|
||||
expect_service_desk_custom_email_delivery_options(service_desk_setting)
|
||||
expect(mail.delivery_method.settings[:read_timeout]).to eq(described_class::VERIFICATION_EMAIL_TIMEOUT)
|
||||
|
||||
# defaults are unchanged after email overrode settings
|
||||
expect(Mail::SMTP.new({}).settings).to include(expected_delivery_method_defaults)
|
||||
|
|
@ -557,7 +563,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
end
|
||||
|
||||
it 'uses verification email address as recipient' do
|
||||
expect(subject.to).to eq([service_desk_setting.custom_email_address_for_verification])
|
||||
expect(mail.to).to eq([service_desk_setting.custom_email_address_for_verification])
|
||||
end
|
||||
|
||||
it 'contains verification token' do
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ RSpec.describe RuboCop::Cop::Gitlab::AvoidGitlabInstanceChecks, feature_category
|
|||
::Gitlab.com?
|
||||
Gitlab::CurrentSettings.should_check_namespace_plan?
|
||||
::Gitlab::CurrentSettings.should_check_namespace_plan?
|
||||
Gitlab::Saas.enabled?
|
||||
::Gitlab::Saas.enabled?
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -77,13 +77,8 @@ module UsageDataHelpers
|
|||
USAGE_DATA_KEYS = %i[
|
||||
counts
|
||||
recorded_at
|
||||
mattermost_enabled
|
||||
ldap_enabled
|
||||
omniauth_enabled
|
||||
reply_by_email_enabled
|
||||
container_registry_enabled
|
||||
dependency_proxy_enabled
|
||||
gitlab_shared_runners_enabled
|
||||
gitlab_pages
|
||||
git
|
||||
gitaly
|
||||
|
|
|
|||
|
|
@ -15,69 +15,78 @@ RSpec.describe Tooling::Danger::OutdatedTodo, feature_category: :tooling do
|
|||
]
|
||||
end
|
||||
|
||||
subject(:plugin) { described_class.new(filenames, context: fake_danger, todos: todos) }
|
||||
subject(:plugin) { described_class.new(filenames, context: fake_danger, todos: todos, allow_fail: allow_fail) }
|
||||
|
||||
context 'when the filenames are mentioned in single todo' do
|
||||
let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
|
||||
[true, false].each do |allow_failure|
|
||||
context "with allow_fail set to #{allow_failure}" do
|
||||
let(:allow_fail) { allow_failure }
|
||||
let(:expected_method) do
|
||||
allow_failure ? :fail : :warn
|
||||
end
|
||||
|
||||
it 'warns about mentions' do
|
||||
expect(fake_danger)
|
||||
.to receive(:warn)
|
||||
.with <<~MESSAGE
|
||||
`app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
|
||||
MESSAGE
|
||||
context 'when the filenames are mentioned in single todo' do
|
||||
let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
|
||||
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
it 'warns about mentions' do
|
||||
expect(fake_danger)
|
||||
.to receive(expected_method)
|
||||
.with <<~MESSAGE
|
||||
`app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
|
||||
MESSAGE
|
||||
|
||||
context 'when the filenames are mentioned in multiple todos' do
|
||||
let(:filenames) do
|
||||
[
|
||||
'app/controllers/application_controller.rb',
|
||||
'app/controllers/acme_challenges_controller.rb'
|
||||
]
|
||||
end
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
|
||||
it 'warns about mentions' do
|
||||
expect(fake_danger)
|
||||
.to receive(:warn)
|
||||
.with(<<~FIRSTMESSAGE)
|
||||
`app/controllers/application_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:4`
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop2.yml:4`
|
||||
FIRSTMESSAGE
|
||||
context 'when the filenames are mentioned in multiple todos' do
|
||||
let(:filenames) do
|
||||
[
|
||||
'app/controllers/application_controller.rb',
|
||||
'app/controllers/acme_challenges_controller.rb'
|
||||
]
|
||||
end
|
||||
|
||||
expect(fake_danger)
|
||||
.to receive(:warn)
|
||||
.with(<<~SECONDMESSAGE)
|
||||
`app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
|
||||
SECONDMESSAGE
|
||||
it 'warns about mentions' do
|
||||
expect(fake_danger)
|
||||
.to receive(expected_method)
|
||||
.with(<<~FIRSTMESSAGE)
|
||||
`app/controllers/application_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:4`
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop2.yml:4`
|
||||
FIRSTMESSAGE
|
||||
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
expect(fake_danger)
|
||||
.to receive(expected_method)
|
||||
.with(<<~SECONDMESSAGE)
|
||||
`app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
|
||||
- `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
|
||||
SECONDMESSAGE
|
||||
|
||||
context 'when the filenames are not mentioned in todos' do
|
||||
let(:filenames) { ['any/inexisting/file.rb'] }
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not warn' do
|
||||
expect(fake_danger).not_to receive(:warn)
|
||||
context 'when the filenames are not mentioned in todos' do
|
||||
let(:filenames) { ['any/inexisting/file.rb'] }
|
||||
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
it 'does not warn' do
|
||||
expect(fake_danger).not_to receive(expected_method)
|
||||
|
||||
context 'when there is no todos' do
|
||||
let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
|
||||
let(:todos) { [] }
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not warn' do
|
||||
expect(fake_danger).not_to receive(:warn)
|
||||
context 'when there is no todos' do
|
||||
let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
|
||||
let(:todos) { [] }
|
||||
|
||||
plugin.check
|
||||
it 'does not warn' do
|
||||
expect(fake_danger).not_to receive(expected_method)
|
||||
|
||||
plugin.check
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,11 +63,32 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man
|
|||
end
|
||||
|
||||
it 'inserts all records' do
|
||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
|
||||
{ status: :processed, records_inserted: 4, reached_end_of_table: true })
|
||||
|
||||
worker.perform
|
||||
|
||||
events = ClickHouse::Client.select('SELECT * FROM events', :main)
|
||||
expect(events.size).to eq(4)
|
||||
end
|
||||
|
||||
context 'when new records are inserted while processing' do
|
||||
it 'does not process new records created during the iteration' do
|
||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
|
||||
{ status: :processed, records_inserted: 4,
|
||||
reached_end_of_table: true })
|
||||
|
||||
# Simulating the case when there is an insert during the iteration
|
||||
call_count = 0
|
||||
allow(worker).to receive(:next_batch).and_wrap_original do |method|
|
||||
call_count += 1
|
||||
create(:event) if call_count == 3
|
||||
method.call
|
||||
end
|
||||
|
||||
worker.perform
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when time limit is reached' do
|
||||
|
|
@ -96,6 +117,9 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man
|
|||
end
|
||||
|
||||
it 'syncs records after the cursor' do
|
||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
|
||||
{ status: :processed, records_inserted: 3, reached_end_of_table: true })
|
||||
|
||||
worker.perform
|
||||
|
||||
events = ClickHouse::Client.select('SELECT id FROM events ORDER BY id', :main)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ module Tooling
|
|||
spec/support/rspec_order_todo.yml
|
||||
].freeze
|
||||
|
||||
def initialize(filenames, context:, todos: TODOS_GLOBS)
|
||||
def initialize(filenames, context:, todos: TODOS_GLOBS, allow_fail: false)
|
||||
@filenames = filenames
|
||||
@context = context
|
||||
@todos_globs = todos
|
||||
@allow_fail = allow_fail
|
||||
end
|
||||
|
||||
def check
|
||||
|
|
@ -22,17 +23,23 @@ module Tooling
|
|||
|
||||
private
|
||||
|
||||
attr_reader :filenames, :context
|
||||
attr_reader :filenames, :context, :allow_fail
|
||||
|
||||
def check_filename(filename)
|
||||
mentions = all_mentions_for(filename)
|
||||
|
||||
return if mentions.empty?
|
||||
|
||||
context.warn <<~MESSAGE
|
||||
message = <<~MESSAGE
|
||||
`#{filename}` was removed but is mentioned in:
|
||||
#{mentions.join("\n")}
|
||||
MESSAGE
|
||||
|
||||
if allow_fail
|
||||
context.fail message
|
||||
else
|
||||
context.warn message
|
||||
end
|
||||
end
|
||||
|
||||
def all_mentions_for(filename)
|
||||
|
|
|
|||
Loading…
Reference in New Issue