Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
12166c0faf
commit
c1f98d9590
|
|
@ -104,7 +104,8 @@ yarn-audit-dependency_scanning:
|
|||
- mkdir ~/.aws
|
||||
- '[[ -z "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ]] || mv "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ~/.aws/credentials'
|
||||
- npm install --no-save --ignore-scripts @aws-sdk/client-s3@3.49.0
|
||||
- scripts/ingest-reports-to-siem
|
||||
- scripts/ingest-reports-to-siem || true # Allow legacy report to fail as we'll remove it in the future anyway
|
||||
- scripts/ingest-reports-to-siem-devo
|
||||
artifacts:
|
||||
paths:
|
||||
- gl-dependency-scanning-report.json
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
f223d8cbcb6319356cb9f746252b15e541695d2f
|
||||
7910fc0e132402d3ab22cef73924aef360598f35
|
||||
|
|
|
|||
4
Gemfile
4
Gemfile
|
|
@ -348,7 +348,7 @@ gem 'pg_query', '~> 2.2', '>= 2.2.1'
|
|||
|
||||
gem 'premailer-rails', '~> 1.10.3'
|
||||
|
||||
gem 'gitlab-labkit', '~> 0.30.1'
|
||||
gem 'gitlab-labkit', '~> 0.31.0'
|
||||
gem 'thrift', '>= 0.16.0'
|
||||
|
||||
# I18n
|
||||
|
|
@ -373,7 +373,7 @@ gem 'prometheus-client-mmap', '~> 0.17', require: 'prometheus/client'
|
|||
gem 'warning', '~> 1.3.0'
|
||||
|
||||
group :development do
|
||||
gem 'lefthook', '~> 1.2.8', require: false
|
||||
gem 'lefthook', '~> 1.2.9', require: false
|
||||
gem 'rubocop'
|
||||
gem 'solargraph', '~> 0.47.2', require: false
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@
|
|||
{"name":"gitlab-dangerfiles","version":"3.7.0","platform":"ruby","checksum":"35c5bc42e60c575ab5701192ca2384ab414b14c2963602b39e143b1aaeb7e54d"},
|
||||
{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
|
||||
{"name":"gitlab-fog-azure-rm","version":"1.4.0","platform":"ruby","checksum":"af4163c32b028aa5208814a3f4765a5817d50527e6c61931f766bf18a2e0eb7e"},
|
||||
{"name":"gitlab-labkit","version":"0.30.1","platform":"ruby","checksum":"bdedbd86014c83dfd6a50d20dbc1709697bba2bb9e3666383e5f28cbd312b113"},
|
||||
{"name":"gitlab-labkit","version":"0.31.0","platform":"ruby","checksum":"5b044c4ededd7005e6d1ca5a53ac5f9d7a4d12a7363673fbc898e1844246ed1f"},
|
||||
{"name":"gitlab-license","version":"2.2.1","platform":"ruby","checksum":"39fcf6be8b2887df8afe01b5dcbae8d08b7c5d937ff56b0fb40484a8c4f02d30"},
|
||||
{"name":"gitlab-mail_room","version":"0.0.9","platform":"ruby","checksum":"6700374b5c0aa9d9ad4e711aeb677f0b7d415a6d01d3baa699efab25349d851c"},
|
||||
{"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"},
|
||||
|
|
@ -314,7 +314,7 @@
|
|||
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
|
||||
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
|
||||
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
|
||||
{"name":"lefthook","version":"1.2.8","platform":"ruby","checksum":"3776de22e0a3de8fc8c4c58d8b42cd5eeb1cd291232a09a8dc5335f0ad505a7c"},
|
||||
{"name":"lefthook","version":"1.2.9","platform":"ruby","checksum":"1fd4a768e08fc624e756597fc628b3c7991267325974a7a5cc169595b425701d"},
|
||||
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
|
||||
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
|
||||
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},
|
||||
|
|
|
|||
|
|
@ -584,7 +584,7 @@ GEM
|
|||
fog-json (~> 1.2.0)
|
||||
mime-types
|
||||
ms_rest_azure (~> 0.12.0)
|
||||
gitlab-labkit (0.30.1)
|
||||
gitlab-labkit (0.31.0)
|
||||
actionpack (>= 5.0.0, < 8.0.0)
|
||||
activesupport (>= 5.0.0, < 8.0.0)
|
||||
grpc (>= 1.37)
|
||||
|
|
@ -845,7 +845,7 @@ GEM
|
|||
kramdown (~> 2.0)
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
lefthook (1.2.8)
|
||||
lefthook (1.2.9)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
letter_opener_web (2.0.0)
|
||||
|
|
@ -1679,7 +1679,7 @@ DEPENDENCIES
|
|||
gitlab-dangerfiles (~> 3.7.0)
|
||||
gitlab-experiment (~> 0.7.1)
|
||||
gitlab-fog-azure-rm (~> 1.4.0)
|
||||
gitlab-labkit (~> 0.30.1)
|
||||
gitlab-labkit (~> 0.31.0)
|
||||
gitlab-license (~> 2.2.1)
|
||||
gitlab-mail_room (~> 0.0.9)
|
||||
gitlab-markup (~> 1.9.0)
|
||||
|
|
@ -1738,7 +1738,7 @@ DEPENDENCIES
|
|||
knapsack (~> 1.21.1)
|
||||
kramdown (~> 2.3.1)
|
||||
kubeclient (~> 4.9.3)!
|
||||
lefthook (~> 1.2.8)
|
||||
lefthook (~> 1.2.9)
|
||||
letter_opener_web (~> 2.0.0)
|
||||
license_finder (~> 7.0)
|
||||
licensee (~> 9.15)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ const STATUS_MAP = {
|
|||
};
|
||||
|
||||
function isIncompleteImport(stats) {
|
||||
return Object.keys(stats.fetched).some((key) => stats.fetched[key] !== stats.imported[key]);
|
||||
return Object.keys(stats?.fetched ?? []).some(
|
||||
(key) => stats.fetched[key] !== stats.imported[key],
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
@ -91,7 +93,9 @@ export default {
|
|||
computed: {
|
||||
knownStats() {
|
||||
const knownStatisticKeys = Object.keys(STATISTIC_ITEMS);
|
||||
return Object.keys(this.stats.fetched).filter((key) => knownStatisticKeys.includes(key));
|
||||
return Object.keys(this.stats?.fetched ?? []).filter((key) =>
|
||||
knownStatisticKeys.includes(key),
|
||||
);
|
||||
},
|
||||
|
||||
hasStats() {
|
||||
|
|
|
|||
|
|
@ -182,16 +182,16 @@ export default {
|
|||
<div v-if="repositories.length" class="gl-w-full">
|
||||
<table>
|
||||
<thead class="gl-border-0 gl-border-solid gl-border-t-1 gl-border-gray-100">
|
||||
<th class="import-jobs-from-col gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
<th class="gl-w-half gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
{{ fromHeaderText }}
|
||||
</th>
|
||||
<th class="import-jobs-to-col gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
<th class="gl-w-half gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
{{ __('To GitLab') }}
|
||||
</th>
|
||||
<th class="import-jobs-status-col gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
<th class="gl-p-4 gl-vertical-align-top gl-border-b-1">
|
||||
{{ __('Status') }}
|
||||
</th>
|
||||
<th class="import-jobs-cta-col gl-p-4 gl-vertical-align-top gl-border-b-1"></th>
|
||||
<th class="gl-p-4 gl-vertical-align-top gl-border-b-1"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="repo in repositories">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {
|
|||
GlDropdownDivider,
|
||||
GlDropdownSectionHeader,
|
||||
GlTooltip,
|
||||
GlSprintf,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import { __ } from '~/locale';
|
||||
|
|
@ -32,6 +34,10 @@ export default {
|
|||
GlBadge,
|
||||
GlLink,
|
||||
GlTooltip,
|
||||
GlSprintf,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
repo: {
|
||||
|
|
@ -53,6 +59,12 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isSelectedForReimport: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['ciCdOnly']),
|
||||
...mapGetters(['getImportTarget']),
|
||||
|
|
@ -94,7 +106,11 @@ export default {
|
|||
},
|
||||
|
||||
importButtonText() {
|
||||
return this.ciCdOnly ? __('Connect') : __('Import');
|
||||
if (this.ciCdOnly) {
|
||||
return __('Connect');
|
||||
}
|
||||
|
||||
return this.isFinished ? __('Re-import') : __('Import');
|
||||
},
|
||||
|
||||
newNameInput: {
|
||||
|
|
@ -115,6 +131,22 @@ export default {
|
|||
importTarget: { ...this.importTarget, ...changedValues },
|
||||
});
|
||||
},
|
||||
|
||||
handleImportRepo() {
|
||||
if (this.isFinished && !this.isSelectedForReimport) {
|
||||
this.isSelectedForReimport = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.newNameInput.$el.focus();
|
||||
});
|
||||
} else {
|
||||
this.isSelectedForReimport = false;
|
||||
|
||||
this.fetchImport({
|
||||
repoId: this.repo.importSource.id,
|
||||
optionalStages: this.optionalStages,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
helpUrl: helpPagePath('/user/project/import/github.md'),
|
||||
|
|
@ -132,6 +164,20 @@ export default {
|
|||
>{{ repo.importSource.fullName }}
|
||||
<gl-icon v-if="repo.importSource.providerLink" name="external-link" />
|
||||
</gl-link>
|
||||
<div v-if="isFinished" class="gl-font-sm">
|
||||
<gl-sprintf :message="s__('BulkImport|Last imported to %{link}')">
|
||||
<template #link>
|
||||
<gl-link
|
||||
:href="repo.importedProject.fullPath"
|
||||
class="gl-font-sm"
|
||||
target="_blank"
|
||||
data-qa-selector="go_to_project_link"
|
||||
>
|
||||
{{ displayFullPath }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="gl-display-flex gl-sm-flex-wrap gl-p-4 gl-pt-5 gl-vertical-align-top"
|
||||
|
|
@ -139,7 +185,7 @@ export default {
|
|||
data-qa-selector="project_path_content"
|
||||
>
|
||||
<template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
|
||||
<template v-else-if="isImportNotStarted">
|
||||
<template v-else-if="isImportNotStarted || isSelectedForReimport">
|
||||
<div class="gl-display-flex gl-align-items-stretch gl-w-full">
|
||||
<import-group-dropdown #default="{ namespaces }" :text="importTarget.targetNamespace">
|
||||
<template v-if="namespaces.length">
|
||||
|
|
@ -166,6 +212,7 @@ export default {
|
|||
/
|
||||
</div>
|
||||
<gl-form-input
|
||||
ref="newNameInput"
|
||||
v-model="newNameInput"
|
||||
class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
|
||||
data-qa-selector="project_path_field"
|
||||
|
|
@ -177,7 +224,7 @@ export default {
|
|||
<td class="gl-p-4 gl-vertical-align-top" data-qa-selector="import_status_indicator">
|
||||
<import-status :status="importStatus" :stats="stats" />
|
||||
</td>
|
||||
<td data-testid="actions" class="gl-vertical-align-top gl-pt-4">
|
||||
<td data-testid="actions" class="gl-vertical-align-top gl-pt-4 gl-white-space-nowrap">
|
||||
<gl-tooltip :target="() => $refs.cancelButton.$el">
|
||||
<div class="gl-text-left">
|
||||
<p class="gl-mb-5 gl-font-weight-bold">{{ s__('ImportProjects|Cancel import') }}</p>
|
||||
|
|
@ -199,22 +246,26 @@ export default {
|
|||
@click="cancelImport({ repoId: repo.importSource.id })"
|
||||
/>
|
||||
<gl-button
|
||||
v-if="isFinished"
|
||||
class="btn btn-default"
|
||||
:href="repo.importedProject.fullPath"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
data-qa-selector="go_to_project_button"
|
||||
>{{ __('Go to project') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="isImportNotStarted"
|
||||
v-if="isImportNotStarted || isFinished"
|
||||
type="button"
|
||||
data-qa-selector="import_button"
|
||||
@click="fetchImport({ repoId: repo.importSource.id, optionalStages })"
|
||||
@click="handleImportRepo()"
|
||||
>
|
||||
{{ importButtonText }}
|
||||
</gl-button>
|
||||
<gl-icon
|
||||
v-if="isFinished"
|
||||
v-gl-tooltip
|
||||
:size="16"
|
||||
name="information-o"
|
||||
:title="
|
||||
s__(
|
||||
'ImportProjects|Re-import creates a new project. It does not sync with the existing project.',
|
||||
)
|
||||
"
|
||||
class="gl-ml-3"
|
||||
/>
|
||||
|
||||
<gl-badge v-else-if="isIncompatible" variant="danger">{{
|
||||
__('Incompatible project')
|
||||
}}</gl-badge>
|
||||
|
|
|
|||
|
|
@ -2,16 +2,6 @@ import Vue from 'vue';
|
|||
import { STATUSES } from '../../constants';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
const makeNewImportedProject = (importedProject) => ({
|
||||
importSource: {
|
||||
id: importedProject.id,
|
||||
fullName: importedProject.importSource,
|
||||
sanitizedName: importedProject.name,
|
||||
providerLink: importedProject.providerLink,
|
||||
},
|
||||
importedProject: { ...importedProject },
|
||||
});
|
||||
|
||||
const makeNewIncompatibleProject = (project) => ({
|
||||
importSource: { ...project, incompatible: true },
|
||||
importedProject: null,
|
||||
|
|
@ -55,14 +45,6 @@ export default {
|
|||
// Legacy code path, will be removed when all importers will be switched to new pagination format
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091
|
||||
|
||||
const newImportedProjects = processLegacyEntries({
|
||||
newRepositories: repositories.importedProjects.filter(
|
||||
(p) => p.importStatus !== STATUSES.CANCELED,
|
||||
),
|
||||
existingRepositories: state.repositories,
|
||||
factory: makeNewImportedProject,
|
||||
});
|
||||
|
||||
const incompatibleRepos = repositories.incompatibleRepos ?? [];
|
||||
const newIncompatibleProjects = processLegacyEntries({
|
||||
newRepositories: incompatibleRepos,
|
||||
|
|
@ -70,16 +52,22 @@ export default {
|
|||
factory: makeNewIncompatibleProject,
|
||||
});
|
||||
|
||||
const existingProjects = [...newImportedProjects, ...state.repositories];
|
||||
const existingProjectNames = new Set(existingProjects.map((p) => p.importSource.fullName));
|
||||
const existingProjectNames = new Set(state.repositories.map((p) => p.importSource.fullName));
|
||||
const importedProjects = [...(repositories.importedProjects ?? [])].reverse();
|
||||
const newProjects = repositories.providerRepos
|
||||
.filter((project) => !existingProjectNames.has(project.fullName))
|
||||
.map((project) => ({
|
||||
importSource: project,
|
||||
importedProject: null,
|
||||
}));
|
||||
.map((project) => {
|
||||
const importedProject = importedProjects.find(
|
||||
(p) => p.providerLink === project.providerLink,
|
||||
);
|
||||
|
||||
state.repositories = [...existingProjects, ...newProjects, ...newIncompatibleProjects];
|
||||
return {
|
||||
importSource: project,
|
||||
importedProject,
|
||||
};
|
||||
});
|
||||
|
||||
state.repositories = [...state.repositories, ...newProjects, ...newIncompatibleProjects];
|
||||
|
||||
if (incompatibleRepos.length === 0 && repositories.providerRepos.length === 0) {
|
||||
state.pageInfo.page -= 1;
|
||||
|
|
@ -113,7 +101,7 @@ export default {
|
|||
|
||||
[types.RECEIVE_IMPORT_ERROR](state, repoId) {
|
||||
const existingRepo = state.repositories.find((r) => r.importSource.id === repoId);
|
||||
existingRepo.importedProject = null;
|
||||
existingRepo.importedProject.importStatus = STATUSES.FAILED;
|
||||
},
|
||||
|
||||
[types.RECEIVE_JOBS_SUCCESS](state, updatedProjects) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function getImportStatus(project) {
|
|||
export function isProjectImportable(project) {
|
||||
return (
|
||||
!isIncompatible(project) &&
|
||||
[STATUSES.NONE, STATUSES.CANCELED].includes(getImportStatus(project))
|
||||
[STATUSES.NONE, STATUSES.CANCELED, STATUSES.FAILED].includes(getImportStatus(project))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,10 +86,8 @@ export default {
|
|||
</span>
|
||||
<template v-else>
|
||||
<span class="gl-ml-0! gl-text-body! gl-flex-grow-1 gl-w-full gl-md-w-auto gl-mr-2">
|
||||
<bold-text :message="$options.i18n.shouldBeResolved" />
|
||||
<span v-if="!userPermissions.canMerge">
|
||||
{{ $options.i18n.usersWriteBranches }}
|
||||
</span>
|
||||
<bold-text v-if="userPermissions.canMerge" :message="$options.i18n.shouldBeResolved" />
|
||||
<bold-text v-else :message="$options.i18n.usersWriteBranches" />
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -277,11 +277,6 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
|
|||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2098
|
||||
.gl-max-w-0 {
|
||||
max-width: 0;
|
||||
}
|
||||
|
||||
.gl-isolate {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def reorder
|
||||
service = ::Issues::ReorderService.new(project: project, current_user: current_user, params: reorder_params)
|
||||
service = ::Issues::ReorderService.new(container: project, current_user: current_user, params: reorder_params)
|
||||
|
||||
if service.execute(issue)
|
||||
head :ok
|
||||
|
|
@ -216,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
|
||||
def related_branches
|
||||
@related_branches = ::Issues::RelatedBranchesService
|
||||
.new(project: project, current_user: current_user)
|
||||
.new(container: project, current_user: current_user)
|
||||
.execute(issue)
|
||||
.map { |branch| branch.merge(link: branch_link(branch)) }
|
||||
|
||||
|
|
@ -371,7 +371,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
|
||||
def update_service
|
||||
spam_params = ::Spam::SpamParams.new_from_request(request: request)
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: issue_params, spam_params: spam_params)
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: issue_params, spam_params: spam_params)
|
||||
end
|
||||
|
||||
def finder_type
|
||||
|
|
|
|||
|
|
@ -9,20 +9,39 @@ module Ci
|
|||
@pipeline_schedules = project.pipeline_schedules
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute(scope: nil)
|
||||
scoped_schedules =
|
||||
case scope
|
||||
when 'active'
|
||||
pipeline_schedules.active
|
||||
when 'inactive'
|
||||
pipeline_schedules.inactive
|
||||
else
|
||||
pipeline_schedules
|
||||
end
|
||||
def execute(scope: nil, ids: nil)
|
||||
items = pipeline_schedules
|
||||
items = by_ids(items, ids)
|
||||
items = by_scope(items, scope)
|
||||
|
||||
scoped_schedules.order(id: :desc)
|
||||
sort_items(items)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
def by_ids(items, ids)
|
||||
if ids.present?
|
||||
items.id_in(ids)
|
||||
else
|
||||
items
|
||||
end
|
||||
end
|
||||
|
||||
def by_scope(items, scope)
|
||||
case scope
|
||||
when 'active'
|
||||
items.active
|
||||
when 'inactive'
|
||||
items.inactive
|
||||
else
|
||||
items
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable CodeReuse/ActiveRecord
|
||||
def sort_items(items)
|
||||
items.order(id: :desc)
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ module Mutations
|
|||
|
||||
def assign!(resource, users, operation_mode)
|
||||
update_service_class.new(
|
||||
project: resource.project,
|
||||
**update_service_class.constructor_container_arg(resource.project),
|
||||
current_user: current_user,
|
||||
params: { assignee_ids: assignee_ids(resource, users, operation_mode) }
|
||||
).execute(resource)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ module Mutations
|
|||
# spam_params so a check can be performed.
|
||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: { confidential: confidential }, spam_params: spam_params)
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: { confidential: confidential }, spam_params: spam_params)
|
||||
.execute(issue)
|
||||
check_spam_action_response!(issue)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module Mutations
|
|||
issue = authorized_find!(project_path: project_path, iid: iid)
|
||||
project = issue.project
|
||||
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: { due_date: due_date })
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: { due_date: due_date })
|
||||
.execute(issue)
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module Mutations
|
|||
check_feature_availability!(issue)
|
||||
|
||||
::Issues::UpdateService.new(
|
||||
project: project,
|
||||
container: project,
|
||||
current_user: current_user,
|
||||
params: { escalation_status: { status: status } }
|
||||
).execute(issue)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module Mutations
|
|||
def resolve(project_path:, iid:, locked:)
|
||||
issue = authorized_find!(project_path: project_path, iid: iid)
|
||||
|
||||
::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: { discussion_locked: locked })
|
||||
::Issues::UpdateService.new(container: issue.project, current_user: current_user, params: { discussion_locked: locked })
|
||||
.execute(issue)
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ module Mutations
|
|||
issue = authorized_find!(project_path: project_path, iid: iid)
|
||||
project = issue.project
|
||||
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: { severity: severity })
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: { severity: severity })
|
||||
.execute(issue)
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ module Mutations
|
|||
args = parse_arguments(args)
|
||||
|
||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: args, spam_params: spam_params).execute(issue)
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: args, spam_params: spam_params).execute(issue)
|
||||
|
||||
{
|
||||
issue: issue,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ module Mutations
|
|||
interpret_quick_actions!(work_item, current_user, widget_params, attributes)
|
||||
|
||||
update_result = ::WorkItems::UpdateService.new(
|
||||
project: work_item.project,
|
||||
container: work_item.project,
|
||||
current_user: current_user,
|
||||
params: attributes,
|
||||
widget_params: widget_params,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ module Mutations
|
|||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||
|
||||
::WorkItems::UpdateService.new(
|
||||
project: task.project,
|
||||
container: task.project,
|
||||
current_user: current_user,
|
||||
params: task_data_hash.except(:id),
|
||||
spam_params: spam_params
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Resolvers
|
||||
class DataTransferResolver < BaseResolver
|
||||
argument :from, Types::DateType,
|
||||
description: 'Retain egress data for 1 year. Current month will increase dynamically as egress occurs.',
|
||||
required: false
|
||||
argument :to, Types::DateType,
|
||||
description: 'End date for the data.',
|
||||
required: false
|
||||
|
||||
type ::Types::DataTransfer::BaseType, null: false
|
||||
|
||||
def self.source
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.project
|
||||
Class.new(self) do
|
||||
type Types::DataTransfer::ProjectDataTransferType, null: false
|
||||
|
||||
def self.source
|
||||
"Project"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.group
|
||||
Class.new(self) do
|
||||
type Types::DataTransfer::GroupDataTransferType, null: false
|
||||
|
||||
def self.source
|
||||
"Group"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve(**_args)
|
||||
return unless Feature.enabled?(:data_transfer_monitoring)
|
||||
|
||||
start_date = Date.new(2023, 0o1, 0o1)
|
||||
date_for_index = ->(i) { (start_date + i.months).strftime('%Y-%m-%d') }
|
||||
|
||||
nodes = 0.upto(3).map do |i|
|
||||
{
|
||||
date: date_for_index.call(i),
|
||||
repository_egress: 250_000,
|
||||
artifacts_egress: 250_000,
|
||||
packages_egress: 250_000,
|
||||
registry_egress: 250_000
|
||||
}
|
||||
end
|
||||
|
||||
{ egress_nodes: nodes }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,8 +10,13 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Filter pipeline schedules by active status.'
|
||||
|
||||
def resolve(status: nil)
|
||||
::Ci::PipelineSchedulesFinder.new(project).execute(scope: status)
|
||||
argument :ids, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
default_value: nil,
|
||||
description: 'Filter pipeline schedules by IDs.'
|
||||
|
||||
def resolve(status: nil, ids: nil)
|
||||
::Ci::PipelineSchedulesFinder.new(project).execute(scope: status, ids: ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module DataTransfer
|
||||
class BaseType < BaseObject
|
||||
authorize
|
||||
|
||||
field :egress_nodes, type: Types::DataTransfer::EgressNodeType.connection_type,
|
||||
description: 'Data nodes.',
|
||||
null: true # disallow null once data_transfer_monitoring feature flag is rolled-out!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module DataTransfer
|
||||
class EgressNodeType < BaseObject
|
||||
authorize
|
||||
|
||||
field :date, GraphQL::Types::String,
|
||||
description: 'First day of the node range. There is one node per month.',
|
||||
null: false
|
||||
|
||||
field :total_egress, GraphQL::Types::BigInt,
|
||||
description: 'Total egress for that project in that period of time.',
|
||||
null: false
|
||||
|
||||
field :repository_egress, GraphQL::Types::BigInt,
|
||||
description: 'Repository egress for that project in that period of time.',
|
||||
null: false
|
||||
|
||||
field :artifacts_egress, GraphQL::Types::BigInt,
|
||||
description: 'Artifacts egress for that project in that period of time.',
|
||||
null: false
|
||||
|
||||
field :packages_egress, GraphQL::Types::BigInt,
|
||||
description: 'Packages egress for that project in that period of time.',
|
||||
null: false
|
||||
|
||||
field :registry_egress, GraphQL::Types::BigInt,
|
||||
description: 'Registery egress for that project in that period of time.',
|
||||
null: false
|
||||
|
||||
def total_egress
|
||||
object.values.select { |x| x.is_a?(Integer) }.sum
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module DataTransfer
|
||||
class GroupDataTransferType < BaseType
|
||||
graphql_name 'GroupDataTransfer'
|
||||
authorize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module DataTransfer
|
||||
class ProjectDataTransferType < BaseType
|
||||
graphql_name 'ProjectDataTransfer'
|
||||
authorize
|
||||
|
||||
field :total_egress, GraphQL::Types::BigInt,
|
||||
description: 'Total egress for that project in that period of time.',
|
||||
null: true # disallow null once data_transfer_monitoring feature flag is rolled-out!
|
||||
|
||||
def total_egress(**_)
|
||||
return unless Feature.enabled?(:data_transfer_monitoring)
|
||||
|
||||
40_000_000
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -239,6 +239,11 @@ module Types
|
|||
description: 'Releases belonging to projects in the group.',
|
||||
resolver: Resolvers::GroupReleasesResolver
|
||||
|
||||
field :data_transfer, Types::DataTransfer::GroupDataTransferType,
|
||||
null: true,
|
||||
resolver: Resolvers::DataTransferResolver.group,
|
||||
description: 'Data transfer data point for a specific period. This is mocked data under a development feature flag.'
|
||||
|
||||
def label(title:)
|
||||
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
|
||||
LabelsFinder
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ module Types
|
|||
def related_merge_requests
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
MergeRequest.where(
|
||||
id: ::Issues::ReferencedMergeRequestsService.new(project: object.project, current_user: current_user)
|
||||
id: ::Issues::ReferencedMergeRequestsService.new(container: object.project, current_user: current_user)
|
||||
.execute(object)
|
||||
.first
|
||||
.map(&:id)
|
||||
|
|
|
|||
|
|
@ -566,6 +566,11 @@ module Types
|
|||
resolver: ::Resolvers::Ci::ProjectRunnersResolver,
|
||||
description: "Find runners visible to the current user."
|
||||
|
||||
field :data_transfer, Types::DataTransfer::ProjectDataTransferType,
|
||||
null: true, # disallow null once data_transfer_monitoring feature flag is rolled-out!
|
||||
resolver: Resolvers::DataTransferResolver.project,
|
||||
description: 'Data transfer data point for a specific period. This is mocked data under a development feature flag.'
|
||||
|
||||
def timelog_categories
|
||||
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ module ApplicationSettingsHelper
|
|||
" using their classification label.")
|
||||
end
|
||||
|
||||
def external_authorization_allow_token_help_text
|
||||
s_("ExternalAuthorization|Does not apply if service URL is specified.")
|
||||
end
|
||||
|
||||
def external_authorization_timeout_help_text
|
||||
s_("ExternalAuthorization|Period GitLab waits for a response from the external "\
|
||||
"service. If there is no response, access is denied. Default: 0.5 seconds.")
|
||||
|
|
@ -499,7 +503,8 @@ module ApplicationSettingsHelper
|
|||
:external_authorization_service_default_label,
|
||||
:external_authorization_service_enabled,
|
||||
:external_authorization_service_timeout,
|
||||
:external_authorization_service_url
|
||||
:external_authorization_service_url,
|
||||
:allow_deploy_tokens_and_keys_with_external_authn
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
module Ci
|
||||
class GroupVariable < Ci::ApplicationRecord
|
||||
include Ci::HasVariable
|
||||
include Presentable
|
||||
include Ci::Maskable
|
||||
include Ci::RawVariable
|
||||
include Limitable
|
||||
include Presentable
|
||||
|
||||
prepend HasEnvironmentScope
|
||||
|
||||
belongs_to :group, class_name: "::Group"
|
||||
|
|
@ -21,6 +23,9 @@ module Ci
|
|||
scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
|
||||
scope :for_groups, ->(group_ids) { where(group_id: group_ids) }
|
||||
|
||||
self.limit_name = 'group_ci_variables'
|
||||
self.limit_scope = :group
|
||||
|
||||
def audit_details
|
||||
key
|
||||
end
|
||||
|
|
|
|||
|
|
@ -134,9 +134,6 @@ module Ci
|
|||
belongs_to :project
|
||||
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
|
||||
|
||||
# We will start using this column once we complete https://gitlab.com/gitlab-org/gitlab/-/issues/285597
|
||||
ignore_column :original_filename, remove_with: '14.7', remove_after: '2022-11-22'
|
||||
|
||||
mount_file_store_uploader JobArtifactUploader, skip_store_file: true
|
||||
|
||||
before_save :set_size, if: :file_changed?
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
module Ci
|
||||
class Variable < Ci::ApplicationRecord
|
||||
include Ci::HasVariable
|
||||
include Presentable
|
||||
include Ci::Maskable
|
||||
include Ci::RawVariable
|
||||
include Limitable
|
||||
include Presentable
|
||||
|
||||
prepend HasEnvironmentScope
|
||||
|
||||
belongs_to :project
|
||||
|
|
@ -20,6 +22,9 @@ module Ci
|
|||
scope :unprotected, -> { where(protected: false) }
|
||||
scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
|
||||
|
||||
self.limit_name = 'project_ci_variables'
|
||||
self.limit_scope = :project
|
||||
|
||||
def audit_details
|
||||
key
|
||||
end
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ module Boards
|
|||
end
|
||||
|
||||
def update(issue, issue_modification_params)
|
||||
::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue)
|
||||
::Issues::UpdateService.new(container: issue.project, current_user: current_user, params: issue_modification_params).execute(issue)
|
||||
end
|
||||
|
||||
def moving_to_list_items_relation
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ module ErrorTracking
|
|||
|
||||
def close_issue(issue)
|
||||
Issues::CloseService
|
||||
.new(project: project, current_user: current_user)
|
||||
.new(container: project, current_user: current_user)
|
||||
.execute(issue, system_note: false)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ module Issuable
|
|||
end
|
||||
|
||||
def close_issue
|
||||
close_service = Issues::CloseService.new(project: old_project, current_user: current_user)
|
||||
close_service = Issues::CloseService.new(container: old_project, current_user: current_user)
|
||||
close_service.execute(original_entity, notifications: false, system_note: true)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Issues
|
||||
class AfterCreateService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
def execute(issue)
|
||||
todo_service.new_issue(issue, current_user)
|
||||
delete_milestone_total_issue_counter_cache(issue.milestone)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Issues
|
||||
class CloseService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
# Closes the supplied issue if the current user is able to do so.
|
||||
def execute(issue, commit: nil, notifications: true, system_note: true, skip_authorization: false)
|
||||
return issue unless can_close?(issue, skip_authorization: skip_authorization)
|
||||
|
|
@ -51,6 +56,11 @@ module Issues
|
|||
|
||||
private
|
||||
|
||||
# TODO: remove once MergeRequests::CloseService or IssuableBaseService method is changed.
|
||||
def self.constructor_container_arg(value)
|
||||
{ container: value }
|
||||
end
|
||||
|
||||
def can_close?(issue, skip_authorization: false)
|
||||
skip_authorization || can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Issues
|
||||
class DuplicateService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
def execute(duplicate_issue, canonical_issue)
|
||||
return if canonical_issue == duplicate_issue
|
||||
return unless can?(current_user, :update_issue, duplicate_issue)
|
||||
|
|
@ -10,7 +15,7 @@ module Issues
|
|||
create_issue_duplicate_note(duplicate_issue, canonical_issue)
|
||||
create_issue_canonical_note(canonical_issue, duplicate_issue)
|
||||
|
||||
close_service.new(project: project, current_user: current_user).execute(duplicate_issue)
|
||||
close_service.new(container: project, current_user: current_user).execute(duplicate_issue)
|
||||
duplicate_issue.update(duplicated_to: canonical_issue)
|
||||
|
||||
relate_two_issues(duplicate_issue, canonical_issue)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Issues
|
||||
class ReferencedMergeRequestsService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute(issue)
|
||||
referenced = referenced_merge_requests(issue)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
# those with a merge request open referencing the current issue.
|
||||
module Issues
|
||||
class RelatedBranchesService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
def execute(issue)
|
||||
branch_names_with_mrs = branches_with_merge_request_for(issue)
|
||||
branches = branches_with_iid_of(issue).reject { |b| branch_names_with_mrs.include?(b[:name]) }
|
||||
|
|
@ -27,7 +32,7 @@ module Issues
|
|||
|
||||
def branches_with_merge_request_for(issue)
|
||||
Issues::ReferencedMergeRequestsService
|
||||
.new(project: project, current_user: current_user)
|
||||
.new(container: project, current_user: current_user)
|
||||
.referenced_merge_requests(issue)
|
||||
.map(&:source_branch)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Issues
|
||||
class ReopenService < Issues::BaseService
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
def execute(issue, skip_authorization: false)
|
||||
return issue unless can_reopen?(issue, skip_authorization: skip_authorization)
|
||||
|
||||
|
|
@ -22,6 +27,14 @@ module Issues
|
|||
|
||||
private
|
||||
|
||||
# overriding this because IssuableBaseService#constructor_container_arg returns { project: value }
|
||||
# Issues::ReopenService constructor signature is different now, it takes container instead of project also
|
||||
# IssuableBaseService#change_state dynamically picks one of the `Issues::ReopenService`, `Epics::ReopenService` or
|
||||
# MergeRequests::ReopenService, so we need this method to return { }container: value } for Issues::ReopenService
|
||||
def self.constructor_container_arg(value)
|
||||
{ container: value }
|
||||
end
|
||||
|
||||
def can_reopen?(issue, skip_authorization: false)
|
||||
skip_authorization || can?(current_user, :reopen_issue, issue)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ module Issues
|
|||
class ReorderService < Issues::BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
# TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
|
||||
def initialize(container:, current_user: nil, params: {})
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
end
|
||||
|
||||
def execute(issue)
|
||||
return false unless can?(current_user, :update_issue, issue)
|
||||
return false unless move_between_ids
|
||||
|
|
@ -14,7 +19,7 @@ module Issues
|
|||
private
|
||||
|
||||
def update(issue, attrs)
|
||||
::Issues::UpdateService.new(project: project, current_user: current_user, params: attrs).execute(issue)
|
||||
::Issues::UpdateService.new(container: project, current_user: current_user, params: attrs).execute(issue)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ module Issues
|
|||
# NOTE: For Issues::UpdateService, we default the spam_params to nil, because spam_checking is not
|
||||
# necessary in many cases, and we don't want to require every caller to explicitly pass it as nil
|
||||
# to disable spam checking.
|
||||
def initialize(project:, current_user: nil, params: {}, spam_params: nil)
|
||||
super(project: project, current_user: current_user, params: params)
|
||||
def initialize(container:, current_user: nil, params: {}, spam_params: nil)
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
@spam_params = spam_params
|
||||
end
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ module Issues
|
|||
canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id)
|
||||
|
||||
if canonical_issue
|
||||
Issues::DuplicateService.new(project: project, current_user: current_user).execute(issue, canonical_issue)
|
||||
Issues::DuplicateService.new(container: project, current_user: current_user).execute(issue, canonical_issue)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
@ -116,6 +116,15 @@ module Issues
|
|||
|
||||
attr_reader :spam_params
|
||||
|
||||
# TODO: remove this once MergeRequests::UpdateService#initialize is changed to take container as named argument.
|
||||
#
|
||||
# Issues::UpdateService is used together with MergeRequests::UpdateService in Mutations::Assignable#assign! method
|
||||
# however MergeRequests::UpdateService#initialize still takes `project` as param and Issues::UpdateService is being
|
||||
# changed to take `container` as param. So we are adding this workaround in the meantime.
|
||||
def self.constructor_container_arg(value)
|
||||
{ container: value }
|
||||
end
|
||||
|
||||
def handle_quick_actions(issue)
|
||||
# Do not handle quick actions unless the work item is the default Issue.
|
||||
# The available quick actions for a work item depend on its type and widgets.
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
module Issues
|
||||
class ZoomLinkService < Issues::BaseService
|
||||
def initialize(project:, current_user:, params:)
|
||||
super
|
||||
def initialize(container:, current_user:, params:)
|
||||
super(project: container, current_user: current_user, params: params)
|
||||
|
||||
@issue = params.fetch(:issue)
|
||||
@added_meeting = ZoomMeeting.canonical_meeting(@issue)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module MergeRequests
|
|||
|
||||
def execute
|
||||
assignable_issues.each do |issue|
|
||||
Issues::UpdateService.new(project: issue.project, current_user: current_user, params: { assignee_ids: [current_user.id] }).execute(issue)
|
||||
Issues::UpdateService.new(container: issue.project, current_user: current_user, params: { assignee_ids: [current_user.id] }).execute(issue)
|
||||
end
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ module MergeRequests
|
|||
merge_request.id
|
||||
)
|
||||
else
|
||||
Issues::CloseService.new(project: project, current_user: current_user).execute(issue, commit: merge_request)
|
||||
Issues::CloseService.new(container: project, current_user: current_user).execute(issue, commit: merge_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Milestones
|
|||
update_params = { milestone_id: nil, skip_milestone_email: true }
|
||||
|
||||
milestone.issues.each do |issue|
|
||||
Issues::UpdateService.new(project: parent, current_user: current_user, params: update_params).execute(issue)
|
||||
Issues::UpdateService.new(container: parent, current_user: current_user, params: update_params).execute(issue)
|
||||
end
|
||||
|
||||
milestone.merge_requests.each do |merge_request|
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module TasksToBeDone
|
|||
|
||||
def execute
|
||||
if (issue = existing_task_issue)
|
||||
update_service = Issues::UpdateService.new(project: project, current_user: current_user, params: { add_assignee_ids: params[:assignee_ids] })
|
||||
update_service = Issues::UpdateService.new(container: project, current_user: current_user, params: { add_assignee_ids: params[:assignee_ids] })
|
||||
update_service.execute(issue)
|
||||
else
|
||||
build_service = Issues::BuildService.new(container: project, current_user: current_user, params: params)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ module WorkItems
|
|||
end
|
||||
|
||||
::WorkItems::UpdateService.new(
|
||||
project: @work_item.project,
|
||||
container: @work_item.project,
|
||||
current_user: @current_user,
|
||||
params: { description: source_lines.join("\n"), lock_version: @lock_version }
|
||||
).execute(@work_item)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module WorkItems
|
|||
remove_additional_lines!(source_lines)
|
||||
|
||||
::WorkItems::UpdateService.new(
|
||||
project: @work_item.project,
|
||||
container: @work_item.project,
|
||||
current_user: @current_user,
|
||||
params: { description: source_lines.join("\n"), lock_version: @lock_version }
|
||||
).execute(@work_item)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ module WorkItems
|
|||
class UpdateService < ::Issues::UpdateService
|
||||
include WidgetableService
|
||||
|
||||
def initialize(project:, current_user: nil, params: {}, spam_params: nil, widget_params: {})
|
||||
def initialize(container:, current_user: nil, params: {}, spam_params: nil, widget_params: {})
|
||||
params[:widget_params] = true if widget_params.present?
|
||||
|
||||
super(project: project, current_user: current_user, params: params, spam_params: nil)
|
||||
super(container: container, current_user: current_user, params: params, spam_params: spam_params)
|
||||
|
||||
@widget_params = widget_params
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
= f.gitlab_ui_checkbox_component :external_authorization_service_enabled,
|
||||
s_('ExternalAuthorization|Enable classification control using an external service'),
|
||||
help_text: external_authorization_description
|
||||
= f.gitlab_ui_checkbox_component :allow_deploy_tokens_and_keys_with_external_authn,
|
||||
s_('ExternalAuthorization|Allow deploy tokens and deploy keys to be used with external authorization'),
|
||||
help_text: external_authorization_allow_token_help_text
|
||||
.form-group
|
||||
= f.label :external_authorization_service_url, s_('ExternalAuthorization|Service URL'), class: 'label-bold'
|
||||
= f.text_field :external_authorization_service_url, class: 'form-control gl-form-input'
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ module IncidentManagement
|
|||
|
||||
def close_incident(incident)
|
||||
::Issues::CloseService
|
||||
.new(project: incident.project, current_user: user)
|
||||
.new(container: incident.project, current_user: user)
|
||||
.execute(incident, system_note: false)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ module Issues
|
|||
end
|
||||
|
||||
commit = Commit.build_from_sidekiq_hash(project, params["commit_hash"])
|
||||
service = Issues::CloseService.new(project: project, current_user: author)
|
||||
service = Issues::CloseService.new(container: project, current_user: author)
|
||||
|
||||
service.execute(issue, commit: commit)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ module MergeRequests
|
|||
end
|
||||
|
||||
Issues::CloseService
|
||||
.new(project: project, current_user: user)
|
||||
.new(container: project, current_user: user)
|
||||
.execute(issue, commit: merge_request)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
issuable.create_cross_references!(user)
|
||||
|
||||
Issues::AfterCreateService
|
||||
.new(project: issuable.project, current_user: user)
|
||||
.new(container: issuable.project, current_user: user)
|
||||
.execute(issuable)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: data_transfer_monitoring
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110211
|
||||
rollout_issue_url:
|
||||
milestone: '15.9'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
||||
|
|
@ -903,6 +903,8 @@ Settings['repositories'] ||= Settingslogic.new({})
|
|||
Settings.repositories['storages'] ||= {}
|
||||
|
||||
Settings.repositories.storages.each do |key, storage|
|
||||
next if Settings.repositories.storages[key].is_a?(Gitlab::GitalyClient::StorageSettings)
|
||||
|
||||
Settings.repositories.storages[key] = Gitlab::GitalyClient::StorageSettings.new(storage)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FastGettext.default_available_locales = Gitlab::I18n.available_locales
|
||||
I18n.available_locales = Gitlab::I18n.available_locales
|
||||
|
|
@ -1,31 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
translation_repositories = [
|
||||
FastGettext::TranslationRepository.build(
|
||||
'gitlab',
|
||||
path: File.join(Rails.root, 'locale'),
|
||||
type: :po,
|
||||
ignore_fuzzy: true
|
||||
)
|
||||
]
|
||||
|
||||
Gitlab.jh do
|
||||
translation_repositories.unshift(
|
||||
FastGettext::TranslationRepository.build(
|
||||
'gitlab',
|
||||
path: File.join(Rails.root, 'jh', 'locale'),
|
||||
type: :po,
|
||||
ignore_fuzzy: true
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
FastGettext.add_text_domain(
|
||||
'gitlab',
|
||||
type: :chain,
|
||||
chain: translation_repositories,
|
||||
ignore_fuzzy: true
|
||||
)
|
||||
|
||||
FastGettext.default_text_domain = 'gitlab'
|
||||
FastGettext.default_locale = :en
|
||||
Gitlab::I18n.setup(domain: 'gitlab', default_locale: :en)
|
||||
|
|
|
|||
|
|
@ -178,8 +178,12 @@ class ObjectStoreSettings
|
|||
# 1. The common settings are defined
|
||||
# 2. The legacy settings are not defined
|
||||
def use_consolidated_settings?
|
||||
return false unless settings.dig('object_store', 'enabled')
|
||||
return false unless settings.dig('object_store', 'connection').present?
|
||||
# to_h is needed because we define `default` as a Gitaly storage name
|
||||
# in stub_storage_settings. This causes Settingslogic to redefine Hash#default,
|
||||
# which causes Hash#dig to fail when the key doesn't exist: https://gitlab.com/gitlab-org/gitlab/-/issues/286873
|
||||
settings_h = settings.to_h
|
||||
return false unless settings_h.dig('object_store', 'enabled')
|
||||
return false unless settings_h.dig('object_store', 'connection').present?
|
||||
|
||||
WORKHORSE_ACCELERATED_TYPES.each do |store|
|
||||
# to_h is needed because we define `default` as a Gitaly storage name
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAllowDeployTokensAndKeysWithExternalAuthnToApplicationSettings < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
add_column(:application_settings, :allow_deploy_tokens_and_keys_with_external_authn, :boolean,
|
||||
default: false, null: false)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveTextLimitFromCiJobArtifactsOriginalFilename < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# In preparation for 20230214142813_remove_ci_job_artifacts_original_filename.rb
|
||||
# We first remove the text limit before removing the column.
|
||||
# This is to properly reverse the 2-step migration to add a text column with limit
|
||||
# https://docs.gitlab.com/ee/development/database/strings_and_the_text_data_type.html#add-a-text-column-to-an-existing-table
|
||||
remove_text_limit :ci_job_artifacts, :original_filename
|
||||
end
|
||||
|
||||
def down
|
||||
add_text_limit :ci_job_artifacts, :original_filename, 512
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveCiJobArtifactsOriginalFilename < Gitlab::Database::Migration[2.1]
|
||||
enable_lock_retries!
|
||||
|
||||
def up
|
||||
# This column has never been used and has always been under ignore_column since it was added.
|
||||
# We're doing the removal of the ignore_column in the same MR with this migration and this
|
||||
# is why we are not doing this in post migrate.
|
||||
remove_column :ci_job_artifacts, :original_filename, :text # rubocop:disable Migration/RemoveColumn
|
||||
end
|
||||
|
||||
def down
|
||||
add_column :ci_job_artifacts, :original_filename, :text
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
9e7245187ad1618304f2cdc901a6d8f63e63d007578da92f7ba049def9312923
|
||||
|
|
@ -0,0 +1 @@
|
|||
29006be848d8a5ba33c0e757ac4743cc19dc0274893e2e23c73615218975feef
|
||||
|
|
@ -0,0 +1 @@
|
|||
c7f6778eb181c6c4e97b7d7698bb7df5a4589710426d0a6574d5230f9751ebed
|
||||
|
|
@ -11722,6 +11722,7 @@ CREATE TABLE application_settings (
|
|||
deactivation_email_additional_text text,
|
||||
jira_connect_public_key_storage_enabled boolean DEFAULT false NOT NULL,
|
||||
git_rate_limit_users_alertlist integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
allow_deploy_tokens_and_keys_with_external_authn boolean DEFAULT false NOT NULL,
|
||||
security_policy_global_group_approvers_enabled boolean DEFAULT true 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)),
|
||||
|
|
@ -13141,11 +13142,9 @@ CREATE TABLE ci_job_artifacts (
|
|||
id bigint NOT NULL,
|
||||
job_id bigint NOT NULL,
|
||||
locked smallint DEFAULT 2,
|
||||
original_filename text,
|
||||
partition_id bigint DEFAULT 100 NOT NULL,
|
||||
accessibility smallint DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT check_27f0f6dbab CHECK ((file_store IS NOT NULL)),
|
||||
CONSTRAINT check_85573000db CHECK ((char_length(original_filename) <= 512))
|
||||
CONSTRAINT check_27f0f6dbab CHECK ((file_store IS NOT NULL))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_job_artifacts_id_seq
|
||||
|
|
|
|||
|
|
@ -220,6 +220,8 @@ An [epic exists](https://gitlab.com/groups/gitlab-org/-/epics/4624) to fix this
|
|||
|
||||
Keep in mind that mentioned URLs don't work when [Admin Mode](../../user/admin_area/settings/sign_in_restrictions.md#admin-mode) is enabled.
|
||||
|
||||
When using Unified URL, visiting the secondary site directly means you must route your requests to the secondary site. Exactly how this might be done depends on your networking configuration. If using DNS to route requests to the appropriate site, then you can, for example, edit your local machine's `/etc/hosts` file to route your requests to the desired secondary site. If the Geo sites are all behind a load balancer, then depending on the load balancer, you might be able to configure all requests from your IP to go to a particular secondary site.
|
||||
|
||||
## Setup instructions
|
||||
|
||||
For setup instructions, see [Setting up Geo](setup/index.md).
|
||||
|
|
|
|||
|
|
@ -7843,6 +7843,29 @@ The edge type for [`Discussion`](#discussion).
|
|||
| <a id="discussionedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="discussionedgenode"></a>`node` | [`Discussion`](#discussion) | The item at the end of the edge. |
|
||||
|
||||
#### `EgressNodeConnection`
|
||||
|
||||
The connection type for [`EgressNode`](#egressnode).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="egressnodeconnectionedges"></a>`edges` | [`[EgressNodeEdge]`](#egressnodeedge) | A list of edges. |
|
||||
| <a id="egressnodeconnectionnodes"></a>`nodes` | [`[EgressNode]`](#egressnode) | A list of nodes. |
|
||||
| <a id="egressnodeconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `EgressNodeEdge`
|
||||
|
||||
The edge type for [`EgressNode`](#egressnode).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="egressnodeedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="egressnodeedgenode"></a>`node` | [`EgressNode`](#egressnode) | The item at the end of the edge. |
|
||||
|
||||
#### `EmailConnection`
|
||||
|
||||
The connection type for [`Email`](#email).
|
||||
|
|
@ -12877,6 +12900,19 @@ Returns [`[DoraMetric!]`](#dorametric).
|
|||
| <a id="dorametricdate"></a>`date` | [`String`](#string) | Date of the data point. |
|
||||
| <a id="dorametricvalue"></a>`value` | [`Float`](#float) | Value of the data point. |
|
||||
|
||||
### `EgressNode`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="egressnodeartifactsegress"></a>`artifactsEgress` | [`BigInt!`](#bigint) | Artifacts egress for that project in that period of time. |
|
||||
| <a id="egressnodedate"></a>`date` | [`String!`](#string) | First day of the node range. There is one node per month. |
|
||||
| <a id="egressnodepackagesegress"></a>`packagesEgress` | [`BigInt!`](#bigint) | Packages egress for that project in that period of time. |
|
||||
| <a id="egressnoderegistryegress"></a>`registryEgress` | [`BigInt!`](#bigint) | Registery egress for that project in that period of time. |
|
||||
| <a id="egressnoderepositoryegress"></a>`repositoryEgress` | [`BigInt!`](#bigint) | Repository egress for that project in that period of time. |
|
||||
| <a id="egressnodetotalegress"></a>`totalEgress` | [`BigInt!`](#bigint) | Total egress for that project in that period of time. |
|
||||
|
||||
### `Email`
|
||||
|
||||
#### Fields
|
||||
|
|
@ -14079,6 +14115,19 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="groupcontributionsfrom"></a>`from` | [`ISO8601Date!`](#iso8601date) | Start date of the reporting time range. |
|
||||
| <a id="groupcontributionsto"></a>`to` | [`ISO8601Date!`](#iso8601date) | End date of the reporting time range. The end date must be within 31 days after the start date. |
|
||||
|
||||
##### `Group.dataTransfer`
|
||||
|
||||
Data transfer data point for a specific period. This is mocked data under a development feature flag.
|
||||
|
||||
Returns [`GroupDataTransfer`](#groupdatatransfer).
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupdatatransferfrom"></a>`from` | [`Date`](#date) | Retain egress data for 1 year. Current month will increase dynamically as egress occurs. |
|
||||
| <a id="groupdatatransferto"></a>`to` | [`Date`](#date) | End date for the data. |
|
||||
|
||||
##### `Group.descendantGroups`
|
||||
|
||||
List of descendant groups of this group.
|
||||
|
|
@ -14688,6 +14737,14 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="groupworkitemtypestaskable"></a>`taskable` | [`Boolean`](#boolean) | If `true`, only taskable work item types will be returned. Argument is experimental and can be removed in the future without notice. |
|
||||
|
||||
### `GroupDataTransfer`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupdatatransferegressnodes"></a>`egressNodes` | [`EgressNodeConnection`](#egressnodeconnection) | Data nodes. (see [Connections](#connections)) |
|
||||
|
||||
### `GroupMember`
|
||||
|
||||
Represents a Group Membership.
|
||||
|
|
@ -17979,6 +18036,19 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="projectdastsitevalidationsnormalizedtargeturls"></a>`normalizedTargetUrls` | [`[String!]`](#string) | Normalized URL of the target to be scanned. |
|
||||
| <a id="projectdastsitevalidationsstatus"></a>`status` | [`DastSiteValidationStatusEnum`](#dastsitevalidationstatusenum) | Status of the site validation. |
|
||||
|
||||
##### `Project.dataTransfer`
|
||||
|
||||
Data transfer data point for a specific period. This is mocked data under a development feature flag.
|
||||
|
||||
Returns [`ProjectDataTransfer`](#projectdatatransfer).
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectdatatransferfrom"></a>`from` | [`Date`](#date) | Retain egress data for 1 year. Current month will increase dynamically as egress occurs. |
|
||||
| <a id="projectdatatransferto"></a>`to` | [`Date`](#date) | End date for the data. |
|
||||
|
||||
##### `Project.deployment`
|
||||
|
||||
Details of the deployment of the project.
|
||||
|
|
@ -18561,6 +18631,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectpipelineschedulesids"></a>`ids` | [`[ID!]`](#id) | Filter pipeline schedules by IDs. |
|
||||
| <a id="projectpipelineschedulesstatus"></a>`status` | [`PipelineScheduleStatus`](#pipelineschedulestatus) | Filter pipeline schedules by active status. |
|
||||
|
||||
##### `Project.pipelines`
|
||||
|
|
@ -18986,6 +19057,15 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="projectcicdsettingoptinjwt"></a>`optInJwt` | [`Boolean`](#boolean) | When disabled, the JSON Web Token is always available in all jobs in the pipeline. |
|
||||
| <a id="projectcicdsettingproject"></a>`project` | [`Project`](#project) | Project the CI/CD settings belong to. |
|
||||
|
||||
### `ProjectDataTransfer`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectdatatransferegressnodes"></a>`egressNodes` | [`EgressNodeConnection`](#egressnodeconnection) | Data nodes. (see [Connections](#connections)) |
|
||||
| <a id="projectdatatransfertotalegress"></a>`totalEgress` | [`BigInt`](#bigint) | Total egress for that project in that period of time. |
|
||||
|
||||
### `ProjectMember`
|
||||
|
||||
Represents a Project Membership.
|
||||
|
|
|
|||
|
|
@ -373,6 +373,14 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
|
||||
Sites that have configured `max_concurrency` will not be affected by this change.
|
||||
[Read more about the Sidekiq concurrency setting](../administration/sidekiq/extra_sidekiq_processes.md#concurrency).
|
||||
- GitLab Runner 15.7.0 introduced a breaking change that impacts CI/CD jobs: [Correctly handle expansion of job file variables](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3613).
|
||||
Previously, job-defined variables that referred to
|
||||
[file type variables](../ci/variables/index.md#use-file-type-cicd-variables)
|
||||
were expanded to the value of the file variable (its content). This behavior did not
|
||||
respect the typical rules of shell variable expansion. There was also the potential
|
||||
that secrets or sensitive information could leak if the file variable and its
|
||||
contents printed. For example, if they were printed in an echo output. For more information,
|
||||
see [Understanding the file type variable expansion change in GitLab 15.7](https://about.gitlab.com/blog/2023/02/13/impact-of-the-file-type-variable-change-15-7/).
|
||||
- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover.
|
||||
- Due to [a bug introduced in GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/issues/390155), if one or more Git repositories in Gitaly Cluster is [unavailable](../administration/gitaly/recovery.md#unavailable-repositories), then [Repository checks](../administration/repository_checks.md#repository-checks) and [Geo replication and verification](../administration/geo/index.md) stop running for all project or project wiki repositories in the affected Gitaly Cluster. The bug was fixed by [reverting the change in GitLab 15.9.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110823). Before upgrading to this version, check if you have any "unavailable" repositories. See [the bug issue](https://gitlab.com/gitlab-org/gitlab/-/issues/390155) for more information.
|
||||
- Geo: We discovered an issue where [replication and verification of projects and wikis was not keeping up](https://gitlab.com/gitlab-org/gitlab/-/issues/387980) on small number of Geo installations. Your installation may be affected if you see some projects and/or wikis persistently in the "Queued" state for verification. This can lead to data loss after a failover.
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ For user contributions to be mapped, each user must complete the following befor
|
|||
|
||||
## Import your Bitbucket repositories
|
||||
|
||||
> Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
||||
1. Sign in to GitLab.
|
||||
1. On the top bar, select **New** (**{plus}**).
|
||||
1. Select **New project/repository**.
|
||||
|
|
@ -78,7 +80,9 @@ For user contributions to be mapped, each user must complete the following befor
|
|||
You can filter projects by name and select the namespace
|
||||
each project is imported for.
|
||||
|
||||

|
||||
1. To import a project:
|
||||
- For the first time: Select **Import**.
|
||||
- Again: Select **Re-import**. Specify a new name and select **Re-import** again. Re-importing creates a new copy of the source project.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ created as private in GitLab as well.
|
|||
|
||||
## Import your Bitbucket repositories
|
||||
|
||||
> Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- An administrator must enable **Bitbucket Server** in **Admin > Settings > General > Visibility and access controls > Import sources**.
|
||||
|
|
@ -40,6 +42,9 @@ To import your Bitbucket repositories:
|
|||
1. Log in to Bitbucket and grant GitLab access to your Bitbucket account.
|
||||
1. Select the projects to import, or import all projects. You can filter projects by name and select
|
||||
the namespace for which to import each project.
|
||||
1. To import a project:
|
||||
- For the first time: Select **Import**.
|
||||
- Again: Select **Re-import**. Specify a new name and select **Re-import** again. Re-importing creates a new copy of the source project.
|
||||
|
||||
### Items that are not imported
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Import your project from FogBugz to GitLab **(FREE)**
|
||||
|
||||
> Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
||||
Using the importer, you can import your FogBugz project to GitLab.com
|
||||
or to your self-managed GitLab instance.
|
||||
|
||||
|
|
@ -33,4 +35,6 @@ To import your project from FogBugz:
|
|||

|
||||
1. After the import finishes, select the link to go to the project
|
||||
dashboard. Follow the directions to push your existing repository.
|
||||

|
||||
1. To import a project:
|
||||
- For the first time: Select **Import**.
|
||||
- Again: Select **Re-import**. Specify a new name and select **Re-import** again. Re-importing creates a new copy of the source project.
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ From there, you can view the import statuses of your Gitea repositories:
|
|||
|
||||
- Those that are being imported show a _started_ status.
|
||||
- Those already successfully imported are green with a _done_ status.
|
||||
- Those that aren't yet imported have an **Import** button on the
|
||||
right side of the table.
|
||||
- Those that aren't yet imported have **Import** on the right side of the table.
|
||||
- Those that are already imported have **Re-import** on the right side of the table.
|
||||
|
||||
You also can:
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ You can choose to import these items, but this could significantly increase impo
|
|||
|
||||
### Select which repositories to import
|
||||
|
||||
> Ability to cancel pending or active imports [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247325) in GitLab 15.7.
|
||||
> - Ability to cancel pending or active imports [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247325) in GitLab 15.7.
|
||||
> - Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
||||
After you have authorized access to your GitHub repositories, you are redirected to the GitHub importer page and
|
||||
your GitHub repositories are listed.
|
||||
|
|
@ -168,6 +169,8 @@ If the import has already started, the imported files are kept.
|
|||
|
||||
To open an repository in GitLab URL after it has been imported, select its GitLab path.
|
||||
|
||||
Completed imports can be re-imported by selecting **Re-import** and specifying new name. This creates a new copy of the source project.
|
||||
|
||||

|
||||
|
||||
## Mirror a repository and share pipeline status **(PREMIUM)**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Import multiple repositories by uploading a manifest file **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/28811) in GitLab 11.2.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/28811) in GitLab 11.2.
|
||||
> - Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
||||
GitLab allows you to import all the required Git repositories
|
||||
based on a manifest file like the one used by the
|
||||
|
|
@ -59,6 +60,6 @@ To start the import:
|
|||
1. Select a group you want to import to (you need to create a group first if you don't have one).
|
||||
1. Select **List available repositories**. At this point, you are redirected
|
||||
to the import status page with projects list based on the manifest file.
|
||||
1. Check the list and select **Import all repositories** to start the import.
|
||||
|
||||

|
||||
1. To import:
|
||||
- All projects for the first time: Select **Import all repositories**.
|
||||
- Individual projects again: Select **Re-import**. Specify a new name and select **Re-import** again. Re-importing creates a new copy of the source project.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ module API
|
|||
def delete_draft_note(draft_note)
|
||||
::DraftNotes::DestroyService.new(user_project, current_user).execute(draft_note)
|
||||
end
|
||||
|
||||
def publish_draft_note(params:)
|
||||
::DraftNotes::PublishService
|
||||
.new(merge_request(params: params), current_user)
|
||||
.execute(get_draft_note(params: params))
|
||||
end
|
||||
end
|
||||
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
|
|
@ -90,6 +96,31 @@ module API
|
|||
not_found!("Draft Note")
|
||||
end
|
||||
end
|
||||
|
||||
desc "Publish a pending draft note" do
|
||||
success code: 204
|
||||
failure [
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
end
|
||||
params do
|
||||
requires :id, type: String, desc: "The ID of a project"
|
||||
requires :merge_request_iid, type: Integer, desc: "The ID of a merge request"
|
||||
requires :draft_note_id, type: Integer, desc: "The ID of a draft note"
|
||||
end
|
||||
put(
|
||||
":id/merge_requests/:merge_request_iid/draft_notes/:draft_note_id/publish",
|
||||
feature_category: :code_review_workflow) do
|
||||
result = publish_draft_note(params: params)
|
||||
|
||||
if result[:status] == :success
|
||||
status 204
|
||||
body false
|
||||
else
|
||||
status 500
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ module API
|
|||
update_params = convert_parameters_from_legacy_format(update_params)
|
||||
|
||||
spam_params = ::Spam::SpamParams.new_from_request(request: request)
|
||||
issue = ::Issues::UpdateService.new(project: user_project,
|
||||
issue = ::Issues::UpdateService.new(container: user_project,
|
||||
current_user: current_user,
|
||||
params: update_params,
|
||||
spam_params: spam_params).execute(issue)
|
||||
|
|
@ -350,7 +350,7 @@ module API
|
|||
|
||||
authorize! :update_issue, issue
|
||||
|
||||
if ::Issues::ReorderService.new(project: user_project, current_user: current_user, params: params).execute(issue)
|
||||
if ::Issues::ReorderService.new(container: user_project, current_user: current_user, params: params).execute(issue)
|
||||
present issue, with: Entities::Issue, current_user: current_user, project: user_project
|
||||
else
|
||||
render_api_error!({ error: 'Unprocessable Entity' }, 422)
|
||||
|
|
@ -438,9 +438,10 @@ module API
|
|||
get ':id/issues/:issue_iid/related_merge_requests' do
|
||||
issue = find_project_issue(params[:issue_iid])
|
||||
|
||||
merge_requests = ::Issues::ReferencedMergeRequestsService.new(project: user_project, current_user: current_user)
|
||||
.execute(issue)
|
||||
.first
|
||||
merge_requests = ::Issues::ReferencedMergeRequestsService
|
||||
.new(container: user_project, current_user: current_user)
|
||||
.execute(issue)
|
||||
.first
|
||||
|
||||
present paginate(::Kaminari.paginate_array(merge_requests)),
|
||||
with: Entities::MergeRequest,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ module API
|
|||
custom_params = declared_params(include_missing: false)
|
||||
custom_params.merge!(attrs)
|
||||
|
||||
issuable = update_service.new(project: user_project, current_user: current_user, params: custom_params).execute(load_issuable)
|
||||
issuable = update_service.new(**update_service.constructor_container_arg(user_project),
|
||||
current_user: current_user, params: custom_params).execute(load_issuable)
|
||||
|
||||
if issuable.valid?
|
||||
present issuable, with: Entities::IssuableTimeStats
|
||||
else
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def with_deploy_token(raw, &block)
|
||||
raise ::Gitlab::Auth::UnauthorizedError if Gitlab::ExternalAuthorization.enabled?
|
||||
unless Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?
|
||||
raise ::Gitlab::Auth::UnauthorizedError
|
||||
end
|
||||
|
||||
token = ::DeployToken.active.find_by_token(raw.password)
|
||||
return unless token
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ module Gitlab
|
|||
# deploy tokens are accepted with deploy token headers and basic auth headers
|
||||
def deploy_token_from_request
|
||||
return unless route_authentication_setting[:deploy_token_allowed]
|
||||
return if Gitlab::ExternalAuthorization.enabled?
|
||||
return unless Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?
|
||||
|
||||
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ module Gitlab
|
|||
file.validate_content! if file.valid?
|
||||
file.load_and_validate_expanded_hash! if file.valid?
|
||||
|
||||
if ::Feature.enabled?(:ci_includes_count_duplicates, context.project)
|
||||
if context.expandset.is_a?(Array) # To be removed when FF 'ci_includes_count_duplicates' is removed
|
||||
context.expandset << file
|
||||
else
|
||||
context.expandset.add(file)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@ module Gitlab
|
|||
client_cert.present? && client_key.present?
|
||||
end
|
||||
|
||||
def allow_deploy_tokens_and_deploy_keys?
|
||||
return true unless enabled?
|
||||
|
||||
service_url.blank? && application_settings.allow_deploy_tokens_and_keys_with_external_authn?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def application_settings
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def deploy_key?
|
||||
actor.is_a?(DeployKey) && !Gitlab::ExternalAuthorization.enabled?
|
||||
actor.is_a?(DeployKey) && Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?
|
||||
end
|
||||
|
||||
def deploy_token
|
||||
|
|
@ -375,7 +375,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def deploy_token?
|
||||
actor.is_a?(DeployToken) && !Gitlab::ExternalAuthorization.enabled?
|
||||
actor.is_a?(DeployToken) && Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?
|
||||
end
|
||||
|
||||
def ci?
|
||||
|
|
|
|||
|
|
@ -116,5 +116,43 @@ module Gitlab
|
|||
def with_default_locale(&block)
|
||||
with_locale(::I18n.default_locale, &block)
|
||||
end
|
||||
|
||||
def setup(domain:, default_locale:)
|
||||
setup_repositories(domain)
|
||||
setup_default_locale(default_locale)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_repositories(domain)
|
||||
translation_repositories = [
|
||||
(po_repository(domain, 'jh/locale') if Gitlab.jh?),
|
||||
po_repository(domain, 'locale')
|
||||
].compact
|
||||
|
||||
FastGettext.add_text_domain(
|
||||
domain,
|
||||
type: :chain,
|
||||
chain: translation_repositories,
|
||||
ignore_fuzzy: true
|
||||
)
|
||||
|
||||
FastGettext.default_text_domain = domain
|
||||
end
|
||||
|
||||
def po_repository(domain, path)
|
||||
FastGettext::TranslationRepository.build(
|
||||
domain,
|
||||
path: Rails.root.join(path),
|
||||
type: :po,
|
||||
ignore_fuzzy: true
|
||||
)
|
||||
end
|
||||
|
||||
def setup_default_locale(locale)
|
||||
FastGettext.default_locale = locale
|
||||
FastGettext.default_available_locales = available_locales
|
||||
::I18n.available_locales = available_locales
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ module Gitlab
|
|||
|
||||
if severity
|
||||
if quick_action_target.persisted?
|
||||
::Issues::UpdateService.new(project: quick_action_target.project, current_user: current_user, params: { severity: severity }).execute(quick_action_target)
|
||||
::Issues::UpdateService.new(container: quick_action_target.project, current_user: current_user, params: { severity: severity }).execute(quick_action_target)
|
||||
else
|
||||
quick_action_target.build_issuable_severity(severity: severity)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
def zoom_link_service
|
||||
::Issues::ZoomLinkService.new(project: quick_action_target.project, current_user: current_user, params: { issue: quick_action_target })
|
||||
::Issues::ZoomLinkService.new(container: quick_action_target.project, current_user: current_user, params: { issue: quick_action_target })
|
||||
end
|
||||
|
||||
def zoom_link_params
|
||||
|
|
|
|||
|
|
@ -204,6 +204,14 @@ module Gitlab
|
|||
extra.merge(command_name: command_name, instance_name: instance_name))
|
||||
end
|
||||
|
||||
def default_store
|
||||
use_primary_store_as_default? ? primary_store : secondary_store
|
||||
end
|
||||
|
||||
def fallback_store
|
||||
use_primary_store_as_default? ? secondary_store : primary_store
|
||||
end
|
||||
|
||||
def ping(message = nil)
|
||||
if use_primary_and_secondary_stores?
|
||||
# Both stores have to response success for the ping to be considered success.
|
||||
|
|
@ -226,10 +234,6 @@ module Gitlab
|
|||
false
|
||||
end
|
||||
|
||||
def default_store
|
||||
use_primary_store_as_default? ? primary_store : secondary_store
|
||||
end
|
||||
|
||||
def log_method_missing(command_name, *_args)
|
||||
return if SKIP_LOG_METHOD_MISSING_FOR_COMMANDS.include?(command_name)
|
||||
|
||||
|
|
@ -257,7 +261,7 @@ module Gitlab
|
|||
|
||||
def read_one_with_fallback(command_name, *args, **kwargs, &block)
|
||||
begin
|
||||
value = send_command(primary_store, command_name, *args, **kwargs, &block)
|
||||
value = send_command(default_store, command_name, *args, **kwargs, &block)
|
||||
rescue StandardError => e
|
||||
log_error(e, command_name,
|
||||
multi_store_error_message: FAILED_TO_READ_ERROR_MESSAGE)
|
||||
|
|
@ -276,7 +280,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def fallback_read(command_name, *args, **kwargs, &block)
|
||||
value = send_command(secondary_store, command_name, *args, **kwargs, &block)
|
||||
value = send_command(fallback_store, command_name, *args, **kwargs, &block)
|
||||
|
||||
if value
|
||||
log_error(ReadFromPrimaryError.new, command_name)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
def close_issue(issue:)
|
||||
::Issues::CloseService.new(project: project, current_user: current_user).execute(issue)
|
||||
::Issues::CloseService.new(container: project, current_user: current_user).execute(issue)
|
||||
end
|
||||
|
||||
def presenter(issue)
|
||||
|
|
|
|||
|
|
@ -17010,6 +17010,9 @@ msgstr ""
|
|||
msgid "ExternalAuthorization|Access to projects is validated on an external service using their classification label."
|
||||
msgstr ""
|
||||
|
||||
msgid "ExternalAuthorization|Allow deploy tokens and deploy keys to be used with external authorization"
|
||||
msgstr ""
|
||||
|
||||
msgid "ExternalAuthorization|Certificate used to authenticate with the external authorization service. If blank, the server certificate is validated when accessing over HTTPS."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17028,6 +17031,9 @@ msgstr ""
|
|||
msgid "ExternalAuthorization|Default classification label"
|
||||
msgstr ""
|
||||
|
||||
msgid "ExternalAuthorization|Does not apply if service URL is specified."
|
||||
msgstr ""
|
||||
|
||||
msgid "ExternalAuthorization|Enable classification control using an external service"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21545,6 +21551,9 @@ msgstr ""
|
|||
msgid "ImportProjects|Importing the project failed: %{reason}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ImportProjects|Re-import creates a new project. It does not sync with the existing project."
|
||||
msgstr ""
|
||||
|
||||
msgid "ImportProjects|Requesting namespaces failed"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ module QA
|
|||
element :project_path_field
|
||||
element :import_button
|
||||
element :project_path_content
|
||||
element :go_to_project_button
|
||||
element :go_to_project_link
|
||||
element :import_status_indicator
|
||||
end
|
||||
|
||||
|
|
@ -60,9 +60,9 @@ module QA
|
|||
#
|
||||
# @param [String] gh_project_name
|
||||
# @return [Boolean]
|
||||
def has_go_to_project_button?(gh_project_name)
|
||||
def has_go_to_project_link?(gh_project_name)
|
||||
within_element(:project_import_row, source_project: gh_project_name) do
|
||||
has_element?(:go_to_project_button)
|
||||
has_element?(:go_to_project_link)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ module QA
|
|||
|
||||
aggregate_failures do
|
||||
expect(import_page).to have_imported_project(github_repo, wait: 240)
|
||||
# validate button is present instead of navigating to avoid dealing with multiple tabs
|
||||
# validate link is present instead of navigating to avoid dealing with multiple tabs
|
||||
# which makes the test more complicated
|
||||
expect(import_page).to have_go_to_project_button(github_repo)
|
||||
expect(import_page).to have_go_to_project_link(github_repo)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3')
|
||||
const { fromIni } = require('@aws-sdk/credential-provider-ini')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const crypto = require('crypto')
|
||||
|
||||
function getMD5HashFromFile(data) {
|
||||
const hash = crypto.createHash('md5').update(data).digest('base64')
|
||||
return hash
|
||||
}
|
||||
|
||||
(async function () {
|
||||
const s3Client = new S3Client({
|
||||
region: 'us-east-2',
|
||||
credentials: fromIni({ profile: 'gl-logs-for-panther' }),
|
||||
})
|
||||
try {
|
||||
const file = 'gl-dependency-scanning-report.json'
|
||||
const data = fs.readFileSync(file)
|
||||
|
||||
const [filename, fileext] = path.basename(file).split('.')
|
||||
const uniqueId = process.env['CI_PIPELINE_ID'] && process.env['CI_JOB_ID'] ?
|
||||
process.env['CI_PIPELINE_ID'] + '-' + process.env['CI_JOB_ID'] :
|
||||
Date.now()
|
||||
const key = path.join('package_hunter_logs', filename + '-' + uniqueId + '.' + fileext)
|
||||
|
||||
const responseData = await s3Client.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: 'package-hunter-logs',
|
||||
Key: key,
|
||||
Body: data,
|
||||
ContentMD5: getMD5HashFromFile(data),
|
||||
}),
|
||||
)
|
||||
console.log('Successfully uploaded %s to %s', file, key)
|
||||
} catch (err) {
|
||||
if (err.name === 'CredentialsProviderError' || err.name === 'AuthorizationHeaderMalformed')
|
||||
console.log('Could not upload the report. Are AWS credentials configured in ~/.aws/credentials?')
|
||||
else
|
||||
console.log('Unexpected error during upload: ', err.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})()
|
||||
|
|
@ -190,4 +190,12 @@ RSpec.describe Settings, feature_category: :authentication_and_authorization do
|
|||
expect(described_class.microsoft_graph_mailer.graph_endpoint).to eq('https://graph.microsoft.com')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.repositories' do
|
||||
it 'sets up storage settings' do
|
||||
described_class.repositories.storages.each do |_, storage|
|
||||
expect(storage).to be_a Gitlab::GitalyClient::StorageSettings
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin
|
|||
visit edit_project_issue_path(project, issue)
|
||||
end
|
||||
|
||||
it "previews content" do
|
||||
it "previews content", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391757' do
|
||||
form = first(".gfm-form")
|
||||
|
||||
page.within(form) do
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue